diff --git a/lib/types.js b/lib/types.js index a5211c0e89..929c37d111 100644 --- a/lib/types.js +++ b/lib/types.js @@ -1486,245 +1486,261 @@ declare module "babel-types" { declare function tSTypeParameterInstantiation(params: any): BabelNodeTSTypeParameterInstantiation; declare function tSTypeParameterDeclaration(params: any): BabelNodeTSTypeParameterDeclaration; declare function tSTypeParameter(constraint?: ?BabelNodeTSType, _default?: ?BabelNodeTSType, name?: string): BabelNodeTSTypeParameter; - declare function isArrayExpression(node: Object, opts?: Object): boolean; - declare function isAssignmentExpression(node: Object, opts?: Object): boolean; - declare function isBinaryExpression(node: Object, opts?: Object): boolean; - declare function isDirective(node: Object, opts?: Object): boolean; - declare function isDirectiveLiteral(node: Object, opts?: Object): boolean; - declare function isBlockStatement(node: Object, opts?: Object): boolean; - declare function isBreakStatement(node: Object, opts?: Object): boolean; - declare function isCallExpression(node: Object, opts?: Object): boolean; - declare function isCatchClause(node: Object, opts?: Object): boolean; - declare function isConditionalExpression(node: Object, opts?: Object): boolean; - declare function isContinueStatement(node: Object, opts?: Object): boolean; - declare function isDebuggerStatement(node: Object, opts?: Object): boolean; - declare function isDoWhileStatement(node: Object, opts?: Object): boolean; - declare function isEmptyStatement(node: Object, opts?: Object): boolean; - declare function isExpressionStatement(node: Object, opts?: Object): boolean; - declare function isFile(node: Object, opts?: Object): boolean; - declare function isForInStatement(node: Object, opts?: Object): boolean; - declare function isForStatement(node: Object, opts?: Object): boolean; - declare function isFunctionDeclaration(node: Object, opts?: Object): boolean; - declare function isFunctionExpression(node: Object, opts?: Object): boolean; - declare function isIdentifier(node: Object, opts?: Object): boolean; - declare function isIfStatement(node: Object, opts?: Object): boolean; - declare function isLabeledStatement(node: Object, opts?: Object): boolean; - declare function isStringLiteral(node: Object, opts?: Object): boolean; - declare function isNumericLiteral(node: Object, opts?: Object): boolean; - declare function isNullLiteral(node: Object, opts?: Object): boolean; - declare function isBooleanLiteral(node: Object, opts?: Object): boolean; - declare function isRegExpLiteral(node: Object, opts?: Object): boolean; - declare function isLogicalExpression(node: Object, opts?: Object): boolean; - declare function isMemberExpression(node: Object, opts?: Object): boolean; - declare function isNewExpression(node: Object, opts?: Object): boolean; - declare function isProgram(node: Object, opts?: Object): boolean; - declare function isObjectExpression(node: Object, opts?: Object): boolean; - declare function isObjectMethod(node: Object, opts?: Object): boolean; - declare function isObjectProperty(node: Object, opts?: Object): boolean; - declare function isRestElement(node: Object, opts?: Object): boolean; - declare function isReturnStatement(node: Object, opts?: Object): boolean; - declare function isSequenceExpression(node: Object, opts?: Object): boolean; - declare function isSwitchCase(node: Object, opts?: Object): boolean; - declare function isSwitchStatement(node: Object, opts?: Object): boolean; - declare function isThisExpression(node: Object, opts?: Object): boolean; - declare function isThrowStatement(node: Object, opts?: Object): boolean; - declare function isTryStatement(node: Object, opts?: Object): boolean; - declare function isUnaryExpression(node: Object, opts?: Object): boolean; - declare function isUpdateExpression(node: Object, opts?: Object): boolean; - declare function isVariableDeclaration(node: Object, opts?: Object): boolean; - declare function isVariableDeclarator(node: Object, opts?: Object): boolean; - declare function isWhileStatement(node: Object, opts?: Object): boolean; - declare function isWithStatement(node: Object, opts?: Object): boolean; - declare function isAssignmentPattern(node: Object, opts?: Object): boolean; - declare function isArrayPattern(node: Object, opts?: Object): boolean; - declare function isArrowFunctionExpression(node: Object, opts?: Object): boolean; - declare function isClassBody(node: Object, opts?: Object): boolean; - declare function isClassDeclaration(node: Object, opts?: Object): boolean; - declare function isClassExpression(node: Object, opts?: Object): boolean; - declare function isExportAllDeclaration(node: Object, opts?: Object): boolean; - declare function isExportDefaultDeclaration(node: Object, opts?: Object): boolean; - declare function isExportNamedDeclaration(node: Object, opts?: Object): boolean; - declare function isExportSpecifier(node: Object, opts?: Object): boolean; - declare function isForOfStatement(node: Object, opts?: Object): boolean; - declare function isImportDeclaration(node: Object, opts?: Object): boolean; - declare function isImportDefaultSpecifier(node: Object, opts?: Object): boolean; - declare function isImportNamespaceSpecifier(node: Object, opts?: Object): boolean; - declare function isImportSpecifier(node: Object, opts?: Object): boolean; - declare function isMetaProperty(node: Object, opts?: Object): boolean; - declare function isClassMethod(node: Object, opts?: Object): boolean; - declare function isObjectPattern(node: Object, opts?: Object): boolean; - declare function isSpreadElement(node: Object, opts?: Object): boolean; - declare function isSuper(node: Object, opts?: Object): boolean; - declare function isTaggedTemplateExpression(node: Object, opts?: Object): boolean; - declare function isTemplateElement(node: Object, opts?: Object): boolean; - declare function isTemplateLiteral(node: Object, opts?: Object): boolean; - declare function isYieldExpression(node: Object, opts?: Object): boolean; - declare function isAnyTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isArrayTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isBooleanTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isBooleanLiteralTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isNullLiteralTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isClassImplements(node: Object, opts?: Object): boolean; - declare function isDeclareClass(node: Object, opts?: Object): boolean; - declare function isDeclareFunction(node: Object, opts?: Object): boolean; - declare function isDeclareInterface(node: Object, opts?: Object): boolean; - declare function isDeclareModule(node: Object, opts?: Object): boolean; - declare function isDeclareModuleExports(node: Object, opts?: Object): boolean; - declare function isDeclareTypeAlias(node: Object, opts?: Object): boolean; - declare function isDeclareOpaqueType(node: Object, opts?: Object): boolean; - declare function isDeclareVariable(node: Object, opts?: Object): boolean; - declare function isDeclareExportDeclaration(node: Object, opts?: Object): boolean; - declare function isDeclareExportAllDeclaration(node: Object, opts?: Object): boolean; - declare function isDeclaredPredicate(node: Object, opts?: Object): boolean; - declare function isExistsTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isFunctionTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isFunctionTypeParam(node: Object, opts?: Object): boolean; - declare function isGenericTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isInferredPredicate(node: Object, opts?: Object): boolean; - declare function isInterfaceExtends(node: Object, opts?: Object): boolean; - declare function isInterfaceDeclaration(node: Object, opts?: Object): boolean; - declare function isIntersectionTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isMixedTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isEmptyTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isNullableTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isNumberLiteralTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isNumberTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isObjectTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isObjectTypeCallProperty(node: Object, opts?: Object): boolean; - declare function isObjectTypeIndexer(node: Object, opts?: Object): boolean; - declare function isObjectTypeProperty(node: Object, opts?: Object): boolean; - declare function isObjectTypeSpreadProperty(node: Object, opts?: Object): boolean; - declare function isOpaqueType(node: Object, opts?: Object): boolean; - declare function isQualifiedTypeIdentifier(node: Object, opts?: Object): boolean; - declare function isStringLiteralTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isStringTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isThisTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isTupleTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isTypeofTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isTypeAlias(node: Object, opts?: Object): boolean; - declare function isTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isTypeCastExpression(node: Object, opts?: Object): boolean; - declare function isTypeParameter(node: Object, opts?: Object): boolean; - declare function isTypeParameterDeclaration(node: Object, opts?: Object): boolean; - declare function isTypeParameterInstantiation(node: Object, opts?: Object): boolean; - declare function isUnionTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isVoidTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isJSXAttribute(node: Object, opts?: Object): boolean; - declare function isJSXClosingElement(node: Object, opts?: Object): boolean; - declare function isJSXElement(node: Object, opts?: Object): boolean; - declare function isJSXEmptyExpression(node: Object, opts?: Object): boolean; - declare function isJSXExpressionContainer(node: Object, opts?: Object): boolean; - declare function isJSXSpreadChild(node: Object, opts?: Object): boolean; - declare function isJSXIdentifier(node: Object, opts?: Object): boolean; - declare function isJSXMemberExpression(node: Object, opts?: Object): boolean; - declare function isJSXNamespacedName(node: Object, opts?: Object): boolean; - declare function isJSXOpeningElement(node: Object, opts?: Object): boolean; - declare function isJSXSpreadAttribute(node: Object, opts?: Object): boolean; - declare function isJSXText(node: Object, opts?: Object): boolean; - declare function isNoop(node: Object, opts?: Object): boolean; - declare function isParenthesizedExpression(node: Object, opts?: Object): boolean; - declare function isAwaitExpression(node: Object, opts?: Object): boolean; - declare function isBindExpression(node: Object, opts?: Object): boolean; - declare function isClassProperty(node: Object, opts?: Object): boolean; - declare function isImport(node: Object, opts?: Object): boolean; - declare function isDecorator(node: Object, opts?: Object): boolean; - declare function isDoExpression(node: Object, opts?: Object): boolean; - declare function isExportDefaultSpecifier(node: Object, opts?: Object): boolean; - declare function isExportNamespaceSpecifier(node: Object, opts?: Object): boolean; - declare function isTSParameterProperty(node: Object, opts?: Object): boolean; - declare function isTSDeclareFunction(node: Object, opts?: Object): boolean; - declare function isTSDeclareMethod(node: Object, opts?: Object): boolean; - declare function isTSQualifiedName(node: Object, opts?: Object): boolean; - declare function isTSCallSignatureDeclaration(node: Object, opts?: Object): boolean; - declare function isTSConstructSignatureDeclaration(node: Object, opts?: Object): boolean; - declare function isTSPropertySignature(node: Object, opts?: Object): boolean; - declare function isTSMethodSignature(node: Object, opts?: Object): boolean; - declare function isTSIndexSignature(node: Object, opts?: Object): boolean; - declare function isTSAnyKeyword(node: Object, opts?: Object): boolean; - declare function isTSNumberKeyword(node: Object, opts?: Object): boolean; - declare function isTSObjectKeyword(node: Object, opts?: Object): boolean; - declare function isTSBooleanKeyword(node: Object, opts?: Object): boolean; - declare function isTSStringKeyword(node: Object, opts?: Object): boolean; - declare function isTSSymbolKeyword(node: Object, opts?: Object): boolean; - declare function isTSVoidKeyword(node: Object, opts?: Object): boolean; - declare function isTSUndefinedKeyword(node: Object, opts?: Object): boolean; - declare function isTSNullKeyword(node: Object, opts?: Object): boolean; - declare function isTSNeverKeyword(node: Object, opts?: Object): boolean; - declare function isTSThisType(node: Object, opts?: Object): boolean; - declare function isTSFunctionType(node: Object, opts?: Object): boolean; - declare function isTSConstructorType(node: Object, opts?: Object): boolean; - declare function isTSTypeReference(node: Object, opts?: Object): boolean; - declare function isTSTypePredicate(node: Object, opts?: Object): boolean; - declare function isTSTypeQuery(node: Object, opts?: Object): boolean; - declare function isTSTypeLiteral(node: Object, opts?: Object): boolean; - declare function isTSArrayType(node: Object, opts?: Object): boolean; - declare function isTSTupleType(node: Object, opts?: Object): boolean; - declare function isTSUnionType(node: Object, opts?: Object): boolean; - declare function isTSIntersectionType(node: Object, opts?: Object): boolean; - declare function isTSParenthesizedType(node: Object, opts?: Object): boolean; - declare function isTSTypeOperator(node: Object, opts?: Object): boolean; - declare function isTSIndexedAccessType(node: Object, opts?: Object): boolean; - declare function isTSMappedType(node: Object, opts?: Object): boolean; - declare function isTSLiteralType(node: Object, opts?: Object): boolean; - declare function isTSExpressionWithTypeArguments(node: Object, opts?: Object): boolean; - declare function isTSInterfaceDeclaration(node: Object, opts?: Object): boolean; - declare function isTSInterfaceBody(node: Object, opts?: Object): boolean; - declare function isTSTypeAliasDeclaration(node: Object, opts?: Object): boolean; - declare function isTSAsExpression(node: Object, opts?: Object): boolean; - declare function isTSTypeAssertion(node: Object, opts?: Object): boolean; - declare function isTSEnumDeclaration(node: Object, opts?: Object): boolean; - declare function isTSEnumMember(node: Object, opts?: Object): boolean; - declare function isTSModuleDeclaration(node: Object, opts?: Object): boolean; - declare function isTSModuleBlock(node: Object, opts?: Object): boolean; - declare function isTSImportEqualsDeclaration(node: Object, opts?: Object): boolean; - declare function isTSExternalModuleReference(node: Object, opts?: Object): boolean; - declare function isTSNonNullExpression(node: Object, opts?: Object): boolean; - declare function isTSExportAssignment(node: Object, opts?: Object): boolean; - declare function isTSNamespaceExportDeclaration(node: Object, opts?: Object): boolean; - declare function isTSTypeAnnotation(node: Object, opts?: Object): boolean; - declare function isTSTypeParameterInstantiation(node: Object, opts?: Object): boolean; - declare function isTSTypeParameterDeclaration(node: Object, opts?: Object): boolean; - declare function isTSTypeParameter(node: Object, opts?: Object): boolean; - declare function isExpression(node: Object, opts?: Object): boolean; - declare function isBinary(node: Object, opts?: Object): boolean; - declare function isScopable(node: Object, opts?: Object): boolean; - declare function isBlockParent(node: Object, opts?: Object): boolean; - declare function isBlock(node: Object, opts?: Object): boolean; - declare function isStatement(node: Object, opts?: Object): boolean; - declare function isTerminatorless(node: Object, opts?: Object): boolean; - declare function isCompletionStatement(node: Object, opts?: Object): boolean; - declare function isConditional(node: Object, opts?: Object): boolean; - declare function isLoop(node: Object, opts?: Object): boolean; - declare function isWhile(node: Object, opts?: Object): boolean; - declare function isExpressionWrapper(node: Object, opts?: Object): boolean; - declare function isFor(node: Object, opts?: Object): boolean; - declare function isForXStatement(node: Object, opts?: Object): boolean; - declare function isFunction(node: Object, opts?: Object): boolean; - declare function isFunctionParent(node: Object, opts?: Object): boolean; - declare function isPureish(node: Object, opts?: Object): boolean; - declare function isDeclaration(node: Object, opts?: Object): boolean; - declare function isPatternLike(node: Object, opts?: Object): boolean; - declare function isLVal(node: Object, opts?: Object): boolean; - declare function isTSEntityName(node: Object, opts?: Object): boolean; - declare function isLiteral(node: Object, opts?: Object): boolean; - declare function isImmutable(node: Object, opts?: Object): boolean; - declare function isUserWhitespacable(node: Object, opts?: Object): boolean; - declare function isMethod(node: Object, opts?: Object): boolean; - declare function isObjectMember(node: Object, opts?: Object): boolean; - declare function isProperty(node: Object, opts?: Object): boolean; - declare function isUnaryLike(node: Object, opts?: Object): boolean; - declare function isPattern(node: Object, opts?: Object): boolean; - declare function isClass(node: Object, opts?: Object): boolean; - declare function isModuleDeclaration(node: Object, opts?: Object): boolean; - declare function isExportDeclaration(node: Object, opts?: Object): boolean; - declare function isModuleSpecifier(node: Object, opts?: Object): boolean; - declare function isFlow(node: Object, opts?: Object): boolean; - declare function isFlowBaseAnnotation(node: Object, opts?: Object): boolean; - declare function isFlowDeclaration(node: Object, opts?: Object): boolean; - declare function isFlowPredicate(node: Object, opts?: Object): boolean; - declare function isJSX(node: Object, opts?: Object): boolean; - declare function isTSTypeElement(node: Object, opts?: Object): boolean; - declare function isTSType(node: Object, opts?: Object): boolean; - declare function isNumberLiteral(node: Object, opts?: Object): boolean; - declare function isRegexLiteral(node: Object, opts?: Object): boolean; + declare function isArrayExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeArrayExpression) + declare function isAssignmentExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeAssignmentExpression) + declare function isBinaryExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeBinaryExpression) + declare function isDirective(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDirective) + declare function isDirectiveLiteral(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDirectiveLiteral) + declare function isBlockStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeBlockStatement) + declare function isBreakStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeBreakStatement) + declare function isCallExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeCallExpression) + declare function isCatchClause(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeCatchClause) + declare function isConditionalExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeConditionalExpression) + declare function isContinueStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeContinueStatement) + declare function isDebuggerStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDebuggerStatement) + declare function isDoWhileStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDoWhileStatement) + declare function isEmptyStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeEmptyStatement) + declare function isExpressionStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeExpressionStatement) + declare function isFile(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeFile) + declare function isForInStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeForInStatement) + declare function isForStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeForStatement) + declare function isFunctionDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeFunctionDeclaration) + declare function isFunctionExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeFunctionExpression) + declare function isIdentifier(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeIdentifier) + declare function isIfStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeIfStatement) + declare function isLabeledStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeLabeledStatement) + declare function isStringLiteral(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeStringLiteral) + declare function isNumericLiteral(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeNumericLiteral) + declare function isNullLiteral(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeNullLiteral) + declare function isBooleanLiteral(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeBooleanLiteral) + declare function isRegExpLiteral(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeRegExpLiteral) + declare function isLogicalExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeLogicalExpression) + declare function isMemberExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeMemberExpression) + declare function isNewExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeNewExpression) + declare function isProgram(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeProgram) + declare function isObjectExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeObjectExpression) + declare function isObjectMethod(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeObjectMethod) + declare function isObjectProperty(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeObjectProperty) + declare function isRestElement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeRestElement) + declare function isReturnStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeReturnStatement) + declare function isSequenceExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeSequenceExpression) + declare function isSwitchCase(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeSwitchCase) + declare function isSwitchStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeSwitchStatement) + declare function isThisExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeThisExpression) + declare function isThrowStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeThrowStatement) + declare function isTryStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTryStatement) + declare function isUnaryExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeUnaryExpression) + declare function isUpdateExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeUpdateExpression) + declare function isVariableDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeVariableDeclaration) + declare function isVariableDeclarator(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeVariableDeclarator) + declare function isWhileStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeWhileStatement) + declare function isWithStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeWithStatement) + declare function isAssignmentPattern(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeAssignmentPattern) + declare function isArrayPattern(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeArrayPattern) + declare function isArrowFunctionExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeArrowFunctionExpression) + declare function isClassBody(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeClassBody) + declare function isClassDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeClassDeclaration) + declare function isClassExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeClassExpression) + declare function isExportAllDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeExportAllDeclaration) + declare function isExportDefaultDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeExportDefaultDeclaration) + declare function isExportNamedDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeExportNamedDeclaration) + declare function isExportSpecifier(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeExportSpecifier) + declare function isForOfStatement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeForOfStatement) + declare function isImportDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeImportDeclaration) + declare function isImportDefaultSpecifier(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeImportDefaultSpecifier) + declare function isImportNamespaceSpecifier(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeImportNamespaceSpecifier) + declare function isImportSpecifier(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeImportSpecifier) + declare function isMetaProperty(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeMetaProperty) + declare function isClassMethod(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeClassMethod) + declare function isObjectPattern(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeObjectPattern) + declare function isSpreadElement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeSpreadElement) + declare function isSuper(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeSuper) + declare function isTaggedTemplateExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTaggedTemplateExpression) + declare function isTemplateElement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTemplateElement) + declare function isTemplateLiteral(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTemplateLiteral) + declare function isYieldExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeYieldExpression) + declare function isAnyTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeAnyTypeAnnotation) + declare function isArrayTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeArrayTypeAnnotation) + declare function isBooleanTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeBooleanTypeAnnotation) + declare function isBooleanLiteralTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeBooleanLiteralTypeAnnotation) + declare function isNullLiteralTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeNullLiteralTypeAnnotation) + declare function isClassImplements(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeClassImplements) + declare function isDeclareClass(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDeclareClass) + declare function isDeclareFunction(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDeclareFunction) + declare function isDeclareInterface(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDeclareInterface) + declare function isDeclareModule(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDeclareModule) + declare function isDeclareModuleExports(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDeclareModuleExports) + declare function isDeclareTypeAlias(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDeclareTypeAlias) + declare function isDeclareOpaqueType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDeclareOpaqueType) + declare function isDeclareVariable(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDeclareVariable) + declare function isDeclareExportDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDeclareExportDeclaration) + declare function isDeclareExportAllDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDeclareExportAllDeclaration) + declare function isDeclaredPredicate(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDeclaredPredicate) + declare function isExistsTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeExistsTypeAnnotation) + declare function isFunctionTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeFunctionTypeAnnotation) + declare function isFunctionTypeParam(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeFunctionTypeParam) + declare function isGenericTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeGenericTypeAnnotation) + declare function isInferredPredicate(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeInferredPredicate) + declare function isInterfaceExtends(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeInterfaceExtends) + declare function isInterfaceDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeInterfaceDeclaration) + declare function isIntersectionTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeIntersectionTypeAnnotation) + declare function isMixedTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeMixedTypeAnnotation) + declare function isEmptyTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeEmptyTypeAnnotation) + declare function isNullableTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeNullableTypeAnnotation) + declare function isNumberLiteralTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeNumberLiteralTypeAnnotation) + declare function isNumberTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeNumberTypeAnnotation) + declare function isObjectTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeObjectTypeAnnotation) + declare function isObjectTypeCallProperty(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeObjectTypeCallProperty) + declare function isObjectTypeIndexer(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeObjectTypeIndexer) + declare function isObjectTypeProperty(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeObjectTypeProperty) + declare function isObjectTypeSpreadProperty(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeObjectTypeSpreadProperty) + declare function isOpaqueType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeOpaqueType) + declare function isQualifiedTypeIdentifier(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeQualifiedTypeIdentifier) + declare function isStringLiteralTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeStringLiteralTypeAnnotation) + declare function isStringTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeStringTypeAnnotation) + declare function isThisTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeThisTypeAnnotation) + declare function isTupleTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTupleTypeAnnotation) + declare function isTypeofTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTypeofTypeAnnotation) + declare function isTypeAlias(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTypeAlias) + declare function isTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTypeAnnotation) + declare function isTypeCastExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTypeCastExpression) + declare function isTypeParameter(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTypeParameter) + declare function isTypeParameterDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTypeParameterDeclaration) + declare function isTypeParameterInstantiation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTypeParameterInstantiation) + declare function isUnionTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeUnionTypeAnnotation) + declare function isVoidTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeVoidTypeAnnotation) + declare function isJSXAttribute(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXAttribute) + declare function isJSXClosingElement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXClosingElement) + declare function isJSXElement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXElement) + declare function isJSXEmptyExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXEmptyExpression) + declare function isJSXExpressionContainer(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXExpressionContainer) + declare function isJSXSpreadChild(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXSpreadChild) + declare function isJSXIdentifier(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXIdentifier) + declare function isJSXMemberExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXMemberExpression) + declare function isJSXNamespacedName(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXNamespacedName) + declare function isJSXOpeningElement(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXOpeningElement) + declare function isJSXSpreadAttribute(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXSpreadAttribute) + declare function isJSXText(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeJSXText) + declare function isNoop(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeNoop) + declare function isParenthesizedExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeParenthesizedExpression) + declare function isAwaitExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeAwaitExpression) + declare function isBindExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeBindExpression) + declare function isClassProperty(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeClassProperty) + declare function isImport(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeImport) + declare function isDecorator(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDecorator) + declare function isDoExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeDoExpression) + declare function isExportDefaultSpecifier(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeExportDefaultSpecifier) + declare function isExportNamespaceSpecifier(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeExportNamespaceSpecifier) + declare function isTSParameterProperty(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSParameterProperty) + declare function isTSDeclareFunction(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSDeclareFunction) + declare function isTSDeclareMethod(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSDeclareMethod) + declare function isTSQualifiedName(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSQualifiedName) + declare function isTSCallSignatureDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSCallSignatureDeclaration) + declare function isTSConstructSignatureDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSConstructSignatureDeclaration) + declare function isTSPropertySignature(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSPropertySignature) + declare function isTSMethodSignature(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSMethodSignature) + declare function isTSIndexSignature(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSIndexSignature) + declare function isTSAnyKeyword(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSAnyKeyword) + declare function isTSNumberKeyword(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSNumberKeyword) + declare function isTSObjectKeyword(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSObjectKeyword) + declare function isTSBooleanKeyword(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSBooleanKeyword) + declare function isTSStringKeyword(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSStringKeyword) + declare function isTSSymbolKeyword(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSSymbolKeyword) + declare function isTSVoidKeyword(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSVoidKeyword) + declare function isTSUndefinedKeyword(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSUndefinedKeyword) + declare function isTSNullKeyword(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSNullKeyword) + declare function isTSNeverKeyword(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSNeverKeyword) + declare function isTSThisType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSThisType) + declare function isTSFunctionType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSFunctionType) + declare function isTSConstructorType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSConstructorType) + declare function isTSTypeReference(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTypeReference) + declare function isTSTypePredicate(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTypePredicate) + declare function isTSTypeQuery(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTypeQuery) + declare function isTSTypeLiteral(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTypeLiteral) + declare function isTSArrayType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSArrayType) + declare function isTSTupleType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTupleType) + declare function isTSUnionType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSUnionType) + declare function isTSIntersectionType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSIntersectionType) + declare function isTSParenthesizedType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSParenthesizedType) + declare function isTSTypeOperator(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTypeOperator) + declare function isTSIndexedAccessType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSIndexedAccessType) + declare function isTSMappedType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSMappedType) + declare function isTSLiteralType(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSLiteralType) + declare function isTSExpressionWithTypeArguments(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSExpressionWithTypeArguments) + declare function isTSInterfaceDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSInterfaceDeclaration) + declare function isTSInterfaceBody(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSInterfaceBody) + declare function isTSTypeAliasDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTypeAliasDeclaration) + declare function isTSAsExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSAsExpression) + declare function isTSTypeAssertion(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTypeAssertion) + declare function isTSEnumDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSEnumDeclaration) + declare function isTSEnumMember(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSEnumMember) + declare function isTSModuleDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSModuleDeclaration) + declare function isTSModuleBlock(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSModuleBlock) + declare function isTSImportEqualsDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSImportEqualsDeclaration) + declare function isTSExternalModuleReference(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSExternalModuleReference) + declare function isTSNonNullExpression(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSNonNullExpression) + declare function isTSExportAssignment(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSExportAssignment) + declare function isTSNamespaceExportDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSNamespaceExportDeclaration) + declare function isTSTypeAnnotation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTypeAnnotation) + declare function isTSTypeParameterInstantiation(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTypeParameterInstantiation) + declare function isTSTypeParameterDeclaration(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTypeParameterDeclaration) + declare function isTSTypeParameter(node: Object, opts?: ?Object): boolean %checks (node instanceof BabelNodeTSTypeParameter) + declare function isExpression(node: Object, opts?: ?Object): boolean + declare function isBinary(node: Object, opts?: ?Object): boolean + declare function isScopable(node: Object, opts?: ?Object): boolean + declare function isBlockParent(node: Object, opts?: ?Object): boolean + declare function isBlock(node: Object, opts?: ?Object): boolean + declare function isStatement(node: Object, opts?: ?Object): boolean + declare function isTerminatorless(node: Object, opts?: ?Object): boolean + declare function isCompletionStatement(node: Object, opts?: ?Object): boolean + declare function isConditional(node: Object, opts?: ?Object): boolean + declare function isLoop(node: Object, opts?: ?Object): boolean + declare function isWhile(node: Object, opts?: ?Object): boolean + declare function isExpressionWrapper(node: Object, opts?: ?Object): boolean + declare function isFor(node: Object, opts?: ?Object): boolean + declare function isForXStatement(node: Object, opts?: ?Object): boolean + declare function isFunction(node: Object, opts?: ?Object): boolean + declare function isFunctionParent(node: Object, opts?: ?Object): boolean + declare function isPureish(node: Object, opts?: ?Object): boolean + declare function isDeclaration(node: Object, opts?: ?Object): boolean + declare function isPatternLike(node: Object, opts?: ?Object): boolean + declare function isLVal(node: Object, opts?: ?Object): boolean + declare function isTSEntityName(node: Object, opts?: ?Object): boolean + declare function isLiteral(node: Object, opts?: ?Object): boolean + declare function isImmutable(node: Object, opts?: ?Object): boolean + declare function isUserWhitespacable(node: Object, opts?: ?Object): boolean + declare function isMethod(node: Object, opts?: ?Object): boolean + declare function isObjectMember(node: Object, opts?: ?Object): boolean + declare function isProperty(node: Object, opts?: ?Object): boolean + declare function isUnaryLike(node: Object, opts?: ?Object): boolean + declare function isPattern(node: Object, opts?: ?Object): boolean + declare function isClass(node: Object, opts?: ?Object): boolean + declare function isModuleDeclaration(node: Object, opts?: ?Object): boolean + declare function isExportDeclaration(node: Object, opts?: ?Object): boolean + declare function isModuleSpecifier(node: Object, opts?: ?Object): boolean + declare function isFlow(node: Object, opts?: ?Object): boolean + declare function isFlowBaseAnnotation(node: Object, opts?: ?Object): boolean + declare function isFlowDeclaration(node: Object, opts?: ?Object): boolean + declare function isFlowPredicate(node: Object, opts?: ?Object): boolean + declare function isJSX(node: Object, opts?: ?Object): boolean + declare function isTSTypeElement(node: Object, opts?: ?Object): boolean + declare function isTSType(node: Object, opts?: ?Object): boolean + declare function isNumberLiteral(node: Object, opts?: ?Object): boolean + declare function isRegexLiteral(node: Object, opts?: ?Object): boolean + declare function validate(n: BabelNode, key: string, value: mixed): void; + declare function clone(n: T): T; + declare function cloneDeep(n: T): T; + declare function removeProperties(n: T, opts: ?{}): void; + declare function removePropertiesDeep(n: T, opts: ?{}): T; + declare type TraversalAncestors = Array<{ + node: BabelNode, + key: string, + index?: number, + }>; + declare type TraversalHandler = (BabelNode, TraversalAncestors, T) => void; + declare type TraversalHandlers = { + enter?: TraversalHandler, + exit?: TraversalHandler, + }; + declare function traverse(n: BabelNode, TraversalHandler | TraversalHandlers, state?: T): void; } diff --git a/packages/babel-helper-module-transforms/src/index.js b/packages/babel-helper-module-transforms/src/index.js index 3b2fb229db..f6345b6fe1 100644 --- a/packages/babel-helper-module-transforms/src/index.js +++ b/packages/babel-helper-module-transforms/src/index.js @@ -102,14 +102,6 @@ export function wrapInterop( return t.callExpression(programPath.hub.file.addHelper(helper), [expr]); } -const buildNamespaceInit = template(` - var NAME = SOURCE; -`); - -const buildReexportNamespace = template(` - EXPORTS.NAME = NAMESPACE; -`); - /** * Create the runtime initialization statements for a given requested source. * These will initialize all of the runtime import/export logic that @@ -127,19 +119,19 @@ export function buildNamespaceInitStatements( // Create and assign binding to namespace object statements.push( - buildNamespaceInit({ - NAME: t.identifier(localName), - SOURCE: t.identifier(sourceMetadata.name), + template.statement`var NAME = SOURCE;`({ + NAME: localName, + SOURCE: sourceMetadata.name, }), ); } for (const exportName of sourceMetadata.reexportNamespace) { // Assign export to namespace object. statements.push( - buildReexportNamespace({ - EXPORTS: t.identifier(metadata.exportName), - NAME: t.identifier(exportName), - NAMESPACE: t.identifier(sourceMetadata.name), + template.statement`EXPORTS.NAME = NAMESPACE;`({ + EXPORTS: metadata.exportName, + NAME: exportName, + NAMESPACE: sourceMetadata.name, }), ); } @@ -153,16 +145,6 @@ export function buildNamespaceInitStatements( return statements; } -const moduleHeader = template(` - Object.defineProperty(EXPORTS, "__esModule", { - value: true, - }) -`); - -const moduleHeaderLoose = template(` - EXPORTS.__esModule = true; -`); - /** * Build an "__esModule" header statement setting the property on a given object. */ @@ -170,58 +152,44 @@ function buildESModuleHeader( metadata: ModuleMetadata, enumerable: boolean = false, ) { - if (enumerable) { - return moduleHeaderLoose({ - EXPORTS: t.identifier(metadata.exportName), - }); - } - - return moduleHeader({ - EXPORTS: t.identifier(metadata.exportName), - }); + return (enumerable + ? template.statement` + EXPORTS.__esModule = true; + ` + : template.statement` + Object.defineProperty(EXPORTS, "__esModule", { + value: true, + }); + `)({ EXPORTS: metadata.exportName }); } -const namespaceReexport = template(` - Object.keys(NAMESPACE).forEach(function(key) { - if (key === "default" || key === "__esModule") return; - VERIFY_NAME_LIST; - - Object.defineProperty(EXPORTS, key, { - enumerable: true, - get: function() { - return NAMESPACE[key]; - }, - }); - }); -`); -const buildNameListCheck = template(` - if (Object.prototype.hasOwnProperty.call(EXPORTS_LIST, key)) return; -`); - /** * Create a re-export initialization loop for a specific imported namespace. */ function buildNamespaceReexport(metadata, namespace) { - return namespaceReexport({ - NAMESPACE: t.identifier(namespace), - EXPORTS: t.identifier(metadata.exportName), + return template.statement` + Object.keys(NAMESPACE).forEach(function(key) { + if (key === "default" || key === "__esModule") return; + VERIFY_NAME_LIST; + + Object.defineProperty(EXPORTS, key, { + enumerable: true, + get: function() { + return NAMESPACE[key]; + }, + }); + }); + `({ + NAMESPACE: namespace, + EXPORTS: metadata.exportName, VERIFY_NAME_LIST: metadata.exportNameListName - ? buildNameListCheck({ - EXPORTS_LIST: t.identifier(metadata.exportNameListName), - }) + ? template` + if (Object.prototype.hasOwnProperty.call(EXPORTS_LIST, key)) return; + `({ EXPORTS_LIST: metadata.exportNameListName }) : null, }); } -const reexportGetter = template(` - Object.defineProperty(EXPORTS, EXPORT_NAME, { - enumerable: true, - get: function() { - return NAMESPACE.IMPORT_NAME; - }, - }); -`); - /** * Build a statement declaring a variable that contains all of the exported * variable names in an object so they can easily be referenced from an @@ -289,11 +257,18 @@ function buildExportInitializationStatements( for (const data of metadata.source.values()) { for (const [exportName, importName] of data.reexports) { initStatements.push( - reexportGetter({ - EXPORTS: t.identifier(metadata.exportName), - EXPORT_NAME: t.stringLiteral(exportName), - NAMESPACE: t.identifier(data.name), - IMPORT_NAME: t.identifier(importName), + template` + Object.defineProperty(EXPORTS, "EXPORT_NAME", { + enumerable: true, + get: function() { + return NAMESPACE.IMPORT_NAME; + }, + }); + `({ + EXPORTS: metadata.exportName, + EXPORT_NAME: exportName, + NAMESPACE: data.name, + IMPORT_NAME: importName, }), ); } @@ -315,22 +290,20 @@ function buildExportInitializationStatements( return initStatements; } -const initStatement = template(` - EXPORTS.NAME = VALUE; -`); - /** * Given a set of export names, create a set of nested assignments to * initialize them all to a given expression. */ function buildInitStatement(metadata, exportNames, initExpr) { return t.expressionStatement( - exportNames.reduce((acc, exportName) => { - return initStatement({ - EXPORTS: t.identifier(metadata.exportName), - NAME: t.identifier(exportName), - VALUE: acc, - }).expression; - }, initExpr), + exportNames.reduce( + (acc, exportName) => + template.expression`EXPORTS.NAME = VALUE`({ + EXPORTS: metadata.exportName, + NAME: exportName, + VALUE: acc, + }), + initExpr, + ), ); } diff --git a/packages/babel-helper-module-transforms/src/rewrite-live-references.js b/packages/babel-helper-module-transforms/src/rewrite-live-references.js index bdb53e915e..29c4e9a879 100644 --- a/packages/babel-helper-module-transforms/src/rewrite-live-references.js +++ b/packages/babel-helper-module-transforms/src/rewrite-live-references.js @@ -142,16 +142,12 @@ const buildBindingExportAssignmentExpression = ( }, localExpr); }; -const importThrow = template(` - (function() { - throw new Error('"' + NAME + '" is read-only.'); - })(); -`); - const buildImportThrow = localName => { - return importThrow({ - NAME: t.stringLiteral(localName), - }).expression; + return template.expression.ast` + (function() { + throw new Error('"' + '${localName}' + '" is read-only.'); + })() + `; }; const rewriteReferencesVisitor = { diff --git a/packages/babel-helper-remap-async-to-generator/src/for-await.js b/packages/babel-helper-remap-async-to-generator/src/for-await.js index e9d1d867a8..815b90525a 100644 --- a/packages/babel-helper-remap-async-to-generator/src/for-await.js +++ b/packages/babel-helper-remap-async-to-generator/src/for-await.js @@ -72,7 +72,7 @@ export default function(path, helpers) { OBJECT: node.right, STEP_VALUE: stepValue, STEP_KEY: stepKey, - AWAIT: helpers.wrapAwait, + ...(helpers.wrapAwait ? { AWAIT: helpers.wrapAwait } : {}), }); // remove generator function wrapper diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 0440e2a1f4..0329ff4dd0 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -5,9 +5,9 @@ import template from "@babel/template"; const helpers = {}; export default helpers; -function defineHelper(str) { - return template(str, { sourceType: "module" }); -} +// Helpers never include placeholders, so we disable placeholder pattern +// matching to allow us to use pattern-like variable names. +const defineHelper = template.program({ placeholderPattern: false }); helpers.typeof = defineHelper(` export default function _typeof(obj) { diff --git a/packages/babel-helpers/src/index.js b/packages/babel-helpers/src/index.js index 641d1bab9a..44cd492c07 100644 --- a/packages/babel-helpers/src/index.js +++ b/packages/babel-helpers/src/index.js @@ -232,8 +232,7 @@ function loadHelper(name) { if (!helpers[name]) throw new ReferenceError(`Unknown helper ${name}`); const fn = () => { - const ast = helpers[name](); - return t.file(t.program(Array.isArray(ast) ? ast : [ast])); + return t.file(helpers[name]()); }; const metadata = getHelperMetadata(fn()); diff --git a/packages/babel-helpers/test/helpers/define-helper.js b/packages/babel-helpers/test/helpers/define-helper.js index 134d2e18cf..69cc39e7a9 100644 --- a/packages/babel-helpers/test/helpers/define-helper.js +++ b/packages/babel-helpers/test/helpers/define-helper.js @@ -17,7 +17,7 @@ export default function defineHelper( throw new Error(`The ${id} helper is already defined.`); } Object.defineProperty(helpers, id, { - value: template(code, { sourceType: "module" }), + value: template.program(code), }); return id; } diff --git a/packages/babel-plugin-transform-class-properties/src/index.js b/packages/babel-plugin-transform-class-properties/src/index.js index 4f6f8e1b0f..0fef5b90f6 100644 --- a/packages/babel-plugin-transform-class-properties/src/index.js +++ b/packages/babel-plugin-transform-class-properties/src/index.js @@ -25,30 +25,27 @@ export default function({ types: t }, options) { }, }; - const buildObjectDefineProperty = template(` - Object.defineProperty(REF, KEY, { - configurable: true, - enumerable: true, - writable: true, - value: VALUE - }); - `); - - const buildClassPropertySpec = (ref, { key, value, computed }, scope) => - buildObjectDefineProperty({ + const buildClassPropertySpec = (ref, { key, value, computed }, scope) => { + return template.statement` + Object.defineProperty(REF, KEY, { + configurable: true, + enumerable: true, + writable: true, + value: VALUE + }); + `({ REF: ref, KEY: t.isIdentifier(key) && !computed ? t.stringLiteral(key.name) : key, - VALUE: value ? value : scope.buildUndefinedNode(), + VALUE: value || scope.buildUndefinedNode(), }); + }; - const buildClassPropertyLoose = (ref, { key, value, computed }, scope) => - t.expressionStatement( - t.assignmentExpression( - "=", - t.memberExpression(ref, key, computed || t.isLiteral(key)), - value ? value : scope.buildUndefinedNode(), - ), - ); + const buildClassPropertyLoose = (ref, { key, value, computed }, scope) => { + return template.statement`MEMBER = VALUE`({ + MEMBER: t.memberExpression(ref, key, computed || t.isLiteral(key)), + VALUE: value || scope.buildUndefinedNode(), + }); + }; const buildClassProperty = loose ? buildClassPropertyLoose diff --git a/packages/babel-plugin-transform-es2015-classes/src/vanilla.js b/packages/babel-plugin-transform-es2015-classes/src/vanilla.js index 7a705466b3..27a94be41c 100644 --- a/packages/babel-plugin-transform-es2015-classes/src/vanilla.js +++ b/packages/babel-plugin-transform-es2015-classes/src/vanilla.js @@ -6,12 +6,6 @@ import * as defineMap from "@babel/helper-define-map"; import template from "@babel/template"; import * as t from "@babel/types"; -const buildDerivedConstructor = template(` - (function () { - super(...arguments); - }) -`); - const noMethodVisitor = { "FunctionExpression|FunctionDeclaration"(path) { path.skip(); @@ -212,7 +206,11 @@ export default class ClassTransformer { let params, body; if (this.isDerived) { - const constructor = buildDerivedConstructor().expression; + const constructor = template.expression.ast` + (function () { + super(...arguments); + }) + `; params = constructor.params; body = constructor.body; } else { diff --git a/packages/babel-plugin-transform-es2015-for-of/src/index.js b/packages/babel-plugin-transform-es2015-for-of/src/index.js index b6b1a88bbf..98ee64ce57 100644 --- a/packages/babel-plugin-transform-es2015-for-of/src/index.js +++ b/packages/babel-plugin-transform-es2015-for-of/src/index.js @@ -245,7 +245,6 @@ export default function({ template, types: t }, options) { ITERATOR_KEY: iteratorKey, STEP_KEY: stepKey, OBJECT: node.right, - BODY: null, }); const isLabeledParent = t.isLabeledStatement(parent); diff --git a/packages/babel-plugin-transform-es2015-modules-amd/src/index.js b/packages/babel-plugin-transform-es2015-modules-amd/src/index.js index ca53784ea2..8e202a9f47 100644 --- a/packages/babel-plugin-transform-es2015-modules-amd/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-amd/src/index.js @@ -37,23 +37,16 @@ export default function({ types: t }, options) { }); const amdArgs = []; - const commonjsArgs = []; const importNames = []; if (hasExports(meta)) { amdArgs.push(t.stringLiteral("exports")); - commonjsArgs.push(t.identifier("exports")); importNames.push(t.identifier(meta.exportName)); } for (const [source, metadata] of meta.source) { amdArgs.push(t.stringLiteral(source)); - commonjsArgs.push( - t.callExpression(t.identifier("require"), [ - t.stringLiteral(source), - ]), - ); importNames.push(t.identifier(metadata.name)); if (!isSideEffectImport(metadata)) { @@ -89,7 +82,6 @@ export default function({ types: t }, options) { MODULE_NAME: moduleName, AMD_ARGUMENTS: t.arrayExpression(amdArgs), - COMMONJS_ARGUMENTS: commonjsArgs, IMPORT_NAMES: importNames, }), ])[0]; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index cc64b90bc0..67c6f49773 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -19,19 +19,11 @@ export default function({ types: t, template }, options) { // Defaulting to 'true' for now. May change before 7.x major. allowCommonJSExports = true, } = options; - const moduleAssertion = template(` + const getAssertion = localName => template.expression.ast` (function(){ - throw new Error("The CommonJS 'module' variable is not available in ES6 modules."); - })(); - `); - const exportsAssertion = template(` - (function(){ - throw new Error("The CommonJS 'exports' variable is not available in ES6 modules."); - })(); - `); - const getAssertion = localName => - (localName === "module" ? moduleAssertion() : exportsAssertion()) - .expression; + throw new Error("The CommonJS '" + "${localName}" + "' variable is not available in ES6 modules."); + })() + `; const moduleExportsVisitor = { ReferencedIdentifier(path) { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/misc/module-exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/misc/module-exports/expected.js index f8aefc84ca..e1138b612d 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/misc/module-exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/misc/module-exports/expected.js @@ -3,56 +3,56 @@ require("foo"); console.log(function () { - throw new Error("The CommonJS 'exports' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "exports" + "' variable is not available in ES6 modules."); }()); console.log(function () { - throw new Error("The CommonJS 'exports' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "exports" + "' variable is not available in ES6 modules."); }().prop); exports = function () { - throw new Error("The CommonJS 'exports' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "exports" + "' variable is not available in ES6 modules."); }() + 1; exports = function () { - throw new Error("The CommonJS 'exports' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "exports" + "' variable is not available in ES6 modules."); }() + 4; ({ exports } = ({}, function () { - throw new Error("The CommonJS 'exports' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "exports" + "' variable is not available in ES6 modules."); }())); [exports] = ([], function () { - throw new Error("The CommonJS 'exports' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "exports" + "' variable is not available in ES6 modules."); }()); exports = {}; (function () { - throw new Error("The CommonJS 'exports' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "exports" + "' variable is not available in ES6 modules."); })().prop = ""; console.log(function () { - throw new Error("The CommonJS 'module' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "module" + "' variable is not available in ES6 modules."); }()); console.log(function () { - throw new Error("The CommonJS 'module' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "module" + "' variable is not available in ES6 modules."); }().exports); module = function () { - throw new Error("The CommonJS 'module' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "module" + "' variable is not available in ES6 modules."); }() + 1; module = function () { - throw new Error("The CommonJS 'module' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "module" + "' variable is not available in ES6 modules."); }() + 4; ({ module } = ({}, function () { - throw new Error("The CommonJS 'module' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "module" + "' variable is not available in ES6 modules."); }())); [module] = ([], function () { - throw new Error("The CommonJS 'module' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "module" + "' variable is not available in ES6 modules."); }()); module = {}; (function () { - throw new Error("The CommonJS 'module' variable is not available in ES6 modules."); + throw new Error("The CommonJS '" + "module" + "' variable is not available in ES6 modules."); })().prop = ""; diff --git a/packages/babel-plugin-transform-es2015-modules-systemjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-systemjs/src/index.js index 2e3de1e3c5..61685a071a 100644 --- a/packages/babel-plugin-transform-es2015-modules-systemjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-systemjs/src/index.js @@ -2,11 +2,11 @@ import hoistVariables from "@babel/helper-hoist-variables"; import template from "@babel/template"; const buildTemplate = template(` - SYSTEM_REGISTER(MODULE_NAME, [SOURCES], function (EXPORT_IDENTIFIER, CONTEXT_IDENTIFIER) { + SYSTEM_REGISTER(MODULE_NAME, SOURCES, function (EXPORT_IDENTIFIER, CONTEXT_IDENTIFIER) { "use strict"; BEFORE_BODY; return { - setters: [SETTERS], + setters: SETTERS, execute: function () { BODY; } @@ -365,8 +365,8 @@ export default function({ types: t }, options) { ), BEFORE_BODY: beforeBody, MODULE_NAME: moduleName, - SETTERS: setters, - SOURCES: sources, + SETTERS: t.arrayExpression(setters), + SOURCES: t.arrayExpression(sources), BODY: path.node.body, EXPORT_IDENTIFIER: exportIdent, CONTEXT_IDENTIFIER: contextIdent, diff --git a/packages/babel-plugin-transform-es2015-parameters/src/params.js b/packages/babel-plugin-transform-es2015-parameters/src/params.js index f9e8d0cb20..036cd96d49 100644 --- a/packages/babel-plugin-transform-es2015-parameters/src/params.js +++ b/packages/babel-plugin-transform-es2015-parameters/src/params.js @@ -111,7 +111,7 @@ export default function convertFunctionParams(path, loose) { }); body.push(defNode); } else if (firstOptionalIndex !== null) { - const defNode = buildArgumentsAccess(param.node, t.numericLiteral(i)); + const defNode = buildArgumentsAccess([param.node, t.numericLiteral(i)]); body.push(defNode); } else if (param.isObjectPattern() || param.isArrayPattern()) { const uid = path.scope.generateUidIdentifier("ref"); diff --git a/packages/babel-template/README.md b/packages/babel-template/README.md index cfc16cf5d4..1cf29eaf17 100644 --- a/packages/babel-template/README.md +++ b/packages/babel-template/README.md @@ -1,6 +1,6 @@ # @babel/template -> Generate an AST from a string template. +> Generate an AST from a string template or template literal. In computer science, this is known as an implementation of quasiquotes. @@ -10,7 +10,7 @@ In computer science, this is known as an implementation of quasiquotes. npm install --save-dev @babel/template ``` -## Usage +## String Usage ```js import template from "@babel/template"; @@ -33,6 +33,93 @@ console.log(generate(ast).code); const myModule = require("my-module"); ``` +### `.ast` + +If no placeholders are in use and you just want a simple way to parse a +string into an AST, you can use the `.ast` version of the template. + +```js +const ast = template.ast(` + var myModule = require("my-module"); +`); +``` +which will parse and return the AST directly. + + +## Template Literal Usage + +```js +import template from "babel-template"; +import generate from "babel-generator"; +import * as t from "babel-types"; + +const fn = template` + var IMPORT_NAME = require('${"my-module"}'); +`); + +const ast = fn({ + IMPORT_NAME: t.identifier("myModule"); +}); + +console.log(generate(ast).code); +``` + +Note that placeholders can be passed directly as part of the template literal +in order to make things as readable as possible, or they can be passed into +the template function. + +### `.ast` + +If no placeholders are in use and you just want a simple way to parse a +string into an AST, you can use the `.ast` version of the template. + +```js +const name = "my-module"; +const mod = "myModule"; + +const ast = template.ast` + var ${mod} = require("${name}"); +`; +``` +which will parse and return the AST directly. Note that unlike the string-based +version mentioned earlier, since this is a template literal, it is still +valid to perform replacements using template literal replacements. + + +## AST results + +The `babel-template` API exposes a few flexible APIs to make it as easy as +possible to create ASTs with an expected structure. Each of these also has +the `.ast` property mentioned above. + +### `template` + +`template` returns either a single statement, or an array of +statements, depending on the parsed result. + +### `template.smart` + +This is the same as the default `template` API, returning either a single +node, or an array of nodes, depending on the parsed result. + +### `template.statement` + +`template.statement("foo;")()` returns a single statement node, and throw +an exception if the result is anything but a single statement. + +### `template.statements` + +`template.statements("foo;foo;")()` returns an array of statement nodes. + +### `template.expression` + +`template.expression("foo")()` returns the expression node. + +### `template.program` + +`template.program("foo;")()` returns the `Program` node for the template. + + ## API ### `template(code, [opts])` @@ -48,6 +135,25 @@ some defaults of its own: * `allowReturnOutsideFunction` is set to `true` by default. * `allowSuperOutsideMethod` is set to `true` by default. +* `sourceType` is set to `module` by default. + +##### placeholderWhitelist + +Type: `Set` +Default: `undefined` + +A set of placeholder names to automatically accept. Items in this list do +not need to match the given placeholder pattern. + +##### placeholderPattern + +Type: `RegExp | false` +Default: `/^[_$A-Z0-9]+$/` + +A pattern to search for when looking for Identifier and StringLiteral +nodes that should be considered placeholders. +'false' will disable placeholder searching entirely, leaving only the +'placeholderWhitelist' value to find placeholders. ##### preserveComments @@ -58,7 +164,9 @@ Set this to `true` to preserve any comments from the `code` parameter. #### Return value -`@babel/template` returns a `function` which is invoked with an optional object -of replacements. See the usage section for an example. +By default `@babel/template` returns a `function` which is invoked with an +optional object of replacements. See the usage section for an example. + +When using `.ast`, the AST will be returned directly. [babylon]: https://github.com/babel/babylon#options diff --git a/packages/babel-template/package.json b/packages/babel-template/package.json index 74860000d8..2670c58fd0 100644 --- a/packages/babel-template/package.json +++ b/packages/babel-template/package.json @@ -9,7 +9,6 @@ "main": "lib/index.js", "dependencies": { "@babel/code-frame": "7.0.0-beta.3", - "@babel/traverse": "7.0.0-beta.3", "@babel/types": "7.0.0-beta.3", "babylon": "7.0.0-beta.29", "lodash": "^4.2.0" diff --git a/packages/babel-template/src/builder.js b/packages/babel-template/src/builder.js new file mode 100644 index 0000000000..93b2403272 --- /dev/null +++ b/packages/babel-template/src/builder.js @@ -0,0 +1,131 @@ +// @flow + +import { + merge, + validate, + type TemplateOpts, + type PublicOpts, + type PublicReplacements, +} from "./options"; +import type { Formatter } from "./formatters"; + +import stringTemplate from "./string"; +import literalTemplate from "./literal"; + +export type TemplateBuilder = { + // Build a new builder, merging the given options with the previous ones. + (opts: PublicOpts): TemplateBuilder, + + // Building from a string produces an AST builder function by default. + (tpl: string, opts: ?PublicOpts): (?PublicReplacements) => T, + + // Building from a template literal produces an AST builder function by default. + (tpl: Array, ...args: Array): (?PublicReplacements) => T, + + // Allow users to explicitly create templates that produce ASTs, skipping + // the need for an intermediate function. + ast: { + (tpl: string, opts: ?PublicOpts): T, + (tpl: Array, ...args: Array): T, + }, +}; + +// Prebuild the options that will be used when parsing a `.ast` template. +// These do not use a pattern because there is no way for users to pass in +// replacement patterns to begin with, and disabling pattern matching means +// users have more flexibility in what type of content they have in their +// template JS. +const NO_PLACEHOLDER: TemplateOpts = validate({ + placeholderPattern: false, +}); + +export default function createTemplateBuilder( + formatter: Formatter, + defaultOpts?: TemplateOpts, +): TemplateBuilder { + const templateFnCache = new WeakMap(); + const templateAstCache = new WeakMap(); + const cachedOpts = defaultOpts || validate(null); + + return Object.assign( + ((tpl, ...args) => { + if (typeof tpl === "string") { + if (args.length > 1) throw new Error("Unexpected extra params."); + return extendedTrace( + stringTemplate(formatter, tpl, merge(cachedOpts, validate(args[0]))), + ); + } else if (Array.isArray(tpl)) { + let builder = templateFnCache.get(tpl); + if (!builder) { + builder = literalTemplate(formatter, tpl, cachedOpts); + templateFnCache.set(tpl, builder); + } + return extendedTrace(builder(args)); + } else if (typeof tpl === "object" && tpl) { + if (args.length > 0) throw new Error("Unexpected extra params."); + return createTemplateBuilder( + formatter, + merge(cachedOpts, validate(tpl)), + ); + } + throw new Error(`Unexpected template param ${typeof tpl}`); + }: Function), + { + ast: (tpl, ...args) => { + if (typeof tpl === "string") { + if (args.length > 1) throw new Error("Unexpected extra params."); + return stringTemplate( + formatter, + tpl, + merge(merge(cachedOpts, validate(args[0])), NO_PLACEHOLDER), + )(); + } else if (Array.isArray(tpl)) { + let builder = templateAstCache.get(tpl); + if (!builder) { + builder = literalTemplate( + formatter, + tpl, + merge(cachedOpts, NO_PLACEHOLDER), + ); + templateAstCache.set(tpl, builder); + } + return builder(args)(); + } + + throw new Error(`Unexpected template param ${typeof tpl}`); + }, + }, + ); +} + +function extendedTrace(fn: Arg => Result): Arg => Result { + // Since we lazy parse the template, we get the current stack so we have the + // original stack to append if it errors when parsing + let rootStack = ""; + try { + // error stack gets populated in IE only on throw + // (https://msdn.microsoft.com/en-us/library/hh699850(v=vs.94).aspx) + throw new Error(); + } catch (error) { + if (error.stack) { + // error.stack does not exists in IE <= 9 + // We slice off the top 3 items in the stack to remove the call to + // 'extendedTrace', and the anonymous builder function, with the final + // stripped line being the error message itself since we threw it + // in the first place and it doesn't matter. + rootStack = error.stack + .split("\n") + .slice(3) + .join("\n"); + } + } + + return (arg: Arg) => { + try { + return fn(arg); + } catch (err) { + err.stack += `\n =============\n${rootStack}`; + throw err; + } + }; +} diff --git a/packages/babel-template/src/formatters.js b/packages/babel-template/src/formatters.js new file mode 100644 index 0000000000..ccb7433112 --- /dev/null +++ b/packages/babel-template/src/formatters.js @@ -0,0 +1,74 @@ +// @flow + +export type Formatter = { + code: string => string, + validate: BabelNodeFile => void, + unwrap: BabelNodeFile => T, +}; + +function makeStatementFormatter( + fn: (Array) => T, +): Formatter { + return { + // We need to prepend a ";" to force statement parsing so that + // ExpressionStatement strings won't be parsed as directives. + // Alonside that, we also prepend a comment so that when a syntax error + // is encountered, the user will be less likely to get confused about + // where the random semicolon came from. + code: str => `/* @babel/template */;\n${str}`, + validate: () => {}, + unwrap: (ast: BabelNodeFile): T => { + return fn(ast.program.body.slice(1)); + }, + }; +} + +export const smart: Formatter< + Array | BabelNodeStatement, +> = makeStatementFormatter(body => { + if (body.length > 1) { + return body; + } else { + return body[0]; + } +}); + +export const statements: Formatter< + Array, +> = makeStatementFormatter(body => body); + +export const statement: Formatter< + BabelNodeStatement, +> = makeStatementFormatter(body => { + // We do this validation when unwrapping since the replacement process + // could have added or removed statements. + if (body.length === 0) { + throw new Error("Found nothing to return."); + } + if (body.length > 1) { + throw new Error("Found multiple statements but wanted one"); + } + + return body[0]; +}); + +export const expression: Formatter = { + code: str => `(\n${str}\n)`, + validate: (ast: BabelNodeFile) => { + const { program } = ast; + if (program.body.length > 1) { + throw new Error("Found multiple statements but wanted one"); + } + const expression = program.body[0].expression; + if (expression.start === 0) { + throw new Error("Parse result included parens."); + } + }, + unwrap: ast => ast.program.body[0].expression, +}; + +export const program: Formatter = { + code: str => str, + validate: () => {}, + unwrap: ast => ast.program, +}; diff --git a/packages/babel-template/src/index.js b/packages/babel-template/src/index.js index 11e7dd1a3b..d3946dfb59 100644 --- a/packages/babel-template/src/index.js +++ b/packages/babel-template/src/index.js @@ -1,221 +1,31 @@ -import cloneDeep from "lodash/cloneDeep"; -import has from "lodash/has"; -import traverse from "@babel/traverse"; -import * as babylon from "babylon"; -import { codeFrameColumns } from "@babel/code-frame"; -import * as t from "@babel/types"; +// @flow -const FROM_TEMPLATE = new Set(); +import * as formatters from "./formatters"; +import createTemplateBuilder from "./builder"; -export default function(firstArg, ...rest) { - if (typeof firstArg === "string") { - return factory(firstArg, ...rest); - } else { - return template(firstArg, ...rest); - } -} +export const smart = createTemplateBuilder(formatters.smart); +export const statement = createTemplateBuilder(formatters.statement); +export const statements = createTemplateBuilder(formatters.statements); +export const expression = createTemplateBuilder(formatters.expression); +export const program = createTemplateBuilder(formatters.program); -function template(partials: Object | string[], ...args: Array) { - if (!Array.isArray(partials)) { - // support template({ options })`string` - return templateApply.bind(undefined, partials); - } - return templateApply(null, partials, ...args); -} - -function templateApply( - opts: Object | null, - partials: string[], - ...args: Array -) { - if (partials.some(str => str.includes("$BABEL_TEMPLATE$"))) { - throw new Error("Template contains illegal substring $BABEL_TEMPLATE$"); - } - - if (partials.length == 1) { - return factory(partials[0], opts); - } - - const replacementSet = new Set(); - const replacementMap = new Map(); - const replacementValueMap = new Map(); - let hasNonNumericReplacement = false; - for (const arg of args) { - if (replacementMap.has(arg)) { - continue; - } - - if (typeof arg === "number") { - replacementMap.set(arg, `$${arg}`); - } else if (typeof arg === "string") { - // avoid duplicates should t.toIdentifier produce the same result for different arguments - const replacementBase = `$BABEL_TEMPLATE$$${t.toIdentifier(arg)}`; - let replacement = replacementBase; - for (let i = 2; replacementSet.has(replacement); i++) { - replacement = `${replacementBase}${i}`; - } - replacementSet.add(replacement); - replacementMap.set(arg, replacement); - hasNonNumericReplacement = true; - } else { - // there can't be duplicates as the size always grows - const name = `$BABEL_TEMPLATE$VALUE$${replacementValueMap.size}`; - - // TODO: check if the arg is a Node - replacementMap.set(arg, name); - replacementValueMap.set(name, arg); - hasNonNumericReplacement = true; - } - } - - if (hasNonNumericReplacement && replacementMap.has(0)) { - throw new Error( - "Template cannot have a '0' replacement and a named replacement at the same time", - ); - } - - const code = partials.reduce((acc, partial, i) => { - if (acc == null) { - return partial; - } - - const replacement = replacementMap.get(args[i - 1]); - return `${acc}${replacement}${partial}`; - }, null); - - const func = factory(code, opts); - - return (...args: Array) => { - if (hasNonNumericReplacement) { - const argObj = args[0] || {}; - const converted = {}; - - for (const [key, replacement] of replacementMap) { - if (typeof key === "number") continue; - if (replacementValueMap.has(replacement)) { - converted[replacement] = replacementValueMap.get(replacement); - } else { - converted[replacement] = argObj[key]; - } - } - - args[0] = converted; - } - - return func(...args); - }; -} - -function factory(code: string, opts?: Object): Function { - // since we lazy parse the template, we get the current stack so we have the - // original stack to append if it errors when parsing - let stack; - try { - // error stack gets populated in IE only on throw - // (https://msdn.microsoft.com/en-us/library/hh699850(v=vs.94).aspx) - throw new Error(); - } catch (error) { - if (error.stack) { - // error.stack does not exists in IE <= 9 - stack = error.stack - .split("\n") - .slice(2) - .join("\n"); - } - } - - opts = Object.assign( - { - allowReturnOutsideFunction: true, - allowSuperOutsideMethod: true, - preserveComments: false, - }, - opts, - ); - - let getAst = function() { - let ast; - - try { - ast = babylon.parse(code, opts); - - ast = traverse.removeProperties(ast, { - preserveComments: opts.preserveComments, - }); - } catch (err) { - const loc = err.loc; - if (loc) { - err.loc = null; - err.message += "\n" + codeFrameColumns(code, { start: loc }); - } - err.stack = `${err.stack}\n ==========================\n${stack}`; - throw err; - } - - getAst = function() { - return ast; - }; - - return ast; - }; - - return function(...args) { - return useTemplate(getAst(), args); - }; -} - -function useTemplate(ast, nodes?: Array) { - ast = cloneDeep(ast); - const { program } = ast; - - if (nodes.length) { - traverse.cheap(ast, function(node) { - FROM_TEMPLATE.add(node); - }); - - traverse(ast, templateVisitor, null, nodes); - - FROM_TEMPLATE.clear(); - } - - if (program.body.length > 1) { - return program.body; - } else { - return program.body[0]; - } -} - -const templateVisitor = { - // 360 - noScope: true, - - Identifier(path, args) { - const { node, parentPath } = path; - if (!FROM_TEMPLATE.has(node)) return path.skip(); - - let replacement; - if (has(args[0], node.name)) { - replacement = args[0][node.name]; - } else if (node.name[0] === "$") { - const i = +node.name.slice(1); - if (args[i]) replacement = args[i]; - } - - if (parentPath.isExpressionStatement()) { - path = parentPath; - } - - if (replacement === null) { - path.remove(); - } else if (replacement) { - path.replaceInline(replacement); - path.skip(); - } - }, - - exit({ node }) { - if (!node.loc) { - traverse.clearNode(node); - } - }, +type DefaultTemplateBuilder = typeof smart & { + smart: typeof smart, + statement: typeof statement, + statements: typeof statements, + expression: typeof expression, + program: typeof program, + ast: typeof smart.ast, }; + +export default Object.assign( + ((smart.bind(undefined): any): DefaultTemplateBuilder), + { + smart, + statement, + statements, + expression, + program, + ast: smart.ast, + }, +); diff --git a/packages/babel-template/src/literal.js b/packages/babel-template/src/literal.js new file mode 100644 index 0000000000..ef78142497 --- /dev/null +++ b/packages/babel-template/src/literal.js @@ -0,0 +1,103 @@ +// @flow + +import type { Formatter } from "./formatters"; +import { normalizeReplacements, type TemplateOpts } from "./options"; +import parseAndBuildMetadata from "./parse"; +import populatePlaceholders from "./populate"; + +export default function literalTemplate( + formatter: Formatter, + tpl: Array, + opts: TemplateOpts, +): (Array) => mixed => T { + const { metadata, names } = buildLiteralData(formatter, tpl, opts); + + return (arg: Array) => { + const defaultReplacements = arg.reduce((acc, replacement, i) => { + acc[names[i]] = replacement; + return acc; + }, {}); + + return (arg: mixed) => { + const replacements = normalizeReplacements(arg); + + if (replacements) { + Object.keys(replacements).forEach(key => { + if (Object.prototype.hasOwnProperty.call(defaultReplacements, key)) { + throw new Error("Unexpected replacement overlap."); + } + }); + } + + return formatter.unwrap( + populatePlaceholders( + metadata, + replacements + ? Object.assign(replacements, defaultReplacements) + : defaultReplacements, + ), + ); + }; + }; +} + +function buildLiteralData( + formatter: Formatter, + tpl: Array, + opts: TemplateOpts, +) { + let names; + let nameSet; + let metadata; + let prefix = ""; + + do { + // If there are cases where the template already contains $0 or any other + // matching pattern, we keep adding "$" characters until a unique prefix + // is found. + prefix += "$"; + const result = buildTemplateCode(tpl, prefix); + + names = result.names; + nameSet = new Set(names); + metadata = parseAndBuildMetadata(formatter, formatter.code(result.code), { + parser: opts.parser, + + // Explicitly include our generated names in the whitelist so users never + // have to think about whether their placeholder pattern will match. + placeholderWhitelist: new Set( + result.names.concat( + opts.placeholderWhitelist + ? Array.from(opts.placeholderWhitelist) + : [], + ), + ), + placeholderPattern: opts.placeholderPattern, + preserveComments: opts.preserveComments, + }); + } while ( + metadata.placeholders.some( + placeholder => placeholder.isDuplicate && nameSet.has(placeholder.name), + ) + ); + + return { metadata, names }; +} + +function buildTemplateCode( + tpl: Array, + prefix: string, +): { names: Array, code: string } { + const names = []; + + let code = tpl[0]; + + for (let i = 1; i < tpl.length; i++) { + const value = `${prefix}${i - 1}`; + names.push(value); + + code += value + tpl[i]; + } + + return { names, code }; +} diff --git a/packages/babel-template/src/options.js b/packages/babel-template/src/options.js new file mode 100644 index 0000000000..81fafb7ae6 --- /dev/null +++ b/packages/babel-template/src/options.js @@ -0,0 +1,116 @@ +// @flow + +/** + * These are the options that 'babel-template' actually accepts and typechecks + * when called. All other options are passed through to the parser. + */ +export type PublicOpts = { + /** + * A set of placeholder names to automatically accept, ignoring the given + * pattern entirely. + */ + placeholderWhitelist?: ?Set, + + /** + * A pattern to search for when looking for Identifier and StringLiteral + * nodes that can be replaced. + * + * 'false' will disable placeholder searching entirely, leaving only the + * 'placeholderWhitelist' value to find replacements. + * + * Defaults to /^[_$A-Z0-9]+$/. + */ + placeholderPattern?: ?(RegExp | false), + + /** + * 'true' to pass through comments from the template into the resulting AST, + * or 'false' to automatically discard comments. Defaults to 'false'. + */ + preserveComments?: ?boolean, +}; + +export type TemplateOpts = {| + parser: {}, + placeholderWhitelist: Set | void, + placeholderPattern: RegExp | false | void, + preserveComments: boolean | void, +|}; + +export function merge(a: TemplateOpts, b: TemplateOpts): TemplateOpts { + const { + placeholderWhitelist = a.placeholderWhitelist, + placeholderPattern = a.placeholderPattern, + preserveComments = a.preserveComments, + } = b; + + return { + parser: Object.assign({}, a.parser, b.parser), + placeholderWhitelist, + placeholderPattern, + preserveComments, + }; +} + +export function validate(opts: mixed): TemplateOpts { + if (opts != null && typeof opts !== "object") { + throw new Error("Unknown template options."); + } + + const { + placeholderWhitelist, + placeholderPattern, + preserveComments, + ...parser + } = + opts || {}; + + if (placeholderWhitelist != null && !(placeholderWhitelist instanceof Set)) { + throw new Error( + "'.placeholderWhitelist' must be a Set, null, or undefined", + ); + } + + if ( + placeholderPattern != null && + !(placeholderPattern instanceof RegExp) && + placeholderPattern !== false + ) { + throw new Error( + "'.placeholderPattern' must be a RegExp, false, null, or undefined", + ); + } + + if (preserveComments != null && typeof preserveComments !== "boolean") { + throw new Error( + "'.preserveComments' must be a boolean, null, or undefined", + ); + } + + return { + parser, + placeholderWhitelist: placeholderWhitelist || undefined, + placeholderPattern: + placeholderPattern == null ? undefined : placeholderPattern, + preserveComments: preserveComments == null ? false : preserveComments, + }; +} + +export type PublicReplacements = { [string]: mixed } | Array; +export type TemplateReplacements = { [string]: mixed } | void; + +export function normalizeReplacements( + replacements: mixed, +): TemplateReplacements { + if (Array.isArray(replacements)) { + return replacements.reduce((acc, replacement, i) => { + acc["$" + i] = replacement; + return acc; + }, {}); + } else if (typeof replacements === "object" || replacements == null) { + return replacements || undefined; + } + + throw new Error( + "Template replacements must be an array, object, null, or undefined", + ); +} diff --git a/packages/babel-template/src/parse.js b/packages/babel-template/src/parse.js new file mode 100644 index 0000000000..c1ec46bde2 --- /dev/null +++ b/packages/babel-template/src/parse.js @@ -0,0 +1,156 @@ +// @flow +import * as t from "@babel/types"; +import type { TraversalAncestors, TraversalHandler } from "@babel/types"; +import { parse } from "babylon"; +import { codeFrameColumns } from "@babel/code-frame"; +import type { TemplateOpts } from "./options"; +import type { Formatter } from "./formatters"; + +export type Metadata = { + ast: BabelNodeFile, + placeholders: Array, + placeholderNames: Set, +}; + +type PlaceholderType = "string" | "param" | "statement" | "other"; +export type Placeholder = {| + name: string, + resolve: BabelNodeFile => { parent: BabelNode, key: string, index?: number }, + type: PlaceholderType, + isDuplicate: boolean, +|}; + +const PATTERN = /^[_$A-Z0-9]+$/; + +export default function parseAndBuildMetadata( + formatter: Formatter, + code: string, + opts: TemplateOpts, +): Metadata { + const ast = parseWithCodeFrame(code, opts.parser); + + const { + placeholderWhitelist, + placeholderPattern = PATTERN, + preserveComments, + } = opts; + + t.removePropertiesDeep(ast, { + preserveComments, + }); + + formatter.validate(ast); + + const placeholders = []; + const placeholderNames = new Set(); + + t.traverse(ast, (placeholderVisitorHandler: TraversalHandler<*>), { + placeholders, + placeholderNames, + placeholderWhitelist, + placeholderPattern, + }); + + return { + ast, + placeholders, + placeholderNames, + }; +} + +function placeholderVisitorHandler( + node: BabelNode, + ancestors: TraversalAncestors, + state: MetadataState, +) { + let name; + if (t.isIdentifier(node)) { + name = ((node: any): BabelNodeIdentifier).name; + } else if (t.isStringLiteral(node)) { + name = ((node: any): BabelNodeStringLiteral).value; + } else { + return; + } + + if ( + (!state.placeholderPattern || !state.placeholderPattern.test(name)) && + (!state.placeholderWhitelist || !state.placeholderWhitelist.has(name)) + ) { + return; + } + + // Keep our own copy of the ancestors so we can use it in .resolve(). + ancestors = ancestors.slice(); + + const { node: parent, key } = ancestors[ancestors.length - 1]; + + let type: PlaceholderType; + if (t.isStringLiteral(node)) { + type = "string"; + } else if ( + (t.isNewExpression(parent) && key === "arguments") || + (t.isCallExpression(parent) && key === "arguments") || + (t.isFunction(parent) && key === "params") + ) { + type = "param"; + } else if (t.isExpressionStatement(parent)) { + type = "statement"; + ancestors = ancestors.slice(0, -1); + } else { + type = "other"; + } + + state.placeholders.push({ + name, + type, + resolve: ast => resolveAncestors(ast, ancestors), + isDuplicate: state.placeholderNames.has(name), + }); + state.placeholderNames.add(name); +} + +function resolveAncestors(ast: BabelNodeFile, ancestors: TraversalAncestors) { + let parent: BabelNode = ast; + for (let i = 0; i < ancestors.length - 1; i++) { + const { key, index } = ancestors[i]; + + if (index === undefined) { + parent = (parent: any)[key]; + } else { + parent = (parent: any)[key][index]; + } + } + + const { key, index } = ancestors[ancestors.length - 1]; + + return { parent, key, index }; +} + +type MetadataState = { + placeholders: Array, + placeholderNames: Set, + placeholderWhitelist: Set | void, + placeholderPattern: RegExp | false, +}; + +function parseWithCodeFrame(code: string, parserOpts: {}): BabelNodeFile { + parserOpts = Object.assign( + { + allowReturnOutsideFunction: true, + allowSuperOutsideMethod: true, + sourceType: "module", + }, + parserOpts, + ); + + try { + return parse(code, parserOpts); + } catch (err) { + const loc = err.loc; + if (loc) { + err.loc = null; + err.message += "\n" + codeFrameColumns(code, { start: loc }); + } + throw err; + } +} diff --git a/packages/babel-template/src/populate.js b/packages/babel-template/src/populate.js new file mode 100644 index 0000000000..d4fa00dec1 --- /dev/null +++ b/packages/babel-template/src/populate.js @@ -0,0 +1,131 @@ +// @flow +import * as t from "@babel/types"; + +import type { TemplateReplacements } from "./options"; +import type { Metadata, Placeholder } from "./parse"; + +export default function populatePlaceholders( + metadata: Metadata, + replacements: TemplateReplacements, +): BabelNodeFile { + const ast = t.cloneDeep(metadata.ast); + + if (replacements) { + metadata.placeholders.forEach(placeholder => { + if ( + !Object.prototype.hasOwnProperty.call(replacements, placeholder.name) + ) { + throw new Error(`No substitution given for "${placeholder.name}"`); + } + }); + Object.keys(replacements).forEach(key => { + if (!metadata.placeholderNames.has(key)) { + throw new Error(`Unknown substitution "${key}" given`); + } + }); + } + + // Process in reverse order to AST mutation doesn't change indices that + // will be needed for later calls to `placeholder.resolve()`. + metadata.placeholders + .slice() + .reverse() + .forEach(placeholder => { + try { + applyReplacement( + placeholder, + ast, + (replacements && replacements[placeholder.name]) || null, + ); + } catch (e) { + e.message = `babel-template placeholder "${placeholder.name}": ${e.message}`; + throw e; + } + }); + + return ast; +} + +function applyReplacement( + placeholder: Placeholder, + ast: BabelNodeFile, + replacement: any, +) { + // Track inserted nodes and clone them if they are inserted more than + // once to avoid injecting the same node multiple times. + if (placeholder.isDuplicate) { + if (Array.isArray(replacement)) { + replacement = replacement.map(node => t.cloneDeep(node)); + } else if (typeof replacement === "object") { + replacement = t.cloneDeep(replacement); + } + } + + const { parent, key, index } = placeholder.resolve(ast); + + if (placeholder.type === "string") { + if (typeof replacement === "string") { + replacement = t.stringLiteral(replacement); + } + if (!replacement || !t.isStringLiteral(replacement)) { + throw new Error("Expected string substitution"); + } + } else if (placeholder.type === "statement") { + if (index === undefined) { + if (!replacement) { + replacement = t.emptyStatement(); + } else if (Array.isArray(replacement)) { + replacement = t.blockStatement(replacement); + } else if (typeof replacement === "string") { + replacement = t.expressionStatement(t.identifier(replacement)); + } else if (!t.isStatement(replacement)) { + replacement = t.expressionStatement((replacement: any)); + } + } else { + if (replacement && !Array.isArray(replacement)) { + if (typeof replacement === "string") { + replacement = t.identifier(replacement); + } + if (!t.isStatement(replacement)) { + replacement = t.expressionStatement((replacement: any)); + } + } + } + } else if (placeholder.type === "param") { + if (typeof replacement === "string") { + replacement = t.identifier(replacement); + } + + if (index === undefined) throw new Error("Assertion failure."); + } else { + if (typeof replacement === "string") { + replacement = t.identifier(replacement); + } + if (Array.isArray(replacement)) { + throw new Error("Cannot replace single expression with an array."); + } + } + + if (index === undefined) { + t.validate(parent, key, replacement); + + (parent: any)[key] = replacement; + } else { + const items: Array = (parent: any)[key].slice(); + + if (placeholder.type === "statement" || placeholder.type === "param") { + if (replacement == null) { + items.splice(index, 1); + } else if (Array.isArray(replacement)) { + items.splice(index, 1, ...replacement); + } else { + items[index] = replacement; + } + } else { + items[index] = replacement; + } + + t.validate(parent, key, items); + (parent: any)[key] = items; + } +} diff --git a/packages/babel-template/src/string.js b/packages/babel-template/src/string.js new file mode 100644 index 0000000000..3fbf7df419 --- /dev/null +++ b/packages/babel-template/src/string.js @@ -0,0 +1,23 @@ +// @flow +import type { Formatter } from "./formatters"; +import { normalizeReplacements, type TemplateOpts } from "./options"; +import parseAndBuildMetadata from "./parse"; +import populatePlaceholders from "./populate"; + +export default function stringTemplate( + formatter: Formatter, + code: string, + opts: TemplateOpts, +): mixed => T { + code = formatter.code(code); + + let metadata; + + return (arg?: mixed) => { + const replacements = normalizeReplacements(arg); + + if (!metadata) metadata = parseAndBuildMetadata(formatter, code, opts); + + return formatter.unwrap(populatePlaceholders(metadata, replacements)); + }; +} diff --git a/packages/babel-template/test/index.js b/packages/babel-template/test/index.js index b74b2abf99..f98f1509cd 100644 --- a/packages/babel-template/test/index.js +++ b/packages/babel-template/test/index.js @@ -1,34 +1,202 @@ import generator from "../../babel-generator"; import template from "../lib"; -import chai from "chai"; +import { expect } from "chai"; +import * as t from "babel-types"; const comments = "// Sum two numbers\nconst add = (a, b) => a + b;"; -describe("templating", function() { - it("import statement will cause parser to throw by default", function() { - chai - .expect(function() { - template("import foo from 'foo'")({}); - }) - .to.throw(); +describe("babel-template", function() { + it("import statements are allowed by default", function() { + expect(function() { + template("import foo from 'foo'")({}); + }).not.to.throw(); }); - it("import statements are allowed with sourceType: module", function() { - chai - .expect(function() { - template("import foo from 'foo'", { sourceType: "module" })({}); - }) - .not.to.throw(); + it("with statements are allowed with sourceType: script", function() { + expect(function() { + template("with({}){}", { sourceType: "script" })({}); + }).not.to.throw(); }); it("should strip comments by default", function() { const code = "const add = (a, b) => a + b;"; const output = template(comments)(); - chai.expect(generator(output).code).to.be.equal(code); + expect(generator(output).code).to.be.equal(code); }); it("should preserve comments with a flag", function() { const output = template(comments, { preserveComments: true })(); - chai.expect(generator(output).code).to.be.equal(comments); + expect(generator(output).code).to.be.equal(comments); + }); + + describe("string-based", () => { + it("should handle replacing values from an object", () => { + const value = t.stringLiteral("some string value"); + const result = template(` + if (SOME_VAR === "") {} + `)({ + SOME_VAR: value, + }); + + expect(result.type).to.equal("IfStatement"); + expect(result.test.type).to.equal("BinaryExpression"); + expect(result.test.left).to.equal(value); + }); + + it("should handle replacing values given an array", () => { + const value = t.stringLiteral("some string value"); + const result = template(` + if ($0 === "") {} + `)([value]); + + expect(result.type).to.equal("IfStatement"); + expect(result.test.type).to.equal("BinaryExpression"); + expect(result.test.left).to.equal(value); + }); + + it("should handle replacing values with null to remove them", () => { + const result = template(` + callee(ARG); + `)({ ARG: null }); + + expect(result.type).to.equal("ExpressionStatement"); + expect(result.expression.type).to.equal("CallExpression"); + expect(result.expression.arguments).to.eql([]); + }); + + it("should handle replacing values that are string content", () => { + const result = template(` + ("ARG"); + `)({ ARG: "some new content" }); + + expect(result.type).to.equal("ExpressionStatement"); + expect(result.expression.type).to.equal("StringLiteral"); + expect(result.expression.value).to.equal("some new content"); + }); + + it("should automatically clone nodes if they are injected twice", () => { + const id = t.identifier("someIdent"); + + const result = template(` + ID; + ID; + `)({ ID: id }); + + expect(result[0].type).to.equal("ExpressionStatement"); + expect(result[0].expression).to.equal(id); + expect(result[1].type).to.equal("ExpressionStatement"); + expect(result[1].expression).not.to.equal(id); + expect(result[1].expression).to.eql(id); + }); + + it("should allow passing in a whitelist of replacement names", () => { + const id = t.identifier("someIdent"); + const result = template( + ` + some_id; + `, + { placeholderWhitelist: new Set(["some_id"]) }, + )({ some_id: id }); + + expect(result.type).to.equal("ExpressionStatement"); + expect(result.expression).to.equal(id); + }); + + it("should allow passing in a RegExp to match replacement patterns", () => { + const id = t.identifier("someIdent"); + const result = template( + ` + ID; + ANOTHER_ID; + `, + { placeholderPattern: /^ID$/ }, + )({ ID: id }); + + expect(result[0].type).to.equal("ExpressionStatement"); + expect(result[0].expression).to.equal(id); + expect(result[1].type).to.equal("ExpressionStatement"); + expect(result[1].expression.type).to.equal("Identifier"); + expect(result[1].expression.name).to.equal("ANOTHER_ID"); + }); + + it("should throw if unknown replacements are provided", () => { + expect(() => { + template(` + ID; + `)({ ID: t.identifier("someIdent"), ANOTHER_ID: null }); + }).to.throw(Error, 'Unknown substitution "ANOTHER_ID" given'); + }); + + it("should throw if placeholders are not given explicit values", () => { + expect(() => { + template(` + ID; + ANOTHER_ID; + `)({ ID: t.identifier("someIdent") }); + }).to.throw(Error, 'No substitution given for "ANOTHER_ID"'); + }); + + it("should return the AST directly when using .ast", () => { + const result = template.ast(` + if ("some string value" === "") {} + `); + + expect(result.type).to.equal("IfStatement"); + expect(result.test.type).to.equal("BinaryExpression"); + expect(result.test.left.type).to.equal("StringLiteral"); + expect(result.test.left.value).to.equal("some string value"); + }); + }); + + describe("literal-based", () => { + it("should handle replacing values from an object", () => { + const value = t.stringLiteral("some string value"); + const result = template` + if (${value} === "") {} + `(); + + expect(result.type).to.equal("IfStatement"); + expect(result.test.type).to.equal("BinaryExpression"); + expect(result.test.left).to.equal(value); + }); + + it("should handle replacing values with null to remove them", () => { + const result = template` + callee(${null}); + `(); + + expect(result.type).to.equal("ExpressionStatement"); + expect(result.expression.type).to.equal("CallExpression"); + expect(result.expression.arguments).to.eql([]); + }); + + it("should handle replacing values that are string content", () => { + const result = template` + ("${"some new content"}"); + `(); + + expect(result.type).to.equal("ExpressionStatement"); + expect(result.expression.type).to.equal("StringLiteral"); + expect(result.expression.value).to.equal("some new content"); + }); + + it("should allow setting options by passing an object", () => { + const result = template({ sourceType: "script" })` + with({}){} + `(); + + expect(result.type).to.equal("WithStatement"); + }); + + it("should return the AST directly when using .ast", () => { + const value = t.stringLiteral("some string value"); + const result = template.ast` + if (${value} === "") {} + `; + + expect(result.type).to.equal("IfStatement"); + expect(result.test.type).to.equal("BinaryExpression"); + expect(result.test.left).to.equal(value); + }); }); }); diff --git a/packages/babel-template/test/template.js b/packages/babel-template/test/template.js deleted file mode 100644 index 95dcb9760e..0000000000 --- a/packages/babel-template/test/template.js +++ /dev/null @@ -1,183 +0,0 @@ -import generator from "../../babel-generator"; -import * as t from "@babel/types"; -import template from "../lib"; -import chai from "chai"; -const expect = chai.expect; - -describe("tagged templating", () => { - it("basic support", () => { - const tpl = template`("stringLiteral")`; - const result = tpl(); - - expect(result).to.be.ok; - expect(t.isStringLiteral(result.expression)).to.be.true; - }); - - describe("numeric interpolation", () => { - it("single replacement", () => { - const tpl = template`+${0}`; - const node = t.numericLiteral(123); - const result = tpl(node); - - expect(result).to.be.ok; - expect(t.isUnaryExpression(result.expression)).to.be.true; - expect(result.expression.argument).to.equal(node); - }); - - it("duplicate replacement", () => { - const tpl = template`${0} + ${0}`; - const node = t.numericLiteral(123); - const result = tpl(node); - - expect(result).to.be.ok; - expect(t.isBinaryExpression(result.expression)).to.be.true; - expect(result.expression.left).to.equal(node); - expect(result.expression.right).to.equal(result.expression.left); - }); - - it("multiple replacement", () => { - const tpl = template`${0}.${1}(${2})`; - const object = t.identifier("foo"); - const property = t.identifier("bar"); - const argument = t.numericLiteral(123); - const result = tpl(object, property, argument); - - expect(result).to.be.ok; - expect(t.isCallExpression(result.expression)).to.be.true; - - const { callee, arguments: args } = result.expression; - expect(t.isMemberExpression(callee)).to.be.true; - expect(callee.object).to.equal(object); - expect(callee.property).to.equal(property); - - expect(args).to.deep.equal([argument]); - }); - }); - - describe("string interpolation", () => { - it("has expected internal representation", () => { - const tpl = template`${"foo"}(${"b a r"})`; - expect(generator(tpl()).code).to.equal( - "$BABEL_TEMPLATE$$foo($BABEL_TEMPLATE$$bAR);", - ); - }); - - it("simple replacement", () => { - const tpl = template`${"foo"}(${"b a r"})`; - const arg = { - foo: t.identifier("baz"), - "b a r": t.numericLiteral(123), - }; - - const result = tpl(arg); - - expect(result).to.be.ok; - expect(t.isCallExpression(result.expression)).to.be.true; - - const { callee, arguments: args } = result.expression; - - expect(callee).to.equal(arg.foo); - expect(args).to.deep.equal([arg["b a r"]]); - }); - - it("does not conflict with similar identifiers", () => { - const tpl = template`foo + ${"foo"}`; - const arg = { - foo: t.identifier("foo"), - }; - - const result = tpl(arg); - - expect(result).to.be.ok; - expect(t.isBinaryExpression(result.expression)).to.be.true; - - const { left, right } = result.expression; - expect(left).to.not.equal(right); - expect(t.isIdentifier(left, { name: "foo" })).to.be.true; - - expect(right).to.equal(arg.foo); - }); - - it("does not conflict when t.toIdentifier conflicts", () => { - const tpl = template`${"fOO"} + ${"f o o"}`; - const arg = { - fOO: t.numericLiteral(123), - "f o o": t.numericLiteral(321), - }; - - const result = tpl(arg); - - expect(result).to.be.ok; - expect(t.isBinaryExpression(result.expression)).to.be.true; - - const { left, right } = result.expression; - expect(left).to.not.equal(right); - - expect(left).to.equal(arg.fOO); - expect(right).to.equal(arg["f o o"]); - }); - }); - - describe("mixed interpolation", () => { - it("throws when 0 is used", () => { - expect(() => template`${0} - ${"foo"}`).to.throw( - "Template cannot have a '0' replacement and a named replacement at the same time", - ); - }); - - it("works", () => { - const tpl = template`${1}.${"prop"}`; - const arg = { - prop: t.identifier("prop"), - }; - - const result = tpl(arg, t.thisExpression()); - - expect(result).to.be.ok; - expect(t.isMemberExpression(result.expression)).to.be.true; - - const { object, property } = result.expression; - - expect(t.isThisExpression(object)).to.be.true; - expect(property).to.equal(arg.prop); - }); - }); - - describe("Node interpolation", () => { - it("works", () => { - const node = t.identifier("foo"); - const tpl = template`${node}`; - - const result = tpl(); - - expect(result).to.be.ok; - expect(result.expression).to.equal(node); - }); - }); - - describe("options", () => { - it("works", () => { - const remove = template({ preserveComments: false })`// comment\nid;`; - const preserve = template({ preserveComments: true })`// comment\nid;`; - - const removeResult = remove(); - const preserveResult = preserve(); - - expect(removeResult); - expect(preserveResult).to.be.ok; - - // it exists, it just resets to undefined - expect(removeResult.leadingComments).to.be.undefined; - - expect(Array.isArray(preserveResult.leadingComments)).to.be.true; - expect(preserveResult.leadingComments[0]).to.have.property( - "type", - "CommentLine", - ); - expect(preserveResult.leadingComments[0]).to.have.property( - "value", - " comment", - ); - }); - }); -}); diff --git a/packages/babel-types/src/index.js b/packages/babel-types/src/index.js index 72bdb99560..172e2d4027 100644 --- a/packages/babel-types/src/index.js +++ b/packages/babel-types/src/index.js @@ -66,6 +66,9 @@ export { VISITOR_KEYS, ALIAS_KEYS, NODE_FIELDS, BUILDER_KEYS, DEPRECATED_KEYS }; import * as _react from "./react"; export { _react as react }; +import { traverse, traverseFast } from "./traverse"; +export { traverse, traverseFast }; + /** * Registers `is[Type]` and `assert[Type]` for all types. */ @@ -534,36 +537,6 @@ export function isNode(node?): boolean { toFastProperties(t); toFastProperties(t.VISITOR_KEYS); -/** - * A prefix AST traversal implementation implementation. - */ - -export function traverseFast( - node: Node, - enter: (node: Node) => void, - opts?: Object, -) { - if (!node) return; - - const keys = t.VISITOR_KEYS[node.type]; - if (!keys) return; - - opts = opts || {}; - enter(node, opts); - - for (const key of keys) { - const subNode = node[key]; - - if (Array.isArray(subNode)) { - for (const node of subNode) { - traverseFast(node, enter, opts); - } - } else { - traverseFast(subNode, enter, opts); - } - } -} - const CLEAR_KEYS: Array = ["tokens", "start", "end", "loc", "raw", "rawValue"]; const CLEAR_KEYS_PLUS_COMMENTS: Array = t.COMMENT_KEYS diff --git a/packages/babel-types/src/traverse.js b/packages/babel-types/src/traverse.js new file mode 100644 index 0000000000..c4983d1685 --- /dev/null +++ b/packages/babel-types/src/traverse.js @@ -0,0 +1,99 @@ +import { VISITOR_KEYS } from "./index"; + +/** + * A prefix AST traversal implementation meant for simple searching + * and processing. + */ +export function traverseFast( + node: Node, + enter: (node: Node) => void, + opts?: Object, +) { + if (!node) return; + + const keys = VISITOR_KEYS[node.type]; + if (!keys) return; + + opts = opts || {}; + enter(node, opts); + + for (const key of keys) { + const subNode = node[key]; + + if (Array.isArray(subNode)) { + for (const node of subNode) { + traverseFast(node, enter, opts); + } + } else { + traverseFast(subNode, enter, opts); + } + } +} + +export type TraversalAncestors = Array<{ + node: BabelNode, + key: string, + index?: number, +}>; +export type TraversalHandler = (BabelNode, TraversalAncestors, T) => void; +export type TraversalHandlers = { + enter?: TraversalHandler, + exit?: TraversalHandler, +}; + +/** + * A general AST traversal with both prefix and postfix handlers, and a + * state object. Exposes ancestry data to each handler so that more complex + * AST data can be taken into account. + */ +export function traverse( + node: BabelNode, + handlers: TraversalHandler | TraversalHandlers, + state?: T, +) { + if (typeof handlers === "function") { + handlers = { enter: handlers }; + } + + const { enter, exit } = handlers; + + traverseSimpleImpl(node, enter, exit, state, []); +} +function traverseSimpleImpl(node, enter, exit, state, ancestors) { + const keys = VISITOR_KEYS[node.type]; + if (!keys) return; + + if (enter) enter(node, ancestors, state); + + for (const key of keys) { + const subNode = node[key]; + + if (Array.isArray(subNode)) { + for (let i = 0; i < subNode.length; i++) { + const child = subNode[i]; + if (!child) continue; + + ancestors.push({ + node, + key, + index: i, + }); + + traverseSimpleImpl(child, enter, exit, state, ancestors); + + ancestors.pop(); + } + } else if (subNode) { + ancestors.push({ + node, + key, + }); + + traverseSimpleImpl(subNode, enter, exit, state, ancestors); + + ancestors.pop(); + } + } + + if (exit) exit(node, ancestors, state); +} diff --git a/scripts/generate-interfaces.js b/scripts/generate-interfaces.js index 49c796d497..88552d5611 100644 --- a/scripts/generate-interfaces.js +++ b/scripts/generate-interfaces.js @@ -124,11 +124,37 @@ for (const type in t.NODE_FIELDS) { } for (let i = 0; i < t.TYPES.length; i++) { - lines.push( - `declare function is${t.TYPES[i]}(node: Object, opts?: Object): boolean;` - ); + let decl = `declare function is${t.TYPES[ + i + ]}(node: Object, opts?: ?Object): boolean`; + + if (t.NODE_FIELDS[t.TYPES[i]]) { + decl += ` %checks (node instanceof ${NODE_PREFIX}${t.TYPES[i]})`; + } + + lines.push(decl); } +lines.push( + `declare function validate(n: BabelNode, key: string, value: mixed): void;`, + `declare function clone(n: T): T;`, + `declare function cloneDeep(n: T): T;`, + `declare function removeProperties(n: T, opts: ?{}): void;`, + `declare function removePropertiesDeep(n: T, opts: ?{}): T;`, + `declare type TraversalAncestors = Array<{ + node: BabelNode, + key: string, + index?: number, + }>; + declare type TraversalHandler = (BabelNode, TraversalAncestors, T) => void; + declare type TraversalHandlers = { + enter?: TraversalHandler, + exit?: TraversalHandler, + };`.replace(/(^|\n) {2}/g, "$1"), + // eslint-disable-next-line + `declare function traverse(n: BabelNode, TraversalHandler | TraversalHandlers, state?: T): void;` +); + for (const type in t.FLIPPED_ALIAS_KEYS) { const types = t.FLIPPED_ALIAS_KEYS[type]; code += `type ${NODE_PREFIX}${type} = ${types