Handle emit helpers in --out scenario

This commit is contained in:
Sheetal Nandi
2019-01-23 14:44:07 -08:00
parent c739defe25
commit 36eae04120
16 changed files with 808 additions and 930 deletions

View File

@@ -1136,16 +1136,16 @@ namespace ts {
if (bundle && moduleKind === ModuleKind.None) {
return;
}
const numNodes = bundle ? bundle.sourceFiles.length : 1;
const numPrepends = bundle ? bundle.prepends.length : 0;
const numNodes = bundle ? bundle.sourceFiles.length + numPrepends : 1;
for (let i = 0; i < numNodes; i++) {
const currentNode = bundle ? bundle.sourceFiles[i] : node;
const sourceFile = isSourceFile(currentNode) ? currentNode : currentSourceFile!;
const shouldSkip = printerOptions.noEmitHelpers || getExternalHelpersModuleName(sourceFile) !== undefined;
const shouldBundle = isSourceFile(currentNode) && !isOwnFileEmit;
const helpers = getEmitHelpers(currentNode);
const currentNode = bundle ? i < numPrepends ? bundle.prepends[i] : bundle.sourceFiles[i - numPrepends] : node;
const sourceFile = isSourceFile(currentNode) ? currentNode : isUnparsedSource(currentNode) ? undefined : currentSourceFile!;
const shouldSkip = printerOptions.noEmitHelpers || (!!sourceFile && getExternalHelpersModuleName(sourceFile) !== undefined);
const shouldBundle = (isSourceFile(currentNode) || isUnparsedSource(currentNode)) && !isOwnFileEmit;
const helpers = isUnparsedSource(currentNode) ? currentNode.helpers : getSortedEmitHelpers(currentNode);
if (helpers) {
for (const helper of stableSort(helpers, compareEmitHelpers)) {
for (const helper of helpers) {
if (!helper.scoped) {
// Skip the helper if it can be skipped and the noEmitHelpers compiler
// option is set, or if it can be imported and the importHelpers compiler
@@ -1181,6 +1181,11 @@ namespace ts {
return helpersEmitted;
}
function getSortedEmitHelpers(node: Node) {
const helpers = getEmitHelpers(node);
return helpers && stableSort(helpers, compareEmitHelpers);
}
//
// Literals/Pseudo-literals
//

View File

@@ -2629,6 +2629,71 @@ namespace ts {
return node;
}
interface UnscopedEmitHelpersWithLines {
helper: UnscopedEmitHelpers;
lines: ReadonlyArray<string>;
}
let allUnscopedEmitHelpers: ReadonlyArray<UnscopedEmitHelpersWithLines> | undefined;
function getAllUnscopedEmitHelpers() {
return allUnscopedEmitHelpers ||
(allUnscopedEmitHelpers = [
getUnscopedEmitHelperWithLines(valuesHelper),
getUnscopedEmitHelperWithLines(readHelper),
getUnscopedEmitHelperWithLines(spreadHelper),
getUnscopedEmitHelperWithLines(restHelper),
getUnscopedEmitHelperWithLines(decorateHelper),
getUnscopedEmitHelperWithLines(metadataHelper),
getUnscopedEmitHelperWithLines(paramHelper),
getUnscopedEmitHelperWithLines(awaiterHelper),
getUnscopedEmitHelperWithLines(assignHelper),
getUnscopedEmitHelperWithLines(awaitHelper),
getUnscopedEmitHelperWithLines(asyncGeneratorHelper),
getUnscopedEmitHelperWithLines(asyncDelegator),
getUnscopedEmitHelperWithLines(asyncValues),
getUnscopedEmitHelperWithLines(extendsHelper),
getUnscopedEmitHelperWithLines(templateObjectHelper),
getUnscopedEmitHelperWithLines(generatorHelper),
getUnscopedEmitHelperWithLines(importStarHelper),
getUnscopedEmitHelperWithLines(importDefaultHelper)
]);
}
function getUnscopedEmitHelperWithLines(helper: UnscopedEmitHelpers): UnscopedEmitHelpersWithLines {
const helperLines = helper.text.split(/\r\n?|\n/g);
const indentation = guessIndentation(helperLines);
const lines: string[] = [];
for (const lineText of helperLines) {
const line = indentation ? lineText.slice(indentation) : lineText;
if (line.length) {
lines.push(line);
}
}
return { helper, lines };
}
function tryGetUnscopedEmitHelper(text: string, pos: number) {
const allHelpers = getAllUnscopedEmitHelpers();
if (pos >= text.length) return undefined;
for (const { helper, lines } of allHelpers) {
let newPos = pos;
for (const line of lines) {
const startIndex = text.indexOf(line, newPos);
if (startIndex !== newPos) {
newPos = -1;
break;
}
newPos = skipTrivia(text, newPos + line.length, /*stopAfterLineBreak*/ true);
}
// Found match
if (newPos !== -1) {
return { helper, newPos };
}
}
return undefined;
}
export function createUnparsedSourceFile(text: string): UnparsedSource;
export function createUnparsedSourceFile(inputFile: InputFiles, type: "js" | "dts"): UnparsedSource;
export function createUnparsedSourceFile(text: string, mapPath: string | undefined, map: string | undefined): UnparsedSource;
@@ -2649,7 +2714,11 @@ namespace ts {
node.sourceMapText = map;
}
const text = node.text;
// Shebang
let pos = isShebangTrivia(text, 0) ? skipTrivia(text, 0, /*stopAfterLineBreak*/ true) : 0;
// Prologue
const scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ true, /*languageVariant*/ undefined, text, /*onError*/ undefined, pos);
let prologues: UnparsedPrologue[] | undefined;
while (scanner.scan() === SyntaxKind.StringLiteral) {
@@ -2663,8 +2732,20 @@ namespace ts {
prologue.text = prologueText;
(prologues || (prologues = [])).push(prologue);
}
// Helpers
let helpers: UnscopedEmitHelpers[] | undefined;
while (true) {
const helperInfo = tryGetUnscopedEmitHelper(text, pos);
if (!helperInfo) break;
pos = helperInfo.newPos;
(helpers || (helpers = [])).push(helperInfo.helper);
}
// Rest of the text
node.pos = pos;
node.prologues = prologues || emptyArray;
node.helpers = helpers;
node.getLineAndCharacterOfPosition = pos => getLineAndCharacterOfPosition(node, pos);
return node;
}
@@ -3390,7 +3471,7 @@ namespace ts {
return setEmitFlags(createIdentifier(name), EmitFlags.HelperName | EmitFlags.AdviseOnEmitNode);
}
const valuesHelper: EmitHelper = {
export const valuesHelper: UnscopedEmitHelpers = {
name: "typescript:values",
scoped: false,
text: `
@@ -3418,7 +3499,7 @@ namespace ts {
);
}
const readHelper: EmitHelper = {
export const readHelper: UnscopedEmitHelpers = {
name: "typescript:read",
scoped: false,
text: `
@@ -3454,7 +3535,7 @@ namespace ts {
);
}
const spreadHelper: EmitHelper = {
export const spreadHelper: UnscopedEmitHelpers = {
name: "typescript:spread",
scoped: false,
text: `

View File

@@ -512,7 +512,7 @@ namespace ts {
return name;
}
const restHelper: EmitHelper = {
export const restHelper: UnscopedEmitHelpers = {
name: "typescript:rest",
scoped: false,
text: `

View File

@@ -4383,7 +4383,7 @@ namespace ts {
);
}
const extendsHelper: EmitHelper = {
export const extendsHelper: UnscopedEmitHelpers = {
name: "typescript:extends",
scoped: false,
priority: 0,
@@ -4404,7 +4404,7 @@ namespace ts {
})();`
};
const templateObjectHelper: EmitHelper = {
export const templateObjectHelper: UnscopedEmitHelpers = {
name: "typescript:makeTemplateObject",
scoped: false,
priority: 0,

View File

@@ -750,7 +750,7 @@ namespace ts {
NodeFlags.Const));
}
const awaiterHelper: EmitHelper = {
export const awaiterHelper: UnscopedEmitHelpers = {
name: "typescript:awaiter",
scoped: false,
priority: 5,

View File

@@ -929,7 +929,7 @@ namespace ts {
}
}
const assignHelper: EmitHelper = {
export const assignHelper: UnscopedEmitHelpers = {
name: "typescript:assign",
scoped: false,
priority: 1,
@@ -961,7 +961,7 @@ namespace ts {
);
}
const awaitHelper: EmitHelper = {
export const awaitHelper: UnscopedEmitHelpers = {
name: "typescript:await",
scoped: false,
text: `
@@ -973,7 +973,7 @@ namespace ts {
return createCall(getHelperName("__await"), /*typeArguments*/ undefined, [expression]);
}
const asyncGeneratorHelper: EmitHelper = {
export const asyncGeneratorHelper: UnscopedEmitHelpers = {
name: "typescript:asyncGenerator",
scoped: false,
text: `
@@ -1008,7 +1008,7 @@ namespace ts {
);
}
const asyncDelegator: EmitHelper = {
export const asyncDelegator: UnscopedEmitHelpers = {
name: "typescript:asyncDelegator",
scoped: false,
text: `
@@ -1032,7 +1032,7 @@ namespace ts {
);
}
const asyncValues: EmitHelper = {
export const asyncValues: UnscopedEmitHelpers = {
name: "typescript:asyncValues",
scoped: false,
text: `

View File

@@ -3240,7 +3240,7 @@ namespace ts {
// entering a finally block.
//
// For examples of how these are used, see the comments in ./transformers/generators.ts
const generatorHelper: EmitHelper = {
export const generatorHelper: UnscopedEmitHelpers = {
name: "typescript:generator",
scoped: false,
priority: 6,

View File

@@ -1806,7 +1806,7 @@ namespace ts {
};
// emit helper for `import * as Name from "foo"`
const importStarHelper: EmitHelper = {
export const importStarHelper: UnscopedEmitHelpers = {
name: "typescript:commonjsimportstar",
scoped: false,
text: `
@@ -1820,7 +1820,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
// emit helper for `import Name from "foo"`
const importDefaultHelper: EmitHelper = {
export const importDefaultHelper: UnscopedEmitHelpers = {
name: "typescript:commonjsimportdefault",
scoped: false,
text: `

View File

@@ -3675,7 +3675,7 @@ namespace ts {
);
}
const decorateHelper: EmitHelper = {
export const decorateHelper: UnscopedEmitHelpers = {
name: "typescript:decorate",
scoped: false,
priority: 2,
@@ -3700,7 +3700,7 @@ namespace ts {
);
}
const metadataHelper: EmitHelper = {
export const metadataHelper: UnscopedEmitHelpers = {
name: "typescript:metadata",
scoped: false,
priority: 3,
@@ -3725,7 +3725,7 @@ namespace ts {
);
}
const paramHelper: EmitHelper = {
export const paramHelper: UnscopedEmitHelpers = {
name: "typescript:param",
scoped: false,
priority: 4,

View File

@@ -2777,6 +2777,7 @@ namespace ts {
fileName?: string;
text: string;
prologues: ReadonlyArray<UnparsedPrologue>;
helpers: ReadonlyArray<UnscopedEmitHelpers> | undefined;
sourceMapPath?: string;
sourceMapText?: string;
/*@internal*/ parsedSourceMap?: RawSourceMap | false | undefined;
@@ -5189,6 +5190,11 @@ namespace ts {
readonly priority?: number; // Helpers with a higher priority are emitted earlier than other helpers on the node.
}
export interface UnscopedEmitHelpers extends EmitHelper {
readonly scoped: false; // Indicates whether the helper MUST be emitted in the current scope.
readonly text: string; // ES3-compatible raw script text, or a function yielding such a string
}
/* @internal */
export type UniqueNameHandler = (baseName: string, checkFn?: (name: string) => boolean, optimistic?: boolean) => string;