-
Notifications
You must be signed in to change notification settings - Fork 471
automatically sync API docs on rescript-lang.org #7555
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 8 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
7307d4c
automatically sync API docs on rescript-lang.org
tsnobip ad934a4
use GITHUB_TOKEN
tsnobip d4e4b81
rename package to get rid of constraints
tsnobip 5245657
build website on PR, push on release
tsnobip 5a98ec9
format
tsnobip a83041e
try to sync API docs on PR
tsnobip 7b209f3
fix workspace name
tsnobip bffeab1
add apiDocs folder
tsnobip 28212a7
use deploy key
tsnobip 41704e5
remove unused command
tsnobip 51f2a98
try pushing to website master
tsnobip 223217d
push only on release
tsnobip 53264c2
add changelog
tsnobip 931c6f8
rename api docs job
tsnobip File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,8 @@ env: | |
|
|
||
| jobs: | ||
| build-compiler: | ||
| outputs: | ||
| api-docs-artifact-id: ${{ steps.upload-api-docs.outputs.artifact-id }} | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
|
|
@@ -36,6 +38,7 @@ jobs: | |
| upload_binaries: true | ||
| # Build the playground compiler and run the benchmarks on the fastest runner | ||
| build_playground: true | ||
| generate_api_docs: true | ||
| benchmarks: true | ||
| node-target: linux-arm64 | ||
| rust-target: aarch64-unknown-linux-musl | ||
|
|
@@ -438,6 +441,18 @@ jobs: | |
| name: lib-ocaml | ||
| path: lib/ocaml | ||
|
|
||
| - name: Generate API Docs | ||
| if: ${{ matrix.generate_api_docs }} | ||
| run: yarn apidocs:generate | ||
|
|
||
| - name: "Upload artifacts: scripts/res/apiDocs" | ||
| id: upload-api-docs | ||
| if: ${{ matrix.generate_api_docs }} | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: api-docs | ||
| path: scripts/res/apiDocs/ | ||
|
|
||
| pkg-pr-new: | ||
| needs: | ||
| - build-compiler | ||
|
|
@@ -465,6 +480,49 @@ jobs: | |
| run: | | ||
| yarn dlx pkg-pr-new publish "." "./packages/@rescript/*" | ||
|
|
||
| sync-api-docs: | ||
| needs: | ||
| - build-compiler | ||
| runs-on: ubuntu-24.04-arm | ||
| steps: | ||
| - name: Checkout rescript-lang.org | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| repository: rescript-lang/rescript-lang.org | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Download artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| artifact-ids: ${{ needs.build-compiler.outputs.api-docs-artifact-id }} | ||
| path: data/api | ||
|
|
||
| - name: Check if repo is clean | ||
| id: diffcheck | ||
| run: | | ||
| if [ -z "$(git status --porcelain)" ]; then | ||
| echo "clean=true" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "clean=false" >> $GITHUB_OUTPUT | ||
| fi | ||
|
|
||
| - name: Build website | ||
| if: steps.diffcheck.outputs.clean == 'false' | ||
| run: | | ||
| npm ci | ||
| npx rescript | ||
| npm run build | ||
|
|
||
| - name: Commit and push | ||
| if: ${{ startsWith(github.ref, 'refs/tags/v') }} | ||
| run: | | ||
| echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token | ||
| git config --global user.name "github-actions[bot]" | ||
| git config --global user.email "[email protected]" | ||
| git add data/api | ||
| git commit -m "Update API docs for ${{ github.ref_name }}" | ||
| git push | ||
|
|
||
| test-integration: | ||
| needs: | ||
| - pkg-pr-new | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,7 +52,9 @@ | |
| "check:all": "biome check .", | ||
| "format": "biome check --changed --no-errors-on-unmatched . --fix", | ||
| "coverage": "nyc --timeout=3000 --reporter=html mocha tests/tests/src/*_test.js && open ./coverage/index.html", | ||
| "typecheck": "tsc" | ||
| "typecheck": "tsc", | ||
| "scripts:build": "rescript", | ||
| "apidocs:generate": "yarn workspace @utils/scripts apidocs:generate" | ||
| }, | ||
| "files": [ | ||
| "CHANGELOG.md", | ||
|
|
@@ -96,8 +98,10 @@ | |
| "packages/@rescript/*", | ||
| "tests/dependencies/**", | ||
| "tests/analysis_tests/**", | ||
| "tests/docstring_tests", | ||
| "tests/gentype_tests/**", | ||
| "tests/tools_tests" | ||
| "tests/tools_tests", | ||
| "scripts/res" | ||
| ], | ||
| "packageManager": "[email protected]", | ||
| "preferUnplugged": true | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| *.res.js | ||
| lib | ||
| apiDocs/**/* | ||
| !.gitkeep |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,264 @@ | ||
| /*** | ||
| Generate API docs from ReScript Compiler | ||
|
|
||
| ## Run | ||
|
|
||
| ```bash | ||
| node scripts/res/GenApiDocs.res.js | ||
| ``` | ||
| */ | ||
| open Node | ||
| module Docgen = RescriptTools.Docgen | ||
|
|
||
| let packagePath = Path.join([Node.dirname, "..", "..", "package.json"]) | ||
| let version = switch Fs.readFileSync(packagePath, ~encoding="utf8")->JSON.parseOrThrow { | ||
| | Object(dict{"version": JSON.String(version)}) => version | ||
| | _ => JsError.panic("Invalid package.json format") | ||
| } | ||
| let version = Semver.parse(version)->Option.getExn | ||
| let version = Semver.toString({...version, preRelease: None}) // Remove pre-release identifiers for API docs | ||
| let dirVersion = Path.join([Node.dirname, "apiDocs", version]) | ||
| if !Fs.existsSync(dirVersion) { | ||
| Fs.mkdirSync(dirVersion) | ||
| } | ||
|
|
||
|
|
||
| let entryPointFiles = ["Belt.res", "Dom.res", "Js.res", "Stdlib.res"] | ||
|
|
||
| let hiddenModules = ["Js.Internal", "Js.MapperRt"] | ||
|
|
||
| type module_ = { | ||
| id: string, | ||
| docstrings: array<string>, | ||
| name: string, | ||
| items: array<Docgen.item>, | ||
| } | ||
|
|
||
| type section = { | ||
| name: string, | ||
| docstrings: array<string>, | ||
| deprecated: option<string>, | ||
| topLevelItems: array<Docgen.item>, | ||
| submodules: array<module_>, | ||
| } | ||
|
|
||
| let env = Process.env | ||
|
|
||
| let docsDecoded = entryPointFiles->Array.map(libFile => | ||
| try { | ||
| let entryPointFile = Path.join([Node.dirname, "..", "..", "runtime", libFile]) | ||
|
|
||
| let rescriptToolsPath = Path.join([Node.dirname, "..", "..", "cli", "rescript-tools.js"]) | ||
| let output = ChildProcess.execSync( | ||
| `${rescriptToolsPath} doc ${entryPointFile}`, | ||
| ~options={ | ||
| maxBuffer: 30_000_000., | ||
| }, | ||
| )->Buffer.toString | ||
|
|
||
| let docs = output | ||
| ->JSON.parseOrThrow | ||
| ->Docgen.decodeFromJson | ||
| Console.log(`Generated docs from ${libFile}`) | ||
| docs | ||
| } catch { | ||
| | JsExn(exn) => | ||
| Console.error( | ||
| `Error while generating docs from ${libFile}: ${exn | ||
| ->JsExn.message | ||
| ->Option.getOr("[no message]")}`, | ||
| ) | ||
| JsExn.throw(exn) | ||
| } | ||
| ) | ||
|
|
||
| let removeStdlibOrPrimitive = s => s->String.replaceAllRegExp(/Stdlib_|Primitive_js_extern\./g, "") | ||
|
|
||
| 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<Docgen.item>, moduleNames: list<module_>) => | ||
| 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 { | ||
| getModules( | ||
| list{...rest, ...List.fromArray(items)}, | ||
| list{{id, items, name, docstrings}, ...moduleNames}, | ||
| ) | ||
| } | ||
| | list{Type(_) | Value(_), ...rest} => getModules(rest, moduleNames) | ||
| | list{} => moduleNames | ||
| } | ||
|
|
||
| let id = 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 dict = Dict.fromArray( | ||
| [ | ||
| ("id", id->String), | ||
| ("kind", "value"->String), | ||
| ("name", name->String), | ||
| ( | ||
| "docstrings", | ||
| docstrings | ||
| ->Array.map(s => s->removeStdlibOrPrimitive->String) | ||
| ->Array, | ||
| ), | ||
| ( | ||
| "signature", | ||
| signature | ||
| ->removeStdlibOrPrimitive | ||
| ->String, | ||
| ), | ||
| ]->Array.concat( | ||
| switch deprecated { | ||
| | Some(v) => [("deprecated", v->String)] | ||
| | None => [] | ||
| }, | ||
| ), | ||
| ) | ||
| dict->Object->Some | ||
| } | ||
|
|
||
| | Type({id, name, docstrings, signature, ?deprecated}) => | ||
| let dict = Dict.fromArray( | ||
| [ | ||
| ("id", id->String), | ||
| ("kind", "type"->String), | ||
| ("name", name->String), | ||
| ("docstrings", docstrings->Array.map(s => s->removeStdlibOrPrimitive->String)->Array), | ||
| ("signature", signature->removeStdlibOrPrimitive->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 rest = Dict.fromArray([ | ||
| ("id", mod.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) | ||
|
|
||
| Fs.writeFileSync( | ||
| Path.join([dirVersion, `${topLevelName->String.toLowerCase}.json`]), | ||
| json->JSON.stringify(~space=2), | ||
| ) | ||
| }) | ||
| } | ||
|
|
||
| type rec node = { | ||
| name: string, | ||
| path: array<string>, | ||
| children: array<node>, | ||
| } | ||
|
|
||
| // Generate TOC modules | ||
| let () = { | ||
| let joinPath = (~path: array<string>, ~name: string) => { | ||
| Array.concat(path, [name])->Array.map(path => path->String.toLowerCase) | ||
| } | ||
| let rec getModules = (lst: list<Docgen.item>, 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 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.getExn, | ||
| ) | ||
| Console.log("Generated toc_tree.json") | ||
| Console.log(`API docs generated successfully in ${dirVersion}`) | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need this? It's not any shorter than
yarn rescript.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can get rid of it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually that was a mistake, it should have been elsewhere, I'm removing it.