Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose emitter with visitors and transforms #11561

Closed
wants to merge 10 commits into from
6 changes: 3 additions & 3 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2611,7 +2611,7 @@ namespace ts {
}

// Currently, we only support generators that were originally async function bodies.
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
if (asteriskToken && factory.getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}

Expand Down Expand Up @@ -2686,7 +2686,7 @@ namespace ts {
// down-level generator.
// Currently we do not support transforming any other generator fucntions
// down level.
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
if (asteriskToken && factory.getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}
}
Expand Down Expand Up @@ -2717,7 +2717,7 @@ namespace ts {
// down-level generator.
// Currently we do not support transforming any other generator fucntions
// down level.
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
if (asteriskToken && factory.getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}

Expand Down
4 changes: 2 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3058,7 +3058,7 @@ namespace ts {
}

function isAutoVariableInitializer(initializer: Expression) {
const expr = initializer && skipParentheses(initializer);
const expr = initializer && factory.skipParentheses(initializer);
return !expr || expr.kind === SyntaxKind.NullKeyword || expr.kind === SyntaxKind.Identifier && getResolvedSymbol(<Identifier>expr) === undefinedSymbol;
}

Expand Down Expand Up @@ -13470,7 +13470,7 @@ namespace ts {
* its value is discarded (e.g. the left side of the comma operator).
*/
function isSideEffectFree(node: Node): boolean {
node = skipParentheses(node);
node = factory.skipParentheses(node);
switch (node.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.StringLiteral:
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/comments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ namespace ts {
}

if (node) {
const { pos, end } = getCommentRange(node);
const emitFlags = getEmitFlags(node);
const { pos, end } = factory.getCommentRange(node);
const emitFlags = factory.getEmitFlags(node);
if ((pos < 0 && end < 0) || (pos === end)) {
// Both pos and end are synthesized, so just emit the node without comments.
if (emitFlags & EmitFlags.NoNestedComments) {
Expand Down Expand Up @@ -129,7 +129,7 @@ namespace ts {
}

const { pos, end } = detachedRange;
const emitFlags = getEmitFlags(node);
const emitFlags = factory.getEmitFlags(node);
const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0;
const skipTrailingComments = disabled || end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0;

Expand Down
214 changes: 139 additions & 75 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
/// <reference path="sourcemap.ts"/>
/// <reference path="comments.ts" />

/* @internal */
namespace ts {
// Flags enum to track count of temp variables and a few dedicated names
const enum TempFlags {
Expand All @@ -16,8 +15,132 @@ namespace ts {
const id = (s: SourceFile) => s;
const nullTransformers: Transformer[] = [ctx => id];

export function emit(file: SourceFile, transformers: Transformer[] = nullTransformers, newLine = "\n") {
let result: string;

const options: CompilerOptions = {};
const host: EmitHost = {
getCompilerOptions() {
return options;
},
getSourceFile(fileName: string) {
if (fileName === file.fileName) return file;
},
getSourceFileByPath(path: Path) {
if (path === file.fileName) return file;
},
getCurrentDirectory() {
return "";
},
getSourceFiles() {
return [file];
},
isSourceFileFromExternalLibrary() {
return false;
},
getCommonSourceDirectory() {
return "";
},
getCanonicalFileName(fileName: string) {
return fileName;
},
getNewLine() {
return newLine;
},
isEmitBlocked() {
return false;
},
writeFile(fileName: string, data: string) {
result = data;
}
};

const transform = transformFiles(undefined, host, [file], transformers);

const printFile = createPrinter(host, undefined, createDiagnosticCollection(), transform, () => false);

printFile("output.js", "", [file], /*isBundledEmit*/ false);

// Clean up emit nodes on parse tree
factory.disposeEmitNodes(file);

return {
result
};
}

// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
/* @internal */
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean): EmitResult {
const compilerOptions = host.getCompilerOptions();
const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined;
const emittedFilesList: string[] = compilerOptions.listEmittedFiles ? [] : undefined;
const emitterDiagnostics = createDiagnosticCollection();
const transformers: Transformer[] = emitOnlyDtsFiles ? nullTransformers : getTransformers(compilerOptions);
let emitSkipped = false;

const sourceFiles = getSourceFilesToEmit(host, targetSourceFile);

// Transform the source files
performance.mark("beforeTransform");
const transform = transformFiles(resolver, host, sourceFiles, transformers);
performance.measure("transformTime", "beforeTransform");

const printFile = createPrinter(host, emittedFilesList, emitterDiagnostics, transform, resolver.hasGlobalName, sourceMapDataList);

// Emit each output file
performance.mark("beforePrint");
forEachTransformedEmitFile(host, transform.transformed, emitFile, emitOnlyDtsFiles);
performance.measure("printTime", "beforePrint");

// Clean up emit nodes on parse tree
for (const sourceFile of sourceFiles) {
factory.disposeEmitNodes(sourceFile);
}

return {
emitSkipped,
diagnostics: emitterDiagnostics.getDiagnostics(),
emittedFiles: emittedFilesList,
sourceMaps: sourceMapDataList
};

function emitFile(jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
// Make sure not to write js file and source map file if any of them cannot be written
if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) {
if (!emitOnlyDtsFiles) {
printFile(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
}
}
else {
emitSkipped = true;
}

if (declarationFilePath) {
emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFiles(sourceFiles), isBundledEmit, host, resolver, emitterDiagnostics, emitOnlyDtsFiles) || emitSkipped;
}

if (!emitSkipped && emittedFilesList) {
if (!emitOnlyDtsFiles) {
emittedFilesList.push(jsFilePath);
}
if (sourceMapFilePath) {
emittedFilesList.push(sourceMapFilePath);
}
if (declarationFilePath) {
emittedFilesList.push(declarationFilePath);
}
}
}
}
function createPrinter(
host: EmitHost,
emittedFilesList: string[],
emitterDiagnostics: DiagnosticCollection,
{ emitNodeWithSubstitution, emitNodeWithNotification }: TransformationResult,
hasGlobalName: (name: string) => boolean,
sourceMapDataList?: SourceMapData[]
) {
const delimiters = createDelimiterMap();
const brackets = createBracketsMap();

Expand Down Expand Up @@ -191,11 +314,7 @@ const _super = (function (geti, seti) {
const compilerOptions = host.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const moduleKind = getEmitModuleKind(compilerOptions);
const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined;
const emittedFilesList: string[] = compilerOptions.listEmittedFiles ? [] : undefined;
const emitterDiagnostics = createDiagnosticCollection();
const newLine = host.getNewLine();
const transformers: Transformer[] = emitOnlyDtsFiles ? nullTransformers : getTransformers(compilerOptions);
const writer = createTextWriter(newLine);
const {
write,
Expand Down Expand Up @@ -230,63 +349,8 @@ const _super = (function (geti, seti) {
let paramEmitted: boolean;
let awaiterEmitted: boolean;
let isOwnFileEmit: boolean;
let emitSkipped = false;

const sourceFiles = getSourceFilesToEmit(host, targetSourceFile);

// Transform the source files
performance.mark("beforeTransform");
const {
transformed,
emitNodeWithSubstitution,
emitNodeWithNotification
} = transformFiles(resolver, host, sourceFiles, transformers);
performance.measure("transformTime", "beforeTransform");

// Emit each output file
performance.mark("beforePrint");
forEachTransformedEmitFile(host, transformed, emitFile, emitOnlyDtsFiles);
performance.measure("printTime", "beforePrint");

// Clean up emit nodes on parse tree
for (const sourceFile of sourceFiles) {
disposeEmitNodes(sourceFile);
}

return {
emitSkipped,
diagnostics: emitterDiagnostics.getDiagnostics(),
emittedFiles: emittedFilesList,
sourceMaps: sourceMapDataList
};

function emitFile(jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
// Make sure not to write js file and source map file if any of them cannot be written
if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) {
if (!emitOnlyDtsFiles) {
printFile(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
}
}
else {
emitSkipped = true;
}

if (declarationFilePath) {
emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFiles(sourceFiles), isBundledEmit, host, resolver, emitterDiagnostics, emitOnlyDtsFiles) || emitSkipped;
}

if (!emitSkipped && emittedFilesList) {
if (!emitOnlyDtsFiles) {
emittedFilesList.push(jsFilePath);
}
if (sourceMapFilePath) {
emittedFilesList.push(sourceMapFilePath);
}
if (declarationFilePath) {
emittedFilesList.push(declarationFilePath);
}
}
}
return printFile;

function printFile(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
Expand Down Expand Up @@ -861,7 +925,7 @@ const _super = (function (geti, seti) {
//

function emitIdentifier(node: Identifier) {
if (getEmitFlags(node) & EmitFlags.UMDDefine) {
if (factory.getEmitFlags(node) & EmitFlags.UMDDefine) {
writeLines(umdHelper);
}
else {
Expand Down Expand Up @@ -1134,7 +1198,7 @@ const _super = (function (geti, seti) {
write("{}");
}
else {
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
const indentedFlag = factory.getEmitFlags(node) & EmitFlags.Indented;
if (indentedFlag) {
increaseIndent();
}
Expand All @@ -1152,7 +1216,7 @@ const _super = (function (geti, seti) {
function emitPropertyAccessExpression(node: PropertyAccessExpression) {
let indentBeforeDot = false;
let indentAfterDot = false;
if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) {
if (!(factory.getEmitFlags(node) & EmitFlags.NoIndentation)) {
const dotRangeStart = node.expression.end;
const dotRangeEnd = skipTrivia(currentText, node.expression.end) + 1;
const dotToken = <Node>{ kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd };
Expand Down Expand Up @@ -1181,7 +1245,7 @@ const _super = (function (geti, seti) {
}
else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) {
// check if constant enum value is integer
const constantValue = getConstantValue(expression);
const constantValue = factory.getConstantValue(expression);
// isFinite handles cases when constantValue is undefined
return isFinite(constantValue)
&& Math.floor(constantValue) === constantValue
Expand Down Expand Up @@ -1395,7 +1459,7 @@ const _super = (function (geti, seti) {
}

function emitBlockStatements(node: BlockLike) {
if (getEmitFlags(node) & EmitFlags.SingleLine) {
if (factory.getEmitFlags(node) & EmitFlags.SingleLine) {
emitList(node, node.statements, ListFormat.SingleLineBlockStatements);
}
else {
Expand Down Expand Up @@ -1599,12 +1663,12 @@ const _super = (function (geti, seti) {
const body = node.body;
if (body) {
if (isBlock(body)) {
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
const indentedFlag = factory.getEmitFlags(node) & EmitFlags.Indented;
if (indentedFlag) {
increaseIndent();
}

if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) {
if (factory.getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) {
emitSignatureHead(node);
emitBlockFunctionBody(node, body);
}
Expand Down Expand Up @@ -1648,7 +1712,7 @@ const _super = (function (geti, seti) {
// * A non-synthesized body's start and end position are on different lines.
// * Any statement in the body starts on a new line.

if (getEmitFlags(body) & EmitFlags.SingleLine) {
if (factory.getEmitFlags(body) & EmitFlags.SingleLine) {
return true;
}

Expand Down Expand Up @@ -1719,7 +1783,7 @@ const _super = (function (geti, seti) {
write("class");
emitNodeWithPrefix(" ", node.name, emitIdentifierName);

const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
const indentedFlag = factory.getEmitFlags(node) & EmitFlags.Indented;
if (indentedFlag) {
increaseIndent();
}
Expand Down Expand Up @@ -2052,8 +2116,8 @@ const _super = (function (geti, seti) {
// "comment1" is not considered to be leading comment for node.initializer
// but rather a trailing comment on the previous node.
const initializer = node.initializer;
if ((getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) {
const commentRange = getCommentRange(initializer);
if ((factory.getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) {
const commentRange = factory.getCommentRange(initializer);
emitTrailingCommentsOfPosition(commentRange.pos);
}

Expand Down Expand Up @@ -2125,7 +2189,7 @@ const _super = (function (geti, seti) {
}

function emitHelpers(node: Node) {
const emitFlags = getEmitFlags(node);
const emitFlags = factory.getEmitFlags(node);
let helpersEmitted = false;
if (emitFlags & EmitFlags.EmitEmitHelpers) {
helpersEmitted = emitEmitHelpers(currentSourceFile);
Expand Down Expand Up @@ -2386,7 +2450,7 @@ const _super = (function (geti, seti) {
}

if (shouldEmitInterveningComments) {
const commentRange = getCommentRange(child);
const commentRange = factory.getCommentRange(child);
emitTrailingCommentsOfPosition(commentRange.pos);
}
else {
Expand Down Expand Up @@ -2624,7 +2688,7 @@ const _super = (function (geti, seti) {
}

function isUniqueName(name: string): boolean {
return !resolver.hasGlobalName(name) &&
return !hasGlobalName(name) &&
!hasProperty(currentFileIdentifiers, name) &&
!hasProperty(generatedNameSet, name);
}
Expand Down
Loading