fix(baseurl): support path-less baseurl (#11527)
This commit is contained in:
parent
8f0a5019c0
commit
6b3e596fd8
|
|
@ -44,6 +44,7 @@ export interface TsConfigLoaderResult {
|
||||||
tsConfigPath: string | undefined;
|
tsConfigPath: string | undefined;
|
||||||
baseUrl: string | undefined;
|
baseUrl: string | undefined;
|
||||||
paths: { [key: string]: Array<string> } | undefined;
|
paths: { [key: string]: Array<string> } | undefined;
|
||||||
|
serialized: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TsConfigLoaderParams {
|
export interface TsConfigLoaderParams {
|
||||||
|
|
@ -67,6 +68,7 @@ export function tsConfigLoader({
|
||||||
// tsconfig.loadSync handles if TS_NODE_PROJECT is a file or directory
|
// tsconfig.loadSync handles if TS_NODE_PROJECT is a file or directory
|
||||||
// and also overrides baseURL if TS_NODE_BASEURL is available.
|
// and also overrides baseURL if TS_NODE_BASEURL is available.
|
||||||
const loadResult = loadSync(cwd, TS_NODE_PROJECT, TS_NODE_BASEURL);
|
const loadResult = loadSync(cwd, TS_NODE_PROJECT, TS_NODE_BASEURL);
|
||||||
|
loadResult.serialized = JSON.stringify(loadResult);
|
||||||
return loadResult;
|
return loadResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,6 +86,7 @@ function loadSyncDefault(
|
||||||
tsConfigPath: undefined,
|
tsConfigPath: undefined,
|
||||||
baseUrl: undefined,
|
baseUrl: undefined,
|
||||||
paths: undefined,
|
paths: undefined,
|
||||||
|
serialized: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const config = loadTsconfig(configPath);
|
const config = loadTsconfig(configPath);
|
||||||
|
|
@ -94,6 +97,7 @@ function loadSyncDefault(
|
||||||
baseUrl ||
|
baseUrl ||
|
||||||
(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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import * as url from 'url';
|
||||||
import type { Location } from './types';
|
import type { Location } from './types';
|
||||||
import { TsConfigLoaderResult } from './third_party/tsconfig-loader';
|
import { TsConfigLoaderResult } from './third_party/tsconfig-loader';
|
||||||
|
|
||||||
const version = 5;
|
const version = 6;
|
||||||
const cacheDir = process.env.PWTEST_CACHE_DIR || path.join(os.tmpdir(), 'playwright-transform-cache');
|
const cacheDir = process.env.PWTEST_CACHE_DIR || path.join(os.tmpdir(), 'playwright-transform-cache');
|
||||||
const sourceMaps: Map<string, string> = new Map();
|
const sourceMaps: Map<string, string> = new Map();
|
||||||
|
|
||||||
|
|
@ -47,8 +47,14 @@ sourceMapSupport.install({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function calculateCachePath(content: string, filePath: string): string {
|
function calculateCachePath(tsconfig: TsConfigLoaderResult, content: string, filePath: string): string {
|
||||||
const hash = crypto.createHash('sha1').update(process.env.PW_EXPERIMENTAL_TS_ESM ? 'esm' : 'no_esm').update(content).update(filePath).update(String(version)).digest('hex');
|
const hash = crypto.createHash('sha1')
|
||||||
|
.update(tsconfig.serialized || '')
|
||||||
|
.update(process.env.PW_EXPERIMENTAL_TS_ESM ? 'esm' : 'no_esm')
|
||||||
|
.update(content)
|
||||||
|
.update(filePath)
|
||||||
|
.update(String(version))
|
||||||
|
.digest('hex');
|
||||||
const fileName = path.basename(filePath, path.extname(filePath)).replace(/\W/g, '') + '_' + hash;
|
const fileName = path.basename(filePath, path.extname(filePath)).replace(/\W/g, '') + '_' + hash;
|
||||||
return path.join(cacheDir, hash[0] + hash[1], fileName);
|
return path.join(cacheDir, hash[0] + hash[1], fileName);
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +62,7 @@ function calculateCachePath(content: string, filePath: string): string {
|
||||||
export function transformHook(code: string, filename: string, tsconfig: TsConfigLoaderResult, isModule = false): string {
|
export function transformHook(code: string, filename: string, tsconfig: TsConfigLoaderResult, isModule = false): string {
|
||||||
if (isComponentImport(filename))
|
if (isComponentImport(filename))
|
||||||
return componentStub();
|
return componentStub();
|
||||||
const cachePath = calculateCachePath(code, filename);
|
const cachePath = calculateCachePath(tsconfig, code, filename);
|
||||||
const codePath = cachePath + '.js';
|
const codePath = cachePath + '.js';
|
||||||
const sourceMapPath = cachePath + '.map';
|
const sourceMapPath = cachePath + '.map';
|
||||||
sourceMaps.set(filename, sourceMapPath);
|
sourceMaps.set(filename, sourceMapPath);
|
||||||
|
|
@ -67,15 +73,23 @@ export function transformHook(code: string, filename: string, tsconfig: TsConfig
|
||||||
process.env.BROWSERSLIST_IGNORE_OLD_DATA = 'true';
|
process.env.BROWSERSLIST_IGNORE_OLD_DATA = 'true';
|
||||||
const babel: typeof import('@babel/core') = require('@babel/core');
|
const babel: typeof import('@babel/core') = require('@babel/core');
|
||||||
|
|
||||||
|
const hasBaseUrl = !!tsconfig.baseUrl;
|
||||||
const extensions = ['', '.js', '.ts', '.mjs', ...(process.env.PW_COMPONENT_TESTING ? ['.tsx', '.jsx'] : [])]; const alias: { [key: string]: string | ((s: string[]) => string) } = {};
|
const extensions = ['', '.js', '.ts', '.mjs', ...(process.env.PW_COMPONENT_TESTING ? ['.tsx', '.jsx'] : [])]; const alias: { [key: string]: string | ((s: string[]) => string) } = {};
|
||||||
for (const [key, values] of Object.entries(tsconfig.paths || {})) {
|
for (const [key, values] of Object.entries(tsconfig.paths || { '*': '*' })) {
|
||||||
const regexKey = '^' + key.replace('*', '.*');
|
const regexKey = '^' + key.replace('*', '.*');
|
||||||
alias[regexKey] = ([name]) => {
|
alias[regexKey] = ([name]) => {
|
||||||
for (const value of values) {
|
for (const value of values) {
|
||||||
const relative = (key.endsWith('/*') ? value.substring(0, value.length - 1) + name.substring(key.length - 1) : value)
|
let relative: string;
|
||||||
.replace(/\//g, path.sep);
|
if (key === '*' && value === '*')
|
||||||
|
relative = name;
|
||||||
|
else if (key.endsWith('/*'))
|
||||||
|
relative = value.substring(0, value.length - 1) + name.substring(key.length - 1);
|
||||||
|
else
|
||||||
|
relative = value;
|
||||||
|
relative = relative.replace(/\//g, path.sep);
|
||||||
const result = path.resolve(tsconfig.baseUrl || '', relative);
|
const result = path.resolve(tsconfig.baseUrl || '', relative);
|
||||||
for (const extension of extensions) {
|
for (const extension of extensions) {
|
||||||
|
// TODO: We can't cover this one with the hash!
|
||||||
if (fs.existsSync(result + extension))
|
if (fs.existsSync(result + extension))
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -95,11 +109,14 @@ export function transformHook(code: string, filename: string, tsconfig: TsConfig
|
||||||
[require.resolve('@babel/plugin-syntax-async-generators')],
|
[require.resolve('@babel/plugin-syntax-async-generators')],
|
||||||
[require.resolve('@babel/plugin-syntax-object-rest-spread')],
|
[require.resolve('@babel/plugin-syntax-object-rest-spread')],
|
||||||
[require.resolve('@babel/plugin-proposal-export-namespace-from')],
|
[require.resolve('@babel/plugin-proposal-export-namespace-from')],
|
||||||
[require.resolve('babel-plugin-module-resolver'), {
|
] as any;
|
||||||
|
|
||||||
|
if (hasBaseUrl) {
|
||||||
|
plugins.push([require.resolve('babel-plugin-module-resolver'), {
|
||||||
root: ['./'],
|
root: ['./'],
|
||||||
alias
|
alias
|
||||||
}],
|
}]);
|
||||||
];
|
}
|
||||||
|
|
||||||
if (process.env.PW_COMPONENT_TESTING)
|
if (process.env.PW_COMPONENT_TESTING)
|
||||||
plugins.unshift([require.resolve('@babel/plugin-transform-react-jsx')]);
|
plugins.unshift([require.resolve('@babel/plugin-transform-react-jsx')]);
|
||||||
|
|
@ -127,7 +144,10 @@ export function transformHook(code: string, filename: string, tsconfig: TsConfig
|
||||||
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
||||||
if (result.map)
|
if (result.map)
|
||||||
fs.writeFileSync(sourceMapPath, JSON.stringify(result.map), 'utf8');
|
fs.writeFileSync(sourceMapPath, JSON.stringify(result.map), 'utf8');
|
||||||
fs.writeFileSync(codePath, result.code, 'utf8');
|
// Compiled files with base URL depend on the FS state during compilation,
|
||||||
|
// never cache them.
|
||||||
|
if (!hasBaseUrl)
|
||||||
|
fs.writeFileSync(codePath, result.code, 'utf8');
|
||||||
}
|
}
|
||||||
return result.code || '';
|
return result.code || '';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,37 @@ test('should respect baseurl', async ({ runInlineTest }) => {
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should respect baseurl w/o paths', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'playwright.config.ts': `
|
||||||
|
export default {
|
||||||
|
projects: [{name: 'foo'}],
|
||||||
|
};
|
||||||
|
`,
|
||||||
|
'tsconfig.json': `{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2019",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["esnext", "dom", "DOM.Iterable"],
|
||||||
|
"baseUrl": "./"
|
||||||
|
},
|
||||||
|
}`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { foo } from 'foo/b';
|
||||||
|
const { test } = pwt;
|
||||||
|
test('test', ({}, testInfo) => {
|
||||||
|
expect(testInfo.project.name).toBe(foo);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'foo/b.ts': `
|
||||||
|
export const foo: string = 'foo';
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
test('should respect path resolver in experimental mode', async ({ runInlineTest }) => {
|
test('should respect path resolver in experimental mode', async ({ runInlineTest }) => {
|
||||||
// We only support experimental esm mode on Node 16+
|
// We only support experimental esm mode on Node 16+
|
||||||
test.skip(parseInt(process.version.slice(1), 10) < 16);
|
test.skip(parseInt(process.version.slice(1), 10) < 16);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue