@tkskto/vue-component-analyzer
Advanced tools
Comparing version 0.1.3 to 0.1.4
/*! | ||
@tkskto/vue-component-analyzer v0.1.3 | ||
@tkskto/vue-component-analyzer v0.1.4 | ||
https://github.com/tkskto/ | ||
@@ -17,7 +17,7 @@ Released under the MIT License. | ||
var require$$0 = require('vue-eslint-parser'); | ||
var require$$1 = require('fs'); | ||
var path = require('path'); | ||
var fs_1 = require('fs'); | ||
var path_1 = require('path'); | ||
var vue_eslint_parser_1 = require('vue-eslint-parser'); | ||
var http = require('http'); | ||
var require$$0$1 = require('ejs'); | ||
var require$$0 = require('ejs'); | ||
var express = require('express'); | ||
@@ -32,7 +32,7 @@ var webSocket = require('ws'); | ||
var fs_1__default = /*#__PURE__*/_interopDefaultLegacy(fs_1); | ||
var path_1__default = /*#__PURE__*/_interopDefaultLegacy(path_1); | ||
var vue_eslint_parser_1__default = /*#__PURE__*/_interopDefaultLegacy(vue_eslint_parser_1); | ||
var http__default = /*#__PURE__*/_interopDefaultLegacy(http); | ||
var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0); | ||
var require$$1__default = /*#__PURE__*/_interopDefaultLegacy(require$$1); | ||
var path__default = /*#__PURE__*/_interopDefaultLegacy(path); | ||
var http__default = /*#__PURE__*/_interopDefaultLegacy(http); | ||
var require$$0__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$0$1); | ||
var express__default = /*#__PURE__*/_interopDefaultLegacy(express); | ||
@@ -61,33 +61,12 @@ var webSocket__default = /*#__PURE__*/_interopDefaultLegacy(webSocket); | ||
class FileCounter { | ||
constructor() { | ||
this._count = {}; | ||
} | ||
add(filename) { | ||
if (Object.prototype.hasOwnProperty.call(this._count, filename)) { | ||
this._count[filename]++; | ||
} | ||
else { | ||
this._count[filename] = 1; | ||
} | ||
} | ||
get count() { | ||
return this._count; | ||
} | ||
} | ||
var FileCounter_1 = FileCounter; | ||
var utils = createCommonjsModule(function (module, exports) { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const { parse } = require$$0__default['default']; | ||
const { readFileSync, existsSync, statSync } = require$$1__default['default']; | ||
const { resolve, extname, dirname } = path__default['default']; | ||
const cwd = process.cwd(); | ||
const counter = new FileCounter_1(); | ||
const getImportDeclaration = (nodeArr) => { | ||
exports.resolveFile = exports.getDeclarationSyntax = exports.getImportDeclaration = void 0; | ||
const { existsSync } = fs_1__default['default']; | ||
const { resolve, extname, dirname } = path_1__default['default']; | ||
exports.getImportDeclaration = (nodeArr) => { | ||
return nodeArr.filter((node) => node.type === 'ImportDeclaration'); | ||
}; | ||
const getPropsDeclaration = (tokens) => { | ||
let isPropsToken = false; | ||
exports.getDeclarationSyntax = (tokens, targetKeyName) => { | ||
let isTargetToken = false; | ||
let result = '{'; | ||
@@ -98,5 +77,5 @@ let closedCount = 0; | ||
const { type, value } = token; | ||
if (isPropsToken || (type === 'Identifier' && value === 'props')) { | ||
if (isTargetToken || (!isTargetToken && type === 'Identifier' && value === targetKeyName)) { | ||
const needQuoting = needQuotingTypes.includes(type); | ||
isPropsToken = true; | ||
isTargetToken = true; | ||
if (type === 'Punctuator') { | ||
@@ -128,3 +107,3 @@ if (value === '{') { | ||
}; | ||
const resolveFile = (_filename, _currentFileName) => { | ||
exports.resolveFile = (_filename, _currentFileName) => { | ||
let filename = ''; | ||
@@ -141,4 +120,12 @@ if (_filename.startsWith('../')) { | ||
if (filename) { | ||
if (extname(filename) === '' && existsSync(`${filename}.vue`)) { | ||
return `${filename}.vue`; | ||
if (extname(filename) === '') { | ||
if (existsSync(`${filename}.vue`)) { | ||
return `${filename}.vue`; | ||
} | ||
else if (existsSync(`${filename}.js`)) { | ||
return `${filename}.js`; | ||
} | ||
else if (existsSync(`${filename}.ts`)) { | ||
return `${filename}.ts`; | ||
} | ||
} | ||
@@ -148,58 +135,147 @@ } | ||
}; | ||
const getImportDeclarationTree = (fileName, isTest = false) => { | ||
const filename = resolve(cwd, fileName); | ||
const shortFilename = filename.replace(cwd, ''); | ||
const children = []; | ||
const result = { | ||
name: shortFilename, | ||
props: '', | ||
size: 0, | ||
lastModifiedTime: 0, | ||
children, | ||
}; | ||
console.log(`read ${filename}.`); | ||
counter.add(shortFilename); | ||
if (extname(filename) === '') { | ||
return result; | ||
}); | ||
var FileCounter_1 = createCommonjsModule(function (module, exports) { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.FileCounter = void 0; | ||
class FileCounter { | ||
constructor() { | ||
this._count = {}; | ||
} | ||
const stat = statSync(filename); | ||
if (!isTest) { | ||
result.lastModifiedTime = Number(stat.mtimeMs.toFixed(0)); | ||
add(_filename) { | ||
const filename = path_1__default['default'].resolve(_filename); | ||
if (Object.prototype.hasOwnProperty.call(this._count, filename)) { | ||
this._count[filename]++; | ||
} | ||
else { | ||
this._count[filename] = 1; | ||
} | ||
} | ||
result.size = stat.size; | ||
if (extname(filename) !== '.vue') { | ||
return result; | ||
get count() { | ||
return this._count; | ||
} | ||
try { | ||
const file = readFileSync(filename, 'utf-8'); | ||
const parserOption = { | ||
ecmaVersion: 2018, | ||
sourceType: 'module', | ||
} | ||
exports.FileCounter = FileCounter; | ||
}); | ||
var VueComponent_1 = createCommonjsModule(function (module, exports) { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.VueComponent = void 0; | ||
const parserOption = { | ||
ecmaVersion: 2018, | ||
sourceType: 'module', | ||
}; | ||
class VueComponent { | ||
constructor(filename, contents, stats) { | ||
var _a, _b, _c; | ||
this._filename = ''; | ||
this._lastModifiedTime = 0; | ||
this._size = 0; | ||
this._template = ''; | ||
this._style = ''; | ||
this._props = ''; | ||
this._children = []; | ||
this._importDeclaration = []; | ||
this.getProps = (tokens) => { | ||
try { | ||
const propsDeclaration = JSON.parse(utils.getDeclarationSyntax(tokens, 'props')); | ||
if (propsDeclaration && propsDeclaration.props) { | ||
return propsDeclaration.props; | ||
} | ||
return ''; | ||
} | ||
catch (err) { | ||
console.warn('failed to analyze props.'); | ||
return ''; | ||
} | ||
}; | ||
const esLintProgram = parse(file, parserOption); | ||
this._filename = filename; | ||
this._lastModifiedTime = (stats === null || stats === void 0 ? void 0 : stats.mtimeMs) || 0; | ||
this._size = (stats === null || stats === void 0 ? void 0 : stats.size) || 0; | ||
const templateBody = contents.match(/(?<template><template>[\s\S]*<\/template>)/u); | ||
const scriptBody = contents.match(/(?<script><script>[\s\S]*<\/script>)/u); | ||
const styleBody = contents.match(/(?<style><style>[\s\S]*<\/style>)/u); | ||
this._template = ((_a = templateBody === null || templateBody === void 0 ? void 0 : templateBody.groups) === null || _a === void 0 ? void 0 : _a.template) || ''; | ||
this._style = ((_b = styleBody === null || styleBody === void 0 ? void 0 : styleBody.groups) === null || _b === void 0 ? void 0 : _b.template) || ''; | ||
const scriptString = ((_c = scriptBody === null || scriptBody === void 0 ? void 0 : scriptBody.groups) === null || _c === void 0 ? void 0 : _c.script) || ''; | ||
const esLintProgram = vue_eslint_parser_1__default['default'].parse(scriptString, parserOption); | ||
if (esLintProgram.tokens) { | ||
const propsDeclaration = JSON.parse(getPropsDeclaration(esLintProgram.tokens)); | ||
if (propsDeclaration && propsDeclaration.props) { | ||
result.props = propsDeclaration.props; | ||
this._props = this.getProps(esLintProgram.tokens); | ||
} | ||
this._importDeclaration = utils.getImportDeclaration(esLintProgram.body); | ||
} | ||
addChildReport(report) { | ||
this._children.push(report); | ||
} | ||
get importDeclaration() { | ||
return this._importDeclaration; | ||
} | ||
getFileReport(isTest) { | ||
return { | ||
name: this._filename, | ||
props: this._props, | ||
size: this._size, | ||
lastModifiedTime: isTest ? 0 : Number(this._lastModifiedTime.toFixed(0)), | ||
children: this._children, | ||
}; | ||
} | ||
} | ||
exports.VueComponent = VueComponent; | ||
}); | ||
var Analyzer_1 = createCommonjsModule(function (module, exports) { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Analyzer = void 0; | ||
const cwd = process.cwd(); | ||
class Analyzer { | ||
constructor() { | ||
this.getImportDeclarationTree = (fileName, isTest = false) => { | ||
const filename = path_1__default['default'].resolve(cwd, fileName); | ||
const shortFilename = filename.replace(cwd, ''); | ||
const stat = fs_1__default['default'].statSync(filename); | ||
console.log(`read ${filename}.`); | ||
this._counter.add(shortFilename); | ||
if (path_1__default['default'].extname(filename) === '' || path_1__default['default'].extname(filename) !== '.vue') { | ||
return { | ||
name: shortFilename, | ||
props: '', | ||
size: stat.size, | ||
lastModifiedTime: isTest ? 0 : Number(stat.mtimeMs.toFixed(0)), | ||
children: [], | ||
}; | ||
} | ||
} | ||
const body = getImportDeclaration(esLintProgram.body); | ||
for (let i = 0, len = body.length; i < len; i++) { | ||
const source = String(body[i].source.value); | ||
if (source) { | ||
const nextFilename = resolveFile(source, filename); | ||
if (nextFilename) { | ||
children.push(getImportDeclarationTree(nextFilename, isTest)); | ||
const contents = fs_1__default['default'].readFileSync(filename, 'utf-8'); | ||
const component = new VueComponent_1.VueComponent(shortFilename, contents, stat); | ||
try { | ||
for (let i = 0, len = component.importDeclaration.length; i < len; i++) { | ||
const source = String(component.importDeclaration[i].source.value); | ||
if (source) { | ||
const nextFilename = utils.resolveFile(source, filename); | ||
if (nextFilename) { | ||
component.addChildReport(this.getImportDeclarationTree(nextFilename, isTest)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
catch (err) { | ||
console.error(`Something went wrong with reading ${filename}`); | ||
console.error(err.message); | ||
} | ||
return component.getFileReport(isTest); | ||
}; | ||
this._counter = new FileCounter_1.FileCounter(); | ||
} | ||
catch (err) { | ||
console.error(`Something went wrong with reading ${filename}`); | ||
console.error(err.message); | ||
get counter() { | ||
return this._counter; | ||
} | ||
return result; | ||
}; | ||
exports.counter = counter; | ||
exports.getImportDeclarationTree = getImportDeclarationTree; | ||
} | ||
exports.Analyzer = Analyzer; | ||
}); | ||
@@ -211,7 +287,7 @@ | ||
const { renderFile } = require$$0__default$1['default']; | ||
const { renderFile } = require$$0__default['default']; | ||
const projectRoot = path__default['default'].resolve(__dirname, '..'); | ||
const projectRoot = path_1__default['default'].resolve(__dirname, '..'); | ||
const startServer = (port, json) => { | ||
@@ -263,5 +339,5 @@ const HOST = '127.0.0.1'; | ||
}; | ||
const { getImportDeclarationTree, counter } = utils; | ||
const { Analyzer } = Analyzer_1; | ||
const { startServer } = server; | ||
const { writeFileSync } = require$$1__default['default']; | ||
const { writeFileSync } = fs_1__default['default']; | ||
@@ -277,3 +353,3 @@ | ||
function writeFileExtra(filename, data) { | ||
mkdirp__default['default'](path__default['default'].dirname(filename)).then(() => { | ||
mkdirp__default['default'](path_1__default['default'].dirname(filename)).then(() => { | ||
writeFileSync(filename, data); | ||
@@ -296,2 +372,3 @@ }).catch((err) => { | ||
} | ||
const analyzer = new Analyzer(); | ||
const entries = yield globby__default['default']([argv.dir], { | ||
@@ -305,3 +382,3 @@ expandDirectories: { | ||
const entryFile = entries[i]; | ||
const children = getImportDeclarationTree(entryFile); | ||
const children = analyzer.getImportDeclarationTree(entryFile); | ||
entriesData.push(children); | ||
@@ -311,7 +388,7 @@ } | ||
entries: entriesData, | ||
count: counter.count, | ||
count: analyzer.counter.count, | ||
}; | ||
if (argv.format === FORMAT.BOTH) { | ||
startServer(argv.port, result); | ||
writeFileExtra(path__default['default'].resolve(process.cwd(), `${argv.out}/result.json`), JSON.stringify(result, null, 4)); | ||
writeFileExtra(path_1__default['default'].resolve(process.cwd(), `${argv.out}/result.json`), JSON.stringify(result, null, 4)); | ||
} | ||
@@ -322,3 +399,3 @@ else if (argv.format === FORMAT.BROWSER) { | ||
else if (argv.format === FORMAT.JSON) { | ||
writeFileExtra(path__default['default'].resolve(process.cwd(), `${argv.out}/result.json`), JSON.stringify(result, null, 4)); | ||
writeFileExtra(path_1__default['default'].resolve(process.cwd(), `${argv.out}/result.json`), JSON.stringify(result, null, 4)); | ||
} | ||
@@ -325,0 +402,0 @@ console.log('finished analyzing.'); |
{ | ||
"name": "@tkskto/vue-component-analyzer", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"description": "Analyze dependency tree for Vue.js SFC (Single File Component)", | ||
@@ -11,3 +11,4 @@ "main": "dist/index.js", | ||
"scripts": { | ||
"build": "tsc -p tsconfig.json && rollup --config", | ||
"dev": "tsc -p src/server/tsconfig.json -w", | ||
"build": "tsc -p src/server/tsconfig.json && rollup --config", | ||
"lint": "eslint -c .eslintrc.json src", | ||
@@ -14,0 +15,0 @@ "cover": "npm run cover:test && npm run cover:report", |
@@ -9,2 +9,8 @@ # vue-component-analyzer | ||
## Why? | ||
When you try to change the behavior of components, it will help you to investigate the influence range. Because it is hard to know where the component is used. | ||
When you join a new big project using Vue.js, it will help you to understand dependencies. | ||
## installation and usage | ||
@@ -35,3 +41,3 @@ | ||
- `--dir` : analyze target directory. default is `src`. | ||
- `-f` or `--format` : report type. choose one from [browser | json | both]. default is `browser'. | ||
- `-f` or `--format` : report type. choose one from [browser | json | both]. default is `browser`. | ||
- `-o` or `--out` : output directory. JSON file will output here. | ||
@@ -38,0 +44,0 @@ - `-p` or `--port` : select a port number for the local server. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
31659
11
805
49
1