Skip to content

fix: file was not found error when d.ts exist #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/honest-rice-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"typescript-eslint-parser-for-extra-files": patch
---

fix: file was not found error when d.ts exist
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"release": "changeset publish",
"test": "yarn mocha \"tests/src/**/*.ts\" --reporter dot --timeout 60000",
"ts": "node -r esbuild-register",
"update-fixture": "yarn mocha \"tests/src/**/*.ts\" --update --reporter dot --timeout 60000"
"update-fixture": "yarn mocha \"tests/src/**/types.ts\" --update --reporter dot --timeout 60000"
},
"peerDependencies": {
"@typescript-eslint/parser": ">=5.41.0",
Expand Down
159 changes: 127 additions & 32 deletions src/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ export class TSService {
dirMap: new Map<string, { name: string; path: string }>(),
};

private readonly fileWatchCallbacks = new Map<string, () => void>();
private readonly fileWatchCallbacks = new Map<
string,
{
setupTarget: () => void;
resetTarget: () => void;
update: () => void;
}
>();

public constructor(tsconfigPath: string, extraFileExtensions: string[]) {
this.tsconfigPath = tsconfigPath;
Expand Down Expand Up @@ -83,18 +90,28 @@ export class TSService {
if (!ts.sys.fileExists(targetPath)) {
// Signal a directory change to request a re-scan of the directory
// because it targets a file that does not actually exist.
this.fileWatchCallbacks.get(normalizeFileName(this.tsconfigPath))?.();
this.fileWatchCallbacks
.get(normalizeFileName(this.tsconfigPath))
?.update();
}
getRefreshTargetFileNames(targetPath, this.extraFileExtensions).forEach(
(vFilePath) => {
this.fileWatchCallbacks.get(vFilePath)?.();
}
);
}
getRefreshTargetFileNames(
lastTarget.filePath,
this.extraFileExtensions
).forEach((vFilePath) => {
this.fileWatchCallbacks.get(vFilePath)?.resetTarget();
});
getRefreshTargetFileNames(
this.currTarget.filePath,
this.extraFileExtensions
).forEach((vFilePath) => {
this.fileWatchCallbacks.get(vFilePath)?.setupTarget();
});

const program = this.watch.getProgram().getProgram();
// sets parent pointers in source files
program.getTypeChecker();

return program;
}

Expand Down Expand Up @@ -168,18 +185,9 @@ export class TSService {
results.push(file.path);
}

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);
});
return distinctArray(...results).map((result) =>
toVirtualTSXFileName(result, extraFileExtensions)
);
};
watchCompilerHost.readFile = (fileName, ...args) => {
const realFileName = toRealFileName(fileName, extraFileExtensions);
Expand All @@ -191,6 +199,23 @@ export class TSService {
current: true,
});
}
if (isExtraDts(fileName, extraFileExtensions)) {
const real = normalizeFileName(
extraDtsToExtra(fileName, extraFileExtensions)
);
if (this.currTarget.filePath === real) {
// If try to read the .d.ts of the target file,
// respect the target file and consider the .d.ts doesn't exist.
return undefined;
}
}
if (isVirtualTSX(fileName, extraFileExtensions)) {
const dts = toExtraDtsFileName(normalized, extraFileExtensions);
if (original.fileExists.call(watchCompilerHost, dts)) {
// If the .d.ts file exists, respect it and consider the virtual file not to exist.
return undefined;
}
}

const code = original.readFile.call(
watchCompilerHost,
Expand Down Expand Up @@ -251,11 +276,20 @@ export class TSService {
// It is the file currently being parsed.
return true;
}
const real = toRealFileName(fileName, extraFileExtensions);
if (original.fileExists.call(watchCompilerHost, real, ...args)) {
if (real !== fileName) {
if (
original.fileExists.call(
watchCompilerHost,
normalizedRealFileName,
...args
)
) {
if (isVirtualTSX(fileName, extraFileExtensions)) {
if (
original.fileExists.call(watchCompilerHost, `${real}.d.ts`, ...args)
original.fileExists.call(
watchCompilerHost,
toExtraDtsFileName(normalizedRealFileName, extraFileExtensions),
...args
)
) {
// If the d.ts file exists, respect it and consider the virtual file not to exist.
return false;
Expand All @@ -269,9 +303,30 @@ export class TSService {
// It keeps a callback to mark the parsed file as changed so that it can be reparsed.
watchCompilerHost.watchFile = (fileName, callback) => {
const normalized = normalizeFileName(fileName);
this.fileWatchCallbacks.set(normalized, () =>
callback(fileName, ts.FileWatcherEventKind.Changed)
);
this.fileWatchCallbacks.set(normalized, {
// The function is called when the file is targeted for parsing.
setupTarget: () => {
if (isExtraDts(fileName, extraFileExtensions)) {
callback(fileName, ts.FileWatcherEventKind.Deleted);
} else if (isVirtualTSX(fileName, extraFileExtensions)) {
callback(fileName, ts.FileWatcherEventKind.Created);
} else {
callback(fileName, ts.FileWatcherEventKind.Changed);
}
},
// The function is called when the file leaves the target of parsing.
resetTarget: () => {
if (isExtraDts(fileName, extraFileExtensions)) {
// If the .d.ts file exists, it will take respect.
callback(fileName, ts.FileWatcherEventKind.Created);
} else if (isVirtualTSX(fileName, extraFileExtensions)) {
callback(fileName, ts.FileWatcherEventKind.Deleted);
} else {
callback(fileName, ts.FileWatcherEventKind.Changed);
}
},
update: () => callback(fileName, ts.FileWatcherEventKind.Changed),
});

return {
close: () => {
Expand Down Expand Up @@ -322,25 +377,42 @@ function getRefreshTargetFileNames(
return [fileName];
}

/** If the given filename has extra file extensions, returns the real virtual filename. */
/** If the given filename has extra extensions, returns the real virtual filename. */
function toVirtualTSXFileName(fileName: string, extraFileExtensions: string[]) {
if (isExtra(fileName, extraFileExtensions)) {
return `${fileName}.tsx`;
}
return fileName;
}

/** If the given filename has extra extensions, returns the d.ts filename. */
function toExtraDtsFileName(fileName: string, extraFileExtensions: string[]) {
if (isExtra(fileName, extraFileExtensions)) {
return `${fileName}.d.ts`;
}
return fileName;
}

/** If the given filename is a virtual filename (.vue.tsx), returns the real filename. */
function toRealFileName(fileName: string, extraFileExtensions: string[]) {
for (const extraFileExtension of extraFileExtensions) {
if (fileName.endsWith(`${extraFileExtension}.tsx`)) {
return fileName.slice(0, -4);
}
if (isVirtualTSX(fileName, extraFileExtensions)) {
return fileName.slice(0, -4);
}
return fileName;
}

/** Checks the given filename has extra file extension or not. */
/** If the given filename is has extra extension with d.ts, returns the real filename. */
function extraDtsToExtra(
fileName: string,
extraFileExtensions: string[]
): string {
if (isExtraDts(fileName, extraFileExtensions)) {
return fileName.slice(0, -5);
}
return fileName;
}

/** Checks the given filename has extra extension or not. */
function isExtra(fileName: string, extraFileExtensions: string[]): boolean {
for (const extraFileExtension of extraFileExtensions) {
if (fileName.endsWith(extraFileExtension)) {
Expand All @@ -350,6 +422,29 @@ function isExtra(fileName: string, extraFileExtensions: string[]): boolean {
return false;
}

/** Checks the given filename is virtual file tsx or not. */
function isVirtualTSX(
fileName: string,
extraFileExtensions: string[]
): boolean {
for (const extraFileExtension of extraFileExtensions) {
if (fileName.endsWith(`${extraFileExtension}.tsx`)) {
return true;
}
}
return false;
}

/** Checks the given filename has extra extension with d.ts or not. */
function isExtraDts(fileName: string, extraFileExtensions: string[]): boolean {
for (const extraFileExtension of extraFileExtensions) {
if (fileName.endsWith(`${extraFileExtension}.d.ts`)) {
return true;
}
}
return false;
}

function formatDiagnostics(diagnostics: ts.Diagnostic[]) {
return ts.formatDiagnostics(diagnostics, {
getCanonicalFileName: (f) => f,
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/types/vue/vue-script-setup04/source.vue.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare const A = 42;
export default A;