/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {GeneratedFile} from '@angular/compiler'; import * as ts from 'typescript'; import {TypeScriptNodeEmitter} from './node_emitter'; import {GENERATED_FILES} from './util'; function getPreamble(original: string) { return `/** * @fileoverview This file was generated by the Angular template compiler. Do not edit. * ${original} * @suppress {suspiciousCode,uselessCode,missingProperties,missingOverride,checkTypes} * tslint:disable */`; } /** * Returns a transformer that does two things for generated files (ngfactory etc): * - adds a fileoverview JSDoc comment containing Closure Compiler specific "suppress"ions in JSDoc. * The new comment will contain any fileoverview comment text from the original source file this * file was generated from. * - updates generated files that are not in the given map of generatedFiles to have an empty * list of statements as their body. */ export function getAngularEmitterTransformFactory( generatedFiles: Map, program: ts.Program): () => (sourceFile: ts.SourceFile) => ts.SourceFile { return function() { const emitter = new TypeScriptNodeEmitter(); return function(sourceFile: ts.SourceFile): ts.SourceFile { const g = generatedFiles.get(sourceFile.fileName); const orig = g && program.getSourceFile(g.srcFileUrl); let originalComment = ''; if (orig) originalComment = getFileoverviewComment(orig); const preamble = getPreamble(originalComment); if (g && g.stmts) { const orig = program.getSourceFile(g.srcFileUrl); let originalComment = ''; if (orig) originalComment = getFileoverviewComment(orig); const [newSourceFile] = emitter.updateSourceFile(sourceFile, g.stmts, preamble); return newSourceFile; } else if (GENERATED_FILES.test(sourceFile.fileName)) { // The file should be empty, but emitter.updateSourceFile would still add imports // and various minutiae. // Clear out the source file entirely, only including the preamble comment, so that // ngc produces an empty .js file. return ts.updateSourceFileNode( sourceFile, [emitter.createCommentStatement(sourceFile, preamble)]); } return sourceFile; }; }; } /** * Parses and returns the comment text (without start and end markers) of a \@fileoverview comment * in the given source file. Returns the empty string if no such comment can be found. */ function getFileoverviewComment(sourceFile: ts.SourceFile): string { const trivia = sourceFile.getFullText().substring(0, sourceFile.getStart()); const leadingComments = ts.getLeadingCommentRanges(trivia, 0); if (!leadingComments || leadingComments.length === 0) return ''; const comment = leadingComments[0]; if (comment.kind !== ts.SyntaxKind.MultiLineCommentTrivia) return ''; // Only comments separated with a \n\n from the file contents are considered file-level comments // in TypeScript. if (sourceFile.getFullText().substring(comment.end, comment.end + 2) !== '\n\n') return ''; const commentText = sourceFile.getFullText().substring(comment.pos, comment.end); // Closure Compiler ignores @suppress and similar if the comment contains @license. if (commentText.indexOf('@license') !== -1) return ''; return commentText.replace(/^\/\*\*/, '').replace(/ ?\*\/$/, ''); }