Skip to content

perf(ci): make per-PR cargo-test fast (<10 min) — unit tests for affected crates; integration → nightly/tags#5411

Merged
proggeramlug merged 1 commit into
mainfrom
perf/affected-crate-tests
Jun 18, 2026
Merged

perf(ci): make per-PR cargo-test fast (<10 min) — unit tests for affected crates; integration → nightly/tags#5411
proggeramlug merged 1 commit into
mainfrom
perf/affected-crate-tests

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

cargo-test takes ~90 min because it tests the entire workspace in debug, serially, and 30 of 40 perry integration test files compile via the slow auto-optimize path (whole-program optimized rebuilds — individual tests run 4–6 min each). Most PRs touch a handful of crates, so this is mostly wasted work.

This scopes per-PR cargo-test to only the crates the diff can affect. Release tags, a new nightly cron, and workflow_dispatch still run the full workspace as the safety net.

How

  • scripts/ci_test_scope.py — reads the PR's changed files and prints the packages to test:
    • each changed crates/<dir> plus its reverse-dependency closure (a change to a foundational crate like perry-runtime/perry-hir still fans out to all dependents);
    • runtime-linked crates (perry-stdlib, perry-ffi, perry-ext-*) add a perry edge, since the driver links those archives into compiled output at runtime (not via cargo) and perry's integration tests exercise them;
    • infra changes (.github/, scripts/, rust-toolchain*) or any unrecognized path → full workspace (conservative);
    • metadata-only changes (CHANGELOG.md, CLAUDE.md, *.md, docs/, root Cargo.toml/Cargo.lock) → nothing (a version-bump PR is instantly green);
    • cross-host UI crates / doc fixtures stay excluded.
  • .github/workflows/test.yml — the cargo-test step computes scope from gh pr view --json files (PR) or --full (tag/cron/dispatch), exit 0s when nothing is affected, and only runs the single-threaded perry-runtime pass / builds the staticlibs when those are in scope. Adds a nightly schedule (04:00 UTC) full-workspace run — main otherwise never runs the suite (pushes don't trigger Tests), so this is the cross-crate-regression backstop.

Effect

  • Metadata-only / docs PR: ~90 min → seconds (no crates tested).
  • Single leaf-crate PR (e.g. one perry-ext-*): tests that crate + perry only.
  • Foundational change (perry-hir, perry-runtime): still fans out (correct).
  • This PR itself touches .github/ + scripts/ → runs the full workspace (validates the full path).

Trade-off (deliberate)

A cross-crate regression a scoped PR misses is caught by the nightly full run rather than at PR time.

Verified

scripts/ci_test_scope.py against representative inputs: metadata-only → 0; perry-stdlibperry perry-stdlib; perry-ext-httpperry perry-ext-http; .github/ → full (66); foundational perry-types → 11 dependents. test.yml validates as YAML with triggers push, pull_request, schedule, workflow_dispatch.

Summary by CodeRabbit

  • Chores
    • Version updated to 0.5.1185.
    • CI testing improvements: added a nightly scheduled run and streamlined pull request checks to execute only the scoped set of affected crates, with full workspace coverage reserved for non-PR triggers.
  • Tests
    • Per-PR test execution is now faster by limiting which unit/bin tests run, while keeping single-threaded runtime tests conditional on relevance.
  • Documentation
    • Updated the changelog to describe the new CI test scoping rules and trigger behavior.
    • Updated the documented current version.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f369f3cc-78b7-4fa0-876c-b960ae54adb6

📥 Commits

Reviewing files that changed from the base of the PR and between 1903916 and 0f7a728.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • .github/workflows/test.yml
  • CHANGELOG.md
  • CLAUDE.md
  • Cargo.toml
  • scripts/ci_test_scope.py

📝 Walkthrough

Walkthrough

Adds scripts/ci_test_scope.py, a Python helper that reads changed file paths from stdin, classifies them as ignorable/infra/crate-mapped, loads Cargo workspace metadata, derives a seed set of affected crates, augments seeds for runtime-linked artifacts (perry-stdlib, perry-ffi, perry-ext-*), and outputs the minimal scope via reverse-dependency closure. The cargo-test workflow is updated to call this script for PR events with GH_TOKEN, skip if scope is empty, and conditionally execute perry-runtime/perry-stdlib builds and tests. A nightly cron trigger is added. Workspace version is bumped to 0.5.1185 with corresponding changelog documentation.

Changes

PR-scoped CI test selection

Layer / File(s) Summary
ci_test_scope.py: path classification and metadata loading
scripts/ci_test_scope.py
Defines excluded crate blacklist and infra prefixes, classifies paths as ignorable (metadata/docs), force-full (infra), or crate-mapped (crates/<dir>), loads Cargo workspace metadata via cargo metadata, maps directory names to package names, and computes the set of testable workspace members excluding blacklist.
ci_test_scope.py: seed augmentation and reverse-dependency closure
scripts/ci_test_scope.py
Augments seed set to include perry when changes affect perry-stdlib, perry-ffi, or perry-ext-* (runtime-linked artifacts); traverses reverse Cargo dependency edges from seeds to collect all transitively dependent workspace packages; intersects result with testable set.
ci_test_scope.py: control flow, has-lib mode, and output
scripts/ci_test_scope.py
Adds --has-lib mode to check if packages contain lib targets (for selecting cargo test flags); wires main() to parse --full/--has-lib, read changed paths from stdin, apply infra overrides, compute seeds, apply augmentation and closure, and output sorted crate names; provides module entry point.
Workflow: nightly schedule, GH_TOKEN, and scope computation
.github/workflows/test.yml
Adds a 0 4 * * * cron trigger for nightly runs; sets GH_TOKEN in cargo-test job to enable authenticated gh pr view calls; computes PR scope by piping gh pr view ... --json files to ci_test_scope.py, uses --full scope for non-PR events, and exits successfully when scope is empty.
Workflow: conditional builds and test modes
.github/workflows/test.yml
Makes perry-runtime single-threaded tests conditional on scope membership; conditionally builds perry-runtime and perry-stdlib staticlibs when scope includes perry and/or perry-stdlib. For full runs, iterates over scoped packages excluding perry-runtime with per-package cleanup. For PR runs, uses fast mode: runs perry-runtime tests only if in scope, then single cargo test invocation over remaining packages, selecting --lib --bins vs --bins via ci_test_scope.py --has-lib.
Documentation and version
Cargo.toml, CLAUDE.md, CHANGELOG.md
Bumps workspace version to 0.5.1185; adds changelog entry describing PR-scoped fast-path behavior (scope via ci_test_scope.py with reverse-dependency closure), full workspace tests on nightly/release-tag/workflow-dispatch/extended-test events, and the trade-off that integration-test-only regressions are deferred to nightly/release runs.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PerryTS/perry#5406: Directly modifies the same cargo-test job's perry-runtime/perry-stdlib staticlib build steps that this PR makes scope-conditional.

Poem

🐇 Hoppity-hop through the diff I go,
Only the crates that changed need to run the show!
Reverse-dep closure, a graph-traversal dance,
No more full builds at every PR's glance.
At 4 AM nightly I check the whole tree—
Scoped tests by day, full backstop for free! 🌙

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is comprehensive and well-structured, covering summary, detailed how/what changes, expected effects, trade-offs, and verification. However, it violates three explicit repository guidelines by including changes to CHANGELOG.md, CLAUDE.md, and Cargo.toml version, which should only be managed by maintainers at merge time. Remove version bumps from Cargo.toml and CLAUDE.md, and remove the CHANGELOG.md entry per CONTRIBUTING.md guidelines—maintainers handle these at merge. Keep only the core changes: scripts/ci_test_scope.py and test.yml updates.
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: making per-PR cargo-test fast by scoping to affected crates with unit tests, while deferring integration tests to nightly/tags.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/affected-crate-tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/test.yml:
- Around line 315-327: The gh pr view command execution on line 316 lacks error
handling, which means if the command fails due to authentication or transient
errors, the changed_files variable becomes empty or contains error text and the
script silently proceeds. Add error checking immediately after the gh pr view
command invocation to detect and fail fast when the command fails, ensuring that
transient CLI failures do not result in silently skipped tests. Use set -e or an
explicit error check like if [ $? -ne 0 ] to guard against the gh pr view
command failure and prevent the script from continuing with invalid data.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a06aa730-f59c-4b97-a116-0bcfa30761b8

📥 Commits

Reviewing files that changed from the base of the PR and between 7497483 and 38873c7.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • .github/workflows/test.yml
  • CHANGELOG.md
  • CLAUDE.md
  • Cargo.toml
  • scripts/ci_test_scope.py

Comment on lines +315 to +327
if [ "${{ github.event_name }}" = "pull_request" ]; then
changed_files="$(gh pr view "${{ github.event.pull_request.number }}" \
--json files --jq '.files[].path')"
echo "Changed files in PR:"; printf '%s\n' "$changed_files"
scope="$(printf '%s\n' "$changed_files" | python3 scripts/ci_test_scope.py)"
else
scope="$(python3 scripts/ci_test_scope.py --full </dev/null)"
fi
echo "Packages in test scope:"; printf '%s\n' "$scope"
if [ -z "$scope" ]; then
echo "No crates affected by this diff — nothing to test."
exit 0
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read-only verification: compare gh PR-view file count vs paginated REST count.
# Usage:
#   REPO=owner/name PR_NUMBER=123 bash verify_pr_file_count.sh
set -euo pipefail
: "${REPO:?set REPO=owner/name}"
: "${PR_NUMBER:?set PR_NUMBER=<number>}"

echo "gh pr view files count:"
gh pr view "$PR_NUMBER" --repo "$REPO" --json files --jq '.files | length'

echo "Paginated REST files count:"
gh api "repos/$REPO/pulls/$PR_NUMBER/files?per_page=100" --paginate --jq '.[].filename' | wc -l

Repository: PerryTS/perry

Length of output: 102


🏁 Script executed:

# First, locate and read the workflow file
git ls-files ".github/workflows/*.yml" | head -20

Repository: PerryTS/perry

Length of output: 514


🏁 Script executed:

# Read the specific section being reviewed
cat -n .github/workflows/test.yml | sed -n '310,330p'

Repository: PerryTS/perry

Length of output: 1385


🏁 Script executed:

# Check if there are any other references to this script or error handling patterns
rg "gh pr view|changed_files|ci_test_scope" --type yaml -A 2 -B 2

Repository: PerryTS/perry

Length of output: 39


🌐 Web query:

GitHub CLI gh pr view pagination limit file listing behavior

💡 Result:

The gh pr view command, when used with the --json files flag, historically suffered from a silent truncation of results at 100 items [1][2]. This behavior occurred because the GitHub GraphQL API, which the command relies upon, has a 100-node limit per request, and the command previously did not implement pagination to fetch subsequent pages [1][3]. As of May 2026, this behavior has been updated [1]. A pull request (cli/cli#13340) was merged to introduce pagination for files and commits in gh pr view --json [1]. This update adds PageInfo to the Files and Commits structs and implements concurrent preloading to fetch results beyond the initial 100-node limit [1]. Before this change, users requiring a complete list of files for pull requests with more than 100 changed files were advised to use the gh api command with the --paginate flag to correctly handle GraphQL pagination [3][4]. For simple listings, gh pr diff --name-only is also a common alternative that avoids this specific GraphQL limitation [5].

Citations:


🏁 Script executed:

# Check if GitHub CLI version is pinned in the workflow
cat -n .github/workflows/test.yml | sed -n '1,100p' | grep -i "actions/github-script\|github-cli\|gh"

Repository: PerryTS/perry

Length of output: 311


🏁 Script executed:

# Look for the full context of where this code runs to understand the shell environment
cat -n .github/workflows/test.yml | sed -n '280,340p'

Repository: PerryTS/perry

Length of output: 3976


🏁 Script executed:

# Check if set -euo pipefail is used anywhere in this workflow script
cat .github/workflows/test.yml | sed -n '280,340p'

Repository: PerryTS/perry

Length of output: 3549


🌐 Web query:

GitHub Actions runner GitHub CLI version June 2026 update pagination files

💡 Result:

As of June 2026, the GitHub CLI (gh) is at version 2.94.0 [1], which is the version included in current GitHub Actions runner images [2][3]. Regarding pagination and updates related to GitHub CLI files and commands in June 2026: 1. GitHub CLI 2.94.0 Release: This release, published on June 10, 2026, introduced several new commands, such as the gh discussion set and gh skill list [1]. It does not contain major breaking changes to general pagination logic but maintains standard behavior for existing commands [1]. 2. Pagination in gh pr view: Recent development has addressed silent truncation issues for specific pull request sub-resources [4]. Specifically, pull requests now support improved pagination for files and commits when using gh pr view --json, ensuring that results exceeding the standard 100-item limit are correctly fetched rather than truncated [4]. 3. General Pagination Behavior: - The --paginate flag remains the primary method for handling automatic pagination in gh api calls [5][6]. - For commands like gh issue list or gh pr list, users should continue to use the -L (--limit) flag to define result caps (defaulting to 30) [5]. - Users are advised that --paginate can only handle pagination for a single connection at a time; nested pagination (e.g., paginating comments within an issue) requires custom logic [6]. - Documentation notes that combining --paginate with --jq or --slurp can sometimes lead to complex or unexpected output behavior [7][5]. GitHub Actions runner images have been updated to include GitHub CLI 2.94.0 across all major OS platforms (Ubuntu 26.04, macOS-26, etc.) as of the mid-June 2026 image updates [2][8][9][10][11].

Citations:


🌐 Web query:

cli/cli#13340 GitHub Actions deployment release date

💡 Result:

The GitHub issue referenced as cli/cli#13340 is titled Paginate files and commits in gh pr view --json [1]. This pull request was created on May 4, 2026, and is not related to a GitHub Actions deployment release date [1]. The repository cli/cli does maintain a deployment workflow located at.github/workflows/deployment.yml [2][3], but this file is part of the repository's internal infrastructure and is not associated with a public GitHub Actions release date [3]. There is no specific public release date for a GitHub Actions deployment feature under this issue number [1].

Citations:


Add error handling for gh pr view failures in PR test scope logic.

Line 316 calls gh pr view without guarding against command failures. If the CLI call fails (authentication issue, transient error, etc.), changed_files becomes empty or contains error text, and the script silently proceeds to line 324 where an empty scope exits with success, skipping all tests without alerting.

While GitHub CLI 2.94.0 (deployed to Actions runners mid-June 2026) fixed the 100-item truncation issue for PR files, the lack of error handling remains a availability risk. Tests can be silently skipped due to transient CLI failures.

Suggested hardening
+          set -euo pipefail
           if [ "${{ github.event_name }}" = "pull_request" ]; then
-            changed_files="$(gh pr view "${{ github.event.pull_request.number }}" \
-              --json files --jq '.files[].path')"
-            echo "Changed files in PR:"; printf '%s\n' "$changed_files"
-            scope="$(printf '%s\n' "$changed_files" | python3 scripts/ci_test_scope.py)"
+            if ! changed_files="$(gh api \
+              "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files?per_page=100" \
+              --paginate --jq '.[].filename')"; then
+              echo "::warning::Failed to fetch PR files; falling back to full workspace scope."
+              scope="$(python3 scripts/ci_test_scope.py --full </dev/null)"
+            elif [ -z "$changed_files" ]; then
+              echo "::warning::Empty PR file list; falling back to full workspace scope."
+              scope="$(python3 scripts/ci_test_scope.py --full </dev/null)"
+            else
+              echo "Changed files in PR:"; printf '%s\n' "$changed_files"
+              scope="$(printf '%s\n' "$changed_files" | python3 scripts/ci_test_scope.py)"
+            fi
           else
             scope="$(python3 scripts/ci_test_scope.py --full </dev/null)"
           fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml around lines 315 - 327, The gh pr view command
execution on line 316 lacks error handling, which means if the command fails due
to authentication or transient errors, the changed_files variable becomes empty
or contains error text and the script silently proceeds. Add error checking
immediately after the gh pr view command invocation to detect and fail fast when
the command fails, ensuring that transient CLI failures do not result in
silently skipped tests. Use set -e or an explicit error check like if [ $? -ne 0
] to guard against the gh pr view command failure and prevent the script from
continuing with invalid data.

@proggeramlug proggeramlug force-pushed the perf/affected-crate-tests branch from 38873c7 to 1903916 Compare June 18, 2026 16:02
@proggeramlug proggeramlug changed the title perf(ci): scope per-PR cargo-test to crates affected by the diff perf(ci): make per-PR cargo-test fast (<10 min) — unit tests for affected crates; integration → nightly/tags Jun 18, 2026
…cted crates

cargo-test took ~90 min: full workspace, serial debug build (CARGO_BUILD_JOBS=1
+ clean-between-packages disk workaround), and 30/40 perry integration tests each
auto-optimize-compile via perry (4-6 min each, unparallelizable — concurrent
auto-opt builds thrash a shared target).

Per-PR cargo-test is now (a) scoped to the diff (scripts/ci_test_scope.py:
changed crates + reverse-dep closure, perry edge for runtime-linked
stdlib/ffi/ext; infra/unknown -> full; metadata-only -> nothing) and (b)
unit/lib/bin tests only (cargo test --lib --bins) — the slow auto-optimize
integration tests are NOT run per-PR. Unit binaries are small so builds
parallelize (no serialization/clean churn) and no staticlib is needed.

Full suite (every integration test) runs on release tags, a new nightly
schedule (04:00 UTC), workflow_dispatch, and the run-extended-tests label.
test.yml branches the step on github.event_name.
@proggeramlug proggeramlug force-pushed the perf/affected-crate-tests branch from 1903916 to 0f7a728 Compare June 18, 2026 16:09
@proggeramlug proggeramlug merged commit b72d28b into main Jun 18, 2026
13 of 15 checks passed
@proggeramlug proggeramlug deleted the perf/affected-crate-tests branch June 18, 2026 16:10
proggeramlug pushed a commit that referenced this pull request Jun 18, 2026
…dules (#5389 Tier 2)

A computed require(expr) in a compiled external/compilePackages module whose
specifier const-folds to a finite set (ternary of literals, module-const, or a
directory-anchored template glob) now resolves synchronously to the target
compiled-module namespace, reusing the dynamic import() path resolver.

- New synchronous: bool field on Expr::DynamicImport (false for import(), true
  for require()).
- try_dynamic_require lowers a bare non-literal require in external modules to a
  synchronous DynamicImport; literals stay on try_require_literal.
- collect_modules const-folds/globs paths unchanged; an unresolved synchronous
  node records an empty path set (ambient fallback) instead of import()'s
  deferred-reject/strict error.
- lower_dynamic_require emits the same string_equals dispatch as import() but
  returns the namespace value directly (no Promise) with the Tier 1 ambient
  require (js_module_ambient_require_apply) as the no-match fallthrough.

Genuinely runtime-computed package strings remain unsupported by design (same
AOT boundary as dynamic import()). Also restores the v0.5.1184 changelog entry
dropped from main during the #5411 merge (its code is on main).
proggeramlug added a commit that referenced this pull request Jun 18, 2026
…p zero-unit-test crates (#5413)

The #5411 fast per-PR path built every affected crate's unit-test binary in one
unbounded-parallel invocation. Each links the whole runtime statically, so a
wide diff (~50 crates for a perry-runtime change) OOMed the runner at link
('linking with cc failed') — the failure the FULL path avoids via
CARGO_BUILD_JOBS=1. Surfaced on #5402.

Fast path now: CARGO_BUILD_JOBS=1 (serialize heavy links) + filter scope through
ci_test_scope.py --with-tests (drop crates with no src/ unit tests, whose lib
test binary links the runtime for zero tests).

Co-authored-by: Ralph Küpper <ralph2@skelpo.com>
proggeramlug pushed a commit that referenced this pull request Jun 18, 2026
…dules (#5389 Tier 2)

A computed require(expr) in a compiled external/compilePackages module whose
specifier const-folds to a finite set (ternary of literals, module-const, or a
directory-anchored template glob) now resolves synchronously to the target
compiled-module namespace, reusing the dynamic import() path resolver.

- New synchronous: bool field on Expr::DynamicImport (false for import(), true
  for require()).
- try_dynamic_require lowers a bare non-literal require in external modules to a
  synchronous DynamicImport; literals stay on try_require_literal.
- collect_modules const-folds/globs paths unchanged; an unresolved synchronous
  node records an empty path set (ambient fallback) instead of import()'s
  deferred-reject/strict error.
- lower_dynamic_require emits the same string_equals dispatch as import() but
  returns the namespace value directly (no Promise) with the Tier 1 ambient
  require (js_module_ambient_require_apply) as the no-match fallthrough.

Genuinely runtime-computed package strings remain unsupported by design (same
AOT boundary as dynamic import()). Also restores the v0.5.1184 changelog entry
dropped from main during the #5411 merge (its code is on main).
proggeramlug added a commit that referenced this pull request Jun 18, 2026
…ims (#5415)

Two issues made #5411's fast path fail on foundational diffs (#5402):

1. Feature unification: one multi-package 'cargo test -p A -p B' invocation
   unifies perry-runtime features; a crate enabling e.g. fetch turns on
   perry-runtime's js_fetch_with_options reference, defined in a separate crate
   the other test binaries don't link -> undefined reference. Fix: run each
   crate in its own invocation (per-package), isolating feature sets.
2. Over-broad fan-out: a perry-runtime change closured into ~40 FFI shims
   (perry-ext-*/perry-stdlib), each a perry-runtime feature rebuild, for unit
   tests that don't exercise runtime internals. Fix: ci_test_scope.py no longer
   fans into perry-ext-*/perry-stdlib (_is_fanout_leaf); direct changes still
   select them. perry-runtime change now -> 4 crates, not ~50.

Validated locally end-to-end: green, ~2min warm.

Co-authored-by: Ralph Küpper <ralph2@skelpo.com>
proggeramlug pushed a commit that referenced this pull request Jun 18, 2026
…dules (#5389 Tier 2)

A computed require(expr) in a compiled external/compilePackages module whose
specifier const-folds to a finite set (ternary of literals, module-const, or a
directory-anchored template glob) now resolves synchronously to the target
compiled-module namespace, reusing the dynamic import() path resolver.

- New synchronous: bool field on Expr::DynamicImport (false for import(), true
  for require()).
- try_dynamic_require lowers a bare non-literal require in external modules to a
  synchronous DynamicImport; literals stay on try_require_literal.
- collect_modules const-folds/globs paths unchanged; an unresolved synchronous
  node records an empty path set (ambient fallback) instead of import()'s
  deferred-reject/strict error.
- lower_dynamic_require emits the same string_equals dispatch as import() but
  returns the namespace value directly (no Promise) with the Tier 1 ambient
  require (js_module_ambient_require_apply) as the no-match fallthrough.

Genuinely runtime-computed package strings remain unsupported by design (same
AOT boundary as dynamic import()). Also restores the v0.5.1184 changelog entry
dropped from main during the #5411 merge (its code is on main).
proggeramlug pushed a commit that referenced this pull request Jun 18, 2026
…dules (#5389 Tier 2)

A computed require(expr) in a compiled external/compilePackages module whose
specifier const-folds to a finite set (ternary of literals, module-const, or a
directory-anchored template glob) now resolves synchronously to the target
compiled-module namespace, reusing the dynamic import() path resolver.

- New synchronous: bool field on Expr::DynamicImport (false for import(), true
  for require()).
- try_dynamic_require lowers a bare non-literal require in external modules to a
  synchronous DynamicImport; literals stay on try_require_literal.
- collect_modules const-folds/globs paths unchanged; an unresolved synchronous
  node records an empty path set (ambient fallback) instead of import()'s
  deferred-reject/strict error.
- lower_dynamic_require emits the same string_equals dispatch as import() but
  returns the namespace value directly (no Promise) with the Tier 1 ambient
  require (js_module_ambient_require_apply) as the no-match fallthrough.

Genuinely runtime-computed package strings remain unsupported by design (same
AOT boundary as dynamic import()). Also restores the v0.5.1184 changelog entry
dropped from main during the #5411 merge (its code is on main).
proggeramlug added a commit that referenced this pull request Jun 18, 2026
…dules (#5389 Tier 2) (#5402)

A computed require(expr) in a compiled external/compilePackages module whose
specifier const-folds to a finite set (ternary of literals, module-const, or a
directory-anchored template glob) now resolves synchronously to the target
compiled-module namespace, reusing the dynamic import() path resolver.

- New synchronous: bool field on Expr::DynamicImport (false for import(), true
  for require()).
- try_dynamic_require lowers a bare non-literal require in external modules to a
  synchronous DynamicImport; literals stay on try_require_literal.
- collect_modules const-folds/globs paths unchanged; an unresolved synchronous
  node records an empty path set (ambient fallback) instead of import()'s
  deferred-reject/strict error.
- lower_dynamic_require emits the same string_equals dispatch as import() but
  returns the namespace value directly (no Promise) with the Tier 1 ambient
  require (js_module_ambient_require_apply) as the no-match fallthrough.

Genuinely runtime-computed package strings remain unsupported by design (same
AOT boundary as dynamic import()). Also restores the v0.5.1184 changelog entry
dropped from main during the #5411 merge (its code is on main).

Co-authored-by: Ralph Küpper <ralph2@skelpo.com>
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.

1 participant