Skip to content

Commit 5cfd3dc

Browse files
author
Andy Hanson
committed
Merge branch 'master' into convertExport
2 parents da4cc60 + 374cbd6 commit 5cfd3dc

35 files changed

+499
-136
lines changed

src/compiler/checker.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22601,6 +22601,10 @@ namespace ts {
2260122601
checkSourceElement(node.typeExpression);
2260222602
}
2260322603

22604+
function checkJSDocTypeTag(node: JSDocTypeTag) {
22605+
checkSourceElement(node.typeExpression);
22606+
}
22607+
2260422608
function checkJSDocParameterTag(node: JSDocParameterTag) {
2260522609
checkSourceElement(node.typeExpression);
2260622610
if (!getParameterSymbolFromJSDoc(node)) {
@@ -22895,7 +22899,11 @@ namespace ts {
2289522899
}
2289622900

2289722901
for (const declaration of local.declarations) {
22898-
if (isAmbientModule(declaration)) continue;
22902+
if (isAmbientModule(declaration) ||
22903+
(isVariableDeclaration(declaration) && isForInOrOfStatement(declaration.parent.parent) || isImportedDeclaration(declaration)) && isIdentifierThatStartsWithUnderScore(declaration.name!)) {
22904+
continue;
22905+
}
22906+
2289922907
if (isImportedDeclaration(declaration)) {
2290022908
addToGroup(unusedImports, importClauseFromImported(declaration), declaration, getNodeId);
2290122909
}
@@ -22907,9 +22915,7 @@ namespace ts {
2290722915
}
2290822916
}
2290922917
else if (isVariableDeclaration(declaration)) {
22910-
if (!isIdentifierThatStartsWithUnderScore(declaration.name) || !isForInOrOfStatement(declaration.parent.parent)) {
22911-
addToGroup(unusedVariables, declaration.parent, declaration, getNodeId);
22912-
}
22918+
addToGroup(unusedVariables, declaration.parent, declaration, getNodeId);
2291322919
}
2291422920
else {
2291522921
const parameter = local.valueDeclaration && tryGetRootParameterDeclaration(local.valueDeclaration);
@@ -25560,6 +25566,8 @@ namespace ts {
2556025566
case SyntaxKind.JSDocTypedefTag:
2556125567
case SyntaxKind.JSDocCallbackTag:
2556225568
return checkJSDocTypeAliasTag(node as JSDocTypedefTag);
25569+
case SyntaxKind.JSDocTypeTag:
25570+
return checkJSDocTypeTag(node as JSDocTypeTag);
2556325571
case SyntaxKind.JSDocParameterTag:
2556425572
return checkJSDocParameterTag(node as JSDocParameterTag);
2556525573
case SyntaxKind.JSDocFunctionType:

src/compiler/resolutionCache.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,14 @@ namespace ts {
6060
watcher: FileWatcher;
6161
/** ref count keeping this directory watch alive */
6262
refCount: number;
63+
/** is the directory watched being non recursive */
64+
nonRecursive?: boolean;
6365
}
6466

6567
interface DirectoryOfFailedLookupWatch {
6668
dir: string;
6769
dirPath: Path;
70+
nonRecursive?: boolean;
6871
ignore?: true;
6972
}
7073

@@ -251,7 +254,6 @@ namespace ts {
251254
perDirectoryResolution = createMap();
252255
perDirectoryCache.set(dirPath, perDirectoryResolution);
253256
}
254-
255257
const resolvedModules: R[] = [];
256258
const compilerOptions = resolutionHost.getCompilationSettings();
257259
const hasInvalidatedNonRelativeUnresolvedImport = logChanges && isFileWithInvalidatedNonRelativeUnresolvedImports(path);
@@ -393,6 +395,7 @@ namespace ts {
393395

394396
function getDirectoryToWatchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path): DirectoryOfFailedLookupWatch {
395397
if (isInDirectoryPath(rootPath, failedLookupLocationPath)) {
398+
// Always watch root directory recursively
396399
return { dir: rootDir!, dirPath: rootPath }; // TODO: GH#18217
397400
}
398401

@@ -409,11 +412,12 @@ namespace ts {
409412
dirPath = getDirectoryPath(dirPath);
410413
}
411414

412-
// If the directory is node_modules use it to watch
415+
// If the directory is node_modules use it to watch, always watch it recursively
413416
if (isNodeModulesDirectory(dirPath)) {
414417
return filterFSRootDirectoriesToWatch({ dir, dirPath }, getDirectoryPath(dirPath));
415418
}
416419

420+
let nonRecursive = true;
417421
// Use some ancestor of the root directory
418422
let subDirectoryPath: Path | undefined, subDirectory: string | undefined;
419423
if (rootPath !== undefined) {
@@ -422,14 +426,15 @@ namespace ts {
422426
if (parentPath === dirPath) {
423427
break;
424428
}
429+
nonRecursive = false;
425430
subDirectoryPath = dirPath;
426431
subDirectory = dir;
427432
dirPath = parentPath;
428433
dir = getDirectoryPath(dir);
429434
}
430435
}
431436

432-
return filterFSRootDirectoriesToWatch({ dir: subDirectory || dir, dirPath: subDirectoryPath || dirPath }, dirPath);
437+
return filterFSRootDirectoriesToWatch({ dir: subDirectory || dir, dirPath: subDirectoryPath || dirPath, nonRecursive }, dirPath);
433438
}
434439

435440
function isPathWithDefaultFailedLookupExtension(path: Path) {
@@ -452,7 +457,7 @@ namespace ts {
452457
let setAtRoot = false;
453458
for (const failedLookupLocation of failedLookupLocations) {
454459
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
455-
const { dir, dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
460+
const { dir, dirPath, nonRecursive, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
456461
if (!ignore) {
457462
// If the failed lookup location path is not one of the supported extensions,
458463
// store it in the custom path
@@ -464,23 +469,25 @@ namespace ts {
464469
setAtRoot = true;
465470
}
466471
else {
467-
setDirectoryWatcher(dir, dirPath);
472+
setDirectoryWatcher(dir, dirPath, nonRecursive);
468473
}
469474
}
470475
}
471476

472477
if (setAtRoot) {
478+
// This is always recursive
473479
setDirectoryWatcher(rootDir!, rootPath); // TODO: GH#18217
474480
}
475481
}
476482

477-
function setDirectoryWatcher(dir: string, dirPath: Path) {
483+
function setDirectoryWatcher(dir: string, dirPath: Path, nonRecursive?: boolean) {
478484
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
479485
if (dirWatcher) {
486+
Debug.assert(!!nonRecursive === !!dirWatcher.nonRecursive);
480487
dirWatcher.refCount++;
481488
}
482489
else {
483-
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
490+
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath, nonRecursive), refCount: 1, nonRecursive });
484491
}
485492
}
486493

@@ -530,7 +537,7 @@ namespace ts {
530537
dirWatcher.refCount--;
531538
}
532539

533-
function createDirectoryWatcher(directory: string, dirPath: Path) {
540+
function createDirectoryWatcher(directory: string, dirPath: Path, nonRecursive: boolean | undefined) {
534541
return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrDirectory => {
535542
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
536543
if (cachedDirectoryStructureHost) {
@@ -541,7 +548,7 @@ namespace ts {
541548
if (!allFilesHaveInvalidatedResolution && invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) {
542549
resolutionHost.onInvalidatedResolution();
543550
}
544-
}, WatchDirectoryFlags.Recursive);
551+
}, nonRecursive ? WatchDirectoryFlags.None : WatchDirectoryFlags.Recursive);
545552
}
546553

547554
function removeResolutionsOfFileFromCache(cache: Map<Map<ResolutionWithFailedLookupLocations>>, filePath: Path) {

src/harness/fourslash.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,8 +513,17 @@ namespace FourSlash {
513513
}
514514

515515
private getAllDiagnostics(): ts.Diagnostic[] {
516-
return ts.flatMap(this.languageServiceAdapterHost.getFilenames(), fileName =>
517-
ts.isAnySupportedFileExtension(fileName) ? this.getDiagnostics(fileName) : []);
516+
return ts.flatMap(this.languageServiceAdapterHost.getFilenames(), fileName => {
517+
if (!ts.isAnySupportedFileExtension(fileName)) {
518+
return [];
519+
}
520+
521+
const baseName = ts.getBaseFileName(fileName);
522+
if (baseName === "package.json" || baseName === "tsconfig.json" || baseName === "jsconfig.json") {
523+
return [];
524+
}
525+
return this.getDiagnostics(fileName);
526+
});
518527
}
519528

520529
public verifyErrorExistsAfterMarker(markerName: string, shouldExist: boolean, after: boolean) {

src/parser/utilities.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2097,7 +2097,12 @@ namespace ts {
20972097
if (isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.EqualsToken ||
20982098
isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.EqualsToken ||
20992099
node.kind === SyntaxKind.PropertyAccessExpression && node.parent && node.parent.kind === SyntaxKind.ExpressionStatement) {
2100-
getJSDocCommentsAndTagsWorker(parent);
2100+
if (isBinaryExpression(parent)) {
2101+
getJSDocCommentsAndTagsWorker(parent.parent);
2102+
}
2103+
else {
2104+
getJSDocCommentsAndTagsWorker(parent);
2105+
}
21012106
}
21022107

21032108
// Pull parameter comments from declaring function as well
@@ -7995,7 +8000,7 @@ namespace ts {
79958000
}
79968001

79978002
export function tryGetExtensionFromPath(path: string): Extension | undefined {
7998-
return find<Extension>(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e));
8003+
return find<Extension>(extensionsToRemove, e => fileExtensionIs(path, e));
79998004
}
80008005

80018006
function getAnyExtensionFromPathWorker(path: string, extensions: string | ReadonlyArray<string>, stringEqualityComparer: (a: string, b: string) => boolean) {

src/services/codefixes/importFixes.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ namespace ts.codefix {
194194

195195
function getCodeActionForNewImport(context: SymbolContext & { preferences: UserPreferences }, { moduleSpecifier, importKind }: NewImportInfo): CodeFixAction {
196196
const { sourceFile, symbolName, preferences } = context;
197-
const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax);
198197

199198
const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier);
200199
const quotedModuleSpecifier = makeStringLiteral(moduleSpecifierWithoutQuotes, getQuotePreference(sourceFile, preferences));
@@ -210,14 +209,7 @@ namespace ts.codefix {
210209
createIdentifier(symbolName),
211210
createExternalModuleReference(quotedModuleSpecifier));
212211

213-
const changes = ChangeTracker.with(context, changeTracker => {
214-
if (lastImportDeclaration) {
215-
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl);
216-
}
217-
else {
218-
changeTracker.insertNodeAtTopOfFile(sourceFile, importDecl, /*blankLineBetween*/ true);
219-
}
220-
});
212+
const changes = ChangeTracker.with(context, t => insertImport(t, sourceFile, importDecl));
221213

222214
// if this file doesn't have any import statements, insert an import statement and then insert a new line
223215
// between the only import statement and user code. Otherwise just insert the statement because chances

src/services/getEditsForFileRename.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,16 @@ namespace ts {
103103
preferences: UserPreferences,
104104
): void {
105105
for (const sourceFile of program.getSourceFiles()) {
106-
const newImportFromPath = oldToNew(sourceFile.fileName) || sourceFile.fileName;
106+
const newFromOld = oldToNew(sourceFile.fileName);
107+
const newImportFromPath = newFromOld !== undefined ? newFromOld : sourceFile.fileName;
107108
const newImportFromDirectory = getDirectoryPath(newImportFromPath);
108109

109110
const oldFromNew: string | undefined = newToOld(sourceFile.fileName);
110111
const oldImportFromPath: string = oldFromNew || sourceFile.fileName;
111112
const oldImportFromDirectory = getDirectoryPath(oldImportFromPath);
112113

114+
const importingSourceFileMoved = newFromOld !== undefined || oldFromNew !== undefined;
115+
113116
updateImportsWorker(sourceFile, changeTracker,
114117
referenceText => {
115118
if (!pathIsRelative(referenceText)) return undefined;
@@ -123,7 +126,10 @@ namespace ts {
123126
// TODO:GH#18217
124127
? getSourceFileToImportFromResolved(resolveModuleName(importLiteral.text, oldImportFromPath, program.getCompilerOptions(), host as ModuleResolutionHost), oldToNew, program)
125128
: getSourceFileToImport(importLiteral, sourceFile, program, host, oldToNew);
126-
return toImport === undefined ? undefined : moduleSpecifiers.getModuleSpecifier(program.getCompilerOptions(), sourceFile, newImportFromPath, toImport, host, preferences);
129+
// If neither the importing source file nor the imported file moved, do nothing.
130+
return toImport === undefined || !toImport.updated && !importingSourceFileMoved
131+
? undefined
132+
: moduleSpecifiers.getModuleSpecifier(program.getCompilerOptions(), sourceFile, newImportFromPath, toImport.newFileName, host, preferences);
127133
});
128134
}
129135
}
@@ -135,12 +141,18 @@ namespace ts {
135141
return ensurePathIsNonModuleName(combineNormal(pathA, pathB));
136142
}
137143

138-
function getSourceFileToImport(importLiteral: StringLiteralLike, importingSourceFile: SourceFile, program: Program, host: LanguageServiceHost, oldToNew: PathUpdater): string | undefined {
144+
interface ToImport {
145+
readonly newFileName: string;
146+
/** True if the imported file was renamed. */
147+
readonly updated: boolean;
148+
}
149+
function getSourceFileToImport(importLiteral: StringLiteralLike, importingSourceFile: SourceFile, program: Program, host: LanguageServiceHost, oldToNew: PathUpdater): ToImport | undefined {
139150
const symbol = program.getTypeChecker().getSymbolAtLocation(importLiteral);
140151
if (symbol) {
141152
if (symbol.declarations.some(d => isAmbientModule(d))) return undefined; // No need to update if it's an ambient module
142153
const oldFileName = find(symbol.declarations, isSourceFile)!.fileName;
143-
return oldToNew(oldFileName) || oldFileName;
154+
const newFileName = oldToNew(oldFileName);
155+
return newFileName === undefined ? { newFileName: oldFileName, updated: false } : { newFileName, updated: true };
144156
}
145157
else {
146158
const resolved = host.resolveModuleNames
@@ -150,14 +162,15 @@ namespace ts {
150162
}
151163
}
152164

153-
function getSourceFileToImportFromResolved(resolved: ResolvedModuleWithFailedLookupLocations | undefined, oldToNew: PathUpdater, program: Program): string | undefined {
165+
function getSourceFileToImportFromResolved(resolved: ResolvedModuleWithFailedLookupLocations | undefined, oldToNew: PathUpdater, program: Program): ToImport | undefined {
154166
return resolved && (
155167
(resolved.resolvedModule && getIfInProgram(resolved.resolvedModule.resolvedFileName)) || firstDefined(resolved.failedLookupLocations, getIfInProgram));
156168

157-
function getIfInProgram(oldLocation: string): string | undefined {
169+
function getIfInProgram(oldLocation: string): ToImport | undefined {
158170
const newLocation = oldToNew(oldLocation);
171+
159172
return program.getSourceFile(oldLocation) || newLocation !== undefined && program.getSourceFile(newLocation)
160-
? newLocation || oldLocation
173+
? newLocation !== undefined ? { newFileName: newLocation, updated: true } : { newFileName: oldLocation, updated: false }
161174
: undefined;
162175
}
163176
}

src/services/refactors/moveToNewFile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ namespace ts.refactor {
121121
const quotePreference = getQuotePreference(oldFile, preferences);
122122
const importsFromNewFile = createOldFileImportsFromNewFile(usage.oldFileImportsFromNewFile, newModuleName, useEs6ModuleSyntax, quotePreference);
123123
if (importsFromNewFile) {
124-
changes.insertNodeBefore(oldFile, oldFile.statements[0], importsFromNewFile, /*blankLineBetween*/ true);
124+
insertImport(changes, oldFile, importsFromNewFile);
125125
}
126126

127127
deleteUnusedOldImports(oldFile, toMove.all, changes, usage.unusedImportsFromOldFile, checker);

src/services/utilities.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,17 @@ namespace ts {
13871387
export function findModifier(node: Node, kind: Modifier["kind"]): Modifier | undefined {
13881388
return node.modifiers && find(node.modifiers, m => m.kind === kind);
13891389
}
1390+
1391+
/* @internal */
1392+
export function insertImport(changes: textChanges.ChangeTracker, sourceFile: SourceFile, importDecl: Statement): void {
1393+
const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax);
1394+
if (lastImportDeclaration) {
1395+
changes.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl);
1396+
}
1397+
else {
1398+
changes.insertNodeAtTopOfFile(sourceFile, importDecl, /*blankLineBetween*/ true);
1399+
}
1400+
}
13901401
}
13911402

13921403
// Display-part writer helpers

src/testRunner/unittests/tscWatchMode.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2532,4 +2532,43 @@ declare module "fs" {
25322532
checkWatchedDirectoriesDetailed(host, [mainPackageRoot, linkedPackageRoot, `${mainPackageRoot}/node_modules/@types`, `${projectRoot}/node_modules/@types`], 1, /*recursive*/ true);
25332533
});
25342534
});
2535+
2536+
describe("tsc-watch with custom module resolution", () => {
2537+
const projectRoot = "/user/username/projects/project";
2538+
const configFileJson: any = {
2539+
compilerOptions: { module: "commonjs", resolveJsonModule: true },
2540+
files: ["index.ts"]
2541+
};
2542+
const mainFile: File = {
2543+
path: `${projectRoot}/index.ts`,
2544+
content: "import settings from './settings.json';"
2545+
};
2546+
const config: File = {
2547+
path: `${projectRoot}/tsconfig.json`,
2548+
content: JSON.stringify(configFileJson)
2549+
};
2550+
const settingsJson: File = {
2551+
path: `${projectRoot}/settings.json`,
2552+
content: JSON.stringify({ content: "Print this" })
2553+
};
2554+
2555+
it("verify that module resolution with json extension works when returned without extension", () => {
2556+
const files = [libFile, mainFile, config, settingsJson];
2557+
const host = createWatchedSystem(files, { currentDirectory: projectRoot });
2558+
const compilerHost = createWatchCompilerHostOfConfigFile(config.path, {}, host);
2559+
const parsedCommandResult = parseJsonConfigFileContent(configFileJson, host, config.path);
2560+
compilerHost.resolveModuleNames = (moduleNames, containingFile) => moduleNames.map(m => {
2561+
const result = resolveModuleName(m, containingFile, parsedCommandResult.options, compilerHost);
2562+
const resolvedModule = result.resolvedModule!;
2563+
return {
2564+
resolvedFileName: resolvedModule.resolvedFileName,
2565+
isExternalLibraryImport: resolvedModule.isExternalLibraryImport,
2566+
originalFileName: resolvedModule.originalPath,
2567+
};
2568+
});
2569+
const watch = createWatchProgram(compilerHost);
2570+
const program = watch.getCurrentProgram().getProgram();
2571+
checkProgramActualFiles(program, [mainFile.path, libFile.path, settingsJson.path]);
2572+
});
2573+
});
25352574
}

0 commit comments

Comments
 (0)