Skip to content

Rewatch: feature-gated source directories#8379

Merged
cknitt merged 3 commits intorescript-lang:masterfrom
jfrolich:features-source-tags
Apr 22, 2026
Merged

Rewatch: feature-gated source directories#8379
cknitt merged 3 commits intorescript-lang:masterfrom
jfrolich:features-source-tags

Conversation

@jfrolich
Copy link
Copy Markdown
Collaborator

Summary

  • Add a feature tag on sources entries so packages can ship optional slices of their source tree. A top-level features map declares transitive implications, and dependencies / dev-dependencies accept a new object form {name, features} so consumers can restrict which features of a dep get built.
  • CLI: rewatch build --features=a,b (and watch) restricts the current package. Dependencies inherit from consumer declarations in rescript.json; multiple consumers union. Omitting the flag keeps all features active. Empty --features= is rejected with guidance.
  • Filtering runs in packages::make before source discovery, so the watcher naturally skips disabled dirs and the existing diff-based cleanup removes orphan artifacts when a feature is toggled off. clean intentionally ignores --features and wipes the full tree.
  • Cycles in the features map fail fast at load time with a message naming the participants.
  • Docs: rewatch/Features.md (dedicated), plus a row in rewatch/CompilerConfigurationSpec.md.

Why

Lets a library author gate backend- or platform-specific code behind an opt-in flag without paying the compile cost for consumers who don't need it — and lets consumers restrict heavy deps to only the features they use.

Test plan

  • cargo build --manifest-path rewatch/Cargo.toml
  • cargo clippy --manifest-path rewatch/Cargo.toml --all-targets --all-features — no warnings
  • cargo fmt --check --manifest-path rewatch/Cargo.toml — clean
  • cargo test --manifest-path rewatch/Cargo.toml --lib — 89 passing (21 new)
  • make test-rewatch — full suite including 6 new feature tests under rewatch/tests/features/ (104 assertions pass, 0 fail)

Open follow-ups (not in this PR)

  • Editor tooling (rescript-editor-analysis) doesn't yet know about features. Feature-gated dirs are still visible to the LSP, so users can jump-to-def into code that won't compile under their current feature set — same UX as type: "dev" today.
  • Feature-gated dependencies (as opposed to feature-gated source dirs within a dep) are out of scope.

🤖 Generated with Claude Code

@cknitt
Copy link
Copy Markdown
Member

cknitt commented Apr 21, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 767aca324e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rewatch/src/build/packages.rs Outdated
Comment thread rewatch/src/build/packages.rs Outdated
Comment on lines +702 to +703
if !any_all_request && requested.is_empty() {
any_all_request = true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve empty feature lists instead of expanding to all features

This fallback treats an empty requested set as all features, which breaks the qualified dependency form when a consumer explicitly sets "features": []. In that case no features are requested, but requested remains empty and this branch forces any_all_request = true, enabling all feature-tagged source dirs for that dependency. That makes it impossible to express an untagged-only dependency build despite using the restrictive object form.

Useful? React with 👍 / 👎.

jfrolich and others added 2 commits April 22, 2026 14:13
Add a `feature` tag on source entries so a package can ship optional
slices of its source tree that consumers opt into. The new top-level
`features` map declares transitive implications, and `dependencies` /
`dev-dependencies` accept an object form `{name, features}` so a
consumer can restrict which features of a dep get built.

On the CLI, `rewatch build --features=a,b` (and the same on `watch`)
restricts the current package; dependencies inherit from consumer
declarations in rescript.json (union across consumers). Omitting the
flag keeps all features active.

Filtering runs inside `packages::make` before source files are
discovered, so the watcher automatically skips disabled directories and
the existing diff-based cleanup removes orphan artifacts when a feature
is toggled off. `clean` intentionally ignores `--features` so it
always wipes the full tree.

Cycles in the `features` map are rejected at load time with a message
naming the participants. Empty `--features=` is rejected with guidance
to omit the flag instead.

Includes unit tests for parsing, closure resolution, cycle detection,
and per-package active-set computation; six bash integration tests
covering CLI restriction, transitive expansion, artifact cleanup on
toggle, cycle errors, and empty-flag rejection. Documentation in
rewatch/Features.md and a row added to
rewatch/CompilerConfigurationSpec.md.

Signed-off-by: Jaap Frolich <jaap@tella.com>

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Jaap Frolich <jfrolich@gmail.com>
compute_active_features scanned both dependencies and dev-dependencies
on every consumer regardless of the active build mode. In --prod, a
dep reached via dependencies with a restricted feature list could still
have a shorthand dev-dependencies entry on the same consumer flip
any_all_request = true and force all features active — pulling in
feature-gated code that --prod is supposed to exclude.

Match read_dependencies's rule: dev-dependency edges only contribute
to a dep's active feature set when the consumer is local and we're not
in --prod. Regression test covers both modes.

Signed-off-by: Jaap Frolich <jaap@tella.com>

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Jaap Frolich <jfrolich@gmail.com>
@jfrolich jfrolich force-pushed the features-source-tags branch 2 times, most recently from 36b09f3 to 025f146 Compare April 22, 2026 12:38
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 22, 2026

Open in StackBlitz

rescript

npm i https://pkg.pr.new/rescript@8379

@rescript/darwin-arm64

npm i https://pkg.pr.new/@rescript/darwin-arm64@8379

@rescript/darwin-x64

npm i https://pkg.pr.new/@rescript/darwin-x64@8379

@rescript/linux-arm64

npm i https://pkg.pr.new/@rescript/linux-arm64@8379

@rescript/linux-x64

npm i https://pkg.pr.new/@rescript/linux-x64@8379

@rescript/runtime

npm i https://pkg.pr.new/@rescript/runtime@8379

@rescript/win32-x64

npm i https://pkg.pr.new/@rescript/win32-x64@8379

commit: 6de16a6

compute_active_features conflated "no consumer edge found" with
"consumer requested empty". A qualified dependency with
`"features": []` would land in the same state as an unreached dep
(requested set empty, no `any_all_request`) and trip the fallback that
forced all features active. That made it impossible to express an
untagged-only dependency build.

Track `saw_consumer_entry` explicitly. The all-features fallback now
fires only when no consumer edge was observed; an empty requested set
from an explicit `"features": []` is honoured as "no feature-gated
dirs". Regression test covers the explicit-empty case. Docs updated.

Signed-off-by: Jaap Frolich <jaap@tella.com>

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Jaap Frolich <jfrolich@gmail.com>
@jfrolich jfrolich force-pushed the features-source-tags branch from 025f146 to 6de16a6 Compare April 22, 2026 13:04
@cknitt cknitt merged commit 4f65236 into rescript-lang:master Apr 22, 2026
26 checks passed
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.

2 participants