Skip to content

Commit 12988fa

Browse files
committed
fix: keep DOM globals internal
1 parent c45a824 commit 12988fa

7 files changed

Lines changed: 73 additions & 12 deletions

File tree

docs/content/docs/philosophy.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ In other words, if you are searching for a specific JavaScript binding, begin yo
1919
The bindings are exposed under the `WebAPI` namespace with the same flat module structure as the original package.
2020

2121
```ReScript
22-
open WebAPI.DomGlobal
22+
open WebAPI.DOM
2323
2424
let myElement: WebAPI.Element.t = document->WebAPI.Document.createElement("div")
2525
```
@@ -43,7 +43,7 @@ JavaScript supports function overloads, where a function can have multiple signa
4343
In some cases, type conversion will be required. Subtypes can safely be cast to their base type using conversion helpers within their module.
4444

4545
```ReScript
46-
open WebAPI.DomGlobal
46+
open WebAPI.DOM
4747
4848
let element: WebAPI.Element.t = document->WebAPI.Document.createElement("div")
4949
let node: WebAPI.Node.t = element->WebAPI.Element.asNode

docs/llm.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,38 @@ Module: ${moduleName}${typeString}${functionString}
9696
}
9797

9898
const specByDir = new Map(featureSpecs.map((spec) => [spec.dirName, spec]));
99+
const rootDir = path.join(import.meta.dirname, "..");
100+
const rootConfig = JSON.parse(await fs.readFile(path.join(rootDir, "rescript.json"), "utf-8"));
101+
const publicModulesBySourceDir = new Map(
102+
rootConfig.sources
103+
.filter((source) => typeof source === "object")
104+
.filter((source) => source.dir?.startsWith("src/") && Array.isArray(source.public))
105+
.map((source) => [source.dir, new Set(source.public)]),
106+
);
107+
108+
function normalizeRelativePath(filePath) {
109+
return path.relative(rootDir, filePath).split(path.sep).join("/");
110+
}
111+
112+
function sourceDirForRelativePath(relativePath) {
113+
for (const sourceDir of publicModulesBySourceDir.keys()) {
114+
if (relativePath.startsWith(`${sourceDir}/`)) {
115+
return sourceDir;
116+
}
117+
}
118+
}
119+
120+
function isPublicFile(filePath) {
121+
const relativePath = normalizeRelativePath(filePath);
122+
const sourceDir = sourceDirForRelativePath(relativePath);
123+
124+
if (!sourceDir) {
125+
return false;
126+
}
127+
128+
const moduleName = path.basename(relativePath, ".res").replace("$", "");
129+
return publicModulesBySourceDir.get(sourceDir).has(moduleName);
130+
}
99131

100132
function moduleNameForFile(relativePath) {
101133
const [, dirName, fileName] = relativePath.split(path.sep);
@@ -113,7 +145,11 @@ function moduleNameForFile(relativePath) {
113145
const pattern = "../src/*/**/*.res";
114146
const files = [];
115147
for await (const file of fs.glob(pattern, { recursive: true, cwd: import.meta.dirname })) {
116-
files.push(path.join(import.meta.dirname, file));
148+
const filePath = path.join(import.meta.dirname, file);
149+
150+
if (isPublicFile(filePath)) {
151+
files.push(filePath);
152+
}
117153
}
118154
files.sort();
119155

docs/utils.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ import { micromark } from "micromark";
66
import { featureSpecs } from "../scripts/unmonorepo/feature-spec.mjs";
77

88
const execAsync = promisify(exec);
9+
const rootDir = process.cwd();
10+
const rootConfig = JSON.parse(readFileSync(path.join(rootDir, "rescript.json"), "utf8"));
11+
const publicModulesBySourceDir = new Map(
12+
rootConfig.sources
13+
.filter((source) => typeof source === "object")
14+
.filter((source) => source.dir?.startsWith("src/") && Array.isArray(source.public))
15+
.map((source) => [source.dir, new Set(source.public)]),
16+
);
917

1018
function toKebabCase(input) {
1119
return input
@@ -31,10 +39,12 @@ function mapTypeModules(parentModuleLink, file, spec) {
3139
return [];
3240
}
3341

42+
const publicModules = publicModulesBySourceDir.get(spec.sourceDir) ?? new Set();
3443
const typesFileName = `${spec.internalPrefix}Types.res`;
3544
const files = readdirSync(folder);
3645
return files
3746
.filter((f) => f.endsWith(".res") && f !== typesFileName)
47+
.filter((file) => publicModules.has(file.replace("$", "").replace(".res", "")))
3848
.map((file) => {
3949
const filePath = path.join(folder, file);
4050

rescript.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@
9696
"Document",
9797
"DocumentFragment",
9898
"DocumentTimeline",
99-
"DomGlobal",
10099
"DomHTMLMediaElement",
101100
"Element",
102101
"ElementInternals",

scripts/unmonorepo/write-root-config.mjs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@ import fs from "node:fs";
22
import path from "node:path";
33
import { featureSpecs } from "./feature-spec.mjs";
44

5-
export function publicModulesForSourceDir(sourceDir) {
5+
export function isPublicModuleName(spec, moduleName) {
6+
if (moduleName.endsWith("Types")) return false;
7+
if (spec.dirName === "DOM" && moduleName === "DomGlobal") return false;
8+
9+
return true;
10+
}
11+
12+
export function publicModulesForSourceDir(sourceDir, spec) {
613
if (!fs.existsSync(sourceDir)) return [];
714

815
const publicModules = new Set();
@@ -14,7 +21,7 @@ export function publicModulesForSourceDir(sourceDir) {
1421
if (extension !== ".res" && extension !== ".resi") continue;
1522

1623
const moduleName = path.basename(entry.name, extension);
17-
if (moduleName.endsWith("Types")) continue;
24+
if (!isPublicModuleName(spec, moduleName)) continue;
1825

1926
publicModules.add(moduleName);
2027
}
@@ -30,7 +37,7 @@ export function buildRootRescriptJson(specs) {
3037
dir: `src/${spec.dirName}`,
3138
subdirs: true,
3239
feature: spec.featureName,
33-
public: (spec.publicModules ?? []).filter((moduleName) => !moduleName.endsWith("Types")),
40+
public: (spec.publicModules ?? []).filter((moduleName) => isPublicModuleName(spec, moduleName)),
3441
})),
3542
{
3643
dir: "tests",
@@ -67,7 +74,7 @@ export function writeRootConfig(rootDir) {
6774
const currentPackage = JSON.parse(fs.readFileSync(packagePath, "utf8"));
6875
const specsWithPublicModules = featureSpecs.map((spec) => ({
6976
...spec,
70-
publicModules: publicModulesForSourceDir(path.join(rootDir, spec.sourceDir)),
77+
publicModules: publicModulesForSourceDir(path.join(rootDir, spec.sourceDir), spec),
7178
}));
7279

7380
fs.writeFileSync(rescriptPath, `${JSON.stringify(buildRootRescriptJson(specsWithPublicModules), null, 2)}\n`);

tests/unmonorepo/release-files.test.mjs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,21 @@ test("docs and CI point at the unified package layout", () => {
2020
assert.match(docsIndex, /ReScript WebAPI/);
2121
assert.match(docsIndex, /WebAPI\.Global/);
2222
assert.match(docsIndex, /location->WebAPI\.Location\.reload/);
23-
assert.match(docsPhilosophy, /open WebAPI\.DomGlobal/);
23+
assert.match(docsPhilosophy, /open WebAPI\.DOM/);
24+
assert.doesNotMatch(docsPhilosophy, /WebAPI\.DomGlobal/);
2425
assert.match(docsPhilosophy, /let myElement: WebAPI\.Element\.t = document->WebAPI\.Document\.createElement/);
2526
assert.match(docsPhilosophy, /let node: WebAPI\.Node\.t = element->WebAPI\.Element\.asNode/);
2627
assert.match(docsContributing, /open WebAPI/);
2728
assert.doesNotMatch(docsContributing, /open WebAPI\.DOM/);
2829
assert.match(designSpec, /original flat public API module shape/);
2930
assert.match(designSpec, /Use `WebAPI\.\*` spelling, not `WebApi\.\*`\./);
30-
assert.match(docsLlm, /\.\.\/src\/\*\/\*\*\/\*\.res/);
31-
assert.match(docsLlm, /WebAPI\.\$\{leafName\}/);
31+
assert.match(docsLlm, /rescript\.json/);
32+
assert.match(docsLlm, /source\.public/);
33+
assert.match(docsLlm, /isPublicFile\(filePath\)/);
3234
assert.match(docsUtils, /path\.resolve\(process\.cwd\(\), "src"\)/);
35+
assert.match(docsUtils, /rescript\.json/);
36+
assert.match(docsUtils, /source\.public/);
37+
assert.match(docsUtils, /publicModules\.has/);
3338
assert.doesNotMatch(docsUtils, /path\.resolve\(process\.cwd\(\), "packages"\)/);
3439
assert.match(workflow, /npm pack\b/);
3540
assert.match(workflow, /npm publish --access public --tag experimental/);
@@ -50,4 +55,8 @@ test("root rescript.json keeps generated type modules internal", () => {
5055
`${source.dir} should not expose *Types modules`,
5156
);
5257
}
58+
59+
const domSource = sourceEntries.find((source) => source.dir === "src/DOM");
60+
assert.ok(domSource, "src/DOM source entry should exist");
61+
assert.ok(!domSource.public.includes("DomGlobal"), "src/DOM should keep DomGlobal internal");
5362
});

tests/unmonorepo/write-root-config.test.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ test("builds the unified root rescript.json and package.json", () => {
1212
dirName: "DOM",
1313
publicModule: "DOM",
1414
featureName: "WebAPI.DOM",
15-
publicModules: ["Document", "DomTypes", "Element"],
15+
publicModules: ["Document", "DomGlobal", "DomTypes", "Element"],
1616
},
1717
];
1818

0 commit comments

Comments
 (0)