feat(test runner): support tsconfig.extends array (#22975)

Fixes #22151.
This commit is contained in:
Dmitry Gozman 2023-05-11 19:18:13 -07:00 committed by GitHub
parent 59b9a39740
commit 9ffe33fae8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 18 deletions

View File

@ -39,6 +39,7 @@ interface Tsconfig {
strict?: boolean;
allowJs?: boolean;
};
references?: any[];
}
export interface TsConfigLoaderResult {
@ -123,20 +124,19 @@ export function walkForTsConfig(
function loadTsconfig(
configFilePath: string,
existsSync: (path: string) => boolean = fs.existsSync,
readFileSync: (filename: string) => string = (filename: string) =>
fs.readFileSync(filename, "utf8")
): Tsconfig | undefined {
if (!existsSync(configFilePath)) {
if (!fs.existsSync(configFilePath)) {
return undefined;
}
const configString = readFileSync(configFilePath);
const configString = fs.readFileSync(configFilePath, 'utf-8');
const cleanedJson = StripBom(configString);
let config: Tsconfig = json5.parse(cleanedJson);
let extendedConfig = config.extends;
const parsedConfig: Tsconfig = json5.parse(cleanedJson);
if (extendedConfig) {
let config: Tsconfig = {};
const extendsArray = Array.isArray(parsedConfig.extends) ? parsedConfig.extends : (parsedConfig.extends ? [parsedConfig.extends] : []);
for (let extendedConfig of extendsArray) {
if (
typeof extendedConfig === "string" &&
extendedConfig.indexOf(".json") === -1
@ -148,7 +148,7 @@ function loadTsconfig(
if (
extendedConfig.indexOf("/") !== -1 &&
extendedConfig.indexOf(".") !== -1 &&
!existsSync(extendedConfigPath)
!fs.existsSync(extendedConfigPath)
) {
extendedConfigPath = path.join(
currentDir,
@ -158,7 +158,7 @@ function loadTsconfig(
}
const base =
loadTsconfig(extendedConfigPath, existsSync, readFileSync) || {};
loadTsconfig(extendedConfigPath) || {};
// baseUrl should be interpreted as relative to the base tsconfig,
// but we need to update it so it is relative to the original tsconfig being loaded
@ -170,16 +170,14 @@ function loadTsconfig(
);
}
config = {
...base,
...config,
compilerOptions: {
...base.compilerOptions,
...config.compilerOptions,
},
};
config = mergeConfigs(config, base);
}
config = mergeConfigs(config, parsedConfig);
// The only top-level property that is excluded from inheritance is "references".
// https://www.typescriptlang.org/tsconfig#extends
config.references = parsedConfig.references;
if (path.basename(configFilePath) === 'jsconfig.json' && config.compilerOptions?.allowJs === undefined) {
config.compilerOptions = config.compilerOptions || {};
config.compilerOptions.allowJs = true;
@ -188,6 +186,17 @@ function loadTsconfig(
return config;
}
function mergeConfigs(base: Tsconfig, override: Tsconfig): Tsconfig {
return {
...base,
...override,
compilerOptions: {
...base.compilerOptions,
...override.compilerOptions,
},
};
}
function StripBom(string: string) {
if (typeof string !== 'string') {
throw new TypeError(`Expected a string, got ${typeof string}`);

View File

@ -469,3 +469,39 @@ test('should respect path resolver for JS and TS files from jsconfig.json', asyn
expect(result.passed).toBe(2);
expect(result.exitCode).toBe(0);
});
test('should support extends in tsconfig.json', async ({ runInlineTest }) => {
const result = await runInlineTest({
'tsconfig.json': `{
"extends": ["./tsconfig.base1.json", "./tsconfig.base2.json"],
}`,
'tsconfig.base1.json': `{
"extends": "./tsconfig.base.json",
}`,
'tsconfig.base2.json': `{
"compilerOptions": {
"baseUrl": "dir",
},
}`,
'tsconfig.base.json': `{
"compilerOptions": {
"paths": {
"util/*": ["./foo/bar/util/*"],
},
},
}`,
'a.test.ts': `
const { foo } = require('util/file');
import { test, expect } from '@playwright/test';
test('test', ({}, testInfo) => {
expect(foo).toBe('foo');
});
`,
'dir/foo/bar/util/file.ts': `
module.exports = { foo: 'foo' };
`,
});
expect(result.passed).toBe(1);
expect(result.exitCode).toBe(0);
});