mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Merge pull request #25593 from Microsoft/errorInFileWithDeepImport
Report errors correctly in watch mode by invalidating errors from files referencing modules that export from changed file
This commit is contained in:
commit
76f7ee998a
@ -30,10 +30,18 @@ namespace ts {
|
||||
* These will be commited whenever the iteration through affected files of current changed file is complete
|
||||
*/
|
||||
currentAffectedFilesSignatures: Map<string> | undefined;
|
||||
/**
|
||||
* Newly computed visible to outside referencedSet
|
||||
*/
|
||||
currentAffectedFilesExportedModulesMap: BuilderState.ComputingExportedModulesMap | undefined;
|
||||
/**
|
||||
* Already seen affected files
|
||||
*/
|
||||
seenAffectedFiles: Map<true> | undefined;
|
||||
/**
|
||||
* True if the semantic diagnostics were copied from the old state
|
||||
*/
|
||||
semanticDiagnosticsFromOldState?: Map<true>;
|
||||
/**
|
||||
* program corresponding to this state
|
||||
*/
|
||||
@ -96,6 +104,10 @@ namespace ts {
|
||||
const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
|
||||
if (diagnostics) {
|
||||
state.semanticDiagnosticsPerFile!.set(sourceFilePath, diagnostics);
|
||||
if (!state.semanticDiagnosticsFromOldState) {
|
||||
state.semanticDiagnosticsFromOldState = createMap<true>();
|
||||
}
|
||||
state.semanticDiagnosticsFromOldState.set(sourceFilePath, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -120,17 +132,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);
|
||||
cleanSemanticDiagnosticsOfAffectedFile(state, affectedFile);
|
||||
return affectedFile;
|
||||
}
|
||||
seenAffectedFiles!.set(affectedFile.path, true);
|
||||
seenAffectedFiles.set(affectedFile.path, true);
|
||||
affectedFilesIndex++;
|
||||
}
|
||||
|
||||
@ -140,6 +152,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,14 +173,74 @@ 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<BuilderState.ReferencedSet | false>();
|
||||
}
|
||||
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<true>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (removeSemanticDiagnosticsOf(state, affectedFile.path)) {
|
||||
// If there are no more diagnostics from old cache, done
|
||||
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.semanticDiagnosticsFromOldState) {
|
||||
return false;
|
||||
}
|
||||
state.semanticDiagnosticsFromOldState.delete(path);
|
||||
state.semanticDiagnosticsPerFile!.delete(path);
|
||||
return !state.semanticDiagnosticsFromOldState.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@ -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<BuilderState.ReferencedSet> | undefined;
|
||||
/**
|
||||
* Contains the map of exported modules ReferencedSet=exorted module files from the file if module emit is enabled
|
||||
* Otherwise undefined
|
||||
*/
|
||||
readonly exportedModulesMap: Map<BuilderState.ReferencedSet> | 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<ReferencedSet | false>;
|
||||
|
||||
/**
|
||||
* 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>): BuilderState {
|
||||
const fileInfos = createMap<FileInfo>();
|
||||
const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? createMap<ReferencedSet>() : undefined;
|
||||
const exportedModulesMap = referencedMap ? createMap<ReferencedSet>() : undefined;
|
||||
const hasCalledUpdateShapeSignature = createMap<true>();
|
||||
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<string>): ReadonlyArray<SourceFile> {
|
||||
export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: Map<string>, exportedModulesMapCache?: ComputingExportedModulesMap): ReadonlyArray<SourceFile> {
|
||||
// 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<BuilderState>, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map<string>, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash) {
|
||||
function updateShapeSignature(state: Readonly<BuilderState>, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map<string>, 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,61 @@ 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(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(sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ComputingExportedModulesMap) {
|
||||
if (!exportedModulesFromDeclarationEmit) {
|
||||
exportedModulesMapCache.set(sourceFile.path, false);
|
||||
return;
|
||||
}
|
||||
|
||||
let exportedModules: Map<true> | undefined;
|
||||
exportedModulesFromDeclarationEmit.forEach(symbol => addExportedModule(getReferencedFileFromImportedModuleSymbol(symbol)));
|
||||
exportedModulesMapCache.set(sourceFile.path, exportedModules || false);
|
||||
|
||||
function addExportedModule(exportedModulePath: Path | undefined) {
|
||||
if (exportedModulePath) {
|
||||
if (!exportedModules) {
|
||||
exportedModules = createMap<true>();
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the dependencies of the sourceFile
|
||||
*/
|
||||
@ -347,7 +429,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<string>, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined) {
|
||||
function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map<string>, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined, exportedModulesMapCache: ComputingExportedModulesMap | undefined) {
|
||||
if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) {
|
||||
return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape);
|
||||
}
|
||||
@ -370,7 +452,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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3914,6 +3914,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) {
|
||||
@ -28088,7 +28089,8 @@ namespace ts {
|
||||
setAccessor,
|
||||
getAccessor
|
||||
};
|
||||
}
|
||||
},
|
||||
getSymbolOfExternalModuleSpecifier: moduleName => resolveExternalModuleNameWorker(moduleName, moduleName, /*moduleNotFoundError*/ undefined)
|
||||
};
|
||||
|
||||
function isInHeritageClause(node: PropertyAccessEntityNameExpression) {
|
||||
|
||||
@ -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,10 @@ 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.exportedModulesFromDeclarationEmit;
|
||||
}
|
||||
}
|
||||
declarationTransform.dispose();
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@ namespace ts {
|
||||
let lateMarkedStatements: LateVisibilityPaintedStatement[] | undefined;
|
||||
let lateStatementReplacementMap: Map<VisitResult<LateVisibilityPaintedStatement>>;
|
||||
let suppressNewDiagnosticContexts: boolean;
|
||||
let exportedModulesFromDeclarationEmit: Symbol[] | undefined;
|
||||
|
||||
const host = context.getEmitHost();
|
||||
const symbolTracker: SymbolTracker = {
|
||||
@ -46,6 +47,7 @@ namespace ts {
|
||||
reportPrivateInBaseOfClassExpression,
|
||||
moduleResolverHost: host,
|
||||
trackReferencedAmbientModule,
|
||||
trackExternalModuleSymbolOfImportTypeNode
|
||||
};
|
||||
let errorNameNode: DeclarationName | undefined;
|
||||
|
||||
@ -115,6 +117,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function trackExternalModuleSymbolOfImportTypeNode(symbol: Symbol) {
|
||||
if (!isBundledEmit) {
|
||||
(exportedModulesFromDeclarationEmit || (exportedModulesFromDeclarationEmit = [])).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 +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);
|
||||
updated.exportedModulesFromDeclarationEmit = exportedModulesFromDeclarationEmit;
|
||||
return updated;
|
||||
|
||||
function getFileReferencesForUsedTypeReferences() {
|
||||
@ -483,10 +492,18 @@ namespace ts {
|
||||
function rewriteModuleSpecifier<T extends Node>(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 {
|
||||
const symbol = resolver.getSymbolOfExternalModuleSpecifier(input);
|
||||
if (symbol) {
|
||||
(exportedModulesFromDeclarationEmit || (exportedModulesFromDeclarationEmit = [])).push(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
return input;
|
||||
|
||||
@ -2633,8 +2633,13 @@ namespace ts {
|
||||
/* @internal */ pragmas: PragmaMap;
|
||||
/* @internal */ localJsxNamespace?: __String;
|
||||
/* @internal */ localJsxFactory?: EntityName;
|
||||
|
||||
/*@internal*/ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export type ExportedModulesFromDeclarationEmit = ReadonlyArray<Symbol>;
|
||||
|
||||
export interface Bundle extends Node {
|
||||
kind: SyntaxKind.Bundle;
|
||||
prepends: ReadonlyArray<InputFiles | UnparsedSource>;
|
||||
@ -2878,6 +2883,7 @@ namespace ts {
|
||||
diagnostics: ReadonlyArray<Diagnostic>;
|
||||
emittedFiles?: string[]; // Array of files the compiler wrote to disk
|
||||
/* @internal */ sourceMaps?: SourceMapData[]; // Array of sourceMapData if compiler emitted sourcemaps
|
||||
/* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@ -3381,6 +3387,7 @@ namespace ts {
|
||||
isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean;
|
||||
getJsxFactoryEntity(location?: Node): EntityName | undefined;
|
||||
getAllAccessorDeclarations(declaration: AccessorDeclaration): AllAccessorDeclarations;
|
||||
getSymbolOfExternalModuleSpecifier(node: StringLiteralLike): Symbol | undefined;
|
||||
}
|
||||
|
||||
export const enum SymbolFlags {
|
||||
@ -5338,6 +5345,7 @@ namespace ts {
|
||||
reportInaccessibleUniqueSymbolError?(): void;
|
||||
moduleResolverHost?: ModuleSpecifierResolutionHost;
|
||||
trackReferencedAmbientModule?(decl: ModuleDeclaration, symbol: Symbol): void;
|
||||
trackExternalModuleSymbolOfImportTypeNode?(symbol: Symbol): void;
|
||||
}
|
||||
|
||||
export interface TextSpan {
|
||||
|
||||
@ -1270,6 +1270,49 @@ 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);
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsc-watch emit with outFile or out setting", () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user