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
2 changes: 1 addition & 1 deletion apps/docs/__tests__/Meta_.test.res
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ test("renders DocSearch crawler meta tags", async () => {
)

expect(getMetaContent("docsearch:language"))->toBe("en")
expect(getMetaContent("docsearch:version"))->toBe("v12,latest")
expect(getMetaContent("docsearch:version"))->toBe("v13,next")
})

test("generates Open Graph image URLs from the current page URL", async () => {
Expand Down
7 changes: 5 additions & 2 deletions apps/docs/__tests__/VersionSelect_.test.res
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ test("renders current version label", async () => {
let el = await screen->getByTestId("version-select")
await element(el)->toBeVisible

let label = await screen->getByText("v12 (latest)")
let label = await screen->getByText("v13 (preview)")
await element(label)->toBeVisible
})

Expand All @@ -21,7 +21,10 @@ test("clicking button shows older versions", async () => {
let button = await screen->getByRole(#button)
await button->click

// Older versions should now be visible
// Released documentation should now be visible
let latest = await screen->getByText("v12 (latest)")
await element(latest)->toBeVisible

let v11After = await screen->getByText("v11")
await element(v11After)->toBeVisible

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/docs-version.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"current": "v12"
"current": "v13"
}
27 changes: 27 additions & 0 deletions apps/docs/scripts/__tests__/ApiDataFixtures.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
open ScriptTest

let apiModule = (id, name, version) => JSON.Object(
Dict.fromArray([
("id", JSON.String(id)),
("name", JSON.String(name)),
("docstrings", JSON.Array([JSON.String(`Docs from ${version}`)])),
("items", JSON.Array([])),
]),
)

let snapshot = async (directory, version, moduleName) => {
let target = join([directory, version])
await mkdir(target, {recursive: true})
let writes = ["stdlib", "belt", "dom"]->Array.map(async library => {
let modulePath = `${library}/${moduleName->String.toLowerCase}`
let data = JSON.Object(
Dict.fromArray([
(library, apiModule(library->String.capitalize, library->String.capitalize, version)),
(modulePath, apiModule(`${library->String.capitalize}.${moduleName}`, moduleName, version)),
]),
)
await write(join([target, `${library}.json`]), JSON.stringify(data))
})
let results = await Promise.all(writes)
ignore(results)
}
58 changes: 58 additions & 0 deletions apps/docs/scripts/__tests__/ApiLinks_.test.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
open ScriptTest

let check = async markdown => {
let directory = await temporaryDirectory("v13-api-links-")
let options = {cwd: directory, env: environment, encoding: "utf8"}
expectSuccess(spawn("git", ["init", "--quiet"], options))
expectSuccess(
spawn(
"git",
["remote", "add", "origin", "https://github.com/rescript-lang/rescript-lang.org.git"],
options,
),
)
let docs = join([directory, "apps/docs"])
let manual = join([docs, "markdown-pages/docs/manual"])
await mkdir(manual, {recursive: true})
let apiDirectory = join([directory, "data/api"])
await ApiDataFixtures.snapshot(apiDirectory, "v12.99.0", "MaintenanceModule")
await ApiDataFixtures.snapshot(apiDirectory, "v13.3.1", "Example")
await write(join([manual, "api.mdx"]), markdown)
spawn(
executable,
[join([cwd(), "scripts/test-hrefs.mjs"])],
{
cwd: docs,
env: withEnvironment(environment, [("CI", "1")]),
encoding: "utf8",
},
)
}

test("link validation accepts routes from the selected v13 publication", async () => {
let result = await check(`
[Stdlib](/docs/manual/api/stdlib)
[Dom](/docs/manual/api/dom)
[Nested module](/docs/manual/api/belt/example/)
[API entry](/docs/manual/api/stdlib/example#value-get)
`)
expectSuccess(result)
expect(result.stdout)->toContain("0 issues found")
})

for_([
"/docs/manual/api/stdlibdom",
"/docs/manual/api/stdlib/typo",
"/docs/manual/api/stdlib/maintenancemodule",
"/docs/manual/missing-page",
"./nested/docs/manual/api/dom",
])("link validation rejects missing or wrong-major API targets: %s", async href => {
let result = await check(
`
[Valid API](/docs/manual/api/dom)
[Missing page](${href})
`,
)
expectStatus(result, 1)
expect(result.stdout)->toContain("Cannot find file")
})
139 changes: 139 additions & 0 deletions apps/docs/scripts/__tests__/ApiRoutes_.test.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
open ScriptTest
open ApiDataFixtures

let contentDirectory = async directory => {
let directory = join([directory, "markdown-pages"])
let writes = [
"blog",
"community",
"docs/manual",
"docs/guides",
"docs/react",
"syntax-lookup",
]->Array.map(async name => {
await mkdir(join([directory, name]), {recursive: true})
})
ignore(await Promise.all(writes))
await write(join([directory, "docs/manual/api.mdx"]), "# API")
await write(join([directory, "docs/manual/new-page.mdx"]), "# New page")
directory
}

test(
"v13 routes and content select the same publication without crossing compiler majors",
async () => {
expect(DocsVersion.current)->toBe("v13")
let directory = await temporaryDirectory("v13-api-routes-")
let contentDirectory = await contentDirectory(directory)
let apiDirectory = join([directory, "data/api"])
await snapshot(apiDirectory, "v12.99.0", "MaintenanceModule")
await snapshot(apiDirectory, "v13.9.0", "OldModule")
await snapshot(apiDirectory, "v13.10.0", "NewModule")
await snapshot(apiDirectory, "v14.0.0", "FutureModule")
let selected = ApiDataSource.directory(~options={directory: apiDirectory})
let data = ApiData.load(~directory=apiDirectory, ~major=DocsVersion.current)
expect(selected)->toBe(join([apiDirectory, data.version]))
let {routes} = DocsRouteConfig.loadRoutes(~options={contentDirectory, apiDirectory: selected})
let paths = DocsRouteConfig.concretePaths(routes)
expect(paths->Array.includes("/docs/manual/new-page"))->toBe(true)
["stdlib", "belt", "dom"]->Array.forEach(library => {
expect(paths->Array.includes(`/docs/manual/api/${library}`))->toBe(true)
expect(paths->Array.includes(`/docs/manual/api/${library}/newmodule`))->toBe(true)
expect(paths->Array.includes(`/docs/manual/api/${library}/oldmodule`))->toBe(false)
expect(paths->Array.includes(`/docs/manual/api/${library}/maintenancemodule`))->toBe(false)
expect(paths->Array.includes(`/docs/manual/api/${library}/futuremodule`))->toBe(false)
})
},
)

test("route types include nested index routes and omit URL patterns", async () => {
let routes: array<DocsRouteConfig.route> = [
{
path: "docs",
children: [{index: true}, {path: "guide"}, {path: ":slug"}, {path: "*"}, {path: "/try"}],
},
{path: "optional?"},
]
expect(DocsRouteConfig.concretePaths(routes))->toStrictEqual(["/docs", "/docs/guide", "/try"])
})

test("route generation detects stale types without overwriting them in check mode", async () => {
let directory = await temporaryDirectory("v13-route-types-")
let destination = join([directory, "Path.res"])
let routes: array<DocsRouteConfig.route> = [{path: "/new-api-module"}]
await write(destination, "stale")
expect(DocsRouteConfig.generate(~options={routes, destination, check: true}))->toBe(false)
expect(await read(destination))->toBe("stale")
expect(DocsRouteConfig.generate(~options={routes, destination}))->toBe(true)
expect(await read(destination))->toBe(DocsRouteConfig.renderPathType(routes))
expect(DocsRouteConfig.generate(~options={routes, destination, check: true}))->toBe(true)
})

test(
"route generation checks the live v13 configuration from another working directory",
async () => {
let directory = await temporaryDirectory("v13-route-cwd-")
let script = join([cwd(), "generate-route-types.mjs"])
let result = spawn(
executable,
[script, "--check"],
{
cwd: directory,
env: environment,
encoding: "utf8",
},
)
expectSuccess(result)
},
)

for_(
[
"stdlib",
"stdlib/array",
"belt",
"belt/array",
"dom",
"dom/storage",
]->Array.flatMap(modulePath =>
["", ".data", "/_.data", "/"]->Array.map(suffix => (modulePath, suffix))
),
)("API loader serves the published v13 %s%s module and its sidebar", async ((
modulePath,
suffix,
)) => {
let request = WebAPI.Request.fromURL(
`https://v13.rescript-lang.org/docs/manual/api/${modulePath}${suffix}`,
)
let result = await ApiRoute.loader({request: request})
switch result {
| Error(message) => expect(message)->toBe("expected a valid published API module")
| Ok({module_, toctree}) =>
let library = modulePath->String.split("/")->Array.getUnsafe(0)
let published = ApiData.current()->ApiData.library(library)->Dict.getUnsafe(modulePath)
let published = published->JSON.Decode.object->Option.getOrThrow
expect(module_.id)->toBe(published->Dict.getUnsafe("id")->JSON.Decode.string->Option.getOrThrow)
expect(module_.docstrings)->toStrictEqual(
published
->Dict.getUnsafe("docstrings")
->JSON.Decode.array
->Option.getOrThrow
->Array.map(value => value->JSON.Decode.string->Option.getOrThrow),
)
expect(toctree.name)->toBe(library->String.capitalize)
expect(toctree.children->Array.length > 0)->toBe(true)
}
})

for_(["", ".data", "/_.data", "/"])(
"MDX loaders resolve document and data URLs consistently: %s",
async suffix => {
expect(
MdxFile.resolveFilePath(
`/docs/manual/introduction${suffix}`,
~dir="markdown-pages/docs/manual",
~alias="docs/manual",
),
)->toBe("markdown-pages/docs/manual/introduction.mdx")
},
)
2 changes: 1 addition & 1 deletion apps/docs/scripts/generate_llms.res
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ let generateFile = (
createSectionLlmFiles(~llmsDirectory, ~sectionFiles, ~documents)
}

let currentManualVersion = "v12"
let currentManualVersion = DocsVersion.current

let currentReactVersion = "v0.14.2"
let currentReactRuntimeVersion = "v19.2.4"
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/bindings/ApiDataSource.res
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
@module("../../scripts/api-data.mjs")
external latestVersion: (~major: string, array<string>) => string = "latestApiVersion"

type options = {directory: string, major?: string}
@module("../../scripts/api-data.mjs")
external directory: (~options: options) => string = "apiDataDirectory"
11 changes: 9 additions & 2 deletions apps/docs/src/bindings/DocsRouteConfig.res
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
type route
type rec route = {path?: string, index?: bool, children?: array<route>}
type config = {routes: array<route>}
type options = {contentDirectory?: string}
type options = {contentDirectory?: string, apiDirectory?: string}

@module("../../route-config.mjs")
external loadRoutes: (~options: options=?) => config = "loadRoutes"

@module("../../generate-route-types.mjs")
external concretePaths: array<route> => array<string> = "concretePaths"

@module("../../generate-route-types.mjs")
external renderPathType: array<route> => string = "renderPathType"

type generationOptions = {routes: array<route>, destination: string, check?: bool}
@module("../../generate-route-types.mjs")
external generate: (~options: generationOptions) => bool = "generateRouteTypes"
4 changes: 1 addition & 3 deletions apps/docs/src/common/Constants.res
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
external latestVersion: string = "import.meta.env.VITE_VERSION_LATEST"

let docSearchVersionTokens = [latestVersion->Semver.tryGetMajorString, "latest"]
let docSearchVersionTokens = [DocsVersion.current, "next"]

// Used for the DocsOverview
let languageManual = [
Expand Down
3 changes: 2 additions & 1 deletion apps/docs/src/components/VersionSelect.res
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ type version = {
link: string,
}

let currentVersion = {label: DocsVersion.current ++ " (latest)", link: "#"}
let currentVersion = {label: DocsVersion.current ++ " (preview)", link: "#"}

let olderVersions = [
{label: "v12 (latest)", link: "https://rescript-lang.org/docs/manual/introduction"},
{label: "v11", link: "https://v11.rescript-lang.org/docs/manual/v11.0.0/introduction"},
{label: "v9.1 - v10.1", link: "https://v11.rescript-lang.org/docs/manual/v10.0.0/introduction"},
{label: "v8.2 - v9.0", link: "https://v11.rescript-lang.org/docs/manual/v9.0.0/introduction"},
Expand Down
1 change: 1 addition & 0 deletions apps/docs/vitest.scripts.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { defineConfig } from "vitest/config";

export default defineConfig({
envDir: "../..",
test: {
environment: "node",
include: ["scripts/__tests__/*.test.jsx"],
Expand Down
10 changes: 8 additions & 2 deletions data/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ module must have nonempty `id` and `name` strings, a `docstrings` array of strin
and an `items` array before the snapshot can be cached or used to generate routes.

Route generation, sidebar navigation, and page content use the same snapshot.
`master` selects v12; when porting this loader to the v13 website branch, update
`apps/docs/docs-version.json` to `v13` alongside the displayed documentation version.
`master` selects v12; this website branch selects v13 through
`apps/docs/docs-version.json`. The displayed documentation version and generated
manual indexes use the same setting.

Pushes to `v13`, including compiler API-data publications, trigger
`.github/workflows/deploy.yml` and deploy the website at
`https://v13.rescript-lang.org`. Compiler publication must target this branch
only after this loader is deployed (compiler issue #8753).

Keep the compiler artifact intact, including the generated `js.json` and
`toc_tree.json` files. The website currently builds its sidebar from the module
Expand Down
11 changes: 10 additions & 1 deletion packages/shared/src/Path.res
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,16 @@ type t = [
| #"/docs/manual/api/stdlib"
| #"/docs/manual/api/stdlib/array"
| #"/docs/manual/api/stdlib/arraybuffer"
| #"/docs/manual/api/stdlib/asyncgenerator"
| #"/docs/manual/api/stdlib/asynciterable"
| #"/docs/manual/api/stdlib/asynciterableiterator"
| #"/docs/manual/api/stdlib/asynciterator"
| #"/docs/manual/api/stdlib/bigint"
| #"/docs/manual/api/stdlib/bigint64array"
| #"/docs/manual/api/stdlib/bigint64array/constants"
| #"/docs/manual/api/stdlib/biguint64array"
| #"/docs/manual/api/stdlib/biguint64array/constants"
| #"/docs/manual/api/stdlib/blob"
| #"/docs/manual/api/stdlib/bool"
| #"/docs/manual/api/stdlib/console"
| #"/docs/manual/api/stdlib/dataview"
Expand All @@ -155,16 +159,17 @@ type t = [
| #"/docs/manual/api/stdlib/error/typeerror"
| #"/docs/manual/api/stdlib/error/urierror"
| #"/docs/manual/api/stdlib/exn"
| #"/docs/manual/api/stdlib/file"
| #"/docs/manual/api/stdlib/float"
| #"/docs/manual/api/stdlib/float/constants"
| #"/docs/manual/api/stdlib/float32array"
| #"/docs/manual/api/stdlib/float32array/constants"
| #"/docs/manual/api/stdlib/float64array"
| #"/docs/manual/api/stdlib/float64array/constants"
| #"/docs/manual/api/stdlib/generator"
| #"/docs/manual/api/stdlib/int"
| #"/docs/manual/api/stdlib/int/bitwise"
| #"/docs/manual/api/stdlib/int/constants"
| #"/docs/manual/api/stdlib/int/ref"
| #"/docs/manual/api/stdlib/int16array"
| #"/docs/manual/api/stdlib/int16array/constants"
| #"/docs/manual/api/stdlib/int32array"
Expand All @@ -184,7 +189,10 @@ type t = [
| #"/docs/manual/api/stdlib/intl/relativetimeformat"
| #"/docs/manual/api/stdlib/intl/segmenter"
| #"/docs/manual/api/stdlib/intl/segments"
| #"/docs/manual/api/stdlib/iterable"
| #"/docs/manual/api/stdlib/iterableiterator"
| #"/docs/manual/api/stdlib/iterator"
| #"/docs/manual/api/stdlib/iteratorobject"
| #"/docs/manual/api/stdlib/jserror"
| #"/docs/manual/api/stdlib/jserror/evalerror"
| #"/docs/manual/api/stdlib/jserror/rangeerror"
Expand Down Expand Up @@ -216,6 +224,7 @@ type t = [
| #"/docs/manual/api/stdlib/set"
| #"/docs/manual/api/stdlib/string"
| #"/docs/manual/api/stdlib/symbol"
| #"/docs/manual/api/stdlib/taggedtemplate"
| #"/docs/manual/api/stdlib/timeoutid"
| #"/docs/manual/api/stdlib/type"
| #"/docs/manual/api/stdlib/type/classify"
Expand Down
Loading