analyze-es6-modules
Version:
Performs static analysis of ES6 modules in your codebase.
255 lines (216 loc) • 8.84 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
exports.readModules = readModules;
var _module = require('./module');
var _module2 = _interopRequireDefault(_module);
var _utility = require('./utility');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var pathModule = require('path');
var babel = require('babel-core');
var Promise = require('bluebird');
var ModuleParser = function () {
function ModuleParser(_ref) {
var cwd = _ref.cwd;
var aliases = _ref.aliases;
var filePath = _ref.filePath;
var resolveModulePath = _ref.resolveModulePath;
var ast = _ref.ast;
_classCallCheck(this, ModuleParser);
this.cwd = cwd;
this.aliases = aliases;
this.filePath = filePath;
this.resolveModulePath = resolveModulePath;
this.ast = ast;
}
_createClass(ModuleParser, [{
key: 'parseModule',
value: function parseModule() {
var _this = this;
// Get the relative path, remove the leading dot and slash, then remove the file extension
var modulePath = pathModule.relative(this.cwd, this.filePath).replace(/^\.\//g, '').replace(/\.[^/.]+$/g, '');
var module = new _module2.default(modulePath);
this.ast.program.body.forEach(function (declaration) {
switch (declaration.type) {
case 'ImportDeclaration':
_this.handleImportDeclaration(module, declaration);
break;
case 'ExportAllDeclaration':
_this.handleExportAllDeclaration(module, declaration);
break;
case 'ExportNamedDeclaration':
_this.handleExportNamedDeclaration(module, declaration);
break;
case 'ExportDefaultDeclaration':
_this.handleExportDefaultDeclaration(module, declaration);
break;
}
});
return module;
}
}, {
key: 'handleImportDeclaration',
value: function handleImportDeclaration(module, declaration) {
var rawSourcePath = declaration.source.value;
var sourcePath = this.resolveImportModulePath(module.path, rawSourcePath);
if (declaration.specifiers.length === 0) {
module.addSideEffectImport({
sourcePath: sourcePath, rawSourcePath: rawSourcePath,
lineNumber: declaration.loc.start.line
});
return;
}
declaration.specifiers.forEach(function (specifier) {
// Ignore Flow type imports (for now)
if (specifier.importKind === 'type') {
return;
}
var lineNumber = specifier.loc.start.line;
switch (specifier.type) {
case 'ImportSpecifier':
var exportName = specifier.imported.name;
if (exportName === 'default') {
module.addDefaultImport({ sourcePath: sourcePath, rawSourcePath: rawSourcePath, lineNumber: lineNumber });
} else {
module.addNamedImport({ exportName: exportName, sourcePath: sourcePath, rawSourcePath: rawSourcePath, lineNumber: lineNumber });
}
break;
case 'ImportDefaultSpecifier':
module.addDefaultImport({ sourcePath: sourcePath, rawSourcePath: rawSourcePath, lineNumber: lineNumber });
break;
case 'ImportNamespaceSpecifier':
module.addBatchImport({ sourcePath: sourcePath, rawSourcePath: rawSourcePath, lineNumber: lineNumber });
break;
}
});
}
}, {
key: 'handleExportAllDeclaration',
value: function handleExportAllDeclaration(module, declaration) {
var rawSourcePath = declaration.source.value;
var sourcePath = this.resolveImportModulePath(module.path, rawSourcePath);
var lineNumber = declaration.loc.start.line;
module.addBatchExport({ sourcePath: sourcePath, rawSourcePath: rawSourcePath, lineNumber: lineNumber });
}
}, {
key: 'handleExportNamedDeclaration',
value: function handleExportNamedDeclaration(module, declaration) {
var _this2 = this;
if (declaration.source) {
declaration.specifiers.forEach(function (specifier) {
var rawSourcePath = declaration.source.value;
var sourcePath = _this2.resolveImportModulePath(module.path, rawSourcePath);
module.addReExport({
exportedName: specifier.exported.name,
importedName: specifier.local.name, source: source,
rawSourcePath: rawSourcePath,
sourcePath: sourcePath,
lineNumber: specifier.loc.start.line
});
});
} else if (declaration.declaration) {
if (declaration.declaration.type === 'FunctionDeclaration') {
module.addNamedExport({
exportName: declaration.declaration.id.name,
lineNumber: declaration.declaration.loc.start.line
});
} else if (declaration.declaration.type === 'VariableDeclaration') {
declaration.declaration.declarations.forEach(function (d) {
module.addNamedExport({
exportName: d.id.name,
lineNumber: d.loc.start.line
});
});
}
} else if (declaration.specifiers) {
declaration.specifiers.forEach(function (specifier) {
var exportName = specifier.exported.name;
var lineNumber = specifier.loc.start.line;
if (exportName === 'default') {
module.addDefaultExport({ lineNumber: lineNumber });
} else {
module.addNamedExport({ exportName: exportName, lineNumber: lineNumber });
}
});
}
}
}, {
key: 'handleExportDefaultDeclaration',
value: function handleExportDefaultDeclaration(module, declaration) {
var lineNumber = declaration.loc.start.line;
module.addDefaultExport({ lineNumber: lineNumber });
}
}, {
key: 'resolveImportModulePath',
value: function resolveImportModulePath(importingModulePath, exportingModuleRelativePath) {
var resolveOptions = { cwd: this.cwd, path: exportingModuleRelativePath, importingModulePath: importingModulePath };
var userResolvedPath = this.resolveModulePath(resolveOptions);
if (userResolvedPath !== undefined) {
return userResolvedPath;
}
if (exportingModuleRelativePath[0] === '.') {
var fullImportModulePath = pathModule.join(this.cwd, importingModulePath);
var importingModuleDirectory = fullImportModulePath.replace(/\/[^/]+$/g, '');
var exportingModulePath = pathModule.resolve(importingModuleDirectory, exportingModuleRelativePath);
// Get the relative path and remove the leading dot and slash
return pathModule.relative(this.cwd, exportingModulePath).replace(/^\.\//g, '');
}
var aliased = this.applyAliases(exportingModuleRelativePath);
if (exportingModuleRelativePath !== aliased) {
return aliased;
}
return exportingModuleRelativePath;
}
}, {
key: 'applyAliases',
value: function applyAliases(modulePath) {
if (this.aliases.module[modulePath]) {
return this.aliases.module[modulePath];
}
for (var prefix in this.aliases.path) {
if (this.aliases.path.hasOwnProperty(prefix)) {
if (modulePath.indexOf(prefix) === 0) {
var rest = modulePath.slice(prefix.length);
var fullPath = pathModule.join(this.cwd, this.aliases.path[prefix], rest);
var absolutePath = pathModule.resolve(fullPath);
return pathModule.relative(this.cwd, absolutePath);
}
}
}
return modulePath;
}
}]);
return ModuleParser;
}();
function readModules(_ref2) {
var cwd = _ref2.cwd;
var sources = _ref2.sources;
var aliases = _ref2.aliases;
var resolveModulePath = _ref2.resolveModulePath;
var fileReader = _ref2.fileReader;
var userBabelOptions = _ref2.babel;
var babelOptions = {
compact: false,
plugins: userBabelOptions.plugins || []
};
return (0, _utility.expandFilePatterns)(cwd, sources).then(function (filePaths) {
var modulePromises = filePaths.map(function (filePath) {
return fileReader(filePath).then(function (fileContents) {
try {
var ast = babel.transform(fileContents, babelOptions).ast;
var moduleParser = new ModuleParser({ cwd: cwd, filePath: filePath, aliases: aliases, resolveModulePath: resolveModulePath, ast: ast });
return moduleParser.parseModule();
} catch (error) {
throw generateParsingErrorMessage(filePath, error);
}
});
});
return Promise.all(modulePromises);
});
}
function generateParsingErrorMessage(filePath, error) {
return 'Parsing error: ' + filePath + '\n' + (error ? error.stack || error.message : '');
}