fix(compiler-cli): merge @fileoverview comments. (#20870)
Previously, this code would unconditionally add a @fileoverview comment to generated files, and only if the contained any code at all. However often existing fileoverview comments should be copied from the file the generated file was originally based off of. This allows users to e.g. include Closure Compiler directives in their original `component.ts` file, which will then automaticallly also apply to code generated from it. This special cases `@license` comments, as Closure disregards directives in comments containing `@license`. PR Close #20870
This commit is contained in:

committed by
Jason Aden

parent
add3589451
commit
8c52088346
@ -12,26 +12,68 @@ import * as ts from 'typescript';
|
||||
import {TypeScriptNodeEmitter} from './node_emitter';
|
||||
import {GENERATED_FILES} from './util';
|
||||
|
||||
const PREAMBLE = `/**
|
||||
* @fileoverview This file was generated by the Angular template compiler.
|
||||
* Do not edit.
|
||||
* @suppress {suspiciousCode,uselessCode,missingProperties,missingOverride,checkTypes}
|
||||
* tslint:disable
|
||||
*/`;
|
||||
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
|
||||
*/`;
|
||||
}
|
||||
|
||||
export function getAngularEmitterTransformFactory(generatedFiles: Map<string, GeneratedFile>): () =>
|
||||
/**
|
||||
* 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<string, GeneratedFile>, 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 [newSourceFile] = emitter.updateSourceFile(sourceFile, g.stmts, PREAMBLE);
|
||||
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)) {
|
||||
return ts.updateSourceFileNode(sourceFile, []);
|
||||
// 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(/ ?\*\/$/, '');
|
||||
}
|
||||
|
Reference in New Issue
Block a user