chore: respect tsconfig paths in js files (#18191)
Fixes: https://github.com/microsoft/playwright/issues/17804
This commit is contained in:
parent
af38449f42
commit
ad9729f246
|
|
@ -37,6 +37,7 @@ interface Tsconfig {
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
paths?: { [key: string]: Array<string> };
|
paths?: { [key: string]: Array<string> };
|
||||||
strict?: boolean;
|
strict?: boolean;
|
||||||
|
allowJs?: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,6 +46,7 @@ export interface TsConfigLoaderResult {
|
||||||
baseUrl: string | undefined;
|
baseUrl: string | undefined;
|
||||||
paths: { [key: string]: Array<string> } | undefined;
|
paths: { [key: string]: Array<string> } | undefined;
|
||||||
serialized: string | undefined;
|
serialized: string | undefined;
|
||||||
|
allowJs: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TsConfigLoaderParams {
|
export interface TsConfigLoaderParams {
|
||||||
|
|
@ -72,6 +74,7 @@ function loadSyncDefault(
|
||||||
baseUrl: undefined,
|
baseUrl: undefined,
|
||||||
paths: undefined,
|
paths: undefined,
|
||||||
serialized: undefined,
|
serialized: undefined,
|
||||||
|
allowJs: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const config = loadTsconfig(configPath);
|
const config = loadTsconfig(configPath);
|
||||||
|
|
@ -82,6 +85,7 @@ function loadSyncDefault(
|
||||||
(config && config.compilerOptions && config.compilerOptions.baseUrl),
|
(config && config.compilerOptions && config.compilerOptions.baseUrl),
|
||||||
paths: config && config.compilerOptions && config.compilerOptions.paths,
|
paths: config && config.compilerOptions && config.compilerOptions.paths,
|
||||||
serialized: undefined,
|
serialized: undefined,
|
||||||
|
allowJs: !!config?.compilerOptions?.allowJs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ const sourceMaps: Map<string, string> = new Map();
|
||||||
type ParsedTsConfigData = {
|
type ParsedTsConfigData = {
|
||||||
absoluteBaseUrl: string;
|
absoluteBaseUrl: string;
|
||||||
paths: { key: string, values: string[] }[];
|
paths: { key: string, values: string[] }[];
|
||||||
|
allowJs: boolean;
|
||||||
};
|
};
|
||||||
const cachedTSConfigs = new Map<string, ParsedTsConfigData | undefined>();
|
const cachedTSConfigs = new Map<string, ParsedTsConfigData | undefined>();
|
||||||
|
|
||||||
|
|
@ -73,7 +74,11 @@ function validateTsConfig(tsconfig: TsConfigLoaderResult): ParsedTsConfigData |
|
||||||
// Make 'baseUrl' absolute, because it is relative to the tsconfig.json, not to cwd.
|
// Make 'baseUrl' absolute, because it is relative to the tsconfig.json, not to cwd.
|
||||||
const absoluteBaseUrl = path.resolve(path.dirname(tsconfig.tsConfigPath), tsconfig.baseUrl);
|
const absoluteBaseUrl = path.resolve(path.dirname(tsconfig.tsConfigPath), tsconfig.baseUrl);
|
||||||
const paths = tsconfig.paths || { '*': ['*'] };
|
const paths = tsconfig.paths || { '*': ['*'] };
|
||||||
return { absoluteBaseUrl, paths: Object.entries(paths).map(([key, values]) => ({ key, values })) };
|
return {
|
||||||
|
allowJs: tsconfig.allowJs,
|
||||||
|
absoluteBaseUrl,
|
||||||
|
paths: Object.entries(paths).map(([key, values]) => ({ key, values }))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadAndValidateTsconfigForFile(file: string): ParsedTsConfigData | undefined {
|
function loadAndValidateTsconfigForFile(file: string): ParsedTsConfigData | undefined {
|
||||||
|
|
@ -95,9 +100,16 @@ const builtins = new Set(Module.builtinModules);
|
||||||
export function resolveHook(isModule: boolean, filename: string, specifier: string): string | undefined {
|
export function resolveHook(isModule: boolean, 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 (filename.startsWith(kPlaywrightInternalPrefix))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (isRelativeSpecifier(specifier))
|
||||||
|
return isModule ? js2ts(path.resolve(path.dirname(filename), specifier)) : undefined;
|
||||||
|
|
||||||
const isTypeScript = filename.endsWith('.ts') || filename.endsWith('.tsx');
|
const isTypeScript = filename.endsWith('.ts') || filename.endsWith('.tsx');
|
||||||
const tsconfig = isTypeScript ? loadAndValidateTsconfigForFile(filename) : undefined;
|
const tsconfig = loadAndValidateTsconfigForFile(filename);
|
||||||
if (tsconfig && !isRelativeSpecifier(specifier)) {
|
if (tsconfig && (isTypeScript || tsconfig.allowJs)) {
|
||||||
let longestPrefixLength = -1;
|
let longestPrefixLength = -1;
|
||||||
let pathMatchedByLongestPrefix: string | undefined;
|
let pathMatchedByLongestPrefix: string | undefined;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from './playwright-test-fixtures';
|
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||||
|
|
||||||
test('should respect path resolver', async ({ runInlineTest }) => {
|
test('should respect path resolver', async ({ runInlineTest }) => {
|
||||||
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/11656' });
|
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/11656' });
|
||||||
|
|
@ -343,3 +343,58 @@ test('should not use baseurl for relative imports when dir with same name exists
|
||||||
expect(result.output).not.toContain(`Could not`);
|
expect(result.output).not.toContain(`Could not`);
|
||||||
expect(result.output).not.toContain(`Cannot`);
|
expect(result.output).not.toContain(`Cannot`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should respect path resolver for JS files when allowJs', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'playwright.config.ts': `export default { projects: [{name: 'foo'}], };`,
|
||||||
|
'tsconfig.json': `{
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"util/*": ["./foo/bar/util/*"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}`,
|
||||||
|
'a.test.js': `
|
||||||
|
const { foo } = require('util/b');
|
||||||
|
const { test } = pwt;
|
||||||
|
test('test', ({}, testInfo) => {
|
||||||
|
expect(testInfo.project.name).toBe(foo);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'foo/bar/util/b.ts': `
|
||||||
|
module.exports = { foo: 'foo' };
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not respect path resolver for JS files w/o allowJS', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'playwright.config.ts': `export default { projects: [{name: 'foo'}], };`,
|
||||||
|
'tsconfig.json': `{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"util/*": ["./foo/bar/util/*"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}`,
|
||||||
|
'a.test.js': `
|
||||||
|
const { foo } = require('util/b');
|
||||||
|
const { test } = pwt;
|
||||||
|
test('test', ({}, testInfo) => {
|
||||||
|
expect(testInfo.project.name).toBe(foo);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'foo/bar/util/b.ts': `
|
||||||
|
module.exports = { foo: 'foo' };
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(stripAnsi(result.output)).toContain('Cannot find module \'util/b\'');
|
||||||
|
expect(result.exitCode).toBe(1);
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue