analyze-es6-modules
Version:
Performs static analysis of ES6 modules in your codebase.
367 lines (306 loc) • 9.62 kB
JavaScript
;
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);
}