fix(ts): resolve .js to .ts in non-ESM mode too (#18219)

Fixes https://github.com/microsoft/playwright/issues/18077
This commit is contained in:
Pavel Feldman 2022-10-20 15:21:22 -04:00 committed by GitHub
parent 05678c9986
commit fb9555fb5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 34 additions and 20 deletions

View file

@ -23,7 +23,7 @@ import { transformHook, resolveHook, belongsToNodeModules } from './transform';
async function resolve(specifier: string, context: { parentURL?: string }, defaultResolve: Function) { async function resolve(specifier: string, context: { parentURL?: string }, defaultResolve: Function) {
if (context.parentURL && context.parentURL.startsWith('file://')) { if (context.parentURL && context.parentURL.startsWith('file://')) {
const filename = url.fileURLToPath(context.parentURL); const filename = url.fileURLToPath(context.parentURL);
const resolved = resolveHook(true, filename, specifier); const resolved = resolveHook(filename, specifier);
if (resolved !== undefined) if (resolved !== undefined)
specifier = url.pathToFileURL(resolved).toString(); specifier = url.pathToFileURL(resolved).toString();
} }

View file

@ -97,15 +97,14 @@ const scriptPreprocessor = process.env.PW_TEST_SOURCE_TRANSFORM ?
require(process.env.PW_TEST_SOURCE_TRANSFORM) : undefined; require(process.env.PW_TEST_SOURCE_TRANSFORM) : undefined;
const builtins = new Set(Module.builtinModules); const builtins = new Set(Module.builtinModules);
export function resolveHook(isModule: boolean, filename: string, specifier: string): string | undefined { export function resolveHook(filename: string, specifier: string): string | undefined {
if (builtins.has(specifier)) if (builtins.has(specifier))
return; return;
// In real life, playwright-test is under node_modules, but in the tests it isn't. if (belongsToNodeModules(filename))
if (filename.startsWith(kPlaywrightInternalPrefix))
return; return;
if (isRelativeSpecifier(specifier)) if (isRelativeSpecifier(specifier))
return isModule ? js2ts(path.resolve(path.dirname(filename), specifier)) : undefined; return js2ts(path.resolve(path.dirname(filename), specifier));
const isTypeScript = filename.endsWith('.ts') || filename.endsWith('.tsx'); const isTypeScript = filename.endsWith('.ts') || filename.endsWith('.tsx');
const tsconfig = loadAndValidateTsconfigForFile(filename); const tsconfig = loadAndValidateTsconfigForFile(filename);
@ -138,27 +137,24 @@ export function resolveHook(isModule: boolean, filename: string, specifier: stri
matchedPartOfSpecifier = specifier; matchedPartOfSpecifier = specifier;
} }
if (keyPrefix.length <= longestPrefixLength)
continue;
for (const value of values) { for (const value of values) {
let candidate: string = value; let candidate: string = value;
if (value.includes('*')) if (value.includes('*'))
candidate = candidate.replace('*', matchedPartOfSpecifier); candidate = candidate.replace('*', matchedPartOfSpecifier);
candidate = path.resolve(tsconfig.absoluteBaseUrl, candidate.replace(/\//g, path.sep)); candidate = path.resolve(tsconfig.absoluteBaseUrl, candidate.replace(/\//g, path.sep));
if (isModule) { const ts = js2ts(candidate);
const transformed = js2ts(candidate); if (ts) {
if (transformed || fs.existsSync(candidate)) { longestPrefixLength = keyPrefix.length;
if (keyPrefix.length > longestPrefixLength) { pathMatchedByLongestPrefix = ts;
longestPrefixLength = keyPrefix.length;
pathMatchedByLongestPrefix = transformed || candidate;
}
}
} else { } else {
for (const ext of ['', '.js', '.ts', '.mjs', '.cjs', '.jsx', '.tsx', '.cjs', '.mts', '.cts']) { for (const ext of ['', '.js', '.ts', '.mjs', '.cjs', '.jsx', '.tsx', '.cjs', '.mts', '.cts']) {
if (fs.existsSync(candidate + ext)) { if (fs.existsSync(candidate + ext)) {
if (keyPrefix.length > longestPrefixLength) { longestPrefixLength = keyPrefix.length;
longestPrefixLength = keyPrefix.length; pathMatchedByLongestPrefix = candidate;
pathMatchedByLongestPrefix = candidate;
}
} }
} }
} }
@ -168,8 +164,7 @@ export function resolveHook(isModule: boolean, filename: string, specifier: stri
return pathMatchedByLongestPrefix; return pathMatchedByLongestPrefix;
} }
if (isModule) return js2ts(path.resolve(path.dirname(filename), specifier));
return js2ts(path.resolve(path.dirname(filename), specifier));
} }
export function js2ts(resolved: string): string | undefined { export function js2ts(resolved: string): string | undefined {
@ -223,7 +218,7 @@ export function installTransform(): () => void {
const originalResolveFilename = (Module as any)._resolveFilename; const originalResolveFilename = (Module as any)._resolveFilename;
function resolveFilename(this: any, specifier: string, parent: Module, ...rest: any[]) { function resolveFilename(this: any, specifier: string, parent: Module, ...rest: any[]) {
if (!reverted && parent) { if (!reverted && parent) {
const resolved = resolveHook(false, parent.filename, specifier); const resolved = resolveHook(parent.filename, specifier);
if (resolved !== undefined) if (resolved !== undefined)
specifier = resolved; specifier = resolved;
} }

View file

@ -488,3 +488,22 @@ test('should remove type imports from ts', async ({ runInlineTest }) => {
expect(result.passed).toBe(1); expect(result.passed).toBe(1);
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
}); });
test('should resolve .js import to .ts file in non-ESM mode', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
import { gimmeAOne } from './playwright-utils.js';
test('pass', ({}) => {
expect(gimmeAOne()).toBe(1);
});
`,
'playwright-utils.ts': `
export function gimmeAOne() {
return 1;
}
`,
});
expect(result.passed).toBe(1);
expect(result.exitCode).toBe(0);
});