Merge branch 'master' into add-codefix-cannot-find-name-in-for-loop

This commit is contained in:
Florian Regensburger
2019-07-02 00:41:27 +02:00
1356 changed files with 59797 additions and 13031 deletions
+36 -28
View File
@@ -274,6 +274,9 @@ namespace ts {
if (isStringOrNumericLiteralLike(nameExpression)) {
return escapeLeadingUnderscores(nameExpression.text);
}
if (isSignedNumericLiteral(nameExpression)) {
return tokenToString(nameExpression.operator) + nameExpression.operand.text as __String;
}
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
return getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>nameExpression).name));
@@ -2515,7 +2518,7 @@ namespace ts {
break;
default:
Debug.fail(Debug.showSyntaxKind(thisContainer));
Debug.failBadSyntaxKind(thisContainer);
}
}
@@ -2581,7 +2584,7 @@ namespace ts {
// Fix up parent pointers since we're going to use these nodes before we bind into them
node.left.parent = node;
node.right.parent = node;
if (isIdentifier(lhs.expression) && container === file && isNameOfExportsOrModuleExportsAliasDeclaration(file, lhs.expression)) {
if (isIdentifier(lhs.expression) && container === file && isExportsOrModuleExportsOrAlias(file, lhs.expression)) {
// This can be an alias for the 'exports' or 'module.exports' names, e.g.
// var util = module.exports;
// util.property = function ...
@@ -2975,21 +2978,27 @@ namespace ts {
}
export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean {
return isExportsIdentifier(node) ||
isModuleExportsPropertyAccessExpression(node) ||
isIdentifier(node) && isNameOfExportsOrModuleExportsAliasDeclaration(sourceFile, node);
}
function isNameOfExportsOrModuleExportsAliasDeclaration(sourceFile: SourceFile, node: Identifier): boolean {
const symbol = lookupSymbolForNameWorker(sourceFile, node.escapedText);
return !!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) &&
!!symbol.valueDeclaration.initializer && isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, symbol.valueDeclaration.initializer);
}
function isExportsOrModuleExportsOrAliasOrAssignment(sourceFile: SourceFile, node: Expression): boolean {
return isExportsOrModuleExportsOrAlias(sourceFile, node) ||
(isAssignmentExpression(node, /*excludeCompoundAssignment*/ true) && (
isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, node.left) || isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, node.right)));
let i = 0;
const q = [node];
while (q.length && i < 100) {
i++;
node = q.shift()!;
if (isExportsIdentifier(node) || isModuleExportsPropertyAccessExpression(node)) {
return true;
}
else if (isIdentifier(node)) {
const symbol = lookupSymbolForNameWorker(sourceFile, node.escapedText);
if (!!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && !!symbol.valueDeclaration.initializer) {
const init = symbol.valueDeclaration.initializer;
q.push(init);
if (isAssignmentExpression(init, /*excludeCompoundAssignment*/ true)) {
q.push(init.left);
q.push(init.right);
}
}
}
}
return false;
}
function lookupSymbolForNameWorker(container: Node, name: __String): Symbol | undefined {
@@ -3223,8 +3232,7 @@ namespace ts {
// A ClassDeclaration is ES6 syntax.
transformFlags = subtreeFlags | TransformFlags.AssertES2015;
// A class with a parameter property assignment, property initializer, computed property name, or decorator is
// TypeScript syntax.
// A class with a parameter property assignment or decorator is TypeScript syntax.
// An exported declaration may be TypeScript syntax, but is handled by the visitor
// for a namespace declaration.
if ((subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax)
@@ -3241,8 +3249,7 @@ namespace ts {
// A ClassExpression is ES6 syntax.
let transformFlags = subtreeFlags | TransformFlags.AssertES2015;
// A class with a parameter property assignment, property initializer, or decorator is
// TypeScript syntax.
// A class with a parameter property assignment or decorator is TypeScript syntax.
if (subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax
|| node.typeParameters) {
transformFlags |= TransformFlags.AssertTypeScript;
@@ -3332,7 +3339,6 @@ namespace ts {
|| hasModifier(node, ModifierFlags.TypeScriptModifier)
|| node.typeParameters
|| node.type
|| (node.name && isComputedPropertyName(node.name)) // While computed method names aren't typescript, the TS transform must visit them to emit property declarations correctly
|| !node.body) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -3363,7 +3369,6 @@ namespace ts {
if (node.decorators
|| hasModifier(node, ModifierFlags.TypeScriptModifier)
|| node.type
|| (node.name && isComputedPropertyName(node.name)) // While computed accessor names aren't typescript, the TS transform must visit them to emit property declarations correctly
|| !node.body) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -3378,12 +3383,15 @@ namespace ts {
}
function computePropertyDeclaration(node: PropertyDeclaration, subtreeFlags: TransformFlags) {
// A PropertyDeclaration is TypeScript syntax.
let transformFlags = subtreeFlags | TransformFlags.AssertTypeScript;
let transformFlags = subtreeFlags | TransformFlags.ContainsClassFields;
// If the PropertyDeclaration has an initializer or a computed name, we need to inform its ancestor
// so that it handle the transformation.
if (node.initializer || isComputedPropertyName(node.name)) {
// Decorators, TypeScript-specific modifiers, and type annotations are TypeScript syntax.
if (some(node.decorators) || hasModifier(node, ModifierFlags.TypeScriptModifier) || node.type) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// Hoisted variables related to class properties should live within the TypeScript class wrapper.
if (isComputedPropertyName(node.name) || (hasStaticModifier(node) && node.initializer)) {
transformFlags |= TransformFlags.ContainsTypeScriptClassSyntax;
}
+139 -70
View File
@@ -10,18 +10,13 @@ namespace ts {
export interface ReusableDiagnosticRelatedInformation {
category: DiagnosticCategory;
code: number;
file: Path | undefined;
file: string | undefined;
start: number | undefined;
length: number | undefined;
messageText: string | ReusableDiagnosticMessageChain;
}
export interface ReusableDiagnosticMessageChain {
messageText: string;
category: DiagnosticCategory;
code: number;
next?: ReusableDiagnosticMessageChain;
}
export type ReusableDiagnosticMessageChain = DiagnosticMessageChain;
export interface ReusableBuilderProgramState extends ReusableBuilderState {
/**
@@ -227,7 +222,7 @@ namespace ts {
// Unchanged file copy diagnostics
const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
if (diagnostics) {
state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as ReadonlyArray<ReusableDiagnostic>, newProgram) : diagnostics as ReadonlyArray<Diagnostic>);
state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as ReadonlyArray<ReusableDiagnostic>, newProgram, getCanonicalFileName) : diagnostics as ReadonlyArray<Diagnostic>);
if (!state.semanticDiagnosticsFromOldState) {
state.semanticDiagnosticsFromOldState = createMap<true>();
}
@@ -246,37 +241,32 @@ namespace ts {
return state;
}
function convertToDiagnostics(diagnostics: ReadonlyArray<ReusableDiagnostic>, newProgram: Program): ReadonlyArray<Diagnostic> {
function convertToDiagnostics(diagnostics: ReadonlyArray<ReusableDiagnostic>, newProgram: Program, getCanonicalFileName: GetCanonicalFileName): ReadonlyArray<Diagnostic> {
if (!diagnostics.length) return emptyArray;
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getOutputPathForBuildInfo(newProgram.getCompilerOptions())!, newProgram.getCurrentDirectory()));
return diagnostics.map(diagnostic => {
const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram);
const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram, toPath);
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
result.source = diagnostic.source;
const { relatedInformation } = diagnostic;
result.relatedInformation = relatedInformation ?
relatedInformation.length ?
relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram)) :
relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram, toPath)) :
emptyArray :
undefined;
return result;
});
function toPath(path: string) {
return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
}
}
function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program): DiagnosticRelatedInformation {
const { file, messageText } = diagnostic;
function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program, toPath: (path: string) => Path): DiagnosticRelatedInformation {
const { file } = diagnostic;
return {
...diagnostic,
file: file && newProgram.getSourceFileByPath(file),
messageText: messageText === undefined || isString(messageText) ?
messageText :
convertToDiagnosticMessageChain(messageText, newProgram)
};
}
function convertToDiagnosticMessageChain(diagnostic: ReusableDiagnosticMessageChain, newProgram: Program): DiagnosticMessageChain {
return {
...diagnostic,
next: diagnostic.next && convertToDiagnosticMessageChain(diagnostic.next, newProgram)
file: file ? newProgram.getSourceFileByPath(toPath(file)) : undefined
};
}
@@ -620,19 +610,23 @@ namespace ts {
/**
* Gets the program information to be emitted in buildInfo so that we can use it to create new program
*/
function getProgramBuildInfo(state: Readonly<ReusableBuilderProgramState>): ProgramBuildInfo | undefined {
function getProgramBuildInfo(state: Readonly<ReusableBuilderProgramState>, getCanonicalFileName: GetCanonicalFileName): ProgramBuildInfo | undefined {
if (state.compilerOptions.outFile || state.compilerOptions.out) return undefined;
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getOutputPathForBuildInfo(state.compilerOptions)!, Debug.assertDefined(state.program).getCurrentDirectory()));
const fileInfos: MapLike<BuilderState.FileInfo> = {};
state.fileInfos.forEach((value, key) => {
const signature = state.currentAffectedFilesSignatures && state.currentAffectedFilesSignatures.get(key);
fileInfos[key] = signature === undefined ? value : { version: value.version, signature };
fileInfos[relativeToBuildInfo(key)] = signature === undefined ? value : { version: value.version, signature };
});
const result: ProgramBuildInfo = { fileInfos, options: state.compilerOptions };
const result: ProgramBuildInfo = {
fileInfos,
options: convertToReusableCompilerOptions(state.compilerOptions, relativeToBuildInfo)
};
if (state.referencedMap) {
const referencedMap: MapLike<string[]> = {};
state.referencedMap.forEach((value, key) => {
referencedMap[key] = arrayFrom(value.keys());
referencedMap[relativeToBuildInfo(key)] = arrayFrom(value.keys(), relativeToBuildInfo);
});
result.referencedMap = referencedMap;
}
@@ -642,9 +636,9 @@ namespace ts {
state.exportedModulesMap.forEach((value, key) => {
const newValue = state.currentAffectedFilesExportedModulesMap && state.currentAffectedFilesExportedModulesMap.get(key);
// Not in temporary cache, use existing value
if (newValue === undefined) exportedModulesMap[key] = arrayFrom(value.keys());
if (newValue === undefined) exportedModulesMap[relativeToBuildInfo(key)] = arrayFrom(value.keys(), relativeToBuildInfo);
// Value in cache and has updated value map, use that
else if (newValue) exportedModulesMap[key] = arrayFrom(newValue.keys());
else if (newValue) exportedModulesMap[relativeToBuildInfo(key)] = arrayFrom(newValue.keys(), relativeToBuildInfo);
});
result.exportedModulesMap = exportedModulesMap;
}
@@ -655,50 +649,78 @@ namespace ts {
state.semanticDiagnosticsPerFile.forEach((value, key) => semanticDiagnosticsPerFile.push(
value.length ?
[
key,
relativeToBuildInfo(key),
state.hasReusableDiagnostic ?
value as ReadonlyArray<ReusableDiagnostic> :
convertToReusableDiagnostics(value as ReadonlyArray<Diagnostic>)
convertToReusableDiagnostics(value as ReadonlyArray<Diagnostic>, relativeToBuildInfo)
] :
key
relativeToBuildInfo(key)
));
result.semanticDiagnosticsPerFile = semanticDiagnosticsPerFile;
}
return result;
function relativeToBuildInfo(path: string) {
return ensurePathIsNonModuleName(getRelativePathFromDirectory(buildInfoDirectory, path, getCanonicalFileName));
}
}
function convertToReusableDiagnostics(diagnostics: ReadonlyArray<Diagnostic>): ReadonlyArray<ReusableDiagnostic> {
function convertToReusableCompilerOptions(options: CompilerOptions, relativeToBuildInfo: (path: string) => string) {
const result: CompilerOptions = {};
const optionsNameMap = getOptionNameMap().optionNameMap;
for (const name in options) {
if (hasProperty(options, name)) {
result[name] = convertToReusableCompilerOptionValue(
optionsNameMap.get(name.toLowerCase()),
options[name] as CompilerOptionsValue,
relativeToBuildInfo
);
}
}
if (result.configFilePath) {
result.configFilePath = relativeToBuildInfo(result.configFilePath);
}
return result;
}
function convertToReusableCompilerOptionValue(option: CommandLineOption | undefined, value: CompilerOptionsValue, relativeToBuildInfo: (path: string) => string) {
if (option) {
if (option.type === "list") {
const values = value as ReadonlyArray<string | number>;
if (option.element.isFilePath && values.length) {
return values.map(relativeToBuildInfo);
}
}
else if (option.isFilePath) {
return relativeToBuildInfo(value as string);
}
}
return value;
}
function convertToReusableDiagnostics(diagnostics: ReadonlyArray<Diagnostic>, relativeToBuildInfo: (path: string) => string): ReadonlyArray<ReusableDiagnostic> {
Debug.assert(!!diagnostics.length);
return diagnostics.map(diagnostic => {
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic);
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic, relativeToBuildInfo);
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
result.source = diagnostic.source;
const { relatedInformation } = diagnostic;
result.relatedInformation = relatedInformation ?
relatedInformation.length ?
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r)) :
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r, relativeToBuildInfo)) :
emptyArray :
undefined;
return result;
});
}
function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation): ReusableDiagnosticRelatedInformation {
const { file, messageText } = diagnostic;
function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation, relativeToBuildInfo: (path: string) => string): ReusableDiagnosticRelatedInformation {
const { file } = diagnostic;
return {
...diagnostic,
file: file && file.path,
messageText: messageText === undefined || isString(messageText) ?
messageText :
convertToReusableDiagnosticMessageChain(messageText)
};
}
function convertToReusableDiagnosticMessageChain(diagnostic: DiagnosticMessageChain): ReusableDiagnosticMessageChain {
return {
...diagnostic,
next: diagnostic.next && convertToReusableDiagnosticMessageChain(diagnostic.next)
file: file ? relativeToBuildInfo(file.path) : undefined
};
}
@@ -767,7 +789,7 @@ namespace ts {
const computeHash = host.createHash || generateDjb2Hash;
let state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState);
let backupState: BuilderProgramState | undefined;
newProgram.getProgramBuildInfo = () => getProgramBuildInfo(state);
newProgram.getProgramBuildInfo = () => getProgramBuildInfo(state, getCanonicalFileName);
// To ensure that we arent storing any references to old program or new program without state
newProgram = undefined!; // TODO: GH#18217
@@ -796,6 +818,7 @@ namespace ts {
(result as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
}
else if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
(result as EmitAndSemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
(result as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile;
}
else {
@@ -913,6 +936,11 @@ namespace ts {
);
}
// Add file to affected file pending emit to handle for later emit time
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
addToAffectedFilesPendingEmit(state, [(affected as SourceFile).path]);
}
// Get diagnostics for the affected file if its not ignored
if (ignoreSourceFile && ignoreSourceFile(affected as SourceFile)) {
// Get next affected file
@@ -951,18 +979,8 @@ namespace ts {
// When semantic builder asks for diagnostics of the whole program,
// ensure that all the affected files are handled
let affected: SourceFile | Program | undefined;
let affectedFilesPendingEmit: Path[] | undefined;
while (affected = getNextAffectedFile(state, cancellationToken, computeHash)) {
if (affected !== state.program && kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
(affectedFilesPendingEmit || (affectedFilesPendingEmit = [])).push((affected as SourceFile).path);
}
doneWithAffectedFile(state, affected);
}
// In case of emit builder, cache the files to be emitted
if (affectedFilesPendingEmit) {
addToAffectedFilesPendingEmit(state, affectedFilesPendingEmit);
// tslint:disable-next-line no-empty
while (getSemanticDiagnosticsOfNextAffectedFile(cancellationToken)) {
}
let diagnostics: Diagnostic[] | undefined;
@@ -984,26 +1002,35 @@ namespace ts {
}
}
function getMapOfReferencedSet(mapLike: MapLike<ReadonlyArray<string>> | undefined): ReadonlyMap<BuilderState.ReferencedSet> | undefined {
function getMapOfReferencedSet(mapLike: MapLike<ReadonlyArray<string>> | undefined, toPath: (path: string) => Path): ReadonlyMap<BuilderState.ReferencedSet> | undefined {
if (!mapLike) return undefined;
const map = createMap<BuilderState.ReferencedSet>();
// Copies keys/values from template. Note that for..in will not throw if
// template is undefined, and instead will just exit the loop.
for (const key in mapLike) {
if (hasProperty(mapLike, key)) {
map.set(key, arrayToSet(mapLike[key]));
map.set(toPath(key), arrayToSet(mapLike[key], toPath));
}
}
return map;
}
export function createBuildProgramUsingProgramBuildInfo(program: ProgramBuildInfo): EmitAndSemanticDiagnosticsBuilderProgram & SemanticDiagnosticsBuilderProgram {
const fileInfos = createMapFromTemplate(program.fileInfos);
export function createBuildProgramUsingProgramBuildInfo(program: ProgramBuildInfo, buildInfoPath: string, host: ReadBuildProgramHost): EmitAndSemanticDiagnosticsBuilderProgram {
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
const fileInfos = createMap<BuilderState.FileInfo>();
for (const key in program.fileInfos) {
if (hasProperty(program.fileInfos, key)) {
fileInfos.set(toPath(key), program.fileInfos[key]);
}
}
const state: ReusableBuilderProgramState = {
fileInfos,
compilerOptions: program.options,
referencedMap: getMapOfReferencedSet(program.referencedMap),
exportedModulesMap: getMapOfReferencedSet(program.exportedModulesMap),
compilerOptions: convertFromReusableCompilerOptions(program.options, toAbsolutePath),
referencedMap: getMapOfReferencedSet(program.referencedMap, toPath),
exportedModulesMap: getMapOfReferencedSet(program.exportedModulesMap, toPath),
semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => isString(value) ? value : value[0], value => isString(value) ? emptyArray : value[1]),
hasReusableDiagnostic: true
};
@@ -1029,6 +1056,48 @@ namespace ts {
emitNextAffectedFile: notImplemented,
getSemanticDiagnosticsOfNextAffectedFile: notImplemented,
};
function toPath(path: string) {
return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
}
function toAbsolutePath(path: string) {
return getNormalizedAbsolutePath(path, buildInfoDirectory);
}
}
function convertFromReusableCompilerOptions(options: CompilerOptions, toAbsolutePath: (path: string) => string) {
const result: CompilerOptions = {};
const optionsNameMap = getOptionNameMap().optionNameMap;
for (const name in options) {
if (hasProperty(options, name)) {
result[name] = convertFromReusableCompilerOptionValue(
optionsNameMap.get(name.toLowerCase()),
options[name] as CompilerOptionsValue,
toAbsolutePath
);
}
}
if (result.configFilePath) {
result.configFilePath = toAbsolutePath(result.configFilePath);
}
return result;
}
function convertFromReusableCompilerOptionValue(option: CommandLineOption | undefined, value: CompilerOptionsValue, toAbsolutePath: (path: string) => string) {
if (option) {
if (option.type === "list") {
const values = value as ReadonlyArray<string | number>;
if (option.element.isFilePath && values.length) {
return values.map(toAbsolutePath);
}
}
else if (option.isFilePath) {
return toAbsolutePath(value as string);
}
}
return value;
}
export function createRedirectedBuilderProgram(state: { program: Program | undefined; compilerOptions: CompilerOptions; }, configFileParsingDiagnostics: ReadonlyArray<Diagnostic>): BuilderProgram {
@@ -1181,7 +1250,7 @@ namespace ts {
* The builder that can handle the changes in program and iterate through changed file to emit the files
* The semantic diagnostics are cached per file and managed by clearing for the changed/affected files
*/
export interface EmitAndSemanticDiagnosticsBuilderProgram extends BuilderProgram {
export interface EmitAndSemanticDiagnosticsBuilderProgram extends SemanticDiagnosticsBuilderProgram {
/**
* Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
+762 -369
View File
File diff suppressed because it is too large Load Diff
+14 -12
View File
@@ -147,6 +147,12 @@ namespace ts {
category: Diagnostics.Basic_Options,
description: Diagnostics.Enable_incremental_compilation,
},
{
name: "locale",
type: "string",
category: Diagnostics.Advanced_Options,
description: Diagnostics.The_locale_used_when_displaying_messages_to_the_user_e_g_en_us
},
];
/* @internal */
@@ -239,6 +245,7 @@ namespace ts {
esnext: ModuleKind.ESNext
}),
affectsModuleResolution: true,
affectsEmit: true,
paramType: Diagnostics.KIND,
showInSimplifiedHelpView: true,
category: Diagnostics.Basic_Options,
@@ -584,6 +591,7 @@ namespace ts {
name: "esModuleInterop",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsEmit: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.Enables_emit_interoperability_between_CommonJS_and_ES_Modules_via_creation_of_namespace_objects_for_all_imports_Implies_allowSyntheticDefaultImports
@@ -698,12 +706,6 @@ namespace ts {
category: Diagnostics.Advanced_Options,
description: Diagnostics.Emit_a_UTF_8_Byte_Order_Mark_BOM_in_the_beginning_of_output_files
},
{
name: "locale",
type: "string",
category: Diagnostics.Advanced_Options,
description: Diagnostics.The_locale_used_when_displaying_messages_to_the_user_e_g_en_us
},
{
name: "newLine",
type: createMapFromTemplate({
@@ -969,7 +971,8 @@ namespace ts {
return typeAcquisition;
}
function getOptionNameMap(): OptionNameMap {
/* @internal */
export function getOptionNameMap(): OptionNameMap {
return optionNameMapCache || (optionNameMapCache = createOptionNameMap(optionDeclarations));
}
@@ -1022,8 +1025,7 @@ namespace ts {
}
}
/* @internal */
export interface OptionsBase {
interface OptionsBase {
[option: string]: CompilerOptionsValue | undefined;
}
@@ -1172,7 +1174,7 @@ namespace ts {
export interface ParsedBuildCommand {
buildOptions: BuildOptions;
projects: string[];
errors: ReadonlyArray<Diagnostic>;
errors: Diagnostic[];
}
/*@internal*/
@@ -2361,7 +2363,7 @@ namespace ts {
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, Extension.Json)) {
extendedConfigPath = `${extendedConfigPath}.json`;
if (!host.fileExists(extendedConfigPath)) {
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig));
return undefined;
}
}
@@ -2372,7 +2374,7 @@ namespace ts {
if (resolved.resolvedModule) {
return resolved.resolvedModule.resolvedFileName;
}
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig));
return undefined;
}
+1 -84
View File
@@ -1,7 +1,7 @@
namespace ts {
// WARNING: The script `configureNightly.ts` uses a regexp to parse out these values.
// If changing the text in this section, be sure to test `configureNightly` too.
export const versionMajorMinor = "3.5";
export const versionMajorMinor = "3.6";
/** The version of the TypeScript compiler release */
export const version = `${versionMajorMinor}.0-dev`;
}
@@ -1686,89 +1686,6 @@ namespace ts {
export type AnyFunction = (...args: never[]) => void;
export type AnyConstructor = new (...args: unknown[]) => unknown;
export namespace Debug {
export let currentAssertionLevel = AssertionLevel.None;
export let isDebugging = false;
export function shouldAssert(level: AssertionLevel): boolean {
return currentAssertionLevel >= level;
}
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): void {
if (!expression) {
if (verboseDebugInfo) {
message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
}
fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert);
}
}
export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string): void {
if (a !== b) {
const message = msg ? msg2 ? `${msg} ${msg2}` : msg : "";
fail(`Expected ${a} === ${b}. ${message}`);
}
}
export function assertLessThan(a: number, b: number, msg?: string): void {
if (a >= b) {
fail(`Expected ${a} < ${b}. ${msg || ""}`);
}
}
export function assertLessThanOrEqual(a: number, b: number): void {
if (a > b) {
fail(`Expected ${a} <= ${b}`);
}
}
export function assertGreaterThanOrEqual(a: number, b: number): void {
if (a < b) {
fail(`Expected ${a} >= ${b}`);
}
}
export function fail(message?: string, stackCrawlMark?: AnyFunction): never {
debugger;
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
if ((<any>Error).captureStackTrace) {
(<any>Error).captureStackTrace(e, stackCrawlMark || fail);
}
throw e;
}
export function assertDefined<T>(value: T | null | undefined, message?: string): T {
if (value === undefined || value === null) return fail(message);
return value;
}
export function assertEachDefined<T, A extends ReadonlyArray<T>>(value: A, message?: string): A {
for (const v of value) {
assertDefined(v, message);
}
return value;
}
export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never {
const detail = typeof member === "object" && "kind" in member && "pos" in member ? "SyntaxKind: " + showSyntaxKind(member as Node) : JSON.stringify(member);
return fail(`${message} ${detail}`, stackCrawlMark || assertNever);
}
export function getFunctionName(func: AnyFunction) {
if (typeof func !== "function") {
return "";
}
else if (func.hasOwnProperty("name")) {
return (<any>func).name;
}
else {
const text = Function.prototype.toString.call(func);
const match = /^function\s+([\w\$]+)\s*\(/.exec(text);
return match ? match[1] : "";
}
}
}
export function equateValues<T>(a: T, b: T) {
return a === b;
}
+261
View File
@@ -0,0 +1,261 @@
/* @internal */
namespace ts {
export namespace Debug {
export let currentAssertionLevel = AssertionLevel.None;
export let isDebugging = false;
export function shouldAssert(level: AssertionLevel): boolean {
return currentAssertionLevel >= level;
}
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): void {
if (!expression) {
if (verboseDebugInfo) {
message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
}
fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert);
}
}
export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string): void {
if (a !== b) {
const message = msg ? msg2 ? `${msg} ${msg2}` : msg : "";
fail(`Expected ${a} === ${b}. ${message}`);
}
}
export function assertLessThan(a: number, b: number, msg?: string): void {
if (a >= b) {
fail(`Expected ${a} < ${b}. ${msg || ""}`);
}
}
export function assertLessThanOrEqual(a: number, b: number): void {
if (a > b) {
fail(`Expected ${a} <= ${b}`);
}
}
export function assertGreaterThanOrEqual(a: number, b: number): void {
if (a < b) {
fail(`Expected ${a} >= ${b}`);
}
}
export function fail(message?: string, stackCrawlMark?: AnyFunction): never {
debugger;
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
if ((<any>Error).captureStackTrace) {
(<any>Error).captureStackTrace(e, stackCrawlMark || fail);
}
throw e;
}
export function assertDefined<T>(value: T | null | undefined, message?: string): T {
if (value === undefined || value === null) return fail(message);
return value;
}
export function assertEachDefined<T, A extends ReadonlyArray<T>>(value: A, message?: string): A {
for (const v of value) {
assertDefined(v, message);
}
return value;
}
export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never {
const detail = typeof member === "object" && "kind" in member && "pos" in member && formatSyntaxKind ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member);
return fail(`${message} ${detail}`, stackCrawlMark || assertNever);
}
export function getFunctionName(func: AnyFunction) {
if (typeof func !== "function") {
return "";
}
else if (func.hasOwnProperty("name")) {
return (<any>func).name;
}
else {
const text = Function.prototype.toString.call(func);
const match = /^function\s+([\w\$]+)\s*\(/.exec(text);
return match ? match[1] : "";
}
}
export function formatSymbol(symbol: Symbol): string {
return `{ name: ${unescapeLeadingUnderscores(symbol.escapedName)}; flags: ${formatSymbolFlags(symbol.flags)}; declarations: ${map(symbol.declarations, node => formatSyntaxKind(node.kind))} }`;
}
/**
* Formats an enum value as a string for debugging and debug assertions.
*/
export function formatEnum(value = 0, enumObject: any, isFlags?: boolean) {
const members = getEnumMembers(enumObject);
if (value === 0) {
return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0";
}
if (isFlags) {
let result = "";
let remainingFlags = value;
for (let i = members.length - 1; i >= 0 && remainingFlags !== 0; i--) {
const [enumValue, enumName] = members[i];
if (enumValue !== 0 && (remainingFlags & enumValue) === enumValue) {
remainingFlags &= ~enumValue;
result = `${enumName}${result ? "|" : ""}${result}`;
}
}
if (remainingFlags === 0) {
return result;
}
}
else {
for (const [enumValue, enumName] of members) {
if (enumValue === value) {
return enumName;
}
}
}
return value.toString();
}
function getEnumMembers(enumObject: any) {
const result: [number, string][] = [];
for (const name in enumObject) {
const value = enumObject[name];
if (typeof value === "number") {
result.push([value, name]);
}
}
return stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0]));
}
export function formatSyntaxKind(kind: SyntaxKind | undefined): string {
return formatEnum(kind, (<any>ts).SyntaxKind, /*isFlags*/ false);
}
export function formatNodeFlags(flags: NodeFlags | undefined): string {
return formatEnum(flags, (<any>ts).NodeFlags, /*isFlags*/ true);
}
export function formatModifierFlags(flags: ModifierFlags | undefined): string {
return formatEnum(flags, (<any>ts).ModifierFlags, /*isFlags*/ true);
}
export function formatTransformFlags(flags: TransformFlags | undefined): string {
return formatEnum(flags, (<any>ts).TransformFlags, /*isFlags*/ true);
}
export function formatEmitFlags(flags: EmitFlags | undefined): string {
return formatEnum(flags, (<any>ts).EmitFlags, /*isFlags*/ true);
}
export function formatSymbolFlags(flags: SymbolFlags | undefined): string {
return formatEnum(flags, (<any>ts).SymbolFlags, /*isFlags*/ true);
}
export function formatTypeFlags(flags: TypeFlags | undefined): string {
return formatEnum(flags, (<any>ts).TypeFlags, /*isFlags*/ true);
}
export function formatObjectFlags(flags: ObjectFlags | undefined): string {
return formatEnum(flags, (<any>ts).ObjectFlags, /*isFlags*/ true);
}
export function failBadSyntaxKind(node: Node, message?: string): never {
return fail(
`${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`,
failBadSyntaxKind);
}
export const assertEachNode = shouldAssert(AssertionLevel.Normal)
? (nodes: Node[], test: (node: Node) => boolean, message?: string): void => assert(
test === undefined || every(nodes, test),
message || "Unexpected node.",
() => `Node array did not pass test '${getFunctionName(test)}'.`,
assertEachNode)
: noop;
export const assertNode = shouldAssert(AssertionLevel.Normal)
? (node: Node | undefined, test: ((node: Node | undefined) => boolean) | undefined, message?: string): void => assert(
test === undefined || test(node),
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node!.kind)} did not pass test '${getFunctionName(test!)}'.`,
assertNode)
: noop;
export const assertOptionalNode = shouldAssert(AssertionLevel.Normal)
? (node: Node, test: (node: Node) => boolean, message?: string): void => assert(
test === undefined || node === undefined || test(node),
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`,
assertOptionalNode)
: noop;
export const assertOptionalToken = shouldAssert(AssertionLevel.Normal)
? (node: Node, kind: SyntaxKind, message?: string): void => assert(
kind === undefined || node === undefined || node.kind === kind,
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} was not a '${formatSyntaxKind(kind)}' token.`,
assertOptionalToken)
: noop;
export const assertMissingNode = shouldAssert(AssertionLevel.Normal)
? (node: Node, message?: string): void => assert(
node === undefined,
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} was unexpected'.`,
assertMissingNode)
: noop;
let isDebugInfoEnabled = false;
/**
* Injects debug information into frequently used types.
*/
export function enableDebugInfo() {
if (isDebugInfoEnabled) return;
// Add additional properties in debug mode to assist with debugging.
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
__debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
});
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
__debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } },
__debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((<ObjectType>this).objectFlags) : ""; } },
__debugTypeToString: { value(this: Type) { return this.checker.typeToString(this); } },
});
const nodeConstructors = [
objectAllocator.getNodeConstructor(),
objectAllocator.getIdentifierConstructor(),
objectAllocator.getTokenConstructor(),
objectAllocator.getSourceFileConstructor()
];
for (const ctor of nodeConstructors) {
if (!ctor.prototype.hasOwnProperty("__debugKind")) {
Object.defineProperties(ctor.prototype, {
__debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } },
__debugNodeFlags: { get(this: Node) { return formatNodeFlags(this.flags); } },
__debugModifierFlags: { get(this: Node) { return formatModifierFlags(getModifierFlagsNoCache(this)); } },
__debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
__debugIsParseTreeNode: { get(this: Node) { return isParseTreeNode(this); } },
__debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
__debugGetText: {
value(this: Node, includeTrivia?: boolean) {
if (nodeIsSynthesized(this)) return "";
const parseNode = getParseTreeNode(this);
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
}
}
});
}
}
isDebugInfoEnabled = true;
}
}
}
+70 -10
View File
@@ -655,7 +655,7 @@
"category": "Error",
"code": 1207
},
"Cannot compile namespaces when the '--isolatedModules' flag is provided.": {
"All files must be modules when the '--isolatedModules' flag is provided.": {
"category": "Error",
"code": 1208
},
@@ -1236,7 +1236,7 @@
"category": "Error",
"code": 2348
},
"Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.": {
"This expression is not callable.": {
"category": "Error",
"code": 2349
},
@@ -1244,7 +1244,7 @@
"category": "Error",
"code": 2350
},
"Cannot use 'new' with an expression whose type lacks a call or construct signature.": {
"This expression is not constructable.": {
"category": "Error",
"code": 2351
},
@@ -2621,6 +2621,38 @@
"category": "Error",
"code": 2754
},
"No constituent of type '{0}' is callable.": {
"category": "Error",
"code": 2755
},
"Not all constituents of type '{0}' are callable.": {
"category": "Error",
"code": 2756
},
"Type '{0}' has no call signatures.": {
"category": "Error",
"code": 2757
},
"Each member of the union type '{0}' has signatures, but none of those signatures are compatible with each other.": {
"category": "Error",
"code": 2758
},
"No constituent of type '{0}' is constructable.": {
"category": "Error",
"code": 2759
},
"Not all constituents of type '{0}' are constructable.": {
"category": "Error",
"code": 2760
},
"Type '{0}' has no construct signatures.": {
"category": "Error",
"code": 2761
},
"Each member of the union type '{0}' has construct signatures, but none of those signatures are compatible with each other.": {
"category": "Error",
"code": 2762
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
@@ -2963,6 +2995,10 @@
"category": "Error",
"code": 4104
},
"Private or protected member '{0}' cannot be accessed on a type parameter.": {
"category": "Error",
"code": 4105
},
"The current host does not support the '{0}' option.": {
"category": "Error",
@@ -3811,10 +3847,6 @@
"category": "Error",
"code": 6189
},
"Found 'package.json' at '{0}'. Package ID is '{1}'.": {
"category": "Message",
"code": 6190
},
"Whether to keep outdated console output in watch mode instead of clearing the screen.": {
"category": "Message",
"code": 6191
@@ -3923,6 +3955,18 @@
"category": "Message",
"code": 6217
},
"======== Module name '{0}' was successfully resolved to '{1}' with Package ID '{2}'. ========": {
"category": "Message",
"code": 6218
},
"======== Type reference directive '{0}' was successfully resolved to '{1}' with Package ID '{2}', primary: {3}. ========": {
"category": "Message",
"code": 6219
},
"'package.json' had a falsy '{0}' field.": {
"category": "Message",
"code": 6220
},
"Projects to reference": {
"category": "Message",
@@ -3948,7 +3992,7 @@
"category": "Error",
"code": 6306
},
"File '{0}' is not in project file list. Projects must list all files or use an 'include' pattern.": {
"File '{0}' is not listed within the file list of project '{1}'. Projects must list all files or use an 'include' pattern.": {
"category": "Error",
"code": 6307
},
@@ -4284,6 +4328,14 @@
"category": "Error",
"code": 7052
},
"Element implicitly has an 'any' type because expression of type '{0}' can't be used to index type '{1}'.": {
"category": "Error",
"code": 7053
},
"No index signature with a parameter of type '{0}' was found on type '{1}'.": {
"category": "Error",
"code": 7054
},
"You cannot rename this element.": {
"category": "Error",
"code": 8000
@@ -4974,14 +5026,18 @@
"category": "Message",
"code": 95079
},
"Add 'const' to unresolved variable": {
"Infer 'this' type of '{0}' from usage": {
"category": "Message",
"code": 95080
},
"Add 'const' to all unresolved variables": {
"Add 'const' to unresolved variable": {
"category": "Message",
"code": 95081
},
"Add 'const' to all unresolved variables": {
"category": "Message",
"code": 95082
},
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
"category": "Error",
"code": 18004
@@ -4993,5 +5049,9 @@
"Classes may not have a field named 'constructor'.": {
"category": "Error",
"code": 18006
},
"JSX expressions may not use the comma operator. Did you mean to write an array?": {
"category": "Error",
"code": 18007
}
}
+57 -17
View File
@@ -249,14 +249,16 @@ namespace ts {
};
function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle | undefined) {
let buildInfoDirectory: string | undefined;
if (buildInfoPath && sourceFileOrBundle && isBundle(sourceFileOrBundle)) {
buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
bundleBuildInfo = {
commonSourceDirectory: host.getCommonSourceDirectory(),
sourceFiles: sourceFileOrBundle.sourceFiles.map(file => file.fileName)
commonSourceDirectory: relativeToBuildInfo(host.getCommonSourceDirectory()),
sourceFiles: sourceFileOrBundle.sourceFiles.map(file => relativeToBuildInfo(getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory())))
};
}
emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath);
emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath);
emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath, relativeToBuildInfo);
emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath, relativeToBuildInfo);
emitBuildInfo(bundleBuildInfo, buildInfoPath);
if (!emitSkipped && emittedFilesList) {
@@ -278,13 +280,16 @@ namespace ts {
emittedFilesList.push(declarationMapPath);
}
}
function relativeToBuildInfo(path: string) {
return ensurePathIsNonModuleName(getRelativePathFromDirectory(buildInfoDirectory!, path, host.getCanonicalFileName));
}
}
function emitBuildInfo(bundle: BundleBuildInfo | undefined, buildInfoPath: string | undefined) {
// Write build information if applicable
if (!buildInfoPath || targetSourceFile || emitSkipped) return;
const program = host.getProgramBuildInfo();
if (!bundle && !program) return;
if (host.isEmitBlocked(buildInfoPath) || compilerOptions.noEmit) {
emitSkipped = true;
return;
@@ -292,7 +297,11 @@ namespace ts {
writeFile(host, emitterDiagnostics, buildInfoPath, getBuildInfoText({ bundle, program, version }), /*writeByteOrderMark*/ false);
}
function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle | undefined, jsFilePath: string | undefined, sourceMapFilePath: string | undefined) {
function emitJsFileOrBundle(
sourceFileOrBundle: SourceFile | Bundle | undefined,
jsFilePath: string | undefined,
sourceMapFilePath: string | undefined,
relativeToBuildInfo: (path: string) => string) {
if (!sourceFileOrBundle || emitOnlyDtsFiles || !jsFilePath) {
return;
}
@@ -315,7 +324,8 @@ namespace ts {
inlineSourceMap: compilerOptions.inlineSourceMap,
inlineSources: compilerOptions.inlineSources,
extendedDiagnostics: compilerOptions.extendedDiagnostics,
writeBundleFileInfo: !!bundleBuildInfo
writeBundleFileInfo: !!bundleBuildInfo,
relativeToBuildInfo
};
// Create a printer to print the nodes
@@ -336,7 +346,11 @@ namespace ts {
if (bundleBuildInfo) bundleBuildInfo.js = printer.bundleFileInfo;
}
function emitDeclarationFileOrBundle(sourceFileOrBundle: SourceFile | Bundle | undefined, declarationFilePath: string | undefined, declarationMapPath: string | undefined) {
function emitDeclarationFileOrBundle(
sourceFileOrBundle: SourceFile | Bundle | undefined,
declarationFilePath: string | undefined,
declarationMapPath: string | undefined,
relativeToBuildInfo: (path: string) => string) {
if (!sourceFileOrBundle || !(declarationFilePath && !isInJSFile(sourceFileOrBundle))) {
return;
}
@@ -367,7 +381,8 @@ namespace ts {
extendedDiagnostics: compilerOptions.extendedDiagnostics,
onlyPrintJsDocStyle: true,
writeBundleFileInfo: !!bundleBuildInfo,
recordInternalSection: !!bundleBuildInfo
recordInternalSection: !!bundleBuildInfo,
relativeToBuildInfo
};
const declarationPrinter = createPrinter(printerOptions, {
@@ -614,10 +629,14 @@ namespace ts {
getNewLine(): string;
}
function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo): ReadonlyArray<SourceFile> {
function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo, buildInfoDirectory: string, host: EmitUsingBuildInfoHost): ReadonlyArray<SourceFile> {
const sourceFiles = bundle.sourceFiles.map(fileName => {
const sourceFile = createNode(SyntaxKind.SourceFile, 0, 0) as SourceFile;
sourceFile.fileName = fileName;
sourceFile.fileName = getRelativePathFromDirectory(
host.getCurrentDirectory(),
getNormalizedAbsolutePath(fileName, buildInfoDirectory),
!host.useCaseSensitiveFileNames()
);
sourceFile.text = "";
sourceFile.statements = createNodeArray();
return sourceFile;
@@ -638,7 +657,12 @@ namespace ts {
}
/*@internal*/
export function emitUsingBuildInfo(config: ParsedCommandLine, host: EmitUsingBuildInfoHost, getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined): EmitUsingBuildInfoResult {
export function emitUsingBuildInfo(
config: ParsedCommandLine,
host: EmitUsingBuildInfoHost,
getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined,
customTransformers?: CustomTransformers
): EmitUsingBuildInfoResult {
const { buildInfoPath, jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath } = getOutputPathsForBundle(config.options, /*forceDtsPaths*/ false);
const buildInfoText = host.readFile(Debug.assertDefined(buildInfoPath));
if (!buildInfoText) return buildInfoPath!;
@@ -656,6 +680,7 @@ namespace ts {
const buildInfo = getBuildInfo(buildInfoText);
if (!buildInfo.bundle || !buildInfo.bundle.js || (declarationText && !buildInfo.bundle.dts)) return buildInfoPath!;
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath!, host.getCurrentDirectory()));
const ownPrependInput = createInputFiles(
jsFileText,
declarationText!,
@@ -671,11 +696,11 @@ namespace ts {
);
const outputFiles: OutputFile[] = [];
const prependNodes = createPrependNodes(config.projectReferences, getCommandLine, f => host.readFile(f));
const sourceFilesForJsEmit = createSourceFilesFromBundleBuildInfo(buildInfo.bundle);
const sourceFilesForJsEmit = createSourceFilesFromBundleBuildInfo(buildInfo.bundle, buildInfoDirectory, host);
const emitHost: EmitHost = {
getPrependNodes: memoize(() => [...prependNodes, ownPrependInput]),
getCanonicalFileName: host.getCanonicalFileName,
getCommonSourceDirectory: () => buildInfo.bundle!.commonSourceDirectory,
getCommonSourceDirectory: () => getNormalizedAbsolutePath(buildInfo.bundle!.commonSourceDirectory, buildInfoDirectory),
getCompilerOptions: () => config.options,
getCurrentDirectory: () => host.getCurrentDirectory(),
getNewLine: () => host.getNewLine(),
@@ -721,9 +746,15 @@ namespace ts {
fileExists: f => host.fileExists(f),
directoryExists: host.directoryExists && (f => host.directoryExists!(f)),
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
getProgramBuildInfo: returnUndefined
getProgramBuildInfo: returnUndefined,
getSourceFileFromReference: returnUndefined,
};
emitFiles(notImplementedResolver, emitHost, /*targetSourceFile*/ undefined, getTransformers(config.options), /*emitOnlyDtsFiles*/ false);
emitFiles(
notImplementedResolver,
emitHost,
/*targetSourceFile*/ undefined,
getTransformers(config.options, customTransformers)
);
return outputFiles;
}
@@ -765,6 +796,7 @@ namespace ts {
let write = writeBase;
let isOwnFileEmit: boolean;
const bundleFileInfo = printerOptions.writeBundleFileInfo ? { sections: [] } as BundleFileInfo : undefined;
const relativeToBuildInfo = bundleFileInfo ? Debug.assertDefined(printerOptions.relativeToBuildInfo) : undefined;
const recordInternalSection = printerOptions.recordInternalSection;
let sourceFileTextPos = 0;
let sourceFileTextKind: BundleFileTextLikeKind = BundleFileSectionKind.Text;
@@ -933,7 +965,13 @@ namespace ts {
if (prepend.oldFileOfCurrentEmit) bundleFileInfo.sections.push(...newSections);
else {
newSections.forEach(section => Debug.assert(isBundleFileTextLike(section)));
bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prepend, data: (prepend as UnparsedSource).fileName, texts: newSections as BundleFileTextLike[] });
bundleFileInfo.sections.push({
pos,
end: writer.getTextPos(),
kind: BundleFileSectionKind.Prepend,
data: relativeToBuildInfo!((prepend as UnparsedSource).fileName),
texts: newSections as BundleFileTextLike[]
});
}
}
}
@@ -4527,6 +4565,8 @@ namespace ts {
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return generateNameForMethodOrAccessor(<MethodDeclaration | AccessorDeclaration>node);
case SyntaxKind.ComputedPropertyName:
return makeTempVariableName(TempFlags.Auto, /*reserveInNestedScopes*/ true);
default:
return makeTempVariableName(TempFlags.Auto);
}
+60 -9
View File
@@ -1544,8 +1544,8 @@ namespace ts {
export function createIf(expression: Expression, thenStatement: Statement, elseStatement?: Statement) {
const node = <IfStatement>createSynthesizedNode(SyntaxKind.IfStatement);
node.expression = expression;
node.thenStatement = thenStatement;
node.elseStatement = elseStatement;
node.thenStatement = asEmbeddedStatement(thenStatement);
node.elseStatement = asEmbeddedStatement(elseStatement);
return node;
}
@@ -1559,7 +1559,7 @@ namespace ts {
export function createDo(statement: Statement, expression: Expression) {
const node = <DoStatement>createSynthesizedNode(SyntaxKind.DoStatement);
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
node.expression = expression;
return node;
}
@@ -1574,7 +1574,7 @@ namespace ts {
export function createWhile(expression: Expression, statement: Statement) {
const node = <WhileStatement>createSynthesizedNode(SyntaxKind.WhileStatement);
node.expression = expression;
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -1590,7 +1590,7 @@ namespace ts {
node.initializer = initializer;
node.condition = condition;
node.incrementor = incrementor;
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -1607,7 +1607,7 @@ namespace ts {
const node = <ForInStatement>createSynthesizedNode(SyntaxKind.ForInStatement);
node.initializer = initializer;
node.expression = expression;
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -1624,7 +1624,7 @@ namespace ts {
node.awaitModifier = awaitModifier;
node.initializer = initializer;
node.expression = expression;
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -1676,7 +1676,7 @@ namespace ts {
export function createWith(expression: Expression, statement: Statement) {
const node = <WithStatement>createSynthesizedNode(SyntaxKind.WithStatement);
node.expression = expression;
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -1704,7 +1704,7 @@ namespace ts {
export function createLabel(label: string | Identifier, statement: Statement) {
const node = <LabeledStatement>createSynthesizedNode(SyntaxKind.LabeledStatement);
node.label = asName(label);
node.statement = statement;
node.statement = asEmbeddedStatement(statement);
return node;
}
@@ -2204,6 +2204,13 @@ namespace ts {
return tag;
}
/** @internal */
export function createJSDocThisTag(typeExpression?: JSDocTypeExpression): JSDocThisTag {
const tag = createJSDocTag<JSDocThisTag>(SyntaxKind.JSDocThisTag, "this");
tag.typeExpression = typeExpression;
return tag;
}
/* @internal */
export function createJSDocParamTag(name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, comment?: string): JSDocParameterTag {
const tag = createJSDocTag<JSDocParameterTag>(SyntaxKind.JSDocParameterTag, "param");
@@ -2657,6 +2664,7 @@ namespace ts {
valuesHelper,
readHelper,
spreadHelper,
spreadArraysHelper,
restHelper,
decorateHelper,
metadataHelper,
@@ -3072,6 +3080,12 @@ namespace ts {
return typeof value === "number" ? createToken(value) : value;
}
function asEmbeddedStatement<T extends Node>(statement: T): T | EmptyStatement;
function asEmbeddedStatement<T extends Node>(statement: T | undefined): T | EmptyStatement | undefined;
function asEmbeddedStatement<T extends Node>(statement: T | undefined): T | EmptyStatement | undefined {
return statement && isNotEmittedStatement(statement) ? setTextRange(setOriginalNode(createEmptyStatement(), statement), statement) : statement;
}
/**
* Clears any EmitNode entries from parse-tree nodes.
* @param sourceFile A source file.
@@ -3118,6 +3132,18 @@ namespace ts {
return node.emitNode;
}
/**
* Sets `EmitFlags.NoComments` on a node and removes any leading and trailing synthetic comments.
* @internal
*/
export function removeAllComments<T extends Node>(node: T): T {
const emitNode = getOrCreateEmitNode(node);
emitNode.flags |= EmitFlags.NoComments;
emitNode.leadingComments = undefined;
emitNode.trailingComments = undefined;
return node;
}
export function setTextRange<T extends TextRange>(range: T, location: TextRange | undefined): T {
if (location) {
range.pos = location.pos;
@@ -3693,6 +3719,31 @@ namespace ts {
);
}
export const spreadArraysHelper: UnscopedEmitHelper = {
name: "typescript:spreadArrays",
scoped: false,
text: `
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};`
};
export function createSpreadArraysHelper(context: TransformationContext, argumentList: ReadonlyArray<Expression>, location?: TextRange) {
context.requestEmitHelper(spreadArraysHelper);
return setTextRange(
createCall(
getHelperName("__spreadArrays"),
/*typeArguments*/ undefined,
argumentList
),
location
);
}
// Utilities
export function createForOfBindingStatement(node: ForInitializer, boundValue: Expression): Statement {
+76 -88
View File
@@ -16,12 +16,23 @@ namespace ts {
push(value: T): void;
}
function withPackageId(packageId: PackageId | undefined, r: PathAndExtension | undefined): Resolved | undefined {
function withPackageId(packageInfo: PackageJsonInfo | undefined, r: PathAndExtension | undefined): Resolved | undefined {
let packageId: PackageId | undefined;
if (r && packageInfo) {
const packageJsonContent = packageInfo.packageJsonContent as PackageJson;
if (typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string") {
packageId = {
name: packageJsonContent.name,
subModuleName: r.path.slice(packageInfo.packageDirectory.length + directorySeparator.length),
version: packageJsonContent.version
};
}
}
return r && { path: r.path, extension: r.ext, packageId };
}
function noPackageId(r: PathAndExtension | undefined): Resolved | undefined {
return withPackageId(/*packageId*/ undefined, r);
return withPackageId(/*packageInfo*/ undefined, r);
}
function removeIgnoredPackageId(r: Resolved | undefined): PathAndExtension | undefined {
@@ -130,7 +141,15 @@ namespace ts {
function readPackageJsonPathField<K extends "typings" | "types" | "main" | "tsconfig">(jsonContent: PackageJson, fieldName: K, baseDirectory: string, state: ModuleResolutionState): PackageJson[K] | undefined {
const fileName = readPackageJsonField(jsonContent, fieldName, "string", state);
if (fileName === undefined) return;
if (fileName === undefined) {
return;
}
if (!fileName) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_had_a_falsy_0_field, fieldName);
}
return;
}
const path = normalizePath(combinePaths(baseDirectory, fileName));
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, fileName, path);
@@ -307,7 +326,12 @@ namespace ts {
const { fileName, packageId } = resolved;
const resolvedFileName = options.preserveSymlinks ? fileName : realPath(fileName, host, traceEnabled);
if (traceEnabled) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFileName, primary);
if (packageId) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_with_Package_ID_2_primary_Colon_3, typeReferenceDirectiveName, resolvedFileName, packageIdToString(packageId), primary);
}
else {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFileName, primary);
}
}
resolvedTypeReferenceDirective = { primary, resolvedFileName, packageId, isExternalLibraryImport: pathContainsNodeModules(fileName) };
}
@@ -663,7 +687,12 @@ namespace ts {
if (traceEnabled) {
if (result.resolvedModule) {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
if (result.resolvedModule.packageId) {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1_with_Package_ID_2, moduleName, result.resolvedModule.resolvedFileName, packageIdToString(result.resolvedModule.packageId));
}
else {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
}
}
else {
trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName);
@@ -968,10 +997,9 @@ namespace ts {
}
const resolvedFromFile = loadModuleFromFile(extensions, candidate, onlyRecordFailures, state);
if (resolvedFromFile) {
const nm = considerPackageJson ? parseNodeModuleFromPath(resolvedFromFile) : undefined;
const packageInfo = nm && getPackageJsonInfo(nm.packageDirectory, nm.subModuleName, /*onlyRecordFailures*/ false, state);
const packageId = packageInfo && packageInfo.packageId;
return withPackageId(packageId, resolvedFromFile);
const packageDirectory = considerPackageJson ? parseNodeModuleFromPath(resolvedFromFile) : undefined;
const packageInfo = packageDirectory ? getPackageJsonInfo(packageDirectory, /*onlyRecordFailures*/ false, state) : undefined;
return withPackageId(packageInfo, resolvedFromFile);
}
}
if (!onlyRecordFailures) {
@@ -998,13 +1026,12 @@ namespace ts {
* (Not neeeded for `loadModuleFromNodeModules` as that looks up the `package.json` as part of resolution.)
*
* packageDirectory is the directory of the package itself.
* subModuleName is the path within the package.
* For `blah/node_modules/foo/index.d.ts` this is { packageDirectory: "foo", subModuleName: "index.d.ts" }. (Part before "/node_modules/" is ignored.)
* For `/node_modules/foo/bar.d.ts` this is { packageDirectory: "foo", subModuleName": "bar/index.d.ts" }.
* For `/node_modules/@types/foo/bar/index.d.ts` this is { packageDirectory: "@types/foo", subModuleName: "bar/index.d.ts" }.
* For `/node_modules/foo/bar/index.d.ts` this is { packageDirectory: "foo", subModuleName": "bar/index.d.ts" }.
* For `blah/node_modules/foo/index.d.ts` this is packageDirectory: "foo"
* For `/node_modules/foo/bar.d.ts` this is packageDirectory: "foo"
* For `/node_modules/@types/foo/bar/index.d.ts` this is packageDirectory: "@types/foo"
* For `/node_modules/foo/bar/index.d.ts` this is packageDirectory: "foo"
*/
function parseNodeModuleFromPath(resolved: PathAndExtension): { packageDirectory: string, subModuleName: string } | undefined {
function parseNodeModuleFromPath(resolved: PathAndExtension): string | undefined {
const path = normalizePath(resolved.path);
const idx = path.lastIndexOf(nodeModulesPathPart);
if (idx === -1) {
@@ -1016,9 +1043,7 @@ namespace ts {
if (path.charCodeAt(indexAfterNodeModules) === CharacterCodes.at) {
indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName);
}
const packageDirectory = path.slice(0, indexAfterPackageName);
const subModuleName = removeExtension(path.slice(indexAfterPackageName + 1), resolved.ext) + Extension.Dts;
return { packageDirectory, subModuleName };
return path.slice(0, indexAfterPackageName);
}
function moveToNextDirectorySeparatorIfAvailable(path: string, prevSeparatorIndex: number): number {
@@ -1026,19 +1051,6 @@ namespace ts {
return nextSeparatorIndex === -1 ? prevSeparatorIndex : nextSeparatorIndex;
}
function addExtensionAndIndex(path: string): string {
if (path === "") {
return "index.d.ts";
}
if (endsWith(path, ".d.ts")) {
return path;
}
if (path === "index" || endsWith(path, "/index")) {
return path + ".d.ts";
}
return path + "/index.d.ts";
}
function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
return noPackageId(loadModuleFromFile(extensions, candidate, onlyRecordFailures, state));
}
@@ -1119,61 +1131,29 @@ namespace ts {
}
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true) {
const packageInfo = considerPackageJson ? getPackageJsonInfo(candidate, "", onlyRecordFailures, state) : undefined;
const packageId = packageInfo && packageInfo.packageId;
const packageInfo = considerPackageJson ? getPackageJsonInfo(candidate, onlyRecordFailures, state) : undefined;
const packageJsonContent = packageInfo && packageInfo.packageJsonContent;
const versionPaths = packageInfo && packageInfo.versionPaths;
return withPackageId(packageId, loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths));
return withPackageId(packageInfo, loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths));
}
interface PackageJsonInfo {
packageJsonContent: PackageJsonPathFields | undefined;
packageId: PackageId | undefined;
packageDirectory: string;
packageJsonContent: PackageJsonPathFields;
versionPaths: VersionPaths | undefined;
}
function getPackageJsonInfo(packageDirectory: string, subModuleName: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined {
function getPackageJsonInfo(packageDirectory: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined {
const { host, traceEnabled } = state;
const directoryExists = !onlyRecordFailures && directoryProbablyExists(packageDirectory, host);
const packageJsonPath = combinePaths(packageDirectory, "package.json");
if (directoryExists && host.fileExists(packageJsonPath)) {
const packageJsonContent = readJson(packageJsonPath, host) as PackageJson;
if (subModuleName === "") { // looking up the root - need to handle types/typings/main redirects for subModuleName
const path = readPackageJsonTypesFields(packageJsonContent, packageDirectory, state);
if (typeof path === "string") {
subModuleName = addExtensionAndIndex(path.substring(packageDirectory.length + 1));
}
else {
const jsPath = readPackageJsonMainField(packageJsonContent, packageDirectory, state);
if (typeof jsPath === "string" && jsPath.length > packageDirectory.length) {
const potentialSubModule = jsPath.substring(packageDirectory.length + 1);
subModuleName = (forEach(supportedJSExtensions, extension =>
tryRemoveExtension(potentialSubModule, extension)) || potentialSubModule) + Extension.Dts;
}
else {
subModuleName = "index.d.ts";
}
}
}
if (!endsWith(subModuleName, Extension.Dts)) {
subModuleName = addExtensionAndIndex(subModuleName);
}
const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state);
const packageId: PackageId | undefined = typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string"
? { name: packageJsonContent.name, subModuleName, version: packageJsonContent.version }
: undefined;
if (traceEnabled) {
if (packageId) {
trace(host, Diagnostics.Found_package_json_at_0_Package_ID_is_1, packageJsonPath, packageIdToString(packageId));
}
else {
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
return { packageJsonContent, packageId, versionPaths };
const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state);
return { packageDirectory, packageJsonContent, versionPaths };
}
else {
if (directoryExists && traceEnabled) {
@@ -1328,27 +1308,36 @@ namespace ts {
const candidate = normalizePath(combinePaths(nodeModulesDirectory, moduleName));
// First look for a nested package.json, as in `node_modules/foo/bar/package.json`.
let packageJsonContent: PackageJsonPathFields | undefined;
let packageId: PackageId | undefined;
let versionPaths: VersionPaths | undefined;
const packageInfo = getPackageJsonInfo(candidate, "", !nodeModulesDirectoryExists, state);
let packageInfo = getPackageJsonInfo(candidate, !nodeModulesDirectoryExists, state);
if (packageInfo) {
({ packageJsonContent, packageId, versionPaths } = packageInfo);
const fromFile = loadModuleFromFile(extensions, candidate, !nodeModulesDirectoryExists, state);
if (fromFile) {
return noPackageId(fromFile);
}
const fromDirectory = loadNodeModuleFromDirectoryWorker(extensions, candidate, !nodeModulesDirectoryExists, state, packageJsonContent, versionPaths);
return withPackageId(packageId, fromDirectory);
const fromDirectory = loadNodeModuleFromDirectoryWorker(
extensions,
candidate,
!nodeModulesDirectoryExists,
state,
packageInfo.packageJsonContent,
packageInfo.versionPaths
);
return withPackageId(packageInfo, fromDirectory);
}
const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => {
const pathAndExtension =
loadModuleFromFile(extensions, candidate, onlyRecordFailures, state) ||
loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths);
return withPackageId(packageId, pathAndExtension);
loadNodeModuleFromDirectoryWorker(
extensions,
candidate,
onlyRecordFailures,
state,
packageInfo && packageInfo.packageJsonContent,
packageInfo && packageInfo.versionPaths
);
return withPackageId(packageInfo, pathAndExtension);
};
const { packageName, rest } = parsePackageName(moduleName);
@@ -1356,14 +1345,13 @@ namespace ts {
const packageDirectory = combinePaths(nodeModulesDirectory, packageName);
// Don't use a "types" or "main" from here because we're not loading the root, but a subdirectory -- just here for the packageId and path mappings.
const packageInfo = getPackageJsonInfo(packageDirectory, rest, !nodeModulesDirectoryExists, state);
if (packageInfo) ({ packageId, versionPaths } = packageInfo);
if (versionPaths) {
packageInfo = getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists, state);
if (packageInfo && packageInfo.versionPaths) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, versionPaths.version, version, rest);
trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, packageInfo.versionPaths.version, version, rest);
}
const packageDirectoryExists = nodeModulesDirectoryExists && directoryProbablyExists(packageDirectory, state.host);
const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, versionPaths.paths, loader, !packageDirectoryExists, state);
const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, packageInfo.versionPaths.paths, loader, !packageDirectoryExists, state);
if (fromPaths) {
return fromPaths.value;
}
@@ -1499,8 +1487,8 @@ namespace ts {
}
/**
* LSHost may load a module from a global cache of typings.
* This is the minumum code needed to expose that functionality; the rest is in LSHost.
* A host may load a module from a global cache of typings.
* This is the minumum code needed to expose that functionality; the rest is in the host.
*/
/* @internal */
export function loadModuleFromGlobalCache(moduleName: string, projectName: string | undefined, compilerOptions: CompilerOptions, host: ModuleResolutionHost, globalCache: string): ResolvedModuleWithFailedLookupLocations {
+3 -3
View File
@@ -175,9 +175,9 @@ namespace ts.moduleSpecifiers {
function discoverProbableSymlinks(files: ReadonlyArray<SourceFile>, getCanonicalFileName: GetCanonicalFileName, cwd: string): ReadonlyMap<string> {
const result = createMap<string>();
const symlinks = mapDefined(files, sf =>
sf.resolvedModules && firstDefinedIterator(sf.resolvedModules.values(), res =>
res && res.originalPath && res.resolvedFileName !== res.originalPath ? [res.resolvedFileName, res.originalPath] : undefined));
const symlinks = flatten<readonly [string, string]>(mapDefined(files, sf =>
sf.resolvedModules && compact(arrayFrom(mapIterator(sf.resolvedModules.values(), res =>
res && res.originalPath && res.resolvedFileName !== res.originalPath ? [res.resolvedFileName, res.originalPath] as const : undefined)))));
for (const [resolvedPath, originalPath] of symlinks) {
const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedPath, originalPath, cwd, getCanonicalFileName);
result.set(commonOriginal, commonResolved);
+26 -5
View File
@@ -4430,14 +4430,18 @@ namespace ts {
if (token() !== SyntaxKind.CloseBraceToken) {
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
node.expression = parseAssignmentExpressionOrHigher();
// Only an AssignmentExpression is valid here per the JSX spec,
// but we can unambiguously parse a comma sequence and provide
// a better error message in grammar checking.
node.expression = parseExpression();
}
if (inExpressionContext) {
parseExpected(SyntaxKind.CloseBraceToken);
}
else {
parseExpected(SyntaxKind.CloseBraceToken, /*message*/ undefined, /*shouldAdvance*/ false);
scanJsxText();
if (parseExpected(SyntaxKind.CloseBraceToken, /*message*/ undefined, /*shouldAdvance*/ false)) {
scanJsxText();
}
}
return finishNode(node);
@@ -6427,6 +6431,7 @@ namespace ts {
BeginningOfLine,
SawAsterisk,
SavingComments,
SavingBackticks, // NOTE: Only used when parsing tag comments
}
const enum PropertyLikeParse {
@@ -6687,18 +6692,23 @@ namespace ts {
case SyntaxKind.NewLineTrivia:
if (state >= JSDocState.SawAsterisk) {
state = JSDocState.BeginningOfLine;
// don't use pushComment here because we want to keep the margin unchanged
comments.push(scanner.getTokenText());
}
indent = 0;
break;
case SyntaxKind.AtToken:
if (state === JSDocState.SavingBackticks) {
comments.push(scanner.getTokenText());
break;
}
scanner.setTextPos(scanner.getTextPos() - 1);
// falls through
case SyntaxKind.EndOfFileToken:
// Done
break loop;
case SyntaxKind.WhitespaceTrivia:
if (state === JSDocState.SavingComments) {
if (state === JSDocState.SavingComments || state === JSDocState.SavingBackticks) {
pushComment(scanner.getTokenText());
}
else {
@@ -6720,6 +6730,15 @@ namespace ts {
}
pushComment(scanner.getTokenText());
break;
case SyntaxKind.BacktickToken:
if (state === JSDocState.SavingBackticks) {
state = JSDocState.SavingComments;
}
else {
state = JSDocState.SavingBackticks;
}
pushComment(scanner.getTokenText());
break;
case SyntaxKind.AsteriskToken:
if (state === JSDocState.BeginningOfLine) {
// leading asterisks start recording on the *next* (non-whitespace) token
@@ -6730,7 +6749,9 @@ namespace ts {
// record the * as a comment
// falls through
default:
state = JSDocState.SavingComments; // leading identifiers start recording as well
if (state !== JSDocState.SavingBackticks) {
state = JSDocState.SavingComments; // leading identifiers start recording as well
}
pushComment(scanner.getTokenText());
break;
}
+11 -7
View File
@@ -1395,7 +1395,7 @@ namespace ts {
const filePath = newSourceFile.path;
addFileToFilesByName(newSourceFile, filePath, newSourceFile.resolvedPath);
// Set the file as found during node modules search if it was found that way in old progra,
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(filePath)!)) {
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(newSourceFile.resolvedPath)!)) {
sourceFilesFoundSearchingNodeModules.set(filePath, true);
}
}
@@ -1442,7 +1442,8 @@ namespace ts {
},
...(host.directoryExists ? { directoryExists: f => host.directoryExists!(f) } : {}),
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
getProgramBuildInfo: () => program.getProgramBuildInfo && program.getProgramBuildInfo()
getProgramBuildInfo: () => program.getProgramBuildInfo && program.getProgramBuildInfo(),
getSourceFileFromReference: (file, ref) => program.getSourceFileFromReference(file, ref),
};
}
@@ -2127,7 +2128,7 @@ namespace ts {
}
/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
function getSourceFileFromReference(referencingFile: SourceFile, ref: FileReference): SourceFile | undefined {
function getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined {
return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), fileName => filesByName.get(toPath(fileName)) || undefined);
}
@@ -2232,7 +2233,10 @@ namespace ts {
if (isRedirect) {
inputName = getProjectReferenceRedirect(fileName) || fileName;
}
if (getNormalizedAbsolutePath(checkedName, currentDirectory) !== getNormalizedAbsolutePath(inputName, currentDirectory)) {
// Check if it differs only in drive letters its ok to ignore that error:
const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory);
const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(inputName, currentDirectory);
if (checkedAbsolutePath !== inputAbsolutePath) {
reportFileNamesDifferOnlyInCasingError(inputName, checkedName, refFile, refPos, refEnd);
}
}
@@ -2770,7 +2774,7 @@ namespace ts {
// Ignore file that is not emitted
if (!sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary, getResolvedProjectReferenceToRedirect)) continue;
if (rootPaths.indexOf(file.path) === -1) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern, file.fileName));
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, file.fileName, options.configFilePath || ""));
}
}
}
@@ -2855,10 +2859,10 @@ namespace ts {
createDiagnosticForOptionName(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target");
}
const firstNonExternalModuleSourceFile = find(files, f => !isExternalModule(f) && !f.isDeclarationFile && f.scriptKind !== ScriptKind.JSON);
const firstNonExternalModuleSourceFile = find(files, f => !isExternalModule(f) && !isSourceFileJS(f) && !f.isDeclarationFile && f.scriptKind !== ScriptKind.JSON);
if (firstNonExternalModuleSourceFile) {
const span = getErrorSpanForNode(firstNonExternalModuleSourceFile, firstNonExternalModuleSourceFile);
programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_namespaces_when_the_isolatedModules_flag_is_provided));
programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length, Diagnostics.All_files_must_be_modules_when_the_isolatedModules_flag_is_provided));
}
}
else if (firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget.ES2015 && options.module === ModuleKind.None) {
+48 -36
View File
@@ -51,9 +51,11 @@ namespace ts {
getCachedDirectoryStructureHost(): CachedDirectoryStructureHost | undefined;
projectName?: string;
getGlobalCache?(): string | undefined;
globalCacheResolutionModuleName?(externalModuleName: string): string;
writeLog(s: string): void;
maxNumberOfFilesToIterateForInvalidation?: number;
getCurrentProgram(): Program | undefined;
fileIsOpen(filePath: Path): boolean;
}
interface DirectoryWatchesOfFailedLookup {
@@ -75,6 +77,41 @@ namespace ts {
return some(ignoredPaths, searchPath => stringContains(path, searchPath));
}
/**
* Filter out paths like
* "/", "/user", "/user/username", "/user/username/folderAtRoot",
* "c:/", "c:/users", "c:/users/username", "c:/users/username/folderAtRoot", "c:/folderAtRoot"
* @param dirPath
*/
export function canWatchDirectory(dirPath: Path) {
const rootLength = getRootLength(dirPath);
if (dirPath.length === rootLength) {
// Ignore "/", "c:/"
return false;
}
const nextDirectorySeparator = dirPath.indexOf(directorySeparator, rootLength);
if (nextDirectorySeparator === -1) {
// ignore "/user", "c:/users" or "c:/folderAtRoot"
return false;
}
if (dirPath.charCodeAt(0) !== CharacterCodes.slash &&
dirPath.substr(rootLength, nextDirectorySeparator).search(/users/i) === -1) {
// Paths like c:/folderAtRoot/subFolder are allowed
return true;
}
for (let searchIndex = nextDirectorySeparator + 1, searchLevels = 2; searchLevels > 0; searchLevels--) {
searchIndex = dirPath.indexOf(directorySeparator, searchIndex) + 1;
if (searchIndex === 0) {
// Folder isnt at expected minimun levels
return false;
}
}
return true;
}
export const maxNumberOfFilesToIterateForInvalidation = 256;
type GetResolutionWithResolvedFileName<T extends ResolutionWithFailedLookupLocations = ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName = ResolutionWithResolvedFileName> =
@@ -234,7 +271,12 @@ namespace ts {
if (globalCache !== undefined && !isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTS(primaryResult.resolvedModule.extension))) {
// create different collection of failed lookup locations for second pass
// if it will fail and we've already found something during the first pass - we don't want to pollute its results
const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(moduleName, resolutionHost.projectName, compilerOptions, host, globalCache);
const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(
Debug.assertDefined(resolutionHost.globalCacheResolutionModuleName)(moduleName),
resolutionHost.projectName,
compilerOptions,
host,
globalCache);
if (resolvedModule) {
return { resolvedModule, failedLookupLocations: addRange(primaryResult.failedLookupLocations as string[], failedLookupLocations) };
}
@@ -372,41 +414,6 @@ namespace ts {
return endsWith(dirPath, "/node_modules/@types");
}
/**
* Filter out paths like
* "/", "/user", "/user/username", "/user/username/folderAtRoot",
* "c:/", "c:/users", "c:/users/username", "c:/users/username/folderAtRoot", "c:/folderAtRoot"
* @param dirPath
*/
function canWatchDirectory(dirPath: Path) {
const rootLength = getRootLength(dirPath);
if (dirPath.length === rootLength) {
// Ignore "/", "c:/"
return false;
}
const nextDirectorySeparator = dirPath.indexOf(directorySeparator, rootLength);
if (nextDirectorySeparator === -1) {
// ignore "/user", "c:/users" or "c:/folderAtRoot"
return false;
}
if (dirPath.charCodeAt(0) !== CharacterCodes.slash &&
dirPath.substr(rootLength, nextDirectorySeparator).search(/users/i) === -1) {
// Paths like c:/folderAtRoot/subFolder are allowed
return true;
}
for (let searchIndex = nextDirectorySeparator + 1, searchLevels = 2; searchLevels > 0; searchLevels--) {
searchIndex = dirPath.indexOf(directorySeparator, searchIndex) + 1;
if (searchIndex === 0) {
// Folder isnt at expected minimun levels
return false;
}
}
return true;
}
function getDirectoryToWatchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path): DirectoryOfFailedLookupWatch | undefined {
if (isInDirectoryPath(rootPath, failedLookupLocationPath)) {
// Ensure failed look up is normalized path
@@ -698,6 +705,11 @@ namespace ts {
// If something to do with folder/file starting with "." in node_modules folder, skip it
if (isPathIgnored(fileOrDirectoryPath)) return false;
// prevent saving an open file from over-eagerly triggering invalidation
if (resolutionHost.fileIsOpen(fileOrDirectoryPath)) {
return false;
}
// Some file or directory in the watching directory is created
// Return early if it does not have any of the watching extension or not the custom failed lookup path
const dirOfFileOrDirectory = getDirectoryPath(fileOrDirectoryPath);
+1 -1
View File
@@ -330,7 +330,7 @@ namespace ts {
}
/*@internal*/
export const ignoredPaths = ["/node_modules/.", "/.git"];
export const ignoredPaths = ["/node_modules/.", "/.git", "/.#"];
/*@internal*/
export interface RecursiveDirectoryWatcherHost {
+1
View File
@@ -44,6 +44,7 @@ namespace ts {
addRange(transformers, customTransformers && map(customTransformers.before, wrapScriptTransformerFactory));
transformers.push(transformTypeScript);
transformers.push(transformClassFields);
if (jsx === JsxEmit.React) {
transformers.push(transformJsx);
+511
View File
@@ -0,0 +1,511 @@
/*@internal*/
namespace ts {
const enum ClassPropertySubstitutionFlags {
/**
* Enables substitutions for class expressions with static fields
* which have initializers that reference the class name.
*/
ClassAliases = 1 << 0,
}
/**
* Transforms ECMAScript Class Syntax.
* TypeScript parameter property syntax is transformed in the TypeScript transformer.
* For now, this transforms public field declarations using TypeScript class semantics
* (where the declarations get elided and initializers are transformed as assignments in the constructor).
* Eventually, this transform will change to the ECMAScript semantics (with Object.defineProperty).
*/
export function transformClassFields(context: TransformationContext) {
const {
hoistVariableDeclaration,
endLexicalEnvironment,
resumeLexicalEnvironment
} = context;
const resolver = context.getEmitResolver();
const previousOnSubstituteNode = context.onSubstituteNode;
context.onSubstituteNode = onSubstituteNode;
let enabledSubstitutions: ClassPropertySubstitutionFlags;
let classAliases: Identifier[];
/**
* Tracks what computed name expressions originating from elided names must be inlined
* at the next execution site, in document order
*/
let pendingExpressions: Expression[] | undefined;
/**
* Tracks what computed name expression statements and static property initializers must be
* emitted at the next execution site, in document order (for decorated classes).
*/
let pendingStatements: Statement[] | undefined;
return chainBundle(transformSourceFile);
function transformSourceFile(node: SourceFile) {
if (node.isDeclarationFile) {
return node;
}
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());
return visited;
}
function visitor(node: Node): VisitResult<Node> {
if (!(node.transformFlags & TransformFlags.ContainsClassFields)) return node;
switch (node.kind) {
case SyntaxKind.ClassExpression:
return visitClassExpression(node as ClassExpression);
case SyntaxKind.ClassDeclaration:
return visitClassDeclaration(node as ClassDeclaration);
case SyntaxKind.VariableStatement:
return visitVariableStatement(node as VariableStatement);
}
return visitEachChild(node, visitor, context);
}
/**
* Visits the members of a class that has fields.
*
* @param node The node to visit.
*/
function classElementVisitor(node: Node): VisitResult<Node> {
switch (node.kind) {
case SyntaxKind.Constructor:
// Constructors for classes using class fields are transformed in
// `visitClassDeclaration` or `visitClassExpression`.
return undefined;
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.MethodDeclaration:
// Visit the name of the member (if it's a computed property name).
return visitEachChild(node, classElementVisitor, context);
case SyntaxKind.PropertyDeclaration:
return visitPropertyDeclaration(node as PropertyDeclaration);
case SyntaxKind.ComputedPropertyName:
return visitComputedPropertyName(node as ComputedPropertyName);
case SyntaxKind.SemicolonClassElement:
return node;
default:
return visitor(node);
}
}
function visitVariableStatement(node: VariableStatement) {
const savedPendingStatements = pendingStatements;
pendingStatements = [];
const visitedNode = visitEachChild(node, visitor, context);
const statement = some(pendingStatements) ?
[visitedNode, ...pendingStatements] :
visitedNode;
pendingStatements = savedPendingStatements;
return statement;
}
function visitComputedPropertyName(name: ComputedPropertyName) {
let node = visitEachChild(name, visitor, context);
if (some(pendingExpressions)) {
const expressions = pendingExpressions;
expressions.push(name.expression);
pendingExpressions = [];
node = updateComputedPropertyName(
node,
inlineExpressions(expressions)
);
}
return node;
}
function visitPropertyDeclaration(node: PropertyDeclaration) {
Debug.assert(!some(node.decorators));
// Create a temporary variable to store a computed property name (if necessary).
// If it's not inlineable, then we emit an expression after the class which assigns
// the property name to the temporary variable.
const expr = getPropertyNameExpressionIfNeeded(node.name, !!node.initializer);
if (expr && !isSimpleInlineableExpression(expr)) {
(pendingExpressions || (pendingExpressions = [])).push(expr);
}
return undefined;
}
function visitClassDeclaration(node: ClassDeclaration) {
if (!forEach(node.members, isPropertyDeclaration)) {
return visitEachChild(node, visitor, context);
}
const savedPendingExpressions = pendingExpressions;
pendingExpressions = undefined!;
const extendsClauseElement = getEffectiveBaseTypeNode(node);
const isDerivedClass = !!(extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword);
const statements: Statement[] = [
updateClassDeclaration(
node,
/*decorators*/ undefined,
node.modifiers,
node.name,
/*typeParameters*/ undefined,
visitNodes(node.heritageClauses, visitor, isHeritageClause),
transformClassMembers(node, isDerivedClass)
)
];
// Write any pending expressions from elided or moved computed property names
if (some(pendingExpressions)) {
statements.push(createExpressionStatement(inlineExpressions(pendingExpressions)));
}
pendingExpressions = savedPendingExpressions;
// Emit static property assignment. Because classDeclaration is lexically evaluated,
// it is safe to emit static property assignment after classDeclaration
// From ES6 specification:
// HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using
// a lexical declaration such as a LexicalDeclaration or a ClassDeclaration.
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
if (some(staticProperties)) {
addInitializedPropertyStatements(statements, staticProperties, getInternalName(node));
}
return statements;
}
function visitClassExpression(node: ClassExpression): Expression {
if (!forEach(node.members, isPropertyDeclaration)) {
return visitEachChild(node, visitor, context);
}
const savedPendingExpressions = pendingExpressions;
pendingExpressions = undefined;
// If this class expression is a transformation of a decorated class declaration,
// then we want to output the pendingExpressions as statements, not as inlined
// expressions with the class statement.
//
// In this case, we use pendingStatements to produce the same output as the
// class declaration transformation. The VariableStatement visitor will insert
// these statements after the class expression variable statement.
const isDecoratedClassDeclaration = isClassDeclaration(getOriginalNode(node));
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
const extendsClauseElement = getEffectiveBaseTypeNode(node);
const isDerivedClass = !!(extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword);
const classExpression = updateClassExpression(
node,
node.modifiers,
node.name,
/*typeParameters*/ undefined,
visitNodes(node.heritageClauses, visitor, isHeritageClause),
transformClassMembers(node, isDerivedClass)
);
if (some(staticProperties) || some(pendingExpressions)) {
if (isDecoratedClassDeclaration) {
Debug.assertDefined(pendingStatements, "Decorated classes transformed by TypeScript are expected to be within a variable declaration.");
// Write any pending expressions from elided or moved computed property names
if (pendingStatements && pendingExpressions && some(pendingExpressions)) {
pendingStatements.push(createExpressionStatement(inlineExpressions(pendingExpressions)));
}
pendingExpressions = savedPendingExpressions;
if (pendingStatements && some(staticProperties)) {
addInitializedPropertyStatements(pendingStatements, staticProperties, getInternalName(node));
}
return classExpression;
}
else {
const expressions: Expression[] = [];
const isClassWithConstructorReference = resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference;
const temp = createTempVariable(hoistVariableDeclaration, !!isClassWithConstructorReference);
if (isClassWithConstructorReference) {
// record an alias as the class name is not in scope for statics.
enableSubstitutionForClassAliases();
const alias = getSynthesizedClone(temp);
alias.autoGenerateFlags &= ~GeneratedIdentifierFlags.ReservedInNestedScopes;
classAliases[getOriginalNodeId(node)] = alias;
}
// To preserve the behavior of the old emitter, we explicitly indent
// the body of a class with static initializers.
setEmitFlags(classExpression, EmitFlags.Indented | getEmitFlags(classExpression));
expressions.push(startOnNewLine(createAssignment(temp, classExpression)));
// Add any pending expressions leftover from elided or relocated computed property names
addRange(expressions, map(pendingExpressions, startOnNewLine));
addRange(expressions, generateInitializedPropertyExpressions(staticProperties, temp));
expressions.push(startOnNewLine(temp));
pendingExpressions = savedPendingExpressions;
return inlineExpressions(expressions);
}
}
pendingExpressions = savedPendingExpressions;
return classExpression;
}
function transformClassMembers(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
const members: ClassElement[] = [];
const constructor = transformConstructor(node, isDerivedClass);
if (constructor) {
members.push(constructor);
}
addRange(members, visitNodes(node.members, classElementVisitor, isClassElement));
return setTextRange(createNodeArray(members), /*location*/ node.members);
}
function transformConstructor(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
const constructor = visitNode(getFirstConstructorWithBody(node), visitor, isConstructorDeclaration);
const containsPropertyInitializer = forEach(node.members, isInitializedProperty);
if (!containsPropertyInitializer) {
return constructor;
}
const parameters = visitParameterList(constructor ? constructor.parameters : undefined, visitor, context);
const body = transformConstructorBody(node, constructor, isDerivedClass);
if (!body) {
return undefined;
}
return startOnNewLine(
setOriginalNode(
setTextRange(
createConstructor(
/*decorators*/ undefined,
/*modifiers*/ undefined,
parameters,
body
),
constructor || node
),
constructor
)
);
}
function transformConstructorBody(node: ClassDeclaration | ClassExpression, constructor: ConstructorDeclaration | undefined, isDerivedClass: boolean) {
const properties = getInitializedProperties(node, /*isStatic*/ false);
// Only generate synthetic constructor when there are property initializers to move.
if (!constructor && !some(properties)) {
return visitFunctionBody(/*node*/ undefined, visitor, context);
}
resumeLexicalEnvironment();
let indexOfFirstStatement = 0;
let statements: Statement[] = [];
if (!constructor && isDerivedClass) {
// Add a synthetic `super` call:
//
// super(...arguments);
//
statements.push(
createExpressionStatement(
createCall(
createSuper(),
/*typeArguments*/ undefined,
[createSpread(createIdentifier("arguments"))]
)
)
);
}
if (constructor) {
indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements, visitor);
}
// Add the property initializers. Transforms this:
//
// public x = 1;
//
// Into this:
//
// constructor() {
// this.x = 1;
// }
//
if (constructor && constructor.body) {
let parameterPropertyDeclarationCount = 0;
for (let i = indexOfFirstStatement; i < constructor.body.statements.length; i++) {
if (isParameterPropertyDeclaration(getOriginalNode(constructor.body.statements[i]))) {
parameterPropertyDeclarationCount++;
}
else {
break;
}
}
if (parameterPropertyDeclarationCount > 0) {
addRange(statements, visitNodes(constructor.body.statements, visitor, isStatement, indexOfFirstStatement, parameterPropertyDeclarationCount));
indexOfFirstStatement += parameterPropertyDeclarationCount;
}
}
addInitializedPropertyStatements(statements, properties, createThis());
// Add existing statements, skipping the initial super call.
if (constructor) {
addRange(statements, visitNodes(constructor.body!.statements, visitor, isStatement, indexOfFirstStatement));
}
statements = mergeLexicalEnvironment(statements, endLexicalEnvironment());
return setTextRange(
createBlock(
setTextRange(
createNodeArray(statements),
/*location*/ constructor ? constructor.body!.statements : node.members
),
/*multiLine*/ true
),
/*location*/ constructor ? constructor.body : undefined
);
}
/**
* Generates assignment statements for property initializers.
*
* @param properties An array of property declarations to transform.
* @param receiver The receiver on which each property should be assigned.
*/
function addInitializedPropertyStatements(statements: Statement[], properties: ReadonlyArray<PropertyDeclaration>, receiver: LeftHandSideExpression) {
for (const property of properties) {
const statement = createExpressionStatement(transformInitializedProperty(property, receiver));
setSourceMapRange(statement, moveRangePastModifiers(property));
setCommentRange(statement, property);
setOriginalNode(statement, property);
statements.push(statement);
}
}
/**
* Generates assignment expressions for property initializers.
*
* @param properties An array of property declarations to transform.
* @param receiver The receiver on which each property should be assigned.
*/
function generateInitializedPropertyExpressions(properties: ReadonlyArray<PropertyDeclaration>, receiver: LeftHandSideExpression) {
const expressions: Expression[] = [];
for (const property of properties) {
const expression = transformInitializedProperty(property, receiver);
startOnNewLine(expression);
setSourceMapRange(expression, moveRangePastModifiers(property));
setCommentRange(expression, property);
setOriginalNode(expression, property);
expressions.push(expression);
}
return expressions;
}
/**
* Transforms a property initializer into an assignment statement.
*
* @param property The property declaration.
* @param receiver The object receiving the property assignment.
*/
function transformInitializedProperty(property: PropertyDeclaration, receiver: LeftHandSideExpression) {
// We generate a name here in order to reuse the value cached by the relocated computed name expression (which uses the same generated name)
const propertyName = isComputedPropertyName(property.name) && !isSimpleInlineableExpression(property.name.expression)
? updateComputedPropertyName(property.name, getGeneratedNameForNode(property.name))
: property.name;
const initializer = visitNode(property.initializer!, visitor, isExpression);
const memberAccess = createMemberAccessForPropertyName(receiver, propertyName, /*location*/ propertyName);
return createAssignment(memberAccess, initializer);
}
function enableSubstitutionForClassAliases() {
if ((enabledSubstitutions & ClassPropertySubstitutionFlags.ClassAliases) === 0) {
enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases;
// We need to enable substitutions for identifiers. This allows us to
// substitute class names inside of a class declaration.
context.enableSubstitution(SyntaxKind.Identifier);
// Keep track of class aliases.
classAliases = [];
}
}
/**
* Hooks node substitutions.
*
* @param hint The context for the emitter.
* @param node The node to substitute.
*/
function onSubstituteNode(hint: EmitHint, node: Node) {
node = previousOnSubstituteNode(hint, node);
if (hint === EmitHint.Expression) {
return substituteExpression(node as Expression);
}
return node;
}
function substituteExpression(node: Expression) {
switch (node.kind) {
case SyntaxKind.Identifier:
return substituteExpressionIdentifier(node as Identifier);
}
return node;
}
function substituteExpressionIdentifier(node: Identifier): Expression {
return trySubstituteClassAlias(node) || node;
}
function trySubstituteClassAlias(node: Identifier): Expression | undefined {
if (enabledSubstitutions & ClassPropertySubstitutionFlags.ClassAliases) {
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ConstructorReferenceInClass) {
// Due to the emit for class decorators, any reference to the class from inside of the class body
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
// behavior of class names in ES6.
// Also, when emitting statics for class expressions, we must substitute a class alias for
// constructor references in static property initializers.
const declaration = resolver.getReferencedValueDeclaration(node);
if (declaration) {
const classAlias = classAliases[declaration.id!]; // TODO: GH#18217
if (classAlias) {
const clone = getSynthesizedClone(classAlias);
setSourceMapRange(clone, node);
setCommentRange(clone, node);
return clone;
}
}
}
}
return undefined;
}
/**
* If the name is a computed property, this function transforms it, then either returns an expression which caches the
* value of the result or the expression itself if the value is either unused or safe to inline into multiple locations
* @param shouldHoist Does the expression need to be reused? (ie, for an initializer or a decorator)
*/
function getPropertyNameExpressionIfNeeded(name: PropertyName, shouldHoist: boolean): Expression | undefined {
if (isComputedPropertyName(name)) {
const expression = visitNode(name.expression, visitor, isExpression);
const innerExpression = skipPartiallyEmittedExpressions(expression);
const inlinable = isSimpleInlineableExpression(innerExpression);
const alreadyTransformed = isAssignmentExpression(innerExpression) && isGeneratedIdentifier(innerExpression.left);
if (!alreadyTransformed && !inlinable && shouldHoist) {
const generatedName = getGeneratedNameForNode(name);
hoistVariableDeclaration(generatedName);
return createAssignment(generatedName, expression);
}
return (inlinable || isIdentifier(innerExpression)) ? undefined : expression;
}
}
}
}
+76 -33
View File
@@ -190,6 +190,10 @@ namespace ts {
}
}
function createEmptyExports() {
return createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined);
}
function transformRoot(node: Bundle): Bundle;
function transformRoot(node: SourceFile): SourceFile;
function transformRoot(node: SourceFile | Bundle): SourceFile | Bundle;
@@ -277,7 +281,7 @@ namespace ts {
refs.forEach(referenceVisitor);
const emittedImports = filter(combinedStatements, isAnyImportSyntax);
if (isExternalModule(node) && (!resultHasExternalModuleIndicator || (needsScopeFixMarker && !resultHasScopeMarker))) {
combinedStatements = setTextRange(createNodeArray([...combinedStatements, createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined)]), combinedStatements);
combinedStatements = setTextRange(createNodeArray([...combinedStatements, createEmptyExports()]), combinedStatements);
}
const updated = updateSourceFileNode(node, combinedStatements, /*isDeclarationFile*/ true, references, getFileReferencesForUsedTypeReferences(), node.hasNoDefaultLib, getLibReferences());
updated.exportedModulesFromDeclarationEmit = exportedModulesFromDeclarationEmit;
@@ -348,7 +352,7 @@ namespace ts {
function collectReferences(sourceFile: SourceFile | UnparsedSource, ret: Map<SourceFile>) {
if (noResolve || (!isUnparsedSource(sourceFile) && isSourceFileJS(sourceFile))) return ret;
forEach(sourceFile.referencedFiles, f => {
const elem = tryResolveScriptReference(host, sourceFile, f);
const elem = host.getSourceFileFromReference(sourceFile, f);
if (elem) {
ret.set("" + getOriginalNodeId(elem), elem);
}
@@ -670,7 +674,7 @@ namespace ts {
}
const priorNeedsDeclare = needsDeclare;
needsDeclare = i.parent && isSourceFile(i.parent) && !(isExternalModule(i.parent) && isBundledEmit);
const result = transformTopLevelDeclaration(i, /*privateDeclaration*/ true);
const result = transformTopLevelDeclaration(i);
needsDeclare = priorNeedsDeclare;
lateStatementReplacementMap.set("" + getOriginalNodeId(i), result);
}
@@ -685,12 +689,12 @@ namespace ts {
if (lateStatementReplacementMap.has(key)) {
const result = lateStatementReplacementMap.get(key);
lateStatementReplacementMap.delete(key);
if (result && isSourceFile(statement.parent)) {
if (result) {
if (isArray(result) ? some(result, needsScopeMarker) : needsScopeMarker(result)) {
// Top-level declarations in .d.ts files are always considered exported even without a modifier unless there's an export assignment or specifier
needsScopeFixMarker = true;
}
if (isArray(result) ? some(result, isExternalModuleIndicator) : isExternalModuleIndicator(result)) {
if (isSourceFile(statement.parent) && (isArray(result) ? some(result, isExternalModuleIndicator) : isExternalModuleIndicator(result))) {
resultHasExternalModuleIndicator = true;
}
}
@@ -939,8 +943,8 @@ namespace ts {
case SyntaxKind.ExportDeclaration: {
if (isSourceFile(input.parent)) {
resultHasExternalModuleIndicator = true;
resultHasScopeMarker = true;
}
resultHasScopeMarker = true;
// Always visible if the parent node isn't dropped for being not visible
// Rewrite external module names if necessary
return updateExportDeclaration(input, /*decorators*/ undefined, input.modifiers, input.exportClause, rewriteModuleSpecifier(input, input.moduleSpecifier));
@@ -949,8 +953,8 @@ namespace ts {
// Always visible if the parent node isn't dropped for being not visible
if (isSourceFile(input.parent)) {
resultHasExternalModuleIndicator = true;
resultHasScopeMarker = true;
}
resultHasScopeMarker = true;
if (input.expression.kind === SyntaxKind.Identifier) {
return input;
}
@@ -973,7 +977,19 @@ namespace ts {
return input;
}
function transformTopLevelDeclaration(input: LateVisibilityPaintedStatement, isPrivate?: boolean) {
function stripExportModifiers(statement: Statement): Statement {
if (isImportEqualsDeclaration(statement) || hasModifier(statement, ModifierFlags.Default)) {
// `export import` statements should remain as-is, as imports are _not_ implicitly exported in an ambient namespace
// Likewise, `export default` classes and the like and just be `default`, so we preserve their `export` modifiers, too
return statement;
}
const clone = getMutableClone(statement);
const modifiers = createModifiersFromModifierFlags(getModifierFlags(statement) & (ModifierFlags.All ^ ModifierFlags.Export));
clone.modifiers = modifiers.length ? createNodeArray(modifiers) : undefined;
return clone;
}
function transformTopLevelDeclaration(input: LateVisibilityPaintedStatement) {
if (shouldStripInternal(input)) return;
switch (input.kind) {
case SyntaxKind.ImportEqualsDeclaration: {
@@ -1006,7 +1022,7 @@ namespace ts {
return cleanup(updateTypeAliasDeclaration(
input,
/*decorators*/ undefined,
ensureModifiers(input, isPrivate),
ensureModifiers(input),
input.name,
visitNodes(input.typeParameters, visitDeclarationSubtree, isTypeParameterDeclaration),
visitNode(input.type, visitDeclarationSubtree, isTypeNode)
@@ -1015,7 +1031,7 @@ namespace ts {
return cleanup(updateInterfaceDeclaration(
input,
/*decorators*/ undefined,
ensureModifiers(input, isPrivate),
ensureModifiers(input),
input.name,
ensureTypeParams(input, input.typeParameters),
transformHeritageClauses(input.heritageClauses),
@@ -1027,7 +1043,7 @@ namespace ts {
const clean = cleanup(updateFunctionDeclaration(
input,
/*decorators*/ undefined,
ensureModifiers(input, isPrivate),
ensureModifiers(input),
/*asteriskToken*/ undefined,
input.name,
ensureTypeParams(input, input.typeParameters),
@@ -1036,19 +1052,25 @@ namespace ts {
/*body*/ undefined
));
if (clean && resolver.isExpandoFunctionDeclaration(input)) {
const declarations = mapDefined(resolver.getPropertiesOfContainerFunction(input), p => {
const props = resolver.getPropertiesOfContainerFunction(input);
const fakespace = createModuleDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, clean.name || createIdentifier("_default"), createModuleBlock([]), NodeFlags.Namespace);
fakespace.flags ^= NodeFlags.Synthesized; // unset synthesized so it is usable as an enclosing declaration
fakespace.parent = enclosingDeclaration as SourceFile | NamespaceDeclaration;
fakespace.locals = createSymbolTable(props);
fakespace.symbol = props[0].parent!;
const declarations = mapDefined(props, p => {
if (!isPropertyAccessExpression(p.valueDeclaration)) {
return undefined;
}
getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(p.valueDeclaration);
const type = resolver.createTypeOfDeclaration(p.valueDeclaration, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker);
const type = resolver.createTypeOfDeclaration(p.valueDeclaration, fakespace, declarationEmitNodeBuilderFlags, symbolTracker);
getSymbolAccessibilityDiagnostic = oldDiag;
const varDecl = createVariableDeclaration(unescapeLeadingUnderscores(p.escapedName), type, /*initializer*/ undefined);
return createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList([varDecl]));
});
const namespaceDecl = createModuleDeclaration(/*decorators*/ undefined, ensureModifiers(input, isPrivate), input.name!, createModuleBlock(declarations), NodeFlags.Namespace);
const namespaceDecl = createModuleDeclaration(/*decorators*/ undefined, ensureModifiers(input), input.name!, createModuleBlock(declarations), NodeFlags.Namespace);
if (!hasModifier(clean, ModifierFlags.ExportDefault)) {
if (!hasModifier(clean, ModifierFlags.Default)) {
return [clean, namespaceDecl];
}
@@ -1080,7 +1102,9 @@ namespace ts {
namespaceDecl.name
);
resultHasExternalModuleIndicator = true;
if (isSourceFile(input.parent)) {
resultHasExternalModuleIndicator = true;
}
resultHasScopeMarker = true;
return [cleanDeclaration, namespaceDeclaration, exportDefaultDeclaration];
@@ -1093,10 +1117,32 @@ namespace ts {
needsDeclare = false;
const inner = input.body;
if (inner && inner.kind === SyntaxKind.ModuleBlock) {
const oldNeedsScopeFix = needsScopeFixMarker;
const oldHasScopeFix = resultHasScopeMarker;
resultHasScopeMarker = false;
needsScopeFixMarker = false;
const statements = visitNodes(inner.statements, visitDeclarationStatements);
const body = updateModuleBlock(inner, transformAndReplaceLatePaintedStatements(statements));
let lateStatements = transformAndReplaceLatePaintedStatements(statements);
if (input.flags & NodeFlags.Ambient) {
needsScopeFixMarker = false; // If it was `declare`'d everything is implicitly exported already, ignore late printed "privates"
}
// With the final list of statements, there are 3 possibilities:
// 1. There's an export assignment or export declaration in the namespace - do nothing
// 2. Everything is exported and there are no export assignments or export declarations - strip all export modifiers
// 3. Some things are exported, some are not, and there's no marker - add an empty marker
if (!isGlobalScopeAugmentation(input) && !hasScopeMarker(lateStatements) && !resultHasScopeMarker) {
if (needsScopeFixMarker) {
lateStatements = createNodeArray([...lateStatements, createEmptyExports()]);
}
else {
lateStatements = visitNodes(lateStatements, stripExportModifiers);
}
}
const body = updateModuleBlock(inner, lateStatements);
needsDeclare = previousNeedsDeclare;
const mods = ensureModifiers(input, isPrivate);
needsScopeFixMarker = oldNeedsScopeFix;
resultHasScopeMarker = oldHasScopeFix;
const mods = ensureModifiers(input);
return cleanup(updateModuleDeclaration(
input,
/*decorators*/ undefined,
@@ -1107,7 +1153,7 @@ namespace ts {
}
else {
needsDeclare = previousNeedsDeclare;
const mods = ensureModifiers(input, isPrivate);
const mods = ensureModifiers(input);
needsDeclare = false;
visitNode(inner, visitDeclarationStatements);
// eagerly transform nested namespaces (the nesting doesn't need any elision or painting done)
@@ -1124,7 +1170,7 @@ namespace ts {
}
}
case SyntaxKind.ClassDeclaration: {
const modifiers = createNodeArray(ensureModifiers(input, isPrivate));
const modifiers = createNodeArray(ensureModifiers(input));
const typeParameters = ensureTypeParams(input, input.typeParameters);
const ctor = getFirstConstructorWithBody(input);
let parameterProperties: ReadonlyArray<PropertyDeclaration> | undefined;
@@ -1218,10 +1264,10 @@ namespace ts {
}
}
case SyntaxKind.VariableStatement: {
return cleanup(transformVariableStatement(input, isPrivate));
return cleanup(transformVariableStatement(input));
}
case SyntaxKind.EnumDeclaration: {
return cleanup(updateEnumDeclaration(input, /*decorators*/ undefined, createNodeArray(ensureModifiers(input, isPrivate)), input.name, createNodeArray(mapDefined(input.members, m => {
return cleanup(updateEnumDeclaration(input, /*decorators*/ undefined, createNodeArray(ensureModifiers(input)), input.name, createNodeArray(mapDefined(input.members, m => {
if (shouldStripInternal(m)) return;
// Rewrite enum values to their constants, if available
const constValue = resolver.getConstantValue(m);
@@ -1249,11 +1295,11 @@ namespace ts {
}
}
function transformVariableStatement(input: VariableStatement, privateDeclaration?: boolean) {
function transformVariableStatement(input: VariableStatement) {
if (!forEach(input.declarationList.declarations, getBindingNameVisible)) return;
const nodes = visitNodes(input.declarationList.declarations, visitDeclarationSubtree);
if (!length(nodes)) return;
return updateVariableStatement(input, createNodeArray(ensureModifiers(input, privateDeclaration)), updateVariableDeclarationList(input.declarationList, nodes));
return updateVariableStatement(input, createNodeArray(ensureModifiers(input)), updateVariableDeclarationList(input.declarationList, nodes));
}
function recreateBindingPattern(d: BindingPattern): VariableDeclaration[] {
@@ -1300,28 +1346,25 @@ namespace ts {
return isExportAssignment(node) || isExportDeclaration(node);
}
function hasScopeMarker(node: Node) {
if (isModuleBlock(node)) {
return some(node.statements, isScopeMarker);
}
return false;
function hasScopeMarker(statements: ReadonlyArray<Statement>) {
return some(statements, isScopeMarker);
}
function ensureModifiers(node: Node, privateDeclaration?: boolean): ReadonlyArray<Modifier> | undefined {
function ensureModifiers(node: Node): ReadonlyArray<Modifier> | undefined {
const currentFlags = getModifierFlags(node);
const newFlags = ensureModifierFlags(node, privateDeclaration);
const newFlags = ensureModifierFlags(node);
if (currentFlags === newFlags) {
return node.modifiers;
}
return createModifiersFromModifierFlags(newFlags);
}
function ensureModifierFlags(node: Node, privateDeclaration?: boolean): ModifierFlags {
function ensureModifierFlags(node: Node): ModifierFlags {
let mask = ModifierFlags.All ^ (ModifierFlags.Public | ModifierFlags.Async); // No async modifiers in declaration files
let additions = (needsDeclare && !isAlwaysType(node)) ? ModifierFlags.Ambient : ModifierFlags.None;
const parentIsFile = node.parent.kind === SyntaxKind.SourceFile;
if (!parentIsFile || (isBundledEmit && parentIsFile && isExternalModule(node.parent as SourceFile))) {
mask ^= ((privateDeclaration || (isBundledEmit && parentIsFile) || hasScopeMarker(node.parent) ? 0 : ModifierFlags.Export) | ModifierFlags.Ambient);
mask ^= ModifierFlags.Ambient;
additions = ModifierFlags.None;
}
return maskModifierFlags(node, mask, additions);
+52 -30
View File
@@ -145,7 +145,7 @@ namespace ts {
loopOutParameters: LoopOutParameter[];
}
type LoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined) => Statement;
type LoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined, ancestorFacts: HierarchyFacts) => Statement;
// Facts we track as we traverse the tree
const enum HierarchyFacts {
@@ -163,11 +163,12 @@ namespace ts {
ExportedVariableStatement = 1 << 5, // Enclosed in an exported variable statement in the current scope
TopLevel = 1 << 6, // Enclosing block-scoped container is a top-level container
Block = 1 << 7, // Enclosing block-scoped container is a Block
IterationStatement = 1 << 8, // Enclosed in an IterationStatement
IterationStatement = 1 << 8, // Immediately enclosed in an IterationStatement
IterationStatementBlock = 1 << 9, // Enclosing Block is enclosed in an IterationStatement
ForStatement = 1 << 10, // Enclosing block-scoped container is a ForStatement
ForInOrForOfStatement = 1 << 11, // Enclosing block-scoped container is a ForInStatement or ForOfStatement
ConstructorWithCapturedSuper = 1 << 12, // Enclosed in a constructor that captures 'this' for use with 'super'
IterationContainer = 1 << 10, // Enclosed in an outer IterationStatement
ForStatement = 1 << 11, // Enclosing block-scoped container is a ForStatement
ForInOrForOfStatement = 1 << 12, // Enclosing block-scoped container is a ForInStatement or ForOfStatement
ConstructorWithCapturedSuper = 1 << 13, // Enclosed in a constructor that captures 'this' for use with 'super'
// NOTE: do not add more ancestor flags without also updating AncestorFactsMask below.
// NOTE: when adding a new ancestor flag, be sure to update the subtree flags below.
@@ -184,11 +185,11 @@ namespace ts {
// A source file is a top-level block scope.
SourceFileIncludes = TopLevel,
SourceFileExcludes = BlockScopeExcludes & ~TopLevel,
SourceFileExcludes = BlockScopeExcludes & ~TopLevel | IterationContainer,
// Functions, methods, and accessors are both new lexical scopes and new block scopes.
FunctionIncludes = Function | TopLevel,
FunctionExcludes = BlockScopeExcludes & ~TopLevel | ArrowFunction | AsyncFunctionBody | CapturesThis | NonStaticClassElement | ConstructorWithCapturedSuper,
FunctionExcludes = BlockScopeExcludes & ~TopLevel | ArrowFunction | AsyncFunctionBody | CapturesThis | NonStaticClassElement | ConstructorWithCapturedSuper | IterationContainer,
AsyncFunctionBodyIncludes = FunctionIncludes | AsyncFunctionBody,
AsyncFunctionBodyExcludes = FunctionExcludes & ~NonStaticClassElement,
@@ -205,16 +206,16 @@ namespace ts {
// 'do' and 'while' statements are not block scopes. We track that the subtree is contained
// within an IterationStatement to indicate whether the embedded statement is an
// IterationStatementBlock.
DoOrWhileStatementIncludes = IterationStatement,
DoOrWhileStatementIncludes = IterationStatement | IterationContainer,
DoOrWhileStatementExcludes = None,
// 'for' statements are new block scopes and have special handling for 'let' declarations.
ForStatementIncludes = IterationStatement | ForStatement,
ForStatementIncludes = IterationStatement | ForStatement | IterationContainer,
ForStatementExcludes = BlockScopeExcludes & ~ForStatement,
// 'for-in' and 'for-of' statements are new block scopes and have special handling for
// 'let' declarations.
ForInOrForOfStatementIncludes = IterationStatement | ForInOrForOfStatement,
ForInOrForOfStatementIncludes = IterationStatement | ForInOrForOfStatement | IterationContainer,
ForInOrForOfStatementExcludes = BlockScopeExcludes & ~ForInOrForOfStatement,
// Blocks (other than function bodies) are new block scopes.
@@ -228,8 +229,8 @@ namespace ts {
// Subtree facts
//
NewTarget = 1 << 13, // Contains a 'new.target' meta-property
CapturedLexicalThis = 1 << 14, // Contains a lexical `this` reference captured by an arrow function.
NewTarget = 1 << 14, // Contains a 'new.target' meta-property
CapturedLexicalThis = 1 << 15, // Contains a lexical `this` reference captured by an arrow function.
//
// Subtree masks
@@ -1566,7 +1567,7 @@ namespace ts {
break;
default:
Debug.failBadSyntaxKind(node);
Debug.failBadSyntaxKind(member, currentSourceFile && currentSourceFile.fileName);
break;
}
}
@@ -2227,7 +2228,7 @@ namespace ts {
function visitIterationStatementWithFacts(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts, node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter) {
const ancestorFacts = enterSubtree(excludeFacts, includeFacts);
const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, convert);
const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, ancestorFacts, convert);
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
return updated;
}
@@ -2434,7 +2435,7 @@ namespace ts {
return restoreEnclosingLabel(forStatement, outermostLabeledStatement, convertedLoopState && resetLabel);
}
function convertForOfStatementForIterable(node: ForOfStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[]): Statement {
function convertForOfStatementForIterable(node: ForOfStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[], ancestorFacts: HierarchyFacts): Statement {
const expression = visitNode(node.expression, visitor, isExpression);
const iterator = isIdentifier(expression) ? getGeneratedNameForNode(expression) : createTempVariable(/*recordTempVariable*/ undefined);
const result = isIdentifier(expression) ? getGeneratedNameForNode(iterator) : createTempVariable(/*recordTempVariable*/ undefined);
@@ -2447,13 +2448,18 @@ namespace ts {
hoistVariableDeclaration(errorRecord);
hoistVariableDeclaration(returnMethod);
// if we are enclosed in an outer loop ensure we reset 'errorRecord' per each iteration
const initializer = ancestorFacts & HierarchyFacts.IterationContainer
? inlineExpressions([createAssignment(errorRecord, createVoidZero()), values])
: values;
const forStatement = setEmitFlags(
setTextRange(
createFor(
/*initializer*/ setEmitFlags(
setTextRange(
createVariableDeclarationList([
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, values), node.expression),
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, initializer), node.expression),
createVariableDeclaration(result, /*type*/ undefined, next)
]),
node.expression
@@ -2665,7 +2671,7 @@ namespace ts {
}
}
function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter): VisitResult<Statement> {
function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, ancestorFacts: HierarchyFacts, convert?: LoopConverter): VisitResult<Statement> {
if (!shouldConvertIterationStatement(node)) {
let saveAllowedNonLabeledJumps: Jump | undefined;
if (convertedLoopState) {
@@ -2676,7 +2682,7 @@ namespace ts {
}
const result = convert
? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined)
? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined, ancestorFacts)
: restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement, convertedLoopState && resetLabel);
if (convertedLoopState) {
@@ -2708,7 +2714,7 @@ namespace ts {
let loop: Statement;
if (bodyFunction) {
if (convert) {
loop = convert(node, outermostLabeledStatement, bodyFunction.part);
loop = convert(node, outermostLabeledStatement, bodyFunction.part, ancestorFacts);
}
else {
const clone = convertIterationStatementCore(node, initializerFunction, createBlock(bodyFunction.part, /*multiLine*/ true));
@@ -3813,8 +3819,11 @@ namespace ts {
// [source]
// [a, ...b, c]
//
// [output (downlevelIteration)]
// __spread([a], b, [c])
//
// [output]
// [a].concat(b, [c])
// __spreadArrays([a], b, [c])
// Map spans of spread expressions into their expressions and spans of other
// expressions into an array literal.
@@ -3828,10 +3837,7 @@ namespace ts {
if (compilerOptions.downlevelIteration) {
if (segments.length === 1) {
const firstSegment = segments[0];
if (isCallExpression(firstSegment)
&& isIdentifier(firstSegment.expression)
&& (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName)
&& firstSegment.expression.escapedText === "___spread") {
if (isCallToHelper(firstSegment, "___spread" as __String)) {
return segments[0];
}
}
@@ -3840,17 +3846,33 @@ namespace ts {
}
else {
if (segments.length === 1) {
const firstElement = elements[0];
return needsUniqueCopy && isSpreadElement(firstElement) && firstElement.expression.kind !== SyntaxKind.ArrayLiteralExpression
? createArraySlice(segments[0])
: segments[0];
const firstSegment = segments[0];
if (!needsUniqueCopy
|| isPackedArrayLiteral(firstSegment)
|| isCallToHelper(firstSegment, "___spreadArrays" as __String)) {
return segments[0];
}
}
// Rewrite using the pattern <segment0>.concat(<segment1>, <segment2>, ...)
return createArrayConcat(segments.shift()!, segments);
return createSpreadArraysHelper(context, segments);
}
}
function isPackedElement(node: Expression) {
return !isOmittedExpression(node);
}
function isPackedArrayLiteral(node: Expression) {
return isArrayLiteralExpression(node) && every(node.elements, isPackedElement);
}
function isCallToHelper(firstSegment: Expression, helperName: __String) {
return isCallExpression(firstSegment)
&& isIdentifier(firstSegment.expression)
&& (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName)
&& firstSegment.expression.escapedText === helperName;
}
function partitionSpread(node: Expression) {
return isSpreadElement(node)
? visitSpanOfSpreads
+24
View File
@@ -77,6 +77,8 @@ namespace ts {
return visitObjectLiteralExpression(node as ObjectLiteralExpression);
case SyntaxKind.BinaryExpression:
return visitBinaryExpression(node as BinaryExpression, noDestructuringValue);
case SyntaxKind.CatchClause:
return visitCatchClause(node as CatchClause);
case SyntaxKind.VariableDeclaration:
return visitVariableDeclaration(node as VariableDeclaration);
case SyntaxKind.ForOfStatement:
@@ -272,6 +274,28 @@ namespace ts {
return visitEachChild(node, visitor, context);
}
function visitCatchClause(node: CatchClause) {
if (node.variableDeclaration &&
isBindingPattern(node.variableDeclaration.name) &&
node.variableDeclaration.name.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
const name = getGeneratedNameForNode(node.variableDeclaration.name);
const updatedDecl = updateVariableDeclaration(node.variableDeclaration, node.variableDeclaration.name, /*type*/ undefined, name);
const visitedBindings = flattenDestructuringBinding(updatedDecl, visitor, context, FlattenLevel.ObjectRest);
let block = visitNode(node.block, visitor, isBlock);
if (some(visitedBindings)) {
block = updateBlock(block, [
createVariableStatement(/*modifiers*/ undefined, visitedBindings),
...block.statements,
]);
}
return updateCatchClause(
node,
updateVariableDeclaration(node.variableDeclaration, name, /*type*/ undefined, /*initializer*/ undefined),
block);
}
return visitEachChild(node, visitor, context);
}
/**
* Visits a VariableDeclaration node with a binding pattern.
*
+146 -446
View File
@@ -64,6 +64,7 @@ namespace ts {
let currentLexicalScope: SourceFile | Block | ModuleBlock | CaseBlock;
let currentNameScope: ClassDeclaration | undefined;
let currentScopeFirstDeclarationsOfName: UnderscoreEscapedMap<Node> | undefined;
let currentClassHasParameterProperties: boolean | undefined;
/**
* Keeps track of whether expression substitution has been enabled for specific edge cases.
@@ -83,12 +84,6 @@ namespace ts {
*/
let applicableSubstitutions: TypeScriptSubstitutionFlags;
/**
* Tracks what computed name expressions originating from elided names must be inlined
* at the next execution site, in document order
*/
let pendingExpressions: Expression[] | undefined;
return transformSourceFileOrBundle;
function transformSourceFileOrBundle(node: SourceFile | Bundle) {
@@ -136,6 +131,7 @@ namespace ts {
const savedCurrentScope = currentLexicalScope;
const savedCurrentNameScope = currentNameScope;
const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName;
const savedCurrentClassHasParameterProperties = currentClassHasParameterProperties;
// Handle state changes before visiting a node.
onBeforeVisitNode(node);
@@ -149,6 +145,7 @@ namespace ts {
currentLexicalScope = savedCurrentScope;
currentNameScope = savedCurrentNameScope;
currentClassHasParameterProperties = savedCurrentClassHasParameterProperties;
return visited;
}
@@ -315,12 +312,12 @@ namespace ts {
function classElementVisitorWorker(node: Node): VisitResult<Node> {
switch (node.kind) {
case SyntaxKind.Constructor:
// TypeScript constructors are transformed in `visitClassDeclaration`.
// We elide them here as `visitorWorker` checks transform flags, which could
// erronously include an ES6 constructor without TypeScript syntax.
return undefined;
return visitConstructor(node as ConstructorDeclaration);
case SyntaxKind.PropertyDeclaration:
// Property declarations are not TypeScript syntax, but they must be visited
// for the decorator transformation.
return visitPropertyDeclaration(node as PropertyDeclaration);
case SyntaxKind.IndexSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
@@ -437,7 +434,6 @@ namespace ts {
// - decorators
// - optional `implements` heritage clause
// - parameter property assignments in the constructor
// - property declarations
// - index signatures
// - method overload signatures
return visitClassDeclaration(<ClassDeclaration>node);
@@ -449,7 +445,6 @@ namespace ts {
// - decorators
// - optional `implements` heritage clause
// - parameter property assignments in the constructor
// - property declarations
// - index signatures
// - method overload signatures
return visitClassExpression(<ClassExpression>node);
@@ -612,9 +607,6 @@ namespace ts {
return visitEachChild(node, visitor, context);
}
const savedPendingExpressions = pendingExpressions;
pendingExpressions = undefined;
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
const facts = getClassFacts(node, staticProperties);
@@ -624,25 +616,11 @@ namespace ts {
const name = node.name || (facts & ClassFacts.NeedsName ? getGeneratedNameForNode(node) : undefined);
const classStatement = facts & ClassFacts.HasConstructorDecorators
? createClassDeclarationHeadWithDecorators(node, name, facts)
? createClassDeclarationHeadWithDecorators(node, name)
: createClassDeclarationHeadWithoutDecorators(node, name, facts);
let statements: Statement[] = [classStatement];
// Write any pending expressions from elided or moved computed property names
if (some(pendingExpressions)) {
statements.push(createExpressionStatement(inlineExpressions(pendingExpressions!)));
}
pendingExpressions = savedPendingExpressions;
// Emit static property assignment. Because classDeclaration is lexically evaluated,
// it is safe to emit static property assignment after classDeclaration
// From ES6 specification:
// HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using
// a lexical declaration such as a LexicalDeclaration or a ClassDeclaration.
if (facts & ClassFacts.HasStaticInitializedProperties) {
addInitializedPropertyStatements(statements, staticProperties, facts & ClassFacts.UseImmediatelyInvokedFunctionExpression ? getInternalName(node) : getLocalName(node));
}
// Write any decorators of the node.
addClassElementDecorationStatements(statements, node, /*isStatic*/ false);
@@ -745,7 +723,7 @@ namespace ts {
name,
/*typeParameters*/ undefined,
visitNodes(node.heritageClauses, visitor, isHeritageClause),
transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0)
transformClassMembers(node)
);
// To better align with the old emitter, we should not emit a trailing source map
@@ -755,6 +733,7 @@ namespace ts {
emitFlags |= EmitFlags.NoTrailingSourceMap;
}
aggregateTransformFlags(classDeclaration);
setTextRange(classDeclaration, node);
setOriginalNode(classDeclaration, node);
setEmitFlags(classDeclaration, emitFlags);
@@ -765,7 +744,7 @@ namespace ts {
* Transforms a decorated class declaration and appends the resulting statements. If
* the class requires an alias to avoid issues with double-binding, the alias is returned.
*/
function createClassDeclarationHeadWithDecorators(node: ClassDeclaration, name: Identifier | undefined, facts: ClassFacts) {
function createClassDeclarationHeadWithDecorators(node: ClassDeclaration, name: Identifier | undefined) {
// When we emit an ES6 class that has a class decorator, we must tailor the
// emit to certain specific cases.
//
@@ -860,8 +839,9 @@ namespace ts {
// ${members}
// }
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
const members = transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0);
const members = transformClassMembers(node);
const classExpression = createClassExpression(/*modifiers*/ undefined, name, /*typeParameters*/ undefined, heritageClauses, members);
aggregateTransformFlags(classExpression);
setOriginalNode(classExpression, node);
setTextRange(classExpression, location);
@@ -888,49 +868,18 @@ namespace ts {
return visitEachChild(node, visitor, context);
}
const savedPendingExpressions = pendingExpressions;
pendingExpressions = undefined;
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
const members = transformClassMembers(node, some(heritageClauses, c => c.token === SyntaxKind.ExtendsKeyword));
const classExpression = createClassExpression(
/*modifiers*/ undefined,
node.name,
/*typeParameters*/ undefined,
heritageClauses,
members
visitNodes(node.heritageClauses, visitor, isHeritageClause),
transformClassMembers(node)
);
aggregateTransformFlags(classExpression);
setOriginalNode(classExpression, node);
setTextRange(classExpression, node);
if (some(staticProperties) || some(pendingExpressions)) {
const expressions: Expression[] = [];
const isClassWithConstructorReference = resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference;
const temp = createTempVariable(hoistVariableDeclaration, !!isClassWithConstructorReference);
if (isClassWithConstructorReference) {
// record an alias as the class name is not in scope for statics.
enableSubstitutionForClassAliases();
const alias = getSynthesizedClone(temp);
alias.autoGenerateFlags &= ~GeneratedIdentifierFlags.ReservedInNestedScopes;
classAliases[getOriginalNodeId(node)] = alias;
}
// To preserve the behavior of the old emitter, we explicitly indent
// the body of a class with static initializers.
setEmitFlags(classExpression, EmitFlags.Indented | getEmitFlags(classExpression));
expressions.push(startOnNewLine(createAssignment(temp, classExpression)));
// Add any pending expressions leftover from elided or relocated computed property names
addRange(expressions, map(pendingExpressions, startOnNewLine));
pendingExpressions = savedPendingExpressions;
addRange(expressions, generateInitializedPropertyExpressions(staticProperties, temp));
expressions.push(startOnNewLine(temp));
return inlineExpressions(expressions);
}
pendingExpressions = savedPendingExpressions;
return classExpression;
}
@@ -938,344 +887,31 @@ namespace ts {
* Transforms the members of a class.
*
* @param node The current class.
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
*/
function transformClassMembers(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
function transformClassMembers(node: ClassDeclaration | ClassExpression) {
const members: ClassElement[] = [];
const constructor = transformConstructor(node, isDerivedClass);
if (constructor) {
members.push(constructor);
const constructor = getFirstConstructorWithBody(node);
const parametersWithPropertyAssignments = constructor &&
filter(constructor.parameters, isParameterPropertyDeclaration);
if (parametersWithPropertyAssignments) {
for (const parameter of parametersWithPropertyAssignments) {
if (isIdentifier(parameter.name)) {
members.push(aggregateTransformFlags(createProperty(
/*decorators*/ undefined,
/*modifiers*/ undefined,
parameter.name,
/*questionOrExclamationToken*/ undefined,
/*type*/ undefined,
/*initializer*/ undefined)));
}
}
}
addRange(members, visitNodes(node.members, classElementVisitor, isClassElement));
return setTextRange(createNodeArray(members), /*location*/ node.members);
}
/**
* Transforms (or creates) a constructor for a class.
*
* @param node The current class.
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
*/
function transformConstructor(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
// Check if we have property assignment inside class declaration.
// If there is a property assignment, we need to emit constructor whether users define it or not
// If there is no property assignment, we can omit constructor if users do not define it
const constructor = getFirstConstructorWithBody(node);
const hasInstancePropertyWithInitializer = forEach(node.members, isInstanceInitializedProperty);
const hasParameterPropertyAssignments = constructor &&
constructor.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax &&
forEach(constructor.parameters, isParameterWithPropertyAssignment);
// If the class does not contain nodes that require a synthesized constructor,
// accept the current constructor if it exists.
if (!hasInstancePropertyWithInitializer && !hasParameterPropertyAssignments) {
return visitEachChild(constructor, visitor, context);
}
const parameters = transformConstructorParameters(constructor);
const body = transformConstructorBody(node, constructor, isDerivedClass);
// constructor(${parameters}) {
// ${body}
// }
return startOnNewLine(
setOriginalNode(
setTextRange(
createConstructor(
/*decorators*/ undefined,
/*modifiers*/ undefined,
parameters,
body
),
constructor || node
),
constructor
)
);
}
/**
* Transforms (or creates) the parameters for the constructor of a class with
* parameter property assignments or instance property initializers.
*
* @param constructor The constructor declaration.
*/
function transformConstructorParameters(constructor: ConstructorDeclaration | undefined) {
// The ES2015 spec specifies in 14.5.14. Runtime Semantics: ClassDefinitionEvaluation:
// If constructor is empty, then
// If ClassHeritag_eopt is present and protoParent is not null, then
// Let constructor be the result of parsing the source text
// constructor(...args) { super (...args);}
// using the syntactic grammar with the goal symbol MethodDefinition[~Yield].
// Else,
// Let constructor be the result of parsing the source text
// constructor( ){ }
// using the syntactic grammar with the goal symbol MethodDefinition[~Yield].
//
// While we could emit the '...args' rest parameter, certain later tools in the pipeline might
// downlevel the '...args' portion less efficiently by naively copying the contents of 'arguments' to an array.
// Instead, we'll avoid using a rest parameter and spread into the super call as
// 'super(...arguments)' instead of 'super(...args)', as you can see in "transformConstructorBody".
return visitParameterList(constructor && constructor.parameters, visitor, context)
|| <ParameterDeclaration[]>[];
}
/**
* Transforms (or creates) a constructor body for a class with parameter property
* assignments or instance property initializers.
*
* @param node The current class.
* @param constructor The current class constructor.
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
*/
function transformConstructorBody(node: ClassExpression | ClassDeclaration, constructor: ConstructorDeclaration | undefined, isDerivedClass: boolean) {
let statements: Statement[] = [];
let indexOfFirstStatement = 0;
resumeLexicalEnvironment();
if (constructor) {
indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements);
// Add parameters with property assignments. Transforms this:
//
// constructor (public x, public y) {
// }
//
// Into this:
//
// constructor (x, y) {
// this.x = x;
// this.y = y;
// }
//
const propertyAssignments = getParametersWithPropertyAssignments(constructor);
addRange(statements, map(propertyAssignments, transformParameterWithPropertyAssignment));
}
else if (isDerivedClass) {
// Add a synthetic `super` call:
//
// super(...arguments);
//
statements.push(
createExpressionStatement(
createCall(
createSuper(),
/*typeArguments*/ undefined,
[createSpread(createIdentifier("arguments"))]
)
)
);
}
// Add the property initializers. Transforms this:
//
// public x = 1;
//
// Into this:
//
// constructor() {
// this.x = 1;
// }
//
const properties = getInitializedProperties(node, /*isStatic*/ false);
addInitializedPropertyStatements(statements, properties, createThis());
if (constructor) {
// The class already had a constructor, so we should add the existing statements, skipping the initial super call.
addRange(statements, visitNodes(constructor.body!.statements, visitor, isStatement, indexOfFirstStatement));
}
// End the lexical environment.
statements = mergeLexicalEnvironment(statements, endLexicalEnvironment());
return setTextRange(
createBlock(
setTextRange(
createNodeArray(statements),
/*location*/ constructor ? constructor.body!.statements : node.members
),
/*multiLine*/ true
),
/*location*/ constructor ? constructor.body : undefined
);
}
/**
* Adds super call and preceding prologue directives into the list of statements.
*
* @param ctor The constructor node.
* @returns index of the statement that follows super call
*/
function addPrologueDirectivesAndInitialSuperCall(ctor: ConstructorDeclaration, result: Statement[]): number {
if (ctor.body) {
const statements = ctor.body.statements;
// add prologue directives to the list (if any)
const index = addPrologue(result, statements, /*ensureUseStrict*/ false, visitor);
if (index === statements.length) {
// list contains nothing but prologue directives (or empty) - exit
return index;
}
const statement = statements[index];
if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((<ExpressionStatement>statement).expression)) {
result.push(visitNode(statement, visitor, isStatement));
return index + 1;
}
return index;
}
return 0;
}
/**
* Gets all parameters of a constructor that should be transformed into property assignments.
*
* @param node The constructor node.
*/
function getParametersWithPropertyAssignments(node: ConstructorDeclaration): ReadonlyArray<ParameterDeclaration> {
return filter(node.parameters, isParameterWithPropertyAssignment);
}
/**
* Determines whether a parameter should be transformed into a property assignment.
*
* @param parameter The parameter node.
*/
function isParameterWithPropertyAssignment(parameter: ParameterDeclaration) {
return hasModifier(parameter, ModifierFlags.ParameterPropertyModifier)
&& isIdentifier(parameter.name);
}
/**
* Transforms a parameter into a property assignment statement.
*
* @param node The parameter declaration.
*/
function transformParameterWithPropertyAssignment(node: ParameterDeclaration) {
Debug.assert(isIdentifier(node.name));
const name = node.name as Identifier;
const propertyName = getMutableClone(name);
setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoSourceMap);
const localName = getMutableClone(name);
setEmitFlags(localName, EmitFlags.NoComments);
return startOnNewLine(
setEmitFlags(
setTextRange(
createExpressionStatement(
createAssignment(
setTextRange(
createPropertyAccess(
createThis(),
propertyName
),
node.name
),
localName
)
),
moveRangePos(node, -1)
),
EmitFlags.NoComments
)
);
}
/**
* Gets all property declarations with initializers on either the static or instance side of a class.
*
* @param node The class node.
* @param isStatic A value indicating whether to get properties from the static or instance side of the class.
*/
function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray<PropertyDeclaration> {
return filter(node.members, isStatic ? isStaticInitializedProperty : isInstanceInitializedProperty);
}
/**
* Gets a value indicating whether a class element is a static property declaration with an initializer.
*
* @param member The class element node.
*/
function isStaticInitializedProperty(member: ClassElement): member is PropertyDeclaration {
return isInitializedProperty(member, /*isStatic*/ true);
}
/**
* Gets a value indicating whether a class element is an instance property declaration with an initializer.
*
* @param member The class element node.
*/
function isInstanceInitializedProperty(member: ClassElement): member is PropertyDeclaration {
return isInitializedProperty(member, /*isStatic*/ false);
}
/**
* Gets a value indicating whether a class element is either a static or an instance property declaration with an initializer.
*
* @param member The class element node.
* @param isStatic A value indicating whether the member should be a static or instance member.
*/
function isInitializedProperty(member: ClassElement, isStatic: boolean) {
return member.kind === SyntaxKind.PropertyDeclaration
&& isStatic === hasModifier(member, ModifierFlags.Static)
&& (<PropertyDeclaration>member).initializer !== undefined;
}
/**
* Generates assignment statements for property initializers.
*
* @param properties An array of property declarations to transform.
* @param receiver The receiver on which each property should be assigned.
*/
function addInitializedPropertyStatements(statements: Statement[], properties: ReadonlyArray<PropertyDeclaration>, receiver: LeftHandSideExpression) {
for (const property of properties) {
const statement = createExpressionStatement(transformInitializedProperty(property, receiver));
setSourceMapRange(statement, moveRangePastModifiers(property));
setCommentRange(statement, property);
setOriginalNode(statement, property);
statements.push(statement);
}
}
/**
* Generates assignment expressions for property initializers.
*
* @param properties An array of property declarations to transform.
* @param receiver The receiver on which each property should be assigned.
*/
function generateInitializedPropertyExpressions(properties: ReadonlyArray<PropertyDeclaration>, receiver: LeftHandSideExpression) {
const expressions: Expression[] = [];
for (const property of properties) {
const expression = transformInitializedProperty(property, receiver);
startOnNewLine(expression);
setSourceMapRange(expression, moveRangePastModifiers(property));
setCommentRange(expression, property);
setOriginalNode(expression, property);
expressions.push(expression);
}
return expressions;
}
/**
* Transforms a property initializer into an assignment statement.
*
* @param property The property declaration.
* @param receiver The object receiving the property assignment.
*/
function transformInitializedProperty(property: PropertyDeclaration, receiver: LeftHandSideExpression) {
// We generate a name here in order to reuse the value cached by the relocated computed name expression (which uses the same generated name)
const propertyName = isComputedPropertyName(property.name) && !isSimpleInlineableExpression(property.name.expression)
? updateComputedPropertyName(property.name, getGeneratedNameForNode(property.name))
: property.name;
const initializer = visitNode(property.initializer!, visitor, isExpression);
const memberAccess = createMemberAccessForPropertyName(receiver, propertyName, /*location*/ propertyName);
return createAssignment(memberAccess, initializer);
}
/**
* Gets either the static or instance members of a class that are decorated, or have
@@ -2144,16 +1780,6 @@ namespace ts {
: createIdentifier("BigInt");
}
/**
* A simple inlinable expression is an expression which can be copied into multiple locations
* without risk of repeating any sideeffects and whose value could not possibly change between
* any such locations
*/
function isSimpleInlineableExpression(expression: Expression) {
return !isIdentifier(expression) && isSimpleCopiableExpression(expression) ||
isWellKnownSymbolSyntactically(expression);
}
/**
* Gets an expression that represents a property name. For a computed property, a
* name is generated for the node.
@@ -2175,26 +1801,6 @@ namespace ts {
}
}
/**
* If the name is a computed property, this function transforms it, then either returns an expression which caches the
* value of the result or the expression itself if the value is either unused or safe to inline into multiple locations
* @param shouldHoist Does the expression need to be reused? (ie, for an initializer or a decorator)
* @param omitSimple Should expressions with no observable side-effects be elided? (ie, the expression is not hoisted for a decorator or initializer and is a literal)
*/
function getPropertyNameExpressionIfNeeded(name: PropertyName, shouldHoist: boolean, omitSimple: boolean): Expression | undefined {
if (isComputedPropertyName(name)) {
const expression = visitNode(name.expression, visitor, isExpression);
const innerExpression = skipPartiallyEmittedExpressions(expression);
const inlinable = isSimpleInlineableExpression(innerExpression);
if (!inlinable && shouldHoist) {
const generatedName = getGeneratedNameForNode(name);
hoistVariableDeclaration(generatedName);
return createAssignment(generatedName, expression);
}
return (omitSimple && (inlinable || isIdentifier(innerExpression))) ? undefined : expression;
}
}
/**
* Visits the property name of a class element, for use when emitting property
* initializers. For a computed property on a node with decorators, a temporary
@@ -2204,18 +1810,20 @@ namespace ts {
*/
function visitPropertyNameOfClassElement(member: ClassElement): PropertyName {
const name = member.name!;
let expr = getPropertyNameExpressionIfNeeded(name, some(member.decorators), /*omitSimple*/ false);
if (expr) { // expr only exists if `name` is a computed property name
// Inline any pending expressions from previous elided or relocated computed property name expressions in order to preserve execution order
if (some(pendingExpressions)) {
expr = inlineExpressions([...pendingExpressions, expr]);
pendingExpressions.length = 0;
// Computed property names need to be transformed into a hoisted variable when they are used more than once.
// The names are used more than once when:
// - the property is non-static and its initializer is moved to the constructor (when there are parameter property assignments).
// - the property has a decorator.
if (isComputedPropertyName(name) && ((!hasStaticModifier(member) && currentClassHasParameterProperties) || some(member.decorators))) {
const expression = visitNode(name.expression, visitor, isExpression);
const innerExpression = skipPartiallyEmittedExpressions(expression);
if (!isSimpleInlineableExpression(innerExpression)) {
const generatedName = getGeneratedNameForNode(name);
hoistVariableDeclaration(generatedName);
return updateComputedPropertyName(name, createAssignment(generatedName, expression));
}
return updateComputedPropertyName(name as ComputedPropertyName, expr);
}
else {
return name;
}
return visitNode(name, visitor, isPropertyName);
}
/**
@@ -2257,16 +1865,27 @@ namespace ts {
*
* @param node The declaration node.
*/
function shouldEmitFunctionLikeDeclaration(node: FunctionLikeDeclaration) {
function shouldEmitFunctionLikeDeclaration<T extends FunctionLikeDeclaration>(node: T): node is T & { body: NonNullable<T["body"]> } {
return !nodeIsMissing(node.body);
}
function visitPropertyDeclaration(node: PropertyDeclaration): undefined {
const expr = getPropertyNameExpressionIfNeeded(node.name, some(node.decorators) || !!node.initializer, /*omitSimple*/ true);
if (expr && !isSimpleInlineableExpression(expr)) {
(pendingExpressions || (pendingExpressions = [])).push(expr);
function visitPropertyDeclaration(node: PropertyDeclaration) {
const updated = updateProperty(
node,
/*decorators*/ undefined,
visitNodes(node.modifiers, visitor, isModifier),
visitPropertyNameOfClassElement(node),
/*questionOrExclamationToken*/ undefined,
/*type*/ undefined,
visitNode(node.initializer, visitor)
);
if (updated !== node) {
// While we emit the source map for the node after skipping decorators and modifiers,
// we need to emit the comments for the original range.
setCommentRange(updated, node);
setSourceMapRange(updated, moveRangePastDecorators(node));
}
return undefined;
return updated;
}
function visitConstructor(node: ConstructorDeclaration) {
@@ -2276,10 +1895,90 @@ namespace ts {
return updateConstructor(
node,
visitNodes(node.decorators, visitor, isDecorator),
visitNodes(node.modifiers, visitor, isModifier),
/*decorators*/ undefined,
/*modifiers*/ undefined,
visitParameterList(node.parameters, visitor, context),
visitFunctionBody(node.body, visitor, context)
transformConstructorBody(node.body, node)
);
}
function transformConstructorBody(body: Block, constructor: ConstructorDeclaration) {
const parametersWithPropertyAssignments = constructor &&
filter(constructor.parameters, isParameterPropertyDeclaration);
if (!some(parametersWithPropertyAssignments)) {
return visitFunctionBody(body, visitor, context);
}
let statements: Statement[] = [];
let indexOfFirstStatement = 0;
resumeLexicalEnvironment();
indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements, visitor);
// Add parameters with property assignments. Transforms this:
//
// constructor (public x, public y) {
// }
//
// Into this:
//
// constructor (x, y) {
// this.x = x;
// this.y = y;
// }
//
addRange(statements, map(parametersWithPropertyAssignments, transformParameterWithPropertyAssignment));
// Add the existing statements, skipping the initial super call.
addRange(statements, visitNodes(body.statements, visitor, isStatement, indexOfFirstStatement));
// End the lexical environment.
statements = mergeLexicalEnvironment(statements, endLexicalEnvironment());
const block = createBlock(setTextRange(createNodeArray(statements), body.statements), /*multiLine*/ true);
setTextRange(block, /*location*/ body);
setOriginalNode(block, body);
return block;
}
/**
* Transforms a parameter into a property assignment statement.
*
* @param node The parameter declaration.
*/
function transformParameterWithPropertyAssignment(node: ParameterPropertyDeclaration) {
const name = node.name;
if (!isIdentifier(name)) {
return undefined;
}
const propertyName = getMutableClone(name);
setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoSourceMap);
const localName = getMutableClone(name);
setEmitFlags(localName, EmitFlags.NoComments);
return startOnNewLine(
removeAllComments(
setTextRange(
setOriginalNode(
createExpressionStatement(
createAssignment(
setTextRange(
createPropertyAccess(
createThis(),
propertyName
),
node.name
),
localName
)
),
node
),
moveRangePos(node, -1)
)
)
);
}
@@ -2418,6 +2117,7 @@ namespace ts {
if (parameterIsThisKeyword(node)) {
return undefined;
}
const updated = updateParameter(
node,
/*decorators*/ undefined,
+80
View File
@@ -240,6 +240,47 @@ namespace ts {
isIdentifier(expression);
}
/**
* A simple inlinable expression is an expression which can be copied into multiple locations
* without risk of repeating any sideeffects and whose value could not possibly change between
* any such locations
*/
export function isSimpleInlineableExpression(expression: Expression) {
return !isIdentifier(expression) && isSimpleCopiableExpression(expression) ||
isWellKnownSymbolSyntactically(expression);
}
/**
* Adds super call and preceding prologue directives into the list of statements.
*
* @param ctor The constructor node.
* @param result The list of statements.
* @param visitor The visitor to apply to each node added to the result array.
* @returns index of the statement that follows super call
*/
export function addPrologueDirectivesAndInitialSuperCall(ctor: ConstructorDeclaration, result: Statement[], visitor: Visitor): number {
if (ctor.body) {
const statements = ctor.body.statements;
// add prologue directives to the list (if any)
const index = addPrologue(result, statements, /*ensureUseStrict*/ false, visitor);
if (index === statements.length) {
// list contains nothing but prologue directives (or empty) - exit
return index;
}
const statement = statements[index];
if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((<ExpressionStatement>statement).expression)) {
result.push(visitNode(statement, visitor, isStatement));
return index + 1;
}
return index;
}
return 0;
}
/**
* @param input Template string input strings
* @param args Names which need to be made file-level unique
@@ -255,4 +296,43 @@ namespace ts {
return result;
};
}
/**
* Gets all property declarations with initializers on either the static or instance side of a class.
*
* @param node The class node.
* @param isStatic A value indicating whether to get properties from the static or instance side of the class.
*/
export function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray<PropertyDeclaration> {
return filter(node.members, isStatic ? isStaticInitializedProperty : isInstanceInitializedProperty);
}
/**
* Gets a value indicating whether a class element is a static property declaration with an initializer.
*
* @param member The class element node.
*/
export function isStaticInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } {
return isInitializedProperty(member) && hasStaticModifier(member);
}
/**
* Gets a value indicating whether a class element is an instance property declaration with an initializer.
*
* @param member The class element node.
*/
export function isInstanceInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } {
return isInitializedProperty(member) && !hasStaticModifier(member);
}
/**
* Gets a value indicating whether a class element is either a static or an instance property declaration with an initializer.
*
* @param member The class element node.
* @param isStatic A value indicating whether the member should be a static or instance member.
*/
export function isInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } {
return member.kind === SyntaxKind.PropertyDeclaration
&& (<PropertyDeclaration>member).initializer !== undefined;
}
}
+1727 -1232
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -8,6 +8,7 @@
"files": [
"core.ts",
"debug.ts",
"performance.ts",
"semver.ts",
@@ -29,6 +30,7 @@
"transformers/utilities.ts",
"transformers/destructuring.ts",
"transformers/ts.ts",
"transformers/classFields.ts",
"transformers/es2017.ts",
"transformers/es2018.ts",
"transformers/es2019.ts",
+45 -24
View File
@@ -2546,6 +2546,8 @@ namespace ts {
Shared = 1 << 10, // Referenced as antecedent more than once
PreFinally = 1 << 11, // Injected edge that links pre-finally label and pre-try flow
AfterFinally = 1 << 12, // Injected edge that links post-finally flow with the rest of the graph
/** @internal */
Cached = 1 << 13, // Indicates that at least one cross-call cache entry exists for this node, even if not a loop participant
Label = BranchLabel | LoopLabel,
Condition = TrueCondition | FalseCondition
}
@@ -2973,7 +2975,7 @@ namespace ts {
// For testing purposes only.
/* @internal */ structureIsReused?: StructureIsReused;
/* @internal */ getSourceFileFromReference(referencingFile: SourceFile, ref: FileReference): SourceFile | undefined;
/* @internal */ getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined;
/* @internal */ getLibFileFromReference(ref: FileReference): SourceFile | undefined;
/** Given a source file, get the name of the package it was imported from. */
@@ -3066,6 +3068,9 @@ namespace ts {
// Diagnostics were produced and outputs were generated in spite of them.
DiagnosticsPresent_OutputsGenerated = 2,
// When build skipped because passed in project is invalid
InvalidProject_OutputsSkipped = 3,
}
export interface EmitResult {
@@ -3152,6 +3157,7 @@ namespace ts {
*/
getExportSymbolOfSymbol(symbol: Symbol): Symbol;
getPropertySymbolOfDestructuringAssignment(location: Identifier): Symbol | undefined;
getTypeOfAssignmentPattern(pattern: AssignmentPattern): Type;
getTypeAtLocation(node: Node): Type;
getTypeFromTypeNode(node: TypeNode): Type;
@@ -3749,6 +3755,8 @@ namespace ts {
extendedContainers?: Symbol[]; // Containers (other than the parent) which this symbol is aliased in
extendedContainersByFile?: Map<Symbol[]>; // Containers (other than the parent) which this symbol is aliased in
variances?: VarianceFlags[]; // Alias symbol type argument variance cache
deferralConstituents?: Type[]; // Calculated list of constituents for a deferred type
deferralParent?: Type; // Source union/intersection of a deferred type
}
/* @internal */
@@ -3775,6 +3783,7 @@ namespace ts {
ReverseMapped = 1 << 13, // Property of reverse-inferred homomorphic mapped type
OptionalParameter = 1 << 14, // Optional parameter
RestParameter = 1 << 15, // Rest parameter
DeferredType = 1 << 16, // Calculation of the type of this symbol is deferred due to processing costs, should be fetched with `getTypeOfSymbolWithDeferredType`
Synthetic = SyntheticProperty | SyntheticMethod,
Discriminant = HasNonUniformType | HasLiteralType,
Partial = ReadPartial | WritePartial
@@ -3964,6 +3973,8 @@ namespace ts {
StructuredOrInstantiable = StructuredType | Instantiable,
/* @internal */
ObjectFlagsType = Nullable | Never | Object | Union | Intersection,
/* @internal */
Simplifiable = IndexedAccess | Conditional,
// 'Narrowable' types are types where narrowing actually narrows.
// This *should* be every type other than null, undefined, void, and never
Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
@@ -3972,7 +3983,7 @@ namespace ts {
NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | StructuredOrInstantiable,
// The following flags are aggregated during union and intersection type construction
/* @internal */
IncludesMask = Any | Unknown | Primitive | Never | Object | Union,
IncludesMask = Any | Unknown | Primitive | Never | Object | Union | NonPrimitive,
// The following flags are used for different purposes during union and intersection type construction
/* @internal */
IncludesStructuredOrInstantiable = TypeParameter,
@@ -4005,6 +4016,8 @@ namespace ts {
restrictiveInstantiation?: Type; // Instantiation with type parameters mapped to unconstrained form
/* @internal */
immediateBaseConstraint?: Type; // Immediate base constraint cache
/* @internal */
widened?: Type; // Cached widened form of the type
}
/* @internal */
@@ -4076,19 +4089,20 @@ namespace ts {
MarkerType = 1 << 13, // Marker type used for variance probing
JSLiteral = 1 << 14, // Object type declared in JS - disables errors on read/write of nonexisting members
FreshLiteral = 1 << 15, // Fresh object literal
ArrayLiteral = 1 << 16, // Originates in an array literal
/* @internal */
PrimitiveUnion = 1 << 16, // Union of only primitive types
PrimitiveUnion = 1 << 17, // Union of only primitive types
/* @internal */
ContainsWideningType = 1 << 17, // Type is or contains undefined or null widening type
ContainsWideningType = 1 << 18, // Type is or contains undefined or null widening type
/* @internal */
ContainsObjectLiteral = 1 << 18, // Type is or contains object literal type
ContainsObjectOrArrayLiteral = 1 << 19, // Type is or contains object literal type
/* @internal */
NonInferrableType = 1 << 19, // Type is or contains anyFunctionType or silentNeverType
NonInferrableType = 1 << 20, // Type is or contains anyFunctionType or silentNeverType
ClassOrInterface = Class | Interface,
/* @internal */
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
RequiresWidening = ContainsWideningType | ContainsObjectOrArrayLiteral,
/* @internal */
PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | NonInferrableType
PropagatingFlags = ContainsWideningType | ContainsObjectOrArrayLiteral | NonInferrableType
}
/* @internal */
@@ -4141,6 +4155,8 @@ namespace ts {
export interface TypeReference extends ObjectType {
target: GenericType; // Type reference target
typeArguments?: ReadonlyArray<Type>; // Type reference type arguments (undefined if none)
/* @internal */
literalType?: TypeReference; // Clone of type with ObjectFlags.ArrayLiteral set
}
/* @internal */
@@ -4336,8 +4352,8 @@ namespace ts {
root: ConditionalRoot;
checkType: Type;
extendsType: Type;
trueType: Type;
falseType: Type;
resolvedTrueType: Type;
resolvedFalseType: Type;
/* @internal */
resolvedInferredTrueType?: Type; // The `trueType` instantiated with the `combinedMapper`, if present
/* @internal */
@@ -4422,15 +4438,16 @@ namespace ts {
export type TypeMapper = (t: TypeParameter) => Type;
export const enum InferencePriority {
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
MappedTypeConstraint = 1 << 2, // Reverse inference for mapped type
ReturnType = 1 << 3, // Inference made from return type of generic function
LiteralKeyof = 1 << 4, // Inference made from a string literal to a keyof T
NoConstraints = 1 << 5, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 6, // Always use strict rules for contravariant inferences
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
PartialHomomorphicMappedType = 1 << 2, // Partial reverse inference for homomorphic mapped type
MappedTypeConstraint = 1 << 3, // Reverse inference for mapped type
ReturnType = 1 << 4, // Inference made from return type of generic function
LiteralKeyof = 1 << 5, // Inference made from a string literal to a keyof T
NoConstraints = 1 << 6, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 7, // Always use strict rules for contravariant inferences
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
}
/* @internal */
@@ -5183,6 +5200,7 @@ namespace ts {
ContainsYield = 1 << 17,
ContainsHoistedDeclarationOrCompletion = 1 << 18,
ContainsDynamicImport = 1 << 19,
ContainsClassFields = 1 << 20,
// Please leave this as 1 << 29.
// It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system.
@@ -5325,12 +5343,13 @@ namespace ts {
Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations)
Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation)
Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations)
Await = 1 << 11, // __await (used by ES2017 async generator transformation)
AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation)
AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation)
AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation)
ExportStar = 1 << 15, // __exportStar (used by CommonJS/AMD/UMD module transformation)
MakeTemplateObject = 1 << 16, // __makeTemplateObject (used for constructing template string array objects)
SpreadArrays = 1 << 11, // __spreadArrays (used by ES2015 array spread and argument list spread transformations)
Await = 1 << 12, // __await (used by ES2017 async generator transformation)
AsyncGenerator = 1 << 13, // __asyncGenerator (used by ES2017 async generator transformation)
AsyncDelegator = 1 << 14, // __asyncDelegator (used by ES2017 async generator yield* transformation)
AsyncValues = 1 << 15, // __asyncValues (used by ES2017 for..await..of transformation)
ExportStar = 1 << 16, // __exportStar (used by CommonJS/AMD/UMD module transformation)
MakeTemplateObject = 1 << 17, // __makeTemplateObject (used for constructing template string array objects)
FirstEmitHelper = Extends,
LastEmitHelper = MakeTemplateObject,
@@ -5380,6 +5399,7 @@ namespace ts {
writeFile: WriteFileCallback;
getProgramBuildInfo(): ProgramBuildInfo | undefined;
getSourceFileFromReference: Program["getSourceFileFromReference"];
}
export interface TransformationContext {
@@ -5710,6 +5730,7 @@ namespace ts {
/*@internal*/ writeBundleFileInfo?: boolean;
/*@internal*/ recordInternalSection?: boolean;
/*@internal*/ stripInternal?: boolean;
/*@internal*/ relativeToBuildInfo?: (path: string) => string;
}
/* @internal */
+27 -109
View File
@@ -2073,7 +2073,7 @@ namespace ts {
}
export function importFromModuleSpecifier(node: StringLiteralLike): AnyValidImportOrReExport {
return tryGetImportFromModuleSpecifier(node) || Debug.fail(Debug.showSyntaxKind(node.parent));
return tryGetImportFromModuleSpecifier(node) || Debug.failBadSyntaxKind(node.parent);
}
export function tryGetImportFromModuleSpecifier(node: StringLiteralLike): AnyValidImportOrReExport | undefined {
@@ -2201,13 +2201,13 @@ namespace ts {
let result: (JSDoc | JSDocTag)[] | undefined;
// Pull parameter comments from declaring function as well
if (isVariableLike(hostNode) && hasInitializer(hostNode) && hasJSDocNodes(hostNode.initializer!)) {
result = addRange(result, (hostNode.initializer as HasJSDoc).jsDoc!);
result = append(result, last((hostNode.initializer as HasJSDoc).jsDoc!));
}
let node: Node | undefined = hostNode;
while (node && node.parent) {
if (hasJSDocNodes(node)) {
result = addRange(result, node.jsDoc!);
result = append(result, last(node.jsDoc!));
}
if (node.kind === SyntaxKind.Parameter) {
@@ -2611,13 +2611,6 @@ namespace ts {
return undefined;
}
export function tryResolveScriptReference(host: ScriptReferenceHost, sourceFile: SourceFile | UnparsedSource, reference: FileReference) {
if (!host.getCompilerOptions().noResolve) {
const referenceFileName = isRootedDiskPath(reference.fileName) ? reference.fileName : combinePaths(getDirectoryPath(sourceFile.fileName), reference.fileName);
return host.getSourceFile(referenceFileName);
}
}
export function getAncestor(node: Node | undefined, kind: SyntaxKind): Node | undefined {
while (node) {
if (node.kind === kind) {
@@ -2712,11 +2705,19 @@ namespace ts {
return isStringLiteralLike(node) || isNumericLiteral(node);
}
export function isSignedNumericLiteral(node: Node): node is PrefixUnaryExpression & { operand: NumericLiteral } {
return isPrefixUnaryExpression(node) && (node.operator === SyntaxKind.PlusToken || node.operator === SyntaxKind.MinusToken) && isNumericLiteral(node.operand);
}
/**
* A declaration has a dynamic name if both of the following are true:
* 1. The declaration has a computed property name
* 2. The computed name is *not* expressed as Symbol.<name>, where name
* is a property of the Symbol constructor that denotes a built in
* A declaration has a dynamic name if all of the following are true:
* 1. The declaration has a computed property name.
* 2. The computed name is *not* expressed as a StringLiteral.
* 3. The computed name is *not* expressed as a NumericLiteral.
* 4. The computed name is *not* expressed as a PlusToken or MinusToken
* immediately followed by a NumericLiteral.
* 5. The computed name is *not* expressed as `Symbol.<name>`, where `<name>`
* is a property of the Symbol constructor that denotes a built-in
* Symbol.
*/
export function hasDynamicName(declaration: Declaration): declaration is DynamicNamedDeclaration {
@@ -2727,6 +2728,7 @@ namespace ts {
export function isDynamicName(name: DeclarationName): boolean {
return name.kind === SyntaxKind.ComputedPropertyName &&
!isStringOrNumericLiteralLike(name.expression) &&
!isSignedNumericLiteral(name.expression) &&
!isWellKnownSymbolSyntactically(name.expression);
}
@@ -4186,78 +4188,6 @@ namespace ts {
return getNewLine ? getNewLine() : sys ? sys.newLine : carriageReturnLineFeed;
}
/**
* Formats an enum value as a string for debugging and debug assertions.
*/
function formatEnum(value = 0, enumObject: any, isFlags?: boolean) {
const members = getEnumMembers(enumObject);
if (value === 0) {
return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0";
}
if (isFlags) {
let result = "";
let remainingFlags = value;
for (let i = members.length - 1; i >= 0 && remainingFlags !== 0; i--) {
const [enumValue, enumName] = members[i];
if (enumValue !== 0 && (remainingFlags & enumValue) === enumValue) {
remainingFlags &= ~enumValue;
result = `${enumName}${result ? ", " : ""}${result}`;
}
}
if (remainingFlags === 0) {
return result;
}
}
else {
for (const [enumValue, enumName] of members) {
if (enumValue === value) {
return enumName;
}
}
}
return value.toString();
}
function getEnumMembers(enumObject: any) {
const result: [number, string][] = [];
for (const name in enumObject) {
const value = enumObject[name];
if (typeof value === "number") {
result.push([value, name]);
}
}
return stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0]));
}
export function formatSyntaxKind(kind: SyntaxKind | undefined): string {
return formatEnum(kind, (<any>ts).SyntaxKind, /*isFlags*/ false);
}
export function formatModifierFlags(flags: ModifierFlags | undefined): string {
return formatEnum(flags, (<any>ts).ModifierFlags, /*isFlags*/ true);
}
export function formatTransformFlags(flags: TransformFlags | undefined): string {
return formatEnum(flags, (<any>ts).TransformFlags, /*isFlags*/ true);
}
export function formatEmitFlags(flags: EmitFlags | undefined): string {
return formatEnum(flags, (<any>ts).EmitFlags, /*isFlags*/ true);
}
export function formatSymbolFlags(flags: SymbolFlags | undefined): string {
return formatEnum(flags, (<any>ts).SymbolFlags, /*isFlags*/ true);
}
export function formatTypeFlags(flags: TypeFlags | undefined): string {
return formatEnum(flags, (<any>ts).TypeFlags, /*isFlags*/ true);
}
export function formatObjectFlags(flags: ObjectFlags | undefined): string {
return formatEnum(flags, (<any>ts).ObjectFlags, /*isFlags*/ true);
}
/**
* Creates a new TextRange from the provided pos and end.
*
@@ -5259,6 +5189,9 @@ namespace ts {
return node.parent.left.name;
}
}
else if (isVariableDeclaration(node.parent) && isIdentifier(node.parent.name)) {
return node.parent.name;
}
}
/**
@@ -7646,6 +7579,14 @@ namespace ts {
return root + pathComponents.slice(1).join(directorySeparator);
}
export function getNormalizedAbsolutePathWithoutRoot(fileName: string, currentDirectory: string | undefined) {
return getPathWithoutRoot(getNormalizedPathComponents(fileName, currentDirectory));
}
function getPathWithoutRoot(pathComponents: ReadonlyArray<string>) {
if (pathComponents.length === 0) return "";
return pathComponents.slice(1).join(directorySeparator);
}
}
/* @internal */
@@ -8420,29 +8361,6 @@ namespace ts {
return pathext ? path.slice(0, path.length - pathext.length) + (startsWith(ext, ".") ? ext : "." + ext) : path;
}
export namespace Debug {
export function showSymbol(symbol: Symbol): string {
const symbolFlags = (ts as any).SymbolFlags;
return `{ flags: ${symbolFlags ? showFlags(symbol.flags, symbolFlags) : symbol.flags}; declarations: ${map(symbol.declarations, showSyntaxKind)} }`;
}
function showFlags(flags: number, flagsEnum: { [flag: number]: string }): string {
const out: string[] = [];
for (let pow = 0; pow <= 30; pow++) {
const n = 1 << pow;
if (flags & n) {
out.push(flagsEnum[n]);
}
}
return out.join("|");
}
export function showSyntaxKind(node: Node): string {
const syntaxKind = (ts as any).SyntaxKind;
return syntaxKind ? syntaxKind[node.kind] : node.kind.toString();
}
}
export function tryParsePattern(pattern: string): Pattern | undefined {
// This should be verified outside of here and a proper error thrown.
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
-96
View File
@@ -1558,100 +1558,4 @@ namespace ts {
function aggregateTransformFlagsForChildNodes(transformFlags: TransformFlags, nodes: NodeArray<Node>): TransformFlags {
return transformFlags | aggregateTransformFlagsForNodeArray(nodes);
}
export namespace Debug {
let isDebugInfoEnabled = false;
export function failBadSyntaxKind(node: Node, message?: string): never {
return fail(
`${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`,
failBadSyntaxKind);
}
export const assertEachNode = shouldAssert(AssertionLevel.Normal)
? (nodes: Node[], test: (node: Node) => boolean, message?: string): void => assert(
test === undefined || every(nodes, test),
message || "Unexpected node.",
() => `Node array did not pass test '${getFunctionName(test)}'.`,
assertEachNode)
: noop;
export const assertNode = shouldAssert(AssertionLevel.Normal)
? (node: Node | undefined, test: ((node: Node | undefined) => boolean) | undefined, message?: string): void => assert(
test === undefined || test(node),
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node!.kind)} did not pass test '${getFunctionName(test!)}'.`,
assertNode)
: noop;
export const assertOptionalNode = shouldAssert(AssertionLevel.Normal)
? (node: Node, test: (node: Node) => boolean, message?: string): void => assert(
test === undefined || node === undefined || test(node),
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`,
assertOptionalNode)
: noop;
export const assertOptionalToken = shouldAssert(AssertionLevel.Normal)
? (node: Node, kind: SyntaxKind, message?: string): void => assert(
kind === undefined || node === undefined || node.kind === kind,
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} was not a '${formatSyntaxKind(kind)}' token.`,
assertOptionalToken)
: noop;
export const assertMissingNode = shouldAssert(AssertionLevel.Normal)
? (node: Node, message?: string): void => assert(
node === undefined,
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} was unexpected'.`,
assertMissingNode)
: noop;
/**
* Injects debug information into frequently used types.
*/
export function enableDebugInfo() {
if (isDebugInfoEnabled) return;
// Add additional properties in debug mode to assist with debugging.
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
__debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
});
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
__debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } },
__debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((<ObjectType>this).objectFlags) : ""; } },
__debugTypeToString: { value(this: Type) { return this.checker.typeToString(this); } },
});
const nodeConstructors = [
objectAllocator.getNodeConstructor(),
objectAllocator.getIdentifierConstructor(),
objectAllocator.getTokenConstructor(),
objectAllocator.getSourceFileConstructor()
];
for (const ctor of nodeConstructors) {
if (!ctor.prototype.hasOwnProperty("__debugKind")) {
Object.defineProperties(ctor.prototype, {
__debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } },
__debugModifierFlags: { get(this: Node) { return formatModifierFlags(getModifierFlagsNoCache(this)); } },
__debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
__debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
__debugGetText: {
value(this: Node, includeTrivia?: boolean) {
if (nodeIsSynthesized(this)) return "";
const parseNode = getParseTreeNode(this);
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
}
}
});
}
}
isDebugInfoEnabled = true;
}
}
}
+96 -55
View File
@@ -88,8 +88,6 @@ namespace ts {
return result;
}
export type ReportEmitErrorSummary = (errorCount: number) => void;
export function getErrorCountForSummary(diagnostics: ReadonlyArray<Diagnostic>) {
return countWhere(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error);
}
@@ -113,12 +111,12 @@ namespace ts {
getCurrentDirectory(): string;
getCompilerOptions(): CompilerOptions;
getSourceFiles(): ReadonlyArray<SourceFile>;
getSyntacticDiagnostics(): ReadonlyArray<Diagnostic>;
getOptionsDiagnostics(): ReadonlyArray<Diagnostic>;
getGlobalDiagnostics(): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(): ReadonlyArray<Diagnostic>;
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getOptionsDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback): EmitResult;
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
}
export function listFiles(program: ProgramToEmitFilesAndReportErrors, writeFileName: (s: string) => void) {
@@ -132,25 +130,35 @@ namespace ts {
/**
* Helper that emit files, report diagnostics and lists emitted and/or source files depending on compiler options
*/
export function emitFilesAndReportErrors(program: ProgramToEmitFilesAndReportErrors, reportDiagnostic: DiagnosticReporter, writeFileName?: (s: string) => void, reportSummary?: ReportEmitErrorSummary, writeFile?: WriteFileCallback) {
export function emitFilesAndReportErrors(
program: ProgramToEmitFilesAndReportErrors,
reportDiagnostic: DiagnosticReporter,
writeFileName?: (s: string) => void,
reportSummary?: ReportEmitErrorSummary,
writeFile?: WriteFileCallback,
cancellationToken?: CancellationToken,
emitOnlyDtsFiles?: boolean,
customTransformers?: CustomTransformers
) {
// First get and report any syntactic errors.
const diagnostics = program.getConfigFileParsingDiagnostics().slice();
const configFileParsingDiagnosticsLength = diagnostics.length;
addRange(diagnostics, program.getSyntacticDiagnostics());
addRange(diagnostics, program.getSyntacticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
// If we didn't have any syntactic errors, then also try getting the global and
// semantic errors.
if (diagnostics.length === configFileParsingDiagnosticsLength) {
addRange(diagnostics, program.getOptionsDiagnostics());
addRange(diagnostics, program.getGlobalDiagnostics());
addRange(diagnostics, program.getOptionsDiagnostics(cancellationToken));
addRange(diagnostics, program.getGlobalDiagnostics(cancellationToken));
if (diagnostics.length === configFileParsingDiagnosticsLength) {
addRange(diagnostics, program.getSemanticDiagnostics());
addRange(diagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
}
}
// Emit and report any errors we ran into.
const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit(/*targetSourceFile*/ undefined, writeFile);
const emitResult = program.emit(/*targetSourceFile*/ undefined, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
const { emittedFiles, diagnostics: emitDiagnostics } = emitResult;
addRange(diagnostics, emitDiagnostics);
sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic);
@@ -167,7 +175,34 @@ namespace ts {
reportSummary(getErrorCountForSummary(diagnostics));
}
if (emitSkipped && diagnostics.length > 0) {
return {
emitResult,
diagnostics,
};
}
export function emitFilesAndReportErrorsAndGetExitStatus(
program: ProgramToEmitFilesAndReportErrors,
reportDiagnostic: DiagnosticReporter,
writeFileName?: (s: string) => void,
reportSummary?: ReportEmitErrorSummary,
writeFile?: WriteFileCallback,
cancellationToken?: CancellationToken,
emitOnlyDtsFiles?: boolean,
customTransformers?: CustomTransformers
) {
const { emitResult, diagnostics } = emitFilesAndReportErrors(
program,
reportDiagnostic,
writeFileName,
reportSummary,
writeFile,
cancellationToken,
emitOnlyDtsFiles,
customTransformers
);
if (emitResult.emitSkipped && diagnostics.length > 0) {
// If the emitter didn't emit anything, then pass that value along.
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
@@ -179,7 +214,7 @@ namespace ts {
return ExitStatus.Success;
}
const noopFileWatcher: FileWatcher = { close: noop };
export const noopFileWatcher: FileWatcher = { close: noop };
export function createWatchHost(system = sys, reportWatchStatus?: WatchStatusReporter): WatchHost {
const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system);
@@ -375,43 +410,6 @@ namespace ts {
return host;
}
export function readBuilderProgram(compilerOptions: CompilerOptions, readFile: (path: string) => string | undefined) {
if (compilerOptions.out || compilerOptions.outFile) return undefined;
const buildInfoPath = getOutputPathForBuildInfo(compilerOptions);
if (!buildInfoPath) return undefined;
const content = readFile(buildInfoPath);
if (!content) return undefined;
const buildInfo = getBuildInfo(content);
if (buildInfo.version !== version) return undefined;
if (!buildInfo.program) return undefined;
return createBuildProgramUsingProgramBuildInfo(buildInfo.program);
}
export function createIncrementalCompilerHost(options: CompilerOptions, system = sys): CompilerHost {
const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, system);
host.createHash = maybeBind(system, system.createHash);
setGetSourceFileAsHashVersioned(host, system);
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName));
return host;
}
interface IncrementalProgramOptions<T extends BuilderProgram> {
rootNames: ReadonlyArray<string>;
options: CompilerOptions;
configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>;
projectReferences?: ReadonlyArray<ProjectReference>;
host?: CompilerHost;
createProgram?: CreateProgram<T>;
}
function createIncrementalProgram<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({
rootNames, options, configFileParsingDiagnostics, projectReferences, host, createProgram
}: IncrementalProgramOptions<T>): T {
host = host || createIncrementalCompilerHost(options);
createProgram = createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>;
const oldProgram = readBuilderProgram(options, path => host!.readFile(path)) as any as T;
return createProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences);
}
export interface IncrementalCompilationOptions {
rootNames: ReadonlyArray<string>;
options: CompilerOptions;
@@ -427,7 +425,7 @@ namespace ts {
const system = input.system || sys;
const host = input.host || (input.host = createIncrementalCompilerHost(input.options, system));
const builderProgram = createIncrementalProgram(input);
const exitStatus = emitFilesAndReportErrors(
const exitStatus = emitFilesAndReportErrorsAndGetExitStatus(
builderProgram,
input.reportDiagnostic || createDiagnosticReporter(system),
s => host.trace && host.trace(s),
@@ -439,6 +437,49 @@ namespace ts {
}
namespace ts {
export interface ReadBuildProgramHost {
useCaseSensitiveFileNames(): boolean;
getCurrentDirectory(): string;
readFile(fileName: string): string | undefined;
}
export function readBuilderProgram(compilerOptions: CompilerOptions, host: ReadBuildProgramHost) {
if (compilerOptions.out || compilerOptions.outFile) return undefined;
const buildInfoPath = getOutputPathForBuildInfo(compilerOptions);
if (!buildInfoPath) return undefined;
const content = host.readFile(buildInfoPath);
if (!content) return undefined;
const buildInfo = getBuildInfo(content);
if (buildInfo.version !== version) return undefined;
if (!buildInfo.program) return undefined;
return createBuildProgramUsingProgramBuildInfo(buildInfo.program, buildInfoPath, host);
}
export function createIncrementalCompilerHost(options: CompilerOptions, system = sys): CompilerHost {
const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, system);
host.createHash = maybeBind(system, system.createHash);
setGetSourceFileAsHashVersioned(host, system);
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName));
return host;
}
export interface IncrementalProgramOptions<T extends BuilderProgram> {
rootNames: ReadonlyArray<string>;
options: CompilerOptions;
configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>;
projectReferences?: ReadonlyArray<ProjectReference>;
host?: CompilerHost;
createProgram?: CreateProgram<T>;
}
export function createIncrementalProgram<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({
rootNames, options, configFileParsingDiagnostics, projectReferences, host, createProgram
}: IncrementalProgramOptions<T>): T {
host = host || createIncrementalCompilerHost(options);
createProgram = createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>;
const oldProgram = readBuilderProgram(options, host) as any as T;
return createProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences);
}
export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
export type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference> | undefined) => T;
@@ -567,7 +608,6 @@ namespace ts {
/*@internal*/
getCurrentProgram(): T;
/** Closes the watch */
/*@internal*/
close(): void;
}
@@ -691,6 +731,7 @@ namespace ts {
hasChangedAutomaticTypeDirectiveNames = true;
scheduleProgramUpdate();
};
compilerHost.fileIsOpen = returnFalse;
compilerHost.maxNumberOfFilesToIterateForInvalidation = host.maxNumberOfFilesToIterateForInvalidation;
compilerHost.getCurrentProgram = getCurrentProgram;
compilerHost.writeLog = writeLog;
@@ -710,7 +751,7 @@ namespace ts {
((typeDirectiveNames, containingFile, redirectedReference) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference));
const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives;
builderProgram = readBuilderProgram(compilerOptions, path => compilerHost.readFile(path)) as any as T;
builderProgram = readBuilderProgram(compilerOptions, compilerHost) as any as T;
synchronizeProgram();
// Update the wild card directory watch