feat(tsconfig): respect baseUrl and paths from tsconfig (#10525)

This commit is contained in:
Pavel Feldman 2021-12-08 22:43:00 -08:00 committed by GitHub
parent df64c20a09
commit b5933db279
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 657 additions and 52 deletions

219
package-lock.json generated
View File

@ -2122,6 +2122,21 @@
"object.assign": "^4.1.0"
}
},
"node_modules/babel-plugin-module-resolver": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-4.1.0.tgz",
"integrity": "sha512-MlX10UDheRr3lb3P0WcaIdtCSRlxdQsB1sBqL7W0raF070bGl1HQQq5K3T2vf2XAYie+ww+5AKC/WrkjRO2knA==",
"dependencies": {
"find-babel-config": "^1.2.0",
"glob": "^7.1.6",
"pkg-up": "^3.1.0",
"reselect": "^4.0.0",
"resolve": "^1.13.1"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -4270,6 +4285,34 @@
"node": ">=0.10.0"
}
},
"node_modules/find-babel-config": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.2.0.tgz",
"integrity": "sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==",
"dependencies": {
"json5": "^0.5.1",
"path-exists": "^3.0.0"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/find-babel-config/node_modules/json5": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"bin": {
"json5": "lib/cli.js"
}
},
"node_modules/find-babel-config/node_modules/path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"engines": {
"node": ">=4"
}
},
"node_modules/find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
@ -4983,7 +5026,6 @@
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz",
"integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
},
@ -6515,7 +6557,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -6609,8 +6650,7 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/path-type": {
"version": "4.0.0",
@ -6694,6 +6734,73 @@
"node": ">=8"
}
},
"node_modules/pkg-up": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
"integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
"dependencies": {
"find-up": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/pkg-up/node_modules/find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dependencies": {
"locate-path": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/pkg-up/node_modules/locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dependencies": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/pkg-up/node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pkg-up/node_modules/p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dependencies": {
"p-limit": "^2.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/pkg-up/node_modules/path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"engines": {
"node": ">=4"
}
},
"node_modules/playwright": {
"resolved": "packages/playwright",
"link": true
@ -7199,11 +7306,15 @@
"node": ">=0.10.0"
}
},
"node_modules/reselect": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz",
"integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ=="
},
"node_modules/resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
"dev": true,
"dependencies": {
"is-core-module": "^2.2.0",
"path-parse": "^1.0.6"
@ -9036,12 +9147,14 @@
"@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
"@babel/plugin-transform-modules-commonjs": "^7.14.5",
"@babel/preset-typescript": "^7.14.5",
"babel-plugin-module-resolver": "^4.1.0",
"colors": "^1.4.0",
"commander": "^8.2.0",
"debug": "^4.1.1",
"expect": "=27.2.5",
"jest-matcher-utils": "=27.2.5",
"jpeg-js": "^0.4.2",
"json5": "^2.2.0",
"mime": "^2.4.6",
"minimatch": "^3.0.3",
"ms": "^2.1.2",
@ -9849,12 +9962,14 @@
"@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
"@babel/plugin-transform-modules-commonjs": "^7.14.5",
"@babel/preset-typescript": "^7.14.5",
"babel-plugin-module-resolver": "^4.1.0",
"colors": "^1.4.0",
"commander": "^8.2.0",
"debug": "^4.1.1",
"expect": "=27.2.5",
"jest-matcher-utils": "=27.2.5",
"jpeg-js": "^0.4.2",
"json5": "^2.2.0",
"mime": "^2.4.6",
"minimatch": "^3.0.3",
"ms": "^2.1.2",
@ -10720,6 +10835,18 @@
"object.assign": "^4.1.0"
}
},
"babel-plugin-module-resolver": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-4.1.0.tgz",
"integrity": "sha512-MlX10UDheRr3lb3P0WcaIdtCSRlxdQsB1sBqL7W0raF070bGl1HQQq5K3T2vf2XAYie+ww+5AKC/WrkjRO2knA==",
"requires": {
"find-babel-config": "^1.2.0",
"glob": "^7.1.6",
"pkg-up": "^3.1.0",
"reselect": "^4.0.0",
"resolve": "^1.13.1"
}
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -12366,6 +12493,27 @@
"to-regex-range": "^2.1.0"
}
},
"find-babel-config": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.2.0.tgz",
"integrity": "sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==",
"requires": {
"json5": "^0.5.1",
"path-exists": "^3.0.0"
},
"dependencies": {
"json5": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
}
}
},
"find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
@ -12903,7 +13051,6 @@
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz",
"integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
@ -14070,8 +14217,7 @@
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"param-case": {
"version": "3.0.4",
@ -14141,8 +14287,7 @@
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"path-type": {
"version": "4.0.0",
@ -14204,6 +14349,54 @@
"find-up": "^4.0.0"
}
},
"pkg-up": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
"integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
"requires": {
"find-up": "^3.0.0"
},
"dependencies": {
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"requires": {
"locate-path": "^3.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"requires": {
"p-limit": "^2.0.0"
}
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
}
}
},
"playwright": {
"version": "file:packages/playwright",
"requires": {
@ -14622,11 +14815,15 @@
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
},
"reselect": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz",
"integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ=="
},
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
"dev": true,
"requires": {
"is-core-module": "^2.2.0",
"path-parse": "^1.0.6"

View File

@ -44,12 +44,14 @@
"@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
"@babel/plugin-transform-modules-commonjs": "^7.14.5",
"@babel/preset-typescript": "^7.14.5",
"babel-plugin-module-resolver": "^4.1.0",
"colors": "^1.4.0",
"commander": "^8.2.0",
"debug": "^4.1.1",
"expect": "=27.2.5",
"jest-matcher-utils": "=27.2.5",
"jpeg-js": "^0.4.2",
"json5": "^2.2.0",
"mime": "^2.4.6",
"minimatch": "^3.0.3",
"ms": "^2.1.2",

View File

@ -15,8 +15,12 @@
*/
import fs from 'fs';
import path from 'path';
import { tsConfigLoader, TsConfigLoaderResult } from './third_party/tsconfig-loader';
import { transformHook } from './transform';
const tsConfigCache = new Map<string, TsConfigLoaderResult>();
async function resolve(specifier: string, context: { parentURL: string }, defaultResolve: any) {
if (specifier.endsWith('.js') || specifier.endsWith('.ts') || specifier.endsWith('.mjs'))
return defaultResolve(specifier, context, defaultResolve);
@ -32,8 +36,17 @@ async function resolve(specifier: string, context: { parentURL: string }, defaul
async function load(url: string, context: any, defaultLoad: any) {
if (url.endsWith('.ts')) {
const filename = url.substring('file://'.length);
const cwd = path.dirname(filename);
let tsconfig = tsConfigCache.get(cwd);
if (!tsconfig) {
tsconfig = tsConfigLoader({
getEnv: (name: string) => process.env[name],
cwd
});
tsConfigCache.set(cwd, tsconfig);
}
const code = fs.readFileSync(filename, 'utf-8');
const source = transformHook(code, filename, true);
const source = transformHook(code, filename, tsconfig, true);
return { format: 'module', source };
}
return defaultLoad(url, context, defaultLoad);

View File

@ -27,6 +27,7 @@ import { ProjectImpl } from './project';
import { Reporter } from '../types/testReporter';
import { BuiltInReporter, builtInReporters } from './runner';
import { isRegExp } from 'playwright-core/lib/utils/utils';
import { tsConfigLoader, TsConfigLoaderResult } from './third_party/tsconfig-loader';
export class Loader {
private _defaultConfig: Config;
@ -37,6 +38,7 @@ export class Loader {
private _projects: ProjectImpl[] = [];
private _fileSuites = new Map<string, Suite>();
private _lastModuleInfo: { rootFolder: string, isModule: boolean } | null = null;
private _tsConfigCache = new Map<string, TsConfigLoaderResult>();
constructor(defaultConfig: Config, configOverrides: Config) {
this._defaultConfig = defaultConfig;
@ -192,7 +194,18 @@ export class Loader {
private async _requireOrImport(file: string) {
const revertBabelRequire = installTransform();
// Respect tsconfig paths.
const cwd = path.dirname(file);
let tsconfig = this._tsConfigCache.get(cwd);
if (!tsconfig) {
tsconfig = tsConfigLoader({
getEnv: (name: string) => process.env[name],
cwd
});
this._tsConfigCache.set(cwd, tsconfig);
}
const revertBabelRequire = installTransform(tsconfig);
// Figure out if we are importing or requiring.
let isModule: boolean;

View File

@ -0,0 +1,209 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2016 Jonas Kello
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* eslint-disable */
import * as path from 'path';
import * as fs from 'fs';
import JSON5 from 'json5';
/**
* Typing for the parts of tsconfig that we care about
*/
export interface Tsconfig {
extends?: string;
compilerOptions?: {
baseUrl?: string;
paths?: { [key: string]: Array<string> };
strict?: boolean;
};
}
export interface TsConfigLoaderResult {
tsConfigPath: string | undefined;
baseUrl: string | undefined;
paths: { [key: string]: Array<string> } | undefined;
}
export interface TsConfigLoaderParams {
getEnv: (key: string) => string | undefined;
cwd: string;
loadSync?(
cwd: string,
filename?: string,
baseUrl?: string
): TsConfigLoaderResult;
}
export function tsConfigLoader({
getEnv,
cwd,
loadSync = loadSyncDefault,
}: TsConfigLoaderParams): TsConfigLoaderResult {
const TS_NODE_PROJECT = getEnv("TS_NODE_PROJECT");
const TS_NODE_BASEURL = getEnv("TS_NODE_BASEURL");
// tsconfig.loadSync handles if TS_NODE_PROJECT is a file or directory
// and also overrides baseURL if TS_NODE_BASEURL is available.
const loadResult = loadSync(cwd, TS_NODE_PROJECT, TS_NODE_BASEURL);
return loadResult;
}
function loadSyncDefault(
cwd: string,
filename?: string,
baseUrl?: string
): TsConfigLoaderResult {
// Tsconfig.loadSync uses path.resolve. This is why we can use an absolute path as filename
const configPath = resolveConfigPath(cwd, filename);
if (!configPath) {
return {
tsConfigPath: undefined,
baseUrl: undefined,
paths: undefined,
};
}
const config = loadTsconfig(configPath);
return {
tsConfigPath: configPath,
baseUrl:
baseUrl ||
(config && config.compilerOptions && config.compilerOptions.baseUrl),
paths: config && config.compilerOptions && config.compilerOptions.paths,
};
}
function resolveConfigPath(cwd: string, filename?: string): string | undefined {
if (filename) {
const absolutePath = fs.lstatSync(filename).isDirectory()
? path.resolve(filename, "./tsconfig.json")
: path.resolve(cwd, filename);
return absolutePath;
}
if (fs.statSync(cwd).isFile()) {
return path.resolve(cwd);
}
const configAbsolutePath = walkForTsConfig(cwd);
return configAbsolutePath ? path.resolve(configAbsolutePath) : undefined;
}
export function walkForTsConfig(
directory: string,
existsSync: (path: string) => boolean = fs.existsSync
): string | undefined {
const configPath = path.join(directory, "./tsconfig.json");
if (existsSync(configPath)) {
return configPath;
}
const parentDirectory = path.join(directory, "../");
// If we reached the top
if (directory === parentDirectory) {
return undefined;
}
return walkForTsConfig(parentDirectory, existsSync);
}
export 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)) {
return undefined;
}
const configString = readFileSync(configFilePath);
const cleanedJson = StripBom(configString);
const config: Tsconfig = JSON5.parse(cleanedJson);
let extendedConfig = config.extends;
if (extendedConfig) {
if (
typeof extendedConfig === "string" &&
extendedConfig.indexOf(".json") === -1
) {
extendedConfig += ".json";
}
const currentDir = path.dirname(configFilePath);
let extendedConfigPath = path.join(currentDir, extendedConfig);
if (
extendedConfig.indexOf("/") !== -1 &&
extendedConfig.indexOf(".") !== -1 &&
!existsSync(extendedConfigPath)
) {
extendedConfigPath = path.join(
currentDir,
"node_modules",
extendedConfig
);
}
const base =
loadTsconfig(extendedConfigPath, existsSync, readFileSync) || {};
// 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
if (base.compilerOptions && base.compilerOptions.baseUrl) {
const extendsDir = path.dirname(extendedConfig);
base.compilerOptions.baseUrl = path.join(
extendsDir,
base.compilerOptions.baseUrl
);
}
return {
...base,
...config,
compilerOptions: {
...base.compilerOptions,
...config.compilerOptions,
},
};
}
return config;
}
function StripBom(string: string) {
if (typeof string !== 'string') {
throw new TypeError(`Expected a string, got ${typeof string}`);
}
// Catches EFBBBF (UTF-8 BOM) because the buffer-to-string
// conversion translates it to FEFF (UTF-16 BOM).
if (string.charCodeAt(0) === 0xFEFF) {
return string.slice(1);
}
return string;
}

View File

@ -22,6 +22,7 @@ import * as pirates from 'pirates';
import * as sourceMapSupport from 'source-map-support';
import * as url from 'url';
import type { Location } from './types';
import { TsConfigLoaderResult } from './third_party/tsconfig-loader';
const version = 4;
const cacheDir = process.env.PWTEST_CACHE_DIR || path.join(os.tmpdir(), 'playwright-transform-cache');
@ -52,7 +53,7 @@ function calculateCachePath(content: string, filePath: string): string {
return path.join(cacheDir, hash[0] + hash[1], fileName);
}
export function transformHook(code: string, filename: string, isModule = false): string {
export function transformHook(code: string, filename: string, tsconfig: TsConfigLoaderResult, isModule = false): string {
const cachePath = calculateCachePath(code, filename);
const codePath = cachePath + '.js';
const sourceMapPath = cachePath + '.map';
@ -64,6 +65,21 @@ export function transformHook(code: string, filename: string, isModule = false):
process.env.BROWSERSLIST_IGNORE_OLD_DATA = 'true';
const babel: typeof import('@babel/core') = require('@babel/core');
const alias: { [key: string]: string | ((s: string[]) => string) } = {};
for (const [key, values] of Object.entries(tsconfig.paths || {})) {
const regexKey = '^' + key.replace('*', '.*');
alias[regexKey] = ([name]) => {
for (const value of values) {
const relative = (key.endsWith('/*') ? value.substring(0, value.length - 1) + name.substring(key.length - 1) : value)
.replace(/\//g, path.sep);
const result = path.resolve(tsconfig.baseUrl || '', relative);
if (fs.existsSync(result) || fs.existsSync(result + '.js') || fs.existsSync(result + '.ts'))
return result;
}
return name;
};
}
const plugins = [
[require.resolve('@babel/plugin-proposal-class-properties')],
[require.resolve('@babel/plugin-proposal-numeric-separator')],
@ -75,6 +91,10 @@ export function transformHook(code: string, filename: string, isModule = false):
[require.resolve('@babel/plugin-syntax-async-generators')],
[require.resolve('@babel/plugin-syntax-object-rest-spread')],
[require.resolve('@babel/plugin-proposal-export-namespace-from')],
[require.resolve('babel-plugin-module-resolver'), {
root: ['./'],
alias
}],
];
if (!isModule) {
plugins.push([require.resolve('@babel/plugin-transform-modules-commonjs')]);
@ -104,8 +124,8 @@ export function transformHook(code: string, filename: string, isModule = false):
return result.code || '';
}
export function installTransform(): () => void {
return pirates.addHook(transformHook, { exts: ['.ts'] });
export function installTransform(tsconfig: TsConfigLoaderResult): () => void {
return pirates.addHook((code: string, filename: string) => transformHook(code, filename, tsconfig), { exts: ['.ts'] });
}
export function wrapFunctionWithLocation<A extends any[], R>(func: (location: Location, ...args: A) => R): (...args: A) => R {

View File

@ -0,0 +1,147 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { test, expect } from './playwright-test-fixtures';
test('should respect path resolver', 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": ".",
"paths": {
"util/*": ["./foo/bar/util/*"],
"util2/*": ["./non-existent/*", "./foo/bar/util/*"],
"util3": ["./foo/bar/util/b"],
},
},
}`,
'a.test.ts': `
import { foo } from 'util/b';
const { test } = pwt;
test('test', ({}, testInfo) => {
expect(testInfo.project.name).toBe(foo);
});
`,
'b.test.ts': `
import { foo } from 'util2/b';
const { test } = pwt;
test('test', ({}, testInfo) => {
expect(testInfo.project.name).toBe(foo);
});
`,
'c.test.ts': `
import { foo } from 'util3';
const { test } = pwt;
test('test', ({}, testInfo) => {
expect(testInfo.project.name).toBe(foo);
});
`,
'foo/bar/util/b.ts': `
export const foo: string = 'foo';
`,
});
expect(result.passed).toBe(3);
expect(result.exitCode).toBe(0);
});
test('should respect baseurl', 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": "./foo",
"paths": {
"util/*": ["./bar/util/*"],
"util2": ["./bar/util/b"],
},
},
}`,
'a.test.ts': `
import { foo } from 'util/b';
const { test } = pwt;
test('test', ({}, testInfo) => {
expect(testInfo.project.name).toBe(foo);
});
`,
'b.test.ts': `
import { foo } from 'util2';
const { test } = pwt;
test('test', ({}, testInfo) => {
expect(testInfo.project.name).toBe(foo);
});
`,
'foo/bar/util/b.ts': `
export const foo: string = 'foo';
`,
});
expect(result.passed).toBe(2);
expect(result.exitCode).toBe(0);
});
test('should respect path resolver in experimental mode', async ({ runInlineTest }) => {
// We only support experimental esm mode on Node 16+
test.skip(parseInt(process.version.slice(1), 10) < 16);
const result = await runInlineTest({
'package.json': JSON.stringify({ type: 'module' }),
'playwright.config.ts': `
export default {
projects: [{name: 'foo'}],
};
`,
'tsconfig.json': `{
"compilerOptions": {
"target": "ES2019",
"module": "commonjs",
"lib": ["esnext", "dom", "DOM.Iterable"],
"baseUrl": ".",
"paths": {
"util/*": ["./foo/bar/util/*"],
},
},
}`,
'a.test.ts': `
import { foo } from 'util/b.ts';
const { test } = pwt;
test('check project name', ({}, testInfo) => {
expect(testInfo.project.name).toBe(foo);
});
`,
'foo/bar/util/b.ts': `
export const foo: string = 'foo';
`,
}, {}, {
PW_EXPERIMENTAL_TS_ESM: true
});
expect(result.exitCode).toBe(0);
});

View File

@ -5,7 +5,7 @@
"packages": {
"": {
"dependencies": {
"@playwright/test": "^1.17.0-next-1635886706000"
"@playwright/test": "=1.18.0-alpha-dec-9-2021"
}
},
"node_modules/@babel/code-frame": {
@ -677,9 +677,9 @@
}
},
"node_modules/@playwright/test": {
"version": "1.17.0-next-1635886706000",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.17.0-next-1635886706000.tgz",
"integrity": "sha512-shbXRTr1af9YuekWz/OAJl/3h2j8uERqzWGjMCTGzA16juBfhhAuzfc+CgRPHbEdKDaKykFmjmeeror02OvjdQ==",
"version": "1.18.0-alpha-dec-9-2021",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.18.0-alpha-dec-9-2021.tgz",
"integrity": "sha512-aIMeVxEtzqJT8mJWnImI0vjc0hPQP3eIV5yQL6WguaA/i+TfH0Fwvsm9xDVCaC/Nt4dUMt3lSM7xhQ44RJ+DXQ==",
"dependencies": {
"@babel/code-frame": "^7.14.5",
"@babel/core": "^7.14.8",
@ -704,16 +704,18 @@
"expect": "=27.2.5",
"jest-matcher-utils": "=27.2.5",
"jpeg-js": "^0.4.2",
"mime": "^2.4.6",
"minimatch": "^3.0.3",
"ms": "^2.1.2",
"open": "^8.3.0",
"pirates": "^4.0.1",
"pixelmatch": "^5.2.1",
"playwright-core": "=1.17.0-next-1635886706000",
"playwright-core": "=1.18.0-alpha-dec-9-2021",
"pngjs": "^5.0.0",
"rimraf": "^3.0.2",
"source-map-support": "^0.4.18",
"stack-utils": "^2.0.3"
"stack-utils": "^2.0.3",
"yazl": "^2.5.1"
},
"bin": {
"playwright": "cli.js"
@ -1688,9 +1690,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.17.0-next-1635886706000",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.17.0-next-1635886706000.tgz",
"integrity": "sha512-QenETXX7I7J+s5RYRtZIyW6pKKyfBYNf9XPq/exmv8jBPhhRD+0hy/NB1WwmWUt/nze8GV/ZS+2G5IN2PUXFVw==",
"version": "1.18.0-alpha-dec-9-2021",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.18.0-alpha-dec-9-2021.tgz",
"integrity": "sha512-ySI1nRgwvXzlh6alhU/ha3/NA1atBPFShSF/D/Vt6yaa7xJwFGye+/eXX+XKqhbp/zXjSImgXbgGNoYKJwuXtQ==",
"dependencies": {
"commander": "^8.2.0",
"debug": "^4.1.1",
@ -1822,9 +1824,9 @@
}
},
"node_modules/signal-exit": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz",
"integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ=="
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
"integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ=="
},
"node_modules/slash": {
"version": "3.0.0",
@ -1857,9 +1859,9 @@
}
},
"node_modules/socks-proxy-agent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.0.tgz",
"integrity": "sha512-57e7lwCN4Tzt3mXz25VxOErJKXlPfXmkMLnk310v/jwW20jWRVcgsOit+xNkN3eIEdB47GwnfAEBLacZ/wVIKg==",
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz",
"integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==",
"dependencies": {
"agent-base": "^6.0.2",
"debug": "^4.3.1",
@ -1940,9 +1942,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"node_modules/ws": {
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz",
"integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==",
"version": "7.5.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz",
"integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==",
"engines": {
"node": ">=8.3.0"
},
@ -2499,9 +2501,9 @@
}
},
"@playwright/test": {
"version": "1.17.0-next-1635886706000",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.17.0-next-1635886706000.tgz",
"integrity": "sha512-shbXRTr1af9YuekWz/OAJl/3h2j8uERqzWGjMCTGzA16juBfhhAuzfc+CgRPHbEdKDaKykFmjmeeror02OvjdQ==",
"version": "1.18.0-alpha-dec-9-2021",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.18.0-alpha-dec-9-2021.tgz",
"integrity": "sha512-aIMeVxEtzqJT8mJWnImI0vjc0hPQP3eIV5yQL6WguaA/i+TfH0Fwvsm9xDVCaC/Nt4dUMt3lSM7xhQ44RJ+DXQ==",
"requires": {
"@babel/code-frame": "^7.14.5",
"@babel/core": "^7.14.8",
@ -2526,16 +2528,18 @@
"expect": "=27.2.5",
"jest-matcher-utils": "=27.2.5",
"jpeg-js": "^0.4.2",
"mime": "^2.4.6",
"minimatch": "^3.0.3",
"ms": "^2.1.2",
"open": "^8.3.0",
"pirates": "^4.0.1",
"pixelmatch": "^5.2.1",
"playwright-core": "=1.17.0-next-1635886706000",
"playwright-core": "=1.18.0-alpha-dec-9-2021",
"pngjs": "^5.0.0",
"rimraf": "^3.0.2",
"source-map-support": "^0.4.18",
"stack-utils": "^2.0.3"
"stack-utils": "^2.0.3",
"yazl": "^2.5.1"
}
},
"@types/istanbul-lib-coverage": {
@ -3268,9 +3272,9 @@
}
},
"playwright-core": {
"version": "1.17.0-next-1635886706000",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.17.0-next-1635886706000.tgz",
"integrity": "sha512-QenETXX7I7J+s5RYRtZIyW6pKKyfBYNf9XPq/exmv8jBPhhRD+0hy/NB1WwmWUt/nze8GV/ZS+2G5IN2PUXFVw==",
"version": "1.18.0-alpha-dec-9-2021",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.18.0-alpha-dec-9-2021.tgz",
"integrity": "sha512-ySI1nRgwvXzlh6alhU/ha3/NA1atBPFShSF/D/Vt6yaa7xJwFGye+/eXX+XKqhbp/zXjSImgXbgGNoYKJwuXtQ==",
"requires": {
"commander": "^8.2.0",
"debug": "^4.1.1",
@ -3371,9 +3375,9 @@
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"signal-exit": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz",
"integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ=="
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
"integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ=="
},
"slash": {
"version": "3.0.0",
@ -3395,9 +3399,9 @@
}
},
"socks-proxy-agent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.0.tgz",
"integrity": "sha512-57e7lwCN4Tzt3mXz25VxOErJKXlPfXmkMLnk310v/jwW20jWRVcgsOit+xNkN3eIEdB47GwnfAEBLacZ/wVIKg==",
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz",
"integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==",
"requires": {
"agent-base": "^6.0.2",
"debug": "^4.3.1",
@ -3459,9 +3463,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": {
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz",
"integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==",
"version": "7.5.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz",
"integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==",
"requires": {}
},
"yauzl": {

View File

@ -1,6 +1,6 @@
{
"private": true,
"dependencies": {
"@playwright/test": "^1.17.0-next-1635886706000"
"@playwright/test": "=1.18.0-alpha-dec-9-2021"
}
}