packages/build-plugin-lce/src/compiler/depAnalyze.js (156 lines of code) (raw):

const fse = require('fs-extra'); const path = require('path'); const babel = require('@babel/core'); const { REG_JS } = require('../configs/reg'); const getPkgJSON = (cwd, module) => { const pkgPath = path.join(cwd, 'node_modules', module, 'package.json'); if (!fse.existsSync(pkgPath)) return {}; const jsonString = fse.readFileSync(pkgPath, 'utf-8'); return JSON.parse(jsonString); }; const defaultDynamicImportLibraries = [ 'antd', '@alifd/next', '@alife/next', '@icedesign/base', ]; function analyzePackage(pkg, basicComponents) { // get dependencies from pakage.json const { dependencies = {}, devDependencies = {}, peerDependencies = {} } = pkg; const libraryNames = []; if (basicComponents) { Object.keys({ ...dependencies, ...devDependencies, ...peerDependencies }).forEach((depName) => { // basic component: antd、@alifd/next、@alife/next、@icedesign/base if ( [ ...defaultDynamicImportLibraries, ...basicComponents, ].includes(depName) ) { libraryNames.push(depName); } }); } return libraryNames; } function filterDeps({ deps, rootDir, basicComponents }) { return deps.filter((dep) => { // relative path if (/^\./.test(dep)) { return false; } let basicLibrary = [ /@icedesign\/.*/, /^@icedesign\/base\/(lib|es)\/([^/]+)/, /@alife\/.*/, /^@alife\/next\/(lib|es)\/([^/]+)/, /@alifd\/.*/, /^@alifd\/next\/(lib|es)\/([^/]+)/, /@ali\/ice-.*/, /antd\/.*/, ]; if (basicComponents) { basicComponents.forEach((component) => { basicLibrary.push(new RegExp(`${component}/.*`)); }); } else { // clear basicLibrary if set basicComponents to false basicLibrary = []; } const isBasicLibrary = basicLibrary.some((library) => { return library.test(dep); }); if (isBasicLibrary) { return true; } const pkgJSON = getPkgJSON(rootDir, dep); if (pkgJSON && (pkgJSON.componentConfig || pkgJSON.stylePath)) { return true; } return false; }); } function getFileContent(filepath) { try { return String(fse.readFileSync(filepath)); } catch (err) { console.log('Can not open file ', filepath); return ''; } } // analyze require() function analyzeAST(code) { const result = []; const visitor = { CallExpression(nodePath) { const { callee, arguments: args } = nodePath.node; const isImportNode = ( callee.type === 'Identifier' && callee.name === 'require' ) || callee.type === 'Import'; if ( isImportNode && args.length === 1 && args[0].type === 'StringLiteral' ) { result.push(args[0].value); } }, ImportDeclaration(nodePath) { result.push(nodePath.node.source.value); }, ExportAllDeclaration(nodePath) { const { node } = nodePath; if (node.source) { result.push(node.source.value); } }, ExportNamedDeclaration(nodePath) { const { node } = nodePath; if (node.source) { result.push(node.source.value); } }, }; babel.transformSync(code, { plugins: [{ visitor }], }); return result; } function dedupe(arr) { if (!Array.isArray(arr)) { throw new TypeError('[dedupe]: arr should be an array;'); } const map = {}; for (let i = 0, len = arr.length; i < len; i++) { const key = arr[i]; map[key] = true; } return Object.keys(map); } // require.resolve .jsx and .vue files require.extensions['.jsx'] = require.extensions['.js']; require.extensions['.vue'] = require.extensions['.js']; function analyzeDependencies(entryFilePath, rootDir, basicComponents) { const tracedFiles = {}; let result = []; function trace(filename) { // filter traceFiles and assets files if (tracedFiles[filename] || !REG_JS.test(filename)) { return; } tracedFiles[filename] = true; const fileContent = getFileContent(filename); const analyzeResult = dedupe(analyzeAST(fileContent)); result = result.concat(analyzeResult); analyzeResult.forEach((module) => { if (/^\./.test(module)) { const modulePath = require.resolve( path.join(path.dirname(filename), module), ); trace(modulePath); } }); } trace(require.resolve(entryFilePath)); const deps = dedupe(result); return filterDeps({ deps, rootDir, basicComponents }); } module.exports = { analyzeDependencies, analyzePackage, defaultDynamicImportLibraries, };