chore: respect tsconfig paths in js files (#18191)

Fixes: https://github.com/microsoft/playwright/issues/17804
This commit is contained in:
Pavel Feldman 2022-10-19 22:38:14 -04:00 committed by GitHub
parent af38449f42
commit ad9729f246
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 75 additions and 4 deletions

View file

@ -37,6 +37,7 @@ interface Tsconfig {
baseUrl?: string;
paths?: { [key: string]: Array<string> };
strict?: boolean;
allowJs?: boolean;
};
}
@ -45,6 +46,7 @@ export interface TsConfigLoaderResult {
baseUrl: string | undefined;
paths: { [key: string]: Array<string> } | undefined;
serialized: string | undefined;
allowJs: boolean;
}
export interface TsConfigLoaderParams {
@ -72,6 +74,7 @@ function loadSyncDefault(
baseUrl: undefined,
paths: undefined,
serialized: undefined,
allowJs: false,
};
}
const config = loadTsconfig(configPath);
@ -82,6 +85,7 @@ function loadSyncDefault(
(config && config.compilerOptions && config.compilerOptions.baseUrl),
paths: config && config.compilerOptions && config.compilerOptions.paths,
serialized: undefined,
allowJs: !!config?.compilerOptions?.allowJs,
};
}

View file

@ -33,6 +33,7 @@ const sourceMaps: Map<string, string> = new Map();
type ParsedTsConfigData = {
absoluteBaseUrl: string;
paths: { key: string, values: string[] }[];
allowJs: boolean;
};
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.
const absoluteBaseUrl = path.resolve(path.dirname(tsconfig.tsConfigPath), tsconfig.baseUrl);
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 {
@ -95,9 +100,16 @@ const builtins = new Set(Module.builtinModules);
export function resolveHook(isModule: boolean, filename: string, specifier: string): string | undefined {
if (builtins.has(specifier))
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 tsconfig = isTypeScript ? loadAndValidateTsconfigForFile(filename) : undefined;
if (tsconfig && !isRelativeSpecifier(specifier)) {
const tsconfig = loadAndValidateTsconfigForFile(filename);
if (tsconfig && (isTypeScript || tsconfig.allowJs)) {
let longestPrefixLength = -1;
let pathMatchedByLongestPrefix: string | undefined;

View file

@ -14,7 +14,7 @@
* 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.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(`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);
});