UNPKG

okam-build

Version:

The build tool for Okam develop framework

206 lines (182 loc) 6.32 kB
/** * @file Component transform helper * @author sparklewhy@gmail.com */ 'use strict'; const {removeNode, removeComments, normalizeInternalBehavior} = require('./helper'); /** * Get require expression module id * * @inner * @param {Object} node the require call expression node * @param {Object} t the bable type definition * @return {?string} */ function getRequireExpressionModuleId(node, t) { if (t.isCallExpression(node)) { let {arguments: args, callee} = node; if (t.isIdentifier(callee) && callee.name === 'require' && args.length === 1 && t.isLiteral(args[0]) ) { let id = args[0].value; return id; } } } /** * Remove the bind variable declaration * * @inner * @param {Object} t the babel type definition * @param {Object} bindVar the bind variable path information * @param {Object} removeOpts the remove options */ function removeVariableDeclaration(t, bindVar, removeOpts) { let refPaths = bindVar.referencePaths || []; let removed; // try to remove the bind variable in multiple variable declarations refPaths.forEach(item => { let parentPath = item.getStatementParent(); if (t.isVariableDeclaration(parentPath) && t.isIdentifier(item)) { removeNode(t, item.parentPath, removeOpts); removeComments(t, parentPath, 'leadingComments'); removed = true; } }); // if not multiple variable declarations, // remove the single variable declaration statement if (!removed) { removeNode(t, bindVar.path.getStatementParent(), removeOpts); } } /** * Get the required module path or id information * * @inner * @param {Object} valuePath the used module variable node path * @param {string} moduleName the module variable name * @param {Object} t the babel type definition * @param {boolean=} removeRequireDeclaration whether remove the module require * declaration statement. * @return {string} */ function getRequiredModulePath(valuePath, moduleName, t, removeRequireDeclaration = false) { let bindVar = valuePath.scope.bindings[moduleName]; if (!bindVar) { throw valuePath.buildCodeFrameError(`the variable ${moduleName} import declaration is not found`); } let declareNodePath = bindVar.path; let parentStatementPath = bindVar.path.getStatementParent(); // check import statement if (t.isImportDeclaration(parentStatementPath)) { let id = parentStatementPath.node.source.value; if (removeRequireDeclaration) { let toRemovePath = declareNodePath; if (t.isImportDefaultSpecifier(declareNodePath.node)) { toRemovePath = declareNodePath.parentPath; } removeNode(t, toRemovePath, {tail: true}); } return id; } else if (t.isVariableDeclarator(declareNodePath)) { // check require statement let initNode = declareNodePath.node.init; let id = getRequireExpressionModuleId(initNode, t); if (id) { removeRequireDeclaration && removeVariableDeclaration( t, bindVar, {tail: true} ); return id; } } throw valuePath.buildCodeFrameError(`the variable ${moduleName} import declaration is not found`); } /** * Get component using component info * * @param {Object} node the components node * @param {Object} path the node path * @param {Object} t the babel type definition * @return {Object} */ exports.getUsedComponentInfo = function (node, path, t) { if (!t.isObjectExpression(node)) { throw path.buildCodeFrameError('require object'); } let result = {}; let props = node.properties || []; let propPaths = path.get('value.properties'); let componentPathMap = {}; for (let i = 0, len = props.length; i < len; i++) { let subNode = props[i]; let key; let keyNode = subNode.key; if (t.isIdentifier(keyNode)) { key = keyNode.name; } else if (t.isStringLiteral(keyNode)) { key = keyNode.value; } if (!key) { throw path.buildCodeFrameError('get key info fail'); } let value = subNode.value; if (!t.isIdentifier(value)) { throw path.buildCodeFrameError(`${key} require identifier`); } let componentVarName = value.name; let componentPath = componentPathMap[componentVarName]; if (!componentPath) { let valuePath = propPaths[i].get('value'); componentPath = getRequiredModulePath(valuePath, componentVarName, t, true); componentPathMap[componentVarName] = componentPath; } result[key] = componentPath; } return result; }; /** * Get component mixin module paths info * * @param {Object} node the components node * @param {Object} path the node path * @param {Object} t the babel type definition * @param {Object} opts the transformation options * @return {Array.<string>} */ exports.getUsedMixinModulePaths = function (node, path, t, opts) { if (!t.isArrayExpression(node)) { throw path.buildCodeFrameError('require array'); } let elements = node.elements || []; let elemPaths = path.get('value.elements'); let mixinModulePaths = []; for (let i = 0, len = elements.length; i < len; i++) { let item = elements[i]; let id; if (t.isIdentifier(item)) { let valuePath = elemPaths[i].get('name'); id = getRequiredModulePath(valuePath, item.name, t); } else if (t.isCallExpression(item)) { id = getRequireExpressionModuleId(item); } if (!id) { if (t.isStringLiteral(item)) { let value = item.value; // normalize the internal behavior id item.value = normalizeInternalBehavior(opts.appType, value); } else { throw path.buildCodeFrameError( 'mixins required string literal or using exported mixin module' ); } } id && mixinModulePaths.push(id); } return mixinModulePaths; };