/** * Adds extra imports in the import manage for this source file, after the existing imports * and before the module body. * Can optionally add extra statements (e.g. new constants) before the body as well. */ function addImports(importManager, sf, extraStatements) { if (extraStatements === void 0) { extraStatements = []; } // Generate the import statements to prepend. var addedImports = importManager.getAllImports(sf.fileName).map(function (i) { var qualifier = ts.createIdentifier(i.qualifier); var importClause = ts.createImportClause( /* name */ undefined, /* namedBindings */ ts.createNamespaceImport(qualifier)); return ts.createImportDeclaration( /* decorators */ undefined, /* modifiers */ undefined, /* importClause */ importClause, /* moduleSpecifier */ ts.createLiteral(i.specifier)); }); // Filter out the existing imports and the source file body. All new statements // will be inserted between them. var existingImports = sf.statements.filter(function (stmt) { return isImportStatement(stmt); }); var body = sf.statements.filter(function (stmt) { return !isImportStatement(stmt); }); // Prepend imports if needed. if (addedImports.length > 0) { // If we prepend imports, we also prepend NotEmittedStatement to use it as an anchor // for @fileoverview Closure annotation. If there is no @fileoverview annotations, this // statement would be a noop. var fileoverviewAnchorStmt = ts.createNotEmittedStatement(sf); sf.statements = ts.createNodeArray(tslib_1.__spread([fileoverviewAnchorStmt], existingImports, addedImports, extraStatements, body)); } return sf; }
IvyVisitor.prototype._nonCoreDecoratorsOnly = function (node) { // Shortcut if the node has no decorators. if (node.decorators === undefined) { return undefined; } // Build a Set of the decorators on this node from @angular/core. var coreDecorators = this._angularCoreDecorators(node); if (coreDecorators.size === node.decorators.length) { // If all decorators are to be removed, return `undefined`. return undefined; } else if (coreDecorators.size === 0) { // If no decorators need to be removed, return the original decorators array. return node.decorators; } // Filter out the core decorators. var filtered = node.decorators.filter(function (dec) { return !coreDecorators.has(dec); }); // If no decorators survive, return `undefined`. This can only happen if a core decorator is // repeated on the node. if (filtered.length === 0) { return undefined; } // Create a new `NodeArray` with the filtered decorators that sourcemaps back to the original. var array = ts.createNodeArray(filtered); array.pos = node.decorators.pos; array.end = node.decorators.end; return array; };
exports.getComponentDecorator = function (declaration) { return ts.createNodeArray(declaration.decorators).find(function (d) { return (ts.isCallExpression(d.expression) && d.expression.arguments && d.expression.arguments.length > 0 && exports.getDecoratorName(d) === 'Component'); }); };
exports.getDecoratorPropertyInitializer = function (decorator, name) { var args = ts.isCallExpression(decorator.expression) ? decorator.expression.arguments[0] : undefined; var properties = ts.createNodeArray(args && ts.isObjectLiteralExpression(args) ? args.properties : undefined); return properties .filter(function (prop) { return prop.name && ts.isIdentifier(prop.name) && prop.name.text === name; }) .map(function (prop) { return (ts.isPropertyAssignment(prop) ? prop.initializer : undefined); }) .pop(); };
/** * For each property in the object literal, if it's templateUrl or styleUrls, replace it * with content. * @param node the arguments to @Component() or args property of decorators: [{type:Component}] * @param loader provides access to the loadResource method of the host * @returns updated arguments */ function updateComponentProperties(args, loader) { if (args.length !== 1) { // User should have gotten a type-check error because @Component takes one argument return args; } var componentArg = args[0]; if (!ts.isObjectLiteralExpression(componentArg)) { // User should have gotten a type-check error because @Component takes an object literal // argument return args; } var newProperties = []; var newStyleExprs = []; componentArg.properties.forEach(function (prop) { if (!ts.isPropertyAssignment(prop) || ts.isComputedPropertyName(prop.name)) { newProperties.push(prop); return; } switch (prop.name.text) { case 'styles': if (!ts.isArrayLiteralExpression(prop.initializer)) { throw new Error('styles takes an array argument'); } newStyleExprs.push.apply(newStyleExprs, tslib_1.__spread(prop.initializer.elements)); break; case 'styleUrls': if (!ts.isArrayLiteralExpression(prop.initializer)) { throw new Error('styleUrls takes an array argument'); } newStyleExprs.push.apply(newStyleExprs, tslib_1.__spread(prop.initializer.elements.map(function (expr) { if (!ts.isStringLiteral(expr) && !ts.isNoSubstitutionTemplateLiteral(expr)) { throw new Error('Can only accept string literal arguments to styleUrls. ' + PRECONDITIONS_TEXT); } var styles = loader.get(expr.text); return ts.createLiteral(styles); }))); break; case 'templateUrl': if (!ts.isStringLiteral(prop.initializer) && !ts.isNoSubstitutionTemplateLiteral(prop.initializer)) { throw new Error('Can only accept a string literal argument to templateUrl. ' + PRECONDITIONS_TEXT); } var template = loader.get(prop.initializer.text); newProperties.push(ts.updatePropertyAssignment(prop, ts.createIdentifier('template'), ts.createLiteral(template))); break; default: newProperties.push(prop); } }); // Add the non-inline styles if (newStyleExprs.length > 0) { var newStyles = ts.createPropertyAssignment(ts.createIdentifier('styles'), ts.createArrayLiteral(newStyleExprs)); newProperties.push(newStyles); } return ts.createNodeArray([ts.updateObjectLiteral(componentArg, newProperties)]); }
function maybeFilterDecorator(decorators, toRemove) { if (decorators === undefined) { return undefined; } var filtered = decorators.filter(function (dec) { return toRemove.find(function (decToRemove) { return ts.getOriginalNode(dec) === decToRemove; }) === undefined; }); if (filtered.length === 0) { return undefined; } return ts.createNodeArray(filtered); }
NgWalker.prototype.visitClassDeclaration = function (declaration) { var metadata = this._metadataReader.read(declaration); if (metadata instanceof metadata_1.ComponentMetadata) { this.visitNgComponent(metadata); } else if (metadata instanceof metadata_1.DirectiveMetadata) { this.visitNgDirective(metadata); } utils_1.maybeNodeArray(ts.createNodeArray(declaration.decorators)).forEach(this.visitClassDecorator.bind(this)); _super.prototype.visitClassDeclaration.call(this, declaration); };
/** * A transformer which operates on ts.SourceFiles and applies changes from an `IvyCompilation`. */ function transformIvySourceFile(compilation, context, reflector, coreImportsFrom, file) { var importManager = new translator_1.ImportManager(coreImportsFrom !== null); // Recursively scan through the AST and perform any updates requested by the IvyCompilation. var sf = visitor_1.visit(file, new IvyVisitor(compilation, reflector, importManager, coreImportsFrom !== null), context); // Generate the import statements to prepend. var imports = importManager.getAllImports(file.fileName, coreImportsFrom).map(function (i) { return ts.createImportDeclaration(undefined, undefined, ts.createImportClause(undefined, ts.createNamespaceImport(ts.createIdentifier(i.as))), ts.createLiteral(i.name)); }); // Prepend imports if needed. if (imports.length > 0) { sf.statements = ts.createNodeArray(tslib_1.__spread(imports, sf.statements)); } return sf; }
function transformFactorySourceFile(factoryMap, context, coreImportsFrom, file) { // If this is not a generated file, it won't have factory info associated with it. if (!factoryMap.has(file.fileName)) { // Don't transform non-generated code. return file; } var _a = factoryMap.get(file.fileName), moduleSymbolNames = _a.moduleSymbolNames, sourceFilePath = _a.sourceFilePath; var clone = ts.getMutableClone(file); var transformedStatements = file.statements.map(function (stmt) { if (coreImportsFrom !== null && ts.isImportDeclaration(stmt) && ts.isStringLiteral(stmt.moduleSpecifier) && stmt.moduleSpecifier.text === '@angular/core') { var path = path_1.relativePathBetween(sourceFilePath, coreImportsFrom.fileName); if (path !== null) { return ts.updateImportDeclaration(stmt, stmt.decorators, stmt.modifiers, stmt.importClause, ts.createStringLiteral(path)); } else { return ts.createNotEmittedStatement(stmt); } } else if (ts.isVariableStatement(stmt) && stmt.declarationList.declarations.length === 1) { var decl = stmt.declarationList.declarations[0]; if (ts.isIdentifier(decl.name)) { var match = STRIP_NG_FACTORY.exec(decl.name.text); if (match === null || !moduleSymbolNames.has(match[1])) { // Remove the given factory as it wasn't actually for an NgModule. return ts.createNotEmittedStatement(stmt); } } return stmt; } else { return stmt; } }); if (!transformedStatements.some(ts.isVariableStatement)) { // If the resulting file has no factories, include an empty export to // satisfy closure compiler. transformedStatements.push(ts.createVariableStatement([ts.createModifier(ts.SyntaxKind.ExportKeyword)], ts.createVariableDeclarationList([ts.createVariableDeclaration('ɵNonEmptyModule', undefined, ts.createTrue())], ts.NodeFlags.Const))); } clone.statements = ts.createNodeArray(transformedStatements); return clone; }
/** * Convert leading/trailing detached comment ranges of statement arrays * (e.g. the statements of a ts.SourceFile or ts.Block) into * `ts.NonEmittedStatement`s with `ts.SynthesizedComment`s and * prepends / appends them to the given statement array. * This is needed to allow changing these comments. * * This function takes a visitor to be able to do some * state management after the caller is done changing a node. */ function visitNodeStatementsWithSynthesizedComments(context, sourceFile, node, statements, visitor) { var leading = synthesizeDetachedLeadingComments(sourceFile, node, statements); var trailing = synthesizeDetachedTrailingComments(sourceFile, node, statements); if (leading.commentStmt || trailing.commentStmt) { statements = ts.setTextRange(ts.createNodeArray(statements), { pos: -1, end: -1 }); if (leading.commentStmt) { statements.unshift(leading.commentStmt); } if (trailing.commentStmt) { statements.push(trailing.commentStmt); } var fileContext = assertFileContext(context, sourceFile); if (leading.lastCommentEnd !== -1) { fileContext.lastCommentEnd = leading.lastCommentEnd; } node = visitor(node, statements); if (trailing.lastCommentEnd !== -1) { fileContext.lastCommentEnd = trailing.lastCommentEnd; } return node; } return visitor(node, statements); }
function visitBlockStatements(statements, context) { // copy of statements to modify; lazy initialized let updatedStatements; const visitor = (node) => { if (isBlockLike(node)) { const result = visitBlockStatements(node.statements, context); if (result === node.statements) { return node; } switch (node.kind) { case ts.SyntaxKind.Block: return ts.updateBlock(node, result); case ts.SyntaxKind.ModuleBlock: return ts.updateModuleBlock(node, result); case ts.SyntaxKind.CaseClause: const clause = node; return ts.updateCaseClause(clause, clause.expression, result); case ts.SyntaxKind.DefaultClause: return ts.updateDefaultClause(node, result); default: return node; } } else { return ts.visitEachChild(node, visitor, context); } }; // 'oIndex' is the original statement index; 'uIndex' is the updated statement index for (let oIndex = 0, uIndex = 0; oIndex < statements.length; oIndex++, uIndex++) { const currentStatement = statements[oIndex]; // these can't contain an enum declaration if (currentStatement.kind === ts.SyntaxKind.ImportDeclaration) { continue; } // enum declarations must: // * not be last statement // * be a variable statement // * have only one declaration // * have an identifer as a declaration name if (oIndex < statements.length - 1 && isVariableStatement(currentStatement) && currentStatement.declarationList.declarations.length === 1) { const variableDeclaration = currentStatement.declarationList.declarations[0]; if (isIdentifier(variableDeclaration.name)) { const name = variableDeclaration.name.text; if (!variableDeclaration.initializer) { const enumStatements = findTs2_3EnumStatements(name, statements[oIndex + 1]); if (enumStatements.length > 0) { // found an enum if (!updatedStatements) { updatedStatements = statements.slice(); } // create wrapper and replace variable statement and IIFE updatedStatements.splice(uIndex, 2, createWrappedEnum(name, currentStatement, enumStatements, undefined)); // skip IIFE statement oIndex++; continue; } } else if (isObjectLiteralExpression(variableDeclaration.initializer) && variableDeclaration.initializer.properties.length === 0) { const nextStatements = statements.slice(oIndex + 1); const enumStatements = findTs2_2EnumStatements(name, nextStatements); if (enumStatements.length > 0) { // found an enum if (!updatedStatements) { updatedStatements = statements.slice(); } // create wrapper and replace variable statement and enum member statements updatedStatements.splice(uIndex, enumStatements.length + 1, createWrappedEnum(name, currentStatement, enumStatements, variableDeclaration.initializer)); // skip enum member declarations oIndex += enumStatements.length; continue; } } else if (isObjectLiteralExpression(variableDeclaration.initializer) && variableDeclaration.initializer.properties.length !== 0) { const literalPropertyCount = variableDeclaration.initializer.properties.length; const nextStatements = statements.slice(oIndex + 1); const enumStatements = findTsickleEnumStatements(name, nextStatements); if (enumStatements.length === literalPropertyCount) { // found an enum if (!updatedStatements) { updatedStatements = statements.slice(); } // create wrapper and replace variable statement and enum member statements updatedStatements.splice(uIndex, enumStatements.length + 1, createWrappedEnum(name, currentStatement, enumStatements, variableDeclaration.initializer)); // skip enum member declarations oIndex += enumStatements.length; continue; } } } } const result = ts.visitNode(currentStatement, visitor); if (result !== currentStatement) { if (!updatedStatements) { updatedStatements = statements.slice(); } updatedStatements[uIndex] = result; } } // if changes, return updated statements // otherwise, return original array instance return updatedStatements ? ts.createNodeArray(updatedStatements) : statements; }
function visitSourceFile(sourceFile) { function topLevelStatement(node) { var declarations = []; function visitNode(node) { // Get the original node before tsickle var _a = ts.getOriginalNode(node), pos = _a.pos, end = _a.end, kind = _a.kind, originalParent = _a.parent; var nodeRequest = requests.get(pos); if (nodeRequest && nodeRequest.kind == kind && nodeRequest.end == end) { // This node is requested to be rewritten as a reference to the exported name. if (originalParent && originalParent.kind === ts.SyntaxKind.VariableDeclaration) { // As the value represents the whole initializer of a variable declaration, // just refer to that variable. This e.g. helps to preserve closure comments // at the right place. var varParent = originalParent; if (varParent.name.kind === ts.SyntaxKind.Identifier) { var varName = varParent.name.text; var exportName_1 = nodeRequest.name; declarations.push({ name: exportName_1, node: ts.createIdentifier(varName), order: 1 /* AfterStmt */ }); return node; } } // Record that the node needs to be moved to an exported variable with the given name var exportName = nodeRequest.name; declarations.push({ name: exportName, node: node, order: 0 /* BeforeStmt */ }); return ts.createIdentifier(exportName); } var result = node; if (shouldVisit(pos, end) && !isLexicalScope(node)) { result = ts.visitEachChild(node, visitNode, context); } return result; } // Get the original node before tsickle var _a = ts.getOriginalNode(node), pos = _a.pos, end = _a.end; var resultStmt; if (shouldVisit(pos, end)) { resultStmt = ts.visitEachChild(node, visitNode, context); } else { resultStmt = node; } if (declarations.length) { inserts.push({ relativeTo: resultStmt, declarations: declarations }); } return resultStmt; } var newStatements = sourceFile.statements.map(topLevelStatement); if (inserts.length) { // Insert the declarations relative to the rewritten statement that references them. var insertMap_1 = toMap(inserts, function (i) { return i.relativeTo; }); var tmpStatements_1 = []; newStatements.forEach(function (statement) { var insert = insertMap_1.get(statement); if (insert) { var before = insert.declarations.filter(function (d) { return d.order === 0 /* BeforeStmt */; }); if (before.length) { tmpStatements_1.push(createVariableStatementForDeclarations(before)); } tmpStatements_1.push(statement); var after = insert.declarations.filter(function (d) { return d.order === 1 /* AfterStmt */; }); if (after.length) { tmpStatements_1.push(createVariableStatementForDeclarations(after)); } } else { tmpStatements_1.push(statement); } }); // Insert an exports clause to export the declarations tmpStatements_1.push(ts.createExportDeclaration( /* decorators */ undefined, /* modifiers */ undefined, ts.createNamedExports(inserts .reduce(function (accumulator, insert) { return accumulator.concat(insert.declarations); }, []) .map(function (declaration) { return ts.createExportSpecifier( /* propertyName */ undefined, declaration.name); })))); newStatements = tmpStatements_1; } // Note: We cannot use ts.updateSourcefile here as // it does not work well with decorators. // See https://github.com/Microsoft/TypeScript/issues/17384 var newSf = ts.getMutableClone(sourceFile); if (!(sourceFile.flags & ts.NodeFlags.Synthesized)) { newSf.flags &= ~ts.NodeFlags.Synthesized; } newSf.statements = ts.setTextRange(ts.createNodeArray(newStatements), sourceFile.statements); return newSf; }
NgWalker.prototype.visitPropertyDeclaration = function (prop) { utils_1.maybeNodeArray(ts.createNodeArray(prop.decorators)).forEach(this.visitPropertyDecorator.bind(this)); _super.prototype.visitPropertyDeclaration.call(this, prop); };
NgWalker.prototype.visitMethodDeclaration = function (method) { utils_1.maybeNodeArray(ts.createNodeArray(method.decorators)).forEach(this.visitMethodDecorator.bind(this)); _super.prototype.visitMethodDeclaration.call(this, method); };
var getDecoratorStringArgs = function (decorator) { var expression = decorator.expression; var args = ts.isCallExpression(expression) ? expression.arguments : ts.createNodeArray(); return args.filter(ts.isStringLiteral).map(function (x) { return x.text; }); };