import * as babylon from "babylon"; import * as path from "path"; import * as t from "babel-types"; import { traverse } from "babel-core"; import * as fse from "fs-extra"; function getPackageName(node) { return t.isTemplateLiteral(node.arguments[0]) ? node.arguments[0].quasis[0].value.raw : node.arguments[0].value; } const PARSE_PLUGINS = [ "jsx", "typescript", "asyncFunctions", "classConstructorCall", "doExpressions", "trailingFunctionCommas", "objectRestSpread", "decorators", "classProperties", "exportExtensions", "exponentiationOperator", "asyncGenerators", "functionBind", "functionSent" ]; const ext = [".js", ".jsx", ".ts", ".tsx"]; function resolveEXT(pathName: string): string { const extList = ["/index.js", "/index.jsx", "/index.ts", "/index.tsx", ...ext]; return extList.map(ext => pathName + ext).find(p => { try { const stat = fse.statSync(p); return !!stat; } catch (e) { return false; } }) as string; } export async function getPackages( packageMap: object, filePath: string, rootPath: string ): Promise { return fse.readFile(filePath, "utf8").then(content => { let packages: string[] = []; const visitor = { ImportDeclaration(path) { packages.push(path.node.source.value); }, CallExpression(path) { if (path.node.callee.name === "require") { packages.push(getPackageName(path.node)); } } }; const ast = babylon.parse(content, { sourceType: "module", plugins: PARSE_PLUGINS }); traverse(ast, visitor); const fileDirPath = filePath .split("/") .reverse() .slice(1) .reverse() .join("/"); packages = packages .filter(name => name.indexOf("/") !== -1) .map(name => { if (name.startsWith("./") || name.startsWith("../")) { return path.join(fileDirPath, name); } else { return path.join(rootPath, name); } }) .map(resolveEXT) .filter(p => p && !packageMap[p]); packages.forEach(name => { packageMap[name] = true; }); if (packages.length > 0) { return Promise.all( packages .filter(name => ext.includes(path.extname(name))) .map(name => getPackages(packageMap, name, rootPath)) ); } return Promise.resolve([]); }); }