Skip to content

test(code-splitting): harden strict execution order coverage#10252

Merged
graphite-app[bot] merged 1 commit into
mainfrom
test/strict-execution-order-regressions
Jul 14, 2026
Merged

test(code-splitting): harden strict execution order coverage#10252
graphite-app[bot] merged 1 commit into
mainfrom
test/strict-execution-order-regressions

Conversation

@hyfdev

@hyfdev hyfdev commented Jul 13, 2026

Copy link
Copy Markdown
Member

Summary

This is the second test-only PR in the #10104 stack. It extends the core layer from #10253 with the later real-app and fuzzer-derived regressions, and it runs existing strict fixtures in both wrap-all and on-demand wherever the baseline can be established before the implementation lands.

It contains no production code, test-harness code, or documentation changes. All 185 changed files are under Rolldown fixture directories.

What is covered

  • Adds 14 executed integration fixtures for re-export and barrel init forwarding, dead and ambiguous namespace paths, post-lowering emergent chunk cycles, cross-chunk CJS init exports, entry isolation, dynamic targets hosted in entry chunks, and wrapped-value capture through package barrels.
  • Adds or strengthens runtime assertions so failures are checked by observable execution, not snapshots alone.
  • Adds paired wrap-all/on-demand configurations to existing strict fixtures while preserving the same non-mode options in each pair.
  • Preserves structural snapshots for output-sensitive cases and updates the runtime_merge_respects_preserve_entry_signatures oracle so both legal allow-extension layouts are accepted while strict still requires helper isolation.

Wrap-all and on-demand must preserve the same observable behavior and link-stage tree-shaking result. Their emitted files are not expected to be byte-identical: wrap-all intentionally wraps more modules and may choose additional facades or a separate runtime chunk.

This layer still runs against pre-implementation behavior, so known runtime failures remain guarded by the fail-closed markers introduced in #10253. The final #10104 baseline commit removes every marker, adds the remaining legacy output counterparts whose snapshots depend on the implementation, and establishes the repository-wide 100-pair matrix.

Review guide

  1. Review the 14 new fixture directories and their _test.mjs runtime assertions first.
  2. Treat most remaining _config.json and artifacts.snap changes as the mechanical paired-mode sweep; compare the non-mode options within each pair.
  3. Review runtime_merge_respects_preserve_entry_signatures separately: both entries intentionally demand runtime helpers, allow-extension permits either valid layout, and strict requires entry isolation.
  4. Review the manual-group regular expressions in the affected fixtures for separator-neutral matching; the same cells were also exercised from a deliberately deep checkout path.

Stack

Merge and review in this order:

  1. test(code-splitting): add strict execution order regressions #10253: fail-closed runtime-failure harness and core integration regressions.
  2. test(code-splitting): harden strict execution order coverage #10252 (this PR): additional hardening fixtures and paired-mode coverage.
  3. feat(code-splitting): wrap strict execution order modules on demand #10104: on-demand implementation, removal of all temporary failure markers, and final output baselines.

Validation

Validated at bb18ff9270554026993d06d437764346aa2ba93f:

  • Strict execution-order fixture tests: 77/77 passed on this layer.
  • The PR contains 185 changed files: 118 new files and 67 modified files, all under Rolldown fixture directories.
  • 14 new fixture directories, 15 changed runtime test scripts, and 33 changed artifact snapshots were checked.
  • GitHub's Rolldown, Rust, Node, repository, WASI, and Ubuntu matrix checks pass.
  • Vite Test Ubuntu retains the known scan jsx-runtime / ERR_OUTDATED_OPTIMIZED_DEP and tsconfig error-overlay expectation failures; both signatures reproduce on current main and are unrelated to these fixture-only changes.

hyfdev commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more


How to use the Graphite Merge Queue

Add the label graphite: merge-when-ready to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@hyfdev
hyfdev changed the base branch from codex/vite-chunk-execution-order-plan to graphite-base/10252 July 13, 2026 06:14
@hyfdev
hyfdev force-pushed the test/strict-execution-order-regressions branch from 2db1240 to c548e03 Compare July 13, 2026 06:14
@hyfdev
hyfdev changed the base branch from graphite-base/10252 to test/strict-execution-order-core-regressions July 13, 2026 06:14
@hyfdev
hyfdev force-pushed the test/strict-execution-order-core-regressions branch from 4ea6b3e to f1ffd45 Compare July 13, 2026 07:51
@hyfdev
hyfdev force-pushed the test/strict-execution-order-regressions branch 2 times, most recently from 4e12f0c to b60bf73 Compare July 13, 2026 08:11
@hyfdev hyfdev changed the title test(code-splitting): cover strict execution order test(code-splitting): harden strict execution order coverage Jul 13, 2026
@hyfdev
hyfdev force-pushed the test/strict-execution-order-regressions branch from b60bf73 to 9b7571d Compare July 13, 2026 10:57
@hyfdev
hyfdev force-pushed the test/strict-execution-order-core-regressions branch from f1ffd45 to fdf433b Compare July 13, 2026 10:57
@IWANABETHATGUY

Copy link
Copy Markdown
Member

Two small things I noticed while going through the fixtures — neither blocking.

Variant naming. The variant naming isn't consistent across the fixtures. Most of the sweep uses base = on-demand plus a named wrap-all variant, but a few of the new fixtures flip the direction and leave the on-demand variant unnamed (dynamic_nested_barrel_definer, dynamic_target_in_entry_chunk, entry_fn_captures_wrapped_value, namespace_pure_reexport_barrel, package_barrel_plain_reexport), the two topics/tla fixtures have two unnamed variants, cjs_ns_merge_skipped names the on-demand cell instead, and in esbuild_issue_2598/399 the on-demand cell is called "strict".

Unnamed cells leave globalThis.__configName unset, so when one fails it's harder to tell which cell you're looking at from the test output, and a _test.mjs can't branch on it later if it ever needs to. Might be worth one pass to give every strict cell an explicit name with one convention — happy to leave that for a follow-up though.

emergent_cycle_excluded_forwarder_import. Tiny one: the _test.mjs comment here walks through the hazard and stops at "reads A's not-yet-assigned var require_carrier" — on first read I briefly thought this was an expected-fail test until I checked the snapshot and saw the reader is wrapped and everything's fine. The sibling fixture (emergent_cycle_eager_reexport_overlay) ends its comment with the "projecting the overlay edge wraps chunk B's eligible modules" sentence, which makes the current status obvious. Could you add a closing line like that here too, so it's clear the fixture is green today and just guards the projector/metadata divergence from regressing?

@hyfdev
hyfdev force-pushed the test/strict-execution-order-core-regressions branch from fdf433b to cf6aede Compare July 13, 2026 16:21
@hyfdev
hyfdev force-pushed the test/strict-execution-order-regressions branch 3 times, most recently from 0513f41 to bb18ff9 Compare July 13, 2026 18:43
@hyfdev
hyfdev force-pushed the test/strict-execution-order-core-regressions branch from 34999ed to 9063159 Compare July 13, 2026 18:43
@hyfdev
hyfdev marked this pull request as ready for review July 14, 2026 09:24
Copilot AI review requested due to automatic review settings July 14, 2026 09:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@graphite-app

graphite-app Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Merge activity

  • Jul 14, 10:19 AM UTC: The merge label 'graphite: merge-when-ready' was detected. This PR will be added to the Graphite merge queue once it meets the requirements.
  • Jul 14, 10:21 AM UTC: hyfdev added this pull request to the Graphite merge queue.
  • Jul 14, 10:27 AM UTC: Merged by the Graphite merge queue.

@IWANABETHATGUY

Copy link
Copy Markdown
Member

Same story as #10253: the body was written for an earlier arrangement of the stack (it cites 9b7571de / b60bf733, head is bb18ff92), and its headline numbers no longer hold at this PR's own head.

The body claims "92 strict-enabled fixture directories, 196 configured strict runs, 98 exact pairs, 0 unmatched". I re-derived the matrix from the _config.json trees at this head and get 93 dirs / 190 runs / 91 pairs / 8 unmatched runs across 6 fixtures: concatenate_basic (×3), issue_5303, on_demand_wrapping, pure_annotation_local_fn_reads_global, react-like, and issues/5870. The "0 unmatched" state is only reached at #10104's head, where those six get their wrap-all variants.

Two smaller instances of the same drift:

Two ways to make the body true, either is fine by me:

  1. Move the six missing wrap-all pairs down into this PR, so the "complete parity" purpose statement holds here; or
  2. Keep the diff as-is and scope the claim to "parity for every fixture this PR touches", with a note that the last six pairs land with feat(code-splitting): wrap strict execution order modules on demand #10104.

Everything else in the sweep checks out (zero snapshot deletions, append-only variants), so this is the only thing I'd block on before approving.

## Summary

This is the second test-only PR in the #10104 stack. It extends the core layer from #10253 with the later real-app and fuzzer-derived regressions, and it runs existing strict fixtures in both wrap-all and on-demand wherever the baseline can be established before the implementation lands.

It contains no production code, test-harness code, or documentation changes. All 185 changed files are under Rolldown fixture directories.

## What is covered

- Adds 14 executed integration fixtures for re-export and barrel init forwarding, dead and ambiguous namespace paths, post-lowering emergent chunk cycles, cross-chunk CJS init exports, entry isolation, dynamic targets hosted in entry chunks, and wrapped-value capture through package barrels.
- Adds or strengthens runtime assertions so failures are checked by observable execution, not snapshots alone.
- Adds paired wrap-all/on-demand configurations to existing strict fixtures while preserving the same non-mode options in each pair.
- Preserves structural snapshots for output-sensitive cases and updates the `runtime_merge_respects_preserve_entry_signatures` oracle so both legal `allow-extension` layouts are accepted while `strict` still requires helper isolation.

Wrap-all and on-demand must preserve the same observable behavior and link-stage tree-shaking result. Their emitted files are not expected to be byte-identical: wrap-all intentionally wraps more modules and may choose additional facades or a separate runtime chunk.

This layer still runs against pre-implementation behavior, so known runtime failures remain guarded by the fail-closed markers introduced in #10253. The final #10104 baseline commit removes every marker, adds the remaining legacy output counterparts whose snapshots depend on the implementation, and establishes the repository-wide 100-pair matrix.

## Review guide

1. Review the 14 new fixture directories and their `_test.mjs` runtime assertions first.
2. Treat most remaining `_config.json` and `artifacts.snap` changes as the mechanical paired-mode sweep; compare the non-mode options within each pair.
3. Review `runtime_merge_respects_preserve_entry_signatures` separately: both entries intentionally demand runtime helpers, `allow-extension` permits either valid layout, and `strict` requires entry isolation.
4. Review the manual-group regular expressions in the affected fixtures for separator-neutral matching; the same cells were also exercised from a deliberately deep checkout path.

## Stack

Merge and review in this order:

1. #10253: fail-closed runtime-failure harness and core integration regressions.
2. #10252 (this PR): additional hardening fixtures and paired-mode coverage.
3. #10104: on-demand implementation, removal of all temporary failure markers, and final output baselines.

## Validation

Validated at `bb18ff9270554026993d06d437764346aa2ba93f`:

- Strict execution-order fixture tests: 77/77 passed on this layer.
- The PR contains 185 changed files: 118 new files and 67 modified files, all under Rolldown fixture directories.
- 14 new fixture directories, 15 changed runtime test scripts, and 33 changed artifact snapshots were checked.
- GitHub's Rolldown, Rust, Node, repository, WASI, and Ubuntu matrix checks pass.
- `Vite Test Ubuntu` retains the known `scan jsx-runtime` / `ERR_OUTDATED_OPTIMIZED_DEP` and tsconfig error-overlay expectation failures; both signatures reproduce on current `main` and are unrelated to these fixture-only changes.
graphite-app Bot pushed a commit that referenced this pull request Jul 14, 2026
## Summary

This is the first PR in the #10104 stack. It adds the test infrastructure and core integration coverage before the implementation changes production behavior.

The key harness change is fail-closed support for known runtime failures. A fixture may declare `expectExecutionFailure` with an explicit reason and required output fragments. The runner accepts only a matching nonzero runtime exit: an unexpected pass, a different runtime error, an invalid configuration, or a bundler-creation failure still fails the test.

This PR changes no production bundler code and adds no inline tests to production modules. All strict-order assertions live in integration fixtures or the Rolldown integration-test tree.

## What is covered

- Adds 30 Rolldown fixture directories: 24 directed strict-order regressions, 2 fixture graphs used by the strict-order invariant suite, and 4 top-level-await fixtures.
- Adds 12 integration tests that exercise real `Bundler` instances for entry facades, emitted entries, runtime-helper closure, flag-off compatibility, output-file validation, hazard-free on-demand output, and wrap-all behavior.
- Adds an executable runtime oracle to the existing `issue_4684` fixture.
- Adds a shared console-capture helper used by 12 runtime order oracles; it supports async callbacks and restores `console.log` in `finally`.
- Adds schema and config-variant support for base-level and per-variant expected execution failures.
- Adds 9 self-tests for the failure harness, including matching failure, mismatched failure, XPASS, variant scoping, blank reason/matcher rejection, bundler-creation errors, and manual API coverage.

The fixture graphs cover CJS carriers and cross-chunk cycles, manual groups, user/dynamic/emitted entry facades, retained re-export paths, namespace reads, top-level-await output legality, and external-module boundaries.

Known failures are marked only so this test-first layer can pass on `main`. The markers include exact output signatures and are removed by #10104 only after the implementation makes the corresponding cells pass.

## Review guide

1. Review `crates/rolldown_testing/src/integration_test.rs` together with `crates/rolldown_testing/tests/expected_execution_failure.rs`; this is the fail-closed contract that makes the test-first stack possible.
2. Review `crates/rolldown/tests/rolldown/function/strict_execution_order_invariants/mod.rs`; these are integration tests over real bundler output, not production inline tests.
3. Review each new fixture's `_test.mjs` before its snapshot. Runtime behavior is the primary oracle; snapshots pin the associated chunk and facade shape.
4. Review the four `topics/tla/strict_*` fixtures as output-legality coverage. They do not broaden the semantic guarantee of `strictExecutionOrder`.

## Stack

Merge and review in this order:

1. #10253 (this PR): fail-closed runtime-failure harness and core integration regressions.
2. #10252: additional hardening fixtures and paired wrap-all/on-demand coverage.
3. #10104: on-demand implementation, removal of all temporary failure markers, and final output baselines.

## Validation

Validated at `9063159cb3bb99da98a7d6cec1a214ee45f708cb`:

- Expected-execution-failure harness: 9/9 passed.
- Strict execution-order fixture tests: 63/63 passed on this layer, including the expected-failure cells.
- GitHub required checks pass, including Rust Validation, Cargo Test, Node Validation, repository validation, and the Ubuntu Node matrices.
- The PR contains 250 changed files: 241 new files and 9 modified files, limited to Rolldown tests and test infrastructure.
@graphite-app
graphite-app Bot force-pushed the test/strict-execution-order-core-regressions branch from 9063159 to 19a6a4a Compare July 14, 2026 10:21
@graphite-app
graphite-app Bot force-pushed the test/strict-execution-order-regressions branch from bb18ff9 to 2e9ae51 Compare July 14, 2026 10:22
Base automatically changed from test/strict-execution-order-core-regressions to main July 14, 2026 10:26
@graphite-app
graphite-app Bot merged commit 2e9ae51 into main Jul 14, 2026
29 of 30 checks passed
@graphite-app
graphite-app Bot deleted the test/strict-execution-order-regressions branch July 14, 2026 10:27
graphite-app Bot pushed a commit that referenced this pull request Jul 15, 2026
## Summary

- Give every declared strict-execution-order fixture cell an explicit config name. All 190 cells across 93 fixtures now identify the wrapping mode as `wrap-all` / `on-demand`, with any orthogonal setting included as a prefix.
- Normalize the naming outliers called out in the #10252 review without changing the base, variant, or generated extended-test matrices.
- Complete the `emergent_cycle_excluded_forwarder_import` comment with the projection/fixpoint path that makes the regression green.

## Why

This follows up on [review feedback from #10252](#10252 (comment)). Unnamed cells left `globalThis.__configName` unset, while labels such as `strict`, `minify`, or `cjs` did not consistently say whether the cell used wrap-all or on-demand wrapping.

Relative to `main`, every changed `_config.json` is identical after removing `configName` / `_configName`; the fixture matrix and expected-execution-failure markers are unchanged. Generated output is also unchanged. The 21 updated snapshots only rename 35 `# Variant:` headings.

## Validation

- Audited all 95 config files containing `strictExecutionOrder`: 190 declared strict cells, with zero unnamed cells, mode mismatches, or fixture-local duplicate names.
- Ran `just t-run` for all 95 config files twice; both passes completed 95/95 and the snapshot diff stayed stable.
- Ran the five fixtures whose base mode affects generated extended variants twice with `NEEDS_EXTENDED=true cargo run-fixture`.
- `just lint-repo`
- `git diff --check origin/main`
graphite-app Bot pushed a commit that referenced this pull request Jul 15, 2026
## Summary

This PR isolates the review-only test layout changes requested for #10104.

- Apply the final base/variant ordering and explicit names used by the implementation PR.
- Remove 44 temporary `snapshot: false` gates and record the current `main` output in those positions.
- Keep all 44 expected-execution-failure markers attached to the same semantic configuration cells. No known failure is fixed in this layer.

Only `_config.json` and `artifacts.snap` files change: 86 configs and 85 snapshots. There are no production-code, test-input, harness, or runtime-assertion changes.

## Why this is separate

The previous #10104 diff changed variant order while also changing generated output. GitHub's positional diff then compared wrap-all output on one side with on-demand output on the other, which made unchanged behavior look like a large regression.

With this PR as the base, #10104 compares the same named configurations in the same order. Its config diff only removes expected-failure markers, and its snapshot diff shows the implementation result at the corresponding positions.

## Test-first sequence

The earlier test-first layers are already merged into `main`:

1. #10253 added the fail-closed runtime-failure harness and core integration regressions.
2. #10252 added the later hardening fixtures and paired-mode coverage.

The remaining Graphite stack is:

1. #10287 (this PR) establishes the final review layout and current-`main` snapshots while preserving every known failure.
2. #10104 adds the implementation, removes the failure markers, and records the implementation output.

## Validation

Validation ran on `main` at `c4e25974b8ff7598bf507ae4a2d7602cbcbc86fd`.

- All 86 changed fixture configs passed twice on the old implementation.
- The second pass was snapshot-stable.
- Structural audit: 96 strict-enabled fixture configs, 200 named strict executions, no missing or duplicate names, and 44 expected-failure cells preserved.
- Layout audit: all 87 target config layouts match the final #10104 layout after ignoring names and expected-failure metadata.
- `just lint-repo` passed.
shulaoda pushed a commit that referenced this pull request Jul 15, 2026
## Summary

This PR isolates the review-only test layout changes requested for
#10104.

- Apply the final base/variant ordering and explicit names used by the
implementation PR.
- Remove 44 temporary `snapshot: false` gates and record the current
`main` output in those positions.
- Keep all 44 expected-execution-failure markers attached to the same
semantic configuration cells. No known failure is fixed in this layer.

Only `_config.json` and `artifacts.snap` files change: 86 configs and 85
snapshots. There are no production-code, test-input, harness, or
runtime-assertion changes.

## Why this is separate

The previous #10104 diff changed variant order while also changing
generated output. GitHub's positional diff then compared wrap-all output
on one side with on-demand output on the other, which made unchanged
behavior look like a large regression.

With this PR as the base, #10104 compares the same named configurations
in the same order. Its config diff only removes expected-failure
markers, and its snapshot diff shows the implementation result at the
corresponding positions.

## Test-first sequence

The earlier test-first layers are already merged into `main`:

1. #10253 added the fail-closed runtime-failure harness and core
integration regressions.
2. #10252 added the later hardening fixtures and paired-mode coverage.

The remaining Graphite stack is:

1. #10287 (this PR) establishes the final review layout and
current-`main` snapshots while preserving every known failure.
2. #10104 adds the implementation, removes the failure markers, and
records the implementation output.

## Validation

Validation ran on `main` at `c4e25974b8ff7598bf507ae4a2d7602cbcbc86fd`.

- All 86 changed fixture configs passed twice on the old implementation.
- The second pass was snapshot-stable.
- Structural audit: 96 strict-enabled fixture configs, 200 named strict
executions, no missing or duplicate names, and 44 expected-failure cells
preserved.
- Layout audit: all 87 target config layouts match the final #10104
layout after ignoring names and expected-failure metadata.
- `just lint-repo` passed.
@rolldown-guard rolldown-guard Bot mentioned this pull request Jul 15, 2026
shulaoda added a commit that referenced this pull request Jul 15, 2026
## [1.2.0] - 2026-07-15

### 🚀 Features

- dev: skip shipping factories for newly imported top-level modules (#10223) by @h-a-n-a
- dev: per-client ship map for HMR patch sizing (#10208) by @h-a-n-a
- dev: client-side HMR (#10164) by @h-a-n-a
- dev: send a full-reload update to clients when a tsconfig changes (#10262) by @shulaoda
- treat `import.meta['url']` and `import.meta['ROLLUP_FILE_URL_*']` as side-effect free (#10267) by @sapphi-red
- rewrite `import.meta['url']` (#10251) by @sapphi-red
- add `FILE_NOT_FOUND` error (#10220) by @sapphi-red
- treat `import.meta.ROLLUP_FILE_URL_*` as side-effect free (#10217) by @sapphi-red

### 🐛 Bug Fixes

- sourcemap: preserve unmapped boundaries during composition (#10254) by @hyfdev
- `[format]` in `*FileNames` option for ESM format should be `es` instead of `esm` (#10214) by @sapphi-red
- sourcemap: preserve coarse mappings during composition (#10249) by @hyfdev
- rolldown_plugin_vite_import_glob: support tsconfig paths with `import.meta.glob` (#10167) by @sapphi-red
- dev: clear tsconfig caches for bare full builds (#10276) by @shulaoda
- dev: force a full rebuild when a tsconfig changes (#10261) by @shulaoda
- treat rooted drive-less module ids as absolute in preserveModules naming (#10235) by @IWANABETHATGUY
- watch: rebuild when tsconfig files change (#10258) by @shulaoda
- watch: drop tsconfig-merged transform options on each rebuild (#10257) by @shulaoda
- incorrect `EMPTY_IMPORT_META` warning for `import.meta.ROLLUP_FILE_URL_*` for CJS output (#10221) by @sapphi-red
- deconflict: rename CJS locals shadowing wrapped-ESM namespace objects (#9970) by @IWANABETHATGUY
- rolldown: drop the unused runtime module after entry-level external flattening (#10237) by @IWANABETHATGUY
- rolldown: re-propagate has_dynamic_exports to transitive star importers (#10239) by @IWANABETHATGUY
- tree-shaking: tree-shake destructured dynamic import namespace bindings (#10213) by @logaretm
- s390x: use json-escape-simd 3.1.1 for big-endian JSON escaping fix (#10211) by @satyamg1620

### 🚜 Refactor

- dev: move full-reload to client side (#10207) by @h-a-n-a
- readability follow-ups to the ReplaceWith migration (#10286) by @IWANABETHATGUY
- replace take_in-then-write-back with ReplaceWith and by-value moves (#10285) by @Boshen
- share the main resolver's cache with the transformer's tsconfig lookups (#10205) by @shulaoda
- rolldown: extract the ns star-external __reExport emission rule into LinkingMetadata (#10238) by @IWANABETHATGUY
- rolldown: unify link/generate diagnostics into a Diagnostics accumulator (#10234) by @IWANABETHATGUY
- sourcemap_filenames: drop dead sourcemap-filename plumbing (#10189) by @IWANABETHATGUY
- extract external import symbol merging into a method (#10224) by @IWANABETHATGUY
- rolldown: skip CJS namespace merging under strict execution order (#10203) by @hyfdev
- resolve the manual tsconfig per file instead of once at startup (#10200) by @shulaoda
- rolldown: route interop ESM init emission through a shared init-target view (#10202) by @hyfdev
- rolldown: collapse vestigial wrap-kind state and share chunk sort helper (#10201) by @hyfdev

### 📚 Documentation

- show plugin kinds in JSDoc and each hook's description (#10218) by @sapphi-red
- add an explanation about removing imports from external modules without any messages (#10215) by @sapphi-red

### ⚡ Performance

- sourcemap: owned merge in SourceJoiner::join (4005->5 allocs/chunk) (#10250) by @Boshen
- avoid redundant sourcemap string copies in collapse and minify paths (#10093) by @Boshen

### 🧪 Testing

- code-splitting: establish strict-order review baselines (#10287) by @hyfdev
- dev: add hot API test cases (#10181) by @h-a-n-a
- code-splitting: normalize strict execution order variants (#10277) by @hyfdev
- code-splitting: harden strict execution order coverage (#10252) by @hyfdev
- code-splitting: add strict execution order regressions (#10253) by @hyfdev

### ⚙️ Miscellaneous Tasks

- deps: update github actions (#10241) by @renovate[bot]
- deps: update oxc to 0.140.0 (#10274) by @shulaoda
- update Yunfei's GitHub username (#10275) by @hyfdev
- deps: update napi (#10260) by @renovate[bot]
- deps: update test262 submodule for tests (#10266) by @rolldown-guard[bot]
- deps: update dependency vite-plus to v0.2.4 (#10256) by @renovate[bot]
- deps: update napi (#10240) by @renovate[bot]
- deps: update oxc resolver to v11.24.2 (#10245) by @renovate[bot]
- deps: update rust crates (#10244) by @renovate[bot]
- disable Renovate updates for idna_adapter (#10248) by @shulaoda
- deps: update oxc resolver to v11.24.1 (#10232) by @renovate[bot]
- deps: update rust crate oxc_sourcemap to v8.1.1 (#10233) by @renovate[bot]
- deps: update dependency rolldown-plugin-dts to ^0.27.0 (#10206) by @renovate[bot]
- deps: upgrade sugar_path to v3 (#10230) by @hyfdev
- add `dist-*` to `.gitignore` in sourcemap-filenames/hash-final-content fixture (#10216) by @sapphi-red
- deps: update dependency rust to v1.97.0 (#10209) by @renovate[bot]

### ❤️ New Contributors

* @satyamg1620 made their first contribution in [#10211](#10211)

Co-authored-by: shulaoda <[email protected]>
graphite-app Bot pushed a commit that referenced this pull request Jul 17, 2026
…10104)

## Summary

This PR adds an opt-in on-demand planner for `output.strictExecutionOrder` while preserving both the flag-off path and strict mode's existing wrap-all default.

| Configuration | Execution-order behavior |
| --- | --- |
| `strictExecutionOrder: false` | No execution-order wrapping |
| `strictExecutionOrder: true` | Wrap every eligible module (wrap-all, the conservative default) |
| `strictExecutionOrder: true` + `experimental.onDemandWrapping: true` | Derive a conservative wrapping plan from the generated chunk graph |

The on-demand plan is deliberately conservative rather than minimal. It begins with predicted source-order versus chunk-order hazards, then closes over importers, trigger hosts, retained re-export paths, and cyclic chunks where eager evaluation cannot be proven safe.

This PR does not change the default behavior of `strictExecutionOrder` and does not enable strict execution order globally.

Fixes #10236.

> [!IMPORTANT]
> Strict-order output may change the entry-chunk shape. When an entry implementation must remain inert while another chunk is evaluated, Rolldown emits a small entry facade that calls the relevant `init_*()` function and moves the implementation into a separate chunk. Reviewers and downstream tooling should expect this facade/implementation split.

## Why the analysis runs after chunking

Interop wrapping answers how a module is represented and remains a link-stage decision. Execution-order wrapping answers when an already-included module body may run, which depends on generated chunk placement, manual groups, facade elimination, shared entries, and chunk cycles.

This PR keeps link-owned `WrapKind` and user-code liveness immutable, computes an `OrderWrapPlan` after provisional chunking, and lowers that plan into a separate `OrderWrapState`. Lowering may add synthetic wrappers, init calls, runtime symbols, entry facades, and topology state; it cannot make an excluded user statement or binding live.

The on-demand path then projects imports added by lowering back into the chunk graph. If those imports create a new chunk cycle, every eligible module in the cyclic chunks is added to the plan and projection repeats until stable. The process is monotone and bounded by the module count.

## Correctness hardening

- Preserve init obligations through re-export barrels without reviving tree-shaken imports or unrelated exports.
- Share init-obligation enumeration between emission, cross-chunk registration, and fixpoint projection so their record gates cannot drift.
- Preserve the same link-stage tree-shaking result in wrap-all and on-demand, including consumer-local named and namespace re-export paths.
- Keep cross-chunk `init_*` bindings live and deconflicted for both ESM and CJS output.
- Restore user, dynamic, and emitted entry facades after strict lowering changes chunk topology.
- Recompute topology-derived metadata and final output validation after lowering.

Tree-shaking parity here is specifically between wrap-all and on-demand. It does not claim byte identity with flag-off output and does not include the separate `wrappedModuleTreeshaking` work.

## Review guide

1. Start with `internal-docs/code-splitting/design.md` for the invariants, mode split, conservative decisions, and projected/omitted edge inventory.
2. Read `order_analysis.rs` for source-order versus chunk-order prediction and the emergent-cycle fixpoint.
3. Read `order_wrap_state.rs` and `order_wrapping.rs` for the late synthetic state and lowering boundary.
4. Read `esm_init_obligations.rs` together with the module finalizers and cross-chunk-link pass for the shared emit/register/project contract.
5. Review #10287 for config/variant normalization and the current-main output baseline.
6. Review this PR's test diff for removal of every temporary expected-failure marker and the same-position snapshots of the final facade/runtime/chunk layouts.

## Test-first sequence

The earlier test-first layers are already merged into `main`:

1. #10253 adds the fail-closed runtime-failure harness and core integration regressions.
2. #10252 adds the later hardening fixtures and paired-mode coverage.

The remaining Graphite stack is:

1. #10287 establishes the final config/variant review layout and current-`main` snapshots while preserving all expected-failure markers.
2. #10104 (this PR) adds the implementation, removes all temporary failure markers, and records the implementation output.

The final tree contains 96 strict-enabled fixture configs and 200 named strict executions. Between #10287 and #10104, 21 config files only delete 44 expected-failure markers, and all 93 changed snapshots retain identical variant-heading order.

## Validation

Local validation ran on rebased head `79efe909bb3f0bcd2abcc76dcd33e58069c3474c`, based on `main` at `c4e25974b8ff7598bf507ae4a2d7602cbcbc86fd`.

- The 86 changed fixtures passed twice on #10287 with the old implementation; the second pass was snapshot-stable.
- All 96 strict-enabled fixture configs passed twice on this implementation, with a snapshot-stable final pass.
- Full Rust workspace tests passed with `just test-rust`.
- Rolldown Node tests passed: 793 main tests and 38 watcher tests.
- The Rollup compatibility suite passed 1,213 tests with 0 failures.
- `cargo check --workspace --all-features --all-targets --locked` and `cargo fmt --all --check` passed.
- Clippy passed with `--deny warnings` for every changed Rust crate: `rolldown`, `rolldown_common`, and `rolldown_ecmascript_utils`.
- `just lint-node` and `just lint-repo` passed.
- Full-workspace `just lint-rust` is locally blocked on Rust 1.97 by `clippy::ignore-without-reason` in the unchanged `rolldown_plugin_isolated_declaration/tests/mod.rs`; the failing path is byte-identical to `main`.

## Frozen closure evidence

The broader correctness campaign was frozen on one build of `42628c18b`:

- Differential fuzzer full matrix: 3,000/3,000 passed (ESM/CJS/mixed inputs × wrap-all/on-demand × 500 cases).
- Directed barrel/definer family: 600/600 passed; the same generated shapes failed about 23.7% of the time before the fix.
- Directed eager-barrel family: 20/20 passed in wrap-all and 20/20 in on-demand.
- #9887 strict-path bracket passed on the branch and failed 20/20 on released `[email protected]`. This PR restores strict mode as an escape hatch; the non-strict path remains a separate issue, so this PR does not close #9887.
- #10236 CJS-output/on-demand bracket passed on the branch and failed all 8 directed seeds on released `[email protected]`.
- ComfyUI, Camunda, shadcn-admin, and vue-vben-admin passed the flag-off/on-demand/wrap-all browser-probed matrix.

Fuzzer source and machine-readable evidence: https://github.com/hyf0/rolldown-order-fuzzer/tree/853fb775d1e949ced567deb0c163d044a031ba4f/.agents/evidence

## Boundaries and known residuals

- Top-level-await output remains legal, but this PR does not promise stronger TLA scheduling semantics than the default build.
- External-module evaluation order remains outside the strict-order guarantee; generated output must still be legal.
- #9998 is a separate bug in the non-strict/default code-splitting path and is not fixed here. The strict path covered by this PR passes its directed controls.
- Camunda still emits two `init_*` functions with no call site (`OverflowMenu$1` and `RadioButton$1`). They were harmless on all probed routes and have no synthetic reproduction.
- The partial-forwarder per-obligation refinement is present in the shared contract, but no constructible corpus shape currently exercises that narrower branch directly.
- On vue-vben-admin, the emergent-cycle fixpoint converged in two rounds and added 141 wrappers over the one-shot plan. `ROLLDOWN_ORDER_DEBUG=1` reproduces the per-round trace.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants