UNPKG

san-hot-loader

Version:
418 lines (369 loc) 12.8 kB
/** * Copyright (c) Baidu Inc. All rights reserved. * * This source code is licensed under the MIT license. * See LICENSE file in the project root for license information. * * @file ast.js * @author clark-t */ /** * 获取 property 的关键字,property 可能是 string 也可能是 字符串 * * @param {ASTNode} id property 节点 * @return {string|null} 关键字 */ function val(id) { return (id.type === 'Identifier' && id.name) || (id.type === 'StringLiteral' && id.value); } /** * 获取 Program 下面的 body 节点 * * @param {ASTNode} ast File || Program * @return {Array.<ASTNode>|undefined} body */ function getProgramBody(ast) { return (ast.program || ast).body; } function isImportedAPI(ast, nodeOrTrackers, moduleName, api) { let trackers = Array.isArray(nodeOrTrackers) ? nodeOrTrackers : getTopLevelIdentifierTracker(ast, nodeOrTrackers); if (!trackers || typeof trackers[0] !== 'string' || trackers[0] !== moduleName ) { return false; } if (!api) { return true; } if (trackers.length < 2) { return false; } return api === val(trackers[1]); } function getExportDefault(ast) { const body = getProgramBody(ast); for (let node of body) { // export default xxx if (node.type === 'ExportDefaultDeclaration') { return node.declaration; } // module.exports = xxx if (node.type === 'ExpressionStatement' && isModuleExports(node.expression)) { return node.expression.right; } } } function getTopLevelIdentifierTracker(ast, inputNode) { const body = getProgramBody(ast); let results; if (inputNode.type === 'Identifier') { results = [inputNode]; } else if (inputNode.type === 'MemberExpression') { results = getPropertyList(inputNode); } else { return [inputNode]; } if (!results) { return; } let identifier = results[0]; for (let i = body.length - 1; i > -1; i--) { let node = body[i]; if (node.start > identifier.start) { continue; } // EXP: // import san from 'san' // import * as san from 'san' // import {defineComponent as defComp} from 'san' if (node.type === 'ImportDeclaration') { for (let specifier of node.specifiers) { if (specifier.type === 'ImportNamespaceSpecifier' || specifier.type === 'ImportDefaultSpecifier' ) { if (specifier.local.name === val(identifier)) { results[0] = node.source.value; return results; } } else if (specifier.type === 'ImportSpecifier') { if (specifier.local.name === val(identifier)) { results[0] = specifier.imported; results.unshift(node.source.value); return results; } } } } // EXP: // var san = require('san') // var {defineComponent: def} = require('san') if (node.type === 'VariableDeclaration') { for (let declarator of node.declarations) { if (isRequire(declarator.init)) { if (declarator.id.type === 'Identifier') { if (declarator.id.name === val(identifier)) { results[0] = declarator.init.arguments[0].value; return results; } } else if (declarator.id.type === 'ObjectPattern') { // 先不考虑 var {a: {b: c}} = require('xxx') 这种深层的情况,因为目前在 san HMR 的模块判断上用不到 for (let property of declarator.id.properties) { if (property.value.type !== 'Identifier') { continue; } if (property.value.name === val(identifier)) { results[0] = property.key; results.unshift(declarator.init.arguments[0].value); return results; } } } } } } if (node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression' && node.expression.left.type === 'Identifier' && val(identifier) === node.expression.left.name ) { if (node.expression.right.type === 'Identifier') { identifier = node.expression.right; results[0] = identifier; continue; } if (node.expression.right.type === 'MemberExpression') { let list = getPropertyList(node.expression.right); // 不处理复杂的 MemberExpression if (!list) { return; } results.shift(); results = [...list, ...results]; identifier = list[0]; continue; } // 遇到了其他类型,将其返回到外部做进一步处理 results[0] = node.expression.right; return results; } if (node.type === 'VariableDeclaration') { for (let j = node.declarations.length - 1; j > -1; j--) { let declarator = node.declarations[j]; if (declarator.id.type === 'ObjectPattern') { let list = getObjectPatternList(declarator.id, identifier); if (!list) { continue; } results.shift(); results = [...list, ...results]; } else if (declarator.id.type !== 'Identifier' || declarator.id.name !== val(identifier)) { continue; } if (!declarator.init) { return; } if (declarator.init.type === 'Identifier') { identifier = declarator.init; if (declarator.id.type === 'ObjectPattern') { results.unshift(identifier); } else { results[0] = identifier; } continue; } if (declarator.init.type === 'MemberExpression') { let list = getPropertyList(declarator.init); // 不处理复杂的 MemberExpression if (!list) { return; } if (declarator.id.type === 'Identifier') { results.shift(); } results = [...list, ...results]; identifier = list[0]; continue; } if (declarator.id.type === 'ObjectPattern') { results.unshift(declarator.init); } else { results[0] = declarator.init; } return results; } } if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression' || node.type === 'FunctionDeclaration' ) { if (node.id.name === val(identifier)) { results[0] = node; return results; } } } return results; } function getPropertyList(memberExpression) { let arr = []; while (memberExpression.property) { if (memberExpression.computed === false && memberExpression.property.type === 'Identifier') { arr.unshift(memberExpression.property); } else if (memberExpression.computed === true && memberExpression.property.type === 'StringLiteral') { arr.unshift(memberExpression.property); } else { // 不考虑过于复杂的计算属性,比如 san['defi' + 'neComponent'],因为去做这种检测就会没完没了并且可能会存在误判的问题,因此不如通过文档限定写法 return; } if (memberExpression.object.type === 'MemberExpression') { memberExpression = memberExpression.object; continue; } if (memberExpression.object.type === 'Identifier') { arr.unshift(memberExpression.object); return arr; } return; } } function getObjectPatternList(objectPattern, identifier) { for (let node of objectPattern.properties) { if (node.value.type === 'Identifier' && node.value.name === val(identifier) ) { if (node.key.type === 'Identifier' || node.key.type === 'StringLiteral') { return [node.key]; } // 不处理复杂型解构,如 let {[a + 1]: def} = xxx return; } if (node.value.type === 'ObjectPattern') { let result = getObjectPatternList(node.value, identifier); if (result) { return [node.key, ...result]; } } } } function isModuleImported(ast, moduleName) { const body = getProgramBody(ast); for (let node of body) { if (isImport(node) && node.source.value === moduleName) { return true; } if (node.type === 'VariableDeclaration') { for (let declarator of node.declarations) { if (isRequire(declarator.init) && declarator.init.arguments[0].value === moduleName) { return true; } } } } return false; } function isImport(node) { return node.type === 'ImportDeclaration'; } function isRequire(node) { return node && node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'require' && node.arguments && node.arguments.length === 1 && node.arguments[0].type === 'StringLiteral'; } function isExports(node) { return node.type === 'AssignmentExpression' && node.left.type === 'MemberExpression' && node.left.object.name === 'exports'; } function isModuleExports(node) { return node.type === 'AssignmentExpression' && node.left.type === 'MemberExpression' && node.left.object.name === 'module' && node.left.property.name === 'exports'; } function hasExport(ast) { if (getExportDefault(ast)) { return true; } const body = getProgramBody(ast); for (let node of body) { if (node.type === 'ExportNamedDeclaration') { return true; } // commonjs // exports.xxx = function () {} if (node.type === 'ExpressionStatement' && isExports(node.expression)) { return true; } // commonjs // exports.a = 1, exports.b = 2 if (node.type === 'ExpressionStatement' && node.expression.type === 'SequenceExpression' ) { for (let subNode of node.expression.expressions) { if (isExports(subNode)) { return true; } } } } return false; } function hasModuleHot(ast) { const body = getProgramBody(ast); for (let node of body) { if (node.type === 'IfStatement' && node.test.type === 'MemberExpression' && node.test.object.name === 'module' && node.test.property.name === 'hot' ) { return true; } } return false; } function hasComment(ast, text) { const body = getProgramBody(ast); let comments = []; for (let node of body) { if (node.leadingComments) { comments = comments.concat(node.leadingComments); } if (node.trailingComments) { comments = comments.concat(node.trailingComments); } } for (let comment of comments) { if (comment.value.trim() === text) { return true; } } return false; } module.exports = { val, getProgramBody, isImportedAPI, getExportDefault, getTopLevelIdentifierTracker, getPropertyList, getObjectPatternList, isModuleImported, hasExport, hasModuleHot, hasComment };