UNPKG

analyze-es6-modules

Version:

Performs static analysis of ES6 modules in your codebase.

367 lines (306 loc) 9.62 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.analyzeModules = analyzeModules; var _StringMultiSet = require('./StringMultiSet'); var _StringMultiSet2 = _interopRequireDefault(_StringMultiSet); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function analyzeModules(_ref) { var modules = _ref.modules; var predefinedModules = _ref.predefinedModules; var ignoreUnused = _ref.ignoreUnused; var exportMap = buildExportMap({ modules: modules, predefinedModules: predefinedModules }); var issues = [].concat(findBadModuleReferences({ modules: modules, predefinedModules: predefinedModules })).concat(findBadImports({ modules: modules, predefinedModules: predefinedModules, exportMap: exportMap })).concat(findDuplicateExports({ modules: modules, exportMap: exportMap })).concat(findUnusedModules({ modules: modules, ignoreUnused: ignoreUnused })).concat(findUnusedExports({ modules: modules, ignoreUnused: ignoreUnused })); return { modules: modules, issues: issues }; } function findBadModuleReferences(_ref2) { var modules = _ref2.modules; var predefinedModules = _ref2.predefinedModules; var modulePaths = modules.map(function (module) { return module.path; }); return modules.reduce(function (issues, module) { module.imports.forEach(function (moduleImport) { var source = moduleImport.exportingModule.resolved; if (modulePaths.indexOf(source) < 0 && !predefinedModules[source]) { issues.push({ type: 'missingModule', importingModule: module.path, exportingModule: moduleImport.exportingModule, lineNumber: moduleImport.lineNumber }); } }); module.exports.forEach(function (moduleExport) { if (moduleExport.type !== 'batch') { return; } var source = moduleExport.exportingModule.resolved; if (modulePaths.indexOf(source) < 0 && !predefinedModules[source]) { issues.push({ type: 'missingModule', importingModule: module.path, exportingModule: moduleExport.exportingModule, lineNumber: moduleExport.lineNumber }); } }); return issues; }, []); } function findBadImports(_ref3) { var modules = _ref3.modules; var predefinedModules = _ref3.predefinedModules; var exportMap = _ref3.exportMap; return modules.reduce(function (issues, module) { return module.imports.reduce(function (issues, moduleImport) { var predefined = predefinedModules[moduleImport.exportingModule.resolved]; var moduleExportMap = exportMap[moduleImport.exportingModule.resolved]; // Module not found, error is reported elsewhere if (!predefined && !moduleExportMap) { return issues; } if (predefined) { switch (moduleImport.type) { case 'default': if (predefined === true || predefined && predefined['default']) { break; } issues.push({ type: 'badImport', importingModule: module.path, exportingModule: moduleImport.exportingModule, exportType: 'default', lineNumber: moduleImport.lineNumber }); break; case 'named': if (predefined === true) { break; } if (predefined && predefined.named && predefined.named.indexOf(moduleImport.exportName) >= 0) { break; } issues.push({ type: 'badImport', importingModule: module.path, exportingModule: moduleImport.exportingModule, exportType: 'named', exportName: moduleImport.exportName, lineNumber: moduleImport.lineNumber }); break; } } else { switch (moduleImport.type) { case 'default': if (!moduleExportMap['default']) { issues.push({ type: 'badImport', importingModule: module.path, exportingModule: moduleImport.exportingModule, exportType: 'default', lineNumber: moduleImport.lineNumber }); } break; case 'named': if (moduleExportMap.named.indexOf(moduleImport.exportName) < 0) { issues.push({ type: 'badImport', importingModule: module.path, exportingModule: moduleImport.exportingModule, exportType: 'named', exportName: moduleImport.exportName, lineNumber: moduleImport.lineNumber }); } break; } } return issues; }, issues); }, []); } function findDuplicateExports(_ref4) { var modules = _ref4.modules; var exportMap = _ref4.exportMap; return modules.reduce(function (issues, module) { // TODO: Duplicate default exports? var namedExports = exportMap[module.path].named; var namedSet = new _StringMultiSet2.default(namedExports); if (namedExports.length !== namedSet.size) { namedSet.items.forEach(function (name) { if (namedSet.count(name) > 1) { issues.push({ type: 'duplicateExport', exportingModule: module.path, exportType: 'named', exportName: name, lineNumber: 0 // TODO }); } }); } return issues; }, []); } function findUnusedModules(_ref5) { var modules = _ref5.modules; var ignoreUnused = _ref5.ignoreUnused; var importMap = {}; modules.forEach(function (module) { module.imports.forEach(function (moduleImport) { importMap[moduleImport.exportingModule.resolved] = true; }); module.exports.forEach(function (moduleExport) { if (moduleExport.type === 'batch') { importMap[moduleExport.exportingModule.resolved] = true; } }); }); return modules.reduce(function (issues, module) { if (!importMap[module.path] && ignoreUnused[module.path] !== true) { issues.push({ type: 'unusedModule', module: module.path }); } return issues; }, []); } function findUnusedExports(_ref6) { var modules = _ref6.modules; var ignoreUnused = _ref6.ignoreUnused; var importMap = modules.reduce(function (importMap, module) { importMap[module.path] = { batch: false, 'default': false, named: [] }; return importMap; }, {}); modules.forEach(function (module) { module.imports.forEach(function (moduleImport) { // Either a predefined or non-existent module. if (!importMap[moduleImport.exportingModule.resolved]) { return; } switch (moduleImport.type) { case 'batch': importMap[moduleImport.exportingModule.resolved].batch = true; break; case 'default': importMap[moduleImport.exportingModule.resolved]['default'] = true; break; case 'named': importMap[moduleImport.exportingModule.resolved].named.push(moduleImport.exportName); break; case 'sideEffect': // This only checks for unused exports. Unused modules are checked elsewhere. break; } }); module.exports.forEach(function (moduleExport) { if (moduleExport.type === 'batch') { // Make sure it's not a predefined or non-existent module. if (importMap[moduleExport.exportingModule.resolved]) { importMap[moduleExport.exportingModule.resolved].batch = true; } } }); }); return modules.reduce(function (issues, module) { module.exports.forEach(function (moduleExport) { if (importMap[module.path].batch) { return; } var unused = ignoreUnused[module.path]; if (unused === true) { return; } switch (moduleExport.type) { case 'default': if (unused && unused['default']) { break; } if (!importMap[module.path]['default']) { issues.push({ type: 'unusedExport', exportingModule: module.path, exportType: 'default', lineNumber: 0 // TODO }); } break; case 'named': if (unused && unused.named && unused.named.indexOf(moduleExport.exportName) >= 0) { break; } if (importMap[module.path].named.indexOf(moduleExport.exportName) < 0) { issues.push({ type: 'unusedExport', exportingModule: module.path, exportType: 'named', exportName: moduleExport.exportName, lineNumber: 0 // TODO }); } break; } }); return issues; }, []); } function buildExportMap(_ref7) { var modules = _ref7.modules; var predefinedModules = _ref7.predefinedModules; var exportMap = modules.reduce(function (exportMap, module) { var exports = { 'default': false, named: [], batch: [] }; module.exports.forEach(function (moduleExport) { switch (moduleExport.type) { case 'default': exports['default'] = true; break; case 'named': exports.named.push(moduleExport.exportName); break; case 'batch': exports.batch.push(moduleExport.exportingModule.resolved); break; } }); exportMap[module.path] = exports; return exportMap; }, {}); return Object.keys(exportMap).reduce(function (exportMap, path) { exportMap[path] = { 'default': exportMap[path]['default'], named: resolveAllNamedImports(predefinedModules, exportMap, path) }; return exportMap; }, exportMap); } function resolveAllNamedImports(predefinedModules, exportMap, path) { var stack = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : []; if (predefinedModules[path]) { return predefinedModules[path].named || []; } // Missing module errors are reported elsewhere if (!exportMap[path]) { return []; } if (stack.indexOf(path) >= 0) { return exportMap[path].named; } var batchExports = exportMap[path].batch || []; return batchExports.reduce(function (namedImports, batchPath) { var newStack = stack.concat([path]); return namedImports.concat(resolveAllNamedImports(predefinedModules, exportMap, batchPath, newStack)); }, exportMap[path].named); }