Skip to content
Merged
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
2 changes: 1 addition & 1 deletion packages/angular/build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"xxhash-wasm": "1.1.0"
},
"optionalDependencies": {
"@oxc-parser/binding-wasm32-wasi": "0.150.0",
"@oxc-parser/binding-wasm32-wasi": "0.156.0",
"lmdb": "3.5.6"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/

import '../oxc/oxc-wasi-shim';
import { type DecodedSourceMap, type SourceMapInput } from '@ampproject/remapping';
import {
type ɵParsedTranslation,
Expand Down
1 change: 1 addition & 0 deletions packages/angular/build/src/tools/oxc/oxc-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/

import './oxc-wasi-shim';
import type { DecodedSourceMap } from '@ampproject/remapping';
import { needsLinking } from '@angular/compiler-cli/linker';
import type { BindingIdentifier, Class, Node } from '@oxc-project/types';
Expand Down
81 changes: 81 additions & 0 deletions packages/angular/build/src/tools/oxc/oxc-wasi-shim.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

import { Module, createRequire } from 'node:module';

// Workaround for an upstream incompatibility in `@oxc-parser/binding-wasm32-wasi` when
// falling back to WebAssembly (e.g., inside WebContainers or StackBlitz).
//
// `@oxc-parser/binding-wasm32-wasi` declares a dependency on `@emnapi/core@2.0.0-alpha.6`,
// but depends on `@napi-rs/wasm-runtime@~1.2.6` which expects `@emnapi/core@^1.7.1 || 2.0.0-beta.2`.
// 1. In `@napi-rs/wasm-runtime@1.2.6`, the runtime attempts to assign `initWorker` to
// `emnapi_shared.addonModule`, which is undefined in `alpha.6` where the property was named
// `napiModule`. Aliasing `addonModule` to `this.napiModule` on `Object.prototype` allows the
// WASI runtime to initialize successfully.
// 2. Under npm, `@napi-rs/wasm-runtime@1.2.6`'s peerDependencies cause npm to hoist `@emnapi/core@1.x`
// to top-level `node_modules` while nesting `2.0.0-alpha.6` under `@oxc-parser/binding-wasm32-wasi`,
// causing an ABI mismatch (`this.bridge.setLastError is not a function`). Under pnpm, `oxc-parser`
// does not declare `@oxc-parser/binding-wasm32-wasi` in its own dependencies. Resolving these
// packages relative to `@angular/build` and `@oxc-parser/binding-wasm32-wasi` ensures consistent
// module resolution across package managers.
//
// See: https://github.com/oxc-project/oxc/issues/28717 and https://github.com/napi-rs/napi-rs/issues/3674
const forceWasi = globalThis.process?.env?.['NAPI_RS_FORCE_WASI'];
if (
(globalThis.process?.versions?.['webcontainer'] ||
forceWasi === 'true' ||
forceWasi === 'error') &&
!('addonModule' in Object.prototype)
) {
Object.defineProperty(Object.prototype, 'addonModule', {
get() {
return (this as { napiModule?: unknown }).napiModule;
},
set(value) {
if (this && (typeof this === 'object' || typeof this === 'function')) {
Object.defineProperty(this, 'addonModule', {
value,
writable: true,
configurable: true,
enumerable: true,
});
}
},
Comment thread
alan-agius4 marked this conversation as resolved.
Resolved
configurable: true,
enumerable: false,
});

const resolvedModules = new Map<string, string>();
try {
const wasiBindingPath = require.resolve('@oxc-parser/binding-wasm32-wasi');
resolvedModules.set('@oxc-parser/binding-wasm32-wasi', wasiBindingPath);
const wasiRequire = createRequire(wasiBindingPath);
resolvedModules.set('@emnapi/core', wasiRequire.resolve('@emnapi/core'));
resolvedModules.set('@emnapi/runtime', wasiRequire.resolve('@emnapi/runtime'));
} catch {}

if (resolvedModules.size > 0) {
const moduleWithResolve = Module as typeof Module & {
_resolveFilename: (
request: string,
parent: unknown,
isMain: boolean,
options: unknown,
) => string;
};
const origResolveFilename = moduleWithResolve._resolveFilename;
moduleWithResolve._resolveFilename = function (request, parent, isMain, options) {
const resolved = resolvedModules.get(request);
if (resolved !== undefined) {
return resolved;
}

return origResolveFilename.call(this, request, parent, isMain, options);
};
}
}
1 change: 1 addition & 0 deletions packages/angular/build/src/tools/oxc/traversal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/

import './oxc-wasi-shim';
import type { Node } from '@oxc-project/types';
import { visitorKeys } from 'oxc-parser';

Expand Down
47 changes: 21 additions & 26 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions tests/e2e/tests/vite/wasi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import assert from 'node:assert';
import { getGlobalVariable } from '../../utils/env';
import { ng } from '../../utils/process';

export default async function () {
assert(
getGlobalVariable('argv')['esbuild'],
'This test should not be called in the Webpack suite.',
);

const originalForceWasi = process.env['NAPI_RS_FORCE_WASI'];
process.env['NAPI_RS_FORCE_WASI'] = 'error';

try {
await ng('build');
} finally {
if (originalForceWasi === undefined) {
delete process.env['NAPI_RS_FORCE_WASI'];
} else {
process.env['NAPI_RS_FORCE_WASI'] = originalForceWasi;
}
}
}
Loading