Skip to content

Commit 7c3360c

Browse files
authored
feat: sync docs JS output from compiled examples (#1255)
* refactor: make test-examples importable * fix: restore test examples output * fix: make temp examples build writable * fix: handle spaced temp paths in test examples * feat: warn on stale JS output blocks * fix: limit JS output sync to example blocks Restrict CodeTab JS-output pairing to explicit res example fences so plain res snippets are not treated as runnable examples. Add a focused regression test for the pairing scanner and keep the design and implementation notes with the branch. * docs: add test examples update mode design Describe the line-oriented --update workflow for JS Output blocks in CodeTab examples. Document missing-fence insertion, single-label CodeTab upgrades, and README updates. * feat: add test examples update mode Implement --update in scripts/test-examples.mjs so JS Output fences can be refreshed, filled, or inserted for eligible CodeTab examples. Add integration coverage for stale, empty, and missing JS fences plus single-label and multi-label CodeTab behavior, and document the new command in the README. * fix: improve test examples compiler handling Invoke the local ReScript CLI directly instead of shelling through npm for each docs example build. Format compiler failures as cleaned ReScript errors with markdown file locations and add regression coverage for both behaviors. * docs: update generated JS output examples Commit the updated manual MDX files produced by the JS Output sync flow. This commit includes only the tracked docs example updates and leaves unrelated code and untracked files out. * docs: explain optimized JS output examples Clarify where ReScript precomputes pure expressions in the function, let-binding, and primitive-types docs. Also update the multi-argument function example to bind the computed result so the generated JS output matches the explanation. * more updates to improve example docs * fix: emit ESM JS output examples Configure the temp docs example project to compile with the repo's esmodule package spec and update the sync tests accordingly. Refresh the generated manual and React MDX JS Output blocks from CommonJS exports to ESM export syntax. * fix: refresh docs example output workflow Stop warning on stale JS output during read-only checks and keep --update as the rewrite path. Refresh generated JS output across the docs and rename colliding example-local types so the example checker passes again. * remove docs * docs: add ReScript CodeTab expansion design * add exports.root * docs: refine ReScript CodeTab expansion design * docs: revise ReScript fence checking design * docs: add default res fence migration plan * test: capture default res fence behavior * test: strengthen default res fence tests * test: tighten nocheck and ts output assertions * test: align codetab fixtures with new res contract * test: fix ts output skip assertion * test: prove nocheck fences are excluded from parse output * test: relax brittle res fence assertions * test: prove plain res discovery without preludes * feat: check plain res fences by default * fix: handle checked res codetabs * docs: migrate example fences to checked res Replace res example fences with plain res across the manual and React docs. Update the authoring guidance in README.md and AGENTS.md to describe the new default checked fence contract. * docs: migrate runnable examples to checked res fences Convert the remaining documentation examples from example-only fences to the new default checked res flow. Add nocheck and sig markers where examples are intentionally non-runnable, then refresh JS output with the updated checker. * remove commited plans * test: cover JSX preserved output codetabs * feat: add JSX preserved output codetabs * fix: rename generated example module Rename the temp example source from _tempFile to Example so derived JS output no longer leaks the temp filename. Refresh the JSX preserved-output docs that are in scope for the new three-tab ReScript CodeTab flow. * update gitignore
1 parent 2b469f3 commit 7c3360c

76 files changed

Lines changed: 6578 additions & 1656 deletions

Some content is hidden

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

.gitignore

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@ out/
1111
index_data/*.json
1212

1313
# Generated via test examples script
14-
_tempFile.cmi
15-
_tempFile.cmj
16-
_tempFile.cmt
17-
_tempFile.res
1814
temp
15+
temp-js-output
16+
temp-jsx-preserve
1917

2018
.bsb.lock
2119
.merlin

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ let default: unit => React.element
192192
- Documentation content is in `markdown-pages/` organized by section (blog, docs, community, syntax-lookup).
193193
- MDX is processed by `react-router-mdx` with remark/rehype plugins.
194194
- Custom MDX components are mapped in `app/routes/MdxRoute.res` (e.g. `<Info>`, `<Warn>`, `<CodeTab>`, `<Video>`).
195-
- Code examples in markdown use ` ```res example ` (runnable), ` ```res sig ` (signature), and ` ```res prelude ` (shared context).
195+
- Code examples in markdown use ` ```res ` (runnable), ` ```res sig ` (signature), and ` ```res prelude ` (shared context).
196196

197197
## Formatting & Git Hooks
198198

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,27 @@ Please be selective in pushing up changes to screenshots and only update files t
7272

7373
We check the validity of our code examples marked with:
7474

75-
- ` ```res example ` (ReScript code snippet)
75+
- ` ```res ` (ReScript code snippet)
7676
- ` ```res sig ` (signature)
7777
- ` ```res prelude ` (ReScript code snippet available for all subsequent code snippets)
7878

7979
Run the checks with:
8080

81+
```sh
82+
yarn test
83+
```
84+
85+
Refresh stale or missing JS Output fences with:
86+
87+
```sh
88+
yarn test --update
89+
```
90+
91+
If you only want to run the example checker directly, you can still use:
92+
8193
```sh
8294
node scripts/test-examples.mjs
95+
node scripts/test-examples.mjs --update
8396
```
8497

8598
### Markdown Hyperlink Tests

markdown-pages/docs/manual/array-and-list.mdx

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ Arrays are the main ordered data structure in ReScript. They can be randomly acc
1414

1515
<CodeTab labels={["ReScript", "JS Output"]}>
1616

17-
```res example
17+
```res
1818
let myArray = ["hello", "world", "how are you"]
1919
```
2020

2121
```js
22-
var myArray = ["hello", "world", "how are you"];
22+
let myArray = ["hello", "world", "how are you"];
23+
24+
export { myArray };
2325
```
2426

2527
</CodeTab>
@@ -34,7 +36,7 @@ Accessing items in an array will return an `option` and can be done like so:
3436

3537
<CodeTab labels={["ReScript", "JS Output"]}>
3638

37-
```res example
39+
```res
3840
let myArray = ["hello", "world", "how are you"]
3941
4042
let firstItem = myArray[0] // Some("hello")
@@ -43,11 +45,13 @@ let tenthItem = myArray->Array.get(10) // None
4345
```
4446

4547
```js
46-
var myArray = ["hello", "world", "how are you"];
48+
let myArray = ["hello", "world", "how are you"];
49+
50+
let firstItem = myArray[0];
4751

48-
var firstItem = myArray[0];
52+
let tenthItem = myArray[10];
4953

50-
var tenthItem = myArray[10];
54+
export { myArray, firstItem, tenthItem };
5155
```
5256

5357
</CodeTab>
@@ -58,7 +62,7 @@ Items in an array can be updated by assigning a value to an index or using a fun
5862

5963
<CodeTab labels={["ReScript", "JS Output"]}>
6064

61-
```res example
65+
```res
6266
let myArray = ["hello", "world", "how are you"]
6367
6468
myArray[0] = "hey" // now ["hey", "world", "how are you"]
@@ -69,13 +73,15 @@ myArray->Array.set(0, "bye") // ["bye", "world", "how are you", "?"]
6973
```
7074

7175
```js
72-
var myArray = ["hello", "world", "how are you"];
76+
let myArray = ["hello", "world", "how are you"];
7377

7478
myArray[0] = "hey";
7579

7680
myArray.push("?");
7781

7882
myArray[0] = "bye";
83+
84+
export { myArray };
7985
```
8086

8187
</CodeTab>
@@ -88,21 +94,25 @@ You can spread arrays of the same type into new arrays:
8894

8995
<CodeTab labels={["ReScript", "JS Output"]}>
9096

91-
```res example
97+
```res
9298
let y = [1, 2]
9399
let x = [4, 5, ...y]
94100
let x2 = [4, 5, ...y, 7, ...y]
95101
let x3 = [...y]
96102
```
97103

98104
```javascript
99-
var y = [1, 2];
105+
import * as Belt_Array from "@rescript/runtime/lib/es6/Belt_Array.js";
100106

101-
var x = [4, 5, ...y];
107+
let y = [1, 2];
102108

103-
var x2 = [4, 5, ...y, 7, ...y];
109+
let x = Belt_Array.concatMany([[4, 5], y]);
104110

105-
var x3 = [...y];
111+
let x2 = Belt_Array.concatMany([[4, 5], y, [7], y]);
112+
113+
let x3 = Belt_Array.concatMany([y]);
114+
115+
export { y, x, x2, x3 };
106116
```
107117

108118
</CodeTab>
@@ -118,21 +128,23 @@ ReScript provides a singly linked list too. Lists are:
118128

119129
<CodeTab labels={["ReScript", "JS Output"]}>
120130

121-
```res example
131+
```res
122132
let myList = list{1, 2, 3}
123133
```
124134

125135
```js
126-
var myList = {
136+
let myList = {
127137
hd: 1,
128138
tl: {
129139
hd: 2,
130140
tl: {
131141
hd: 3,
132-
tl: 0,
142+
tl: /* [] */ 0,
133143
},
134144
},
135145
};
146+
147+
export { myList };
136148
```
137149

138150
</CodeTab>
@@ -188,7 +200,7 @@ var anotherList = {
188200

189201
<CodeTab labels={["ReScript", "JS Output"]}>
190202

191-
```res example
203+
```res
192204
let message =
193205
switch myList {
194206
| list{} => "This list is empty"
@@ -198,18 +210,30 @@ let message =
198210
```
199211

200212
```js
213+
let myList = {
214+
hd: 1,
215+
tl: {
216+
hd: 2,
217+
tl: {
218+
hd: 3,
219+
tl: /* [] */ 0,
220+
},
221+
},
222+
};
223+
224+
let anotherList = {
225+
hd: 0,
226+
tl: myList,
227+
};
228+
201229
let message =
202230
myList !== 0
203-
? {
204-
hd: 2,
205-
tl: {
206-
hd: 3,
207-
tl: /* [] */ 0,
208-
},
209-
} !== 0
231+
? myList.tl !== 0
210232
? "The list two items and a potenial remainder of items"
211233
: "The head of the list is the string " + (1).toString()
212234
: "This list is empty";
235+
236+
export { myList, anotherList, message };
213237
```
214238

215239
</CodeTab>

markdown-pages/docs/manual/async-await.mdx

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ let logUserDetails = async (userId: string) => {
4747

4848
```js
4949
async function logUserDetails(userId) {
50-
var email = await GlobalAPI.fetchUserMail(userId);
51-
await GlobalAPI.sendAnalytics(
52-
"User details have been logged for " + userId + "",
53-
);
54-
console.log("Email address for user " + userId + ": " + email + "");
50+
let email = await GlobalAPI.fetchUserMail(userId);
51+
await GlobalAPI.sendAnalytics(`User details have been logged for ` + userId);
52+
console.log(`Email address for user ` + userId + `: ` + email);
5553
}
54+
55+
export { logUserDetails };
5656
```
5757

5858
</CodeTab>
@@ -83,7 +83,7 @@ let fetchUserMail: string => promise<string>
8383

8484
The same logic applies to type definitions in `.res` files:
8585

86-
```res example
86+
```res
8787
// function type
8888
type someAsyncFn = int => promise<int>
8989
@@ -105,7 +105,7 @@ let fetchData = async (userId: string): string => {
105105

106106
For completeness reasons, let's expand the full signature and inline type definitions in one code snippet:
107107

108-
```res
108+
```res nocheck
109109
// Note how the inline return type uses `string`, while the type definition uses `promise<string>`
110110
let fetchData: string => promise<string> = async (userId: string): string {
111111
await fetchUserMail(userId)
@@ -179,7 +179,7 @@ This can be done by wrapping your `await` calls in a new `{}` closure.
179179

180180
<CodeTab labels={["ReScript", "JS Output"]}>
181181

182-
```res example
182+
```res
183183
@val external fetchUserMail: string => promise<string> = "GlobalAPI.fetchUserMail"
184184
185185
let fetchData = async () => {
@@ -189,10 +189,12 @@ let fetchData = async () => {
189189
```
190190

191191
```js
192-
async function fetchData(param) {
193-
var mail = (await GlobalAPI.fetchUserMail("1234")).toUpperCase();
194-
console.log("All upper-cased mail: " + mail + "");
192+
async function fetchData() {
193+
let mail = (await GlobalAPI.fetchUserMail("1234")).toUpperCase();
194+
console.log(`All upper-cased mail: ` + mail);
195195
}
196+
197+
export { fetchData };
196198
```
197199

198200
</CodeTab>
@@ -205,7 +207,7 @@ Note how the original closure was removed in the final JS output. No extra alloc
205207

206208
<CodeTab labels={["ReScript", "JS Output"]}>
207209

208-
```res example
210+
```res
209211
@val external fetchUserMail: string => promise<string> = "GlobalAPI.fetchUserMail"
210212
211213
let fetchData = async () => {
@@ -221,15 +223,17 @@ let fetchData = async () => {
221223
```
222224

223225
```js
224-
async function fetchData(param) {
225-
var val;
226-
var val$1;
226+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
227+
228+
async function fetchData() {
229+
let val;
230+
let val$1;
227231
try {
228232
val = await GlobalAPI.fetchUserMail("user1");
229233
val$1 = await GlobalAPI.fetchUserMail("user2");
230234
} catch (raw_err) {
231-
var err = Caml_js_exceptions.internalToOCamlException(raw_err);
232-
if (err.RE_EXN_ID === "JsError") {
235+
let err = Primitive_exceptions.internalToException(raw_err);
236+
if (err.RE_EXN_ID === "JsExn") {
233237
console.log("Some error occurred", err._1);
234238
return;
235239
}
@@ -238,6 +242,8 @@ async function fetchData(param) {
238242
console.log("user 1 mail: " + val);
239243
console.log("user 2 mail: " + val$1);
240244
}
245+
246+
export { fetchData };
241247
```
242248

243249
</CodeTab>
@@ -275,7 +281,7 @@ let logMultipleValues = async () => {
275281

276282
Here's a full example of using the MDN `fetch` API, using `async` / `await` to simulate a login:
277283

278-
```res
284+
```res nocheck
279285
// A generic Response type for typing our fetch requests
280286
module Response = {
281287
type t<'data>

markdown-pages/docs/manual/attribute.mdx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ let mode2 = mode
2020
```
2121

2222
```js
23-
var mode2 = "dev";
23+
let mode2 = "dev";
24+
25+
export { mode2 };
2426
```
2527

2628
</CodeTab>
@@ -59,7 +61,15 @@ let customTriple = foo => foo * 3
5961
```
6062

6163
```js
64+
function customDouble(foo) {
65+
return foo << 1;
66+
}
67+
68+
function customTriple(foo) {
69+
return (foo * 3) | 0;
70+
}
6271

72+
export { customDouble, customTriple };
6373
```
6474

6575
</CodeTab>
@@ -84,7 +94,7 @@ There's a second category of attributes, called "extension points" (a remnant te
8494
```
8595

8696
```js
87-
var a = 1;
97+
((var a = 1));
8898
```
8999

90100
</CodeTab>

0 commit comments

Comments
 (0)