"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = default_1; var object_assign_1 = __importDefault(require("object.assign")); var estraverse_1 = __importDefault(require("estraverse")); var Node_ts_1 = __importDefault(require("./Node.js")); var ImportNode_ts_1 = __importDefault(require("./ImportNode.js")); function isRequireCallee(node) { return node.type === 'CallExpression' && !!node.callee && node.callee.type === 'Identifier' && node.callee.name === 'require'; } function isDefineCallee(node) { return node.type === 'CallExpression' && !!node.callee && node.callee.type === 'Identifier' && node.callee.name === 'define'; } function isArrayExpr(node) { return node.type === 'ArrayExpression'; } function isFuncExpr(node) { return (/FunctionExpression$/).test(node.type); } // Set up an AST Node similar to an ES6 import node function constructImportNode(ast, node, type) { var start = node.start, end = node.end; return new ImportNode_ts_1.default(ast, node, { type: type, specifiers: [], start: start, end: end, }); } function createImportSpecifier(source, definition, isDef) { var imported; if (definition.type === 'MemberExpression') { imported = (0, object_assign_1.default)({}, definition.property); isDef = false; } // Add the specifier var type = source.type, start = source.start, end = source.end; var name = 'name' in source ? source.name : undefined; return new Node_ts_1.default({ start: start, end: end, type: 'ImportSpecifier', local: { type: type, start: start, end: end, name: name, }, imported: imported, default: isDef, }); } function createSourceNode(node, source) { var start = source.start, end = source.end; var value = 'value' in source ? source.value : undefined; var raw = 'raw' in source ? source.raw : undefined; return new Node_ts_1.default({ type: 'Literal', reference: node, value: value, raw: raw, start: start, end: end, }); } function setImportSource(result, node, importExpr) { if (importExpr.type === 'MemberExpression') { importExpr = importExpr.object; } result.source = createSourceNode(node, importExpr.arguments[0]); return result; } function constructCJSImportNode(ast, node) { var _a; var result = constructImportNode(ast, node, 'CJSImport'); var importExpr = node; switch (node.type) { case 'MemberExpression': case 'CallExpression': importExpr = node; break; case 'AssignmentExpression': { var specifier = createImportSpecifier(node.left, node.right, false); var named = 'property' in node.left ? node.left.property : node.left; var name = 'name' in named ? named.name : undefined; specifier.local.name = name; result.specifiers.push(specifier); importExpr = node.right; break; } case 'VariableDeclarator': // init for var, value for property importExpr = (_a = node.init) !== null && _a !== void 0 ? _a : node; result.specifiers.push(createImportSpecifier(node.id, importExpr, true)); break; case 'Property': { // init for var, value for property importExpr = node.value; result.specifiers.push(createImportSpecifier(node.key, importExpr, false)); break; } default: } return setImportSource(result, node, importExpr); } function findCJS(ast) { // Recursively walk ast searching for requires var requires = []; estraverse_1.default.traverse(ast, { fallback: 'iteration', enter: function (esNode) { var node = esNode; function checkRequire(expr) { if (expr && expr.type === 'MemberExpression') { expr = expr.object; } if (expr && isRequireCallee(expr)) { requires.push(node); return true; } } switch (node.type) { case 'MemberExpression': case 'CallExpression': checkRequire(node); break; case 'AssignmentExpression': checkRequire(node.right); break; case 'Property': checkRequire(node.value); break; case 'VariableDeclarator': checkRequire(node.init); break; default: } }, }); /* * Filter the overlapping requires (e.g. if var x = require('./x') it'll show up twice). * Do this by just checking line #'s */ return requires .filter(function (node) { return !requires.some(function (parent) { return [node.start, node.stop].some(function (pos) { return pos !== undefined && pos > parent.start && pos < parent.end; }); }); }) .map(function (node) { return constructCJSImportNode(ast, node); }); } // Note there can be more than one define per file with global registeration. function findAMD(ast) { return ast.body .filter(function (node) { return node.type === 'ExpressionStatement'; }) .map(function (node) { return node.expression; }) .filter(isDefineCallee) // Ensure the define takes params and has a function .filter(function (node) { return node.arguments.length <= 3; }) .filter(function (node) { return node.arguments.filter(isFuncExpr).length === 1; }) .filter(function (node) { return node.arguments.filter(isArrayExpr).length <= 1; }) // Now just zip the array arguments and the provided function params .map(function (node) { var outnode = constructImportNode(ast, node, 'AMDImport'); var func = node.arguments.filter(isFuncExpr)[0]; var imports = node.arguments.find(isArrayExpr) || { elements: [] }; var params = func.params.slice(0, imports.elements.length); outnode.specifiers = params; if (imports) { /* * Use an array even though its not spec as there isn't a better way to * represent this structure */ outnode.sources = imports.elements.map(function (imp) { return createSourceNode(node, imp); }); // Make nicer repr: [[importSrc, paramName]] outnode.imports = imports.elements.map(function (imp, i) { return [imp, params[i]]; }); } return outnode; }); } function default_1(ast, options) { options = (0, object_assign_1.default)({ cjs: true, // TODO amd: false, es6: true, }, options); var result = []; var root = ast; if (options.cjs) { result.push.apply(result, findCJS(root)); } if (options.es6) { result.push.apply(result, root.body .filter(function (node) { return node.type === 'ImportDeclaration'; }) .map(function (node) { return new ImportNode_ts_1.default(root, node, node); })); } if (options.amd) { result.push.apply(result, findAMD(root)); } return result.sort(function (a, b) { return a.start - b.start; }); }