From c704e16b4d82aa627242c5d4d27b165ac376e069 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Sun, 11 Oct 2026 23:15:06 +0200 Subject: [PATCH 01/13] fix: align local test commands with their supported workflows Remove local screenshot-update aliases and document the feature-branch CI workflow that owns visual baselines. Point the full-site Cypress suite at the Vite dev server, explain server startup and URL overrides, and distinguish the managed production homepage suite. Fixes #1415 --- README.md | 61 +++++++++++++++++++++++++++--------- apps/docs/cypress.config.mjs | 2 +- apps/docs/package.json | 3 +- package.json | 3 +- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 2630705a2..66af0a28a 100644 --- a/README.md +++ b/README.md @@ -87,18 +87,20 @@ Tailwind is configured in [`styles/main.css`](styles/main.css). There is no `tai ## Common Commands -| Command | Purpose | -| -------------------- | ----------------------------------------------------------------- | -| `yarn dev` | Prepare generated files and run the local development environment | -| `yarn build` | Run the full production build | -| `yarn preview` | Build and serve the generated static client locally | -| `yarn build:res` | Compile ReScript only | -| `yarn dev:res` | Run the ReScript compiler in watch mode | -| `yarn format` | Run Prettier and the ReScript formatter | -| `yarn test` | Run markdown example and href validation | -| `yarn ci:test` | Run Vitest browser tests headlessly | -| `yarn vitest` | Run Vitest directly | -| `yarn vitest:update` | Update screenshot baselines headlessly | +| Command | Purpose | +| ---------------- | ----------------------------------------------------------------- | +| `yarn dev` | Prepare generated files and run the local development environment | +| `yarn build` | Run the full production build | +| `yarn preview` | Build and serve the generated static client locally | +| `yarn build:res` | Compile ReScript only | +| `yarn dev:res` | Run the ReScript compiler in watch mode | +| `yarn format` | Run Prettier and the ReScript formatter | +| `yarn test` | Run markdown example and href validation | +| `yarn ci:test` | Run Vitest browser tests headlessly | +| `yarn vitest` | Run Vitest directly | +| `yarn cy:open` | Open the full-site Cypress suite against the running dev server | +| `yarn cy:e2e` | Run the full-site Cypress suite headlessly in Chrome | +| `yarn test:e2e` | Build and run homepage Cypress tests with a temporary server | ## Testing @@ -107,6 +109,9 @@ Tailwind is configured in [`styles/main.css`](styles/main.css). There is no `tai We use [Vitest](https://vitest.dev/) in browser mode with Playwright for component-level tests. Test files live in `__tests__/` and are written in ReScript. ```sh +# Compile the ReScript tests first +yarn build:res + # Watch mode yarn vitest @@ -114,13 +119,39 @@ yarn vitest yarn ci:test ``` -To update screenshot baselines, run: +Visual screenshot baselines are generated in CI's pinned Linux browser environment. Local snapshot updates are blocked. Push your feature branch, then open [Update Visual Regression Screenshots](https://github.com/rescript-lang/rescript-lang.org/actions/workflows/update-visual-screenshots.yml), select that branch, and choose **Run workflow**. The workflow refuses `master` and commits any changed PNGs back to the selected branch. + +You can also dispatch the workflow with the GitHub CLI, replacing `` with your pushed branch: + +```sh +gh workflow run update-visual-screenshots.yml --repo rescript-lang/rescript-lang.org --ref +``` + +After the workflow finishes, pull its commit and review the screenshot diff. Only accept screenshots intentionally affected by your change. + +### Cypress E2E Tests + +The full-site suite covers documentation, navigation, and the playground. Start the local development server in one terminal: + +```sh +yarn dev +``` + +Wait for Vite to report its local URL. In another terminal, run one of these commands: + +```sh +yarn cy:open # Interactive Cypress runner (Electron) +yarn cy:run # Headless run (Electron) +yarn cy:e2e # Headless run (Chrome must be installed) +``` + +These commands compile the ReScript specs and default to `http://127.0.0.1:5173`. If Vite reports another port, or you want to test a deployed site, pass its URL explicitly: ```sh -yarn vitest:update +yarn cy:e2e --config baseUrl=http://127.0.0.1:5174 ``` -Only update screenshots that are intentionally affected by your change. +The homepage suite uses the production build and its own configuration. `yarn test:e2e` builds the site, starts a temporary server at `http://127.0.0.1:4173`, runs the homepage specs in Chrome, and stops the server when the tests finish. It does not require `yarn dev`. ### Markdown Example and Link Checks diff --git a/apps/docs/cypress.config.mjs b/apps/docs/cypress.config.mjs index 613b3f9db..a231b6d56 100644 --- a/apps/docs/cypress.config.mjs +++ b/apps/docs/cypress.config.mjs @@ -7,7 +7,7 @@ export default defineConfig({ openMode: 0, }, e2e: { - baseUrl: "http://localhost:8080", + baseUrl: "http://127.0.0.1:5173", specPattern: "e2e/**/*.cy.jsx", excludeSpecPattern: "e2e/homepage/**", supportFile: "cypress/support/e2e.js", diff --git a/apps/docs/package.json b/apps/docs/package.json index e7350a978..b519d4356 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -39,8 +39,7 @@ "cy:run": "yarn build:res && cypress run", "cy:e2e": "yarn build:res && cypress run --browser chrome", "vitest": "vitest", - "vitest:visual": "VISUAL_TESTS=1 vitest", - "vitest:update": "VISUAL_TESTS=1 VISUAL_BASELINE_UPDATE=1 vitest --run --browser.headless --update" + "vitest:visual": "VISUAL_TESTS=1 vitest" }, "dependencies": { "@cloudflare/pages-plugin-vercel-og": "^0.1.2", diff --git a/package.json b/package.json index b54140105..01455f85f 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,7 @@ "cy:e2e": "yarn workspace @rescript-lang/docs cy:e2e", "vitest": "yarn workspace @rescript-lang/docs vitest", "vitest:visual": "yarn workspace @rescript-lang/docs vitest:visual", - "vitest:guide": "yarn workspace @rescript-lang/guide vitest", - "vitest:update": "yarn workspace @rescript-lang/docs vitest:update" + "vitest:guide": "yarn workspace @rescript-lang/guide vitest" }, "devDependencies": { "lefthook": "^2.1.4", From 63e925408e8fcaa419f309aedf2864e65b0318db Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Mon, 12 Oct 2026 00:20:32 +0200 Subject: [PATCH 02/13] fix: retain localhost in local Cypress URLs Keep localhost as the full-site Cypress hostname while correcting the default port to 5173. Align the README default URL and alternate-port example with the config. --- README.md | 4 ++-- apps/docs/cypress.config.mjs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 66af0a28a..7582050c3 100644 --- a/README.md +++ b/README.md @@ -145,10 +145,10 @@ yarn cy:run # Headless run (Electron) yarn cy:e2e # Headless run (Chrome must be installed) ``` -These commands compile the ReScript specs and default to `http://127.0.0.1:5173`. If Vite reports another port, or you want to test a deployed site, pass its URL explicitly: +These commands compile the ReScript specs and default to `http://localhost:5173`. If Vite reports another port, or you want to test a deployed site, pass its URL explicitly: ```sh -yarn cy:e2e --config baseUrl=http://127.0.0.1:5174 +yarn cy:e2e --config baseUrl=http://localhost:5174 ``` The homepage suite uses the production build and its own configuration. `yarn test:e2e` builds the site, starts a temporary server at `http://127.0.0.1:4173`, runs the homepage specs in Chrome, and stops the server when the tests finish. It does not require `yarn dev`. diff --git a/apps/docs/cypress.config.mjs b/apps/docs/cypress.config.mjs index a231b6d56..fbe629e46 100644 --- a/apps/docs/cypress.config.mjs +++ b/apps/docs/cypress.config.mjs @@ -7,7 +7,7 @@ export default defineConfig({ openMode: 0, }, e2e: { - baseUrl: "http://127.0.0.1:5173", + baseUrl: "http://localhost:5173", specPattern: "e2e/**/*.cy.jsx", excludeSpecPattern: "e2e/homepage/**", supportFile: "cypress/support/e2e.js", From a2bdd1f347a13eebb5c0d46b5f07fadd14fd02f7 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Sat, 10 Oct 2026 01:17:15 +0200 Subject: [PATCH 03/13] refactor: remove obsolete website code and check unused exports Remove abandoned scripts, compiler installations, components, and data. Keep live MDX mappings and framework entry points, trim unused interfaces, and enforce the monorepo dead-value check in CI. Fixes #1409 --- .github/workflows/pull-request.yml | 4 + .gitignore | 6 - .oxfmtrc.json | 3 - AGENTS.md | 1 - README.md | 13 +- apps/docs/__tests__/Url_.test.res | 29 - apps/docs/app/DocsRoot.res | 2 +- apps/docs/app/DocsRoot.resi | 2 +- apps/docs/app/DocsRoutes.res | 2 + apps/docs/app/DocsRoutes.resi | 15 +- apps/docs/app/layouts/ContentLayoutRoute.res | 3 +- apps/docs/app/layouts/ContentLayoutRoute.resi | 3 +- apps/docs/app/layouts/DocsLayoutRoute.res | 2 +- apps/docs/app/layouts/DocsLayoutRoute.resi | 2 +- apps/docs/app/layouts/HomepageLayoutRoute.res | 3 +- .../docs/app/layouts/HomepageLayoutRoute.resi | 3 +- apps/docs/app/routes/ApiDocs.res | 9 +- apps/docs/app/routes/ApiOverviewRoute.res | 2 + apps/docs/app/routes/ApiOverviewRoute.resi | 4 +- apps/docs/app/routes/ApiRoute.res | 2 + apps/docs/app/routes/ApiRoute.resi | 2 + apps/docs/app/routes/BlogArticleRoute.res | 2 + apps/docs/app/routes/BlogArticleRoute.resi | 2 + apps/docs/app/routes/BlogRoute.res | 3 +- apps/docs/app/routes/BlogRoute.resi | 3 +- apps/docs/app/routes/BrandRoute.res | 1 + apps/docs/app/routes/BrandRoute.resi | 1 + apps/docs/app/routes/CommunityRoute.res | 2 + apps/docs/app/routes/CommunityRoute.resi | 2 + apps/docs/app/routes/DocsGuidesRoute.res | 2 + apps/docs/app/routes/DocsGuidesRoute.resi | 2 + apps/docs/app/routes/DocsManualRoute.res | 2 + apps/docs/app/routes/DocsManualRoute.resi | 2 + apps/docs/app/routes/DocsReactRoute.res | 2 + apps/docs/app/routes/DocsReactRoute.resi | 2 + apps/docs/app/routes/LandingPageRoute.res | 4 +- apps/docs/app/routes/LandingPageRoute.resi | 4 +- apps/docs/app/routes/NotFoundRoute.res | 2 +- apps/docs/app/routes/NotFoundRoute.resi | 2 +- apps/docs/app/routes/PackagesRoute.res | 2 + apps/docs/app/routes/PackagesRoute.resi | 2 + apps/docs/app/routes/SyntaxLookup.res | 53 - .../app/routes/SyntaxLookupDetailRoute.res | 2 + .../app/routes/SyntaxLookupDetailRoute.resi | 2 + apps/docs/app/routes/SyntaxLookupRoute.res | 2 + apps/docs/app/routes/SyntaxLookupRoute.resi | 2 + apps/docs/app/routes/TryRoute.res | 3 + apps/docs/app/routes/TryRoute.resi | 3 + apps/docs/compilers/README.md | 5 - apps/docs/compilers/dummy/Dummy.res | 1 - apps/docs/compilers/package-lock.json | 287 - apps/docs/compilers/package.json | 18 - apps/docs/compilers/rescript.json | 10 - apps/docs/e2e/bindings/Cy.res | 57 - apps/docs/e2e/bindings/Cypress.res | 1 - .../functions/ogimage/[[path]]/index.png.res | 1 + apps/docs/package.json | 6 +- apps/docs/scripts/extract-syntax.mjs | 60 - apps/docs/scripts/figma-fetch.js | 210 - apps/docs/scripts/gendocs.res | 303 - apps/docs/scripts/markdown.js | 72 - apps/docs/scripts/sync-redirects.mjs | 21 - apps/docs/scripts/watch-tests.mjs | 49 - apps/docs/src/bindings/Env.res | 23 - apps/docs/src/bindings/HeadlessUI.res | 77 - apps/docs/src/bindings/Jsdom.res | 5 - apps/docs/src/bindings/Mdast.res | 41 - apps/docs/src/bindings/Remark.res | 15 - apps/docs/src/common/Constants.res | 54 +- apps/docs/src/common/DateStr.res | 1 - apps/docs/src/common/DateStr.resi | 1 - .../src/common/EnableCollapsibleNavbar.res | 20 - apps/docs/src/common/Hooks.res | 38 - apps/docs/src/common/Url.res | 30 - apps/docs/src/common/Url.resi | 17 - apps/docs/src/common/Util.res | 50 - apps/docs/src/common/Util.resi | 6 - apps/docs/src/components/ApiIntro.res | 2 - apps/docs/src/components/ApiIntro.resi | 2 - apps/docs/src/components/ApiMarkdown.res | 39 - apps/docs/src/components/ApiMarkdown.resi | 1 - apps/docs/src/components/CodeMirrorSetup.js | 19 - apps/docs/src/components/CommunityContent.res | 2 - apps/docs/src/components/Image.res | 10 - apps/docs/src/components/Intro.res | 2 - apps/docs/src/components/Markdown.resi | 8 - apps/docs/src/components/Navigation.res | 283 - apps/docs/src/components/Navigation.resi | 6 - apps/docs/src/components/SidebarNav.resi | 8 - apps/docs/src/components/VersionSelect.res | 6 - apps/docs/src/components/VersionSelect.resi | 4 - apps/docs/src/components/Video.res | 4 - apps/docs/src/data/BlogApi.res | 43 - apps/docs/src/data/BlogApi.resi | 3 - apps/docs/src/data/MetaTagsApi.res | 98 - apps/docs/src/ffi/react-codemirror-hooks.js | 162 - apps/docs/src/markdown/DocFrontmatter.res | 30 - apps/docs/src/markdown/DocFrontmatter.resi | 8 - apps/docs/src/markdown/Mdx.res | 3 - apps/docs/src/markdown/MdxFile.res | 11 - apps/docs/src/markdown/MdxFile.resi | 8 - apps/docs/src/markdown/SidebarHelpers.resi | 2 - apps/docs/src/markdown/TableOfContents.res | 33 - apps/docs/src/shims/Shims.res | 2 - apps/docs/src/shims/_shims.mjs | 8 - apps/docs/stdlib-toc.json | 7256 ----------------- apps/docs/styles/utils.css | 14 - apps/docs/test-utils/TestUtils.res | 9 - apps/docs/vitest.setup.mjs | 1 - .../__tests__/GuideCompilerSettings_.test.res | 10 +- apps/guide/__tests__/GuideHome_.test.res | 22 +- apps/guide/__tests__/GuideLayout_.test.res | 13 +- apps/guide/app/GuideCompilerFeedback.res | 4 +- apps/guide/app/GuideCompilerSettings.res | 2 - apps/guide/app/GuideHomeRoute.res | 3 +- apps/guide/app/GuideHomeRoute.resi | 3 +- apps/guide/app/GuideLayout.res | 7 - apps/guide/app/GuideLessonFrontmatter.res | 6 - apps/guide/app/root.res | 2 +- apps/guide/app/root.resi | 2 +- apps/guide/app/routes.jsx | 4 +- apps/guide/app/routes.res | 1 + apps/guide/app/routes.resi | 1 + package.json | 3 +- .../playground/src/CompilerManagerHook.resi | 2 - packages/playground/src/Playground.res | 7 - packages/shared/src/Ansi.res | 83 - packages/shared/src/Ansi.resi | 28 - packages/shared/src/Babel.res | 26 - packages/shared/src/CodeMirror.res | 24 - packages/shared/src/CodeMirror.resi | 1 - packages/shared/src/Icon.res | 75 - packages/shared/src/Icon.resi | 23 - packages/shared/src/Node.res | 11 - packages/shared/src/ReactRouter.res | 16 - packages/shared/src/RescriptCompilerApi.res | 3 - packages/shared/src/RescriptCompilerApi.resi | 31 - packages/shared/src/ScrollLockContext.res | 14 - packages/shared/src/Text.res | 1 - packages/shared/src/Text.resi | 1 - packages/shared/src/Vitest.res | 12 - .../shared/src/WarningFlagDescription.res | 34 - scripts/__tests__/check-dead-code.test.mjs | 63 + scripts/check-dead-code.mjs | 73 + yarn.lock | 275 +- 145 files changed, 275 insertions(+), 10400 deletions(-) delete mode 100644 apps/docs/__tests__/Url_.test.res delete mode 100644 apps/docs/compilers/README.md delete mode 100644 apps/docs/compilers/dummy/Dummy.res delete mode 100644 apps/docs/compilers/package-lock.json delete mode 100644 apps/docs/compilers/package.json delete mode 100644 apps/docs/compilers/rescript.json delete mode 100644 apps/docs/scripts/extract-syntax.mjs delete mode 100644 apps/docs/scripts/figma-fetch.js delete mode 100644 apps/docs/scripts/gendocs.res delete mode 100644 apps/docs/scripts/markdown.js delete mode 100644 apps/docs/scripts/sync-redirects.mjs delete mode 100644 apps/docs/scripts/watch-tests.mjs delete mode 100644 apps/docs/src/bindings/HeadlessUI.res delete mode 100644 apps/docs/src/bindings/Jsdom.res delete mode 100644 apps/docs/src/bindings/Remark.res delete mode 100644 apps/docs/src/common/EnableCollapsibleNavbar.res delete mode 100644 apps/docs/src/components/ApiIntro.res delete mode 100644 apps/docs/src/components/ApiIntro.resi delete mode 100644 apps/docs/src/components/ApiMarkdown.res delete mode 100644 apps/docs/src/components/ApiMarkdown.resi delete mode 100644 apps/docs/src/components/CodeMirrorSetup.js delete mode 100644 apps/docs/src/components/Image.res delete mode 100644 apps/docs/src/components/Intro.res delete mode 100644 apps/docs/src/components/Navigation.res delete mode 100644 apps/docs/src/components/Navigation.resi delete mode 100644 apps/docs/src/components/Video.res delete mode 100644 apps/docs/src/data/MetaTagsApi.res delete mode 100644 apps/docs/src/ffi/react-codemirror-hooks.js delete mode 100644 apps/docs/src/markdown/DocFrontmatter.res delete mode 100644 apps/docs/src/markdown/DocFrontmatter.resi delete mode 100644 apps/docs/src/shims/Shims.res delete mode 100644 apps/docs/src/shims/_shims.mjs delete mode 100644 apps/docs/stdlib-toc.json delete mode 100644 apps/docs/styles/utils.css delete mode 100644 packages/shared/src/ScrollLockContext.res create mode 100644 scripts/__tests__/check-dead-code.test.mjs create mode 100644 scripts/check-dead-code.mjs diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 941da3094..a314fc3b6 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -37,6 +37,10 @@ jobs: run: yarn - name: Test examples and hrefs run: yarn test + - name: Check unused ReScript values and exports + run: yarn check:dead-code + - name: Test dead-code diagnostics + run: node --test scripts/__tests__/check-dead-code.test.mjs # Confirms that Wrangler can start the Docs Pages and Guide local dev servers. # This startup check intentionally excludes route-level application and Pages Function behavior. Wrangler: diff --git a/.gitignore b/.gitignore index 177e198e7..4b699a440 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,6 @@ node_modules/ .next/ out/ apps/docs/out/ -index_data/*.json -apps/docs/index_data/*.json # Generated via docs test examples script temp @@ -36,9 +34,7 @@ apps/guide/src/**/*.mjs apps/guide/src/**/*.jsx packages/*/src/**/*.mjs packages/*/src/**/*.jsx -apps/docs/scripts/gendocs.mjs apps/docs/scripts/generate_*.mjs -apps/docs/scripts/gendocs.jsx apps/docs/scripts/generate_*.jsx apps/docs/scripts/LogAlgoliaEnvStatus.jsx apps/docs/scripts/__tests__/**/*.jsx @@ -80,8 +76,6 @@ apps/guide/__tests__/**/*.mjs apps/guide/__tests__/**/*.jsx apps/docs/e2e/**/*.mjs apps/docs/e2e/**/*.jsx -!_shims.mjs -!_shims.jsx # Yarn .yarn/* diff --git a/.oxfmtrc.json b/.oxfmtrc.json index f4e51e821..51dc686ee 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -3,7 +3,6 @@ "printWidth": 80, "sortPackageJson": false, "ignorePatterns": [ - "!_shims.mjs", "!apps/docs/public/_redirects", ".DS_Store", ".worktrees/", @@ -17,7 +16,6 @@ "data/api/**/*.json", "apps/docs/data/api/**/*.json", "dist", - "apps/docs/index_data/*.json", "node_modules/", "apps/docs/out/", "apps/docs/public/playground-bundles/", @@ -33,7 +31,6 @@ "apps/docs/lib/", ".vercel", "apps/docs/src/**/*.mjs", - "apps/docs/scripts/gendocs.mjs", "apps/docs/scripts/generate_*.mjs", "apps/docs/public/llms/manual/**/llm*.txt", "apps/docs/public/llms/react/**/llm*.txt", diff --git a/AGENTS.md b/AGENTS.md index 20a6c4e85..a3fe16175 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,7 +25,6 @@ data/ → Hand-curated data (API docs JSON, sidebar ordering) styles/ → CSS files (Tailwind v4 config in main.css, custom utilities) scripts/ → Build/codegen scripts (ReScript + JS) functions/ → Cloudflare Pages Functions (e.g. OG image generation) -compilers/ → Bundled ReScript compiler versions (for the playground) plugins/ → HighlightJS & CodeMirror plugins public/ → Static assets (images, fonts, favicons) __tests__/ → Vitest browser-mode tests (Playwright) diff --git a/README.md b/README.md index 2630705a2..2b8610bdc 100644 --- a/README.md +++ b/README.md @@ -80,11 +80,22 @@ Production crawler start URLs, ranking, and crawler schedules live in the Algoli - `styles/`: Tailwind v4 theme and custom CSS - `public/`: Static assets such as images, fonts, and favicons - `plugins/`: HighlightJS, CodeMirror, and other content/build plugins -- `compilers/`: Bundled ReScript compiler versions for playground and example validation - `__tests__/`: Vitest browser tests written in ReScript Tailwind is configured in [`styles/main.css`](styles/main.css). There is no `tailwind.config.js`. +## Dead-code checks + +`yarn check:dead-code` compiles every workspace and runs reanalyze across the +monorepo, including FFI exports. CI fails on unused values and exports. Bindings +whose names start with `_` are treated as intentional discards. Record fields, +variant cases, and argument diagnostics remain advisory because JavaScript and +FFI access can be invisible to the analyzer; inspect them with `yarn reanalyze`. + +React Router and Cloudflare call exports from JavaScript. Their entry points are +marked `@live` individually. Keep these annotations limited to real entry points; +check JavaScript imports and MDX mappings before removing a reported export. + ## Common Commands | Command | Purpose | diff --git a/apps/docs/__tests__/Url_.test.res b/apps/docs/__tests__/Url_.test.res deleted file mode 100644 index fd29c2185..000000000 --- a/apps/docs/__tests__/Url_.test.res +++ /dev/null @@ -1,29 +0,0 @@ -open Vitest - -test("Url.parse splits an unversioned route into path segments", async () => { - let result = Url.parse("/docs/manual/introduction") - expect(result.base)->toEqual(["docs", "manual", "introduction"]) - expect(result.pagepath)->toEqual([]) - expect(result.fullpath)->toEqual(["docs", "manual", "introduction"]) -}) - -test("Url.parse treats version-like segments as ordinary path content", async () => { - let result = Url.parse("/docs/manual/v12.0.0/introduction") - expect(result.base)->toEqual(["docs", "manual", "v12.0.0", "introduction"]) - expect(result.pagepath)->toEqual([]) - expect(result.fullpath)->toEqual(["docs", "manual", "v12.0.0", "introduction"]) -}) - -test("Url.parse treats latest as ordinary path content", async () => { - let result = Url.parse("/docs/manual/latest/arrays") - expect(result.base)->toEqual(["docs", "manual", "latest", "arrays"]) - expect(result.pagepath)->toEqual([]) - expect(result.fullpath)->toEqual(["docs", "manual", "latest", "arrays"]) -}) - -test("Url.parse parses routes outside docs without special handling", async () => { - let result = Url.parse("/community/overview") - expect(result.base)->toEqual(["community", "overview"]) - expect(result.pagepath)->toEqual([]) - expect(result.fullpath)->toEqual(["community", "overview"]) -}) diff --git a/apps/docs/app/DocsRoot.res b/apps/docs/app/DocsRoot.res index 3e38c1f3d..9cd1473e8 100644 --- a/apps/docs/app/DocsRoot.res +++ b/apps/docs/app/DocsRoot.res @@ -6,7 +6,7 @@ external hljsCss: string = "default" open ReactRouter -@react.component +@react.component @live let default = () => { diff --git a/apps/docs/app/DocsRoot.resi b/apps/docs/app/DocsRoot.resi index e9a4c6ad1..cde121a48 100644 --- a/apps/docs/app/DocsRoot.resi +++ b/apps/docs/app/DocsRoot.resi @@ -1,5 +1,5 @@ /** Shared shell, token styles, and the stable Cypress bootstrap slot. Runtime grammars belong to content routes and the playground. */ /** Keep server-rendered content visible; head stylesheets handle render blocking. */ -@react.component +@react.component @live let default: unit => Jsx.element diff --git a/apps/docs/app/DocsRoutes.res b/apps/docs/app/DocsRoutes.res index 35886a279..d2ec266a6 100644 --- a/apps/docs/app/DocsRoutes.res +++ b/apps/docs/app/DocsRoutes.res @@ -1,5 +1,6 @@ open ReactRouter.Routes +@live let stdlibPaths = { let rawFile = await Node.Fs.readFile("./markdown-pages/docs/api/stdlib.json", "utf-8") let json = JSON.parseOrThrow(rawFile) @@ -72,6 +73,7 @@ let syntaxLookupDetailRoutes = route(path, "./routes/SyntaxLookupDetailRoute.jsx", ~options={id: path}) ) +@live let default = [ layout("./layouts/HomepageLayoutRoute.jsx", [index("./routes/LandingPageRoute.jsx")]), route("try", "./routes/TryRoute.jsx"), diff --git a/apps/docs/app/DocsRoutes.resi b/apps/docs/app/DocsRoutes.resi index ba1514a46..e8fb86cd6 100644 --- a/apps/docs/app/DocsRoutes.resi +++ b/apps/docs/app/DocsRoutes.resi @@ -1,14 +1,5 @@ +// Consumed by react-router.config.mjs and app/routes.js. +@live let stdlibPaths: array -let domPaths: array -let beltPaths: array -let stdlibRoutes: array -let domRoutes: array -let beltRoutes: array -let blogArticleRoutes: array -let docsManualRoutes: array -let docsReactRoutes: array -let docsGuidesRoutes: array -let communityRoutes: array -let syntaxLookupDetailRoutes: array -/** Keep homepage, playground, and content styles scoped to their route groups. */ +@live let default: array diff --git a/apps/docs/app/layouts/ContentLayoutRoute.res b/apps/docs/app/layouts/ContentLayoutRoute.res index a03d7b926..d862b178a 100644 --- a/apps/docs/app/layouts/ContentLayoutRoute.res +++ b/apps/docs/app/layouts/ContentLayoutRoute.res @@ -3,10 +3,11 @@ external contentCss: string = "default" type stylesheet = {rel: string, href: string} +@live let links = () => [{rel: "stylesheet", href: contentCss}] // Route-module initialization runs before any content children render. let () = ContentHighlighting.register(HighlightLanguages.defaultInstance) -@react.component +@react.component @live let default = () => diff --git a/apps/docs/app/layouts/ContentLayoutRoute.resi b/apps/docs/app/layouts/ContentLayoutRoute.resi index 381da2b21..d11cb3818 100644 --- a/apps/docs/app/layouts/ContentLayoutRoute.resi +++ b/apps/docs/app/layouts/ContentLayoutRoute.resi @@ -1,7 +1,8 @@ type stylesheet = {rel: string, href: string} +@live let links: unit => array /** Own content styles and runtime highlighting without adding a DOM wrapper. */ -@react.component +@react.component @live let default: unit => React.element diff --git a/apps/docs/app/layouts/DocsLayoutRoute.res b/apps/docs/app/layouts/DocsLayoutRoute.res index fb19c2034..65a1dd283 100644 --- a/apps/docs/app/layouts/DocsLayoutRoute.res +++ b/apps/docs/app/layouts/DocsLayoutRoute.res @@ -1,4 +1,4 @@ -@react.component +@react.component @live let default = () => { let location = ReactRouter.useLocation() diff --git a/apps/docs/app/layouts/DocsLayoutRoute.resi b/apps/docs/app/layouts/DocsLayoutRoute.resi index 47fa1ac2e..30cde7b0b 100644 --- a/apps/docs/app/layouts/DocsLayoutRoute.resi +++ b/apps/docs/app/layouts/DocsLayoutRoute.resi @@ -1,2 +1,2 @@ -@react.component +@react.component @live let default: unit => React.element diff --git a/apps/docs/app/layouts/HomepageLayoutRoute.res b/apps/docs/app/layouts/HomepageLayoutRoute.res index c8bdddb7d..fc3990af6 100644 --- a/apps/docs/app/layouts/HomepageLayoutRoute.res +++ b/apps/docs/app/layouts/HomepageLayoutRoute.res @@ -3,7 +3,8 @@ external homepageCss: string = "default" type stylesheet = {rel: string, href: string} +@live let links = () => [{rel: "stylesheet", href: homepageCss}] -@react.component +@react.component @live let default = () => diff --git a/apps/docs/app/layouts/HomepageLayoutRoute.resi b/apps/docs/app/layouts/HomepageLayoutRoute.resi index bf0517cf9..61ed8faa7 100644 --- a/apps/docs/app/layouts/HomepageLayoutRoute.resi +++ b/apps/docs/app/layouts/HomepageLayoutRoute.resi @@ -1,7 +1,8 @@ type stylesheet = {rel: string, href: string} +@live let links: unit => array /** Own homepage styles without adding a DOM wrapper. */ -@react.component +@react.component @live let default: unit => React.element diff --git a/apps/docs/app/routes/ApiDocs.res b/apps/docs/app/routes/ApiDocs.res index 3d5df459b..36c054158 100644 --- a/apps/docs/app/routes/ApiDocs.res +++ b/apps/docs/app/routes/ApiDocs.res @@ -258,7 +258,8 @@ module DocstringsStylize = { } } -@react.componentWithProps +// reanalyze does not see the JSX props spread call in ApiRoute. +@live @react.componentWithProps let make = (props: props) => { let children = { open Markdown @@ -342,12 +343,6 @@ module Data = { tree: Dict.t, } - let dir = try { - Node.Path.resolve("data", "api") - } catch { - | _ => "" - } - let getVersion = (~moduleName: string) => { open Node diff --git a/apps/docs/app/routes/ApiOverviewRoute.res b/apps/docs/app/routes/ApiOverviewRoute.res index b267717c0..6ee7999f2 100644 --- a/apps/docs/app/routes/ApiOverviewRoute.res +++ b/apps/docs/app/routes/ApiOverviewRoute.res @@ -21,6 +21,7 @@ type loaderData = { description: string, } +@live let loader: ReactRouter.Loader.t = async ({request}) => { let {pathname} = WebAPI.URL.make(~url=request.url) let filePath = MdxFile.resolveFilePath( @@ -43,6 +44,7 @@ let loader: ReactRouter.Loader.t = async ({request}) => { } } +@live let default = () => { let {compiledMdx, title, description} = ReactRouter.useLoaderData() diff --git a/apps/docs/app/routes/ApiOverviewRoute.resi b/apps/docs/app/routes/ApiOverviewRoute.resi index 44e515171..6d1265c42 100644 --- a/apps/docs/app/routes/ApiOverviewRoute.resi +++ b/apps/docs/app/routes/ApiOverviewRoute.resi @@ -1,11 +1,11 @@ -let categories: array - type loaderData = { compiledMdx: CompiledMdx.t, title: string, description: string, } +@live let loader: ReactRouter.Loader.t +@live let default: unit => React.element diff --git a/apps/docs/app/routes/ApiRoute.res b/apps/docs/app/routes/ApiRoute.res index 5ec2579f1..345c7ca84 100644 --- a/apps/docs/app/routes/ApiRoute.res +++ b/apps/docs/app/routes/ApiRoute.res @@ -121,6 +121,7 @@ let makeBreadcrumbs = (~prefix: Url.breadcrumb, route: Path.t): listList.fromArray } +@live let loader: ReactRouter.Loader.t = async args => { let path = WebAPI.URL.make(~url=args.request.url).pathname @@ -160,6 +161,7 @@ let loader: ReactRouter.Loader.t = async args => { }) } +@live let default = () => { let loaderData: loaderData = ReactRouter.useLoaderData() let {pathname} = ReactRouter.useLocation() diff --git a/apps/docs/app/routes/ApiRoute.resi b/apps/docs/app/routes/ApiRoute.resi index 41d12d95f..8169e8d51 100644 --- a/apps/docs/app/routes/ApiRoute.resi +++ b/apps/docs/app/routes/ApiRoute.resi @@ -9,6 +9,8 @@ type rec apiItem = { } /** Loads API documentation after normalizing React Router data-request pathnames. */ +@live let loader: ReactRouter.Loader.t +@live let default: unit => React.element diff --git a/apps/docs/app/routes/BlogArticleRoute.res b/apps/docs/app/routes/BlogArticleRoute.res index 6644a1509..6356e6e66 100644 --- a/apps/docs/app/routes/BlogArticleRoute.res +++ b/apps/docs/app/routes/BlogArticleRoute.res @@ -4,6 +4,7 @@ type loaderData = { title: string, } +@live let loader: ReactRouter.Loader.t = async ({request}) => { let {pathname} = WebAPI.URL.make(~url=request.url) let filePath = MdxFile.resolveFilePath( @@ -45,6 +46,7 @@ let loader: ReactRouter.Loader.t = async ({request}) => { } } +@live let default = () => { let {compiledMdx, blogPost: {frontmatter, archived, path}} = ReactRouter.useLoaderData() diff --git a/apps/docs/app/routes/BlogArticleRoute.resi b/apps/docs/app/routes/BlogArticleRoute.resi index 40ba9580c..c459baab4 100644 --- a/apps/docs/app/routes/BlogArticleRoute.resi +++ b/apps/docs/app/routes/BlogArticleRoute.resi @@ -4,6 +4,8 @@ type loaderData = { title: string, } +@live let loader: ReactRouter.Loader.t +@live let default: unit => React.element diff --git a/apps/docs/app/routes/BlogRoute.res b/apps/docs/app/routes/BlogRoute.res index d3999e6f4..bc1ed6704 100644 --- a/apps/docs/app/routes/BlogRoute.res +++ b/apps/docs/app/routes/BlogRoute.res @@ -1,5 +1,6 @@ type loaderData = {posts: array, category: Blog.category} +@live let loader: ReactRouter.Loader.t = async ({request}) => { let showArchived = request.url->String.includes("archived") let posts = async () => @@ -17,7 +18,7 @@ let loader: ReactRouter.Loader.t = async ({request}) => { data } -@react.component +@react.component @live let default = () => { let {posts, category} = ReactRouter.useLoaderData() <> diff --git a/apps/docs/app/routes/BlogRoute.resi b/apps/docs/app/routes/BlogRoute.resi index 34ac9a2a6..d3a21e01a 100644 --- a/apps/docs/app/routes/BlogRoute.resi +++ b/apps/docs/app/routes/BlogRoute.resi @@ -1,6 +1,7 @@ type loaderData = {posts: array, category: Blog.category} +@live let loader: ReactRouter.Loader.t -@react.component +@react.component @live let default: unit => React.element diff --git a/apps/docs/app/routes/BrandRoute.res b/apps/docs/app/routes/BrandRoute.res index 69a8d4d9e..7415646c8 100644 --- a/apps/docs/app/routes/BrandRoute.res +++ b/apps/docs/app/routes/BrandRoute.res @@ -22,6 +22,7 @@ let assetCard = (~title, ~description, ~image, ~imageClassName="", ~dark=false, } +@live let default = () => { React.element diff --git a/apps/docs/app/routes/CommunityRoute.res b/apps/docs/app/routes/CommunityRoute.res index 9fc078338..11572c331 100644 --- a/apps/docs/app/routes/CommunityRoute.res +++ b/apps/docs/app/routes/CommunityRoute.res @@ -19,6 +19,7 @@ let communityTableOfContents = async () => { SidebarHelpers.getAllGroups(groups, ["Resources"]) } +@live let loader: ReactRouter.Loader.t = async ({request}) => { let {pathname} = WebAPI.URL.make(~url=request.url) let filePath = MdxFile.resolveFilePath( @@ -49,6 +50,7 @@ let loader: ReactRouter.Loader.t = async ({request}) => { } } +@live let default = () => { let {compiledMdx, entries, filePath, categories} = ReactRouter.useLoaderData() diff --git a/apps/docs/app/routes/CommunityRoute.resi b/apps/docs/app/routes/CommunityRoute.resi index 456908ac9..7a46a4347 100644 --- a/apps/docs/app/routes/CommunityRoute.resi +++ b/apps/docs/app/routes/CommunityRoute.resi @@ -7,6 +7,8 @@ type loaderData = { categories: array, } +@live let loader: ReactRouter.Loader.t +@live let default: unit => React.element diff --git a/apps/docs/app/routes/DocsGuidesRoute.res b/apps/docs/app/routes/DocsGuidesRoute.res index 820e81d92..478aa060f 100644 --- a/apps/docs/app/routes/DocsGuidesRoute.res +++ b/apps/docs/app/routes/DocsGuidesRoute.res @@ -19,6 +19,7 @@ let guidesTableOfContents = async () => { SidebarHelpers.getAllGroups(groups, ["Overview", "Packages"]) } +@live let loader: ReactRouter.Loader.t = async ({request}) => { let {pathname} = WebAPI.URL.make(~url=request.url) let filePath = MdxFile.resolveFilePath( @@ -49,6 +50,7 @@ let loader: ReactRouter.Loader.t = async ({request}) => { } } +@live let default = () => { let {compiledMdx, categories, entries, title, description, filePath} = ReactRouter.useLoaderData() diff --git a/apps/docs/app/routes/DocsGuidesRoute.resi b/apps/docs/app/routes/DocsGuidesRoute.resi index 6eea0299c..1f083c417 100644 --- a/apps/docs/app/routes/DocsGuidesRoute.resi +++ b/apps/docs/app/routes/DocsGuidesRoute.resi @@ -7,6 +7,8 @@ type loaderData = { filePath: string, } +@live let loader: ReactRouter.Loader.t +@live let default: unit => React.element diff --git a/apps/docs/app/routes/DocsManualRoute.res b/apps/docs/app/routes/DocsManualRoute.res index 9c163bb03..94dcf6f2f 100644 --- a/apps/docs/app/routes/DocsManualRoute.res +++ b/apps/docs/app/routes/DocsManualRoute.res @@ -30,6 +30,7 @@ let manualTableOfContents = async () => { ) } +@live let loader: ReactRouter.Loader.t = async ({request}) => { let {pathname} = WebAPI.URL.make(~url=request.url) let filePath = MdxFile.resolveFilePath( @@ -60,6 +61,7 @@ let loader: ReactRouter.Loader.t = async ({request}) => { } } +@live let default = () => { let {compiledMdx, categories, entries, title, description, filePath} = ReactRouter.useLoaderData() diff --git a/apps/docs/app/routes/DocsManualRoute.resi b/apps/docs/app/routes/DocsManualRoute.resi index 6eea0299c..1f083c417 100644 --- a/apps/docs/app/routes/DocsManualRoute.resi +++ b/apps/docs/app/routes/DocsManualRoute.resi @@ -7,6 +7,8 @@ type loaderData = { filePath: string, } +@live let loader: ReactRouter.Loader.t +@live let default: unit => React.element diff --git a/apps/docs/app/routes/DocsReactRoute.res b/apps/docs/app/routes/DocsReactRoute.res index 8e3dcb073..693964d32 100644 --- a/apps/docs/app/routes/DocsReactRoute.res +++ b/apps/docs/app/routes/DocsReactRoute.res @@ -23,6 +23,7 @@ let reactTableOfContents = async () => { ) } +@live let loader: ReactRouter.Loader.t = async ({request}) => { let {pathname} = WebAPI.URL.make(~url=request.url) let filePath = MdxFile.resolveFilePath( @@ -53,6 +54,7 @@ let loader: ReactRouter.Loader.t = async ({request}) => { } } +@live let default = () => { let {compiledMdx, categories, entries, title, description, filePath} = ReactRouter.useLoaderData() diff --git a/apps/docs/app/routes/DocsReactRoute.resi b/apps/docs/app/routes/DocsReactRoute.resi index 6eea0299c..1f083c417 100644 --- a/apps/docs/app/routes/DocsReactRoute.resi +++ b/apps/docs/app/routes/DocsReactRoute.resi @@ -7,6 +7,8 @@ type loaderData = { filePath: string, } +@live let loader: ReactRouter.Loader.t +@live let default: unit => React.element diff --git a/apps/docs/app/routes/LandingPageRoute.res b/apps/docs/app/routes/LandingPageRoute.res index fd83445f4..f40730e6d 100644 --- a/apps/docs/app/routes/LandingPageRoute.res +++ b/apps/docs/app/routes/LandingPageRoute.res @@ -16,6 +16,7 @@ let fontPreload = (~media=?, href) => { ?media, } +@live let links = () => [ fontPreload("/fonts/subset-Inter-Regular.woff2"), fontPreload("/fonts/subset-Inter-SemiBold.woff2"), @@ -25,11 +26,12 @@ let links = () => [ type loaderData = {playgroundData: LandingPagePlayground.playgroundData} +@live let loader: ReactRouter.Loader.t = async _ => { playgroundData: LandingPagePlaygroundLoader.build(), } -@react.component +@react.component @live let default = () => { let {playgroundData}: loaderData = ReactRouter.useLoaderData() diff --git a/apps/docs/app/routes/LandingPageRoute.resi b/apps/docs/app/routes/LandingPageRoute.resi index 1c6062553..c1ab3df4e 100644 --- a/apps/docs/app/routes/LandingPageRoute.resi +++ b/apps/docs/app/routes/LandingPageRoute.resi @@ -8,12 +8,14 @@ type fontPreload = { } /** Preload above-the-fold faces, including Inter 700 only at the desktop breakpoint. */ +@live let links: unit => array type loaderData = {playgroundData: LandingPagePlayground.playgroundData} /** Prerender the fixed example so highlighting and URL compression stay off the client. */ +@live let loader: ReactRouter.Loader.t -@react.component +@react.component @live let default: unit => React.element diff --git a/apps/docs/app/routes/NotFoundRoute.res b/apps/docs/app/routes/NotFoundRoute.res index c594ef16a..43a8b55c7 100644 --- a/apps/docs/app/routes/NotFoundRoute.res +++ b/apps/docs/app/routes/NotFoundRoute.res @@ -1,4 +1,4 @@ -@react.component +@react.component @live let default = () => { let {pathname} = ReactRouter.useLocation() let navigate = ReactRouter.useNavigate() diff --git a/apps/docs/app/routes/NotFoundRoute.resi b/apps/docs/app/routes/NotFoundRoute.resi index 47fa1ac2e..30cde7b0b 100644 --- a/apps/docs/app/routes/NotFoundRoute.resi +++ b/apps/docs/app/routes/NotFoundRoute.resi @@ -1,2 +1,2 @@ -@react.component +@react.component @live let default: unit => React.element diff --git a/apps/docs/app/routes/PackagesRoute.res b/apps/docs/app/routes/PackagesRoute.res index 837f7aaf0..ffd00544b 100644 --- a/apps/docs/app/routes/PackagesRoute.res +++ b/apps/docs/app/routes/PackagesRoute.res @@ -1,9 +1,11 @@ +@live let loader = async () => { let props = await Packages.getStaticProps() props } +@live let default = () => { let props = ReactRouter.useLoaderData() diff --git a/apps/docs/app/routes/PackagesRoute.resi b/apps/docs/app/routes/PackagesRoute.resi index fc06a02b0..4da28a456 100644 --- a/apps/docs/app/routes/PackagesRoute.resi +++ b/apps/docs/app/routes/PackagesRoute.resi @@ -1,2 +1,4 @@ +@live let loader: unit => promise +@live let default: unit => React.element diff --git a/apps/docs/app/routes/SyntaxLookup.res b/apps/docs/app/routes/SyntaxLookup.res index fdddb1a60..2c88212aa 100644 --- a/apps/docs/app/routes/SyntaxLookup.res +++ b/apps/docs/app/routes/SyntaxLookup.res @@ -51,16 +51,6 @@ module Category = { | _ => Other } } - - @react.component - let make = (~title, ~children) => { -
-

- {React.string(title)} -

-
children
-
- } } module Status = { @@ -119,15 +109,6 @@ module Item = { } } -type itemInfo = { - id: string, - keywords: array, - name: string, - summary: string, - category: Category.t, - status: Status.t, -} - module Tag = { @react.component let make = (~deprecated: bool, ~text: string, ~href) => { @@ -174,40 +155,6 @@ type state = | ShowFiltered(string, array) // (search, filteredItems) | ShowDetails(Item.t) -let scrollToTop = () => WebAPI.Window.scrollTo(window, ~options={left: 0.0, top: 0.0}) - -type params = {slug: string} - -let decode = (json: JSON.t) => { - open JSON - switch json { - | Object(dict{ - "id": String(id), - "keywords": Array(keywords), - "name": String(name), - "summary": String(summary), - "category": String(category), - "status": ?status, - }) => { - id, - name, - summary, - category: Category.fromString(category), - keywords: keywords->Array.filterMap(k => - switch k { - | String(k) => Some(k) - | _ => None - } - ), - status: switch status { - | Some(String(status)) => status->Status.fromString - | _ => Status.Active - }, - } - | _ => throw(Failure(`Failed to decode SyntaxLookup. ${__LOC__}`)) - } -} - type item = { id: string, keywords: array, diff --git a/apps/docs/app/routes/SyntaxLookupDetailRoute.res b/apps/docs/app/routes/SyntaxLookupDetailRoute.res index b5bdfda99..b1d7b1f7d 100644 --- a/apps/docs/app/routes/SyntaxLookupDetailRoute.res +++ b/apps/docs/app/routes/SyntaxLookupDetailRoute.res @@ -6,6 +6,7 @@ type loaderData = { description: string, } +@live let loader: ReactRouter.Loader.t = async ({request}) => { let {pathname} = WebAPI.URL.make(~url=request.url) let filePath = MdxFile.resolveFilePath( @@ -38,6 +39,7 @@ let loader: ReactRouter.Loader.t = async ({request}) => { } } +@live let default = () => { let {compiledMdx, mdxSources, activeSyntaxItem} = ReactRouter.useLoaderData() diff --git a/apps/docs/app/routes/SyntaxLookupDetailRoute.resi b/apps/docs/app/routes/SyntaxLookupDetailRoute.resi index 5436eda52..e6b480cac 100644 --- a/apps/docs/app/routes/SyntaxLookupDetailRoute.resi +++ b/apps/docs/app/routes/SyntaxLookupDetailRoute.resi @@ -6,6 +6,8 @@ type loaderData = { description: string, } +@live let loader: ReactRouter.Loader.t +@live let default: unit => React.element diff --git a/apps/docs/app/routes/SyntaxLookupRoute.res b/apps/docs/app/routes/SyntaxLookupRoute.res index 12d09d42c..74ce34245 100644 --- a/apps/docs/app/routes/SyntaxLookupRoute.res +++ b/apps/docs/app/routes/SyntaxLookupRoute.res @@ -13,6 +13,7 @@ let convert = (mdx: Mdx.attributes): SyntaxLookup.item => { } } +@live let loader: Loader.t = async _ => { let mdxSources = (await MdxFile.loadAllAttributes(~dir="markdown-pages/syntax-lookup"))->Array.map(convert) @@ -22,6 +23,7 @@ let loader: Loader.t = async _ => { } } +@live let default = () => { let {mdxSources} = useLoaderData() <> diff --git a/apps/docs/app/routes/SyntaxLookupRoute.resi b/apps/docs/app/routes/SyntaxLookupRoute.resi index fa1131538..e7f2f6f88 100644 --- a/apps/docs/app/routes/SyntaxLookupRoute.resi +++ b/apps/docs/app/routes/SyntaxLookupRoute.resi @@ -1,5 +1,7 @@ type loaderData = {mdxSources: array} +@live let loader: ReactRouter.Loader.t +@live let default: unit => React.element // TODO: move this out of the route file so it can export only a single react component let convert: Mdx.attributes => SyntaxLookup.item diff --git a/apps/docs/app/routes/TryRoute.res b/apps/docs/app/routes/TryRoute.res index 22785fe5b..dcebe3768 100644 --- a/apps/docs/app/routes/TryRoute.res +++ b/apps/docs/app/routes/TryRoute.res @@ -3,6 +3,7 @@ external playgroundCss: string = "default" type stylesheet = {rel: string, href: string} +@live let links = () => [{rel: "stylesheet", href: playgroundCss}] type props = { @@ -10,6 +11,7 @@ type props = { versions: array, } +@live let loader = async () => { let (bundleBaseUrl, versionsBaseUrl) = switch ( Node.Process.Env.playgroundBundleEndpoint, @@ -58,6 +60,7 @@ module ClientOnly = { } } +@live let default = () => { let data = ReactRouter.useLoaderData() <> diff --git a/apps/docs/app/routes/TryRoute.resi b/apps/docs/app/routes/TryRoute.resi index 65e640435..9285f0b75 100644 --- a/apps/docs/app/routes/TryRoute.resi +++ b/apps/docs/app/routes/TryRoute.resi @@ -1,5 +1,6 @@ type stylesheet = {rel: string, href: string} +@live let links: unit => array type props = { @@ -7,6 +8,8 @@ type props = { versions: array, } +@live let loader: unit => promise> +@live let default: unit => React.element diff --git a/apps/docs/compilers/README.md b/apps/docs/compilers/README.md deleted file mode 100644 index ceebd0b28..000000000 --- a/apps/docs/compilers/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# compilers - -This folder is needed to install additional rescript compiler versions for testing our documented source code on multiple compiler versions. - -Our `scripts/test-examples.mjs` script will automatically install all package.json dependencies if it cannot find any compiler versions. diff --git a/apps/docs/compilers/dummy/Dummy.res b/apps/docs/compilers/dummy/Dummy.res deleted file mode 100644 index 4992ee3d2..000000000 --- a/apps/docs/compilers/dummy/Dummy.res +++ /dev/null @@ -1 +0,0 @@ -Console.debug("I am a dummy file") diff --git a/apps/docs/compilers/package-lock.json b/apps/docs/compilers/package-lock.json deleted file mode 100644 index 9a20b57aa..000000000 --- a/apps/docs/compilers/package-lock.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "name": "compilers", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "compilers", - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@rescript/core": "^1.3.0", - "@rescript/react": "^0.12.0", - "rescript-1000": "npm:rescript@10.0.0", - "rescript-1010": "npm:rescript@10.1.0", - "rescript-1100": "npm:rescript@11.0.0", - "rescript-1110": "npm:rescript@11.1.0", - "rescript-820": "npm:bs-platform@8.2.0", - "rescript-902": "npm:bs-platform@9.0.2", - "rescript-912": "npm:rescript@9.1.2" - } - }, - "node_modules/@rescript/core": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@rescript/core/-/core-1.3.0.tgz", - "integrity": "sha512-wNZOZ63sYcaIYZCmTZeIPCeLa3HCGgPbIOR8zjyNkoBYUlxNV8Nb2ZyqlXR5Mb9ttvv8fTV56JbKhyVEZEYo8g==", - "peerDependencies": { - "rescript": "^11.1.0-rc.7" - } - }, - "node_modules/@rescript/react": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@rescript/react/-/react-0.12.0.tgz", - "integrity": "sha512-EBLsf5rD7sJOjgfLLGwuLw/hONszc3UtYnIVgv7OdTyUNR41/m4deVm62PI0agvr3kWakXz4KchKRSd+19/bRA==", - "peerDependencies": { - "react": ">=18.0.0", - "react-dom": ">=18.0.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "peer": true - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/rescript": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.1.1.tgz", - "integrity": "sha512-FMELeoiR1n3LzBdBt+k7U4l0vsz5Xh0HBSHf+0NhyhzZkMRLkEKEDNrcqZc6RIux9bxmxoO+zEa9qFM01VOXAw==", - "hasInstallScript": true, - "peer": true, - "bin": { - "bsc": "bsc", - "bstracing": "lib/bstracing", - "rescript": "rescript" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/rescript-1000": { - "name": "rescript", - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-10.0.0.tgz", - "integrity": "sha512-LhNg/4+0j8NvoFeslgAeYLlzUwkq6kR6l6v8BnZ61VDTxopK2l96uT1lq5lv1aMxzMDynvE2qnX0zalre+6XxA==", - "hasInstallScript": true, - "bin": { - "bsc": "bsc", - "bsrefmt": "bsrefmt", - "bstracing": "lib/bstracing", - "rescript": "rescript" - } - }, - "node_modules/rescript-1010": { - "name": "rescript", - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-10.1.0.tgz", - "integrity": "sha512-85KLtBECo+hYwObbifXk5xpxUCUFMWgCR1w7hDDGhhXIXqEpHQ2ds6o++YPxzXjLuU1/qA7Iovq2zUX4opejQw==", - "hasInstallScript": true, - "bin": { - "bsc": "bsc", - "bsrefmt": "bsrefmt", - "bstracing": "lib/bstracing", - "rescript": "rescript" - } - }, - "node_modules/rescript-1100": { - "name": "rescript", - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.0.0.tgz", - "integrity": "sha512-uIUwDZZmDUb7ymGkBiiGioxMg8hXh1mze/2k/qhYQcZGgi7PrLHQIW9AksM7gb9WnpjCAvFsA8U2VgC0nA468w==", - "hasInstallScript": true, - "bin": { - "bsc": "bsc", - "bstracing": "lib/bstracing", - "rescript": "rescript" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/rescript-1110": { - "name": "rescript", - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.1.0.tgz", - "integrity": "sha512-9la2Dv+ACylQ77I8s4spPu1JnLZXbH5WgxcLHLLUBWgFFSiv0wXqgzWztrBIZqwFgVX5BYcwldUqUVcEzdCyHg==", - "hasInstallScript": true, - "bin": { - "bsc": "bsc", - "bstracing": "lib/bstracing", - "rescript": "rescript" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/rescript-820": { - "name": "bs-platform", - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-8.2.0.tgz", - "integrity": "sha512-quvmUac/ZxGDsT7L5+6RNXrLPvLHkWFownacaqlwVoyAm770bPyupTRU49ALPGk3HpjfD5eE+lpGdOSPtuwJiA==" - }, - "node_modules/rescript-902": { - "name": "bs-platform", - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-9.0.2.tgz", - "integrity": "sha512-Ye9JqJ4Oa7mcjjoOVRYI8Uc2Cf8N7jQLWDcdUplY7996d/YErSR7WitmV7XnSwr4EvdrbwjEsg1NxNjUQv3ChA==" - }, - "node_modules/rescript-912": { - "name": "rescript", - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-9.1.2.tgz", - "integrity": "sha512-4wHvTDv3nyYnAPJHcg1RGG8z7u3HDiBf6RN3P/dITDv859Qo35aKOzJWQtfBzbAs0EKNafLqei3TnUqiAv6BwQ==", - "hasInstallScript": true, - "bin": { - "bsc": "bsc", - "bsrefmt": "bsrefmt", - "bstracing": "lib/bstracing", - "rescript": "rescript" - } - }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - } - } - }, - "dependencies": { - "@rescript/core": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@rescript/core/-/core-1.3.0.tgz", - "integrity": "sha512-wNZOZ63sYcaIYZCmTZeIPCeLa3HCGgPbIOR8zjyNkoBYUlxNV8Nb2ZyqlXR5Mb9ttvv8fTV56JbKhyVEZEYo8g==", - "requires": {} - }, - "@rescript/react": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@rescript/react/-/react-0.12.0.tgz", - "integrity": "sha512-EBLsf5rD7sJOjgfLLGwuLw/hONszc3UtYnIVgv7OdTyUNR41/m4deVm62PI0agvr3kWakXz4KchKRSd+19/bRA==", - "requires": {} - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "peer": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "peer": true, - "requires": { - "loose-envify": "^1.1.0" - } - }, - "react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "peer": true, - "requires": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - } - }, - "rescript": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.1.1.tgz", - "integrity": "sha512-FMELeoiR1n3LzBdBt+k7U4l0vsz5Xh0HBSHf+0NhyhzZkMRLkEKEDNrcqZc6RIux9bxmxoO+zEa9qFM01VOXAw==", - "peer": true - }, - "rescript-1000": { - "version": "npm:rescript@10.0.0", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-10.0.0.tgz", - "integrity": "sha512-LhNg/4+0j8NvoFeslgAeYLlzUwkq6kR6l6v8BnZ61VDTxopK2l96uT1lq5lv1aMxzMDynvE2qnX0zalre+6XxA==" - }, - "rescript-1010": { - "version": "npm:rescript@10.1.0", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-10.1.0.tgz", - "integrity": "sha512-85KLtBECo+hYwObbifXk5xpxUCUFMWgCR1w7hDDGhhXIXqEpHQ2ds6o++YPxzXjLuU1/qA7Iovq2zUX4opejQw==" - }, - "rescript-1100": { - "version": "npm:rescript@11.0.0", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.0.0.tgz", - "integrity": "sha512-uIUwDZZmDUb7ymGkBiiGioxMg8hXh1mze/2k/qhYQcZGgi7PrLHQIW9AksM7gb9WnpjCAvFsA8U2VgC0nA468w==" - }, - "rescript-1110": { - "version": "npm:rescript@11.1.0", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.1.0.tgz", - "integrity": "sha512-9la2Dv+ACylQ77I8s4spPu1JnLZXbH5WgxcLHLLUBWgFFSiv0wXqgzWztrBIZqwFgVX5BYcwldUqUVcEzdCyHg==" - }, - "rescript-820": { - "version": "npm:bs-platform@8.2.0", - "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-8.2.0.tgz", - "integrity": "sha512-quvmUac/ZxGDsT7L5+6RNXrLPvLHkWFownacaqlwVoyAm770bPyupTRU49ALPGk3HpjfD5eE+lpGdOSPtuwJiA==" - }, - "rescript-902": { - "version": "npm:bs-platform@9.0.2", - "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-9.0.2.tgz", - "integrity": "sha512-Ye9JqJ4Oa7mcjjoOVRYI8Uc2Cf8N7jQLWDcdUplY7996d/YErSR7WitmV7XnSwr4EvdrbwjEsg1NxNjUQv3ChA==" - }, - "rescript-912": { - "version": "npm:rescript@9.1.2", - "resolved": "https://registry.npmjs.org/rescript/-/rescript-9.1.2.tgz", - "integrity": "sha512-4wHvTDv3nyYnAPJHcg1RGG8z7u3HDiBf6RN3P/dITDv859Qo35aKOzJWQtfBzbAs0EKNafLqei3TnUqiAv6BwQ==" - }, - "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "peer": true, - "requires": { - "loose-envify": "^1.1.0" - } - } - } -} diff --git a/apps/docs/compilers/package.json b/apps/docs/compilers/package.json deleted file mode 100644 index 1cf8ac044..000000000 --- a/apps/docs/compilers/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "compilers", - "version": "1.0.0", - "private": true, - "license": "MIT", - "main": "index.js", - "dependencies": { - "@rescript/core": "^1.3.0", - "@rescript/react": "^0.12.0", - "rescript-1000": "npm:rescript@10.0.0", - "rescript-1010": "npm:rescript@10.1.0", - "rescript-1100": "npm:rescript@11.0.0", - "rescript-1110": "npm:rescript@11.1.0", - "rescript-820": "npm:bs-platform@8.2.0", - "rescript-902": "npm:bs-platform@9.0.2", - "rescript-912": "npm:rescript@9.1.2" - } -} diff --git a/apps/docs/compilers/rescript.json b/apps/docs/compilers/rescript.json deleted file mode 100644 index f135476c7..000000000 --- a/apps/docs/compilers/rescript.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "dummy", - "jsx": { "version": 4 }, - "sources": { - "dir": "dummy", - "subdirs": true - }, - "bs-dependencies": ["@rescript/core", "@rescript/react"], - "bsc-flags": ["-open RescriptCore"] -} diff --git a/apps/docs/e2e/bindings/Cy.res b/apps/docs/e2e/bindings/Cy.res index d34ef610d..537103062 100644 --- a/apps/docs/e2e/bindings/Cy.res +++ b/apps/docs/e2e/bindings/Cy.res @@ -7,9 +7,6 @@ type t @val @scope("cy") external visit: string => unit = "visit" -@val @scope("cy") -external visitWithOptions: (string, {..}) => unit = "visit" - @val @scope("cy") external get: string => t = "get" @@ -19,9 +16,6 @@ external contains: string => t = "contains" @val @scope("cy") external containsSelector: (string, string) => t = "contains" -@val @scope("cy") -external title: unit => t = "title" - @val @scope("cy") external url: unit => t = "url" @@ -31,21 +25,12 @@ external cyLocation: string => t = "location" @val @scope("cy") external wait: int => unit = "wait" -@val @scope("cy") -external reload: unit => unit = "reload" - -@val @scope("cy") -external go: string => unit = "go" - @val @scope("cy") external viewport: (int, int) => unit = "viewport" @val @scope("cy") external cyScrollTo: string => unit = "scrollTo" -@val @scope("cy") -external log: string => unit = "log" - // -- Chainable commands ------------------------------------------------------- // Contains (chainable versions) @@ -55,65 +40,33 @@ external log: string => unit = "log" // Queries @send external find: (t, string) => t = "find" @send external first: t => t = "first" -@send external last: t => t = "last" -@send external eq: (t, int) => t = "eq" -@send external children: t => t = "children" -@send external parent: t => t = "parent" -@send external parents: (t, string) => t = "parents" -@send external closest: (t, string) => t = "closest" -@send external next: t => t = "next" -@send external prev: t => t = "prev" -@send external siblings: t => t = "siblings" -@send external filter: (t, string) => t = "filter" -@send external not_: (t, string) => t = "not" // Actions @send external click: t => t = "click" -@send external clickWithOptions: (t, {..}) => t = "click" -@send external dblclick: t => t = "dblclick" -@send external type_: (t, string) => t = "type" @send external typeWithOptions: (t, string, {..}) => t = "type" -@send external clear: t => t = "clear" -@send external check: t => t = "check" -@send external uncheck: t => t = "uncheck" @send external select: (t, string) => t = "select" -@send external trigger: (t, string) => t = "trigger" @send external scrollIntoView: t => t = "scrollIntoView" -@send external focus: t => t = "focus" -@send external blur: t => t = "blur" // Assertions @send external should: (t, string) => t = "should" @send external shouldWithValue: (t, string, string) => t = "should" @send external shouldWithKeyValue: (t, string, string, string) => t = "should" -@send external and_: (t, string) => t = "and" -@send external andWithValue: (t, string, string) => t = "and" // Traversal -@send external each: (t, t => unit) => t = "each" -@send external then: (t, t => unit) => t = "then" @send external its: (t, string) => t = "its" // Attributes -@send external invoke: (t, string) => t = "invoke" @send external invokeWithArg: (t, string, string) => t = "invoke" -@send external attr: (t, string) => t = "attr" // Visibility & state -@send external asChainable: (t, string) => t = "as" // Yielding -@send external within: (t, unit => unit) => t = "within" // -- Describe / It (Mocha globals) -------------------------------------------- @val external describe: (string, unit => unit) => unit = "describe" @val external it: (string, unit => unit) => unit = "it" -@val external before: (unit => unit) => unit = "before" @val external beforeEach: (unit => unit) => unit = "beforeEach" -@val external after: (unit => unit) => unit = "after" -@val external afterEach: (unit => unit) => unit = "afterEach" -@val external context: (string, unit => unit) => unit = "context" // -- Window ------------------------------------------------------------------- @@ -124,18 +77,8 @@ external cyWindow: unit => t = "window" let getByTestId = testId => get(`[data-testid="${testId}"]`) -let getByRole = role => get(`[role="${role}"]`) - -let getByHref = href => get(`a[href="${href}"]`) - let shouldBeVisible = chain => chain->should("be.visible") -let shouldBeVisibleAndClick = chain => chain->should("be.visible")->click - -let shouldExist = chain => chain->should("exist") - let shouldContainText = (chain, text) => chain->shouldWithValue("contain.text", text) -let shouldHaveAttr = (chain, attr, value) => chain->shouldWithKeyValue("have.attr", attr, value) - let shouldInclude = (chain, value) => chain->shouldWithValue("include", value) diff --git a/apps/docs/e2e/bindings/Cypress.res b/apps/docs/e2e/bindings/Cypress.res index be8c3c607..a0666ddc2 100644 --- a/apps/docs/e2e/bindings/Cypress.res +++ b/apps/docs/e2e/bindings/Cypress.res @@ -82,7 +82,6 @@ external interceptDeferredRequest: (routeMatcher, request => promise) => c @val @scope("cy") external onBeforeLoad: (@as("window:before:load") _, window => unit) => unit = "on" @val @scope("cy") external spy: (console, @as("error") _) => spy = "spy" -@get external callCount: spy => int = "callCount" @val @scope("Cypress") external automate: (@as("remote:debugger:protocol") _, automation) => promise = "automation" @val @scope("Cypress") external baseUrl: @as("baseUrl") _ => string = "config" diff --git a/apps/docs/functions/ogimage/[[path]]/index.png.res b/apps/docs/functions/ogimage/[[path]]/index.png.res index e3526a3f3..7e55d19d7 100644 --- a/apps/docs/functions/ogimage/[[path]]/index.png.res +++ b/apps/docs/functions/ogimage/[[path]]/index.png.res @@ -254,6 +254,7 @@ let renderImage = async (~assets, ~requestUrl: URLAPI.url, ~targetUrl: URLAPI.ur } } +@live let onRequest = async ({request, params, env}: context) => { let requestUrl = URL.make(~url=request.url) diff --git a/apps/docs/package.json b/apps/docs/package.json index e7350a978..30e23caf7 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -32,7 +32,7 @@ "prepare": "yarn build:res && yarn build:scripts && yarn check:algolia-public-env && yarn build:update-index", "preview": "yarn build && static-server build/client", "serve:build": "static-server --host 127.0.0.1 --port 4173 build/client", - "reanalyze": "rescript-tools reanalyze -all-cmt .", + "reanalyze": "cd ../.. && yarn reanalyze", "test": "node scripts/test.mjs", "test:e2e": "yarn build && bash ../../.github/scripts/homepage-e2e.sh", "cy:open": "yarn build:res && cypress open --e2e --browser electron", @@ -45,7 +45,6 @@ "dependencies": { "@cloudflare/pages-plugin-vercel-og": "^0.1.2", "@docsearch/react": "^4.6.2", - "@headlessui/react": "^2.2.9", "@mdx-js/mdx": "^3.1.1", "@node-cli/static-server": "^3.1.10", "@react-router/node": "^8.3.1", @@ -60,13 +59,11 @@ "isbot": "^5.1.37", "mdast-util-from-markdown": "^2.0.3", "mdast-util-to-string": "^4.0.0", - "mdast-util-toc": "^7.1.0", "react": "^19.3.0", "react-dom": "^19.3.0", "react-markdown": "^10.1.0", "react-router": "^8.3.1", "rehype-slug": "^6.0.0", - "rehype-stringify": "^10.0.1", "remark": "^15.0.1", "remark-comment": "^1.0.0", "remark-frontmatter": "^5.0.0", @@ -89,7 +86,6 @@ "@vitest/browser-playwright": "^5.0.0", "auto-image-converter": "^2.2.0", "babel-plugin-react-compiler": "^1.0.0", - "chokidar": "^4.0.3", "cypress": "^15.13.1", "cypress-real-events": "^1.15.1", "dotenv": "^16.6.1", diff --git a/apps/docs/scripts/extract-syntax.mjs b/apps/docs/scripts/extract-syntax.mjs deleted file mode 100644 index f83926a4a..000000000 --- a/apps/docs/scripts/extract-syntax.mjs +++ /dev/null @@ -1,60 +0,0 @@ -import { globSync } from "tinyglobby"; -import path from "path"; -import fs from "fs"; -import { URL } from "url"; - -import { defaultProcessor } from "./markdown.js"; - -const pathname = new URL(".", import.meta.url).pathname; -const __dirname = - process.platform !== "win32" ? pathname : pathname.substring(1); - -const processFile = (filepath) => { - const content = fs.readFileSync(filepath, "utf8"); - const { - data: { matter }, - } = defaultProcessor.processSync(content); - - const syntaxPath = path.resolve("./misc_docs/syntax"); - const relFilePath = path.relative(syntaxPath, filepath); - const parsedPath = path.parse(relFilePath); - - if ( - matter.id && - matter.keywords && - matter.name && - matter.summary && - matter.category - ) { - return { - file: parsedPath.name, - id: matter.id, - keywords: matter.keywords, - name: matter.name, - summary: matter.summary, - category: matter.category, - }; - } - - console.error("Metadata missing in " + parsedPath.name + ".mdx"); - return null; -}; - -const extractSyntax = async (version) => { - const SYNTAX_MD_DIR = path.join(__dirname, "../misc_docs/syntax"); - const SYNTAX_INDEX_FILE = path.join( - __dirname, - "../index_data/syntax_index.json", - ); - const syntaxFiles = globSync(["*.md", "*.mdx"], { - cwd: SYNTAX_MD_DIR, - absolute: true, - }); - const syntaxIndex = syntaxFiles - .map(processFile) - .filter(Boolean) - .sort((a, b) => a.name.localeCompare(b.name)); - fs.writeFileSync(SYNTAX_INDEX_FILE, JSON.stringify(syntaxIndex), "utf8"); -}; - -extractSyntax(); diff --git a/apps/docs/scripts/figma-fetch.js b/apps/docs/scripts/figma-fetch.js deleted file mode 100644 index 829354ef6..000000000 --- a/apps/docs/scripts/figma-fetch.js +++ /dev/null @@ -1,210 +0,0 @@ -/* - * This script fetches all color styles from a Figma team/document. - * - * Dependencies: - * - * - node-fetch - * - * Due to a limitation in the Figma /styles endpoint, we need to use a - * document for actually using the colors in a color grid. That's why - * we're both fetching from /styles and /files below. - * - * For now, you need to input the page and team IDs, as well as the file keys. - * The team ID is in the Figma URL of your team, and the file key is the long - * string in the full URL of a Figma file. The page ID is visible in the JSON - * payload when you call /files - */ -// Inspect the /files JSON response, or the URL of the Figma page: -// https://www.figma.com/file//Some-Name?node-id= -const PAGE_ID = "312:2"; -// Go to a team URL and get the ID: -// https://www.figma.com/files/team//Team-Name -const TEAM_ID = "977136127691894995"; -// Get this from the URL of a single file: -// https://www.figma.com/file//Some-Name?node-id=182%3A0 -const FILE_KEY = "FsQT67sVviqPXhYoTbpfIb"; - -const fetch = require("node-fetch"); -const fs = require("fs"); -const { promisify } = require("util"); -const path = require("path"); - -const writeFile = promisify(fs.writeFile); - -const personalToken = process.env.FIGMA_PERSONAL_TOKEN; - -if (!personalToken) { - console.error("Please pass FIGMA_PERSONAL_TOKEN to this script and re-run"); - process.exit(1); -} - -const figmaBase = "https://api.figma.com/"; - -const rgbToHex = (r, g, b) => { - const color = - "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); - - if (color.length > 7) { - return color.slice(0, 7); - } - return color; -}; - -const slugify = (str) => str.toLowerCase().replace(/\s+/, "-"); - -const doFetch = (url) => - fetch(`${figmaBase}v1${url}`, { - headers: { - "X-Figma-Token": personalToken, - }, - }) - .then((res) => { - if (!res.ok) { - throw new Error(`Status: ${res.status}`); - } - - return res.json(); - }) - .then((json) => { - if (json.error || (json.status && json.status !== 200)) { - throw new Error(json.error || `Status ${json.status}: ${json.err}`); - } - - return json; - }); - -const fetchStyles = async (teamId) => { - const json = await doFetch(`/teams/${teamId}/styles?page_size=99`); - - return json.meta.styles; -}; - -const fetchFile = async (key) => await doFetch(`/files/${key}`); - -// eslint-disable-next-line -const fetchStyle = async (key) => await doFetch(`/styles/${key}`); - -// Retrieves all elements from groups from a given root -const flattenTree = (root) => { - let ret = []; - - let { children = [] } = root; - - for (let i = 0; i < children.length; i++) { - const el = children[i]; - const type = el.type; - - if (type === "GROUP") { - let sub = flattenTree(el); - ret = ret.concat(sub); - } else { - ret.push(el); - } - } - - return ret; -}; - -/** - * Fetches all color styles from the Figma doc and returns an object - * array with this shape: - * - * ```json - * { - * "key": "206f4e4753e2e8f2e7ac24744bd1843ac206ead1", - * "file_key": "bMb57SxaX0ugGmWMmi7KVzIP", - * "node_id": "189:85", - * "style_type": "FILL", - * "thumbnail_url": "", - * "name": "Green 10", - * "description": "Desc", - * "created_at": "2019-02-16T16:00:39.126Z", - * "updated_at": "2019-02-16T16:00:39.126Z", - * "user": { - * "id": "575212366706412863", - * "handle": "Johan Brook", - * "img_url": "" - * }, - * "sort_position": "=O", - * "color": "#ebfff7" - * } - ``` - */ -const fetchAllColorStyles = async () => { - const styles = await fetchStyles(TEAM_ID); - const file = await fetchFile(FILE_KEY); - - const root = file.document.children[0]; - - if (!root) { - return []; - } - - const canvas = root.children.find((page) => { - return page.id === PAGE_ID; - }); - - const isRectOrVector = (c) => c.type === "RECTANGLE" || c.type === "VECTOR"; - - console.log(JSON.stringify(canvas, null, 2)); - - const allElements = flattenTree(canvas); - - return allElements - .filter( - (c) => isRectOrVector(c) && c.styles != null && c.styles.fill != null, - ) - .map((c) => { - const { r, g, b, a } = c.fills[0].color; - const { opacity = a } = c.fills[0]; - const nodeId = c.styles.fill; - - let color; - if (opacity < 1) { - color = `rgba(${(r * 255).toFixed(0)}, ${(g * 255).toFixed(0)}, ${(b * 255).toFixed(0)}, ${opacity.toFixed(2)})`; - } else { - color = rgbToHex(r * 256, g * 256, b * 256); - } - - return { - // Cross reference to the array of styles, since Figma doesn't - // give us the HEX color codes in their /styles endpoint .. :( - ...styles.find((s) => s.node_id === nodeId), - color, - }; - }) - .filter((c) => c.name != null); -}; - -/** - * Calls Figma's API and saves to a `colors.js` file in the project root. - */ -const writeColorsFromFigma = async () => { - const styles = await fetchAllColorStyles(); - - if (!styles) { - throw new Error("No styles found"); - } - - const colors = styles - .sort((a, b) => (a.sort_position < b.sort_position ? -1 : 1)) - .map((s) => { - return ( - (s.description ? ` /** ${s.description} */\n` : "") + - ` '${slugify(s.name)}': '${s.color}',` - ); - }) - .join("\n"); - - const fileContents = `/* eslint-disable */ -/* Updated at ${new Date().toUTCString()}*/ -module.exports = { -${colors} -}`; - - await writeFile(path.resolve(__dirname + "/../colors.js"), fileContents); - - console.log(`Wrote ${styles.length} colors to colors.js`); -}; - -writeColorsFromFigma().catch(console.error); diff --git a/apps/docs/scripts/gendocs.res b/apps/docs/scripts/gendocs.res deleted file mode 100644 index b2660f93e..000000000 --- a/apps/docs/scripts/gendocs.res +++ /dev/null @@ -1,303 +0,0 @@ -/*** -Generate docs from ReScript Compiler - -## Run - -```bash -node scripts/gendocs.mjs path/to/rescript-compiler path/to/rescript-core/src/RescriptCore.res version forceReWrite -``` - -## Examples - -```bash -node scripts/gendocs.mjs path/to/rescript-compiler latest true -``` -*/ -@val @scope(("import", "meta")) external url: string = "url" - -open Node -module Docgen = RescriptTools.Docgen - -let args = Process.argv->Array.slice(~start=2) -let dirname = - url - ->URL.fileURLToPath - ->Path.dirname - -let compilerLibPath = switch args->Array.get(0) { -| Some(path) => Path.join([path, "jscomp", "others"]) -| None => failwith("First argument should be path to rescript-compiler repo") -} - -let corePath = switch args->Array.get(1) { -| Some(path) => path -| _ => failwith("Second argument should be path to rescript-core/src/RescriptCore.res") -} - -let version = switch args->Array.get(2) { -| Some(version) => version -| None => failwith("Third argument should be a version, `latest`, `v10`") -} - -let forceReWrite = switch args->Array.get(3) { -| Some("true") => true -| _ => false -} - -let dirVersion = Path.join([dirname, "..", "data", "api", version]) - -if Fs.existsSync(dirVersion) { - Console.error(`Directory ${dirVersion} already exists`) - if !forceReWrite { - Process.exit(1) - } -} else { - Fs.mkdirSync(dirVersion) -} - -let entryPointFiles = ["js.ml", "belt.res", "dom.res"] - -let hiddenModules = ["Js.Internal", "Js.MapperRt"] - -type module_ = { - id: string, - docstrings: array, - name: string, - items: array, -} - -type section = { - name: string, - docstrings: array, - deprecated: option, - topLevelItems: array, - submodules: array, -} - -let env = Process.env - -let docsDecoded = entryPointFiles->Array.map(libFile => { - let entryPointFile = Path.join2(compilerLibPath, libFile) - - Dict.set(env, "FROM_COMPILER", "true") - - let output = - ChildProcess.execSync( - `./node_modules/.bin/rescript-tools doc ${entryPointFile}`, - )->Buffer.toString - - output - ->JSON.parseOrThrow - ->Docgen.decodeFromJson -}) - -let coreDocs = { - Dict.set(env, "FROM_COMPILER", "false") - - let output = - ChildProcess.execSync(`./node_modules/.bin/rescript-tools doc ${corePath}`)->Buffer.toString - - output - ->JSON.parseOrThrow - ->Docgen.decodeFromJson -} - -let docsDecoded = Array.concat(docsDecoded, [coreDocs]) - -let docs = docsDecoded->Array.map(doc => { - let topLevelItems = doc.items->Array.filterMap(item => - switch item { - | Value(_) as item | Type(_) as item => item->Some - | _ => None - } - ) - - let rec getModules = (lst: list, moduleNames: list) => - switch lst { - | list{ - Module({id, items, name, docstrings}) - | ModuleAlias({id, items, name, docstrings}) - | ModuleType({id, items, name, docstrings}), - ...rest, - } => - if Array.includes(hiddenModules, id) { - getModules(rest, moduleNames) - } else { - let id = String.startsWith(id, "RescriptCore") - ? String.replace(id, "RescriptCore", "Core") - : id - getModules( - list{...rest, ...List.fromArray(items)}, - list{{id, items, name, docstrings}, ...moduleNames}, - ) - } - | list{Type(_) | Value(_), ...rest} => getModules(rest, moduleNames) - | list{} => moduleNames - } - - let id = String.startsWith(doc.name, "RescriptCore") - ? String.replace(doc.name, "RescriptCore", "Core") - : doc.name - - let top = {id, name: id, docstrings: doc.docstrings, items: topLevelItems} - let submodules = getModules(doc.items->List.fromArray, list{})->List.toArray - let result = [top]->Array.concat(submodules) - - (id, result) -}) - -let allModules = { - open JSON - let encodeItem = (docItem: Docgen.item) => { - switch docItem { - | Value({id, name, docstrings, signature, ?deprecated}) => { - let id = String.startsWith(id, "RescriptCore") - ? String.replace(id, "RescriptCore", "Core") - : id - let dict = Dict.fromArray( - [ - ("id", id->String), - ("kind", "value"->String), - ("name", name->String), - ( - "docstrings", - docstrings - ->Array.map(s => s->String) - ->Array, - ), - ("signature", signature->String), - ]->Array.concat( - switch deprecated { - | Some(v) => [("deprecated", v->String)] - | None => [] - }, - ), - ) - dict->Object->Some - } - - | Type({id, name, docstrings, signature, ?deprecated}) => - let id = String.startsWith(id, "RescriptCore") - ? String.replace(id, "RescriptCore", "Core") - : id - let dict = Dict.fromArray( - [ - ("id", id->String), - ("kind", "type"->String), - ("name", name->String), - ("docstrings", docstrings->Array.map(s => s->String)->Array), - ("signature", signature->String), - ]->Array.concat( - switch deprecated { - | Some(v) => [("deprecated", v->String)] - | None => [] - }, - ), - ) - Object(dict)->Some - - | _ => None - } - } - - docs->Array.map(((topLevelName, modules)) => { - let submodules = - modules - ->Array.map(mod => { - let items = - mod.items - ->Array.filterMap(item => encodeItem(item)) - ->Array - - let id = String.startsWith(mod.id, "RescriptCore") ? "Core" : mod.id - - let rest = Dict.fromArray([ - ("id", id->String), - ("name", mod.name->String), - ("docstrings", mod.docstrings->Array.map(s => s->String)->Array), - ("items", items), - ]) - ( - mod.id - ->String.split(".") - ->Array.join("/") - ->String.toLowerCase, - rest->Object, - ) - }) - ->Dict.fromArray - - (topLevelName, submodules) - }) -} - -let () = { - allModules->Array.forEach(((topLevelName, mod)) => { - let json = JSON.Object(mod) - - let topLevelName = String.startsWith(topLevelName, "RescriptCore") ? "Core" : topLevelName - - Fs.writeFileSync( - Path.join([dirVersion, `${topLevelName->String.toLowerCase}.json`]), - json->JSON.stringify(~space=2), - ) - }) -} - -type rec node = { - name: string, - path: array, - children: array, -} - -// Generate TOC modules -let () = { - let joinPath = (~path: array, ~name: string) => { - Array.concat(path, [name])->Array.map(path => path->String.toLowerCase) - } - let rec getModules = (lst: list, moduleNames, path) => { - switch lst { - | list{ - Module({id, items, name}) | ModuleAlias({id, items, name}) | ModuleType({id, items, name}), - ...rest, - } => - if Array.includes(hiddenModules, id) { - getModules(rest, moduleNames, path) - } else { - let itemsList = items->List.fromArray - let children = getModules(itemsList, [], joinPath(~path, ~name)) - - getModules( - rest, - Array.concat([{name, path: joinPath(~path, ~name), children}], moduleNames), - path, - ) - } - | list{Type(_) | Value(_), ...rest} => getModules(rest, moduleNames, path) - | list{} => moduleNames - } - } - - let tocTree = docsDecoded->Array.map(({name, items}) => { - let name = String.startsWith(name, "RescriptCore") ? "Core" : name - let path = name->String.toLowerCase - ( - path, - { - name, - path: [path], - children: items - ->List.fromArray - ->getModules([], [path]), - }, - ) - }) - - Fs.writeFileSync( - Path.join([dirVersion, "toc_tree.json"]), - tocTree - ->Dict.fromArray - ->JSON.stringifyAny - ->Option.getOrThrow, - ) -} diff --git a/apps/docs/scripts/markdown.js b/apps/docs/scripts/markdown.js deleted file mode 100644 index 5e13cd52f..000000000 --- a/apps/docs/scripts/markdown.js +++ /dev/null @@ -1,72 +0,0 @@ -import { unified } from "unified"; -import remarkParse from "remark-parse"; -import remarkGfm from "remark-gfm"; -import remarkFrontmatter from "remark-frontmatter"; -import remarkRehype from "remark-rehype"; -import rehypeSlug from "rehype-slug"; -import rehypeStringify from "rehype-stringify"; -import { matter } from "vfile-matter"; - -const remarkVfileMatter = (options) => (tree, file) => { - matter(file); -}; - -const remarkCodeblocks = (options) => (tree, file) => { - const { children } = tree; - const codeblocks = {}; - - const formatter = (value) => { - // Strip newlines and weird spacing - return value - .replace(/\n/g, " ") - .replace(/\s+/g, " ") - .replace(/\(\s+/g, "(") - .replace(/\s+\)/g, ")"); - }; - - children.forEach((child) => { - if (child.type === "code" && child.value) { - const { meta, lang } = child; - if (meta === "sig" && lang === "re") { - if (codeblocks[lang] == null) { - codeblocks[lang] = []; - } - codeblocks[lang].push(formatter(child.value)); - } - } - }); - - Object.assign(file.data, { codeblocks }); -}; - -const rehypeHeaders = (options) => (tree, file) => { - const headers = []; - let mainHeader; - tree.children.forEach((child) => { - if (child.tagName === "h1") { - if (child.children.length > 0) { - mainHeader = child.children.map((element) => element.value).join(""); - } - } - if (child.tagName === "h2") { - if (child.children.length > 0) { - const id = child.properties.id || ""; - const name = child.children.map((element) => element.value).join(""); - headers.push({ name, href: id }); - } - } - }); - - Object.assign(file.data, { headers, mainHeader }); -}; - -export const defaultProcessor = unified() - .use(remarkParse) - .use(remarkGfm) - .use(remarkFrontmatter, [{ type: "yaml", marker: "-" }]) - .use(remarkVfileMatter) - .use(remarkCodeblocks) - .use(remarkRehype) - .use(rehypeSlug) - .use(rehypeHeaders) - .use(rehypeStringify); diff --git a/apps/docs/scripts/sync-redirects.mjs b/apps/docs/scripts/sync-redirects.mjs deleted file mode 100644 index 356897b1c..000000000 --- a/apps/docs/scripts/sync-redirects.mjs +++ /dev/null @@ -1,21 +0,0 @@ -import * as path from "node:path"; -import * as fs from "node:fs"; - -import nextConfig from "../next.config.mjs"; - -const redirectsConfig = await nextConfig.redirects(); - -/** - * @param {{ - * source: string, - * destination: string, - * permanent: boolean, - * }} config - * @return {string} - */ -function lineFormat({ source, destination, permanent }) { - return `${source} ${destination} ${permanent ? 308 : 307}`; -} - -const redirects = redirectsConfig.map(lineFormat).join("\n"); -const redirectsFile = path.join(import.meta.dirname, "../public/_redirects"); diff --git a/apps/docs/scripts/watch-tests.mjs b/apps/docs/scripts/watch-tests.mjs deleted file mode 100644 index 950970394..000000000 --- a/apps/docs/scripts/watch-tests.mjs +++ /dev/null @@ -1,49 +0,0 @@ -import chokidar from "chokidar"; -// We switch from 'exec' to 'spawn' and 'execSync' is not needed -import { spawn } from "child_process"; - -// 1. Define the folder to watch -const folderToWatch = "markdown-pages"; - -// 2. Define the command and arguments to run -// The command is 'node' and the arguments are the script and its options -const command = "node"; -const args = ["scripts/test-hrefs.mjs"]; - -console.log(`\n👀 Watching for changes in: ${folderToWatch}`); -console.log(`\n➡️ Command to run on change: ${command} ${args.join(" ")}\n`); - -// 3. Initialize the watcher -chokidar - .watch(folderToWatch, { - ignored: /(^|[\/\\])\../, - persistent: true, - }) - .on("change", (path) => { - console.log(`\n--- 📝 File changed: ${path} ---`); - - // Use spawn instead of exec to run the script - const child = spawn(command, args, { - // *** KEY CHANGE: 'inherit' is used to connect the child's stdout/stderr - // to the parent process's (your watcher script's) stdout/stderr. *** - stdio: "inherit", - shell: true, // Use 'shell: true' if the script is simple, or if it needs shell features - }); - - // The 'close' event fires when the process exits - child.on("close", (code) => { - if (code !== 0) { - // Log a custom error message if the script fails - console.log(`\n❌ Script finished with exit code ${code}`); - } else { - // Log a success message - console.log(`\n✅ Script execution complete.`); - } - console.log(`\nWatching again...\n`); - }); - - // Handle process errors (e.g., command not found) - child.on("error", (err) => { - console.error(`\n💣 Failed to start child process: ${err}`); - }); - }); diff --git a/apps/docs/src/bindings/Env.res b/apps/docs/src/bindings/Env.res index 29e21321d..b55591838 100644 --- a/apps/docs/src/bindings/Env.res +++ b/apps/docs/src/bindings/Env.res @@ -16,31 +16,8 @@ external algoliaAppIdRaw: option = "import.meta.env.VITE_ALGOLIA_APP_ID" external algoliaIndexNameRaw: option = "import.meta.env.VITE_ALGOLIA_INDEX_NAME" external algoliaSearchApiKeyRaw: option = "import.meta.env.VITE_ALGOLIA_SEARCH_API_KEY" -let algoliaMissingPublicVars = AlgoliaConfig.missingPublicVars( - ~appId=algoliaAppIdRaw, - ~indexName=algoliaIndexNameRaw, - ~searchApiKey=algoliaSearchApiKeyRaw, -) - let algoliaPublicConfig = AlgoliaConfig.publicConfigFrom( ~appId=algoliaAppIdRaw, ~indexName=algoliaIndexNameRaw, ~searchApiKey=algoliaSearchApiKeyRaw, ) - -let algolia_app_id = switch algoliaPublicConfig { -| Some(config) => config.appId -| None => "" -} - -let algolia_index_name = switch algoliaPublicConfig { -| Some(config) => config.indexName -| None => "" -} - -let algolia_search_api_key = switch algoliaPublicConfig { -| Some(config) => config.searchApiKey -| None => "" -} - -let algolia_read_api_key = algolia_search_api_key diff --git a/apps/docs/src/bindings/HeadlessUI.res b/apps/docs/src/bindings/HeadlessUI.res deleted file mode 100644 index e54bd7148..000000000 --- a/apps/docs/src/bindings/HeadlessUI.res +++ /dev/null @@ -1,77 +0,0 @@ -module Menu = { - type state = {@as("open") open_: bool} - - @module("@headlessui/react") @react.component - external make: ( - ~_as: [#div]=?, - ~className: string=?, - ~children: state => React.element, - ) => React.element = "Menu" - - module Button = { - @module("@headlessui/react") @scope("Menu") @react.component - external make: (~className: string=?, ~children: React.element) => React.element = "Button" - } - - module Items = { - @module("@headlessui/react") @scope("Menu") @react.component - external make: (~static: bool=?, ~children: React.element) => React.element = "Items" - } - - module Item = { - type state = {active: bool} - @module("@headlessui/react") @scope("Menu") @react.component - external make: (~children: state => React.element) => React.element = "Item" - } -} - -module Listbox = { - type state = {@as("open") open_: bool} - - @module("@headlessui/react") @react.component - external make: ( - ~value: 'state=?, - ~onChange: 'state => unit=?, - ~className: string=?, - ~children: state => React.element, - ) => React.element = "Listbox" - - module Button = { - @module("@headlessui/react") @scope("Listbox") @react.component - external make: (~className: string=?, ~children: React.element=?) => React.element = "Button" - } - - module Options = { - @module("@headlessui/react") @scope("Listbox") @react.component - external make: ( - ~static: bool=?, - ~className: string=?, - ~children: React.element, - ) => React.element = "Options" - } - - module Option = { - type state = {selected: bool, active: bool} - @module("@headlessui/react") @scope("Listbox") @react.component - external make: ( - ~children: state => React.element, - ~value: 'item, - ~disabled: bool=?, - ) => React.element = "Option" - } -} - -module Transition = { - @module("@headlessui/react") @react.component - external make: ( - ~appear: bool=?, - ~show: bool, - ~enter: string=?, - ~enterFrom: string=?, - ~enterTo: string=?, - ~leave: string=?, - ~leaveFrom: string=?, - ~leaveTo: string=?, - ~children: React.element, - ) => React.element = "Transition" -} diff --git a/apps/docs/src/bindings/Jsdom.res b/apps/docs/src/bindings/Jsdom.res deleted file mode 100644 index 43f12c740..000000000 --- a/apps/docs/src/bindings/Jsdom.res +++ /dev/null @@ -1,5 +0,0 @@ -type window = {document: WebAPI.DOMAPI.document} -type t = {window: WebAPI.DOMAPI.window} - -@module("jsdom") @new -external make: string => t = "JSDOM" diff --git a/apps/docs/src/bindings/Mdast.res b/apps/docs/src/bindings/Mdast.res index c9fd2c016..de3d531a6 100644 --- a/apps/docs/src/bindings/Mdast.res +++ b/apps/docs/src/bindings/Mdast.res @@ -8,44 +8,3 @@ external toString: {..} => string = "toString" @module("unist-util-visit") external visit: (tree, string, {..} => unit) => unit = "visit" - -@unboxed -type listType = - | @as("list") List - | @as("listItem") ListItem - | @as("paragraph") Paragraph - | @as("link") Link - -type rec listContent = { - @as("type") _type: listType, - url?: Path.t, - title?: string, - value: string, - children?: array, -} - -type result = { - index?: int, - endIndex?: int, - map: listContent, -} - -type tocOptions = {maxDepth?: int} - -@module("mdast-util-toc") -external toc: (tree, tocOptions) => result = "toc" - -let rec reduceHeaders = (list: listContent, links: dict) => { - if list._type == Link { - let child = list.children->Option.flatMap(children => children[0]) - switch (list.url, child) { - | (Some(url), Some(child)) => links->Dict.set(child.value, url) - | (_, _) => () - } - } else { - switch list.children { - | Some(children) => children->Array.forEach(child => reduceHeaders(child, links)) - | None => () - } - } -} diff --git a/apps/docs/src/bindings/Remark.res b/apps/docs/src/bindings/Remark.res deleted file mode 100644 index 44314c8e4..000000000 --- a/apps/docs/src/bindings/Remark.res +++ /dev/null @@ -1,15 +0,0 @@ -type io -type processor -type plugin = processor => io - -@unboxed -type pluginOrOption = Plugin(plugin) - -@unboxed -type rec remarkPlugin = - | Plugin(processor => io) - | WithOptions(array) - -@module("remark-comment") external comment: plugin = "default" -@module("remark-gfm") external gfm: plugin = "default" -@module("remark-frontmatter") external frontmatter: plugin = "default" diff --git a/apps/docs/src/common/Constants.res b/apps/docs/src/common/Constants.res index 9183573f6..79d3a924c 100644 --- a/apps/docs/src/common/Constants.res +++ b/apps/docs/src/common/Constants.res @@ -1,56 +1,8 @@ -type env = { - @as("VITE_VERSION_LATEST") - latestVersion: string, - @as("VITE_VERSION_NEXT") - nextVersion: string, -} +external latestVersion: string = "import.meta.env.VITE_VERSION_LATEST" -@scope("import.meta") external env: env = "env" +let docSearchVersionTokens = [latestVersion->Semver.tryGetMajorString, "latest"] -type versionMapping = { - latest: string, - next: string, -} - -let versions = { - latest: env.latestVersion, - next: env.nextVersion, -} - -let latestVersion = (versions.latest, versions.latest->Semver.tryGetMajorString) - -let docSearchVersionTokens = { - let (_, majorVersion) = latestVersion - [majorVersion, "latest"] -} - -// This is used for the version dropdown in the manual layouts -let allManualVersions = [ - latestVersion, - ("v10.0.0", "v9.1 - v10.1"), - ("v9.0.0", "v8.2 - v9.0"), - ("v8.0.0", "v6.0 - v8.2"), -] - -let nextVersion = - versions.latest === versions.next - ? None - : Some(versions.next, versions.next->Semver.tryGetMajorString) - -let stdlibVersions = - versions.latest === "v11.0.0" ? [latestVersion] : [("v11.0.0", "v11"), latestVersion] - -let latestReactVersion = "latest" -let allReactVersions = [ - ("latest", latestReactVersion), - ("v0.11.0", "v0.11.0"), - ("v0.10.0", "v0.10.0"), -] - -let dropdownLabelNext = "--- Next ---" -let dropdownLabelReleased = "--- Released ---" - -// Used for the DocsOverview and collapsible navigation +// Used for the DocsOverview let languageManual = [ ("Overview", "/docs/manual/introduction"), ("Language Features", "/docs/manual/overview"), diff --git a/apps/docs/src/common/DateStr.res b/apps/docs/src/common/DateStr.res index a4d50d0fd..5928fefbc 100644 --- a/apps/docs/src/common/DateStr.res +++ b/apps/docs/src/common/DateStr.res @@ -3,7 +3,6 @@ type t = string // Used to prevent issues with webkit based date representations let parse = (dateStr: string): Date.t => dateStr->String.replaceRegExp(/-/g, "/")->Date.fromString -let fromDate = date => Date.toString(date) let toDate = dateStr => parse(dateStr) external fromString: string => t = "%identity" diff --git a/apps/docs/src/common/DateStr.resi b/apps/docs/src/common/DateStr.resi index 80dfc9407..e426fa12a 100644 --- a/apps/docs/src/common/DateStr.resi +++ b/apps/docs/src/common/DateStr.resi @@ -3,5 +3,4 @@ type t external fromString: string => t = "%identity" -let fromDate: Date.t => t let toDate: t => Date.t diff --git a/apps/docs/src/common/EnableCollapsibleNavbar.res b/apps/docs/src/common/EnableCollapsibleNavbar.res deleted file mode 100644 index 2a841e8cf..000000000 --- a/apps/docs/src/common/EnableCollapsibleNavbar.res +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Hides the navbar when scrolling down on mobile, and causes it to reappear when scrolling back up. - */ -@jsx.component -let make = (~children, ~isEnabled) => { - let scrollDir = Hooks.useScrollDirection() - if isEnabled { -
"group nav-appear" - - | Down(_) => "group nav-disappear" - }} - > - children -
- } else { - children - } -} diff --git a/apps/docs/src/common/Hooks.res b/apps/docs/src/common/Hooks.res index 682355c21..1e8ae16ee 100644 --- a/apps/docs/src/common/Hooks.res +++ b/apps/docs/src/common/Hooks.res @@ -1,19 +1,3 @@ -/* Contains some generic hooks */ -let useOutsideClick: (ReactDOM.Ref.t, unit => unit) => unit = %raw(`(outerRef, trigger) => { - function handleClickOutside(event) { - if (outerRef.current && !outerRef.current.contains(event.target)) { - trigger(); - } - } - - React.useEffect(() => { - document.addEventListener("mousedown", handleClickOutside); - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }); -}`) - /** scrollDir is not memo-friendly. It must be used with pattern matching. Do not pass it directly to child components. */ @@ -61,31 +45,9 @@ let useScrollDirection = (~topMargin=80, ~threshold=20) => { }) } - // let onScroll = Util.debounce(onScroll, 50) - WebAPI.Window.addEventListener(window, Scroll, onScroll) Some(() => WebAPI.Window.removeEventListener(window, Scroll, onScroll)) }, [topMargin, threshold]) scrollDir } - -type mediaQueryListEvent = {matches: bool} - -let useMediaQuery = (query: string) => { - let (matches, setMatches) = React.useState(() => { - false - }) - - React.useEffect(() => { - let mediaQueryList = WebAPI.Window.matchMedia(window, query) - setMatches(_ => mediaQueryList.matches) - - let listener = (e: mediaQueryListEvent) => setMatches(_ => e.matches) - - WebAPI.MediaQueryList.addEventListener(mediaQueryList, Change, listener) - Some(() => WebAPI.MediaQueryList.removeEventListener(mediaQueryList, Change, listener)) - }, [query]) - - matches -} diff --git a/apps/docs/src/common/Url.res b/apps/docs/src/common/Url.res index 7f56de3fc..c799fea51 100644 --- a/apps/docs/src/common/Url.res +++ b/apps/docs/src/common/Url.res @@ -1,9 +1,3 @@ -type t = { - fullpath: array, - base: array, - pagepath: array, -} - type breadcrumb = { name: string, href: string, @@ -15,30 +9,6 @@ let prettyString = (str: string) => { str->camelCase->capitalize } -let parse = (route: string): t => { - let routePath = route->String.split("/")->Array.filter(s => s !== "") - - { - fullpath: routePath, - base: routePath, - pagepath: [], - } -} - -@unboxed -type storageKey = - | @as("manual_version") Manual - | @as("react_version") React - | @as("playground_version") Playground - -let getVersionFromStorage = (key: storageKey) => { - try { - WebAPI.Storage.getItem(window.localStorage, (key :> string))->Null.toOption - } catch { - | JsExn(_) => None - } -} - let normalizePath = string => { string->String.replaceRegExp(/\/$/, "")->String.toLocaleLowerCase } diff --git a/apps/docs/src/common/Url.resi b/apps/docs/src/common/Url.resi index e0ce5d1b3..708c5a322 100644 --- a/apps/docs/src/common/Url.resi +++ b/apps/docs/src/common/Url.resi @@ -1,27 +1,10 @@ -type t = { - fullpath: array, - base: array, - pagepath: array, -} - -// Moved here from the original sidebar URL helper. type breadcrumb = { name: string, href: string, } -let parse: string => t - let prettyString: string => string -@unboxed -type storageKey = - | @as("manual_version") Manual - | @as("react_version") React - | @as("playground_version") Playground - -let getVersionFromStorage: storageKey => option - let normalizePath: string => string let normalizeAnchor: string => string diff --git a/apps/docs/src/common/Util.res b/apps/docs/src/common/Util.res index 27e8d354e..db2ce0fa7 100644 --- a/apps/docs/src/common/Util.res +++ b/apps/docs/src/common/Util.res @@ -7,12 +7,6 @@ module String = { return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); }") - let kebabCase = string => - string - ->String.replaceAllRegExp(/([a-z])([A-Z])/g, "$1-$2") - ->String.toLowerCase - ->String.replaceAll(" ", "-") - let capitalize: string => string = %raw("str => { return str && str.charAt(0).toUpperCase() + str.substring(1); }") @@ -22,8 +16,6 @@ module String = { ->String.split(" ") ->Array.map(str => str->String.length > 2 ? str->String.capitalize : str) ->Array.join(" ") - - let leadingSlash = str => str->String.startsWith("/") ? str : "/" ++ str } module Url = { @@ -49,34 +41,6 @@ module Url = { } } - let getRootPath = path => { - if path->Stdlib.String.includes("docs/manual") { - "/docs/manual" - } else if path->Stdlib.String.includes("docs/react") { - "/docs/react" - } else { - "" - } - } - - let createRelativePath = (currentPath, href) => { - if ( - href->Stdlib.String.includes("docs/manual") || - href->Stdlib.String.includes("docs/react") || - href->Stdlib.String.includes("community") || - href->Stdlib.String.includes("blog") || - href->Stdlib.String.includes("try") || - href->Stdlib.String.includes("/llms/") || - href->Stdlib.String.startsWith("#") - ) { - href - } else { - let rootPath = getRootPath(currentPath) - let href = href->Stdlib.String.replace("docs/manual", "") - (rootPath ++ href->String.leadingSlash)->Stdlib.String.replaceAll("//", "/") - } - } - let makeOpenGraphImageUrl = url => { `${baseUrl()}ogimage/index.png?url=${encodeURIComponent(url)}` } @@ -95,17 +59,3 @@ module Date = { dateTimeFormat("en-US", {"month": "short", "day": "numeric", "year": "numeric"})->format(date) } } - -/** - * 防抖 - * @param fn the func to debounce - * @param delay milliseconds - * @returns new debounced function - */ -let debounce = (fn: unit => unit, delay: int) => { - let timer = ref(None) - () => { - timer.contents->Option.forEach(clearTimeout) - timer := Some(setTimeout(~handler=fn, ~timeout=delay)) - } -} diff --git a/apps/docs/src/common/Util.resi b/apps/docs/src/common/Util.resi index fb0da2991..c807a7f84 100644 --- a/apps/docs/src/common/Util.resi +++ b/apps/docs/src/common/Util.resi @@ -1,8 +1,6 @@ module Url: { let isAbsolute: string => bool let makeAbsoluteUrl: string => string - let getRootPath: string => string - let createRelativePath: (string, string) => string let makeOpenGraphImageUrl: string => string } @@ -12,14 +10,10 @@ module Unsafe: { module String: { let camelCase: string => string - let kebabCase: string => string let capitalize: string => string let capitalizeSentence: string => string - let leadingSlash: string => string } module Date: { let toDayMonthYear: Date.t => string } - -let debounce: (unit => unit, int) => unit => unit diff --git a/apps/docs/src/components/ApiIntro.res b/apps/docs/src/components/ApiIntro.res deleted file mode 100644 index f74f3fc88..000000000 --- a/apps/docs/src/components/ApiIntro.res +++ /dev/null @@ -1,2 +0,0 @@ -@react.component -let make = (~children) =>
children
diff --git a/apps/docs/src/components/ApiIntro.resi b/apps/docs/src/components/ApiIntro.resi deleted file mode 100644 index 940ed3d87..000000000 --- a/apps/docs/src/components/ApiIntro.resi +++ /dev/null @@ -1,2 +0,0 @@ -@react.component -let make: (~children: React.element) => React.element diff --git a/apps/docs/src/components/ApiMarkdown.res b/apps/docs/src/components/ApiMarkdown.res deleted file mode 100644 index e517f4fa9..000000000 --- a/apps/docs/src/components/ApiMarkdown.res +++ /dev/null @@ -1,39 +0,0 @@ -open Markdown -/* -module H1 = { - @react.component - let make = (~children) => -

children

-}*/ - -module H2 = { - // We will currently hide the headline, to keep the structure, - // but having an Elm like documentation - @react.component - let make = (~id, ~children, ~title) => <> -
- children - -} - -type aliasH2 = Markdown.H2.props => React.element -external asMarkdownH2: 'a => aliasH2 = "%identity" - -let default = { - MarkdownComponents.intro: Intro.make, - p: P.make, - li: Li.make, - h1: H1.make, - h2: H2.make->asMarkdownH2, - h3: H3.make, - h4: H4.make, - h5: H5.make, - ul: Ul.make, - ol: Ol.make, - a: A.make, - thead: Thead.make, - th: Th.make, - td: Td.make, - pre: Pre.make, - code: Code.make, -} diff --git a/apps/docs/src/components/ApiMarkdown.resi b/apps/docs/src/components/ApiMarkdown.resi deleted file mode 100644 index 1129e877b..000000000 --- a/apps/docs/src/components/ApiMarkdown.resi +++ /dev/null @@ -1 +0,0 @@ -let default: MarkdownComponents.t diff --git a/apps/docs/src/components/CodeMirrorSetup.js b/apps/docs/src/components/CodeMirrorSetup.js deleted file mode 100644 index 6e7ef254e..000000000 --- a/apps/docs/src/components/CodeMirrorSetup.js +++ /dev/null @@ -1,19 +0,0 @@ -// CodeMirror setup and mode imports -import CodeMirror from "codemirror"; - -import "codemirror/lib/codemirror.css"; - -// Import required modes and addons -import "codemirror/mode/javascript/javascript"; -import "../../plugins/cm-rescript-mode.js"; -import "../../plugins/cm-reason-mode.js"; - -// Import vim keymap if needed -import "codemirror/keymap/vim"; - -// Make sure CodeMirror is available globally -if (typeof window !== "undefined") { - window.CodeMirror = CodeMirror; -} - -export default CodeMirror; diff --git a/apps/docs/src/components/CommunityContent.res b/apps/docs/src/components/CommunityContent.res index cc79fe3d7..9e2f601c5 100644 --- a/apps/docs/src/components/CommunityContent.res +++ b/apps/docs/src/components/CommunityContent.res @@ -59,5 +59,3 @@ let make = () => {
} - -let default = make diff --git a/apps/docs/src/components/Image.res b/apps/docs/src/components/Image.res deleted file mode 100644 index e99f9818d..000000000 --- a/apps/docs/src/components/Image.res +++ /dev/null @@ -1,10 +0,0 @@ -@react.component -let default = ( - ~src: string, - ~size=#large, - ~withShadow=false, - ~caption: option=?, - ~className: option=?, -) => { - -} diff --git a/apps/docs/src/components/Intro.res b/apps/docs/src/components/Intro.res deleted file mode 100644 index 04c24c397..000000000 --- a/apps/docs/src/components/Intro.res +++ /dev/null @@ -1,2 +0,0 @@ -@react.component -let default = (~children) => children diff --git a/apps/docs/src/components/Markdown.resi b/apps/docs/src/components/Markdown.resi index 4c42ad8ff..6fa6541ce 100644 --- a/apps/docs/src/components/Markdown.resi +++ b/apps/docs/src/components/Markdown.resi @@ -30,14 +30,6 @@ module UrlBox: { let make: (~text: string, ~href: string, ~children: MdxLegacy.MdxChildren.t) => React.element } -// Used for creating invisible, hoverable anchors for url linking -module Anchor: { - /* let idFormat = (id: string): string => id */ - - @react.component - let make: (~id: string, ~title: string=?) => React.element -} - module H1: { @react.component let make: (~id: string=?, ~title: string=?, ~children: React.element) => React.element diff --git a/apps/docs/src/components/Navigation.res b/apps/docs/src/components/Navigation.res deleted file mode 100644 index 4502e767d..000000000 --- a/apps/docs/src/components/Navigation.res +++ /dev/null @@ -1,283 +0,0 @@ -module Link = ReactRouter.Link - -let link = "no-underline block hover:cursor-pointer hover:text-fire-30 mb-px" -let activeLink = "font-medium text-fire-30 border-b border-fire" - -let linkOrActiveLink = (~target: Path.t, ~route: Path.t) => target === route ? activeLink : link - -let linkOrActiveLinkSubroute = (~target: Path.t, ~route: Path.t) => - String.startsWith((route :> string), (target :> string)) ? activeLink : link - -let isActiveLink = (~includes: string, ~excludes: option=?, ~route: Path.t) => { - let route = (route :> string) - // includes means we want the lnk to be active if it contains the expected text - let includes = route->String.includes(includes) - // excludes allows us to not have links be active even if they do have the includes text - let excludes = switch excludes { - | Some(excludes) => route->String.includes(excludes) - | None => false - } - includes && !excludes ? activeLink : link -} - -let isDocRoute = (~route: Path.t) => { - let route = (route :> string) - route->String.includes("/docs/") || route->String.includes("/syntax-lookup") || route == "/docs" -} - -let isDocRouteActive = (~route: Path.t) => isDocRoute(~route) ? activeLink : link - -module MobileNav = { - @react.component - let make = (~route: Path.t) => { - let base = "font-normal mx-4 py-5 text-gray-40 border-b border-gray-80" - let extLink = "block hover:cursor-pointer hover:text-white text-gray-60" - - } -} - -/* isOverlayOpen: if the mobile sidebar is toggled open */ -@react.component -let make = (~fixed=true, ~isOverlayOpen: bool, ~setOverlayOpen: (bool => bool) => unit) => { - let location = ReactRouter.useLocation() - let route = location.pathname - - // TODO: post RR7 - I am not sure we need this, but it was fidgety to get this working right so I am leaving it for now - let (_isLocked, toggleScrollLock) = ScrollLockContext.useScrollLock() - - let toggleOverlay = () => { - setOverlayOpen(prev => !prev) - toggleScrollLock(prev => !prev) - } - - let fixedNavClassName = fixed ? "fixed top-0" : "relative" - - <> - - -} - -let make = React.memo(make) diff --git a/apps/docs/src/components/Navigation.resi b/apps/docs/src/components/Navigation.resi deleted file mode 100644 index 4c1f8c476..000000000 --- a/apps/docs/src/components/Navigation.resi +++ /dev/null @@ -1,6 +0,0 @@ -@react.component -let make: ( - ~fixed: bool=?, - ~isOverlayOpen: bool, - ~setOverlayOpen: (bool => bool) => unit, -) => React.element diff --git a/apps/docs/src/components/SidebarNav.resi b/apps/docs/src/components/SidebarNav.resi index 0ec003f44..a4fd95954 100644 --- a/apps/docs/src/components/SidebarNav.resi +++ b/apps/docs/src/components/SidebarNav.resi @@ -3,14 +3,6 @@ module NavItem: { name: string, href: string, } - @react.component - let make: ( - ~getActiveToc: t => option=?, - ~isHidden: bool=?, - ~isItemActive: t => bool=?, - ~items: array, - ~onClick: unit => unit, - ) => React.element } module Category: { diff --git a/apps/docs/src/components/VersionSelect.res b/apps/docs/src/components/VersionSelect.res index cf58b7d89..e79ae0903 100644 --- a/apps/docs/src/components/VersionSelect.res +++ b/apps/docs/src/components/VersionSelect.res @@ -12,12 +12,6 @@ let olderVersions = [ {label: "v6.0 - v8.1", link: "https://v11.rescript-lang.org/docs/manual/v8.0.0/introduction"}, ] -module SectionHeader = { - @react.component - let make = (~value) => - -} - // This is the current version let version = "v12" diff --git a/apps/docs/src/components/VersionSelect.resi b/apps/docs/src/components/VersionSelect.resi index 2b3c727a8..3d9d5083c 100644 --- a/apps/docs/src/components/VersionSelect.resi +++ b/apps/docs/src/components/VersionSelect.resi @@ -1,6 +1,2 @@ -module SectionHeader: { - @react.component - let make: (~value: string) => Jsx.element -} @react.component let make: unit => Jsx.element diff --git a/apps/docs/src/components/Video.res b/apps/docs/src/components/Video.res deleted file mode 100644 index 45583c6bb..000000000 --- a/apps/docs/src/components/Video.res +++ /dev/null @@ -1,4 +0,0 @@ -@react.component -let default = (~src: string, ~caption: option=?) => { - -} diff --git a/apps/docs/src/data/BlogApi.res b/apps/docs/src/data/BlogApi.res index 4afd795c3..e73bd5023 100644 --- a/apps/docs/src/data/BlogApi.res +++ b/apps/docs/src/data/BlogApi.res @@ -72,49 +72,6 @@ let getAllPosts = () => { ) } -let getLivePosts = () => { - let postsDirectory = Node.Path.join2(Node.Process.cwd(), "markdown-pages/blog") - - let livePosts = mdxFiles(postsDirectory)->Array.map(path => { - let {frontmatter} = - Node.Path.join2(postsDirectory, path)->Node.Fs.readFileSync->MarkdownParser.parseSync - switch BlogFrontmatter.decode(frontmatter) { - | Error(msg) => JsError.throwWithMessage(msg) - | Ok(d) => { - path, - frontmatter: d, - archived: false, - } - } - }) - - livePosts->Array.toSorted((a, b) => - String.compare(Node.Path.basename(b.path), Node.Path.basename(a.path)) - ) -} - -let getArchivedPosts = () => { - let postsDirectory = Node.Path.join2(Node.Process.cwd(), "markdown-pages/blog") - let archivedPostsDirectory = Node.Path.join2(postsDirectory, "archived") - - let archivedPosts = mdxFiles(archivedPostsDirectory)->Array.map(path => { - let {frontmatter} = - Node.Path.join2(archivedPostsDirectory, path)->Node.Fs.readFileSync->MarkdownParser.parseSync - switch BlogFrontmatter.decode(frontmatter) { - | Error(msg) => JsError.throwWithMessage(msg) - | Ok(d) => { - path: Node.Path.join2("archived", path), - frontmatter: d, - archived: true, - } - } - }) - - archivedPosts->Array.toSorted((a, b) => - String.compare(Node.Path.basename(b.path), Node.Path.basename(a.path)) - ) -} - module RssFeed = { // Module inspired by // https://gist.github.com/fredrikbergqvist/36704828353ebf5379a5c08c7583fe2d diff --git a/apps/docs/src/data/BlogApi.resi b/apps/docs/src/data/BlogApi.resi index 3c47faadc..bbada4f90 100644 --- a/apps/docs/src/data/BlogApi.resi +++ b/apps/docs/src/data/BlogApi.resi @@ -4,9 +4,6 @@ type post = { frontmatter: BlogFrontmatter.t, } -let getAllPosts: unit => array -let getLivePosts: unit => array -let getArchivedPosts: unit => array let blogPathToSlug: string => string module RssFeed: { diff --git a/apps/docs/src/data/MetaTagsApi.res b/apps/docs/src/data/MetaTagsApi.res deleted file mode 100644 index 7f3655d02..000000000 --- a/apps/docs/src/data/MetaTagsApi.res +++ /dev/null @@ -1,98 +0,0 @@ -type t = { - title: option, - description: option, - image: option, -} - -/** - This function uses JSDOM to fetch a webpage and extract the meta tags from it. - JSDOM is required since this runs on Node. - */ -let extractMetaTags = async (url: string) => { - try { - let response = await fetch(url) - - let html = await response->WebAPI.Response.text - let dom = Jsdom.make(html) - let document = dom.window.document - - let nodeList = document->WebAPI.Document.querySelectorAll("meta") - - let elements = [] - - for i in 0 to nodeList.length { - let node = WebAPI.NodeList.item(nodeList, i) - // cast Node elements to Element - elements->Array.push((Obj.magic(node): WebAPI.DOMAPI.element)) - } - - let metaTags = elements->Array.reduce(Dict.fromArray([]), (tags, meta) => { - let name = meta->WebAPI.Element.getAttribute("name") - let property = meta->WebAPI.Element.getAttribute("property") - let itemprop = meta->WebAPI.Element.getAttribute("itemprop") - - let name = switch (name, property, itemprop) { - | (Value(name), _, _) => Some(name) - | (_, Value(property), _) => Some(property) - | (_, _, Value(itemprop)) => Some(itemprop) - | _ => None - } - - let content = meta->WebAPI.Element.getAttribute("content") - - switch (name, content) { - | (Some(name), Value(content)) => tags->Dict.set(name, content) - | _ => () - } - - tags - }) - - let title = metaTags->Dict.get("og:title") - let description = metaTags->Dict.get("og:description") - let image = metaTags->Dict.get("og:image") - - Some({ - title, - description, - image, - }) - } catch { - | _ => { - Console.error(`Error fetching Open Graph details for ${url}`) - None - } - } -} - -type tags = { - ...t, - url: string, -} - -/* - Pass an array of URLs and get back an array of meta tags for each URL. - */ -let getMetaTags = async (urls: array) => { - let metaTags: array = [] - for i in 0 to Array.length(urls) - 1 { - let url = urls[i] - switch url { - | Some(url) => { - let tags = await extractMetaTags(url) - switch tags { - | Some(tags) => - metaTags->Array.push({ - title: tags.title, - description: tags.description, - image: tags.image, - url, - }) - | None => () - } - } - | None => () - } - } - metaTags -} diff --git a/apps/docs/src/ffi/react-codemirror-hooks.js b/apps/docs/src/ffi/react-codemirror-hooks.js deleted file mode 100644 index 9b5f14093..000000000 --- a/apps/docs/src/ffi/react-codemirror-hooks.js +++ /dev/null @@ -1,162 +0,0 @@ -// -// OBSOLETE! -// -// This file is reimplemented in common/CodeMirrorBase.re -// We keep this around for reference in case we find some -// bugs in the new implementation -// -import CodeMirror from "codemirror"; -import "codemirror/lib/codemirror.css"; -import { useEffect, useRef } from "react"; - -if (typeof window !== "undefined" && typeof window.navigator !== "undefined") { - require("codemirror/mode/javascript/javascript"); - require("../plugins/cm-reason-mode"); -} - -function makeErrorMarker(cm) { - const wrapper = cm.getWrapperElement(); - - const msg = document.createElement("div"); - msg.className = "z-10 absolute px-2 rounded bg-fire-15"; - msg.innerHTML = "Some Error Message"; - msg.style.display = "none"; - - const showMsg = () => { - msg.style.display = "block"; - }; - - const hideMsg = () => { - msg.style.display = "none"; - }; - - const marker = document.createElement("div"); - marker.className = - "text-center text-fire font-bold bg-fire-15 rounded w-4 ml-2 hover:cursor-pointer"; - marker.innerHTML = "!"; - marker.onmouseover = function markerMouseOver() { - const wrapperPos = wrapper.getBoundingClientRect(); - const pos = marker.getBoundingClientRect(); - - msg.style.left = pos.x - wrapperPos.x + pos.width + "px"; - msg.style.top = pos.y - wrapperPos.y + "px"; - - console.log("wrapperPos", wrapperPos); - console.log("markerPos", pos); - console.log(msg.style.left); - - showMsg(); - }; - - marker.onmouseleave = function markerMouseLeave() { - hideMsg(); - }; - - wrapper.appendChild(msg); - - return marker; -} - -const ERROR_GUTTER_ID = "errors"; - -function clearMarks(cm) { - var state = cm.state.errors; - - for (var i = 0; i < state.marked.length; ++i) { - state.marked[i].clear(); - } - - state.marked.length = 0; -} - -function updateErrors(cm, errors) { - // Clear out all previous errors first - clearMarks(cm); - cm.clearGutter(ERROR_GUTTER_ID); - - const state = cm.state.errors; - - errors.forEach((e) => { - const marker = makeErrorMarker(cm); - cm.setGutterMarker(e.row, ERROR_GUTTER_ID, marker); - - const from = { line: e.row, ch: e.column }; - const to = { line: e.endRow, ch: e.endColumn }; - - state.marked.push(cm.markText(from, to, { className: "bg-fire-15" })); - console.log("marked", state.marked); - }); - - //cm.setGutterMarker(0, ERROR_GUTTER_ID, marker); -} - -export default function CodeMirrorReact(props) { - const options = props.options || {}; - - const errors = props.errors || []; - const value = props.value || ""; - - const inputElement = useRef(); - const cmRef = useRef(); - - useEffect(() => { - const cm = CodeMirror.fromTextArea(inputElement.current, { - theme: "material", - gutters: [ERROR_GUTTER_ID, "CodeMirror-linenumbers"], - ...options, - }); - - cm.getScrollerElement().style.minHeight = props.minHeight; - cm.refresh(); - - // For some reason, injecting value with the options doesn't work - // so we need to set the initial value imperatively - cm.setValue(value); - - // Set up event handlers - const onChange = props.onChange; - if (onChange != null) { - cm.on("change", (instance, obj) => { - onChange(instance.getValue()); - }); - } - - cmRef.current = cm; - - // Set state - cm.state.errors = { marked: [] }; - - return () => { - console.log("cleanup", options.mode); - // This will destroy the CM instance - cm.toTextArea(); - }; - }, []); - - useEffect(() => { - const cm = cmRef.current; - - if (cm != null) { - cm.setValue(value); - } - }, [value]); - - useEffect(() => { - const cm = cmRef.current; - - if (cm != null) { - //console.log("setting errors"); - //console.log(errors); - - cm.operation(() => { - updateErrors(cm, errors); - }); - } - }, [errors]); - - return ( -
-