vue-component-analyzer
Advanced tools
Comparing version 0.1.0 to 0.1.1
{ | ||
"name": "vue-component-analyzer", | ||
"description": "Analyze dependencies, inheritance and some other useful info of Vue components in static code.", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"author": "Rainfore <rainforest92@126.com>", | ||
@@ -6,0 +6,0 @@ "scripts": { |
@@ -0,1 +1,2 @@ | ||
const fs = require('fs-extra'); | ||
const path = require('path'); | ||
@@ -18,2 +19,9 @@ const babel = require('babel-core'); | ||
purge(files) { | ||
files.forEach((file) => Object.keys(this.caches).forEach((key) => { | ||
if (key.startsWith(file)) | ||
delete this.caches[key]; | ||
})); | ||
} | ||
build() { | ||
@@ -29,24 +37,19 @@ // @TODO | ||
else { | ||
return new Promise((resolve, reject) => { | ||
this.loader.fs.readFile(fullPath, (err, content) => { | ||
if (err) | ||
return reject(err); | ||
return fs.readFile(fullPath).then((content) => { | ||
content = content.toString(); | ||
const isVue = fullPath.endsWith('.vue'); | ||
if (isVue) { | ||
const found = content.match(/<script>([\s\S]+)<\/script>/); | ||
content = found ? found[1] : ''; | ||
} | ||
content = content.toString(); | ||
const isVue = fullPath.endsWith('.vue'); | ||
if (isVue) { | ||
const found = content.match(/<script>([\s\S]+)<\/script>/); | ||
content = found ? found[1] : ''; | ||
} | ||
const jsFile = new JSFile({ | ||
fullPath, | ||
content, | ||
babelResult: babel.transform(content), | ||
isVue, | ||
}); | ||
const jsFile = new JSFile({ | ||
fullPath, | ||
content, | ||
babelResult: babel.transform(content), | ||
isVue, | ||
}); | ||
this.caches[fullPath] = jsFile; | ||
return resolve(jsFile); | ||
}); | ||
this.caches[fullPath] = jsFile; | ||
return jsFile; | ||
}); | ||
@@ -65,11 +68,7 @@ } | ||
return new Promise((resolve, reject) => { | ||
this.loader.resolve(path.dirname(fullPath), sourcePath, (err, importPath) => { | ||
if (err) | ||
return reject(err); | ||
if (importPath.endsWith('.vue') && this.loader.fs.statSync(importPath).isDirectory()) | ||
importPath += '/index.js'; | ||
return resolve(this.loadJSFile(importPath) | ||
.then((jsFile) => this.findVueObject(jsFile, identifier, true, true))); | ||
}); | ||
let importPath = this.resolve(sourcePath, path.dirname(fullPath)); | ||
if (importPath.endsWith('.vue') && fs.statSync(importPath).isDirectory()) | ||
importPath += '/index.js'; | ||
return resolve(this.loadJSFile(importPath) | ||
.then((jsFile) => this.findVueObject(jsFile, 'export', identifier, true))); | ||
}); | ||
@@ -82,11 +81,34 @@ } | ||
* @param {*} identifier | ||
* @param {*} exported - find from exports | ||
* @param {FromType} from - 'local' or 'export' - find from local or exports; | ||
* @param {*} recursive | ||
* @return { objectExpression, jsFile, identifier } | ||
* | ||
* @examples | ||
* e-1. export default {} | ||
* e-2. export default ID [local]-> e-5, e-6, i-7, i-8, i-9, v-10, v-11 | ||
* e-3. export { A as ID } [local]-> e-5, e-6, i-7, i-8, i-9, v-10, v-11 | ||
* e-4. export * from | ||
* e-5. export const ID = {} | ||
* e-6. export const A = ID [local]-> | ||
* i-7. import ID from [export]-> e-1, e-2 | ||
* i-8. import { A as ID } from [export]-> e-3, e-4, e-5, e-6 | ||
* i-9. import * from from [export]-> e-3, e-4, e-5, e-6 | ||
* v-10. const ID | ||
* v-11. const A = ID [local]-> | ||
* | ||
* 3 types: | ||
* from export default | ||
* from export | ||
* from local | ||
*/ | ||
findVueObject(jsFile, identifier = 'default', exported = false, recursive = false) { | ||
findVueObject(jsFile, from = 'export', identifier = 'default', recursive = false, beforeNode) { | ||
if (!identifier) | ||
throw new Error('Argument identifier is required!'); | ||
return Promise.resolve(jsFile).then((jsFile) => { | ||
const babelResult = jsFile.babelResult; | ||
if (identifier === 'default') { | ||
if (from !== 'export' && identifier === 'default') | ||
throw new Error('Identifier `default` is reserved word! Please set `from` as `export`'); | ||
else if (from === 'export' && identifier === 'default') { // Find from export default, ignore 'from' param for easy way | ||
const exportDefault = babelResult.ast.program.body.find((node) => node.type === 'ExportDefaultDeclaration'); | ||
@@ -99,2 +121,3 @@ if (!exportDefault) | ||
objectExpression: exportDefault.declaration, | ||
objectDeclaration: exportDefault, | ||
jsFile, | ||
@@ -105,6 +128,8 @@ identifier, | ||
const exportDefaultName = exportDefault.declaration.name; | ||
return this.findVueObject(jsFile, exportDefaultName); | ||
} | ||
return this.findVueObject(jsFile, 'local', exportDefaultName, recursive); | ||
} else | ||
return null; | ||
} else { | ||
if (exported) { | ||
// Find from exports | ||
if (from === 'export') { | ||
const exportsNode = babelResult.metadata.modules.exports; | ||
@@ -120,6 +145,6 @@ const externalAllSpecifiers = []; | ||
if (exportSpecifier) | ||
identifier = exportSpecifier.local; | ||
identifier = exportSpecifier.local; // Change identifier to local | ||
else if (recursive) | ||
return Promise.all(externalAllSpecifiers.map((specifier) => this.importVueObject(jsFile.fullPath, specifier.source, identifier))) | ||
.then((results) => results.find((result) => !!result.objectExpression)); | ||
.then((results) => results.find((result) => !!result)); | ||
else | ||
@@ -130,3 +155,3 @@ throw new Error('Cannot find identifier in exports: ' + identifier); | ||
if (recursive) { | ||
// find in imports | ||
// Find from imports | ||
let importSpecifier; | ||
@@ -144,21 +169,31 @@ const importsNode = babelResult.metadata.modules.imports.find((impt) => impt.specifiers.some((specifier) => { | ||
// find in body | ||
let objectExpression; | ||
babelResult.ast.program.body.some((node) => { | ||
if (node.type !== 'VariableDeclaration') | ||
return false; | ||
return node.declarations.some((declaration) => { | ||
if (declaration.id.name === identifier && declaration.init.type === 'ObjectExpression') { | ||
objectExpression = declaration.init; | ||
return true; | ||
} else | ||
return false; | ||
}); | ||
}); | ||
// Find from local | ||
for (const node of babelResult.ast.program.body) { | ||
if (node === beforeNode) // 必须在 beforeNode 声明之前 | ||
return null; | ||
return { | ||
objectExpression, | ||
jsFile, | ||
identifier, | ||
}; | ||
let declarations; | ||
if (node.type === 'VariableDeclaration') | ||
declarations = node.declarations; | ||
else if (node.type === 'ExportNamedDeclaration') | ||
declarations = node.declaration.declarations; | ||
else | ||
continue; | ||
for (const declarator of declarations) { | ||
if (declarator.type !== 'VariableDeclarator' || declarator.id.name !== identifier) | ||
continue; | ||
if (declarator.init.type === 'ObjectExpression') { | ||
return { | ||
objectExpression: declarator.init, | ||
objectDeclaration: node, | ||
jsFile, | ||
identifier, | ||
}; | ||
} else if (declarator.init.type === 'Identifier') | ||
return this.findVueObject(jsFile, 'local', declarator.init.name, recursive, node); | ||
} | ||
} | ||
return null; | ||
} | ||
@@ -169,16 +204,23 @@ }); | ||
findSuper(jsFile) { | ||
return this.findVueObject(jsFile, 'default').then(({ objectExpression, jsFile, identifier }) => { | ||
if (jsFile.extends) // Cached | ||
return jsFile.extends; | ||
return this.findVueObject(jsFile).then((vueResult) => { | ||
// this.loader.addDependency(jsFile.fullPath); | ||
const extendsName = objectExpression ? this.findExtendsName(objectExpression) : identifier; | ||
if (!vueResult) | ||
throw new Error('Cannot find vue object!'); | ||
if (vueResult.jsFile.extends) // Cached | ||
return vueResult.jsFile.extends; | ||
const extendsName = vueResult.objectExpression ? this.findExtendsName(vueResult.objectExpression) : vueResult.identifier; | ||
if (!extendsName) | ||
throw new Error('Cannot find extends name'); | ||
throw new Error('Cannot find extends name!'); | ||
return this.findVueObject(jsFile, extendsName, false, true).then((result) => { | ||
if (!result.objectExpression) | ||
throw new Error('Cannot find vue object'); | ||
return this.findVueObject(vueResult.jsFile, 'local', extendsName, true, vueResult.objectDeclaration).then((extendsResult) => { | ||
// this.loader.addDependency(vueResult.jsFile.fullPath); | ||
jsFile.extends = result.jsFile; | ||
return result.jsFile; | ||
if (!extendsResult.objectExpression) | ||
throw new Error('Cannot find vue object!'); | ||
vueResult.jsFile.extends = extendsResult.jsFile; | ||
return extendsResult.jsFile; | ||
}); | ||
@@ -185,0 +227,0 @@ }); |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
16538
12
237
1