mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Fix issue with wildcard with supported extensions when plugins add external files and override getScriptKind to add script kinds for the additional extensions (#55716)
This commit is contained in:
parent
a0c51b5336
commit
3bc41784f0
@ -20,9 +20,11 @@ import {
|
||||
FileWatcherCallback,
|
||||
FileWatcherEventKind,
|
||||
find,
|
||||
getAllowJSCompilerOption,
|
||||
getBaseFileName,
|
||||
getDirectoryPath,
|
||||
getNormalizedAbsolutePath,
|
||||
getResolveJsonModule,
|
||||
hasExtension,
|
||||
identity,
|
||||
insertSorted,
|
||||
@ -44,6 +46,7 @@ import {
|
||||
removeIgnoredPath,
|
||||
returnNoopFileWatcher,
|
||||
returnTrue,
|
||||
ScriptKind,
|
||||
setSysLog,
|
||||
SortedArray,
|
||||
SortedReadonlyArray,
|
||||
@ -563,6 +566,7 @@ export interface IsIgnoredFileFromWildCardWatchingInput {
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
writeLog: (s: string) => void;
|
||||
toPath: (fileName: string) => Path;
|
||||
getScriptKind?: (fileName: string) => ScriptKind;
|
||||
}
|
||||
/** @internal */
|
||||
export function isIgnoredFileFromWildCardWatching({
|
||||
@ -577,6 +581,7 @@ export function isIgnoredFileFromWildCardWatching({
|
||||
useCaseSensitiveFileNames,
|
||||
writeLog,
|
||||
toPath,
|
||||
getScriptKind,
|
||||
}: IsIgnoredFileFromWildCardWatchingInput): boolean {
|
||||
const newPath = removeIgnoredPath(fileOrDirectoryPath);
|
||||
if (!newPath) {
|
||||
@ -588,8 +593,12 @@ export function isIgnoredFileFromWildCardWatching({
|
||||
if (fileOrDirectoryPath === watchedDirPath) return false;
|
||||
|
||||
// If the the added or created file or directory is not supported file name, ignore the file
|
||||
// But when watched directory is added/removed, we need to reload the file list
|
||||
if (hasExtension(fileOrDirectoryPath) && !isSupportedSourceFileName(fileOrDirectory, options, extraFileExtensions)) {
|
||||
if (
|
||||
hasExtension(fileOrDirectoryPath) && !(
|
||||
isSupportedSourceFileName(fileOrDirectory, options, extraFileExtensions) ||
|
||||
isSupportedScriptKind()
|
||||
)
|
||||
) {
|
||||
writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrDirectory}`);
|
||||
return true;
|
||||
}
|
||||
@ -634,6 +643,25 @@ export function isIgnoredFileFromWildCardWatching({
|
||||
builderProgram.getState().fileInfos.has(file) :
|
||||
!!find(program as readonly string[], rootFile => toPath(rootFile) === file);
|
||||
}
|
||||
|
||||
function isSupportedScriptKind() {
|
||||
if (!getScriptKind) return false;
|
||||
const scriptKind = getScriptKind(fileOrDirectory);
|
||||
switch (scriptKind) {
|
||||
case ScriptKind.TS:
|
||||
case ScriptKind.TSX:
|
||||
case ScriptKind.Deferred:
|
||||
case ScriptKind.External:
|
||||
return true;
|
||||
case ScriptKind.JS:
|
||||
case ScriptKind.JSX:
|
||||
return getAllowJSCompilerOption(options);
|
||||
case ScriptKind.JSON:
|
||||
return getResolveJsonModule(options);
|
||||
case ScriptKind.Unknown:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isBuilderProgram<T extends BuilderProgram>(program: Program | T): program is T {
|
||||
|
||||
@ -1526,6 +1526,7 @@ export class ProjectService {
|
||||
useCaseSensitiveFileNames: this.host.useCaseSensitiveFileNames,
|
||||
writeLog: s => this.logger.info(s),
|
||||
toPath: s => this.toPath(s),
|
||||
getScriptKind: configuredProjectForConfig ? (fileName => configuredProjectForConfig.getScriptKind(fileName)) : undefined,
|
||||
})
|
||||
) return;
|
||||
|
||||
|
||||
@ -663,7 +663,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
|
||||
}
|
||||
|
||||
getScriptKind(fileName: string) {
|
||||
const info = this.getOrCreateScriptInfoAndAttachToProject(fileName);
|
||||
const info = this.projectService.getScriptInfoForPath(this.toPath(fileName));
|
||||
return (info && info.scriptKind)!; // TODO: GH#18217
|
||||
}
|
||||
|
||||
|
||||
@ -218,3 +218,72 @@ describe("unittests:: tsserver:: plugins:: overriding getSupportedCodeFixes", ()
|
||||
baselineTsserverLogs("plugins", "getSupportedCodeFixes can be proxied", session);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unittests:: tsserver:: plugins:: supportedExtensions::", () => {
|
||||
it("new files with non ts extensions and wildcard matching", () => {
|
||||
const aTs: File = {
|
||||
path: "/user/username/projects/myproject/a.ts",
|
||||
content: `export const a = 10;`,
|
||||
};
|
||||
const bVue: File = {
|
||||
path: "/user/username/projects/myproject/b.vue",
|
||||
content: "bVue file",
|
||||
};
|
||||
const config: File = {
|
||||
path: "/user/username/projects/myproject/tsconfig.json",
|
||||
content: JSON.stringify(
|
||||
{
|
||||
compilerOptions: { composite: true },
|
||||
include: ["*.ts", "*.vue"],
|
||||
},
|
||||
undefined,
|
||||
" ",
|
||||
),
|
||||
};
|
||||
const host = createServerHost([aTs, bVue, config, libFile]);
|
||||
host.require = () => {
|
||||
return {
|
||||
module: () => ({
|
||||
create(info: ts.server.PluginCreateInfo) {
|
||||
const proxy = Harness.LanguageService.makeDefaultProxy(info);
|
||||
const originalScriptKind = info.languageServiceHost.getScriptKind!.bind(info.languageServiceHost);
|
||||
info.languageServiceHost.getScriptKind = fileName =>
|
||||
ts.fileExtensionIs(fileName, ".vue") ?
|
||||
ts.ScriptKind.TS :
|
||||
originalScriptKind(fileName);
|
||||
const originalGetScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
|
||||
info.languageServiceHost.getScriptSnapshot = fileName =>
|
||||
ts.fileExtensionIs(fileName, ".vue") ?
|
||||
ts.ScriptSnapshot.fromString(`export const y = "${info.languageServiceHost.readFile(fileName)}";`) :
|
||||
originalGetScriptSnapshot(fileName);
|
||||
return proxy;
|
||||
},
|
||||
getExternalFiles: (project: ts.server.Project) => {
|
||||
if (project.projectKind !== ts.server.ProjectKind.Configured) return [];
|
||||
const configFile = project.getProjectName();
|
||||
const config = ts.readJsonConfigFile(configFile, project.readFile.bind(project));
|
||||
const parseHost: ts.ParseConfigHost = {
|
||||
useCaseSensitiveFileNames: project.useCaseSensitiveFileNames(),
|
||||
fileExists: project.fileExists.bind(project),
|
||||
readFile: project.readFile.bind(project),
|
||||
readDirectory: (...args) => {
|
||||
args[1] = [".vue"];
|
||||
return project.readDirectory(...args);
|
||||
},
|
||||
};
|
||||
const parsed = ts.parseJsonSourceFileConfigFileContent(config, parseHost, project.getCurrentDirectory());
|
||||
return parsed.fileNames;
|
||||
},
|
||||
}),
|
||||
error: undefined,
|
||||
};
|
||||
};
|
||||
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host), globalPlugins: ["myplugin"] });
|
||||
openFilesForSession([aTs], session);
|
||||
|
||||
host.writeFile("/user/username/projects/myproject/c.vue", "cVue file");
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
baselineTsserverLogs("plugins", "new files with non ts extensions with wildcard matching", session);
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,185 @@
|
||||
currentDirectory:: / useCaseSensitiveFileNames: false
|
||||
Info seq [hh:mm:ss:mss] Provided types map file "/a/lib/typesMap.json" doesn't exist
|
||||
Before request
|
||||
//// [/user/username/projects/myproject/a.ts]
|
||||
export const a = 10;
|
||||
|
||||
//// [/user/username/projects/myproject/b.vue]
|
||||
bVue file
|
||||
|
||||
//// [/user/username/projects/myproject/tsconfig.json]
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"include": [
|
||||
"*.ts",
|
||||
"*.vue"
|
||||
]
|
||||
}
|
||||
|
||||
//// [/a/lib/lib.d.ts]
|
||||
/// <reference no-default-lib="true"/>
|
||||
interface Boolean {}
|
||||
interface Function {}
|
||||
interface CallableFunction {}
|
||||
interface NewableFunction {}
|
||||
interface IArguments {}
|
||||
interface Number { toExponential: any; }
|
||||
interface Object {}
|
||||
interface RegExp {}
|
||||
interface String { charAt: any; }
|
||||
interface Array<T> { length: number; [n: number]: T; }
|
||||
|
||||
|
||||
Info seq [hh:mm:ss:mss] request:
|
||||
{
|
||||
"command": "open",
|
||||
"arguments": {
|
||||
"file": "/user/username/projects/myproject/a.ts"
|
||||
},
|
||||
"seq": 1,
|
||||
"type": "request"
|
||||
}
|
||||
Info seq [hh:mm:ss:mss] Search path: /user/username/projects/myproject
|
||||
Info seq [hh:mm:ss:mss] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
|
||||
Info seq [hh:mm:ss:mss] Creating configuration project /user/username/projects/myproject/tsconfig.json
|
||||
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file
|
||||
Info seq [hh:mm:ss:mss] Config: /user/username/projects/myproject/tsconfig.json : {
|
||||
"rootNames": [
|
||||
"/user/username/projects/myproject/a.ts"
|
||||
],
|
||||
"options": {
|
||||
"composite": true,
|
||||
"configFilePath": "/user/username/projects/myproject/tsconfig.json"
|
||||
}
|
||||
}
|
||||
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 0 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory
|
||||
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 0 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory
|
||||
Info seq [hh:mm:ss:mss] Loading global plugin myplugin
|
||||
Info seq [hh:mm:ss:mss] Enabling plugin myplugin from candidate paths: /a/lib/tsc.js/../../..
|
||||
Info seq [hh:mm:ss:mss] Loading myplugin from /a/lib/tsc.js/../../.. (resolved to /a/lib/tsc.js/../../../node_modules)
|
||||
Info seq [hh:mm:ss:mss] Plugin validation succeeded
|
||||
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/b.vue 500 undefined WatchType: Closed Script info
|
||||
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json
|
||||
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined WatchType: Closed Script info
|
||||
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots
|
||||
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots
|
||||
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots
|
||||
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots
|
||||
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
|
||||
Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured)
|
||||
Info seq [hh:mm:ss:mss] Files (3)
|
||||
/a/lib/lib.d.ts Text-1 "/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }"
|
||||
/user/username/projects/myproject/a.ts SVC-1-0 "export const a = 10;"
|
||||
/user/username/projects/myproject/b.vue Text-1 "export const y = \"bVue file\";"
|
||||
|
||||
|
||||
../../../../a/lib/lib.d.ts
|
||||
Default library for target 'es5'
|
||||
a.ts
|
||||
Matched by include pattern '*.ts' in 'tsconfig.json'
|
||||
b.vue
|
||||
Matched by include pattern '*.vue' in 'tsconfig.json'
|
||||
|
||||
Info seq [hh:mm:ss:mss] -----------------------------------------------
|
||||
Info seq [hh:mm:ss:mss] Search path: /user/username/projects/myproject
|
||||
Info seq [hh:mm:ss:mss] For info: /user/username/projects/myproject/tsconfig.json :: No config files found.
|
||||
Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured)
|
||||
Info seq [hh:mm:ss:mss] Files (3)
|
||||
|
||||
Info seq [hh:mm:ss:mss] -----------------------------------------------
|
||||
Info seq [hh:mm:ss:mss] Open files:
|
||||
Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined
|
||||
Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json
|
||||
Info seq [hh:mm:ss:mss] response:
|
||||
{
|
||||
"responseRequired": false
|
||||
}
|
||||
After request
|
||||
|
||||
PolledWatches::
|
||||
/user/username/projects/myproject/node_modules/@types: *new*
|
||||
{"pollingInterval":500}
|
||||
/user/username/projects/node_modules/@types: *new*
|
||||
{"pollingInterval":500}
|
||||
|
||||
FsWatches::
|
||||
/a/lib/lib.d.ts: *new*
|
||||
{}
|
||||
/user/username/projects/myproject: *new*
|
||||
{}
|
||||
/user/username/projects/myproject/b.vue: *new*
|
||||
{}
|
||||
/user/username/projects/myproject/tsconfig.json: *new*
|
||||
{}
|
||||
|
||||
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Triggered with /user/username/projects/myproject/c.vue :: WatchInfo: /user/username/projects/myproject 0 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory
|
||||
Info seq [hh:mm:ss:mss] Scheduled: /user/username/projects/myproject/tsconfig.json
|
||||
Info seq [hh:mm:ss:mss] Scheduled: *ensureProjectForOpenFiles*
|
||||
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/c.vue :: WatchInfo: /user/username/projects/myproject 0 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory
|
||||
Before running Timeout callback:: count: 2
|
||||
1: /user/username/projects/myproject/tsconfig.json
|
||||
2: *ensureProjectForOpenFiles*
|
||||
//// [/user/username/projects/myproject/c.vue]
|
||||
cVue file
|
||||
|
||||
|
||||
Info seq [hh:mm:ss:mss] Running: /user/username/projects/myproject/tsconfig.json
|
||||
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/c.vue 500 undefined WatchType: Closed Script info
|
||||
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json
|
||||
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 2 structureChanged: true structureIsReused:: Not Elapsed:: *ms
|
||||
Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured)
|
||||
Info seq [hh:mm:ss:mss] Files (4)
|
||||
/a/lib/lib.d.ts Text-1 "/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }"
|
||||
/user/username/projects/myproject/a.ts SVC-1-0 "export const a = 10;"
|
||||
/user/username/projects/myproject/b.vue Text-1 "export const y = \"bVue file\";"
|
||||
/user/username/projects/myproject/c.vue Text-1 "export const y = \"cVue file\";"
|
||||
|
||||
|
||||
../../../../a/lib/lib.d.ts
|
||||
Default library for target 'es5'
|
||||
a.ts
|
||||
Matched by include pattern '*.ts' in 'tsconfig.json'
|
||||
b.vue
|
||||
Matched by include pattern '*.vue' in 'tsconfig.json'
|
||||
c.vue
|
||||
Matched by include pattern '*.vue' in 'tsconfig.json'
|
||||
|
||||
Info seq [hh:mm:ss:mss] -----------------------------------------------
|
||||
Info seq [hh:mm:ss:mss] Running: *ensureProjectForOpenFiles*
|
||||
Info seq [hh:mm:ss:mss] Before ensureProjectForOpenFiles:
|
||||
Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured)
|
||||
Info seq [hh:mm:ss:mss] Files (4)
|
||||
|
||||
Info seq [hh:mm:ss:mss] -----------------------------------------------
|
||||
Info seq [hh:mm:ss:mss] Open files:
|
||||
Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined
|
||||
Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json
|
||||
Info seq [hh:mm:ss:mss] After ensureProjectForOpenFiles:
|
||||
Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured)
|
||||
Info seq [hh:mm:ss:mss] Files (4)
|
||||
|
||||
Info seq [hh:mm:ss:mss] -----------------------------------------------
|
||||
Info seq [hh:mm:ss:mss] Open files:
|
||||
Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined
|
||||
Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json
|
||||
After running Timeout callback:: count: 0
|
||||
|
||||
PolledWatches::
|
||||
/user/username/projects/myproject/node_modules/@types:
|
||||
{"pollingInterval":500}
|
||||
/user/username/projects/node_modules/@types:
|
||||
{"pollingInterval":500}
|
||||
|
||||
FsWatches::
|
||||
/a/lib/lib.d.ts:
|
||||
{}
|
||||
/user/username/projects/myproject:
|
||||
{}
|
||||
/user/username/projects/myproject/b.vue:
|
||||
{}
|
||||
/user/username/projects/myproject/c.vue: *new*
|
||||
{}
|
||||
/user/username/projects/myproject/tsconfig.json:
|
||||
{}
|
||||
Loading…
x
Reference in New Issue
Block a user