Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,31 @@ Only update screenshots that are intentionally affected by your change.

### Markdown Example and Link Checks

`yarn test` runs both of the following:
`yarn test` runs the following:

- `scripts/test-examples.mjs` to validate ReScript code examples in markdown
- `scripts/test-hrefs.mjs` to validate relative markdown links under `markdown-pages/`
- `apps/docs/scripts/test-examples.mjs` to compile examples in the manual, React docs, guides and syntax lookup, and compare shown JS and JSX output with the installed compiler
- `apps/docs/scripts/test-hrefs.mjs` to validate relative markdown links under `markdown-pages/`
- `apps/docs/scripts/test-redirects.mjs` to validate redirect rules

Supported ReScript markdown code fences:

- ` ```res `
- ` ```rescript ` (alias for `res`)
- ` ```res sig `
- ` ```resi ` (alias for `res sig`)
- ` ```res prelude `
- ` ```res file=MathUtils.res ` (a separate source file for multi-file examples)
- ` ```resi file=MathUtils.resi ` (its interface)

Refresh generated JS output fences with:
Indented fences are supported. Prelude blocks provide context for later examples. Use `res nocheck` only for intentionally invalid or incomplete snippets. Malformed CodeTabs fail the check, including missing closing tags and mismatched labels and fences.

Named source files are compiled together with the examples on their page. Filenames must be PascalCase, end in `.res` or `.resi`, and be unique on the page; `Example` is reserved for the checker's main module. Supporting files are removed before checking the next page.

Output comparison ignores formatting and comments. `JS Output` and `JS Output (Module)` use ESM, `JS Output (CommonJS)` uses CommonJS, and `JSX Preserved Output` uses preserved JSX. TypeScript output is not generated or compared by this checker; its ReScript input is still compiled.

Compiler diagnostics embedded in output, such as `%todo`, show the example's filename and source range with the temporary build directory removed.

Refresh generated JS output fences with the installed compiler and format the result:

```sh
yarn test --update
Expand All @@ -144,12 +157,15 @@ yarn test --update
You can also run the scripts directly:

```sh
node scripts/test-examples.mjs
node scripts/test-examples.mjs --update
node scripts/test-hrefs.mjs
node scripts/test-hrefs.mjs "markdown-pages/docs/manual/**/*.mdx"
node apps/docs/scripts/test-examples.mjs
node apps/docs/scripts/test-examples.mjs --update
node apps/docs/scripts/test-examples.mjs "manual/module-functions.mdx"
node apps/docs/scripts/test-examples.mjs --include-blog
node apps/docs/scripts/test-hrefs.mjs
```

Example-checker patterns are relative to `apps/docs/markdown-pages/docs`. Historical blog posts describe earlier compiler versions, so the default run reports them as skipped. `--include-blog` audits them with the installed compiler; reported differences need review against the original version. Blog files are never rewritten, even with `--update`.

Run `yarn test` before pushing content changes so CI does not fail on markdown regressions.

## Writing Blog Posts
Expand Down
49 changes: 9 additions & 40 deletions apps/docs/markdown-pages/docs/manual/array-and-list.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,7 @@ let myList = list{1, 2, 3}
```

```js
let myList = {
hd: 1,
tl: {
hd: 2,
tl: {
hd: 3,
tl: /* [] */ 0,
},
},
};
let myList = { hd: 1, tl: { hd: 2, tl: { hd: 3, tl: /* [] */ 0 } } };

export { myList };
```
Expand Down Expand Up @@ -171,21 +162,11 @@ let anotherList = list{0, ...myList}
```

```js
var myList = {
hd: 1,
tl: {
hd: 2,
tl: {
hd: 3,
tl: 0,
},
},
};

var anotherList = {
hd: 0,
tl: myList,
};
let myList = { hd: 1, tl: { hd: 2, tl: { hd: 3, tl: /* [] */ 0 } } };

let anotherList = { hd: 0, tl: myList };

export { myList, anotherList };
```

</CodeTab>
Expand All @@ -210,21 +191,9 @@ let message =
```

```js
let myList = {
hd: 1,
tl: {
hd: 2,
tl: {
hd: 3,
tl: /* [] */ 0,
},
},
};

let anotherList = {
hd: 0,
tl: myList,
};
let myList = { hd: 1, tl: { hd: 2, tl: { hd: 3, tl: /* [] */ 0 } } };

let anotherList = { hd: 0, tl: myList };

let message =
myList !== 0
Expand Down
6 changes: 4 additions & 2 deletions apps/docs/markdown-pages/docs/manual/attribute.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,13 @@ There's a second category of attributes, called "extension points" (a remnant te
<CodeTab labels={["ReScript", "JS Output"]}>

```res
%raw("var a = 1")
let result = %raw("1 + 2")
```

```js
((var a = 1));
let result = 1 + 2;

export { result };
```

</CodeTab>
Expand Down
5 changes: 1 addition & 4 deletions apps/docs/markdown-pages/docs/manual/bind-to-js-function.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,7 @@ let stripe = new Stripe("sk\_...");

let session = stripe.checkout.sessions.create({});

export {
stripe,
session,
}
export { stripe, session };
````

</CodeTab>
Expand Down
4 changes: 1 addition & 3 deletions apps/docs/markdown-pages/docs/manual/bind-to-js-object.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@ let action = {type_: "ADD_USER"}
```

```js
let action = {
type: "ADD_USER",
};
let action = { type: "ADD_USER" };

export { action };
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ function queryResult(usePayload, payload) {
return school.getStudentById(defaultId);
}
}

let defaultId = 10;

export { defaultId };
Expand Down
70 changes: 52 additions & 18 deletions apps/docs/markdown-pages/docs/manual/dict.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ let d2 = Dict.fromArray([("A", 5), ("B", 6)])
```

```js
let d = {
A: 5,
B: 6,
};
let d = { A: 5, B: 6 };

let d2 = Object.fromEntries([
["A", 5],
["B", 6],
]);

export { d, d2 };
```

</CodeTab>
Expand Down Expand Up @@ -66,11 +65,12 @@ let dict{"C": ?c} = d
```

```js
let d = {
A: 5,
B: 6,
C: 7,
};
let d2 = Object.fromEntries([
["A", 5],
["B", 6],
]);

let d = { A: 5, B: 6, C: 7 };

let a = d["A"];

Expand All @@ -79,6 +79,8 @@ let b = d.B;
let b$1 = b !== undefined ? b : undefined;

let c = d.C;

export { d2, d, a, b$1 as b, c };
```

</CodeTab>
Expand All @@ -102,12 +104,26 @@ d->Dict.set("C", 7)
```

```js
let d = {
A: 5,
B: 6,
};
let d2 = Object.fromEntries([
["A", 5],
["B", 6],
]);

let d = { A: 5, B: 6, C: 7 };

let a = d["A"];

let b = d.B;

let b$1 = b !== undefined ? b : undefined;

let d$1 = { A: 5, B: 6 };

d$1["C"] = 7;

d["C"] = 7;
let c = d.C;

export { d2, a, b$1 as b, c, d$1 as d };
```

</CodeTab>
Expand Down Expand Up @@ -136,6 +152,23 @@ let decodeUser = (json: JSON.t) => {
```

```js
let d2 = Object.fromEntries([
["A", 5],
["B", 6],
]);

let d = { A: 5, B: 6, C: 7 };

let a = d["A"];

let b = d.B;

let b$1 = b !== undefined ? b : undefined;

let d$1 = { A: 5, B: 6 };

d$1["C"] = 7;

function decodeUser(json) {
if (typeof json !== "object" || json === null || Array.isArray(json)) {
return;
Expand All @@ -146,12 +179,13 @@ function decodeUser(json) {
}
let email = json.email;
if (typeof email === "string") {
return {
name: name,
email: email,
};
return { name: name, email: email };
}
}

let c = d.C;

export { d2, a, b$1 as b, c, d$1 as d, decodeUser };
```

</CodeTab>
15 changes: 10 additions & 5 deletions apps/docs/markdown-pages/docs/manual/editor-code-analysis.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ Reactive dead code updates are a newer enhancement of Editor Code Analysis and r
### 1. **Unused Record Fields**

```rescript
type item = {id: string, checked: bool}

type useReturn = {
items: array<item>,
toggleItemChecked: string => unit, // ← Never used
Expand Down Expand Up @@ -85,19 +87,20 @@ Old validation logic might remain after refactors—clean it up.

### 4. **Unnecessary Interface Exposure**

```rescript
```resi
// DrilldownTarget.resi
MetricParam.parse // ← Never used
MetricParam.serialize // ← Never used
type t
let parse: string => option<t> // ← Never used
let serialize: t => string // ← Never used
```

Keep interfaces minimal by removing unused exports.

### 5. **Unused Functions**

```rescript
let routerUrlToPath = ... // ← Never used
let routeUrlStartsWith = ... // ← Never used
let routerUrlToPath = url => url->String.split("?")->Array.get(0) // ← Never used
let routeUrlStartsWith = (url, prefix) => url->String.startsWith(prefix) // ← Never used
```

Removing these often uncovers further unused logic.
Expand Down Expand Up @@ -126,6 +129,8 @@ type user = {
Permanently marks code as alive (no future warnings).

```rescript
type user = {name: string}

@live
let getUserName = user => user.name
```
Expand Down
21 changes: 17 additions & 4 deletions apps/docs/markdown-pages/docs/manual/editor-plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,23 @@ let twoExceptions = (x, y) => {
It is possible to silence the analysis by adding a `@doesNotThrow` annotation:

```rescript
exception Invalid_argument(string)

let makeSpaces = length => {
if length < 0 {
throw(Invalid_argument("negative length"))
}
" "->String.repeat(length)
}

@throws(Invalid_argument)
let stringMake1 = String.make(12, ' ')
let stringMake1 = makeSpaces(12)

// Silence only the make function
let stringMake2 = (@doesNotThrow String.make)(12, ' ')
let stringMake2 = (@doesNotThrow makeSpaces)(12)

// Silence the entire call (including arguments to make)
let stringMake3 = @doesNotThrow String.make(12, ' ')
let stringMake3 = @doesNotThrow makeSpaces(12)

```

Expand All @@ -107,7 +116,11 @@ let stringMake3 = @doesNotThrow String.make(12, ' ')
- Generic exceptions are not understood by the analysis. For example `exn` is not recognized below (only concrete exceptions are):

```rescript
try (foo()) { | exn => throw(exn) }
let foo = () => throw(Not_found)

try foo() catch {
| exn => throw(exn)
}
```

- Uses of e.g. `List.head` are interpreted as belonging to the standard library. If you re-define `List` in the local scope, the analysis it will think it's dealing with `List` from the standard library.
Expand Down
Loading
Loading