Skip to content

Commit fde4ee8

Browse files
committed
Flatten unmonorepo public module surface
1 parent aafc860 commit fde4ee8

211 files changed

Lines changed: 1574 additions & 2088 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/content/docs/contributing/api-modelling.mdx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,7 @@ mutable fillStyle: fillStyle
130130
When we wish to read and write the `fillStyle` property, we can use a helper module to lift the type to an actual ReScript variant:
131131

132132
export const fillStyleModule = `
133-
open WebAPI.Canvas
134-
open WebAPI.DOM
133+
open WebAPI
135134
136135
external fromString: string => fillStyle = "%identity"
137136
external fromCanvasGradient: canvasGradient => fillStyle = "%identity"

docs/content/docs/philosophy.mdx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@ The bindings are generated from the [MDN Web API documentation](https://develope
1616

1717
In other words, if you are searching for a specific JavaScript binding, begin your journey at the [MDN Web API documentation](https://developer.mozilla.org/en-US/docs/Web/API) and determine which module contains your sample. Ensure that the module is available in the bindings by checking the specific API. Please [open an issue](https://github.com/rescript-lang/experimental-rescript-webapi/issues/new/choose) if you require an API that is not yet present.
1818

19-
The bindings are exposed under the `WebAPI` namespace, with feature entry modules such as `WebAPI.DOM` available when you want to scope imports more narrowly.
19+
The bindings are exposed under the `WebAPI` namespace with the same flat module structure as the original package.
2020

2121
```ReScript
22-
open WebAPI.Global
23-
// or WebAPI.DOM
22+
open WebAPI.DomGlobal
2423
2524
let myElement: WebAPI.Element.t = document->WebAPI.Document.createElement("div")
2625
```
@@ -44,8 +43,7 @@ JavaScript supports function overloads, where a function can have multiple signa
4443
In some cases, type conversion will be required. Subtypes can safely be cast to their base type using conversion helpers within their module.
4544

4645
```ReScript
47-
open WebAPI.Global
48-
// or WebAPI.DOM
46+
open WebAPI.DomGlobal
4947
5048
let element: WebAPI.Element.t = document->WebAPI.Document.createElement("div")
5149
let node: WebAPI.Node.t = element->WebAPI.Element.asNode

docs/llm.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as path from "node:path";
22
import { exec } from "node:child_process";
33
import { promisify } from "node:util";
44
import fs from "node:fs/promises";
5-
import { featureSpecs, publicNameForLeafModule } from "../scripts/unmonorepo/feature-spec.mjs";
5+
import { featureSpecs } from "../scripts/unmonorepo/feature-spec.mjs";
66

77
const execAsync = promisify(exec);
88

@@ -107,11 +107,7 @@ function moduleNameForFile(relativePath) {
107107

108108
const leafName = path.basename(fileName, ".res");
109109

110-
if (leafName === spec.publicModule) {
111-
return `WebAPI.${spec.publicModule}`;
112-
}
113-
114-
return `WebAPI.${spec.publicModule}.${publicNameForLeafModule(leafName, spec.internalPrefix)}`;
110+
return `WebAPI.${leafName}`;
115111
}
116112

117113
const pattern = "../src/*/**/*.res";

docs/superpowers/specs/2026-04-22-unmonorepo-webapi-design.md

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@
77

88
The repository was split into npm workspaces so each Web API area could build and publish independently from `packages/*`. The ReScript compiler now supports feature-gated source directories and feature-gated dependencies, so the package split is no longer required to support partial builds.
99

10-
The new target is a single published npm package, `@rescript/webapi`, with a unified internal build and an external API that still preserves feature-level boundaries such as `WebAPI.DOM`, `WebAPI.Fetch`, and `WebAPI.Base`.
10+
The new target is a single published npm package, `@rescript/webapi`, with a unified internal build and feature-gated source groups such as `WebAPI.DOM`, `WebAPI.Fetch`, and `WebAPI.Base`.
1111

1212
## Goals
1313

1414
- Remove the monorepo/workspace package split.
1515
- Move all feature sources from `packages/<Pkg>/src` to `src/<Pkg>`.
1616
- Delete every subpackage `package.json` and `rescript.json`.
1717
- Make the root `rescript.json` the single source of truth for sources, features, and ReScript dependencies.
18-
- Preserve the logical public API boundaries as `WebAPI.<Feature>`.
19-
- Keep internal helper modules private through the new `public` source setting.
18+
- Preserve the original flat public API module shape.
19+
- Keep feature boundaries in the build configuration rather than generated wrapper modules.
2020
- Publish one npm package: `@rescript/webapi`.
2121
- Let internal builds and downstream consumers compile only the features they need.
2222

@@ -62,14 +62,12 @@ Each former package source directory is listed as its own source entry. Example
6262
{
6363
"dir": "src/DOM",
6464
"subdirs": true,
65-
"feature": "WebAPI.DOM",
66-
"public": ["DOM"]
65+
"feature": "WebAPI.DOM"
6766
},
6867
{
6968
"dir": "src/Fetch",
7069
"subdirs": true,
71-
"feature": "WebAPI.Fetch",
72-
"public": ["Fetch"]
70+
"feature": "WebAPI.Fetch"
7371
},
7472
{
7573
"dir": "tests",
@@ -84,8 +82,7 @@ Rules:
8482

8583
- Every former package gets one root source entry.
8684
- The `feature` value matches the public module name, for example `"WebAPI.DOM"` and `"WebAPI.Fetch"`.
87-
- The `public` list exposes only the feature entry module for that source directory.
88-
- Helper modules such as `DomTypes` and `DomGlobal` stay internal because they are not listed in `public`.
85+
- Source directories expose their modules directly, matching the original plain package structure.
8986
- `tests` remains a dev-only source.
9087

9188
### Dependencies
@@ -112,23 +109,22 @@ Migration rule:
112109

113110
## Public API Shape
114111

115-
The public API remains feature-oriented:
112+
The build remains feature-oriented:
116113

117114
- `WebAPI.Base`
118115
- `WebAPI.DOM`
119116
- `WebAPI.Fetch`
120117
- `WebAPI.WebCrypto`
121118
- and the rest of the former package surfaces
122119

123-
The unified build must not expose raw internal file modules as first-class public API. Consumers should interact with a curated surface through the feature entry modules only.
120+
The unified build keeps the original flat module surface instead of adding generated feature entry modules. For example, consumers should use modules such as:
124121

125-
Each feature directory therefore needs one public entry module whose filename matches the feature name:
122+
- `WebAPI.Document`
123+
- `WebAPI.Element`
124+
- `WebAPI.Headers`
125+
- `WebAPI.URL`
126126

127-
- `src/DOM/DOM.res`
128-
- `src/Fetch/Fetch.res`
129-
- `src/Base/Base.res`
130-
131-
Those entry modules are the only modules exported from their source directory through `public`.
127+
Shared DOM base types should be owned by `DOM`, so common references stay short, for example `DOM.element` instead of `BaseDOM.element` or `Base.DOM.element`.
132128

133129
## Internal Module Naming
134130

@@ -144,6 +140,7 @@ In a unified package these names would collide, so the migration must rename gen
144140

145141
Examples:
146142

143+
- `src/Base/DOM.res` -> `src/Base/DOM.res`
147144
- `src/DOM/Types.res` -> `src/DOM/DomTypes.res`
148145
- `src/DOM/Global.res` -> `src/DOM/DomGlobal.res`
149146
- `src/Fetch/Global.res` -> `src/Fetch/FetchGlobal.res`
@@ -153,7 +150,7 @@ Examples:
153150
Naming rule:
154151

155152
- Prefix internal modules with the feature’s public module stem.
156-
- Keep the public entry module itself unprefixed when it is the exported surface module, for example `DOM.res` and `Fetch.res`.
153+
- Keep legacy same-name public modules unprefixed when the feature owns that name, for example `src/URL/URL.res`, `src/Event/Event.res`, and `src/File/File.res`.
157154

158155
This keeps the public API stable while making the unified internal module graph collision-free.
159156

@@ -196,8 +193,7 @@ The build becomes unified at the repository level:
196193
1. ReScript reads the root `rescript.json`.
197194
2. Each feature source directory is included in the build graph.
198195
3. Feature gating determines which source directories and gated dependencies participate in a given build.
199-
4. Only the explicitly public feature entry modules are exposed to consumers.
200-
5. Internal helper modules remain compile-time implementation details.
196+
4. The original flat public modules are exposed directly to consumers.
201197

202198
This provides one coherent package without losing the ability to compile narrower slices of the API surface.
203199

@@ -240,4 +236,4 @@ The most important regression risks are:
240236
- Use `WebAPI.*` spelling, not `WebApi.*`.
241237
- Map feature names to public module names such as `"WebAPI.DOM"`.
242238
- Rename generic internal modules to feature-qualified names.
243-
- Use source-level `public` settings so only the feature entry module is exposed.
239+
- Keep feature-gated source directories, but do not generate feature entry modules.

docs/utils.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { exec } from "node:child_process";
33
import { promisify } from "node:util";
44
import { readdirSync, existsSync, readFileSync } from "fs";
55
import { micromark } from "micromark";
6-
import { featureSpecs, publicNameForLeafModule } from "../scripts/unmonorepo/feature-spec.mjs";
6+
import { featureSpecs } from "../scripts/unmonorepo/feature-spec.mjs";
77

88
const execAsync = promisify(exec);
99

@@ -32,15 +32,14 @@ function mapTypeModules(parentModuleLink, file, spec) {
3232
}
3333

3434
const typesFileName = `${spec.internalPrefix}Types.res`;
35-
const entryFileName = `${spec.publicModule}.res`;
3635
const files = readdirSync(folder);
3736
return files
38-
.filter((f) => f.endsWith(".res") && f !== typesFileName && f !== entryFileName)
37+
.filter((f) => f.endsWith(".res") && f !== typesFileName)
3938
.map((file) => {
4039
const filePath = path.join(folder, file);
4140

4241
const leafName = file.replace("$", "").replace(".res", "");
43-
const moduleName = publicNameForLeafModule(leafName, spec.internalPrefix);
42+
const moduleName = leafName;
4443
const apiRouteParameter = toKebabCase(moduleName);
4544
const link = createTypeModuleLink(parentModuleLink, moduleName);
4645
const typeName = moduleName[0].toLocaleLowerCase() + moduleName.slice(1);

0 commit comments

Comments
 (0)