Example #1
0
      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);
        }
      })      
Example #2
0
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;
};
Example #3
0
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;
}
Example #4
0
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);
                        }
                    }
                }
            }
        }
    }
}
Example #5
0
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;
}
Example #6
0
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;
  }
}
Example #7
0
  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);
  }
Example #8
0
 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);
   }
 });
Example #9
0
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}`);
}
Example #10
0
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;
}
Example #11
0
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];
  }
}
Example #12
0
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;
 });
Example #14
0
function toString(node) {
    if (t.isStringLiteral(node)) return node.value;
    if (t.isMemberExpression) return `{{${generate(node).code}}}`;
}
Example #15
0
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;
    }
};
Example #16
0
  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;
  });
Example #17
0
      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;
      });
Example #18
0
      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;
Example #19
0
	/* 
	*	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
            }
        }
    }
Example #20
0
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)

      }
    }
  };
}
Example #22
0
File: index.js Project: t-mart/gvd
var isLiteral = (n) => t.isNumericLiteral(n) ||
t.isBooleanLiteral(n) ||
t.isNullLiteral(n) ||
t.isStringLiteral(n) ||
t.isRegExpLiteral(n);
Example #23
0
 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;
 }, {});
Example #24
0
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
  }
}