diff --git a/.changeset/flat-bobcats-hang.md b/.changeset/flat-bobcats-hang.md new file mode 100644 index 0000000..c6b0f9b --- /dev/null +++ b/.changeset/flat-bobcats-hang.md @@ -0,0 +1,5 @@ +--- +"typescript-eslint-parser-for-extra-files": patch +--- + +fix: wrong type when d.ts file exists diff --git a/package.json b/package.json index 9c45267..7e8aa8f 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "astrojs-compiler-sync": ">=0.3.1", "svelte2tsx": ">=0.5.20", "typescript": ">=4.8.4", - "vue": "^3.2.41" + "vue": "^3.2.45" }, "peerDependenciesMeta": { "astrojs-compiler-sync": { diff --git a/src/ts.ts b/src/ts.ts index 20c9b8e..21fa785 100644 --- a/src/ts.ts +++ b/src/ts.ts @@ -85,12 +85,11 @@ export class TSService { // because it targets a file that does not actually exist. this.fileWatchCallbacks.get(normalizeFileName(this.tsconfigPath))?.(); } - getFileNamesIncludingVirtualTSX( - targetPath, - this.extraFileExtensions - ).forEach((vFilePath) => { - this.fileWatchCallbacks.get(vFilePath)?.(); - }); + getRefreshTargetFileNames(targetPath, this.extraFileExtensions).forEach( + (vFilePath) => { + this.fileWatchCallbacks.get(vFilePath)?.(); + } + ); } const program = this.watch.getProgram().getProgram(); @@ -169,9 +168,18 @@ export class TSService { results.push(file.path); } - return distinctArray(...results).map((result) => - toVirtualTSXFileName(result, extraFileExtensions) - ); + return distinctArray(...results).map((result) => { + if (!isExtra(result, extraFileExtensions)) { + return result; + } + + if (original.fileExists.call(watchCompilerHost, `${result}.d.ts`)) { + // If the d.ts file exists, respect it and consider the virtual file not to exist. + return result; + } + + return toVirtualTSXFileName(result, extraFileExtensions); + }); }; watchCompilerHost.readFile = (fileName, ...args) => { const realFileName = toRealFileName(fileName, extraFileExtensions); @@ -243,11 +251,19 @@ export class TSService { // It is the file currently being parsed. return true; } - return original.fileExists.call( - watchCompilerHost, - toRealFileName(fileName, extraFileExtensions), - ...args - ); + const real = toRealFileName(fileName, extraFileExtensions); + if (original.fileExists.call(watchCompilerHost, real, ...args)) { + if (real !== fileName) { + if ( + original.fileExists.call(watchCompilerHost, `${real}.d.ts`, ...args) + ) { + // If the d.ts file exists, respect it and consider the virtual file not to exist. + return false; + } + } + return true; + } + return false; }; // It keeps a callback to mark the parsed file as changed so that it can be reparsed. @@ -292,25 +308,24 @@ export class TSService { } } -/** If the given filename is a `.vue` file, return a list of filenames containing virtual filename (.vue.tsx). */ -function getFileNamesIncludingVirtualTSX( +/** + * If the given filename is a extra extension file (.vue), + * return a list of filenames containing virtual filename (.vue.tsx) and type def filename (.vue.d.ts). + */ +function getRefreshTargetFileNames( fileName: string, extraFileExtensions: string[] ) { - for (const extraFileExtension of extraFileExtensions) { - if (fileName.endsWith(extraFileExtension)) { - return [`${fileName}.tsx`, fileName]; - } + if (isExtra(fileName, extraFileExtensions)) { + return [`${fileName}.tsx`, `${fileName}.d.ts`, fileName]; } return [fileName]; } /** If the given filename has extra file extensions, returns the real virtual filename. */ function toVirtualTSXFileName(fileName: string, extraFileExtensions: string[]) { - for (const extraFileExtension of extraFileExtensions) { - if (fileName.endsWith(extraFileExtension)) { - return `${fileName}.tsx`; - } + if (isExtra(fileName, extraFileExtensions)) { + return `${fileName}.tsx`; } return fileName; } @@ -325,6 +340,16 @@ function toRealFileName(fileName: string, extraFileExtensions: string[]) { return fileName; } +/** Checks the given filename has extra file extension or not. */ +function isExtra(fileName: string, extraFileExtensions: string[]): boolean { + for (const extraFileExtension of extraFileExtensions) { + if (fileName.endsWith(extraFileExtension)) { + return true; + } + } + return false; +} + function formatDiagnostics(diagnostics: ts.Diagnostic[]) { return ts.formatDiagnostics(diagnostics, { getCanonicalFileName: (f) => f, diff --git a/tests/fixtures/types/vue/vue-script-setup03/component.vue b/tests/fixtures/types/vue/vue-script-setup03/component.vue new file mode 100644 index 0000000..4e5667d --- /dev/null +++ b/tests/fixtures/types/vue/vue-script-setup03/component.vue @@ -0,0 +1,32 @@ + + + diff --git a/tests/fixtures/types/vue/vue-script-setup03/source.vue b/tests/fixtures/types/vue/vue-script-setup03/source.vue new file mode 100644 index 0000000..295a820 --- /dev/null +++ b/tests/fixtures/types/vue/vue-script-setup03/source.vue @@ -0,0 +1,29 @@ + + + diff --git a/tests/fixtures/types/vue/vue-script-setup03/types.vue b/tests/fixtures/types/vue/vue-script-setup03/types.vue new file mode 100644 index 0000000..bf63073 --- /dev/null +++ b/tests/fixtures/types/vue/vue-script-setup03/types.vue @@ -0,0 +1,29 @@ + + + diff --git a/tests/fixtures/types/vue/vue-script-setup04/component.vue b/tests/fixtures/types/vue/vue-script-setup04/component.vue new file mode 100644 index 0000000..4e5667d --- /dev/null +++ b/tests/fixtures/types/vue/vue-script-setup04/component.vue @@ -0,0 +1,32 @@ + + + diff --git a/tests/fixtures/types/vue/vue-script-setup04/component.vue.d.ts b/tests/fixtures/types/vue/vue-script-setup04/component.vue.d.ts new file mode 100644 index 0000000..137401d --- /dev/null +++ b/tests/fixtures/types/vue/vue-script-setup04/component.vue.d.ts @@ -0,0 +1,64 @@ +/* eslint eslint-comments/no-unlimited-disable: 0, eslint-comments/disable-enable-pair: 0 -- ignore */ +/* eslint-disable -- ignore */ +declare const _default: { + new (...args: any[]): { + $: import("vue").ComponentInternalInstance; + $data: {}; + $props: Partial<{}> & Omit> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>; + $attrs: { + [x: string]: unknown; + }; + $refs: { + [x: string]: unknown; + }; + $slots: Readonly<{ + [name: string]: import("vue").Slot | undefined; + }>; + $root: import("vue").ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, import("vue").ComponentOptionsBase, {}> | null; + $parent: import("vue").ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, import("vue").ComponentOptionsBase, {}> | null; + $emit: (event: string, ...args: any[]) => void; + $el: any; + $options: import("vue").ComponentOptionsBase>, { + exposeA: any; + exposeFn(): { + num: () => number; + } | null; + exposeFn3(): number; + }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, {}, {}, string> & { + beforeCreate?: ((() => void) | (() => void)[]) | undefined; + created?: ((() => void) | (() => void)[]) | undefined; + beforeMount?: ((() => void) | (() => void)[]) | undefined; + mounted?: ((() => void) | (() => void)[]) | undefined; + beforeUpdate?: ((() => void) | (() => void)[]) | undefined; + updated?: ((() => void) | (() => void)[]) | undefined; + activated?: ((() => void) | (() => void)[]) | undefined; + deactivated?: ((() => void) | (() => void)[]) | undefined; + beforeDestroy?: ((() => void) | (() => void)[]) | undefined; + beforeUnmount?: ((() => void) | (() => void)[]) | undefined; + destroyed?: ((() => void) | (() => void)[]) | undefined; + unmounted?: ((() => void) | (() => void)[]) | undefined; + renderTracked?: (((e: import("vue").DebuggerEvent) => void) | ((e: import("vue").DebuggerEvent) => void)[]) | undefined; + renderTriggered?: (((e: import("vue").DebuggerEvent) => void) | ((e: import("vue").DebuggerEvent) => void)[]) | undefined; + errorCaptured?: (((err: unknown, instance: import("vue").ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, import("vue").ComponentOptionsBase, {}> | null, info: string) => boolean | void) | ((err: unknown, instance: import("vue").ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, import("vue").ComponentOptionsBase, {}> | null, info: string) => boolean | void)[]) | undefined; + }; + $forceUpdate: () => void; + $nextTick: typeof import("vue").nextTick; + $watch any)>(source: T, cb: T extends (...args: any) => infer R ? (args_0: R, args_1: R) => any : (...args: any) => any, options?: import("vue").WatchOptions | undefined): import("vue").WatchStopHandle; + } & Readonly> & import("vue").ShallowUnwrapRef<{ + exposeA: any; + exposeFn(): { + num: () => number; + } | null; + exposeFn3(): number; + }> & {} & import("vue").ComponentCustomProperties & {}; + __isFragment?: undefined; + __isTeleport?: undefined; + __isSuspense?: undefined; +} & import("vue").ComponentOptionsBase>, { + exposeA: any; + exposeFn(): { + num: () => number; + } | null; + exposeFn3(): number; +}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, {}, {}, string> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps; +export default _default; diff --git a/tests/fixtures/types/vue/vue-script-setup04/source.vue b/tests/fixtures/types/vue/vue-script-setup04/source.vue new file mode 100644 index 0000000..295a820 --- /dev/null +++ b/tests/fixtures/types/vue/vue-script-setup04/source.vue @@ -0,0 +1,29 @@ + + + diff --git a/tests/fixtures/types/vue/vue-script-setup04/types.vue b/tests/fixtures/types/vue/vue-script-setup04/types.vue new file mode 100644 index 0000000..5da8781 --- /dev/null +++ b/tests/fixtures/types/vue/vue-script-setup04/types.vue @@ -0,0 +1,29 @@ + + +