X Tutup
Skip to content

Commit 045c0fb

Browse files
committed
Fix an issue where API Extractor would accidentally analyze a .ts file if found in the same folder as the corresponding .d.ts file (GitHub microsoft#1310)
1 parent 9ca2244 commit 045c0fb

File tree

1 file changed

+58
-1
lines changed

1 file changed

+58
-1
lines changed

apps/api-extractor/src/api/CompilerState.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,72 @@ export class CompilerState {
132132
private static _createCompilerHost(commandLine: ts.ParsedCommandLine,
133133
options: IExtractorInvokeOptions | undefined): ts.CompilerHost {
134134

135-
// Create a default CompilerHost that we can override
135+
// Create a default CompilerHost that we will override
136136
const compilerHost: ts.CompilerHost = ts.createCompilerHost(commandLine.options);
137137

138+
// Save a copy of the original members. Note that "compilerHost" cannot be the copy, because
139+
// createCompilerHost() captures that instance in a closure that is used by the members.
140+
const defaultCompilerHost: ts.CompilerHost = { ...compilerHost };
141+
138142
if (options && options.typescriptCompilerFolder) {
139143
// Prevent a closure parameter
140144
const typescriptCompilerLibFolder: string = path.join(options.typescriptCompilerFolder, 'lib');
141145
compilerHost.getDefaultLibLocation = () => typescriptCompilerLibFolder;
142146
}
143147

148+
// Used by compilerHost.fileExists()
149+
// .d.ts file path --> whether the file exists
150+
const dtsExistsCache: Map<string, boolean> = new Map<string, boolean>();
151+
152+
// Used by compilerHost.fileExists()
153+
// Example: "c:/folder/file.part.ts"
154+
const fileExtensionRegExp: RegExp = /^(.+)(\.[a-z0-9_]+)$/i;
155+
156+
compilerHost.fileExists = (fileName: string): boolean => {
157+
// In certain deprecated setups, the compiler may write its output files (.js and .d.ts)
158+
// in the same folder as the corresponding input file (.ts or .tsx). When following imports,
159+
// API Extractor wants to analyze the .d.ts file; however recent versions of the compiler engine
160+
// will instead choose the .ts file. To work around this, we hook fileExists() to hide the
161+
// existence of those files.
162+
163+
// Is "fileName" a .d.ts file? The double extension ".d.ts" needs to be matched specially.
164+
if (!ExtractorConfig.hasDtsFileExtension(fileName)) {
165+
// It's not a .d.ts file. Is the file extension a potential source file?
166+
const match: RegExpExecArray | null = fileExtensionRegExp.exec(fileName);
167+
if (match) {
168+
// Example: "c:/folder/file.part"
169+
const pathWithoutExtension: string = match[1];
170+
// Example: ".ts"
171+
const fileExtension: string = match[2];
172+
173+
switch (fileExtension.toLocaleLowerCase()) {
174+
case '.ts':
175+
case '.tsx':
176+
case '.js':
177+
case '.jsx':
178+
// Yes, this is a possible source file. Is there a corresponding .d.ts file in the same folder?
179+
const dtsFileName: string = `${pathWithoutExtension}.d.ts`;
180+
181+
let dtsFileExists: boolean | undefined = dtsExistsCache.get(dtsFileName);
182+
if (dtsFileExists === undefined) {
183+
dtsFileExists = defaultCompilerHost.fileExists!(dtsFileName);
184+
dtsExistsCache.set(dtsFileName, dtsFileExists);
185+
}
186+
187+
if (dtsFileExists) {
188+
// fileName is a potential source file and a corresponding .d.ts file exists.
189+
// Thus, API Extractor should ignore this file (so the .d.ts file will get analyzed instead).
190+
return false;
191+
}
192+
break;
193+
}
194+
}
195+
}
196+
197+
// Fall through to the default implementation
198+
return defaultCompilerHost.fileExists!(fileName);
199+
};
200+
144201
return compilerHost;
145202
}
146203
}

0 commit comments

Comments
 (0)
X Tutup