Skip to content

fix(rspack): preload @rspack/core before loading rspack config files - #36687

Open
federicobartoli wants to merge 1 commit into
nrwl:masterfrom
federicobartoli:fix/rspack-plugin-preload-rspack-core
Open

fix(rspack): preload @rspack/core before loading rspack config files#36687
federicobartoli wants to merge 1 commit into
nrwl:masterfrom
federicobartoli:fix/rspack-plugin-preload-rspack-core

Conversation

@federicobartoli

Copy link
Copy Markdown

Current Behavior

On Node 22.18+/24 with native TypeScript stripping (the default NX_PREFER_NODE_STRIP_TYPES path), the @nx/rspack/plugin inference plugin can take down project graph creation for every command (nx show projects, nx serve, nx report, ...):

Failed to process project graph.
  - apps/a/rspack.config.ts:
      Unexpected module status 0. Cannot require() ES Module .../@rspack/core/dist/index.js because it is not yet fully loaded.
      This may be caused by a race condition if the module is simultaneously dynamically import()-ed via Promise.all().

Root cause (deterministic, not actually a Promise.all race): loadTsFile require()s rspack.config.ts, which Node treats as ESM. When that ESM link fails — e.g. an extensionless .ts sibling import like import config from './module-federation.config', which the legacy MF generators emit — Node removes the failing config from its ESM loader cache but leaves already-linked dependencies such as @rspack/core (pure ESM in v2) cached in the un-instantiated state (ModuleJobSync cleanup only deletes the module itself). Nx then falls back to swc/ts-node, the swc-compiled config (or loadRspackCore() in applyBaseConfig) does require('@rspack/core'), and Node's importSyncForRequire hits the cached job with status 0 → ERR_INTERNAL_ASSERTION.

Minimal standalone repro (Node 24.15, @rspack/core@2.0.8):

// a.ts
import { CopyRspackPlugin } from '@rspack/core';
import cfg from './missing-sibling';
export default {};
// run.cjs
try { require('./a.ts'); } catch (e) { console.log(e.code); } // ERR_MODULE_NOT_FOUND
require('@rspack/core'); // ERR_INTERNAL_ASSERTION: Unexpected module status 0

Expected Behavior

Graph creation succeeds. @rspack/core is loaded (via import()) before any user rspack.config file is loaded, in resolveUserDefinedRspackConfig — the single place both the inference plugin and the executor go through. Once @rspack/core is fully evaluated, a failed config link can no longer leave it half-loaded, and every later require('@rspack/core') (from the fallback-compiled config or from loadRspackCore) resolves from cache. This is the same approach as the vitest/node preload in #34261.

The preload is lazy (only when a config actually needs to be loaded), so cached graphs pay nothing.

Verified locally on Node 24.15 with a 6-app workspace whose rspack.config.ts files import @rspack/core and an extensionless .ts sibling: fails deterministically at iteration 1 after nx reset without the change; passes (targets inferred, devServer.port read from the config) with it. Added an e2e test in e2e/rspack covering that shape.

Related Issue(s)

Fixes #36685

@federicobartoli
federicobartoli requested a review from a team as a code owner August 17, 2026 09:53
@netlify

netlify Bot commented Aug 17, 2026

Copy link
Copy Markdown

👷 Deploy request for nx-docs pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 781d3d0

@netlify

netlify Bot commented Aug 17, 2026

Copy link
Copy Markdown

👷 Deploy request for nx-dev pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 781d3d0

On Node 22.18+/24 with native TypeScript stripping, `@nx/rspack/plugin`
loads `rspack.config.ts` files via `require()`, which Node treats as ESM.
When that ESM link fails (e.g. an extensionless `.ts` sibling import such
as `./module-federation.config`), Node drops the failing config from its
loader cache but leaves already-linked dependencies like `@rspack/core`
cached in the un-instantiated state. The loader then falls back to
swc/ts-node, and the re-required `@rspack/core` fails with

  ERR_INTERNAL_ASSERTION: Unexpected module status 0. Cannot require()
  ES Module .../@rspack/core/dist/index.js because it is not yet fully
  loaded.

taking down project graph creation for every command.

Load `@rspack/core` before any user rspack config is loaded, in
`resolveUserDefinedRspackConfig` (used by both the inference plugin and
the executor). Once it is fully evaluated, a failed config link can no
longer leave it half-loaded. Same approach as the `vitest/node` preload
in nrwl#34261.

Fixes nrwl#36685
@federicobartoli
federicobartoli force-pushed the fix/rspack-plugin-preload-rspack-core branch from 049dccc to 781d3d0 Compare August 17, 2026 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant