UNPKG

get-es-imports-exports

Version:

Recursively get imports and exports for files using ES import/export syntax

223 lines (158 loc) 10.3 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.defaultParserOptions = exports.defaultParser = undefined; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _fp = require('lodash/fp'); var _resolve = require('resolve'); var _resolve2 = _interopRequireDefault(_resolve); var _minimatch = require('minimatch'); var _minimatch2 = _interopRequireDefault(_minimatch); var _path = require('path'); var _fs = require('fs'); var _tinyPromisify = require('tiny-promisify'); var _tinyPromisify2 = _interopRequireDefault(_tinyPromisify); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { return step("next", value); }, function (err) { return step("throw", err); }); } } return step("next"); }); }; } const readFilePromise = (0, _tinyPromisify2.default)(_fs.readFile); const defaultParser = exports.defaultParser = 'espree'; const defaultParserOptions = exports.defaultParserOptions = { ecmaVersion: 6, sourceType: 'module', ecmaFeatures: { jsx: true } }; const relativeToNodeModulesRe = /^[./\\]+\/node_modules[/\\]/; const pointsToNodeModules = value => relativeToNodeModulesRe.test(value); const getDeclarationFilename = (0, _fp.get)(['source', 'value']); const getAstDeclarationImports = (0, _fp.flow)((0, _fp.get)('body'), (0, _fp.filter)({ type: 'ImportDeclaration' })); const getImportDeclarationImportedNames = (0, _fp.flow)((0, _fp.get)('specifiers'), (0, _fp.map)((0, _fp.cond)([[(0, _fp.matchesProperty)('type', 'ImportDefaultSpecifier'), (0, _fp.constant)('default')], [(0, _fp.matchesProperty)('type', 'ImportNamespaceSpecifier'), (0, _fp.constant)('*')], [(0, _fp.matchesProperty)('type', 'ImportSpecifier'), (0, _fp.get)(['imported', 'name'])]]))); const getAstImportsFromImportStatements = (0, _fp.flow)(getAstDeclarationImports, (0, _fp.groupBy)(getDeclarationFilename), (0, _fp.mapValues)((0, _fp.flatMap)(getImportDeclarationImportedNames))); // Exports const getVariableDeclarationExportedNames = (0, _fp.cond)([[(0, _fp.matchesProperty)('type', 'FunctionDeclaration'), (0, _fp.get)(['id', 'name'])], [(0, _fp.matchesProperty)('type', 'ClassDeclaration'), (0, _fp.get)(['id', 'name'])], [(0, _fp.matchesProperty)('type', 'VariableDeclaration'), (0, _fp.flow)((0, _fp.get)('declarations'), (0, _fp.flatMap)('id.name'))]]); const exportNamedDeclarationExportedNames = (0, _fp.flow)((0, _fp.get)('specifiers'), (0, _fp.flatMap)('exported.name')); const exportNamedDeclarationImportNames = (0, _fp.flow)((0, _fp.get)('specifiers'), (0, _fp.flatMap)('local.name')); const getExportNamedDeclarationExportedNames = (0, _fp.cond)([[(0, _fp.matchesProperty)('declaration', null), exportNamedDeclarationExportedNames], [(0, _fp.constant)(true), (0, _fp.flow)((0, _fp.get)('declaration'), getVariableDeclarationExportedNames)]]); const isExportDefaultDeclaration = (0, _fp.matchesProperty)('type', 'ExportDefaultDeclaration'); const isExportAllDeclaration = (0, _fp.matchesProperty)('type', 'ExportAllDeclaration'); const isExportNamedDeclaration = (0, _fp.matchesProperty)('type', 'ExportNamedDeclaration'); const getExportDeclarationExportedNames = (0, _fp.cond)([[isExportDefaultDeclaration, (0, _fp.constant)(['default'])], [isExportAllDeclaration, (0, _fp.constant)(['*'])], [isExportNamedDeclaration, getExportNamedDeclarationExportedNames]]); const getExportDeclarationImportedNames = (0, _fp.cond)([[isExportNamedDeclaration, exportNamedDeclarationImportNames], [isExportAllDeclaration, (0, _fp.constant)(['*'])]]); const getAstExportsDeclarations = (0, _fp.flow)((0, _fp.get)('body'), (0, _fp.filter)((0, _fp.overSome)([isExportDefaultDeclaration, isExportAllDeclaration, isExportNamedDeclaration]))); const getAstImportsFromExportStatements = (0, _fp.flow)(getAstExportsDeclarations, (0, _fp.filter)(getDeclarationFilename), (0, _fp.groupBy)(getDeclarationFilename), (0, _fp.mapValues)((0, _fp.flatMap)(getExportDeclarationImportedNames))); const getAstLocalExports = (0, _fp.flow)(getAstExportsDeclarations, (0, _fp.flatMap)(getExportDeclarationExportedNames)); const getAstLocalImports = (0, _fp.flow)((0, _fp.over)([getAstImportsFromImportStatements, getAstImportsFromExportStatements]), (0, _fp.reduce)((0, _fp.assignWith)(_fp.union), {})); const resolveDeclrationFilenamePair = (basedir, resolveOptions, _ref) => { var _ref2 = _slicedToArray(_ref, 2); let dependency = _ref2[0]; let value = _ref2[1]; return new Promise((res, rej) => { (0, _resolve2.default)(dependency, (0, _fp.set)('basedir', basedir, resolveOptions), (err, resolvedFilename) => !err ? res([resolvedFilename, value]) : rej(err)); }); }; exports.default = (() => { var ref = _asyncToGenerator(function* (_ref3) { var _ref3$files = _ref3.files; let files = _ref3$files === undefined ? [] : _ref3$files; var _ref3$recurse = _ref3.recurse; let recurse = _ref3$recurse === undefined ? true : _ref3$recurse; var _ref3$exclude = _ref3.exclude; let exclude = _ref3$exclude === undefined ? [] : _ref3$exclude; var _ref3$parser = _ref3.parser; let parser = _ref3$parser === undefined ? defaultParser : _ref3$parser; var _ref3$parserOptions = _ref3.parserOptions; let parserOptions = _ref3$parserOptions === undefined ? defaultParserOptions : _ref3$parserOptions; var _ref3$resolveOptions = _ref3.resolveOptions; let resolveOptions = _ref3$resolveOptions === undefined ? {} : _ref3$resolveOptions; var _require = require(parser); const parse = _require.parse; // eslint-disable-line const excludeValues = (0, _fp.castArray)(exclude); const fileIsExcluded = (0, _fp.isEmpty)(excludeValues) ? (0, _fp.constant)(false) : function (file) { return (0, _fp.some)((0, _fp.cond)([[(0, _fp.includes)('*'), (0, _fp.partial)(_minimatch2.default, [file])], [(0, _fp.constant)(true), (0, _fp.equals)(file)]]), excludeValues); }; const addFile = (() => { var ref = _asyncToGenerator(function* (state, filename) { const imports = state.imports; const exports = state.exports; const loadedFiles = state.loadedFiles; const stats = state.stats; const skip = (0, _fp.includes)(filename, loadedFiles) || fileIsExcluded(filename); if (skip) return { localImports: [], state: state }; let contents; try { contents = String((yield readFilePromise(filename, 'utf-8'))); } catch (e) { if (e.code === 'ENOENT') { throw new Error(`Failed to read file ${ filename }`); } else { throw e; } } const basedir = (0, _path.dirname)(filename); let ast; try { ast = parse(contents, parserOptions); } catch (e) { if ((0, _path.extname)(filename) === '.js') { throw e; } return (0, _fp.update)(['state', 'stats'], (0, _fp.concat)([`Failed to open file ${ filename }`]), state); } const localExports = getAstLocalExports(ast); const allUnresolvedImportsPairsPromises = (0, _fp.flow)(getAstLocalImports, _fp.toPairs, (0, _fp.map)((0, _fp.partial)(resolveDeclrationFilenamePair, [basedir, resolveOptions])))(ast); const allImportsPairs = yield Promise.all(allUnresolvedImportsPairsPromises); const allImports = (0, _fp.fromPairs)(allImportsPairs); const pairIsNodeModule = (0, _fp.flow)(_fp.first, (0, _fp.partial)(_path.relative, [filename]), pointsToNodeModules); const localImports = (0, _fp.flow)((0, _fp.reject)(pairIsNodeModule), _fp.fromPairs)(allImportsPairs); return { localImports: (0, _fp.keys)(localImports), state: { imports: (0, _fp.assignWith)(_fp.union, allImports, imports), exports: (0, _fp.set)([filename], localExports, exports), loadedFiles: (0, _fp.union)([filename], loadedFiles), stats: stats } }; }); return function addFile(_x2, _x3) { return ref.apply(this, arguments); }; })(); let state = { imports: {}, exports: {}, loadedFiles: [], stats: [] }; const getDependenciesFromFile = (() => { var ref = _asyncToGenerator(function* (inputState, filename) { var _ref4 = yield addFile(inputState, filename); let localImports = _ref4.localImports; let state = _ref4.state; // eslint-disable-line if (!recurse) { return state; } for (const otherImport of localImports) { state = yield getDependenciesFromFile(state, otherImport); } return state; }); return function getDependenciesFromFile(_x4, _x5) { return ref.apply(this, arguments); }; })(); const filesValues = (0, _fp.castArray)(files); for (const filename of filesValues) { state = yield getDependenciesFromFile(state, filename); } const sortValues = (0, _fp.mapValues)((0, _fp.sortBy)(_fp.identity)); state = (0, _fp.flow)((0, _fp.update)('imports', sortValues), (0, _fp.update)('exports', sortValues))(state); return state; }); function getDependencies(_x) { return ref.apply(this, arguments); } return getDependencies; })();