From d93e0d40967dee0a2383479b9d2b20ca82669978 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 10 Jul 2018 15:43:00 -0700 Subject: [PATCH 1/8] Add test for reporting errors from deep import. Test case for #24986 --- src/testRunner/unittests/tscWatchMode.ts | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/testRunner/unittests/tscWatchMode.ts b/src/testRunner/unittests/tscWatchMode.ts index 13361d13003..f962d736465 100644 --- a/src/testRunner/unittests/tscWatchMode.ts +++ b/src/testRunner/unittests/tscWatchMode.ts @@ -1270,6 +1270,46 @@ export default test;`; checkOutputErrorsIncremental(host, expectedErrors); } }); + + it("updates errors when deep import file changes", () => { + const currentDirectory = "/user/username/projects/myproject"; + const aFile: File = { + path: `${currentDirectory}/a.ts`, + content: `import {B} from './b'; +declare var console: any; +let b = new B(); +console.log(b.c.d);` + }; + const bFile: File = { + path: `${currentDirectory}/b.ts`, + content: `import {C} from './c'; +export class B +{ + c = new C(); +}` + }; + const cFile: File = { + path: `${currentDirectory}/c.ts`, + content: `export class C +{ + d = 1; +}` + }; + const config: File = { + path: `${currentDirectory}/tsconfig.json`, + content: `{}` + }; + const files = [aFile, bFile, cFile, config, libFile]; + const host = createWatchedSystem(files, { currentDirectory }); + const watch = createWatchOfConfigFile("tsconfig.json", host); + checkProgramActualFiles(watch(), [aFile.path, bFile.path, cFile.path, libFile.path]); + checkOutputErrorsInitial(host, emptyArray); + host.writeFile(cFile.path, cFile.content.replace("d", "d2")); + host.runQueuedTimeoutCallbacks(); + checkOutputErrorsIncremental(host, [ + getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.lastIndexOf("d"), 1, Diagnostics.Property_0_does_not_exist_on_type_1, "d", "C") + ]); + }); }); describe("tsc-watch emit with outFile or out setting", () => { From 0774fdc61d484f0b39c50ad34763f8f57b33b054 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 11 Jul 2018 13:34:30 -0700 Subject: [PATCH 2/8] Expose visible to outside import declarations and dynamic imports through emitDts output --- src/compiler/checker.ts | 1 + src/compiler/emitter.ts | 7 ++++++ src/compiler/transformers/declarations.ts | 28 +++++++++++++++++++---- src/compiler/types.ts | 10 ++++++++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2e48c4ca1bc..f788e8a78d6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3908,6 +3908,7 @@ namespace ts { const typeParameterNodes = overrideTypeArguments || lookupTypeParameterNodes(chain, 0, context); const specifier = getSpecifierForModuleSymbol(chain[0], context); const lit = createLiteralTypeNode(createLiteral(specifier)); + if (context.tracker.trackExternalModuleSymbolOfImportTypeNode) context.tracker.trackExternalModuleSymbolOfImportTypeNode(chain[0]); context.approximateLength += specifier.length + 10; // specifier + import("") if (!nonRootParts || isEntityName(nonRootParts)) { if (nonRootParts) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index e0bc826f66c..b2b52d11ffd 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -113,6 +113,7 @@ namespace ts { let bundleInfo: BundleInfo = createDefaultBundleInfo(); let emitSkipped = false; + let exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined; // Emit each output file performance.mark("beforePrint"); @@ -125,6 +126,7 @@ namespace ts { diagnostics: emitterDiagnostics.getDiagnostics(), emittedFiles: emittedFilesList, sourceMaps: sourceMapDataList, + exportedModulesFromDeclarationEmit }; function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { @@ -222,6 +224,11 @@ namespace ts { if (!declBlocked || emitOnlyDtsFiles) { Debug.assert(declarationTransform.transformed.length === 1, "Should only see one output from the decl transform"); printSourceFileOrBundle(declarationFilePath, declarationMapPath, declarationTransform.transformed[0], /* bundleInfopath*/ undefined, declarationPrinter, declarationSourceMap); + if (emitOnlyDtsFiles && declarationTransform.transformed[0].kind === SyntaxKind.SourceFile) { + const sourceFile = declarationTransform.transformed[0] as SourceFile; + exportedModulesFromDeclarationEmit = sourceFile.getExportedModulesFromDeclarationEmit && + sourceFile.getExportedModulesFromDeclarationEmit(); + } } declarationTransform.dispose(); } diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index dcfc50dd007..700dac68d2b 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -37,6 +37,8 @@ namespace ts { let lateMarkedStatements: LateVisibilityPaintedStatement[] | undefined; let lateStatementReplacementMap: Map>; let suppressNewDiagnosticContexts: boolean; + let exportedModuleSpecifiers: StringLiteralLike[] | undefined; + let exportedModuleSymbolsUsingImportTypeNodes: Symbol[] | undefined; const host = context.getEmitHost(); const symbolTracker: SymbolTracker = { @@ -46,6 +48,7 @@ namespace ts { reportPrivateInBaseOfClassExpression, moduleResolverHost: host, trackReferencedAmbientModule, + trackExternalModuleSymbolOfImportTypeNode }; let errorNameNode: DeclarationName | undefined; @@ -115,6 +118,12 @@ namespace ts { } } + function trackExternalModuleSymbolOfImportTypeNode(symbol: Symbol) { + if (!isBundledEmit) { + (exportedModuleSymbolsUsingImportTypeNodes || (exportedModuleSymbolsUsingImportTypeNodes = [])).push(symbol); + } + } + function trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) { if (symbol.flags & SymbolFlags.TypeParameter) return; handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true)); @@ -224,6 +233,12 @@ namespace ts { combinedStatements = setTextRange(createNodeArray([...combinedStatements, createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined)]), combinedStatements); } const updated = updateSourceFileNode(node, combinedStatements, /*isDeclarationFile*/ true, references, getFileReferencesForUsedTypeReferences(), node.hasNoDefaultLib); + if (exportedModuleSpecifiers || exportedModuleSymbolsUsingImportTypeNodes) { + updated.getExportedModulesFromDeclarationEmit = () => ({ + exportedModuleSpecifiers: exportedModuleSpecifiers || emptyArray, + exportedModuleSymbolsUsingImportTypeNodes: exportedModuleSymbolsUsingImportTypeNodes || emptyArray + }); + } return updated; function getFileReferencesForUsedTypeReferences() { @@ -483,10 +498,15 @@ namespace ts { function rewriteModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode, input: T | undefined): T | StringLiteral { if (!input) return undefined!; // TODO: GH#18217 resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || (parent.kind !== SyntaxKind.ModuleDeclaration && parent.kind !== SyntaxKind.ImportType); - if (input.kind === SyntaxKind.StringLiteral && isBundledEmit) { - const newName = getExternalModuleNameFromDeclaration(context.getEmitHost(), resolver, parent); - if (newName) { - return createLiteral(newName); + if (isStringLiteralLike(input)) { + if (isBundledEmit) { + const newName = getExternalModuleNameFromDeclaration(context.getEmitHost(), resolver, parent); + if (newName) { + return createLiteral(newName); + } + } + else { + (exportedModuleSpecifiers || (exportedModuleSpecifiers = [])).push(input); } } return input; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e1f347282b9..79d0f91b0fb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2624,6 +2624,14 @@ namespace ts { /* @internal */ pragmas: PragmaMap; /* @internal */ localJsxNamespace?: __String; /* @internal */ localJsxFactory?: EntityName; + + /*@internal*/ getExportedModulesFromDeclarationEmit?(): ExportedModulesFromDeclarationEmit; + } + + /*@internal*/ + export interface ExportedModulesFromDeclarationEmit { + exportedModuleSpecifiers: ReadonlyArray; + exportedModuleSymbolsUsingImportTypeNodes: ReadonlyArray; } export interface Bundle extends Node { @@ -2866,6 +2874,7 @@ namespace ts { diagnostics: ReadonlyArray; emittedFiles?: string[]; // Array of files the compiler wrote to disk /* @internal */ sourceMaps?: SourceMapData[]; // Array of sourceMapData if compiler emitted sourcemaps + /* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit; } /* @internal */ @@ -5324,6 +5333,7 @@ namespace ts { reportInaccessibleUniqueSymbolError?(): void; moduleResolverHost?: ModuleSpecifierResolutionHost; trackReferencedAmbientModule?(decl: ModuleDeclaration, symbol: Symbol): void; + trackExternalModuleSymbolOfImportTypeNode?(symbol: Symbol): void; } export interface TextSpan { From 585acb19901dea9bf3aaafb108a905bb236f425f Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 11 Jul 2018 15:49:11 -0700 Subject: [PATCH 3/8] Use exported modules through declaration emit to invalidate the semantic diagnostics Fixes #24986 --- src/compiler/builder.ts | 12 +- src/compiler/builderState.ts | 152 +++++++++++++++++++++-- src/testRunner/unittests/tscWatchMode.ts | 3 + 3 files changed, 153 insertions(+), 14 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index fc52559a318..bbbdb043616 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -30,6 +30,10 @@ namespace ts { * These will be commited whenever the iteration through affected files of current changed file is complete */ currentAffectedFilesSignatures: Map | undefined; + /** + * Newly computed visible to outside referencedSet + */ + currentAffectedFilesExportedModulesMap: BuilderState.ComputingExportedModulesMap | undefined; /** * Already seen affected files */ @@ -128,6 +132,8 @@ namespace ts { // Set the next affected file as seen and remove the cached semantic diagnostics state.affectedFilesIndex = affectedFilesIndex; semanticDiagnosticsPerFile!.delete(affectedFile.path); + // Remove semantic diagnostics for files that are affected by using exports of this module + BuilderState.getFilesAffectedByExportedModule(state, affectedFile.path, state.currentAffectedFilesExportedModulesMap).forEach(path => semanticDiagnosticsPerFile!.delete(path)); return affectedFile; } seenAffectedFiles!.set(affectedFile.path, true); @@ -140,6 +146,7 @@ namespace ts { // Commit the changes in file signature BuilderState.updateSignaturesFromCache(state, state.currentAffectedFilesSignatures!); state.currentAffectedFilesSignatures!.clear(); + BuilderState.updateExportedFilesMapFromCache(state, state.currentAffectedFilesExportedModulesMap); state.affectedFiles = undefined; } @@ -160,7 +167,10 @@ namespace ts { // Get next batch of affected files state.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures || createMap(); - state.affectedFiles = BuilderState.getFilesAffectedBy(state, state.program, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures); + if (state.exportedModulesMap) { + state.currentAffectedFilesExportedModulesMap = state.currentAffectedFilesExportedModulesMap || createMap(); + } + state.affectedFiles = BuilderState.getFilesAffectedBy(state, state.program, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures, state.currentAffectedFilesExportedModulesMap); state.currentChangedFilePath = nextKey.value as Path; state.semanticDiagnosticsPerFile!.delete(nextKey.value as Path); state.affectedFilesIndex = 0; diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index ed41ef4a8d3..9afc9575552 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -2,6 +2,7 @@ namespace ts { export interface EmitOutput { outputFiles: OutputFile[]; emitSkipped: boolean; + /* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit; } export interface OutputFile { @@ -17,7 +18,7 @@ namespace ts { cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput { const outputFiles: OutputFile[] = []; const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); - return { outputFiles, emitSkipped: emitResult.emitSkipped }; + return { outputFiles, emitSkipped: emitResult.emitSkipped, exportedModulesFromDeclarationEmit: emitResult.exportedModulesFromDeclarationEmit }; function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) { outputFiles.push({ name: fileName, writeByteOrderMark, text }); @@ -35,6 +36,11 @@ namespace ts { * Thus non undefined value indicates, module emit */ readonly referencedMap: ReadonlyMap | undefined; + /** + * Contains the map of exported modules ReferencedSet=exorted module files from the file if module emit is enabled + * Otherwise undefined + */ + readonly exportedModulesMap: Map | undefined; /** * Map of files that have already called update signature. * That means hence forth these files are assumed to have @@ -70,6 +76,30 @@ namespace ts.BuilderState { */ export type ComputeHash = (data: string) => string; + /** + * Exported modules to from declaration emit being computed. + * This can contain false in the affected file path to specify that there are no exported module(types from other modules) for this file + */ + export type ComputingExportedModulesMap = Map; + + /** + * Get the referencedFile from the imported module symbol + */ + function getReferencedFileFromImportedModuleSymbol(symbol: Symbol) { + if (symbol.declarations && symbol.declarations[0]) { + const declarationSourceFile = getSourceFileOfNode(symbol.declarations[0]); + return declarationSourceFile && declarationSourceFile.path; + } + } + + /** + * Get the referencedFile from the import name node from file + */ + function getReferencedFileFromImportLiteral(checker: TypeChecker, importName: StringLiteralLike) { + const symbol = checker.getSymbolAtLocation(importName); + return symbol && getReferencedFileFromImportedModuleSymbol(symbol); + } + /** * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true */ @@ -82,12 +112,9 @@ namespace ts.BuilderState { if (sourceFile.imports && sourceFile.imports.length > 0) { const checker: TypeChecker = program.getTypeChecker(); for (const importName of sourceFile.imports) { - const symbol = checker.getSymbolAtLocation(importName); - if (symbol && symbol.declarations && symbol.declarations[0]) { - const declarationSourceFile = getSourceFileOfNode(symbol.declarations[0]); - if (declarationSourceFile) { - addReferencedFile(declarationSourceFile.path); - } + const declarationSourceFilePath = getReferencedFileFromImportLiteral(checker, importName); + if (declarationSourceFilePath) { + addReferencedFile(declarationSourceFilePath); } } } @@ -137,6 +164,7 @@ namespace ts.BuilderState { export function create(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly): BuilderState { const fileInfos = createMap(); const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? createMap() : undefined; + const exportedModulesMap = referencedMap ? createMap() : undefined; const hasCalledUpdateShapeSignature = createMap(); const useOldState = canReuseOldState(referencedMap, oldState); @@ -149,6 +177,13 @@ namespace ts.BuilderState { if (newReferences) { referencedMap.set(sourceFile.path, newReferences); } + // Copy old visible to outside files map + if (useOldState) { + const exportedModules = oldState!.exportedModulesMap!.get(sourceFile.path); + if (exportedModules) { + exportedModulesMap!.set(sourceFile.path, exportedModules); + } + } } fileInfos.set(sourceFile.path, { version, signature: oldInfo && oldInfo.signature }); } @@ -156,6 +191,7 @@ namespace ts.BuilderState { return { fileInfos, referencedMap, + exportedModulesMap, hasCalledUpdateShapeSignature, allFilesExcludingDefaultLibraryFile: undefined, allFileNames: undefined @@ -165,7 +201,7 @@ namespace ts.BuilderState { /** * Gets the files affected by the path from the program */ - export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: Map): ReadonlyArray { + export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: Map, exportedModulesMapCache?: ComputingExportedModulesMap): ReadonlyArray { // Since the operation could be cancelled, the signatures are always stored in the cache // They will be commited once it is safe to use them // eg when calling this api from tsserver, if there is no cancellation of the operation @@ -176,11 +212,11 @@ namespace ts.BuilderState { return emptyArray; } - if (!updateShapeSignature(state, programOfThisState, sourceFile, signatureCache, cancellationToken, computeHash)) { + if (!updateShapeSignature(state, programOfThisState, sourceFile, signatureCache, cancellationToken, computeHash, exportedModulesMapCache)) { return [sourceFile]; } - const result = (state.referencedMap ? getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit)(state, programOfThisState, sourceFile, signatureCache, cancellationToken, computeHash); + const result = (state.referencedMap ? getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit)(state, programOfThisState, sourceFile, signatureCache, cancellationToken, computeHash, exportedModulesMapCache); if (!cacheToUpdateSignature) { // Commit all the signatures in the signature cache updateSignaturesFromCache(state, signatureCache); @@ -202,8 +238,9 @@ namespace ts.BuilderState { /** * Returns if the shape of the signature has changed since last emit */ - function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash) { + function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ComputingExportedModulesMap) { Debug.assert(!!sourceFile); + Debug.assert(!exportedModulesMapCache || !!state.exportedModulesMap, "Compute visible to outside map only if visibleToOutsideReferencedMap present in the state"); // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate if (state.hasCalledUpdateShapeSignature.has(sourceFile.path) || cacheToUpdateSignature.has(sourceFile.path)) { @@ -222,16 +259,105 @@ namespace ts.BuilderState { const emitOutput = getFileEmitOutput(programOfThisState, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken); if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { latestSignature = computeHash(emitOutput.outputFiles[0].text); + if (exportedModulesMapCache && latestSignature !== prevSignature) { + updateExportedModules(programOfThisState, sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache); + } } else { latestSignature = prevSignature!; // TODO: GH#18217 } + } cacheToUpdateSignature.set(sourceFile.path, latestSignature); return !prevSignature || latestSignature !== prevSignature; } + /** + * Coverts the declaration emit result into exported modules map + */ + function updateExportedModules(programOfThisState: Program, sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ComputingExportedModulesMap) { + if (!exportedModulesFromDeclarationEmit) { + exportedModulesMapCache.set(sourceFile.path, false); + return; + } + + const checker = programOfThisState.getTypeChecker(); + let exportedModules: Map | undefined; + + exportedModulesFromDeclarationEmit.exportedModuleSpecifiers.forEach(importName => + addExportedModule(getReferencedFileFromImportLiteral(checker, importName))); + exportedModulesFromDeclarationEmit.exportedModuleSymbolsUsingImportTypeNodes.forEach(symbol => + addExportedModule(getReferencedFileFromImportedModuleSymbol(symbol))); + + exportedModulesMapCache.set(sourceFile.path, exportedModules || false); + + function addExportedModule(exportedModulePath: Path | undefined) { + if (exportedModulePath) { + if (!exportedModules) { + exportedModules = createMap(); + } + exportedModules.set(exportedModulePath, true); + } + } + } + + /** + * Updates the exported modules from cache into state's exported modules map + * This should be called whenever it is safe to commit the state of the builder + */ + export function updateExportedFilesMapFromCache(state: BuilderState, exportedModulesMapCache: ComputingExportedModulesMap | undefined) { + if (exportedModulesMapCache) { + Debug.assert(!!state.exportedModulesMap); + exportedModulesMapCache.forEach((exportedModules, path) => { + if (exportedModules) { + state.exportedModulesMap!.set(path, exportedModules); + } + else { + state.exportedModulesMap!.delete(path); + } + }); + } + } + + /** + * Gets the files affected by exported module + */ + export function getFilesAffectedByExportedModule(state: BuilderState, path: Path, exportedModulesMapCache?: ComputingExportedModulesMap): ReadonlyArray { + if (!state.exportedModulesMap) { + return emptyArray; + } + + Debug.assert(!!exportedModulesMapCache); + let affectedFiles: Map | undefined; + // Go through exported modules from cache first + exportedModulesMapCache!.forEach((exportedModules, exportedFromPath) => { + // If exported modules has path, all files referencing file exported from are affected + if (exportedModules && exportedModules.has(path)) { + addFilesReferencing(exportedFromPath as Path); + } + }); + state.exportedModulesMap.forEach((exportedModules, exportedFromPath) => { + // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected + if (!exportedModulesMapCache!.has(exportedFromPath) && exportedModules.has(path)) { + addFilesReferencing(exportedFromPath as Path); + } + }); + + return affectedFiles ? arrayFrom(affectedFiles.keys()) as Path[] : emptyArray; + + function addFilesReferencing(referencingFilePath: Path) { + state.referencedMap!.forEach((referencesInFile, filePath) => { + if (referencesInFile.has(referencingFilePath)) { + if (!affectedFiles) { + affectedFiles = createMap(); + } + affectedFiles.set(filePath, true); + } + }); + } + } + /** * Get all the dependencies of the sourceFile */ @@ -347,7 +473,7 @@ namespace ts.BuilderState { /** * When program emits modular code, gets the files affected by the sourceFile whose shape has changed */ - function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined) { + function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined, exportedModulesMapCache: ComputingExportedModulesMap | undefined) { if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) { return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape); } @@ -370,7 +496,7 @@ namespace ts.BuilderState { if (!seenFileNamesMap.has(currentPath)) { const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath)!; seenFileNamesMap.set(currentPath, currentSourceFile); - if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken, computeHash!)) { // TODO: GH#18217 + if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken, computeHash!, exportedModulesMapCache)) { // TODO: GH#18217 queue.push(...getReferencedByPaths(state, currentPath)); } } diff --git a/src/testRunner/unittests/tscWatchMode.ts b/src/testRunner/unittests/tscWatchMode.ts index f962d736465..797fcbc2a79 100644 --- a/src/testRunner/unittests/tscWatchMode.ts +++ b/src/testRunner/unittests/tscWatchMode.ts @@ -1304,11 +1304,14 @@ export class B const watch = createWatchOfConfigFile("tsconfig.json", host); checkProgramActualFiles(watch(), [aFile.path, bFile.path, cFile.path, libFile.path]); checkOutputErrorsInitial(host, emptyArray); + const modifiedTimeOfAJs = host.getModifiedTime(`${currentDirectory}/a.js`); host.writeFile(cFile.path, cFile.content.replace("d", "d2")); host.runQueuedTimeoutCallbacks(); checkOutputErrorsIncremental(host, [ getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.lastIndexOf("d"), 1, Diagnostics.Property_0_does_not_exist_on_type_1, "d", "C") ]); + // File a need not be rewritten + assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs); }); }); From 50b75e7585018da4b4047d75d82559434229ca69 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 12 Jul 2018 13:24:51 -0700 Subject: [PATCH 4/8] Optimize the deletion and calculation of bigger set of semantic diagnostics to be calculated --- src/compiler/builder.ts | 79 ++++++++++++++++++++++++++++++++---- src/compiler/builderState.ts | 38 ----------------- 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index bbbdb043616..8d155a78922 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -38,6 +38,10 @@ namespace ts { * Already seen affected files */ seenAffectedFiles: Map | undefined; + /** + * True if the semantic diagnostics were copied from the old state + */ + semanticDiagnosticsFromOldStateFiles?: number; /** * program corresponding to this state */ @@ -100,6 +104,7 @@ namespace ts { const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath); if (diagnostics) { state.semanticDiagnosticsPerFile!.set(sourceFilePath, diagnostics); + state.semanticDiagnosticsFromOldStateFiles = (state.semanticDiagnosticsFromOldStateFiles || 0) + 1; } } }); @@ -124,19 +129,17 @@ namespace ts { while (true) { const { affectedFiles } = state; if (affectedFiles) { - const { seenAffectedFiles, semanticDiagnosticsPerFile } = state; + const seenAffectedFiles = state.seenAffectedFiles!; let affectedFilesIndex = state.affectedFilesIndex!; // TODO: GH#18217 while (affectedFilesIndex < affectedFiles.length) { const affectedFile = affectedFiles[affectedFilesIndex]; - if (!seenAffectedFiles!.has(affectedFile.path)) { + if (!seenAffectedFiles.has(affectedFile.path)) { // Set the next affected file as seen and remove the cached semantic diagnostics state.affectedFilesIndex = affectedFilesIndex; - semanticDiagnosticsPerFile!.delete(affectedFile.path); - // Remove semantic diagnostics for files that are affected by using exports of this module - BuilderState.getFilesAffectedByExportedModule(state, affectedFile.path, state.currentAffectedFilesExportedModulesMap).forEach(path => semanticDiagnosticsPerFile!.delete(path)); + cleanSemanticDiagnosticsOfAffectedFile(state, affectedFile); return affectedFile; } - seenAffectedFiles!.set(affectedFile.path, true); + seenAffectedFiles.set(affectedFile.path, true); affectedFilesIndex++; } @@ -172,12 +175,74 @@ namespace ts { } state.affectedFiles = BuilderState.getFilesAffectedBy(state, state.program, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures, state.currentAffectedFilesExportedModulesMap); state.currentChangedFilePath = nextKey.value as Path; - state.semanticDiagnosticsPerFile!.delete(nextKey.value as Path); state.affectedFilesIndex = 0; state.seenAffectedFiles = state.seenAffectedFiles || createMap(); } } + /** + * Remove the semantic diagnostics cached from old state for affected File and the files that are referencing modules that export entities from affected file + */ + function cleanSemanticDiagnosticsOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile) { + if (!state.semanticDiagnosticsFromOldStateFiles) { + Debug.assert(!state.semanticDiagnosticsPerFile!.has(affectedFile.path)); + return; + } + + if (removeSemanticDiagnosticsOf(state, affectedFile.path)) { + // If there are no more diagnostics from old cache, remove them + return; + } + + // If there was change in signature for the changed file, + // then delete the semantic diagnostics for files that are affected by using exports of this module + + if (!state.exportedModulesMap || state.affectedFiles!.length === 1 || !state.changedFilesSet.has(affectedFile.path)) { + return; + } + + Debug.assert(!!state.currentAffectedFilesExportedModulesMap); + // Go through exported modules from cache first + // If exported modules has path, all files referencing file exported from are affected + if (forEachEntry(state.currentAffectedFilesExportedModulesMap!, (exportedModules, exportedFromPath) => + exportedModules && + exportedModules.has(affectedFile.path) && + removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path) + )) { + return; + } + + // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected + forEachEntry(state.exportedModulesMap, (exportedModules, exportedFromPath) => + !state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it + exportedModules.has(affectedFile.path) && + removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path) + ); + } + + /** + * removes the semantic diagnostics of files referencing referencedPath and + * returns true if there are no more semantic diagnostics from old state + */ + function removeSemanticDiagnosticsOfFilesReferencingPath(state: BuilderProgramState, referencedPath: Path) { + return forEachEntry(state.referencedMap!, (referencesInFile, filePath) => + referencesInFile.has(referencedPath) && removeSemanticDiagnosticsOf(state, filePath as Path) + ); + } + + /** + * Removes semantic diagnostics for path and + * returns true if there are no more semantic diagnostics from the old state + */ + function removeSemanticDiagnosticsOf(state: BuilderProgramState, path: Path) { + if (state.semanticDiagnosticsPerFile!.delete(path)) { + Debug.assert((state.semanticDiagnosticsFromOldStateFiles || 0) > 0); + state.semanticDiagnosticsFromOldStateFiles!--; + return !state.semanticDiagnosticsFromOldStateFiles; + } + return false; + } + /** * This is called after completing operation on the next affected file. * The operations here are postponed to ensure that cancellation during the iteration is handled correctly diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index 9afc9575552..377d7313284 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -320,44 +320,6 @@ namespace ts.BuilderState { } } - /** - * Gets the files affected by exported module - */ - export function getFilesAffectedByExportedModule(state: BuilderState, path: Path, exportedModulesMapCache?: ComputingExportedModulesMap): ReadonlyArray { - if (!state.exportedModulesMap) { - return emptyArray; - } - - Debug.assert(!!exportedModulesMapCache); - let affectedFiles: Map | undefined; - // Go through exported modules from cache first - exportedModulesMapCache!.forEach((exportedModules, exportedFromPath) => { - // If exported modules has path, all files referencing file exported from are affected - if (exportedModules && exportedModules.has(path)) { - addFilesReferencing(exportedFromPath as Path); - } - }); - state.exportedModulesMap.forEach((exportedModules, exportedFromPath) => { - // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected - if (!exportedModulesMapCache!.has(exportedFromPath) && exportedModules.has(path)) { - addFilesReferencing(exportedFromPath as Path); - } - }); - - return affectedFiles ? arrayFrom(affectedFiles.keys()) as Path[] : emptyArray; - - function addFilesReferencing(referencingFilePath: Path) { - state.referencedMap!.forEach((referencesInFile, filePath) => { - if (referencesInFile.has(referencingFilePath)) { - if (!affectedFiles) { - affectedFiles = createMap(); - } - affectedFiles.set(filePath, true); - } - }); - } - } - /** * Get all the dependencies of the sourceFile */ From f7bc8e18e8ec81c3b4c683154cba046de17164fb Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 1 Aug 2018 12:20:54 -0700 Subject: [PATCH 5/8] No need for method to get the exportedModulesFromDeclarationEmit --- src/compiler/emitter.ts | 3 +-- src/compiler/transformers/declarations.ts | 4 ++-- src/compiler/types.ts | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index b2b52d11ffd..9e7cbd1bb0c 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -226,8 +226,7 @@ namespace ts { printSourceFileOrBundle(declarationFilePath, declarationMapPath, declarationTransform.transformed[0], /* bundleInfopath*/ undefined, declarationPrinter, declarationSourceMap); if (emitOnlyDtsFiles && declarationTransform.transformed[0].kind === SyntaxKind.SourceFile) { const sourceFile = declarationTransform.transformed[0] as SourceFile; - exportedModulesFromDeclarationEmit = sourceFile.getExportedModulesFromDeclarationEmit && - sourceFile.getExportedModulesFromDeclarationEmit(); + exportedModulesFromDeclarationEmit = sourceFile.exportedModulesFromDeclarationEmit; } } declarationTransform.dispose(); diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 700dac68d2b..13c05e0f0de 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -234,10 +234,10 @@ namespace ts { } const updated = updateSourceFileNode(node, combinedStatements, /*isDeclarationFile*/ true, references, getFileReferencesForUsedTypeReferences(), node.hasNoDefaultLib); if (exportedModuleSpecifiers || exportedModuleSymbolsUsingImportTypeNodes) { - updated.getExportedModulesFromDeclarationEmit = () => ({ + updated.exportedModulesFromDeclarationEmit = { exportedModuleSpecifiers: exportedModuleSpecifiers || emptyArray, exportedModuleSymbolsUsingImportTypeNodes: exportedModuleSymbolsUsingImportTypeNodes || emptyArray - }); + }; } return updated; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 79d0f91b0fb..1bd2ca76db7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2625,7 +2625,7 @@ namespace ts { /* @internal */ localJsxNamespace?: __String; /* @internal */ localJsxFactory?: EntityName; - /*@internal*/ getExportedModulesFromDeclarationEmit?(): ExportedModulesFromDeclarationEmit; + /*@internal*/ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit; } /*@internal*/ From 51837bb59ab656ad8f3e18a098d4d467610171d4 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 1 Aug 2018 12:37:39 -0700 Subject: [PATCH 6/8] Make ExportedModulesFromDeclarationEmit as ReadonlyArray of symbols combining exportSpecifiers emitted and dynamic import type nodes written --- src/compiler/builderState.ts | 12 +++--------- src/compiler/checker.ts | 3 ++- src/compiler/transformers/declarations.ts | 17 +++++++---------- src/compiler/types.ts | 6 ++---- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index 377d7313284..12936e5b2e8 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -260,7 +260,7 @@ namespace ts.BuilderState { if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { latestSignature = computeHash(emitOutput.outputFiles[0].text); if (exportedModulesMapCache && latestSignature !== prevSignature) { - updateExportedModules(programOfThisState, sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache); + updateExportedModules(sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache); } } else { @@ -276,20 +276,14 @@ namespace ts.BuilderState { /** * Coverts the declaration emit result into exported modules map */ - function updateExportedModules(programOfThisState: Program, sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ComputingExportedModulesMap) { + function updateExportedModules(sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ComputingExportedModulesMap) { if (!exportedModulesFromDeclarationEmit) { exportedModulesMapCache.set(sourceFile.path, false); return; } - const checker = programOfThisState.getTypeChecker(); let exportedModules: Map | undefined; - - exportedModulesFromDeclarationEmit.exportedModuleSpecifiers.forEach(importName => - addExportedModule(getReferencedFileFromImportLiteral(checker, importName))); - exportedModulesFromDeclarationEmit.exportedModuleSymbolsUsingImportTypeNodes.forEach(symbol => - addExportedModule(getReferencedFileFromImportedModuleSymbol(symbol))); - + exportedModulesFromDeclarationEmit.forEach(symbol => addExportedModule(getReferencedFileFromImportedModuleSymbol(symbol))); exportedModulesMapCache.set(sourceFile.path, exportedModules || false); function addExportedModule(exportedModulePath: Path | undefined) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f788e8a78d6..b4e7683ed6b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27784,7 +27784,8 @@ namespace ts { setAccessor, getAccessor }; - } + }, + getSymbolAtLocation }; function isInHeritageClause(node: PropertyAccessEntityNameExpression) { diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 13c05e0f0de..e1ba313d584 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -37,8 +37,7 @@ namespace ts { let lateMarkedStatements: LateVisibilityPaintedStatement[] | undefined; let lateStatementReplacementMap: Map>; let suppressNewDiagnosticContexts: boolean; - let exportedModuleSpecifiers: StringLiteralLike[] | undefined; - let exportedModuleSymbolsUsingImportTypeNodes: Symbol[] | undefined; + let exportedModulesFromDeclarationEmit: Symbol[] | undefined; const host = context.getEmitHost(); const symbolTracker: SymbolTracker = { @@ -120,7 +119,7 @@ namespace ts { function trackExternalModuleSymbolOfImportTypeNode(symbol: Symbol) { if (!isBundledEmit) { - (exportedModuleSymbolsUsingImportTypeNodes || (exportedModuleSymbolsUsingImportTypeNodes = [])).push(symbol); + (exportedModulesFromDeclarationEmit || (exportedModulesFromDeclarationEmit = [])).push(symbol); } } @@ -233,12 +232,7 @@ namespace ts { combinedStatements = setTextRange(createNodeArray([...combinedStatements, createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined)]), combinedStatements); } const updated = updateSourceFileNode(node, combinedStatements, /*isDeclarationFile*/ true, references, getFileReferencesForUsedTypeReferences(), node.hasNoDefaultLib); - if (exportedModuleSpecifiers || exportedModuleSymbolsUsingImportTypeNodes) { - updated.exportedModulesFromDeclarationEmit = { - exportedModuleSpecifiers: exportedModuleSpecifiers || emptyArray, - exportedModuleSymbolsUsingImportTypeNodes: exportedModuleSymbolsUsingImportTypeNodes || emptyArray - }; - } + updated.exportedModulesFromDeclarationEmit = exportedModulesFromDeclarationEmit; return updated; function getFileReferencesForUsedTypeReferences() { @@ -506,7 +500,10 @@ namespace ts { } } else { - (exportedModuleSpecifiers || (exportedModuleSpecifiers = [])).push(input); + const symbol = resolver.getSymbolAtLocation(input); + if (symbol) { + (exportedModulesFromDeclarationEmit || (exportedModulesFromDeclarationEmit = [])).push(symbol); + } } } return input; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1bd2ca76db7..d948825a778 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2629,10 +2629,7 @@ namespace ts { } /*@internal*/ - export interface ExportedModulesFromDeclarationEmit { - exportedModuleSpecifiers: ReadonlyArray; - exportedModuleSymbolsUsingImportTypeNodes: ReadonlyArray; - } + export type ExportedModulesFromDeclarationEmit = ReadonlyArray; export interface Bundle extends Node { kind: SyntaxKind.Bundle; @@ -3375,6 +3372,7 @@ namespace ts { isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean; getJsxFactoryEntity(location?: Node): EntityName | undefined; getAllAccessorDeclarations(declaration: AccessorDeclaration): AllAccessorDeclarations; + getSymbolAtLocation(node: Node): Symbol | undefined; } export const enum SymbolFlags { From 8ea95023f16441774301e8b066b2f1dca930f055 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 1 Aug 2018 13:00:26 -0700 Subject: [PATCH 7/8] Make a map for semantic diagnostics from old state --- src/compiler/builder.ts | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 8d155a78922..e2d59975d04 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -41,7 +41,7 @@ namespace ts { /** * True if the semantic diagnostics were copied from the old state */ - semanticDiagnosticsFromOldStateFiles?: number; + semanticDiagnosticsFromOldState?: Map; /** * program corresponding to this state */ @@ -104,7 +104,10 @@ namespace ts { const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath); if (diagnostics) { state.semanticDiagnosticsPerFile!.set(sourceFilePath, diagnostics); - state.semanticDiagnosticsFromOldStateFiles = (state.semanticDiagnosticsFromOldStateFiles || 0) + 1; + if (!state.semanticDiagnosticsFromOldState) { + state.semanticDiagnosticsFromOldState = createMap(); + } + state.semanticDiagnosticsFromOldState.set(sourceFilePath, true); } } }); @@ -184,13 +187,8 @@ namespace ts { * Remove the semantic diagnostics cached from old state for affected File and the files that are referencing modules that export entities from affected file */ function cleanSemanticDiagnosticsOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile) { - if (!state.semanticDiagnosticsFromOldStateFiles) { - Debug.assert(!state.semanticDiagnosticsPerFile!.has(affectedFile.path)); - return; - } - if (removeSemanticDiagnosticsOf(state, affectedFile.path)) { - // If there are no more diagnostics from old cache, remove them + // If there are no more diagnostics from old cache, done return; } @@ -235,12 +233,12 @@ namespace ts { * returns true if there are no more semantic diagnostics from the old state */ function removeSemanticDiagnosticsOf(state: BuilderProgramState, path: Path) { - if (state.semanticDiagnosticsPerFile!.delete(path)) { - Debug.assert((state.semanticDiagnosticsFromOldStateFiles || 0) > 0); - state.semanticDiagnosticsFromOldStateFiles!--; - return !state.semanticDiagnosticsFromOldStateFiles; + if (!state.semanticDiagnosticsFromOldState) { + return false; } - return false; + state.semanticDiagnosticsFromOldState.delete(path); + state.semanticDiagnosticsPerFile!.delete(path); + return !state.semanticDiagnosticsFromOldState.size; } /** From 06fead598743d7a18d0e90e0302918904bac5233 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 1 Aug 2018 16:25:22 -0700 Subject: [PATCH 8/8] Instead of getSymbolAtLocation use resolveExternalModuleName without error reporting --- src/compiler/checker.ts | 2 +- src/compiler/transformers/declarations.ts | 2 +- src/compiler/types.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 68de568d2cf..3c307585a9d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28090,7 +28090,7 @@ namespace ts { getAccessor }; }, - getSymbolAtLocation + getSymbolOfExternalModuleSpecifier: moduleName => resolveExternalModuleNameWorker(moduleName, moduleName, /*moduleNotFoundError*/ undefined) }; function isInHeritageClause(node: PropertyAccessEntityNameExpression) { diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 35c8f103791..2fa093128bc 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -500,7 +500,7 @@ namespace ts { } } else { - const symbol = resolver.getSymbolAtLocation(input); + const symbol = resolver.getSymbolOfExternalModuleSpecifier(input); if (symbol) { (exportedModulesFromDeclarationEmit || (exportedModulesFromDeclarationEmit = [])).push(symbol); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 40b5f1295f4..bc5456ffe7f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3387,7 +3387,7 @@ namespace ts { isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean; getJsxFactoryEntity(location?: Node): EntityName | undefined; getAllAccessorDeclarations(declaration: AccessorDeclaration): AllAccessorDeclarations; - getSymbolAtLocation(node: Node): Symbol | undefined; + getSymbolOfExternalModuleSpecifier(node: StringLiteralLike): Symbol | undefined; } export const enum SymbolFlags {