Extract printer from emitter for reusability.

This commit is contained in:
Ron Buckton 2017-01-26 15:46:26 -08:00
parent 64dd8065aa
commit 028e4e2b13
21 changed files with 2939 additions and 2850 deletions

View file

@ -84,6 +84,7 @@ var compilerSources = [
"sourcemap.ts", "sourcemap.ts",
"comments.ts", "comments.ts",
"declarationEmitter.ts", "declarationEmitter.ts",
"printer.ts",
"emitter.ts", "emitter.ts",
"program.ts", "program.ts",
"commandLineParser.ts", "commandLineParser.ts",
@ -121,6 +122,7 @@ var servicesSources = [
"sourcemap.ts", "sourcemap.ts",
"comments.ts", "comments.ts",
"declarationEmitter.ts", "declarationEmitter.ts",
"printer.ts",
"emitter.ts", "emitter.ts",
"program.ts", "program.ts",
"commandLineParser.ts", "commandLineParser.ts",

View file

@ -5,17 +5,13 @@ namespace ts {
export interface CommentWriter { export interface CommentWriter {
reset(): void; reset(): void;
setSourceFile(sourceFile: SourceFile): void; setSourceFile(sourceFile: SourceFile): void;
emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void; emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void;
emitTrailingCommentsOfPosition(pos: number): void; emitTrailingCommentsOfPosition(pos: number): void;
} }
export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter { export function createCommentWriter(writer: EmitTextWriter, compilerOptions: CompilerOptions, newLine: string, emitPos: (pos: number) => void): CommentWriter {
const compilerOptions = host.getCompilerOptions();
const extendedDiagnostics = compilerOptions.extendedDiagnostics; const extendedDiagnostics = compilerOptions.extendedDiagnostics;
const newLine = host.getNewLine();
const { emitPos } = sourceMap;
let containerPos = -1; let containerPos = -1;
let containerEnd = -1; let containerEnd = -1;
let declarationListContainerEnd = -1; let declarationListContainerEnd = -1;
@ -34,9 +30,9 @@ namespace ts {
emitTrailingCommentsOfPosition, emitTrailingCommentsOfPosition,
}; };
function emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { function emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (disabled) { if (disabled) {
emitCallback(emitContext, node); emitCallback(hint, node);
return; return;
} }
@ -47,11 +43,11 @@ namespace ts {
// Both pos and end are synthesized, so just emit the node without comments. // Both pos and end are synthesized, so just emit the node without comments.
if (emitFlags & EmitFlags.NoNestedComments) { if (emitFlags & EmitFlags.NoNestedComments) {
disabled = true; disabled = true;
emitCallback(emitContext, node); emitCallback(hint, node);
disabled = false; disabled = false;
} }
else { else {
emitCallback(emitContext, node); emitCallback(hint, node);
} }
} }
else { else {
@ -94,11 +90,11 @@ namespace ts {
if (emitFlags & EmitFlags.NoNestedComments) { if (emitFlags & EmitFlags.NoNestedComments) {
disabled = true; disabled = true;
emitCallback(emitContext, node); emitCallback(hint, node);
disabled = false; disabled = false;
} }
else { else {
emitCallback(emitContext, node); emitCallback(hint, node);
} }
if (extendedDiagnostics) { if (extendedDiagnostics) {

View file

@ -35,13 +35,15 @@ namespace ts {
forEachEmittedFile(host, getDeclarationDiagnosticsFromFile, targetSourceFile); forEachEmittedFile(host, getDeclarationDiagnosticsFromFile, targetSourceFile);
return declarationDiagnostics.getDiagnostics(targetSourceFile ? targetSourceFile.fileName : undefined); return declarationDiagnostics.getDiagnostics(targetSourceFile ? targetSourceFile.fileName : undefined);
function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sources: SourceFile[], isBundledEmit: boolean) { function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) {
emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sources, isBundledEmit, /*emitOnlyDtsFiles*/ false); emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sourceFileOrBundle, /*emitOnlyDtsFiles*/ false);
} }
} }
function emitDeclarations(host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, declarationFilePath: string, function emitDeclarations(host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, declarationFilePath: string,
sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean): DeclarationEmit { sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean): DeclarationEmit {
const sourceFiles = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle.sourceFiles : [sourceFileOrBundle];
const isBundledEmit = sourceFileOrBundle.kind === SyntaxKind.Bundle;
const newLine = host.getNewLine(); const newLine = host.getNewLine();
const compilerOptions = host.getCompilerOptions(); const compilerOptions = host.getCompilerOptions();
@ -1803,8 +1805,9 @@ namespace ts {
} }
return addedBundledEmitReference; return addedBundledEmitReference;
function getDeclFileName(emitFileNames: EmitFileNames, _sourceFiles: SourceFile[], isBundledEmit: boolean) { function getDeclFileName(emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) {
// Dont add reference path to this file if it is a bundled emit and caller asked not emit bundled file path // Dont add reference path to this file if it is a bundled emit and caller asked not emit bundled file path
const isBundledEmit = sourceFileOrBundle.kind === SyntaxKind.Bundle;
if (isBundledEmit && !addBundledFileReference) { if (isBundledEmit && !addBundledFileReference) {
return; return;
} }
@ -1817,10 +1820,11 @@ namespace ts {
} }
/* @internal */ /* @internal */
export function writeDeclarationFile(declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) { export function writeDeclarationFile(declarationFilePath: string, sourceFileOrBundle: SourceFile | Bundle, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) {
const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFiles, isBundledEmit, emitOnlyDtsFiles); const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFileOrBundle, emitOnlyDtsFiles);
const emitSkipped = emitDeclarationResult.reportedDeclarationError || host.isEmitBlocked(declarationFilePath) || host.getCompilerOptions().noEmit; const emitSkipped = emitDeclarationResult.reportedDeclarationError || host.isEmitBlocked(declarationFilePath) || host.getCompilerOptions().noEmit;
if (!emitSkipped) { if (!emitSkipped) {
const sourceFiles = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle.sourceFiles : [sourceFileOrBundle];
const declarationOutput = emitDeclarationResult.referencesOutput const declarationOutput = emitDeclarationResult.referencesOutput
+ getDeclarationOutput(emitDeclarationResult.synchronousDeclarationOutput, emitDeclarationResult.moduleElementDeclarationEmitInfo); + getDeclarationOutput(emitDeclarationResult.synchronousDeclarationOutput, emitDeclarationResult.moduleElementDeclarationEmitInfo);
writeFile(host, emitterDiagnostics, declarationFilePath, declarationOutput, host.getCompilerOptions().emitBOM, sourceFiles); writeFile(host, emitterDiagnostics, declarationFilePath, declarationOutput, host.getCompilerOptions().emitBOM, sourceFiles);

File diff suppressed because it is too large Load diff

View file

@ -1530,6 +1530,19 @@ namespace ts {
return node; return node;
} }
export function createBundle(sourceFiles: SourceFile[]) {
const node = <Bundle>createNode(SyntaxKind.Bundle);
node.sourceFiles = sourceFiles;
return node;
}
export function updateBundle(node: Bundle, sourceFiles: SourceFile[]) {
if (node.sourceFiles !== sourceFiles) {
return createBundle(sourceFiles);
}
return node;
}
// Compound nodes // Compound nodes
export function createComma(left: Expression, right: Expression) { export function createComma(left: Expression, right: Expression) {

2659
src/compiler/printer.ts Normal file

File diff suppressed because it is too large Load diff

View file

@ -8,10 +8,9 @@ namespace ts {
* *
* @param filePath The path to the generated output file. * @param filePath The path to the generated output file.
* @param sourceMapFilePath The path to the output source map file. * @param sourceMapFilePath The path to the output source map file.
* @param sourceFiles The input source files for the program. * @param sourceFileOrBundle The input source file or bundle for the program.
* @param isBundledEmit A value indicating whether the generated output file is a bundle.
*/ */
initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void; initialize(filePath: string, sourceMapFilePath: string, sourceFileOrBundle: SourceFile | Bundle): void;
/** /**
* Reset the SourceMapWriter to an empty state. * Reset the SourceMapWriter to an empty state.
@ -38,11 +37,11 @@ namespace ts {
/** /**
* Emits a node with possible leading and trailing source maps. * Emits a node with possible leading and trailing source maps.
* *
* @param emitContext The current emit context * @param hint The current emit context
* @param node The node to emit. * @param node The node to emit.
* @param emitCallback The callback used to emit the node. * @param emitCallback The callback used to emit the node.
*/ */
emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; emitNodeWithSourceMap(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
/** /**
* Emits a token of a node node with possible leading and trailing source maps. * Emits a token of a node node with possible leading and trailing source maps.
@ -115,10 +114,9 @@ namespace ts {
* *
* @param filePath The path to the generated output file. * @param filePath The path to the generated output file.
* @param sourceMapFilePath The path to the output source map file. * @param sourceMapFilePath The path to the output source map file.
* @param sourceFiles The input source files for the program. * @param sourceFileOrBundle The input source file or bundle for the program.
* @param isBundledEmit A value indicating whether the generated output file is a bundle.
*/ */
function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { function initialize(filePath: string, sourceMapFilePath: string, sourceFileOrBundle: SourceFile | Bundle) {
if (disabled) { if (disabled) {
return; return;
} }
@ -161,11 +159,10 @@ namespace ts {
if (compilerOptions.mapRoot) { if (compilerOptions.mapRoot) {
sourceMapDir = normalizeSlashes(compilerOptions.mapRoot); sourceMapDir = normalizeSlashes(compilerOptions.mapRoot);
if (!isBundledEmit) { // emitting single module file if (sourceFileOrBundle.kind === SyntaxKind.SourceFile) { // emitting single module file
Debug.assert(sourceFiles.length === 1);
// For modules or multiple emit files the mapRoot will have directory structure like the sources // For modules or multiple emit files the mapRoot will have directory structure like the sources
// So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map // So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map
sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(sourceFiles[0], host, sourceMapDir)); sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(sourceFileOrBundle, host, sourceMapDir));
} }
if (!isRootedDiskPath(sourceMapDir) && !isUrl(sourceMapDir)) { if (!isRootedDiskPath(sourceMapDir) && !isUrl(sourceMapDir)) {
@ -311,12 +308,13 @@ namespace ts {
/** /**
* Emits a node with possible leading and trailing source maps. * Emits a node with possible leading and trailing source maps.
* *
* @param hint A hint as to the intended usage of the node.
* @param node The node to emit. * @param node The node to emit.
* @param emitCallback The callback used to emit the node. * @param emitCallback The callback used to emit the node.
*/ */
function emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { function emitNodeWithSourceMap(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (disabled) { if (disabled) {
return emitCallback(emitContext, node); return emitCallback(hint, node);
} }
if (node) { if (node) {
@ -332,11 +330,11 @@ namespace ts {
if (emitFlags & EmitFlags.NoNestedSourceMaps) { if (emitFlags & EmitFlags.NoNestedSourceMaps) {
disabled = true; disabled = true;
emitCallback(emitContext, node); emitCallback(hint, node);
disabled = false; disabled = false;
} }
else { else {
emitCallback(emitContext, node); emitCallback(hint, node);
} }
if (node.kind !== SyntaxKind.NotEmittedStatement if (node.kind !== SyntaxKind.NotEmittedStatement

View file

@ -105,14 +105,16 @@ namespace ts {
hoistFunctionDeclaration, hoistFunctionDeclaration,
requestEmitHelper, requestEmitHelper,
readEmitHelpers, readEmitHelpers,
onSubstituteNode: (_emitContext, node) => node, onSubstituteNode: (_, node) => node,
enableSubstitution, enableSubstitution,
isSubstitutionEnabled, isSubstitutionEnabled,
onEmitNode: (node, emitContext, emitCallback) => emitCallback(node, emitContext), onEmitNode: (hint, node, callback) => callback(hint, node),
enableEmitNotification, enableEmitNotification,
isEmitNotificationEnabled isEmitNotificationEnabled
}; };
performance.mark("beforeTransform");
// Chain together and initialize each transformer. // Chain together and initialize each transformer.
const transformation = chain(...transformers)(context); const transformation = chain(...transformers)(context);
@ -122,6 +124,9 @@ namespace ts {
// Disable modification of the lexical environment. // Disable modification of the lexical environment.
lexicalEnvironmentDisabled = true; lexicalEnvironmentDisabled = true;
performance.mark("afterTransform");
performance.measure("transformTime", "beforeTransform", "afterTransform");
return { return {
transformed, transformed,
emitNodeWithSubstitution, emitNodeWithSubstitution,
@ -159,21 +164,16 @@ namespace ts {
/** /**
* Emits a node with possible substitution. * Emits a node with possible substitution.
* *
* @param emitContext The current emit context. * @param hint A hint as to the intended usage of the node.
* @param node The node to emit. * @param node The node to emit.
* @param emitCallback The callback used to emit the node or its substitute. * @param emitCallback The callback used to emit the node or its substitute.
*/ */
function emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { function emitNodeWithSubstitution(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (node) { if (node) {
if (isSubstitutionEnabled(node)) { if (isSubstitutionEnabled(node)) {
const substitute = context.onSubstituteNode(emitContext, node); node = context.onSubstituteNode(hint, node) || node;
if (substitute && substitute !== node) {
emitCallback(emitContext, substitute);
return;
}
} }
emitCallback(hint, node);
emitCallback(emitContext, node);
} }
} }
@ -196,17 +196,17 @@ namespace ts {
/** /**
* Emits a node with possible emit notification. * Emits a node with possible emit notification.
* *
* @param emitContext The current emit context. * @param hint A hint as to the intended usage of the node.
* @param node The node to emit. * @param node The node to emit.
* @param emitCallback The callback used to emit the node. * @param emitCallback The callback used to emit the node.
*/ */
function emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { function emitNodeWithNotification(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (node) { if (node) {
if (isEmitNotificationEnabled(node)) { if (isEmitNotificationEnabled(node)) {
context.onEmitNode(emitContext, node, emitCallback); context.onEmitNode(hint, node, emitCallback);
} }
else { else {
emitCallback(emitContext, node); emitCallback(hint, node);
} }
} }
} }

View file

@ -3435,9 +3435,11 @@ namespace ts {
/** /**
* Called by the printer just before a node is printed. * Called by the printer just before a node is printed.
* *
* @param hint A hint as to the intended usage of the node.
* @param node The node to be printed. * @param node The node to be printed.
* @param emitCallback The callback used to emit the node.
*/ */
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (enabledSubstitutions & ES2015SubstitutionFlags.CapturedThis && isFunctionLike(node)) { if (enabledSubstitutions & ES2015SubstitutionFlags.CapturedThis && isFunctionLike(node)) {
// If we are tracking a captured `this`, keep track of the enclosing function. // If we are tracking a captured `this`, keep track of the enclosing function.
const ancestorFacts = enterSubtree( const ancestorFacts = enterSubtree(
@ -3445,11 +3447,11 @@ namespace ts {
getEmitFlags(node) & EmitFlags.CapturesThis getEmitFlags(node) & EmitFlags.CapturesThis
? HierarchyFacts.FunctionIncludes | HierarchyFacts.CapturesThis ? HierarchyFacts.FunctionIncludes | HierarchyFacts.CapturesThis
: HierarchyFacts.FunctionIncludes); : HierarchyFacts.FunctionIncludes);
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
return; return;
} }
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
} }
/** /**
@ -3484,13 +3486,13 @@ namespace ts {
/** /**
* Hooks node substitutions. * Hooks node substitutions.
* *
* @param emitContext The context for the emitter. * @param hint The context for the emitter.
* @param node The node to substitute. * @param node The node to substitute.
*/ */
function onSubstituteNode(emitContext: EmitContext, node: Node) { function onSubstituteNode(hint: EmitHint, node: Node) {
node = previousOnSubstituteNode(emitContext, node); node = previousOnSubstituteNode(hint, node);
if (emitContext === EmitContext.Expression) { if (hint === EmitHint.Expression) {
return substituteExpression(node); return substituteExpression(node);
} }

View file

@ -396,33 +396,33 @@ namespace ts {
/** /**
* Hook for node emit. * Hook for node emit.
* *
* @param hint A hint as to the intended usage of the node.
* @param node The node to emit. * @param node The node to emit.
* @param emit A callback used to emit the node in the printer. * @param emit A callback used to emit the node in the printer.
*/ */
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
// If we need to support substitutions for `super` in an async method, // If we need to support substitutions for `super` in an async method,
// we should track it here. // we should track it here.
if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) { if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) {
const savedCurrentSuperContainer = currentSuperContainer; const savedCurrentSuperContainer = currentSuperContainer;
currentSuperContainer = node; currentSuperContainer = node;
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
currentSuperContainer = savedCurrentSuperContainer; currentSuperContainer = savedCurrentSuperContainer;
} }
else { else {
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
} }
} }
/** /**
* Hooks node substitutions. * Hooks node substitutions.
* *
* @param hint A hint as to the intended usage of the node.
* @param node The node to substitute. * @param node The node to substitute.
* @param isExpression A value indicating whether the node is to be used in an expression
* position.
*/ */
function onSubstituteNode(emitContext: EmitContext, node: Node) { function onSubstituteNode(hint: EmitHint, node: Node) {
node = previousOnSubstituteNode(emitContext, node); node = previousOnSubstituteNode(hint, node);
if (emitContext === EmitContext.Expression) { if (hint === EmitHint.Expression) {
return substituteExpression(<Expression>node); return substituteExpression(<Expression>node);
} }

View file

@ -12,7 +12,7 @@ namespace ts {
const compilerOptions = context.getCompilerOptions(); const compilerOptions = context.getCompilerOptions();
// enable emit notification only if using --jsx preserve or react-native // enable emit notification only if using --jsx preserve or react-native
let previousOnEmitNode: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void; let previousOnEmitNode: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
let noSubstitution: boolean[]; let noSubstitution: boolean[];
if (compilerOptions.jsx === JsxEmit.Preserve || compilerOptions.jsx === JsxEmit.ReactNative) { if (compilerOptions.jsx === JsxEmit.Preserve || compilerOptions.jsx === JsxEmit.ReactNative) {
previousOnEmitNode = context.onEmitNode; previousOnEmitNode = context.onEmitNode;
@ -41,9 +41,11 @@ namespace ts {
/** /**
* Called by the printer just before a node is printed. * Called by the printer just before a node is printed.
* *
* @param node The node to be printed. * @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emitCallback A callback used to emit the node.
*/ */
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { function onEmitNode(hint: EmitHint, node: Node, emitCallback: (emitContext: EmitHint, node: Node) => void) {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.JsxOpeningElement: case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxClosingElement: case SyntaxKind.JsxClosingElement:
@ -53,21 +55,21 @@ namespace ts {
break; break;
} }
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
} }
/** /**
* Hooks node substitutions. * Hooks node substitutions.
* *
* @param emitContext The context for the emitter. * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute. * @param node The node to substitute.
*/ */
function onSubstituteNode(emitContext: EmitContext, node: Node) { function onSubstituteNode(hint: EmitHint, node: Node) {
if (node.id && noSubstitution && noSubstitution[node.id]) { if (node.id && noSubstitution && noSubstitution[node.id]) {
return previousOnSubstituteNode(emitContext, node); return previousOnSubstituteNode(hint, node);
} }
node = previousOnSubstituteNode(emitContext, node); node = previousOnSubstituteNode(hint, node);
if (isPropertyAccessExpression(node)) { if (isPropertyAccessExpression(node)) {
return substitutePropertyAccessExpression(node); return substitutePropertyAccessExpression(node);
} }

View file

@ -1909,9 +1909,9 @@ namespace ts {
return -1; return -1;
} }
function onSubstituteNode(emitContext: EmitContext, node: Node): Node { function onSubstituteNode(hint: EmitHint, node: Node): Node {
node = previousOnSubstituteNode(emitContext, node); node = previousOnSubstituteNode(hint, node);
if (emitContext === EmitContext.Expression) { if (hint === EmitHint.Expression) {
return substituteExpression(<Expression>node); return substituteExpression(<Expression>node);
} }
return node; return node;

View file

@ -71,18 +71,18 @@ namespace ts {
/** /**
* Hook for node emit. * Hook for node emit.
* *
* @param emitContext A context hint for the emitter. * @param hint A hint as to the intended usage of the node.
* @param node The node to emit. * @param node The node to emit.
* @param emit A callback used to emit the node in the printer. * @param emit A callback used to emit the node in the printer.
*/ */
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
if (isSourceFile(node)) { if (isSourceFile(node)) {
currentSourceFile = node; currentSourceFile = node;
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
currentSourceFile = undefined; currentSourceFile = undefined;
} }
else { else {
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
} }
} }
@ -93,12 +93,12 @@ namespace ts {
/** /**
* Hooks node substitutions. * Hooks node substitutions.
* *
* @param emitContext A context hint for the emitter. * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute. * @param node The node to substitute.
*/ */
function onSubstituteNode(emitContext: EmitContext, node: Node) { function onSubstituteNode(hint: EmitHint, node: Node) {
node = previousOnSubstituteNode(emitContext, node); node = previousOnSubstituteNode(hint, node);
if (isIdentifier(node) && emitContext === EmitContext.Expression) { if (isIdentifier(node) && hint === EmitHint.Expression) {
return substituteExpressionIdentifier(node); return substituteExpressionIdentifier(node);
} }
return node; return node;

View file

@ -1207,24 +1207,24 @@ namespace ts {
/** /**
* Hook for node emit notifications. * Hook for node emit notifications.
* *
* @param emitContext A context hint for the emitter. * @param hint A hint as to the intended usage of the node.
* @param node The node to emit. * @param node The node to emit.
* @param emit A callback used to emit the node in the printer. * @param emit A callback used to emit the node in the printer.
*/ */
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
if (node.kind === SyntaxKind.SourceFile) { if (node.kind === SyntaxKind.SourceFile) {
currentSourceFile = <SourceFile>node; currentSourceFile = <SourceFile>node;
currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)]; currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)];
noSubstitution = []; noSubstitution = [];
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
currentSourceFile = undefined; currentSourceFile = undefined;
currentModuleInfo = undefined; currentModuleInfo = undefined;
noSubstitution = undefined; noSubstitution = undefined;
} }
else { else {
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
} }
} }
@ -1235,16 +1235,16 @@ namespace ts {
/** /**
* Hooks node substitutions. * Hooks node substitutions.
* *
* @param emitContext A context hint for the emitter. * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute. * @param node The node to substitute.
*/ */
function onSubstituteNode(emitContext: EmitContext, node: Node) { function onSubstituteNode(hint: EmitHint, node: Node) {
node = previousOnSubstituteNode(emitContext, node); node = previousOnSubstituteNode(hint, node);
if (node.id && noSubstitution[node.id]) { if (node.id && noSubstitution[node.id]) {
return node; return node;
} }
if (emitContext === EmitContext.Expression) { if (hint === EmitHint.Expression) {
return substituteExpression(<Expression>node); return substituteExpression(<Expression>node);
} }
else if (isShorthandPropertyAssignment(node)) { else if (isShorthandPropertyAssignment(node)) {

View file

@ -1548,11 +1548,11 @@ namespace ts {
/** /**
* Hook for node emit notifications. * Hook for node emit notifications.
* *
* @param emitContext A context hint for the emitter. * @param hint A hint as to the intended usage of the node.
* @param node The node to emit. * @param node The node to emit.
* @param emit A callback used to emit the node in the printer. * @param emitCallback A callback used to emit the node in the printer.
*/ */
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
if (node.kind === SyntaxKind.SourceFile) { if (node.kind === SyntaxKind.SourceFile) {
const id = getOriginalNodeId(node); const id = getOriginalNodeId(node);
currentSourceFile = <SourceFile>node; currentSourceFile = <SourceFile>node;
@ -1564,7 +1564,7 @@ namespace ts {
delete noSubstitutionMap[id]; delete noSubstitutionMap[id];
} }
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
currentSourceFile = undefined; currentSourceFile = undefined;
moduleInfo = undefined; moduleInfo = undefined;
@ -1572,7 +1572,7 @@ namespace ts {
noSubstitution = undefined; noSubstitution = undefined;
} }
else { else {
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
} }
} }
@ -1583,16 +1583,16 @@ namespace ts {
/** /**
* Hooks node substitutions. * Hooks node substitutions.
* *
* @param emitContext A context hint for the emitter. * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute. * @param node The node to substitute.
*/ */
function onSubstituteNode(emitContext: EmitContext, node: Node) { function onSubstituteNode(hint: EmitHint, node: Node) {
node = previousOnSubstituteNode(emitContext, node); node = previousOnSubstituteNode(hint, node);
if (isSubstitutionPrevented(node)) { if (isSubstitutionPrevented(node)) {
return node; return node;
} }
if (emitContext === EmitContext.Expression) { if (hint === EmitHint.Expression) {
return substituteExpression(<Expression>node); return substituteExpression(<Expression>node);
} }

View file

@ -3154,11 +3154,11 @@ namespace ts {
/** /**
* Hook for node emit. * Hook for node emit.
* *
* @param emitContext A context hint for the emitter. * @param hint A hint as to the intended usage of the node.
* @param node The node to emit. * @param node The node to emit.
* @param emit A callback used to emit the node in the printer. * @param emit A callback used to emit the node in the printer.
*/ */
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
const savedApplicableSubstitutions = applicableSubstitutions; const savedApplicableSubstitutions = applicableSubstitutions;
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node)) { if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node)) {
@ -3169,7 +3169,7 @@ namespace ts {
applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers; applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers;
} }
previousOnEmitNode(emitContext, node, emitCallback); previousOnEmitNode(hint, node, emitCallback);
applicableSubstitutions = savedApplicableSubstitutions; applicableSubstitutions = savedApplicableSubstitutions;
} }
@ -3177,12 +3177,12 @@ namespace ts {
/** /**
* Hooks node substitutions. * Hooks node substitutions.
* *
* @param emitContext A context hint for the emitter. * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute. * @param node The node to substitute.
*/ */
function onSubstituteNode(emitContext: EmitContext, node: Node) { function onSubstituteNode(hint: EmitHint, node: Node) {
node = previousOnSubstituteNode(emitContext, node); node = previousOnSubstituteNode(hint, node);
if (emitContext === EmitContext.Expression) { if (hint === EmitHint.Expression) {
return substituteExpression(<Expression>node); return substituteExpression(<Expression>node);
} }
else if (isShorthandPropertyAssignment(node)) { else if (isShorthandPropertyAssignment(node)) {

View file

@ -34,6 +34,7 @@
"comments.ts", "comments.ts",
"sourcemap.ts", "sourcemap.ts",
"declarationEmitter.ts", "declarationEmitter.ts",
"printer.ts",
"emitter.ts", "emitter.ts",
"program.ts", "program.ts",
"commandLineParser.ts", "commandLineParser.ts",

View file

@ -348,6 +348,7 @@
EnumMember, EnumMember,
// Top-level nodes // Top-level nodes
SourceFile, SourceFile,
Bundle,
// JSDoc nodes // JSDoc nodes
JSDocTypeExpression, JSDocTypeExpression,
@ -557,15 +558,6 @@
export type ModifiersArray = NodeArray<Modifier>; export type ModifiersArray = NodeArray<Modifier>;
/*@internal*/
export const enum GeneratedIdentifierKind {
None, // Not automatically generated.
Auto, // Automatically generated identifier.
Loop, // Automatically generated identifier with a preference for '_i'.
Unique, // Unique name based on the 'text' property.
Node, // Unique name based on the node in the 'original' property.
}
export interface Identifier extends PrimaryExpression { export interface Identifier extends PrimaryExpression {
kind: SyntaxKind.Identifier; kind: SyntaxKind.Identifier;
text: string; // Text of identifier (with escapes converted to characters) text: string; // Text of identifier (with escapes converted to characters)
@ -580,7 +572,14 @@
resolvedSymbol: Symbol; resolvedSymbol: Symbol;
} }
/*@internal*/ export const enum GeneratedIdentifierKind {
None, // Not automatically generated.
Auto, // Automatically generated identifier.
Loop, // Automatically generated identifier with a preference for '_i'.
Unique, // Unique name based on the 'text' property.
Node, // Unique name based on the node in the 'original' property.
}
export interface GeneratedIdentifier extends Identifier { export interface GeneratedIdentifier extends Identifier {
autoGenerateKind: GeneratedIdentifierKind.Auto autoGenerateKind: GeneratedIdentifierKind.Auto
| GeneratedIdentifierKind.Loop | GeneratedIdentifierKind.Loop
@ -2201,6 +2200,11 @@
/* @internal */ ambientModuleNames: string[]; /* @internal */ ambientModuleNames: string[];
} }
export interface Bundle extends Node {
kind: SyntaxKind.Bundle;
sourceFiles: SourceFile[];
}
export interface ScriptReferenceHost { export interface ScriptReferenceHost {
getCompilerOptions(): CompilerOptions; getCompilerOptions(): CompilerOptions;
getSourceFile(fileName: string): SourceFile; getSourceFile(fileName: string): SourceFile;
@ -3780,8 +3784,7 @@
LastEmitHelper = Generator LastEmitHelper = Generator
} }
/* @internal */ export const enum EmitHint {
export const enum EmitContext {
SourceFile, // Emitting a SourceFile SourceFile, // Emitting a SourceFile
Expression, // Emitting an Expression Expression, // Emitting an Expression
IdentifierName, // Emitting an IdentifierName IdentifierName, // Emitting an IdentifierName
@ -3856,7 +3859,7 @@
* Hook used by transformers to substitute expressions just before they * Hook used by transformers to substitute expressions just before they
* are emitted by the pretty printer. * are emitted by the pretty printer.
*/ */
onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node; onSubstituteNode?: (hint: EmitHint, node: Node) => Node;
/** /**
* Enables before/after emit notifications in the pretty printer for the provided * Enables before/after emit notifications in the pretty printer for the provided
@ -3874,7 +3877,7 @@
* Hook used to allow transformers to capture state before or after * Hook used to allow transformers to capture state before or after
* the printer emits a node. * the printer emits a node.
*/ */
onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void; onEmitNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
} }
/* @internal */ /* @internal */
@ -3887,25 +3890,61 @@
/** /**
* Emits the substitute for a node, if one is available; otherwise, emits the node. * Emits the substitute for a node, if one is available; otherwise, emits the node.
* *
* @param emitContext The current emit context. * @param hint A hint as to the intended usage of the node.
* @param node The node to substitute. * @param node The node to substitute.
* @param emitCallback A callback used to emit the node or its substitute. * @param emitCallback A callback used to emit the node or its substitute.
*/ */
emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; emitNodeWithSubstitution(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
/** /**
* Emits a node with possible notification. * Emits a node with possible notification.
* *
* @param emitContext The current emit context. * @param hint A hint as to the intended usage of the node.
* @param node The node to emit. * @param node The node to emit.
* @param emitCallback A callback used to emit the node. * @param emitCallback A callback used to emit the node.
*/ */
emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; emitNodeWithNotification(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
} }
/* @internal */ /* @internal */
export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile; export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile;
export interface EmitTextWriter {
write(s: string): void;
writeTextOfNode(text: string, node: Node): void;
writeLine(): void;
increaseIndent(): void;
decreaseIndent(): void;
getText(): string;
rawWrite(s: string): void;
writeLiteral(s: string): void;
getTextPos(): number;
getLine(): number;
getColumn(): number;
getIndent(): number;
isAtStartOfLine(): boolean;
reset(): void;
}
export interface Printer {
printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): void;
printFile(sourceFile: SourceFile): void;
printBundle(bundle: Bundle): void;
}
export interface PrinterOptions {
hasGlobalName?: (name: string) => boolean;
onEmitSourceMapOfNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
onEmitSourceMapOfToken?: (node: Node, token: SyntaxKind, pos: number, emitCallback: (token: SyntaxKind, pos: number) => number) => number;
onEmitCommentsOfNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
onEmitDetachedCommentsOfNode?: (node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) => void;
onEmitTrailingCommentsOfPosition?: (pos: number) => void;
onEmitNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
onEmitHelpers?: (node: Node, writeLines: (text: string) => void) => void;
onSetSourceFile?: (node: SourceFile) => void;
onSubstituteNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
}
export interface TextSpan { export interface TextSpan {
start: number; start: number;
length: number; length: number;

View file

@ -2088,16 +2088,19 @@ namespace ts {
return undefined; return undefined;
} }
export function getOriginalSourceFiles(sourceFiles: SourceFile[]) { export function getOriginalSourceFileOrBundle(sourceFileOrBundle: SourceFile | Bundle) {
const originalSourceFiles: SourceFile[] = []; if (sourceFileOrBundle.kind === SyntaxKind.Bundle) {
for (const sourceFile of sourceFiles) { return updateBundle(sourceFileOrBundle, sameMap(sourceFileOrBundle.sourceFiles, getOriginalSourceFile));
const originalSourceFile = getParseTreeNode(sourceFile, isSourceFile);
if (originalSourceFile) {
originalSourceFiles.push(originalSourceFile);
}
} }
return getOriginalSourceFile(sourceFileOrBundle);
}
return originalSourceFiles; function getOriginalSourceFile(sourceFile: SourceFile) {
return getParseTreeNode(sourceFile, isSourceFile) || sourceFile;
}
export function getOriginalSourceFiles(sourceFiles: SourceFile[]) {
return sameMap(sourceFiles, getOriginalSourceFile);
} }
export function getOriginalNodeId(node: Node) { export function getOriginalNodeId(node: Node) {
@ -2436,23 +2439,6 @@ namespace ts {
s; s;
} }
export interface EmitTextWriter {
write(s: string): void;
writeTextOfNode(text: string, node: Node): void;
writeLine(): void;
increaseIndent(): void;
decreaseIndent(): void;
getText(): string;
rawWrite(s: string): void;
writeLiteral(s: string): void;
getTextPos(): number;
getLine(): number;
getColumn(): number;
getIndent(): number;
isAtStartOfLine(): boolean;
reset(): void;
}
const indentStrings: string[] = ["", " "]; const indentStrings: string[] = ["", " "];
export function getIndentString(level: number) { export function getIndentString(level: number) {
if (indentStrings[level] === undefined) { if (indentStrings[level] === undefined) {
@ -2635,7 +2621,7 @@ namespace ts {
* Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit. * Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit.
*/ */
export function forEachEmittedFile( export function forEachEmittedFile(
host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean) => void, host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean) => void,
sourceFilesOrTargetSourceFile?: SourceFile[] | SourceFile, sourceFilesOrTargetSourceFile?: SourceFile[] | SourceFile,
emitOnlyDtsFiles?: boolean) { emitOnlyDtsFiles?: boolean) {
@ -2646,7 +2632,7 @@ namespace ts {
const jsFilePath = options.outFile || options.out; const jsFilePath = options.outFile || options.out;
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + ".d.ts" : undefined; const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + ".d.ts" : undefined;
action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFiles, /*isBundledEmit*/true, emitOnlyDtsFiles); action({ jsFilePath, sourceMapFilePath, declarationFilePath }, createBundle(sourceFiles), emitOnlyDtsFiles);
} }
} }
else { else {
@ -2654,7 +2640,7 @@ namespace ts {
const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options)); const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options));
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined; const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
action({ jsFilePath, sourceMapFilePath, declarationFilePath }, [sourceFile], /*isBundledEmit*/false, emitOnlyDtsFiles); action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFile, emitOnlyDtsFiles);
} }
} }
} }

View file

@ -37,6 +37,7 @@
"../compiler/comments.ts", "../compiler/comments.ts",
"../compiler/sourcemap.ts", "../compiler/sourcemap.ts",
"../compiler/declarationEmitter.ts", "../compiler/declarationEmitter.ts",
"../compiler/printer.ts",
"../compiler/emitter.ts", "../compiler/emitter.ts",
"../compiler/program.ts", "../compiler/program.ts",
"../compiler/commandLineParser.ts", "../compiler/commandLineParser.ts",

View file

@ -34,6 +34,7 @@
"../compiler/comments.ts", "../compiler/comments.ts",
"../compiler/sourcemap.ts", "../compiler/sourcemap.ts",
"../compiler/declarationEmitter.ts", "../compiler/declarationEmitter.ts",
"../compiler/printer.ts",
"../compiler/emitter.ts", "../compiler/emitter.ts",
"../compiler/program.ts", "../compiler/program.ts",
"../compiler/commandLineParser.ts", "../compiler/commandLineParser.ts",