electron/spec/esm-spec.ts

215 lines
7.5 KiB
TypeScript
Raw Normal View History

feat: I guess it's esm (#37535) * fix: allow ESM loads from within ASAR files * fix: ensure that ESM entry points finish loading before app ready * fix: allow loading ESM entrypoints via default_app * fix: allow ESM loading for renderer preloads * docs: document current known limitations of esm * chore: add patches to support blending esm handlers * refactor: use SetDefersLoading instead of JoinAppCode in renderers Blink has it's own event loop so pumping the uv loop in the renderer is not enough, luckily in blink we can suspend the loading of the frame while we do additional work. * chore: add patch to expose SetDefersLoading * fix: use fileURLToPath instead of pathname * chore: update per PR feedback * fix: fs.exists/existsSync should never throw * fix: convert path to file url before importing * fix: oops * fix: oops * Update docs/tutorial/esm-limitations.md Co-authored-by: Jeremy Rose <jeremya@chromium.org> * windows... * windows... * chore: update patches * spec: fix tests and document empty body edge case * Apply suggestions from code review Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com> Co-authored-by: Jeremy Rose <jeremya@chromium.org> * spec: add tests for esm * spec: windows * chore: update per PR feedback * chore: update patches * Update shell/common/node_bindings.h Co-authored-by: Jeremy Rose <jeremya@chromium.org> * chore: update patches * rebase * use cjs loader by default for preload scripts * chore: fix lint * chore: update patches * chore: update patches * chore: fix patches * build: debug depshash * ? * Revert "build: debug depshash" This reverts commit 0de82523fb93f475226356b37418ce4b69acdcdf. * chore: allow electron as builtin protocol in esm loader * Revert "Revert "build: debug depshash"" This reverts commit ff86b1243ca6d05c9b3b38e0a6d717fb380343a4. * chore: fix esm doc * chore: update node patches --------- Co-authored-by: Jeremy Rose <jeremya@chromium.org> Co-authored-by: electron-patch-conflict-fixer[bot] <83340002+electron-patch-conflict-fixer[bot]@users.noreply.github.com> Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com>
2023-08-31 00:38:07 +00:00
import { expect } from 'chai';
import * as cp from 'node:child_process';
import { BrowserWindow } from 'electron';
import * as fs from 'fs-extra';
import * as os from 'node:os';
import * as path from 'node:path';
import { pathToFileURL } from 'node:url';
const runFixture = async (appPath: string, args: string[] = []) => {
const result = cp.spawn(process.execPath, [appPath, ...args], {
stdio: 'pipe'
});
const stdout: Buffer[] = [];
const stderr: Buffer[] = [];
result.stdout.on('data', (chunk) => stdout.push(chunk));
result.stderr.on('data', (chunk) => stderr.push(chunk));
const [code, signal] = await new Promise<[number | null, NodeJS.Signals | null]>((resolve) => {
result.on('close', (code, signal) => {
resolve([code, signal]);
});
});
return {
code,
signal,
stdout: Buffer.concat(stdout).toString().trim(),
stderr: Buffer.concat(stderr).toString().trim()
};
};
const fixturePath = path.resolve(__dirname, 'fixtures', 'esm');
describe('esm', () => {
describe('main process', () => {
it('should load an esm entrypoint', async () => {
const result = await runFixture(path.resolve(fixturePath, 'entrypoint.mjs'));
expect(result.code).to.equal(0);
expect(result.stdout).to.equal('ESM Launch, ready: false');
});
it('should load an esm entrypoint based on type=module', async () => {
const result = await runFixture(path.resolve(fixturePath, 'package'));
expect(result.code).to.equal(0);
expect(result.stdout).to.equal('ESM Package Launch, ready: false');
});
it('should wait for a top-level await before declaring the app ready', async () => {
const result = await runFixture(path.resolve(fixturePath, 'top-level-await.mjs'));
expect(result.code).to.equal(0);
expect(result.stdout).to.equal('Top level await, ready: false');
});
it('should allow usage of pre-app-ready apis in top-level await', async () => {
const result = await runFixture(path.resolve(fixturePath, 'pre-app-ready-apis.mjs'));
expect(result.code).to.equal(0);
});
it('should allow use of dynamic import', async () => {
const result = await runFixture(path.resolve(fixturePath, 'dynamic.mjs'));
expect(result.code).to.equal(0);
expect(result.stdout).to.equal('Exit with app, ready: false');
});
});
describe('renderer process', () => {
let w: BrowserWindow | null = null;
const tempDirs: string[] = [];
afterEach(async () => {
if (w) w.close();
w = null;
while (tempDirs.length) {
await fs.remove(tempDirs.pop()!);
}
});
async function loadWindowWithPreload (preload: string, webPreferences: Electron.WebPreferences) {
const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'e-spec-preload-'));
tempDirs.push(tmpDir);
const preloadPath = path.resolve(tmpDir, 'preload.mjs');
await fs.writeFile(preloadPath, preload);
w = new BrowserWindow({
show: false,
webPreferences: {
...webPreferences,
preload: preloadPath
}
});
let error: Error | null = null;
w.webContents.on('preload-error', (_, __, err) => {
error = err;
});
await w.loadFile(path.resolve(fixturePath, 'empty.html'));
return [w.webContents, error] as [Electron.WebContents, Error | null];
}
describe('nodeIntegration', () => {
it('should support an esm entrypoint', async () => {
const [webContents] = await loadWindowWithPreload('import { resolve } from "path"; window.resolvePath = resolve;', {
nodeIntegration: true,
sandbox: false,
contextIsolation: false
});
const exposedType = await webContents.executeJavaScript('typeof window.resolvePath');
expect(exposedType).to.equal('function');
});
it('should delay load until the ESM import chain is complete', async () => {
const [webContents] = await loadWindowWithPreload(`import { resolve } from "path";
await new Promise(r => setTimeout(r, 500));
window.resolvePath = resolve;`, {
nodeIntegration: true,
sandbox: false,
contextIsolation: false
});
const exposedType = await webContents.executeJavaScript('typeof window.resolvePath');
expect(exposedType).to.equal('function');
});
it('should support a top-level await fetch blocking the page load', async () => {
const [webContents] = await loadWindowWithPreload(`
const r = await fetch("package/package.json");
window.packageJson = await r.json();`, {
nodeIntegration: true,
sandbox: false,
contextIsolation: false
});
const packageJson = await webContents.executeJavaScript('window.packageJson');
expect(packageJson).to.deep.equal(require('./fixtures/esm/package/package.json'));
});
const hostsUrl = pathToFileURL(process.platform === 'win32' ? 'C:\\Windows\\System32\\drivers\\etc\\hosts' : '/etc/hosts');
describe('without context isolation', () => {
it('should use Blinks dynamic loader in the main world', async () => {
feat: I guess it's esm (#37535) * fix: allow ESM loads from within ASAR files * fix: ensure that ESM entry points finish loading before app ready * fix: allow loading ESM entrypoints via default_app * fix: allow ESM loading for renderer preloads * docs: document current known limitations of esm * chore: add patches to support blending esm handlers * refactor: use SetDefersLoading instead of JoinAppCode in renderers Blink has it's own event loop so pumping the uv loop in the renderer is not enough, luckily in blink we can suspend the loading of the frame while we do additional work. * chore: add patch to expose SetDefersLoading * fix: use fileURLToPath instead of pathname * chore: update per PR feedback * fix: fs.exists/existsSync should never throw * fix: convert path to file url before importing * fix: oops * fix: oops * Update docs/tutorial/esm-limitations.md Co-authored-by: Jeremy Rose <jeremya@chromium.org> * windows... * windows... * chore: update patches * spec: fix tests and document empty body edge case * Apply suggestions from code review Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com> Co-authored-by: Jeremy Rose <jeremya@chromium.org> * spec: add tests for esm * spec: windows * chore: update per PR feedback * chore: update patches * Update shell/common/node_bindings.h Co-authored-by: Jeremy Rose <jeremya@chromium.org> * chore: update patches * rebase * use cjs loader by default for preload scripts * chore: fix lint * chore: update patches * chore: update patches * chore: fix patches * build: debug depshash * ? * Revert "build: debug depshash" This reverts commit 0de82523fb93f475226356b37418ce4b69acdcdf. * chore: allow electron as builtin protocol in esm loader * Revert "Revert "build: debug depshash"" This reverts commit ff86b1243ca6d05c9b3b38e0a6d717fb380343a4. * chore: fix esm doc * chore: update node patches --------- Co-authored-by: Jeremy Rose <jeremya@chromium.org> Co-authored-by: electron-patch-conflict-fixer[bot] <83340002+electron-patch-conflict-fixer[bot]@users.noreply.github.com> Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com>
2023-08-31 00:38:07 +00:00
const [webContents] = await loadWindowWithPreload('', {
nodeIntegration: true,
sandbox: false,
contextIsolation: false
});
let error: Error | null = null;
try {
await webContents.executeJavaScript(`import(${JSON.stringify(hostsUrl)})`);
} catch (err) {
error = err as Error;
}
expect(error).to.not.equal(null);
// This is a blink specific error message
expect(error?.message).to.include('Failed to fetch dynamically imported module');
});
it('should use import.meta callback handling from Node.js for Node.js modules', async () => {
const result = await runFixture(path.resolve(fixturePath, 'import-meta'));
expect(result.code).to.equal(0);
});
feat: I guess it's esm (#37535) * fix: allow ESM loads from within ASAR files * fix: ensure that ESM entry points finish loading before app ready * fix: allow loading ESM entrypoints via default_app * fix: allow ESM loading for renderer preloads * docs: document current known limitations of esm * chore: add patches to support blending esm handlers * refactor: use SetDefersLoading instead of JoinAppCode in renderers Blink has it's own event loop so pumping the uv loop in the renderer is not enough, luckily in blink we can suspend the loading of the frame while we do additional work. * chore: add patch to expose SetDefersLoading * fix: use fileURLToPath instead of pathname * chore: update per PR feedback * fix: fs.exists/existsSync should never throw * fix: convert path to file url before importing * fix: oops * fix: oops * Update docs/tutorial/esm-limitations.md Co-authored-by: Jeremy Rose <jeremya@chromium.org> * windows... * windows... * chore: update patches * spec: fix tests and document empty body edge case * Apply suggestions from code review Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com> Co-authored-by: Jeremy Rose <jeremya@chromium.org> * spec: add tests for esm * spec: windows * chore: update per PR feedback * chore: update patches * Update shell/common/node_bindings.h Co-authored-by: Jeremy Rose <jeremya@chromium.org> * chore: update patches * rebase * use cjs loader by default for preload scripts * chore: fix lint * chore: update patches * chore: update patches * chore: fix patches * build: debug depshash * ? * Revert "build: debug depshash" This reverts commit 0de82523fb93f475226356b37418ce4b69acdcdf. * chore: allow electron as builtin protocol in esm loader * Revert "Revert "build: debug depshash"" This reverts commit ff86b1243ca6d05c9b3b38e0a6d717fb380343a4. * chore: fix esm doc * chore: update node patches --------- Co-authored-by: Jeremy Rose <jeremya@chromium.org> Co-authored-by: electron-patch-conflict-fixer[bot] <83340002+electron-patch-conflict-fixer[bot]@users.noreply.github.com> Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com>
2023-08-31 00:38:07 +00:00
});
describe('with context isolation', () => {
chore: bump node to v20.10.0 (main) (#40675) * chore: bump node in DEPS to v20.10.0 * chore: update feat_initialize_asar_support.patch no code changes; patch just needed an update due to nearby upstream changes Xref: https://github.com/nodejs/node/pull/49986 * chore: update pass_all_globals_through_require.patch no manual changes; patch applied with fuzz Xref: https://github.com/nodejs/node/pull/49657 * chore: update refactor_allow_embedder_overriding_of_internal_fs_calls Xref: https://github.com/nodejs/node/pull/49912 no code changes; patch just needed an update due to nearby upstream changes * chore: update chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch Xref: https://github.com/nodejs/node/pull/49986 minor manual changes needed to sync with upstream change * update fix_expose_the_built-in_electron_module_via_the_esm_loader.patch Xref: https://github.com/nodejs/node/pull/50096 Xref: https://github.com/nodejs/node/pull/50314 in lib/internal/modules/esm/load.js, update the code that checks for `format === 'electron'`. I'd like 👀 on this Xref: https://github.com/nodejs/node/pull/49657 add braces in lib/internal/modules/esm/translators.js to sync with upstream * fix: lazyload fs in esm loaders to apply asar patches * https://github.com/nodejs/node/pull/50127 * https://github.com/nodejs/node/pull/50096 * esm: jsdoc for modules code https://github.com/nodejs/node/pull/49523 * test: set test-cli-node-options as flaky https://github.com/nodejs/node/pull/50296 * deps: update c-ares to 1.20.1 https://github.com/nodejs/node/pull/50082 * esm: bypass CommonJS loader under --default-type=module https://github.com/nodejs/node/pull/49986 * deps: update uvwasi to 0.0.19 https://github.com/nodejs/node/pull/49908 * lib,test: do not hardcode Buffer.kMaxLength https://github.com/nodejs/node/pull/49876 * crypto: account for disabled SharedArrayBuffer https://github.com/nodejs/node/pull/50034 * test: fix edge snapshot stack traces https://github.com/nodejs/node/pull/49659 * src: generate snapshot with --predictable https://github.com/nodejs/node/pull/48749 * chore: fixup patch indices * fs: throw errors from sync branches instead of separate implementations https://github.com/nodejs/node/pull/49913 * crypto: ensure valid point on elliptic curve in SubtleCrypto.importKey https://github.com/nodejs/node/pull/50234 * esm: detect ESM syntax in ambiguous JavaScrip https://github.com/nodejs/node/pull/50096 * fixup! test: fix edge snapshot stack traces * esm: unflag extensionless ES module JavaScript and Wasm in module scope https://github.com/nodejs/node/pull/49974 * [tagged-ptr] Arrowify objects https://chromium-review.googlesource.com/c/v8/v8/+/4705331 --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr <charles@charleskerr.com> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-12-11 20:09:50 +00:00
let badFilePath = '';
beforeEach(async () => {
badFilePath = path.resolve(path.resolve(os.tmpdir(), 'bad-file.badjs'));
await fs.promises.writeFile(badFilePath, 'const foo = "bar";');
});
afterEach(async () => {
await fs.promises.unlink(badFilePath);
});
it('should use Node.js ESM dynamic loader in the isolated context', async () => {
chore: bump node to v20.10.0 (main) (#40675) * chore: bump node in DEPS to v20.10.0 * chore: update feat_initialize_asar_support.patch no code changes; patch just needed an update due to nearby upstream changes Xref: https://github.com/nodejs/node/pull/49986 * chore: update pass_all_globals_through_require.patch no manual changes; patch applied with fuzz Xref: https://github.com/nodejs/node/pull/49657 * chore: update refactor_allow_embedder_overriding_of_internal_fs_calls Xref: https://github.com/nodejs/node/pull/49912 no code changes; patch just needed an update due to nearby upstream changes * chore: update chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch Xref: https://github.com/nodejs/node/pull/49986 minor manual changes needed to sync with upstream change * update fix_expose_the_built-in_electron_module_via_the_esm_loader.patch Xref: https://github.com/nodejs/node/pull/50096 Xref: https://github.com/nodejs/node/pull/50314 in lib/internal/modules/esm/load.js, update the code that checks for `format === 'electron'`. I'd like 👀 on this Xref: https://github.com/nodejs/node/pull/49657 add braces in lib/internal/modules/esm/translators.js to sync with upstream * fix: lazyload fs in esm loaders to apply asar patches * https://github.com/nodejs/node/pull/50127 * https://github.com/nodejs/node/pull/50096 * esm: jsdoc for modules code https://github.com/nodejs/node/pull/49523 * test: set test-cli-node-options as flaky https://github.com/nodejs/node/pull/50296 * deps: update c-ares to 1.20.1 https://github.com/nodejs/node/pull/50082 * esm: bypass CommonJS loader under --default-type=module https://github.com/nodejs/node/pull/49986 * deps: update uvwasi to 0.0.19 https://github.com/nodejs/node/pull/49908 * lib,test: do not hardcode Buffer.kMaxLength https://github.com/nodejs/node/pull/49876 * crypto: account for disabled SharedArrayBuffer https://github.com/nodejs/node/pull/50034 * test: fix edge snapshot stack traces https://github.com/nodejs/node/pull/49659 * src: generate snapshot with --predictable https://github.com/nodejs/node/pull/48749 * chore: fixup patch indices * fs: throw errors from sync branches instead of separate implementations https://github.com/nodejs/node/pull/49913 * crypto: ensure valid point on elliptic curve in SubtleCrypto.importKey https://github.com/nodejs/node/pull/50234 * esm: detect ESM syntax in ambiguous JavaScrip https://github.com/nodejs/node/pull/50096 * fixup! test: fix edge snapshot stack traces * esm: unflag extensionless ES module JavaScript and Wasm in module scope https://github.com/nodejs/node/pull/49974 * [tagged-ptr] Arrowify objects https://chromium-review.googlesource.com/c/v8/v8/+/4705331 --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr <charles@charleskerr.com> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-12-11 20:09:50 +00:00
const [, preloadError] = await loadWindowWithPreload(`await import(${JSON.stringify((pathToFileURL(badFilePath)))})`, {
feat: I guess it's esm (#37535) * fix: allow ESM loads from within ASAR files * fix: ensure that ESM entry points finish loading before app ready * fix: allow loading ESM entrypoints via default_app * fix: allow ESM loading for renderer preloads * docs: document current known limitations of esm * chore: add patches to support blending esm handlers * refactor: use SetDefersLoading instead of JoinAppCode in renderers Blink has it's own event loop so pumping the uv loop in the renderer is not enough, luckily in blink we can suspend the loading of the frame while we do additional work. * chore: add patch to expose SetDefersLoading * fix: use fileURLToPath instead of pathname * chore: update per PR feedback * fix: fs.exists/existsSync should never throw * fix: convert path to file url before importing * fix: oops * fix: oops * Update docs/tutorial/esm-limitations.md Co-authored-by: Jeremy Rose <jeremya@chromium.org> * windows... * windows... * chore: update patches * spec: fix tests and document empty body edge case * Apply suggestions from code review Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com> Co-authored-by: Jeremy Rose <jeremya@chromium.org> * spec: add tests for esm * spec: windows * chore: update per PR feedback * chore: update patches * Update shell/common/node_bindings.h Co-authored-by: Jeremy Rose <jeremya@chromium.org> * chore: update patches * rebase * use cjs loader by default for preload scripts * chore: fix lint * chore: update patches * chore: update patches * chore: fix patches * build: debug depshash * ? * Revert "build: debug depshash" This reverts commit 0de82523fb93f475226356b37418ce4b69acdcdf. * chore: allow electron as builtin protocol in esm loader * Revert "Revert "build: debug depshash"" This reverts commit ff86b1243ca6d05c9b3b38e0a6d717fb380343a4. * chore: fix esm doc * chore: update node patches --------- Co-authored-by: Jeremy Rose <jeremya@chromium.org> Co-authored-by: electron-patch-conflict-fixer[bot] <83340002+electron-patch-conflict-fixer[bot]@users.noreply.github.com> Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com>
2023-08-31 00:38:07 +00:00
nodeIntegration: true,
sandbox: false,
contextIsolation: true
});
expect(preloadError).to.not.equal(null);
// This is a node.js specific error message
expect(preloadError!.toString()).to.include('Unknown file extension');
});
it('should use Blinks dynamic loader in the main world', async () => {
feat: I guess it's esm (#37535) * fix: allow ESM loads from within ASAR files * fix: ensure that ESM entry points finish loading before app ready * fix: allow loading ESM entrypoints via default_app * fix: allow ESM loading for renderer preloads * docs: document current known limitations of esm * chore: add patches to support blending esm handlers * refactor: use SetDefersLoading instead of JoinAppCode in renderers Blink has it's own event loop so pumping the uv loop in the renderer is not enough, luckily in blink we can suspend the loading of the frame while we do additional work. * chore: add patch to expose SetDefersLoading * fix: use fileURLToPath instead of pathname * chore: update per PR feedback * fix: fs.exists/existsSync should never throw * fix: convert path to file url before importing * fix: oops * fix: oops * Update docs/tutorial/esm-limitations.md Co-authored-by: Jeremy Rose <jeremya@chromium.org> * windows... * windows... * chore: update patches * spec: fix tests and document empty body edge case * Apply suggestions from code review Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com> Co-authored-by: Jeremy Rose <jeremya@chromium.org> * spec: add tests for esm * spec: windows * chore: update per PR feedback * chore: update patches * Update shell/common/node_bindings.h Co-authored-by: Jeremy Rose <jeremya@chromium.org> * chore: update patches * rebase * use cjs loader by default for preload scripts * chore: fix lint * chore: update patches * chore: update patches * chore: fix patches * build: debug depshash * ? * Revert "build: debug depshash" This reverts commit 0de82523fb93f475226356b37418ce4b69acdcdf. * chore: allow electron as builtin protocol in esm loader * Revert "Revert "build: debug depshash"" This reverts commit ff86b1243ca6d05c9b3b38e0a6d717fb380343a4. * chore: fix esm doc * chore: update node patches --------- Co-authored-by: Jeremy Rose <jeremya@chromium.org> Co-authored-by: electron-patch-conflict-fixer[bot] <83340002+electron-patch-conflict-fixer[bot]@users.noreply.github.com> Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com>
2023-08-31 00:38:07 +00:00
const [webContents] = await loadWindowWithPreload('', {
nodeIntegration: true,
sandbox: false,
contextIsolation: true
});
let error: Error | null = null;
try {
await webContents.executeJavaScript(`import(${JSON.stringify(hostsUrl)})`);
} catch (err) {
error = err as Error;
}
expect(error).to.not.equal(null);
// This is a blink specific error message
expect(error?.message).to.include('Failed to fetch dynamically imported module');
});
});
});
});
});