Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 5 additions & 18 deletions packages/playwright/src/common/configLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ import path from 'path';
import { isRegExp } from '@isomorphic/rtti';

import { requireOrImport, setSingleTSConfig, setTransformConfig } from '../transform/transform';
import { errorWithFile, fileIsModule } from '../util';
import { errorWithFile } from '../util';
import { FullConfigInternal } from './config';
import { configureESMLoader, configureESMLoaderTransformConfig, registerESMLoader } from './esmLoaderHost';
import { addToCompilationCache } from '../transform/compilationCache';

import type { ConfigLocation } from './config';
Expand Down Expand Up @@ -101,17 +100,8 @@ async function loadUserConfig(location: ConfigLocation): Promise<Config> {
}

export async function loadConfig(location: ConfigLocation, overrides?: ConfigCLIOverrides, ignoreProjectDependencies = false, metadata?: Config['metadata']): Promise<FullConfigInternal> {
// 0. Setup ESM loader if needed.
if (!registerESMLoader()) {
// In Node.js < 18, complain if the config file is ESM. Historically, we would restart
// the process with --loader, but now we require newer Node.js.
if (location.resolvedConfigFile && fileIsModule(location.resolvedConfigFile))
throw errorWithFile(location.resolvedConfigFile, `Playwright requires Node.js 18.19 or higher to load esm modules. Please update your version of Node.js.`);
}

// 1. Setup tsconfig; configure ESM loader with tsconfig and compilation cache.
setSingleTSConfig(overrides?.tsconfig);
await configureESMLoader();
// 1. Set the initial tsconfig before loading the config file.
await setSingleTSConfig(overrides?.tsconfig);

// 2. Load and validate playwright config.
const userConfig = await loadUserConfig(location);
Expand All @@ -129,12 +119,9 @@ export async function loadConfig(location: ConfigLocation, overrides?: ConfigCLI
const babelPlugins = (userConfig as any)['@playwright/test']?.babelPlugins || [];
const external = userConfig.build?.external || [];
const jsxImportSource = path.dirname(require.resolve('playwright'));
setTransformConfig({ babelPlugins, external, jsxImportSource });
await setTransformConfig({ babelPlugins, external, jsxImportSource });
if (!overrides?.tsconfig)
setSingleTSConfig(fullConfig?.singleTSConfigPath);

// 4. Send transform options to ESM loader.
await configureESMLoaderTransformConfig();
await setSingleTSConfig(fullConfig?.singleTSConfigPath);

return fullConfig;
}
Expand Down
93 changes: 0 additions & 93 deletions packages/playwright/src/common/esmLoaderHost.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/playwright/src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
export * as cc from '../transform/compilationCache';
export * as config from './config';
export * as configLoader from './configLoader';
export * as esm from './esmLoaderHost';
export * as fixtures from './fixtures';
export * as ipc from './ipc';
export * as poolBuilder from './poolBuilder';
Expand Down
16 changes: 5 additions & 11 deletions packages/playwright/src/common/testLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@
import path from 'path';
import util from 'util';

import * as esmLoaderHost from './esmLoaderHost';
import { isWorkerProcess, setCurrentlyLoadingFileSuite } from '../globals';
import { Suite } from './test';
import { startCollectingFileDeps, stopCollectingFileDeps } from '../transform/compilationCache';
import { requireOrImport } from '../transform/transform';
import { requireOrImport, startCollectingFileDeps, stopCollectingFileDeps } from '../transform/transform';
import { filterStackTrace } from '../util';

import type { TestError } from '../../types/testReporter';
Expand All @@ -42,10 +40,8 @@ export async function loadTestFile(file: string, config: FullConfigInternal, tes
suite._tags = [...config.config.tags];

setCurrentlyLoadingFileSuite(suite);
if (!isWorkerProcess()) {
startCollectingFileDeps();
await esmLoaderHost.startCollectingFileDeps();
}
if (!isWorkerProcess())
await startCollectingFileDeps();
try {
await requireOrImport(file);
cachedFileSuites.set(file, suite);
Expand All @@ -55,10 +51,8 @@ export async function loadTestFile(file: string, config: FullConfigInternal, tes
testErrors.push(serializeLoadError(file, e));
} finally {
setCurrentlyLoadingFileSuite(undefined);
if (!isWorkerProcess()) {
stopCollectingFileDeps(file);
await esmLoaderHost.stopCollectingFileDeps(file);
}
if (!isWorkerProcess())
await stopCollectingFileDeps(file);
}

{
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright/src/loader/loaderMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { cc, configLoader, esm, FullConfigInternal, ipc, poolBuilder, ProcessRunner, testLoader } from '../common';
import { cc, configLoader, FullConfigInternal, ipc, poolBuilder, ProcessRunner, testLoader, transform } from '../common';

import type { TestError } from '../../types/testReporter';

Expand Down Expand Up @@ -43,7 +43,7 @@ export class LoaderMain extends ProcessRunner {
}

async getCompilationCacheFromLoader() {
await esm.incorporateCompilationCache();
await transform.incorporateCompilationCache();
return cc.serializeCompilationCache();
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright/src/runner/loaderHost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

import { ProcessHost } from './processHost';
import { cc, esm, FullConfigInternal, ipc, poolBuilder, test as testNs, testLoader } from '../common';
import { cc, FullConfigInternal, ipc, poolBuilder, test as testNs, testLoader, transform } from '../common';

import type { TestError } from '../../types/testReporter';

Expand All @@ -40,7 +40,7 @@ export class InProcessLoaderHost {
}

async stop() {
await esm.incorporateCompilationCache();
await transform.incorporateCompilationCache();
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/playwright/src/transform/esmLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ function initialize(data: { port: MessagePort }) {
function createTransport(port: MessagePort) {
return new PortTransport(port, async (method, params) => {
if (method === 'setSingleTSConfig') {
setSingleTSConfig(params.tsconfig);
await setSingleTSConfig(params.tsconfig);
return;
}

if (method === 'setTransformConfig') {
setTransformConfig(params.config);
await setTransformConfig(params.config);
return;
}

Expand Down
84 changes: 84 additions & 0 deletions packages/playwright/src/transform/esmLoaderSync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* 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 fs from 'fs';
import url from 'url';

import { currentFileDepsCollector } from './compilationCache';
import { resolveHook, shouldTransform, transformHook } from './transform';
import { fileIsModule } from '../util';

export function resolve(specifier: string, context: { parentURL?: string }, nextResolve: Function) {
if (context.parentURL && context.parentURL.startsWith('file://')) {
const filename = url.fileURLToPath(context.parentURL);
const resolved = resolveHook(filename, specifier);
// Pass the resolved absolute path (not a file:// URL) to nextResolve: unlike the
// ESM-only asynchronous loader, these hooks also serve require(), whose default
// resolver rejects file:// specifiers but accepts absolute paths for both kinds.
if (resolved !== undefined)
specifier = resolved;
}
const result = nextResolve(specifier, context);
if (result?.url && result.url.startsWith('file://'))
currentFileDepsCollector()?.add(url.fileURLToPath(result.url));
return result;
}

// non-js files have undefined
// some js files have null
// {module/commonjs}-typescript are changed to {module,commonjs} because we handle typescript ourselves
// plain 'typescript' (a require()-d .ts with module kind not yet determined) maps to null,
// so the module kind is computed below via fileIsModule().
const kSupportedFormats = new Map([
['commonjs', 'commonjs'],
['module', 'module'],
['commonjs-typescript', 'commonjs'],
['module-typescript', 'module'],
['typescript', null],
[null, null],
[undefined, undefined]
]);

export function load(moduleUrl: string, context: { format?: string }, nextLoad: Function) {
// Bail out for wasm, json, etc.
if (!kSupportedFormats.has(context.format))
return nextLoad(moduleUrl, context);

// Bail for built-in modules.
if (!moduleUrl.startsWith('file://'))
return nextLoad(moduleUrl, context);

const filename = url.fileURLToPath(moduleUrl);

// Bail for node_modules.
if (!shouldTransform(filename))
return nextLoad(moduleUrl, context);

// Output format is required, so we determine it manually when unknown.
const format = kSupportedFormats.get(context.format) || (fileIsModule(filename) ? 'module' : 'commonjs');

const code = fs.readFileSync(filename, 'utf-8');
// Pass `moduleUrl` only for ESM. For CommonJS we omit it so that babel
// down-transpiles `import`/`export` to `require`/`exports`.
const transformed = transformHook(code, filename, format === 'module' ? moduleUrl : undefined);

// shortCircuit is required to designate no more loaders should be called.
return {
format,
source: transformed.code,
shortCircuit: true,
};
}
Loading
Loading