Object.keys(node).forEach(function(k) { var child = node[k]; if (child && typeof child === "object" && child.length) { child.forEach(function (c) { if (c && typeof c.type === 'string') { walk(c); } }); let i,j; for (i=0; i<child.length; i++) { let start, end; let fragment = [t.identifier('pug_html')] if (needCompaction(child[i])) { start = i; end = i; // locate sequential buffer operations while (needCompaction(child[end]) && end < child.length && fragment.length < 101) { fragment.push(child[end].expression.right.right) end++; } // join adjacent stringLiterals for (j=0; j<fragment.length;j++) { let start, end; if (t.isStringLiteral(fragment[j])) { start = j; end = j; while (t.isStringLiteral(fragment[end]) && end < fragment.length) { end++ } let lit = t.stringLiteral(fragment.slice(start, end).map(function(v) { return v.value}).join('')); lit.extra = { rawValue: lit.value, raw: stringify(lit.value)} fragment.splice(start, end-start, lit) } } // join fragments let expr = t.expressionStatement( t.assignmentExpression( '=', t.identifier('pug_html'), fragment.reduce(function(acc, val) { return t.binaryExpression('+', acc, val); }) ) ) child.splice(start, end-start, expr) } } } else if (child && typeof child.type === 'string') { walk(child); } })
module.exports = function matchesPattern(member, match, allowPartial) { // not a member expression if (!types.isMemberExpression(member)) return false; const parts = Array.isArray(match) ? match : match.split('.'); const nodes = []; let node; for (node = member; types.isMemberExpression(node); node = node.object) { nodes.push(node.property); } nodes.push(node); if (nodes.length < parts.length) return false; if (!allowPartial && nodes.length > parts.length) return false; for (let i = 0, j = nodes.length - 1; i < parts.length; i++, j--) { const node = nodes[j]; let value; if (types.isIdentifier(node)) { value = node.name; } else if (types.isStringLiteral(node)) { value = node.value; } else { return false; } if (parts[i] !== value) return false; } return true; };
export function push(mutatorMap: Object, node: Object, kind: string, file, scope?): Object { let alias = t.toKeyAlias(node); // let map = {}; if (has(mutatorMap, alias)) map = mutatorMap[alias]; mutatorMap[alias] = map; // map._inherits = map._inherits || []; map._inherits.push(node); map._key = node.key; if (node.computed) { map._computed = true; } if (node.decorators) { let decorators = map.decorators = map.decorators || t.arrayExpression([]); decorators.elements = decorators.elements.concat(node.decorators.map(dec => dec.expression).reverse()); } if (map.value || map.initializer) { throw file.buildCodeFrameError(node, "Key conflict with sibling node"); } let key, value; // save the key so we can possibly do function name inferences if (t.isObjectProperty(node) || t.isObjectMethod(node) || t.isClassMethod(node)) { key = t.toComputedKey(node, node.key); } if (t.isObjectProperty(node) || t.isClassProperty(node)) { value = node.value; } else if (t.isObjectMethod(node) || t.isClassMethod(node)) { value = t.functionExpression(null, node.params, node.body, node.generator, node.async); } let inheritedKind = toKind(node); if (!kind || inheritedKind !== "value") { kind = inheritedKind; } // infer function name if (scope && t.isStringLiteral(key) && (kind === "value" || kind === "initializer") && t.isFunctionExpression(value)) { value = nameFunction({ id: key, node: value, scope }); } if (value) { t.inheritsComments(value, node); map[kind] = value; } return map; }
function handleThirdPartyComponent(expr) { if (t.isClassProperty(expr) && expr.key.name === 'config' && t.isObjectExpression(expr.value)) { const properties = expr.value.properties; for (const prop of properties) { if (t.isObjectProperty(prop) && (t.isIdentifier(prop.key, { name: 'usingComponents' }) || t.isStringLiteral(prop.key, { value: 'usingComponents' })) && t.isObjectExpression(prop.value)) { for (const value of prop.value.properties) { if (t.isObjectProperty(value)) { if (t.isStringLiteral(value.key)) { constant_1.THIRD_PARTY_COMPONENTS.add(value.key.value); } if (t.isIdentifier(value.key)) { constant_1.THIRD_PARTY_COMPONENTS.add(value.key.name); } } } } } } }
function getMemberValue(node) { if (types.isIdentifier(node)) { return node.name; } else if (types.isStringLiteral(node)) { return node.value; } else if (types.isMemberExpression(node)) { if (node.computed && !types.isStringLiteral(node.property)) { return null; } var objVal = getMemberValue(node.object); if (objVal == null) { return null; } var propVal = getMemberValue(node.property); if (propVal == null) { return null; } return objVal + '.' + propVal; } return null; }
function isRequire(node, filename) { if ( t.isCallExpression(node) && t.isIdentifier(node.callee, { name: 'require' }) && node.arguments.length === 1 && t.isStringLiteral(node.arguments[0]) ) { var relative = node.arguments[0].value; var fullPath = resolve.sync(relative, { basedir: dirname(filename) }); return { relative: relative, fullPath: fullPath }; } else { return false; } }
function convertAttribute(node) { let value = convertAttributeValue(node.value || t.booleanLiteral(true)); if (t.isStringLiteral(value)) { value.value = value.value.replace(/\n\s+/g, " "); } if (t.isValidIdentifier(node.name.name)) { node.name.type = "Identifier"; } else { node.name = t.stringLiteral(node.name.name); } return t.inherits(t.objectProperty(node.name, value), node); }
bodyBlockPath.get("body").forEach(function(childPath) { let node = childPath.node; if (t.isExpressionStatement(node) && t.isStringLiteral(node.expression)) { // Babylon represents directives like "use strict" as elements // of a bodyBlockPath.node.directives array, but they could just // as easily be represented (by other parsers) as traditional // string-literal-valued expression statements, so we need to // handle that here. (#248) outerBody.push(node); } else if (node && node._blockHoist != null) { outerBody.push(node); } else { innerBody.push(node); } });
function getArgumentName(arg) { if (t.isThisExpression(arg)) { return 'this'; } else if (t.isNullLiteral(arg)) { return 'null'; } else if (t.isStringLiteral(arg) || t.isNumericLiteral(arg)) { return arg.value; } else if (t.isIdentifier(arg)) { return arg.name; } else { return babel_generator_1.default(arg).code; } throw new Error(`bind 不支持传入该参数: ${arg}`); }
function findMethodName(expression) { let methodName; if (t.isIdentifier(expression) || t.isJSXIdentifier(expression)) { methodName = expression.name; } else if (t.isStringLiteral(expression)) { methodName = expression.value; } else if (t.isMemberExpression(expression) && t.isIdentifier(expression.property)) { const { code } = babel_generator_1.default(expression); const ids = code.split('.'); if (ids[0] === 'this' && ids[1] === 'props' && ids[2]) { methodName = code.replace('this.props.', ''); } else { methodName = expression.property.name; } } else if (t.isCallExpression(expression) && t.isMemberExpression(expression.callee) && t.isIdentifier(expression.callee.object)) { methodName = expression.callee.object.name; } else if (t.isCallExpression(expression) && t.isMemberExpression(expression.callee) && t.isMemberExpression(expression.callee.object) && t.isIdentifier(expression.callee.property) && expression.callee.property.name === 'bind' && t.isIdentifier(expression.callee.object.property)) { methodName = expression.callee.object.property.name; } else { throw codeFrameError(expression.loc, '当 props 为事件时(props name 以 `on` 开头),只能传入一个 this 作用域下的函数。'); } return methodName; }
const types = require('babel-types'); const matchesPattern = require('./matches-pattern'); module.exports = { MemberExpression(node, asset) { // Inline environment variables accessed on process.env if (matchesPattern(node.object, 'process.env')) { let key = types.toComputedKey(node); if (types.isStringLiteral(key)) { let val = types.valueToNode(process.env[key.value]); morph(node, val); asset.isAstDirty = true; asset.cacheData.env[key.value] = process.env[key.value]; } } } }; // replace object properties function morph(object, newProperties) { for (let key in object) { delete object[key]; } for (let key in newProperties) { object[key] = newProperties[key]; } }
function parseAst (ast, filePath, files, isProduction, npmConfig, buildAdapter = BUILD_TYPES.WEAPP) { const excludeRequire = [] traverse(ast, { IfStatement (astPath) { astPath.traverse({ BinaryExpression (astPath) { const node = astPath.node const left = node.left if (generate(left).code === 'process.env.TARO_ENV' && node.right.value !== buildAdapter) { const consequentSibling = astPath.getSibling('consequent') consequentSibling.traverse({ CallExpression (astPath) { if (astPath.get('callee').isIdentifier({ name: 'require'})) { const arg = astPath.get('arguments')[0] if (t.isStringLiteral(arg.node)) { excludeRequire.push(arg.node.value) } } } }) } } }) }, Program: { exit (astPath) { astPath.traverse({ CallExpression (astPath) { const node = astPath.node const callee = node.callee if (callee.name === 'require') { const args = node.arguments let requirePath = args[0].value if (excludeRequire.indexOf(requirePath) < 0) { if (isNpmPkg(requirePath)) { if (excludeNpmPkgs.indexOf(requirePath) < 0) { const res = resolveNpmFilesPath(requirePath, isProduction, npmConfig, buildAdapter, path.dirname(recursiveFindNodeModules(filePath))) let relativeRequirePath = promoteRelativePath(path.relative(filePath, res.main)) relativeRequirePath = relativeRequirePath.replace(/node_modules/g, npmConfig.name) args[0].value = relativeRequirePath } } else { let realRequirePath = path.resolve(path.dirname(filePath), requirePath) let tempPathWithJS = `${realRequirePath}.js` let tempPathWithIndexJS = `${realRequirePath}${path.sep}index.js` if (fs.existsSync(tempPathWithJS)) { realRequirePath = tempPathWithJS requirePath += '.js' } else if (fs.existsSync(tempPathWithIndexJS)) { realRequirePath = tempPathWithIndexJS requirePath += '/index.js' } if (files.indexOf(realRequirePath) < 0) { files.push(realRequirePath) recursiveRequire(realRequirePath, files, isProduction, npmConfig, buildAdapter) } args[0].value = requirePath } } } } }) } } }) return generate(ast).code }
return __awaiter(this, void 0, void 0, function* () { const warnings = []; const elements = document.getFeatures({ kind: 'polymer-element' }); if (elements.size === 0) { return warnings; } for (const el of elements) { if (el.tagName === undefined) { continue; } const containingDoc = util_1.getDocumentContaining(el.sourceRange, document); if (containingDoc === undefined) { continue; } const validationResult = validate(el.tagName); if (validationResult.isValid && validationResult.message === undefined) { continue; // Valid element } if (el.astNode === undefined || el.astNode.language !== 'js') { continue; } const isP2 = babel.isClassDeclaration(el.astNode.node); let sourceRange = el.sourceRange; babel_traverse_1.default(el.astNode.node, { noScope: true, ObjectProperty(path) { if (isP2) { return; } if (babel.isIdentifier(path.node.key) && path.node.key.name === 'is' && babel.isStringLiteral(path.node.value)) { sourceRange = containingDoc.sourceRangeForNode(path.node.value); } }, ClassMethod(path) { if (!isP2) { return; } if (babel.isIdentifier(path.node.key) && path.node.key.name === 'is' && path.node.kind === 'get' && path.node.static) { const body = path.node.body.body[0]; if (babel.isReturnStatement(body) && babel.isStringLiteral(body.argument)) { sourceRange = containingDoc.sourceRangeForNode(body.argument); } } } }); if (sourceRange === undefined) { continue; } const isError = !validationResult.isValid; warnings.push(new polymer_analyzer_1.Warning({ parsedDocument: document.parsedDocument, code: isError ? 'invalid-element-name' : 'potential-element-naming-issue', severity: isError ? polymer_analyzer_1.Severity.ERROR : polymer_analyzer_1.Severity.WARNING, sourceRange: sourceRange, message: validationResult.message })); } return warnings; });
function toString(node) { if (t.isStringLiteral(node)) return node.value; if (t.isMemberExpression) return `{{${generate(node).code}}}`; }
module.exports = function(astPath) { let expr = astPath.node.expression; let attrName = astPath.parent.name.name; let isEventRegex = buildType == 'ali' || buildType == 'quick' ? /^(on|catch)/ : /^(bind|catch)/; let isEvent = isEventRegex.test(attrName); if (isEvent && buildType == 'quick') { let n = attrName.charAt(0) === 'o' ? 2 : 5; astPath.parent.name.name = 'on' + attrName.slice(n).toLowerCase(); } if (!isEvent) { astPath.traverse({ ThisExpression(nodePath) { if (t.isMemberExpression(nodePath.parentPath)) { nodePath.parentPath.replaceWith( t.identifier(nodePath.parent.property.name) ); } } }); } let attrValue = generate(expr).code; switch (astPath.node.expression.type) { case 'NumericLiteral': //11 case 'StringLiteral': // "string" case 'Identifier': // kkk undefined case 'NullLiteral': // null case 'BooleanLiteral': if (isEvent) { throwEventValue(attrName, attrValue); } replaceWithExpr(astPath, attrValue); break; case 'BinaryExpression': { let { left, right } = astPath.node.expression; if (t.isStringLiteral(left) || t.isStringLiteral(right)) { const attrName = astPath.parentPath.node.name.name; if (attrName === 'class' || attrName === 'className') { // 快应用的 bug // class={{this.className0 + ' dynamicClassName'}} 快应用会将后者的空格吞掉 // 影响 class 的求值 let className = buildType == 'quick' ? `${toString( astPath.node.expression.left )} ${toString(astPath.node.expression.right)}` : `${toString( astPath.node.expression.left )}${toString(astPath.node.expression.right)}`; astPath.replaceWith(t.stringLiteral(className)); return; } } replaceWithExpr(astPath, attrValue.replace(/^\s*this\./, '')); break; } case 'LogicalExpression': case 'UnaryExpression': replaceWithExpr(astPath, attrValue.replace(/^\s*this\./, '')); break; case 'MemberExpression': if (isEvent) { bindEvent( astPath, attrName, attrValue.replace(/^\s*this\./, '') ); } else { replaceWithExpr(astPath, attrValue.replace(/^\s*this\./, '')); } break; case 'CallExpression': if (isEvent) { let match = attrValue.match(/this\.(\w+)\.bind/); if (match && match[1]) { bindEvent(astPath, attrName, match[1]); } else { throwEventValue(attrName, attrValue); } } else { if ( attrName === 'style' && attrValue.indexOf('React.toStyle') === 0 ) { // style={{}} 类型解析 let start = attrValue.indexOf('\'style'); let end = attrValue.lastIndexOf(')'); let styleID = attrValue.slice(start, end); replaceWithExpr(astPath, `props[${styleID}] `); } else { replaceWithExpr(astPath, attrValue); } } break; case 'ObjectExpression': if (attrName === 'style') { let styleValue = getStyleValue(expr); replaceWithExpr(astPath, styleValue, true); } else if (isEvent) { throwEventValue(attrName, attrValue); } break; case 'ConditionalExpression': replaceWithExpr(astPath, attrValue.replace(/\s*this\./, '')); break; default: break; } };
ast.program.body = ast.program.body.filter(item => { if (t.isVariableDeclaration(item)) { for (let idx = -1, len = item.declarations.length; ++idx < len; ) { const dec = item.declarations[idx]; if ( // var ... !t.isVariableDeclarator(dec) || // var {...} !t.isObjectPattern(dec.id) || // var {x} = require(...) !t.isCallExpression(dec.init) || !t.isIdentifier(dec.init.callee) || dec.init.callee.name !== 'require' || // var {x} = require('one-thing') dec.init.arguments.length !== 1 || !t.isStringLiteral(dec.init.arguments[0]) ) { continue; } // var {x} = require('jsxstyle') if (!JSXSTYLE_SOURCES.hasOwnProperty(dec.init.arguments[0].value)) { continue; } if (jsxstyleSrc) { invariant( jsxstyleSrc === dec.init.arguments[0].value, 'Expected duplicate `require` to be from "%s", received "%s"', jsxstyleSrc, dec.init.arguments[0].value ); } for (let idx = -1, len = dec.id.properties.length; ++idx < len; ) { const prop = dec.id.properties[idx]; if ( !t.isObjectProperty(prop) || !t.isIdentifier(prop.key) || !t.isIdentifier(prop.value) ) { continue; } // only add uppercase identifiers to validComponents if ( prop.key.name[0] !== prop.key.name[0].toUpperCase() || prop.value.name[0] !== prop.value.name[0].toUpperCase() ) { continue; } // map imported name to source component name validComponents[prop.value.name] = prop.key.name; hasValidComponents = true; } jsxstyleSrc = dec.init.arguments[0].value; // if this is the only variable declaration, remove it // TODO: handle weird const a = 1, b = 2; maybe if (len === 1) return false; } } else if (t.isImportDeclaration(item)) { // omfg everyone please just use import syntax // not imported from jsxstyle? byeeee if ( !t.isStringLiteral(item.source) || !JSXSTYLE_SOURCES.hasOwnProperty(item.source.value) ) { return true; } if (jsxstyleSrc) { invariant( jsxstyleSrc === item.source.value, 'Expected duplicate `import` to be from "%s", received "%s"', jsxstyleSrc, item.source.value ); } jsxstyleSrc = item.source.value; useImportSyntax = true; for (let idx = -1, len = item.specifiers.length; ++idx < len; ) { const specifier = item.specifiers[idx]; if ( !t.isImportSpecifier(specifier) || !t.isIdentifier(specifier.imported) || !t.isIdentifier(specifier.local) ) { continue; } if ( specifier.imported.name[0] !== specifier.imported.name[0].toUpperCase() || specifier.local.name[0] !== specifier.local.name[0].toUpperCase() ) { continue; } validComponents[specifier.local.name] = specifier.imported.name; hasValidComponents = true; } // remove import return false; } return true; });
node.attributes = node.attributes.filter((attribute, idx) => { if ( // keep the weirdos !attribute.name || !attribute.name.name || // haven't hit the last spread operator idx < lastSpreadIndex ) { inlinePropCount++; return true; } const name = attribute.name.name; const value = t.isJSXExpressionContainer(attribute.value) ? attribute.value.expression : attribute.value; // if one or more spread operators are present and we haven't hit the last one yet, the prop stays inline if (lastSpreadIndex !== null && idx <= lastSpreadIndex) { inlinePropCount++; return true; } // pass ref, key, and style props through untouched if (UNTOUCHED_PROPS.hasOwnProperty(name)) { return true; } // className prop will be handled below if (name === classPropName) { return true; } // validate component prop if (name === 'component') { if (t.isLiteral(value) && typeof value.value === 'string') { const char1 = value.value[0]; // component="article" if (char1 === char1.toUpperCase()) { // an uppercase string with be turned into a component, which is not what we want. // TODO: look into transforming to React.createElement. // main downside is that further transformations that rely on JSX won't work. inlinePropCount++; } } else if (t.isIdentifier(value)) { const char1 = value.name[0]; // component={Avatar} if (char1 === char1.toLowerCase()) { // a lowercase identifier will be transformed to a DOM element. that's not good. inlinePropCount++; } } else if (t.isMemberExpression(value)) { // component={variable.prop} } else { // TODO: extract more complex expressions out as separate var errorCallback( '`component` prop value was not handled by extractStyles: %s', generate(value).code ); inlinePropCount++; } return true; } // pass key and style props through untouched if (UNTOUCHED_PROPS.hasOwnProperty(name)) { return true; } if (name === 'props') { if (!value) { errorCallback('`props` prop does not have a value'); inlinePropCount++; return true; } if (t.isObjectExpression(value)) { let errorCount = 0; const attributes = []; for (const k in value.properties) { const propObj = value.properties[k]; if (t.isObjectProperty(propObj)) { let key; if (t.isIdentifier(propObj.key)) { key = propObj.key.name; } else if (t.isStringLiteral(propObj.key)) { // starts with a-z or _ followed by a-z, -, or _ if (/^\w[\w-]+$/.test(propObj.key.value)) { key = propObj.key.value; } else { errorCallback( '`props` prop contains an invalid key: `%s`', propObj.key.value ); errorCount++; continue; } } else { errorCallback( 'unhandled object property key type: `%s`', propObj.type ); errorCount++; } if (ALL_SPECIAL_PROPS.hasOwnProperty(key)) { errorCallback( '`props` prop cannot contain `%s` as it is used by jsxstyle and will be overwritten.', key ); errorCount++; continue; } if (t.isStringLiteral(propObj.value)) { // convert literal value back to literal to ensure it has double quotes (siiiigh) attributes.push( t.jSXAttribute( t.jSXIdentifier(key), t.stringLiteral(propObj.value.value) ) ); } else { // wrap everything else in a JSXExpressionContainer attributes.push( t.jSXAttribute( t.jSXIdentifier(key), t.jSXExpressionContainer(propObj.value) ) ); } } else if (t.isSpreadProperty(propObj)) { attributes.push(t.jSXSpreadAttribute(propObj.argument)); } else { errorCallback( 'unhandled object property value type: `%s`', propObj.type ); errorCount++; } } if (errorCount > 0) { inlinePropCount++; } else { propsAttributes = attributes; } return true; } if ( // if it's not an object, spread it // props={wow()} t.isCallExpression(value) || // props={wow.cool} t.isMemberExpression(value) || // props={wow} t.isIdentifier(value) ) { propsAttributes = [t.jSXSpreadAttribute(value)]; return true; } // if props prop is weird-looking, leave it and warn errorCallback('props prop is an unhandled type: `%s`', value.type); inlinePropCount++; return true; } if (name === 'mediaQueries') { if (canEvaluateObject(staticNamespace, value)) { staticAttributes[name] = vm.runInContext( // parens so V8 doesn't parse the object like a block '(' + generate(value).code + ')', evalContext ); } else if (canEvaluate(staticNamespace, value)) { staticAttributes[name] = vm.runInContext( generate(value).code, evalContext ); } else { inlinePropCount++; return true; } return false; } // if value can be evaluated, extract it and filter it out if (canEvaluate(staticNamespace, value)) { staticAttributes[name] = vm.runInContext( generate(value).code, evalContext ); return false; } if (t.isConditionalExpression(value)) { // if both sides of the ternary can be evaluated, extract them if ( canEvaluate(staticNamespace, value.consequent) && canEvaluate(staticNamespace, value.alternate) ) { staticTernaries.push({ name, ternary: value }); // mark the prop as extracted staticAttributes[name] = null; return false; } } else if (t.isLogicalExpression(value)) { // convert a simple logical expression to a ternary with a null alternate if ( value.operator === '&&' && canEvaluate(staticNamespace, value.right) ) { staticTernaries.push({ name, ternary: { test: value.left, consequent: value.right, alternate: t.nullLiteral(), }, }); staticAttributes[name] = null; return false; } } if (removeAllTrace) { errorCallback( 'jsxstyle-loader encountered a dynamic prop (`%s`) on a lite ' + 'jsxstyle component (`%s`). If you would like to pass dynamic ' + 'styles to this component, specify them in the `style` prop.', generate(attribute).code, originalNodeName ); return false; } // if we've made it this far, the prop stays inline inlinePropCount++; return true; });
return; } let {left, right} = path.node; if (t.isIdentifier(left) && left.name === 'exports') { path.get('left').replaceWith(getExportsIdentifier(asset)); asset.cacheData.isCommonJS = true; } // If we can statically evaluate the name of a CommonJS export, create an ES6-style export for it. // This allows us to remove the CommonJS export object completely in many cases. if ( t.isMemberExpression(left) && t.isIdentifier(left.object, {name: 'exports'}) && ((t.isIdentifier(left.property) && !left.computed) || t.isStringLiteral(left.property)) ) { let name = t.isIdentifier(left.property) ? left.property.name : left.property.value; let identifier = getExportIdentifier(asset, name); // Replace the CommonJS assignment with a reference to the ES6 identifier. path.get('left.object').replaceWith(getExportsIdentifier(asset)); path.get('right').replaceWith(identifier); // If this is the first assignment, create a binding for the ES6-style export identifier. // Otherwise, assign to the existing export binding. let scope = path.scope.getProgramParent(); if (!scope.hasBinding(identifier.name)) { asset.cacheData.exports[name] = identifier.name;
/* * Finds the normal extend name, overridden methods and possibly java proxy name from passed node. * Writes to "customExtendsArr" or "normalExtendsArr". * Left whole for readability. */ function traverseEs5Extend(path, config) { var callee = path.parent.callee; if (callee) { var o = callee.object extendClass = _getWholeName(o); var extendArguments = path.parent.arguments; var arg0, arg1; if (extendArguments.length === 1 && t.isObjectExpression(arg0)) { arg0 = extendArguments[0]; } else if (t.isStringLiteral(arg0)) { } var arg0 = "", arg1; if (extendArguments.length) { // Get implementation object when there is only 1 argument if (extendArguments.length === 1 && t.isObjectExpression(extendArguments[0])) { arg1 = extendArguments[0]; } // Get the name of the extended class and the implementation object when both arguments are present else if (extendArguments.length === 2) { if (t.isStringLiteral(extendArguments[0]) && t.isObjectExpression(extendArguments[1])) { arg0 = extendArguments[0]; arg1 = extendArguments[1]; } } else { // don't throw here, because there can be a valid js extend that has nothing to do with NS return; throw { message: "Not enough or too many arguments passed(" + extendArguments.length + ") when trying to extend class in file: " + config.filePath, errCode: 1 } } } else { // don't throw here, because there can be a valid js extend that has nothing to do with NS return; throw { message: "You need to call the extend with parameters. Example: '...extend(\"a.b.C\", {...overrides...})') in file: " + config.filePath, errCode: 1 } } className = arg0.value ? arg0.value : ""; // Get all methods from the implementation object var methodsAndInterfaces = _getOverridenMethodsAndImplementedInterfaces(arg1, config); var overriddenMethodNames = methodsAndInterfaces[0]; var implementedInterfaces = methodsAndInterfaces[1]; var isCorrectExtendClassName = _testJavaProxyName(className); var isCorrectClassName = _testClassName(className); if (className && !isCorrectClassName && !isCorrectExtendClassName) { throw { message: "The 'extend' you are trying to make has an invalid name. Example: '...extend(\"a.b.C\", {...overrides...})'), file: " + config.filePath, errCode: 1 } } var lineToWrite = ""; if (isCorrectExtendClassName) { if (config.logger) { config.logger.info(lineToWrite) } var classNameFromDecorator = isCorrectExtendClassName ? className : ""; lineToWrite = _generateLineToWrite(classNameFromDecorator, extendClass.reverse().join("."), overriddenMethodNames, "", config.fullPathName, implementedInterfaces); addCustomExtend(classNameFromDecorator, config.fullPathName, lineToWrite); return; } if (config.logger) { config.logger.info(lineToWrite) } var extendInfo = FILE_SEPARATOR + config.filePath + LINE_SEPARATOR + path.node.property.loc.start.line + COLUMN_SEPARATOR + (path.node.property.loc.start.column + columnOffset) + DECLARED_CLASS_SEPARATOR + className; lineToWrite = _generateLineToWrite(isCorrectExtendClassName ? className : "", extendClass.reverse().join("."), overriddenMethodNames, extendInfo, "", implementedInterfaces); normalExtendsArr.push(lineToWrite) } else { // don't throw here, because there can be a valid js extend that has nothing to do with NS return; throw { message: "You need to call the extend '...extend(\"extend_name\", {...overrides...})'), file: " + config.filePath, errCode: 1 } } }
function transform(options) { if (options.adapter) { adapter_1.setAdapter(options.adapter); } if (adapter_1.Adapter.type === "swan" /* swan */) { constant_1.setLoopOriginal('privateOriginal'); } constant_1.THIRD_PARTY_COMPONENTS.clear(); const code = options.isTyped ? ts.transpile(options.code, { jsx: ts.JsxEmit.Preserve, target: ts.ScriptTarget.ESNext, importHelpers: true, noEmitHelpers: true }) : options.code; options.env = Object.assign({ 'process.env.TARO_ENV': options.adapter || 'weapp' }, options.env || {}); options_1.setTransformOptions(options); utils_1.setting.sourceCode = code; // babel-traverse 无法生成 Hub // 导致 Path#getSource|buildCodeFrameError 都无法直接使用 // 原因大概是 babylon.parse 没有生成 File 实例导致 scope 和 path 原型上都没有 `file` // 将来升级到 babel@7 可以直接用 parse 而不是 transform const ast = babel_core_1.transform(code, options_1.buildBabelTransformOptions()).ast; if (options.isNormal) { return { ast }; } // transformFromAst(ast, code) let result; const componentSourceMap = new Map(); const imageSource = new Set(); const importSources = new Set(); let componentProperies = []; let mainClass; let storeName; let renderMethod; let isImportTaro = false; babel_traverse_1.default(ast, { TemplateLiteral(path) { const nodes = []; const { quasis, expressions } = path.node; let index = 0; if (path.parentPath.isTaggedTemplateExpression()) { return; } for (const elem of quasis) { if (elem.value.cooked) { nodes.push(t.stringLiteral(elem.value.cooked)); } if (index < expressions.length) { const expr = expressions[index++]; if (!t.isStringLiteral(expr, { value: '' })) { nodes.push(expr); } } } // + 号连接符必须保证第一和第二个 node 都是字符串 if (!t.isStringLiteral(nodes[0]) && !t.isStringLiteral(nodes[1])) { nodes.unshift(t.stringLiteral('')); } let root = nodes[0]; for (let i = 1; i < nodes.length; i++) { root = t.binaryExpression('+', root, nodes[i]); } path.replaceWith(root); }, ClassDeclaration(path) { mainClass = path; const superClass = utils_1.getSuperClassCode(path); if (superClass) { try { componentProperies = transform({ isRoot: false, isApp: false, code: superClass.code, isTyped: true, sourcePath: superClass.sourcePath, outputPath: superClass.sourcePath }).componentProperies; } catch (error) { // } } }, ClassExpression(path) { mainClass = path; }, ClassMethod(path) { if (t.isIdentifier(path.node.key) && path.node.key.name === 'render') { renderMethod = path; } }, IfStatement(path) { const consequent = path.get('consequent'); if (!consequent.isBlockStatement()) { consequent.replaceWith(t.blockStatement([ consequent.node ])); } }, CallExpression(path) { const callee = path.get('callee'); if (utils_1.isContainJSXElement(path)) { return; } if (callee.isReferencedMemberExpression()) { const id = utils_1.findFirstIdentifierFromMemberExpression(callee.node); const property = callee.node.property; if (t.isIdentifier(property) && property.name.startsWith('on')) { const funcExpr = path.findParent(p => p.isFunctionExpression()); if (funcExpr && funcExpr.isFunctionExpression()) { const taroAPI = funcExpr.findParent(p => p.isCallExpression() && t.isMemberExpression(p.node.callee) && t.isIdentifier(p.node.callee.object, { name: 'Taro' })); if (taroAPI && taroAPI.isCallExpression()) { throw utils_1.codeFrameError(funcExpr.node, '在回调函数使用从 props 传递的函数时,请把回调函数改造为箭头函数并一直使用 `this` 取值'); } } } const calleeIds = getIdsFromMemberProps(callee.node); if (t.isIdentifier(id) && id.name.startsWith('on') && "alipay" /* alipay */ !== adapter_1.Adapter.type) { const fullPath = buildFullPathThisPropsRef(id, calleeIds, path); if (fullPath) { path.replaceWith(t.callExpression(fullPath, path.node.arguments)); } } } if (callee.isReferencedIdentifier()) { const id = callee.node; const ids = [id.name]; if (t.isIdentifier(id) && id.name.startsWith('on')) { const funcExpr = path.findParent(p => p.isFunctionExpression()); if (funcExpr && funcExpr.isFunctionExpression()) { const taroAPI = funcExpr.findParent(p => p.isCallExpression() && t.isMemberExpression(p.node.callee) && t.isIdentifier(p.node.callee.object, { name: 'Taro' })); if (taroAPI && taroAPI.isCallExpression()) { throw utils_1.codeFrameError(funcExpr.node, '在回调函数使用从 props 传递的函数时,请把回调函数改造为箭头函数并一直使用 `this` 取值'); } } const fullPath = buildFullPathThisPropsRef(id, ids, path); if (fullPath) { path.replaceWith(t.callExpression(fullPath, path.node.arguments)); } } } }, // JSXIdentifier (path) { // const parentPath = path.parentPath // if (!parentPath.isJSXAttribute()) { // return // } // const element = parentPath.parentPath // if (!element.isJSXOpeningElement()) { // return // } // const elementName = element.get('name') // if (!elementName.isJSXIdentifier()) { // return // } // if (DEFAULT_Component_SET.has(elementName.node.name)) { // return // } // const expr = parentPath.get('value.expression') // }, JSXElement(path) { const assignment = path.findParent(p => p.isAssignmentExpression()); if (assignment && assignment.isAssignmentExpression() && !options.isTyped) { const left = assignment.node.left; if (t.isIdentifier(left)) { const binding = assignment.scope.getBinding(left.name); if (binding && binding.scope === assignment.scope) { if (binding.path.isVariableDeclarator()) { binding.path.node.init = path.node; assignment.remove(); } else { throw utils_1.codeFrameError(path.node, '同一个作用域的JSX 变量延时赋值没有意义。详见:https://github.com/NervJS/taro/issues/550'); } } } } const switchStatement = path.findParent(p => p.isSwitchStatement()); if (switchStatement && switchStatement.isSwitchStatement()) { const { discriminant, cases } = switchStatement.node; const ifStatement = cases.map((Case, index) => { const [consequent] = Case.consequent; if (!t.isBlockStatement(consequent)) { throw utils_1.codeFrameError(switchStatement.node, '含有 JSX 的 switch case 语句必须每种情况都用花括号 `{}` 包裹结果'); } const block = t.blockStatement(consequent.body.filter(b => !t.isBreakStatement(b))); if (index !== cases.length - 1 && t.isNullLiteral(Case.test)) { throw utils_1.codeFrameError(Case, '含有 JSX 的 switch case 语句只有最后一个 case 才能是 default'); } const test = Case.test === null ? t.nullLiteral() : t.binaryExpression('===', discriminant, Case.test); return { block, test }; }).reduceRight((ifStatement, item) => { if (t.isNullLiteral(item.test)) { ifStatement.alternate = item.block; return ifStatement; } const newStatement = t.ifStatement(item.test, item.block, t.isBooleanLiteral(ifStatement.test, { value: false }) ? ifStatement.alternate : ifStatement); return newStatement; }, t.ifStatement(t.booleanLiteral(false), t.blockStatement([]))); switchStatement.insertAfter(ifStatement); switchStatement.remove(); } const isForStatement = (p) => p && (p.isForStatement() || p.isForInStatement() || p.isForOfStatement()); const forStatement = path.findParent(isForStatement); if (isForStatement(forStatement)) { throw utils_1.codeFrameError(forStatement.node, '不行使用 for 循环操作 JSX 元素,详情:https://github.com/NervJS/taro/blob/master/packages/eslint-plugin-taro/docs/manipulate-jsx-as-array.md'); } const loopCallExpr = path.findParent(p => utils_1.isArrayMapCallExpression(p)); if (loopCallExpr && loopCallExpr.isCallExpression()) { const [func] = loopCallExpr.node.arguments; if (t.isArrowFunctionExpression(func) && !t.isBlockStatement(func.body)) { func.body = t.blockStatement([ t.returnStatement(func.body) ]); } } }, JSXOpeningElement(path) { const { name } = path.node.name; const binding = path.scope.getBinding(name); if (process.env.NODE_ENV !== 'test' && constant_1.DEFAULT_Component_SET.has(name) && binding && binding.kind === 'module') { const bindingPath = binding.path; if (bindingPath.parentPath.isImportDeclaration()) { const source = bindingPath.parentPath.node.source; if (source.value !== constant_1.COMPONENTS_PACKAGE_NAME) { throw utils_1.codeFrameError(bindingPath.parentPath.node, `内置组件名: '${name}' 只能从 ${constant_1.COMPONENTS_PACKAGE_NAME} 引入。`); } } } if (name === 'Provider') { const modules = path.scope.getAllBindings('module'); const providerBinding = Object.values(modules).some((m) => m.identifier.name === 'Provider'); if (providerBinding) { path.node.name = t.jSXIdentifier('view'); const store = path.node.attributes.find(attr => attr.name.name === 'store'); if (store && t.isJSXExpressionContainer(store.value) && t.isIdentifier(store.value.expression)) { storeName = store.value.expression.name; } path.node.attributes = []; } } if (constant_1.IMAGE_COMPONENTS.has(name)) { for (const attr of path.node.attributes) { if (attr.name.name === 'src') { if (t.isStringLiteral(attr.value)) { imageSource.add(attr.value.value); } else if (t.isJSXExpressionContainer(attr.value)) { if (t.isStringLiteral(attr.value.expression)) { imageSource.add(attr.value.expression.value); } } } } } }, JSXAttribute(path) { const { name, value } = path.node; if (options.jsxAttributeNameReplace) { for (const r in options.jsxAttributeNameReplace) { if (options.jsxAttributeNameReplace.hasOwnProperty(r)) { const element = options.jsxAttributeNameReplace[r]; if (t.isJSXIdentifier(name, { name: r })) { path.node.name = t.jSXIdentifier(element); } } } } if (!t.isJSXIdentifier(name) || value === null || t.isStringLiteral(value) || t.isJSXElement(value)) { return; } const expr = value.expression; const exprPath = path.get('value.expression'); const classDecl = path.findParent(p => p.isClassDeclaration()); const classDeclName = classDecl && classDecl.isClassDeclaration() && lodash_1.get(classDecl, 'node.id.name', ''); let isConverted = false; if (classDeclName) { isConverted = classDeclName === '_C' || classDeclName.endsWith('Tmpl'); } if (!t.isBinaryExpression(expr, { operator: '+' }) && !t.isLiteral(expr) && name.name === 'style' && !isConverted) { const jsxID = path.findParent(p => p.isJSXOpeningElement()).get('name'); if (jsxID && jsxID.isJSXIdentifier() && constant_1.DEFAULT_Component_SET.has(jsxID.node.name)) { exprPath.replaceWith(t.callExpression(t.identifier(constant_1.INTERNAL_INLINE_STYLE), [expr])); } } if (name.name.startsWith('on')) { if (exprPath.isReferencedIdentifier()) { const ids = [expr.name]; const fullPath = buildFullPathThisPropsRef(expr, ids, path); if (fullPath) { exprPath.replaceWith(fullPath); } } if (exprPath.isReferencedMemberExpression()) { const id = utils_1.findFirstIdentifierFromMemberExpression(expr); const ids = getIdsFromMemberProps(expr); if (t.isIdentifier(id)) { const fullPath = buildFullPathThisPropsRef(id, ids, path); if (fullPath) { exprPath.replaceWith(fullPath); } } } // @TODO: bind 的处理待定 } }, ImportDeclaration(path) { const source = path.node.source.value; if (importSources.has(source)) { throw utils_1.codeFrameError(path.node, '无法在同一文件重复 import 相同的包。'); } else { importSources.add(source); } const names = []; if (source === constant_1.TARO_PACKAGE_NAME) { isImportTaro = true; path.node.specifiers.push(t.importSpecifier(t.identifier(constant_1.INTERNAL_SAFE_GET), t.identifier(constant_1.INTERNAL_SAFE_GET)), t.importSpecifier(t.identifier(constant_1.INTERNAL_GET_ORIGNAL), t.identifier(constant_1.INTERNAL_GET_ORIGNAL)), t.importSpecifier(t.identifier(constant_1.INTERNAL_INLINE_STYLE), t.identifier(constant_1.INTERNAL_INLINE_STYLE)), t.importSpecifier(t.identifier(constant_1.GEL_ELEMENT_BY_ID), t.identifier(constant_1.GEL_ELEMENT_BY_ID))); } if (source === constant_1.REDUX_PACKAGE_NAME || source === constant_1.MOBX_PACKAGE_NAME) { path.node.specifiers.forEach((s, index, specs) => { if (s.local.name === 'Provider') { specs.splice(index, 1); specs.push(t.importSpecifier(t.identifier('setStore'), t.identifier('setStore'))); } }); } path.traverse({ ImportDefaultSpecifier(path) { const name = path.node.local.name; names.push(name); }, ImportSpecifier(path) { const name = path.node.imported.name; names.push(name); if (source === constant_1.TARO_PACKAGE_NAME && name === 'Component') { path.node.local = t.identifier('__BaseComponent'); } } }); componentSourceMap.set(source, names); } }); if (!isImportTaro) { ast.program.body.unshift(t.importDeclaration([ t.importDefaultSpecifier(t.identifier('Taro')), t.importSpecifier(t.identifier(constant_1.INTERNAL_SAFE_GET), t.identifier(constant_1.INTERNAL_SAFE_GET)), t.importSpecifier(t.identifier(constant_1.INTERNAL_GET_ORIGNAL), t.identifier(constant_1.INTERNAL_GET_ORIGNAL)), t.importSpecifier(t.identifier(constant_1.INTERNAL_INLINE_STYLE), t.identifier(constant_1.INTERNAL_INLINE_STYLE)) ], t.stringLiteral('@tarojs/taro'))); } if (!mainClass) { throw new Error('未找到 Taro.Component 的类定义'); } mainClass.node.body.body.forEach(handleThirdPartyComponent); const storeBinding = mainClass.scope.getBinding(storeName); mainClass.scope.rename('Component', '__BaseComponent'); if (storeBinding) { const statementPath = storeBinding.path.getStatementParent(); if (statementPath) { ast.program.body.forEach((node, index, body) => { if (node === statementPath.node) { body.splice(index + 1, 0, t.expressionStatement(t.callExpression(t.identifier('setStore'), [ t.identifier(storeName) ]))); } }); } } resetTSClassProperty(mainClass.node.body.body); if (options.isApp) { renderMethod.replaceWith(t.classMethod('method', t.identifier('_createData'), [], t.blockStatement([]))); return { ast }; } result = new class_1.Transformer(mainClass, options.sourcePath, componentProperies).result; result.code = babel_generator_1.default(ast).code; result.ast = ast; const lessThanSignReg = new RegExp(constant_1.lessThanSignPlacehold, 'g'); result.compressedTemplate = result.template; result.template = html_1.prettyPrint(result.template, { max_char: 0, unformatted: process.env.NODE_ENV === 'test' ? [] : ['text'] }); result.template = result.template.replace(lessThanSignReg, '<'); result.imageSrcs = Array.from(imageSource); return result; }
module.exports = function () { return { visitor: { ExpressionStatement(path) { // only lists of assignments need compaction if (!path.inList) { return; } let first = path.get('expression'); // compaction is started only upon a specific pug buffer assignment if (!isPugAssignment(first)) { return; } // the first assignment in a list was found. // compact siblings (max N) let N = 100; let fragment = [t.identifier('pug_html')]; let litSerie = false let litValue = ''; let bufferNode = first.get('right.right').node; if (t.isStringLiteral(bufferNode)) { litSerie = true; litValue = bufferNode.value; } else { fragment.push(bufferNode); } let sibling = path.getSibling(path.key + 1); let count = 0; let expr; while (--N>0 &&t.isExpressionStatement(sibling) && isPugAssignment(expr = sibling.get('expression'))) { count++; bufferNode = expr.get('right.right').node; if (t.isStringLiteral(bufferNode)) { litSerie = true; litValue += bufferNode.value; } else { if (litSerie) { const lit = t.stringLiteral(litValue); lit.extra = { rawValue: litValue, raw: stringify(litValue)} fragment.push(lit); litSerie = false; litValue = ''; } fragment.push(bufferNode) } sibling.remove(); sibling = path.getSibling(path.key + 1); } if (count == 0) { return; } if (litSerie) { const lit = t.stringLiteral(litValue); lit.extra = { rawValue: litValue, raw: stringify(litValue)} fragment.push(lit); } let op = fragment.reduce(function(acc, val) { return t.binaryExpression('+', acc, val); }); first.get('right').replaceWith(op) } } }; }
var isLiteral = (n) => t.isNumericLiteral(n) || t.isBooleanLiteral(n) || t.isNullLiteral(n) || t.isStringLiteral(n) || t.isRegExpLiteral(n);
attributesTrans = attributes.reduce((obj, attr) => { if (t.isJSXSpreadAttribute(attr)) { throw utils_1.codeFrameError(attr.loc, 'JSX 参数暂不支持 ...spread 表达式'); } let name = attr.name.name; if (constant_1.DEFAULT_Component_SET.has(componentName)) { if (name === 'className') { name = 'class'; } } let value = true; let attrValue = attr.value; if (typeof name === 'string') { const isAlipayEvent = adapter_1.Adapter.type === "alipay" /* alipay */ && /(^on[A-Z_])|(^catch[A-Z_])/.test(name); if (t.isStringLiteral(attrValue)) { value = attrValue.value; } else if (t.isJSXExpressionContainer(attrValue)) { let isBindEvent = (name.startsWith('bind') && name !== 'bind') || (name.startsWith('catch') && name !== 'catch'); let code = utils_1.decodeUnicode(babel_generator_1.default(attrValue.expression, { quotes: 'single', concise: true }).code) .replace(/"/g, "'") .replace(/(this\.props\.)|(this\.data\.)/g, '') .replace(/this\./g, ''); if ("swan" /* swan */ === adapter_1.Adapter.type && code !== 'true' && code !== 'false' && constant_1.swanSpecialAttrs[componentName] && constant_1.swanSpecialAttrs[componentName].includes(name)) { value = `{= ${code} =}`; } else { if (adapter_1.Adapter.key === name) { const splitCode = code.split('.'); if (splitCode.length > 1) { value = splitCode.slice(1).join('.'); } else { value = code; } } else { value = isBindEvent || isAlipayEvent ? code : `{{${code}}}`; } } if (adapter_1.Adapter.type === "swan" /* swan */ && name === adapter_1.Adapter.for) { value = code; } if (t.isStringLiteral(attrValue.expression)) { value = attrValue.expression.value; } } else if (attrValue === null && name !== adapter_1.Adapter.else) { value = `{{true}}`; } if (constant_1.THIRD_PARTY_COMPONENTS.has(componentName) && /^bind/.test(name) && name.includes('-')) { name = name.replace(/^bind/, 'bind:'); } if (componentTransfromProps && componentTransfromProps[componentName]) { const transfromProps = componentTransfromProps[componentName]; Object.keys(transfromProps).forEach(oriName => { if (transfromProps.hasOwnProperty(name)) { name = transfromProps[oriName]; } }); } if ((componentName === 'Input' || componentName === 'input') && name === 'maxLength') { obj['maxlength'] = value; } else if (componentSpecialProps && componentSpecialProps.has(name) || name.startsWith('__fn_') || isAlipayEvent) { obj[name] = value; } else { obj[isDefaultComponent && !name.includes('-') && !name.includes(':') ? lodash_1.kebabCase(name) : name] = value; } } if (!isDefaultComponent && !specialComponentName.includes(componentName)) { //obj[TRIGGER_OBSERER] = '{{ _triggerObserer }}'; } return obj; }, {});
function processEntry (code, filePath) { let ast = wxTransformer({ code, sourcePath: filePath, isNormal: true, isTyped: Util.REG_TYPESCRIPT.test(filePath), adapter: 'h5' }).ast let taroImportDefaultName let providorImportName let storeName let renderCallCode let hasAddNervJsImportDefaultName = false let hasConstructor = false let hasComponentWillMount = false let hasComponentDidMount = false let hasComponentDidShow = false let hasComponentDidHide = false let hasComponentWillUnmount = false let hasJSX = false let hasState = false const initPxTransformNode = toAst(`Taro.initPxTransform(${JSON.stringify(pxTransformConfig)})`) const additionalConstructorNode = toAst(`Taro._set$app(this)`) ast = babel.transformFromAst(ast, '', { plugins: [ [require('babel-plugin-danger-remove-unused-import'), { ignore: ['@tarojs/taro', 'react', 'nervjs'] }] ] }).ast const ClassDeclarationOrExpression = { enter (astPath) { const node = astPath.node if (!node.superClass) return if ( node.superClass.type === 'MemberExpression' && node.superClass.object.name === taroImportDefaultName ) { node.superClass.object.name = taroImportDefaultName if (node.id === null) { const renameComponentClassName = '_TaroComponentClass' astPath.replaceWith( t.classExpression( t.identifier(renameComponentClassName), node.superClass, node.body, node.decorators || [] ) ) } //@fix } else if (node.superClass.name === 'Component' || node.superClass.name === 'WeElement') { resetTSClassProperty(node.body.body) if (node.id === null) { const renameComponentClassName = '_TaroComponentClass' astPath.replaceWith( t.classExpression( t.identifier(renameComponentClassName), node.superClass, node.body, node.decorators || [] ) ) } } } } /** * ProgramExit使用的visitor * 负责修改render函数的内容,在componentDidMount中增加componentDidShow调用,在componentWillUnmount中增加componentDidHide调用。 */ const programExitVisitor = { ClassMethod: { exit (astPath) { const node = astPath.node const key = node.key const keyName = getObjKey(key) let funcBody const isRender = keyName === 'render' const isComponentWillMount = keyName === 'componentWillMount' const isComponentDidMount = keyName === 'componentDidMount' const isComponentWillUnmount = keyName === 'componentWillUnmount' const isConstructor = keyName === 'constructor' const basename = JSON.stringify(addLeadingSlash(stripTrailingSlash(routerBasename))) if (isRender) { const routes = pages.map((v, k) => { const absPagename = addLeadingSlash(v) const relPagename = `.${absPagename}` const chunkName = relPagename.split('/').filter(v => !/^(pages|\.)$/i.test(v)).join('_') return createRoute({ absPagename, relPagename, chunkName, isIndex: k === 0 }) }) //@fix funcBody = `<o-router mode={${JSON.stringify(routerMode)}} publicPath={${JSON.stringify(routerMode === 'hash' ? '/' : publicPath)}} routes={[${routes.join(',')}]} customRoutes={${JSON.stringify(customRoutes)}} basename={${basename}} />` /* 插入Tabbar */ if (tabBar) { const homePage = pages[0] || '' if (tabbarPos === 'top') { funcBody = ` <${tabBarContainerComponentName}> <${tabBarComponentName} conf={${tabBarConfigName}} homePage="${homePage}" router={${taroImportDefaultName}}/> <${tabBarPanelComponentName}> ${funcBody} </${tabBarPanelComponentName}> </${tabBarContainerComponentName}>` } else { funcBody = ` <${tabBarContainerComponentName}> <${tabBarPanelComponentName}> ${funcBody} </${tabBarPanelComponentName}> <${tabBarComponentName} mode={${JSON.stringify(routerMode)}} conf={this.state.${tabBarConfigName}} homePage="${homePage}" router={${taroImportDefaultName}} basename={${basename}} /> </${tabBarContainerComponentName}>` } } /* 插入<Provider /> */ if (providerComponentName && storeName) { // 使用redux 或 mobx funcBody = ` <${providorImportName} store={${storeName}}> ${funcBody} </${providorImportName}>` } /* 插入<Router /> */ node.body = toAst(`{return (${funcBody});}`, { preserveComments: true }) } if (tabBar && isComponentWillMount) { const initTabBarApisCallNode = toAst(`Taro.initTabBarApis(this, Taro)`) astPath.get('body').pushContainer('body', initTabBarApisCallNode) } // if (hasConstructor && isConstructor) { // astPath.get('body').pushContainer('body', additionalConstructorNode) // } if (hasComponentDidShow && isComponentDidMount) { const componentDidShowCallNode = toAst(`this.componentDidShow()`) astPath.get('body').pushContainer('body', componentDidShowCallNode) } if (hasComponentDidHide && isComponentWillUnmount) { const componentDidHideCallNode = toAst(`this.componentDidHide()`) astPath.get('body').unshiftContainer('body', componentDidHideCallNode) } } }, ClassProperty: { exit (astPath) { const node = astPath.node const key = node.key const value = node.value if (key.name !== 'state' || !t.isObjectExpression(value)) return astPath.node.value.properties.push(t.objectProperty( t.identifier(tabBarConfigName), tabBar )) } }, ClassBody: { exit (astPath) { if (hasComponentDidShow && !hasComponentDidMount) { astPath.pushContainer('body', t.classMethod( 'method', t.identifier('componentDidMount'), [], t.blockStatement([]), false, false)) } if (hasComponentDidHide && !hasComponentWillUnmount) { astPath.pushContainer('body', t.classMethod( 'method', t.identifier('componentWillUnmount'), [], t.blockStatement([]), false, false)) } //@fix // if (!hasConstructor) { // astPath.pushContainer('body', t.classMethod( // 'method', t.identifier('constructor'), [t.identifier('props'), t.identifier('context')], // t.blockStatement([toAst('super(props, context)'), additionalConstructorNode]), false, false)) // } if (tabBar) { if (!hasComponentWillMount) { astPath.pushContainer('body', t.classMethod( 'method', t.identifier('componentWillMount'), [], t.blockStatement([]), false, false)) } if (!hasState) { astPath.unshiftContainer('body', t.classProperty( t.identifier('state'), t.objectExpression([]) )) } } } } } /** * ClassProperty使用的visitor * 负责收集config中的pages,收集tabbar的position,替换icon。 */ const classPropertyVisitor = { ObjectProperty (astPath) { const node = astPath.node const key = node.key const value = node.value const keyName = getObjKey(key) if (keyName === 'pages' && t.isArrayExpression(value)) { const subPackageParent = astPath.findParent(isUnderSubPackages) let root = '' if (subPackageParent) { /* 在subPackages属性下,说明是分包页面,需要处理root属性 */ const rootNode = astPath.parent.properties.find(v => { return getObjKey(v.key) === 'root' }) root = rootNode ? rootNode.value.value : '' } value.elements.forEach(v => { const pagePath = `${root}/${v.value}`.replace(/\/{2,}/g, '/') pages.push(pagePath.replace(/^\//, '')) }) } else if (keyName === 'tabBar' && t.isObjectExpression(value)) { // tabBar tabBar = value value.properties.forEach(node => { if (node.keyName === 'position') tabbarPos = node.value.value }) } else if ((keyName === 'iconPath' || keyName === 'selectedIconPath') && t.isStringLiteral(value)) { astPath.replaceWith( t.objectProperty(t.stringLiteral(keyName), t.callExpression(t.identifier('require'), [t.stringLiteral(`./${value.value}`)])) ) } } } traverse(ast, { ClassExpression: ClassDeclarationOrExpression, ClassDeclaration: ClassDeclarationOrExpression, ClassProperty: { enter (astPath) { const node = astPath.node const key = node.key const value = node.value const keyName = getObjKey(key) if (keyName === 'state') hasState = true if (keyName !== 'config' || !t.isObjectExpression(value)) return astPath.traverse(classPropertyVisitor) } }, ImportDeclaration: { enter (astPath) { const node = astPath.node const source = node.source const specifiers = node.specifiers let value = source.value //@fix if(value.endsWith('.css')){ appCSS = fs.readFileSync(filePath.replace('.tsx','.css').replace('.js','.css'), 'utf-8').replace(/\/\*[^*]*\*+([^/][^*]*\*+)*\//g, '') // astPath.replaceWith(t.variableDeclaration('const',[t.variableDeclarator(t.identifier(`___css`),t.stringLiteral(appCSS))])) astPath.remove() return } if (Util.isAliasPath(value, pathAlias)) { source.value = value = Util.replaceAliasPath(filePath, value, pathAlias) } if (!Util.isNpmPkg(value)) { if (value.indexOf('.') === 0) { const pathArr = value.split('/') if (pathArr.indexOf('pages') >= 0) { astPath.remove() } else if (Util.REG_SCRIPTS.test(value)) { const realPath = path.resolve(filePath, '..', value) const dirname = path.dirname(realPath) const extname = path.extname(realPath) const removeExtPath = path.join(dirname, path.basename(realPath, extname)) node.source = t.stringLiteral(Util.promoteRelativePath(path.relative(filePath, removeExtPath)).replace(/\\/g, '/')) } } return } if (value === PACKAGES['@tarojs/taro']) { let specifier = specifiers.find(item => item.type === 'ImportDefaultSpecifier') if (specifier) { hasAddNervJsImportDefaultName = true taroImportDefaultName = specifier.local.name specifier.local.name = nervJsImportDefaultName } else if (!hasAddNervJsImportDefaultName) { hasAddNervJsImportDefaultName = true //@fix // node.specifiers.unshift( // t.importDefaultSpecifier(t.identifier(nervJsImportDefaultName)) // ) } const taroApisSpecifiers = [] const deletedIdx = [] specifiers.forEach((item, index) => { if (item.imported && taroApis.indexOf(item.imported.name) >= 0) { taroApisSpecifiers.push(t.importSpecifier(t.identifier(item.local.name), t.identifier(item.imported.name))) deletedIdx.push(index) } }) _.pullAt(specifiers, deletedIdx) source.value = PACKAGES['nervjs'] if (taroApisSpecifiers.length) { astPath.insertBefore(t.importDeclaration(taroApisSpecifiers, t.stringLiteral(PACKAGES['@tarojs/taro-h5']))) } if (!specifiers.length) { astPath.remove() } } else if (value === PACKAGES['@tarojs/redux']) { const specifier = specifiers.find(item => { return t.isImportSpecifier(item) && item.imported.name === providerComponentName }) if (specifier) { providorImportName = specifier.local.name } else { providorImportName = providerComponentName specifiers.push(t.importSpecifier(t.identifier(providerComponentName), t.identifier(providerComponentName))) } source.value = PACKAGES['@tarojs/redux-h5'] } else if (value === PACKAGES['@tarojs/mobx']) { const specifier = specifiers.find(item => { return t.isImportSpecifier(item) && item.imported.name === providerComponentName }) if (specifier) { providorImportName = specifier.local.name } else { providorImportName = providerComponentName specifiers.push(t.importSpecifier(t.identifier(providerComponentName), t.identifier(providerComponentName))) } source.value = PACKAGES['@tarojs/mobx-h5'] } } }, CallExpression: { enter (astPath) { const node = astPath.node const callee = node.callee const calleeName = callee.name const parentPath = astPath.parentPath if (t.isMemberExpression(callee)) { if (callee.object.name === taroImportDefaultName && callee.property.name === 'render') { callee.object.name = nervJsImportDefaultName renderCallCode = generate(astPath.node).code astPath.remove() } } else { if (calleeName === setStoreFuncName) { if (parentPath.isAssignmentExpression() || parentPath.isExpressionStatement() || parentPath.isVariableDeclarator()) { parentPath.remove() } } } } }, ClassMethod: { exit (astPath) { const node = astPath.node const key = node.key const keyName = getObjKey(key) if (keyName === 'constructor') { hasConstructor = true } else if (keyName === 'componentWillMount') { hasComponentWillMount = true } else if (keyName === 'componentDidMount') { hasComponentDidMount = true } else if (keyName === 'componentDidShow') { hasComponentDidShow = true } else if (keyName === 'componentDidHide') { hasComponentDidHide = true } else if (keyName === 'componentWillUnmount') { hasComponentWillUnmount = true } } }, JSXElement: { enter (astPath) { hasJSX = true } }, JSXOpeningElement: { enter (astPath) { if (astPath.node.name.name === 'Provider') { for (let v of astPath.node.attributes) { if (v.name.name !== 'store') continue storeName = v.value.expression.name break } } } }, Program: { exit (astPath) { const importNervjsNode = t.importDefaultSpecifier(t.identifier(nervJsImportDefaultName)) const importRouterNode = toAst(`import '${PACKAGES['@tarojs/router']}'`) //@fix const importMpNode = toAst(`import './libs/mp'`) const importTaroH5Node = toAst(`import ${taroImportDefaultName} from '${PACKAGES['@tarojs/taro-h5']}'`) const importComponentNode = toAst(`import { View, ${tabBarComponentName}, ${tabBarContainerComponentName}, ${tabBarPanelComponentName}} from '${PACKAGES['@tarojs/components']}'`) const lastImportIndex = _.findLastIndex(astPath.node.body, t.isImportDeclaration) const lastImportNode = astPath.get(`body.${lastImportIndex > -1 ? lastImportIndex : 0}`) const extraNodes = [ //@fix //importTaroH5Node, importMpNode, importRouterNode, //@fix //initPxTransformNode ] astPath.traverse(programExitVisitor) if (hasJSX && !hasAddNervJsImportDefaultName) { //@fix //extraNodes.unshift(importNervjsNode) } if (tabBar) { extraNodes.unshift(importComponentNode) } lastImportNode.insertAfter(extraNodes) if (renderCallCode) { const renderCallNode = toAst(renderCallCode) astPath.pushContainer('body', renderCallNode) } } } }) const generateCode = generate(ast, { jsescOption: { minimal: true } }).code return { code: generateCode, ast } }