|
| 1 | +# Features |
| 2 | + |
| 3 | +Features let a package declare optional parts of its source tree that can be included or excluded at build time. Use them to ship a library with optional backends, experimental modules, or platform-specific code without paying the compile cost when a consumer doesn't need them. |
| 4 | + |
| 5 | +Features are a `rewatch` extension and are not part of the legacy `bsb` build-configuration spec. |
| 6 | + |
| 7 | +## Tagging a source directory |
| 8 | + |
| 9 | +Add a `feature` property to any entry in `sources`. The directory is included in the build only when the feature is active for that package. |
| 10 | + |
| 11 | +```json |
| 12 | +{ |
| 13 | + "name": "@example/lib", |
| 14 | + "sources": [ |
| 15 | + { "dir": "src" }, |
| 16 | + { "dir": "src-native", "feature": "native" }, |
| 17 | + { "dir": "src-experimental", "feature": "experimental" } |
| 18 | + ] |
| 19 | +} |
| 20 | +``` |
| 21 | + |
| 22 | +Untagged source directories (no `feature`) are always compiled. A tagged source's `feature` cascades down into nested `subdirs`: a child that doesn't declare its own `feature` inherits the parent's. |
| 23 | + |
| 24 | +## Declaring feature relationships |
| 25 | + |
| 26 | +A top-level `features` map declares names and optional implications. Listing a feature here is only required when you want one feature to imply another; leaf features can stay undeclared and still work as source-dir tags. |
| 27 | + |
| 28 | +```json |
| 29 | +{ |
| 30 | + "features": { |
| 31 | + "full": ["native", "experimental"] |
| 32 | + } |
| 33 | +} |
| 34 | +``` |
| 35 | + |
| 36 | +With the above, requesting `full` transitively enables `native` and `experimental`. Cycles (e.g. `a -> b -> a`) are rejected at build time with a clear error. |
| 37 | + |
| 38 | +## Selecting features on the command line |
| 39 | + |
| 40 | +When you run `rewatch build` or `rewatch watch`, pass `--features` to restrict compilation to a specific set. Without the flag, every feature is active and the whole source tree compiles: |
| 41 | + |
| 42 | +``` |
| 43 | +rewatch build # all features active (default) |
| 44 | +rewatch build --features native # only untagged + native |
| 45 | +rewatch build --features native,full # multiple features; also expands `full` |
| 46 | +``` |
| 47 | + |
| 48 | +The CLI flag applies only to the **current package** — the one you're building from. It does not flow down to dependencies; each dependency's active feature set comes from its consumer declarations (see below). |
| 49 | + |
| 50 | +Passing an empty value (`--features ""` or `--features ,`) is rejected. Omit the flag to mean "all features". |
| 51 | + |
| 52 | +## Restricting a dependency's features |
| 53 | + |
| 54 | +When consuming another ReScript package that uses features, switch the entry in `dependencies` or `dev-dependencies` from the shorthand string to an object form and list which features you want: |
| 55 | + |
| 56 | +```json |
| 57 | +{ |
| 58 | + "dependencies": [ |
| 59 | + "@plain/dep", |
| 60 | + { "name": "@example/lib", "features": ["native"] } |
| 61 | + ] |
| 62 | +} |
| 63 | +``` |
| 64 | + |
| 65 | +Rules: |
| 66 | + |
| 67 | +- **Shorthand (`"@plain/dep"`)** — the consumer wants every feature of that dependency. This is the existing behavior; nothing changes for configs that don't opt into features. |
| 68 | +- **Object with `features`** — the consumer restricts the dependency to the listed features (and whatever they transitively imply through the dependency's own `features` map). An explicit empty list (`"features": []`) means "only untagged source dirs, no feature-gated code". |
| 69 | +- **Object without `features`** — equivalent to the shorthand. All features active. |
| 70 | + |
| 71 | +When the same dependency is referenced by multiple consumers with different feature sets, the union of requests wins. If any consumer asks for all features, the dependency builds with all of its features. Features are always additive — enabling more features never removes modules, so the union is always safe. |
| 72 | + |
| 73 | +## Interaction with other flags |
| 74 | + |
| 75 | +- **`type: "dev"` and `--prod`** are orthogonal to features. A source directory may declare both `type: "dev"` and a `feature`; it will only build when both filters pass (not in `--prod`, and the feature is active). |
| 76 | +- **`rewatch clean`** ignores `--features` and always cleans the full set of build artifacts across every feature-gated directory. This keeps `clean` predictable regardless of which features happen to be active. |
| 77 | + |
| 78 | +## How incremental builds handle feature changes |
| 79 | + |
| 80 | +Toggling a feature off between builds removes its source files from the build's view. The next `rewatch build` sees the shrunken file set and cleans up the corresponding artifacts (`.mjs`, `.cmj`, etc.) through the same diff mechanism that handles deleted source files. |
| 81 | + |
| 82 | +For `rewatch watch`, a change to `features` in `rescript.json` triggers a full rebuild that recomputes the active set and re-registers watches on the active source directories. The CLI `--features` flag is evaluated once at watcher start; to change it you must restart the watcher. |
| 83 | + |
| 84 | +## Validating feature names |
| 85 | + |
| 86 | +- Unknown feature names in CLI input or source tags are accepted as leaf features (they simply match nothing unless a source directory is tagged with that exact name). |
| 87 | +- Cycles in the top-level `features` map are a hard error that names the cycle participants. |
| 88 | + |
| 89 | +## Example |
| 90 | + |
| 91 | +```json |
| 92 | +{ |
| 93 | + "name": "@example/lib", |
| 94 | + "sources": [ |
| 95 | + { "dir": "src" }, |
| 96 | + { "dir": "src-native", "feature": "native" }, |
| 97 | + { "dir": "src-web", "feature": "web" }, |
| 98 | + { "dir": "src-experimental", "feature": "experimental" } |
| 99 | + ], |
| 100 | + "features": { |
| 101 | + "all-backends": ["native", "web"] |
| 102 | + }, |
| 103 | + "dependencies": [ |
| 104 | + "@plain/dep", |
| 105 | + { "name": "@other/heavy", "features": ["native"] } |
| 106 | + ] |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +- `rewatch build` at `@example/lib` — compiles every source dir; `@other/heavy` builds with just its `native` feature because that's all the consumer requested; `@plain/dep` builds with all of its features. |
| 111 | +- `rewatch build --features all-backends` — compiles `src`, `src-native`, `src-web`; skips `src-experimental`. |
| 112 | +- `rewatch build --features experimental` — compiles `src`, `src-experimental`; skips the backends. |
0 commit comments