diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 76ac9c5fc..000000000 --- a/.babelrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presets": [ - "next/babel" - ], - "plugins": [ - ] -} diff --git a/.env b/.env new file mode 100644 index 000000000..431fdc073 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +VERSION_LATEST="v11.0.0" +VERSION_NEXT="v12.0.0" \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..291b72280 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.res linguist-language=ReScript +*.resi linguist-language=ReScript diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..2931b7fef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,20 @@ +blank_issues_enabled: false +contact_links: + - name: 🚀 rescript-compiler + url: https://github.com/rescript-lang/rescript/issues + about: Please report problems about the ReScript compiler, build system and tools here. + - name: 💻 rescript-vscode + url: https://github.com/rescript-lang/rescript-vscode/issues + about: VSCode language support, LSP + - name: 📦 create-rescript-app + url: https://github.com/rescript-lang/create-rescript-app/issues + about: ReScript's project generator + - name: ⚛️ rescript-react + url: https://github.com/rescript-lang/rescript-react/issues + about: ReScript bindings to React.js + - name: 🌐 rescript-core + url: https://github.com/rescript-lang/rescript-core/issues + about: New ReScript standard library + - name: 💬 ReScript Forum + url: https://forum.rescript-lang.org/ + about: For discussions about ReScript, please visit the official ReScript forum. diff --git a/.github/ISSUE_TEMPLATE/documentation_issue.md b/.github/ISSUE_TEMPLATE/documentation_issue.md new file mode 100644 index 000000000..f371b6270 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_issue.md @@ -0,0 +1,9 @@ +--- +name: 🚨 Documentation issue +about: Create an issue to help us improve the rescript-lang.org documentation website +title: '' +labels: '' +assignees: '' + +--- + diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 7060a6cb9..58df60cb2 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -2,13 +2,16 @@ name: Test on: [pull_request] jobs: build: - name: Test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 14.x + node-version: 20 + cache: npm - run: npm ci - run: npx rescript - run: npm test + + - name: Format check + run: npx rescript format -check -all diff --git a/.gitignore b/.gitignore index 45fba9594..d364e12a7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,14 @@ node_modules/ .next/ index_data/*.json +# Generated via test examples script +_tempFile.cmi +_tempFile.cmj +_tempFile.cmt + # these docs are checked in, but we consider them frozen. -pages/docs/manual/v8.0.0/ -pages/docs/manual/v9.0.0/ +# pages/docs/manual/v8.0.0/ +# pages/docs/manual/v9.0.0/ .bsb.lock .merlin @@ -19,3 +24,11 @@ lib/ .vscode/ .vercel + +src/**/*.mjs +scripts/**/*.mjs + +# Generated via generate-llms script +public/llms/manual/**/llm*.txt +public/llms/react/**/llm*.txt +pages/docs/**/**/llms.mdx diff --git a/.node-version b/.node-version new file mode 100644 index 000000000..7af24b7dd --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +22.11.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 66daddd57..d80d2ea86 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,8 +6,8 @@ Please make sure to check out our [Code of Conduct](CODE_OF_CONDUCT.md) and make ## Ways to contribute -- Writing docs for the manual (Check for issues that are marked with a [`manual`](https://github.com/reason-association/rescript-lang.org/issues?q=is%3Aissue+is%3Aopen+label%3A"manual") and [`help wanted`](https://github.com/reason-association/rescript-lang.org/issues?q=is%3Aissue+is%3Aopen+label%3A"help+wanted") tag) -- Joining in discussions on our [issue tracker](https://github.com/reason-association/rescript-lang.org/issues) +- Writing docs for the manual (Check for issues that are marked with a [`manual`](https://github.com/rescript-lang/rescript-lang.org/issues?q=is%3Aissue+is%3Aopen+label%3A"manual") and [`help wanted`](https://github.com/rescript-lang/rescript-lang.org/issues?q=is%3Aissue+is%3Aopen+label%3A"help+wanted") tag) +- Joining in discussions on our [issue tracker](https://github.com/rescript-lang/rescript-lang.org/issues) - Give feedback for improvements (incomplete / missing docs, bad wording, search user experience / design, etc.) - Advanced: Help building platform features (design system, automatic testing, markdown parsing, etc.) @@ -16,7 +16,7 @@ Please make sure to check out our [Code of Conduct](CODE_OF_CONDUCT.md) and make ### Find an issue -Before you start any work or submit any PRs, make sure to check our [issue tracker](https://github.com/reason-association/rescript-lang.org/issues) for any issues or discussions on the topic. +Before you start any work or submit any PRs, make sure to check our [issue tracker](https://github.com/rescript-lang/rescript-lang.org/issues) for any issues or discussions on the topic. If you can't find any relevant issues, feel free to create a new one to start a discussion. We usually assign issues to a responsible person to prevent confusion and duplicate work, so always double check if an issue is currently being worked on, or talk to the current assignee to take over the task. @@ -24,7 +24,7 @@ If you can't find any relevant issues, feel free to create a new one to start a The project follows very specific goals and tries to deliver the highest value with the least amount of resources. Please help us focus on the tasks at hand and don't submit any code / bigger refactorings without any proper discussion on the issue tracker. Otherwise your PR might not be accepted! -If you need inspiration on what to work on, you can check out issues tagged with [`good first issue`](https://github.com/reason-association/rescript-lang.org/issues?q=is%3Aissue+is%3Aopen+label%3A"good+first+issue") or [`help wanted`](https://github.com/reason-association/rescript-lang.org/issues?q=is%3Aissue+is%3Aopen+label%3A"help+wanted"). +If you need inspiration on what to work on, you can check out issues tagged with [`good first issue`](https://github.com/rescript-lang/rescript-lang.org/issues?q=is%3Aissue+is%3Aopen+label%3A"good+first+issue") or [`help wanted`](https://github.com/rescript-lang/rescript-lang.org/issues?q=is%3Aissue+is%3Aopen+label%3A"help+wanted"). ### Discuss an issue @@ -44,7 +44,7 @@ We value your voluntary work, and of course it's fine to step back from a ticket ### Communication Channels -- [Issue Tracker](https://github.com/reason-association/rescript-lang.org/issues) +- [Issue Tracker](https://github.com/rescript-lang/rescript-lang.org/issues) - [ReScript Discourse (General / mostly unrelated discussions)](http://forum.rescript-lang.org) ## Working on the rescript-lang.org @@ -55,7 +55,7 @@ We try to keep our contribution guidelines to a minimum. Please keep following r The less code we write, the better. If there's a way to do rendering on the server, or enhance existing markdown files, we prefer that over client-side rendering and external loading. -We also try to keep our third-party dependencies to a minimum. We use specific frameworks to make things work (`unified`, `remark`, `mdx`, `bs-platform`, etc). Please try to keep a small JS footprint, especially for client side code (to keep the bundle size small). +We also try to keep our third-party dependencies to a minimum. We use specific frameworks to make things work (`unified`, `remark`, `mdx`, `bs-platform`, etc). Please try to keep a small JS footprint, especially for client side code (to keep the bundle size small). ### Think about the target audience & UX @@ -72,7 +72,7 @@ Always check if there are any designs for certain UI components and think about ### Tailwind for CSS Development -We use [TailwindCSS](https://tailwindcss.com) for our component styling. Check out the [tailwind.config.js](tailwind.config.js) file for configured tailwind features, colors, border-radius values etc.. If you are not familiar with Tailwind, check out existing components for inspiration. +We use [TailwindCSS](https://tailwindcss.com) for our component styling. Check out the [tailwind.config.js](tailwind.config.js) file for configured tailwind features, colors, border-radius values etc.. If you are not familiar with Tailwind, check out existing components for inspiration. We sometimes also need to fall back to common css (with tailwind `@apply` directives to enforce our style system). You can find the CSS main entrypoint in [styles/main.css](styles/main.css). diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..5e97af21a --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +SHELL = /bin/bash + +node_modules/.bin/rescript: + npm install + npm run update-index + +build: node_modules/.bin/rescript + node_modules/.bin/rescript + npm run update-index + +dev: build + npm run dev + +test: build + npm run test + +clean: + rm -r node_modules lib + +.DEFAULT_GOAL := build + +.PHONY: clean test diff --git a/README.md b/README.md index 89e984a8d..48b2e84bd 100644 --- a/README.md +++ b/README.md @@ -6,19 +6,18 @@ This is the official documentation platform for the [ReScript](https://rescript-lang.org) programming language. -**In case you want to report a technical issue, please refer to the appropriate repository:** -- [rescript-compiler](https://github.com/rescript-lang/rescript-compiler): The compiler and build system -- [rescript-syntax](https://github.com/rescript-lang/syntax): The ReScript syntax +**Please report any technical issues with ReScript to the [compiler repository](https://github.com/rescript-lang/rescript).** **In case you are missing some specific documentation:** + - Some language / compiler feature may not be documented yet - Create an issue to let us know what you are missing - In case you want to contribute missing docs, please refer to our [Contribution section](#contributing) ## System Requirements -- `node@12.22.1` or higher (for ES6 module compat) -- `npm@7` (package-lock v2) +- `node@20` or higher +- `npm@10` or higher ## Setup @@ -26,13 +25,10 @@ This is the official documentation platform for the [ReScript](https://rescript- # For first time clone / build (install dependencies) npm i -# Only needed for initial clone (or content H2 changes) -npm run update-index - # Initial build npx rescript -# Build the index data +# Build the index data. Only needed for initial clone (or content H2 changes) npm run update-index # In a new tab @@ -44,7 +40,7 @@ open localhost:3000 In case you want to run ReScript in watchmode: ```sh -npx rescript build -w +npx rescript -w ``` ## Build Index Data @@ -84,14 +80,15 @@ build specific pages (file `index_data/x.json` not found). ### Markdown Codeblock Tests We check the validity of our code examples marked with: -- `` ```res example `` (ReScript code snippet) -- `` ```res sig `` (signature) -- `` ```res prelude `` (ReScript code snippet available for all subsequent code snippets) + +- ` ```res example ` (ReScript code snippet) +- ` ```res sig ` (signature) +- ` ```res prelude ` (ReScript code snippet available for all subsequent code snippets) Run the checks with: ```sh -node scripts/test-examples.js +node scripts/test-examples.mjs ``` ### Markdown Hyperlink Tests @@ -117,10 +114,10 @@ Here is an example on how to run the tests: ```sh # Tests all files -node scripts/test-hrefs.js +node scripts/test-hrefs.mjs # Or just a subset (glob pattern) -node scripts/test-hrefs.js "pages/docs/manual/**/*.mdx" +node scripts/test-hrefs.mjs "pages/docs/manual/**/*.mdx" ``` ### Continous Integration diff --git a/_blogposts/2020-08-10-bucklescript-is-rebranding.mdx b/_blogposts/2020-08-10-bucklescript-is-rebranding.mdx index f924b55eb..168c3abf5 100644 --- a/_blogposts/2020-08-10-bucklescript-is-rebranding.mdx +++ b/_blogposts/2020-08-10-bucklescript-is-rebranding.mdx @@ -61,7 +61,7 @@ There's no dedicated name for the syntax anymore. It's simply called the ReScrip **Will there be a migration script to gradually convert our code to the new syntax?** -Yes. See our [migration page](/docs/manual/latest/migrate-from-bucklescript-reason). You can mix and match old and new code for a smoother transition. +Yes. See our [migration page](/docs/manual/v10.0.0/migrate-from-bucklescript-reason). You can mix and match old and new code for a smoother transition. **Will BuckleScript (now ReScript) break my existing code?** @@ -99,7 +99,7 @@ The ReScript community will comprise of the majority of the old Reason community **The new changes make me worried about the future support I'm going to receive from the team.** -_Retrospective edit, in 2021_: suffice to day that we've been doing well =). +_Retrospective edit, in 2021_: suffice to say that we've been doing well =). ## Conclusion diff --git a/_blogposts/2020-09-25-release-8-3-2.mdx b/_blogposts/2020-09-25-release-8-3-2.mdx index b092f53d4..a0ba49a92 100644 --- a/_blogposts/2020-09-25-release-8-3-2.mdx +++ b/_blogposts/2020-09-25-release-8-3-2.mdx @@ -86,7 +86,7 @@ There are two ways of removing staled artifacts, the second one is introduced in - Based on live analysis and prebuilt-in knowledge -We scan `lib/bs` directory and check some dangling cm{i,t,j,ti} files, if it does not exist in +We scan `lib/bs` directory and check some dangling `cm{i,t,j,ti}` files, if it does not exist in the current build set, it is considered stale artifacts. If it is `cmt` file, it would trigger some hooks of `genType`, notably -cmt-rm. - Based on previous build logs diff --git a/_blogposts/2020-09-25-release-8-3.mdx b/_blogposts/2020-09-25-release-8-3.mdx index a3a629be9..5975e6678 100644 --- a/_blogposts/2020-09-25-release-8-3.mdx +++ b/_blogposts/2020-09-25-release-8-3.mdx @@ -17,7 +17,7 @@ It's focused on type safety, performance and JS interop. It used to be called Bu npm i bs-platform@8.3.0 ``` -The changes are listed [here](https://github.com/rescript-lang/rescript-compiler/blob/master/Changes.md#83), this is a large release, and we will go through some highlighted changes. +The changes are listed [here](https://github.com/rescript-lang/rescript/blob/master/Changes.md#83), this is a large release, and we will go through some highlighted changes. @@ -122,7 +122,7 @@ Now user can pick up their js file extension support per module format: ## More flexible filename support -To have better integration with other [JS infrastructures](https://github.com/rescript-lang/rescript-compiler/issues/4624), +To have better integration with other [JS infrastructures](https://github.com/rescript-lang/rescript/issues/4624), for example, Next.js/React Native, we allow file names like `404.res`, `Button.Android.res` so that it can just be picked up by those tools diff --git a/_blogposts/2020-11-17-editor-support-custom-operators-and-more.mdx b/_blogposts/2020-11-17-editor-support-custom-operators-and-more.mdx index 661b6ea36..43b21fe30 100644 --- a/_blogposts/2020-11-17-editor-support-custom-operators-and-more.mdx +++ b/_blogposts/2020-11-17-editor-support-custom-operators-and-more.mdx @@ -8,7 +8,7 @@ description: | Update on what we're doing around the end of 2020 and early next year. --- -import Video from "src/components/Video" +import Video from "src/components/Video"; ## Upcoming Improvements @@ -28,7 +28,7 @@ Hongbo continues to improve the compiler experience in monorepo-like setups. Exp ## Docs -Patrick is [rearranging the React documentation](https://github.com/reason-association/rescript-lang.org/pull/96), and continues to improve the main documentation site with Cheng Lou. +Patrick is [rearranging the React documentation](https://github.com/rescript-lang/rescript-lang.org/pull/96), and continues to improve the main documentation site with Cheng Lou. ## Syntax diff --git a/_blogposts/2020-12-07-release-8-4.mdx b/_blogposts/2020-12-07-release-8-4.mdx index a51a0dfe0..bc7484feb 100644 --- a/_blogposts/2020-12-07-release-8-4.mdx +++ b/_blogposts/2020-12-07-release-8-4.mdx @@ -19,7 +19,7 @@ It's focused on type safety, performance and JS interop. It used to be called Bu npm i bs-platform@8.4.2 ``` -The changes are listed [here](https://github.com/rescript-lang/rescript-compiler/blob/master/Changes.md#841). +The changes are listed [here](https://github.com/rescript-lang/rescript/blob/master/Changes.md#841). We will go through some highlighted changes. @@ -33,11 +33,10 @@ In this release, we fix the integrity of `bsb -make-world` which allows user to The fix is well implemented that people who don't do such modifications will not pay for it. This is one of the highest desired feature request based on the -[user feedback](https://github.com/rescript-lang/rescript-compiler/issues/4361#issuecomment-739538789), so we will expand a bit here why it is tricky -to implement it without compromising performance. +[user feedback](https://github.com/rescript-lang/rescript/issues/4361#issuecomment-739538789), so we will expand a bit here why it is tricky to implement it without compromising performance. In ReScript compilation scheme, dependencies as packages are treated as a black box, -changes of dependencies should be *transitive*. The is due to that we have cross module +changes of dependencies should be _transitive_. The is due to that we have cross module optimizations and the binary interface itself is a hash of its dependencies. So for a package dependency chain: A -> B -> C, if A changes and B does not change, C still needs get rebuilt. Because the intermediate output of B may still change due to the change of A. @@ -68,6 +67,6 @@ of such stale outptu, such stale `List.cmi` file will break the integrity of the In this release, we introduced a more robust algorithm that will always remove stale output before the build so that such integrity is not broken. -Last but not the least, we continue improving the readability, debuggability of the [generated output](https://github.com/rescript-lang/rescript-compiler/pull/4858) : ) +Last but not the least, we continue improving the readability, debuggability of the [generated output](https://github.com/rescript-lang/rescript/pull/4858) : ) Happy Hacking! -- Hongbo Zhang diff --git a/_blogposts/2021-02-09-release-9-0.mdx b/_blogposts/2021-02-09-release-9-0.mdx index 264b4d363..0ae1f049c 100644 --- a/_blogposts/2021-02-09-release-9-0.mdx +++ b/_blogposts/2021-02-09-release-9-0.mdx @@ -16,19 +16,19 @@ ReScript is a robustly typed language that compiles to efficient and human-reada Use `npm` to install the newest [9.0.1 release](https://www.npmjs.com/package/bs-platform/v/9.0.1) with the following command: -``` -npm install bs-platform@9.0.1 --save-dev +```sh +npm install bs-platform@9.0.1 ``` You can also try our new release in the [Online Playground](/try). -In this post we will highlight the most notable changes. The full changelog for this release can be found [here](https://github.com/rescript-lang/rescript-compiler/blob/master/Changes.md#90). +In this post we will highlight the most notable changes. The full changelog for this release can be found [here](https://github.com/rescript-lang/rescript/blob/master/Changes.md#90). ## Compiler Improvements ### New External Stdlib Configuration -This is a long-awaited [feature request](https://github.com/rescript-lang/rescript-compiler/pull/2171). +This is a long-awaited [feature request](https://github.com/rescript-lang/rescript/pull/2171). Our compiler comes with a set of stdlib modules (such as `Belt`, `Pervasives`, etc.) for core functionality. Compiled ReScript code relies on the JS runtime version of these stdlib modules. @@ -47,7 +47,7 @@ We made a small [demo repo](https://github.com/bobzhang/zero-cost-rescript) and ### Improved Code Generation for Pattern Matching -We fine-tuned our pattern matching engine to optimize the JS output even more. Here is an example of a pretty substantial optimization, based on [this issue](https://github.com/rescript-lang/rescript-compiler/issues/4924): +We fine-tuned our pattern matching engine to optimize the JS output even more. Here is an example of a pretty substantial optimization, based on [this issue](https://github.com/rescript-lang/rescript/issues/4924): ```res type test = @@ -87,7 +87,7 @@ function test(x) { As you can see, the 9.0 compiler removes all the unnecessary `typeof` checks! -This is possible because our optimizer will try to analyze several predicates and get rid of redundant ones. More diffs can be found [here](https://github.com/rescript-lang/rescript-compiler/pull/4927/files?file-filters%5B%5D=.js). +This is possible because our optimizer will try to analyze several predicates and get rid of redundant ones. More diffs can be found [here](https://github.com/rescript-lang/rescript/pull/4927/files?file-filters%5B%5D=.js). Another important improvement is that we fixed the pattern match offset issue, which lead to the consequence that magic numbers will not be generated for complex pattern matches anymore. diff --git a/_blogposts/2021-03-03-rescript-association-rebranding.mdx b/_blogposts/2021-03-03-rescript-association-rebranding.mdx index b330f4be9..e216cb619 100644 --- a/_blogposts/2021-03-03-rescript-association-rebranding.mdx +++ b/_blogposts/2021-03-03-rescript-association-rebranding.mdx @@ -25,7 +25,7 @@ Founded in 2018, the ReScript Association provides a legal and financial foundat - [rescript-lang.org](https://rescript-lang.org). - [Community forum](https://forum.rescript-lang.org) & server. - ReScript related domains and [analytics data](https://simpleanalytics.com/rescript-lang.org). -- [genType’s](https://github.com/rescript-association/genType) release automation. +- [genType’s](https://github.com/rescript-lang/genType) release automation. - Help maintaining editor related tools such as [rescript-vscode](https://github.com/rescript-lang/rescript-vscode), [vim-rescript](https://github.com/rescript-lang/vim-rescript) and the underlying [editor-support](https://github.com/rescript-lang/rescript-editor-support). - Design & logo assets (together with our designer) for all of ReScript. - Helping out on upcoming [ocaml.org](https://ocaml.org) work. @@ -48,6 +48,7 @@ High quality, long-term Open Source work doesn’t come from some good words and If your company relies on the ReScript platform for building commercial products, please consider supporting our efforts by [sending a donation](https://rescript-association.org/donate). It’s the best way to future proof your product’s foundation. Alternatively, you can sponsor individual members like [ryyppy](https://github.com/sponsors/ryyppy/) on GitHub Sponsors. We want to take this opportunity to thank our previous and active sponsors: + - [Tezos Foundation](https://tezos.foundation) (2020-21) - [Ahrefs](https://ahrefs.com) (2019) - [OCaml Software Foundation](https://ocaml-sf.org) (2018-19) diff --git a/_blogposts/2021-05-07-release-9-1.mdx b/_blogposts/2021-05-07-release-9-1.mdx index e5573cf00..75f021180 100644 --- a/_blogposts/2021-05-07-release-9-1.mdx +++ b/_blogposts/2021-05-07-release-9-1.mdx @@ -10,7 +10,7 @@ description: | ## Exciting Improvements in ReScript 9.1 -Our recent few releases of ReScript contains [lots of improvements](https://github.com/rescript-lang/rescript-compiler/blob/3134392a364b70c9c172aa6c1dbaa1ac6580265d/Changes.md#91), among which are a few standout features we'd like to further promote. Hope you're as excited as we are about these! It goes without saying, our [updated editor plugin](https://forum.rescript-lang.org/t/ann-rescript-vscode-1-1-1-released/1542/3) works with the new releases. +Our recent few releases of ReScript contains [lots of improvements](https://github.com/rescript-lang/rescript/blob/3134392a364b70c9c172aa6c1dbaa1ac6580265d/Changes.md#91), among which are a few standout features we'd like to further promote. Hope you're as excited as we are about these! It goes without saying, our [updated editor plugin](https://forum.rescript-lang.org/t/ann-rescript-vscode-1-1-1-released/1542/3) works with the new releases. ### New NPM Package @@ -159,6 +159,6 @@ let helloUnicode = (x) =>{ ## Conclusion -Don't miss our various other improvements in [our changelog](https://github.com/rescript-lang/rescript-compiler/blob/3134392a364b70c9c172aa6c1dbaa1ac6580265d/Changes.md#91). As always we try to keep our changes performant, lean and robust. We hope you'll enjoy these. +Don't miss our various other improvements in [our changelog](https://github.com/rescript-lang/rescript/blob/3134392a364b70c9c172aa6c1dbaa1ac6580265d/Changes.md#91). As always we try to keep our changes performant, lean and robust. We hope you'll enjoy these. See you next time! diff --git a/_blogposts/2021-06-25-roadmap-2021-and-new-landing-page.mdx b/_blogposts/2021-06-25-roadmap-2021-and-new-landing-page.mdx index 543106c1d..a1da83db4 100644 --- a/_blogposts/2021-06-25-roadmap-2021-and-new-landing-page.mdx +++ b/_blogposts/2021-06-25-roadmap-2021-and-new-landing-page.mdx @@ -21,7 +21,7 @@ It has almost been a year since we originally [launched our new ReScript brand]( - Accessible object system (no need for `Js.t`) - The release of our new `rescript` npm package and cli to replace `bs-platform` - Making every part of ReScript fully community owned -- etc. +- etc. Stay tuned, this is just the beginning! @@ -30,6 +30,7 @@ Stay tuned, this is just the beginning! We had some thorough discussions about the future of the project and outlined the most important milestones for the next upcoming releases. **Here's the gist:** + - Two release channels: `stable` and `experimental` - More predictable release dates and better migration steps - Better communication and discussion for breaking changes @@ -41,7 +42,11 @@ The detailed roadmap with all our planned changes (and definition of our release After several iterations, we are happy to announce our new [landing page](/). - + This is an incredible milestone for the documentation, and will act as a foundation for some cool new future improvements, such as: @@ -49,7 +54,6 @@ This is an incredible milestone for the documentation, and will act as a foundat - An interactive playground widget for the headline code examples - New starter templates and guides - -Furthermore, in case you are a **production user of ReScript** and you want to see your company logo highlighted on the landing page, please [open an issue](https://github.com/rescript-association/rescript-lang.org/issues) and let us know! +Furthermore, in case you are a **production user of ReScript** and you want to see your company logo highlighted on the landing page, please [open an issue](https://github.com/rescript-lang/rescript-lang.org/issues) and let us know! Happy hacking. diff --git a/_blogposts/2022-08-25-release-10-0-0.mdx b/_blogposts/2022-08-25-release-10-0-0.mdx new file mode 100644 index 000000000..b66493af2 --- /dev/null +++ b/_blogposts/2022-08-25-release-10-0-0.mdx @@ -0,0 +1,91 @@ +--- +author: rescript-team +date: "2022-08-25" +previewImg: static/blog/grid_0.jpeg +title: ReScript 10.0 +badge: release +description: | + The first community powered release. +--- + +ReScript version 10 is available! Version 10 is a culmination of over a year's worth of work, bringing faster builds, improving JS interop, and including a bunch of bug fixes. It's also the first fully community powered release, with contributions from over 20 community members. + +``` +npm install rescript@10 +``` + +All changes are listed [here](https://github.com/rescript-lang/rescript/blob/10.0_release/CHANGELOG.md). Let's take a tour of a few of the features we're extra excited about. + +## Faster builds with native M1 support +Users with M1 chips should see a notable speedup, as the new ReScript version has full native support for M1. + +## Better ergonomics with Unicode support in regular strings +You can now use Unicode characters directly in regular strings. This will now produce what you'd expect: +```res +let str = "Σ" +``` + +You can also pattern match on Unicode characters: +```res +switch someCharacter { + | 'Σ' => "what a fine Unicode char" + | _ => "Unicode is fun" +} +``` + +## Experimental optional record fields +Previously, a record would always have to define all its optional fields: +```res +type user = { + name: string, + age: option +} + +let userWithoutAge = { + name: "Name", + age: None, +} + +let userWithAge = { + name: "Name", + age: Some(34), +} +``` +For small records like the one above, this is typically fine. But for records with many fields, the friction of having to always set all optional fields explicitly adds up. This release has a new experimental feature called optional record fields, allowing you to rewrite the above to this instead: + +```res +type user = { + name: string, + age?: int +} + +// No need to set `age` unless it should have a value +let userWithoutAge = { + name: "Name", +} + +let userWithAge = { + name: "Name", + age: 34 +} +``` + +Other than drastically improving the experience when working with large records with optional fields, this also has implications for bindings. For example, binding to JS APIs with large configuration objects is now more ergonomic. +This feature also paves the way for other exciting features coming in the next release, such as a more idiomatic representation of React components. + +## What's next + +Version 10 brings the building blocks needed for a number of exciting new features that'll be available in the next version. Features ranging from native support for async/await, to a new version of the JSX integration, making it leaner and more flexible. You'll hear more about this soon. + +## Upgrade guide + +Please see the detailed [changelog](https://github.com/rescript-lang/rescript/blob/10.0_release/CHANGELOG.md) for a list of breaking changes. +Each breaking change lists suggestions on how to upgrade your project. +This can be out of your control in case of dependencies. In that case, please raise issues with the maintainers of those libraries. + +One special word for PPXs, in particular for PPX authors: As mentioned in the changelog, some PPXs may give an error `"Attributes not allowed here"`. The solution is to adapt the PPXs following the example of `rescript-relay` in https://github.com/zth/rescript-relay/pull/372. + +## Acknowledgements + +We would like to thank everyone from the community who volunteered their precious time to suport this project with contributions of any kind, from documentation, to PRs, to discussions in the forum. +In particular, thank you [@cknitt](https://github.com/cknitt), [@TheSpyder](https://github.com/TheSpyder), [@mattdamon108](https://github.com/mattdamon108), [@DZakh](https://github.com/DZakh), [@fhammerschmidt](https://github.com/fhammerschmidt), [@amiralies](https://github.com/amiralies), [@Minnozz](https://github.com/Minnozz), [@Zeta611](https://github.com/Zeta611), [@jchavarri](https://github.com/jchavarri), [@nkrkv](https://github.com/nkrkv), [@whitchapman](https://github.com/whitchapman), [@ostera](https://github.com/ostera), [@benadamstyles](https://github.com/benadamstyles), [@cannorin](https://github.com/cannorin), [@ClaireNeveu](https://github.com/ClaireNeveu), [@kevinbarabash](https://github.com/kevinbarabash), [@JsonKim](https://github.com/JsonKim), [@Sehun0819](https://github.com/Sehun0819), [@glennsl](https://github.com/glennsl), [@namenu](https://github.com/namenu), [@a-c-sreedhar-reddy](https://github.com/a-c-sreedhar-reddy). diff --git a/_blogposts/2023-02-02-release-10-1.mdx b/_blogposts/2023-02-02-release-10-1.mdx new file mode 100644 index 000000000..cce653a2e --- /dev/null +++ b/_blogposts/2023-02-02-release-10-1.mdx @@ -0,0 +1,308 @@ +--- +author: rescript-team +date: "2023-02-02" +title: ReScript 10.1 +badge: release +description: | + Async/await & better Promise support, JSX v4, and more! +--- + +## Introduction + +We are happy to announce ReScript 10.1! + +ReScript is a robustly typed language that compiles to efficient and human-readable JavaScript. It comes with one of the fastest build toolchains and offers first class support for interoperating with ReactJS and other existing JavaScript code. + +Use `npm` to install the newest [10.1 release](https://www.npmjs.com/package/rescript/v/10.1.2): + +``` +npm install rescript + +# or + +npm install rescript@10.1 +``` + +This version comes with two major language improvements we've all been waiting for. **async/await support** for an easy way to write asynchronous code in a synchronous manner, and a **new JSX transform** with better ergonomics, code generation and React 18 support. + +Alongside the major changes, there have been many bugfixes and other improvements that won't be covered in this post. + +Feel free to check the [Changelog](https://github.com/rescript-lang/rescript/blob/master/CHANGELOG.md#1011) for all the details. + +## New `async` / `await` syntax + +Async / await has arrived. Similar to its JS counterparts, you are now able to define `async` functions and use the `await` operator to unwrap a promise value. This allows writing asynchronous code in a synchronous fashion. + +**Example:** + +```res +// Some fictive functionality that offers asynchronous network actions +@val external fetchUserMail: string => promise = "GlobalAPI.fetchUserMail" +@val external sendAnalytics: string => promise = "GlobalAPI.sendAnalytics" + +// We use the `async` keyword to allow the use of `await` in the function body +let logUserDetails = async (userId: string) => { + // We use `await` to fetch the user email from our fictive user endpoint + let email = await fetchUserMail(userId) + + await sendAnalytics(`User details have been logged for ${userId}`) + + Js.log(`Email address for user ${userId}: ${email}`) +} +``` + +To learn more about our async / await feature, check out the relevant [manual section](/docs/manual/latest/async-await). + +## New `promise` builtin type and `Js.Promise2` module + +In previous versions of ReScript, promises were expressed as a `Js.Promise.t<'a>` type, which was a little tedious to type. From now on, users may use the `promise<'a>` type instead. + +Quick example of a `.resi` file using the new `promise` type: + +```resi +// User.resi +type user + +let fetchUser: string => promise +``` + +Way easier on the eyes, don't you think? Note that the new `promise` type is fully compatible with `Js.Promise.t` (no breaking changes). + +Additionally, we also introduced the `Js.Promise2` module as a stepping stone to migrate `Js.Promise` based code to a first-pipe (->) friendly solution. For the daily practise you'll almost always want to use `async` / `await` to handle promises. + +(*Sidenote*: We are also well aware that our users want a solution to unify `Belt`, `Js` and `Js.xxx2` and have a fully featured "standard library" instead of adding more `Js.xxx2` modules. Good news is that we have a solution in the pipeline to fix this. `Js.Promise2` was introduced to ease the process later on and is not supposed to be the panacea of promise handling.) + +If you are already using a third-party promise library like [ryyppy/rescript-promise](https://github.com/ryyppy/rescript-promise) or similar, there's no need to migrate any existing code. Introduce `async` / `await` gradually in your codebase as you go. + + +## New JSX v4 syntax + +ReScript 10.1 now ships with JSX v4. Here's what's new: + +- **Cleaner interop.** Due to recent improvements in the type checker, the `@react.component` transformation doesn't require any `makeProps` convention anymore. `make` functions will now be transformed into a `prop` type and a component function. That's it. +- **Two new transformation modes**. JSX v4 comes with a `classic` mode (= `React.createElement`) and `automatic` mode (= `jsx-runtime` calls). The latter is the new default, moving forward with `rescript/react@0.11` and `React@18`. +- **Allow mixing JSX configurations on the project and module level.** Gradually mix and match JSX transformations and modes without migrating any old code! +- **Pass `prop` types** to `@react.component`. You can now fine tune `@react.component` with your specific prop type needs. Very useful for libraries and frameworks to define component interfaces. +- **Less boilerplate when using `React.Context`**. Check out our [example](/docs/react/latest/migrate-react#reactcontext) for comparison. +- **Revisited props spread operator.** This will allow users to spread records in JSX without sacrificing their sanity. Note that this implementation has harder constraints than its JS counterpart. (requires `rescript/react@0.11` or higher) +- **Better type inference of props.** Type inference when passing e.g. variants that are defined in the same module as the component is much improved. With the earlier JSX version, you'd often need to write code like this in order for the compiler to understand which variant you're passing: ` + + preludeSection +
+
+ {"Overview"->React.string} +
+ Array.length == 1 ? classNameActive : "")} + href={node.path->Array.join("/")}> + {node.name->React.string} + + {moduleRoute->Array.length === 1 ? subMenu : React.null} +
+
{"submodules"->React.string}
+ {node.children + ->Array.toSorted((v1, v2) => String.compare(v1.name, v2.name)) + ->Array.map(renderNode) + ->React.array} + + + } +} + +type module_ = { + id: string, + docstrings: array, + deprecated: Null.t, + name: string, + items: array, +} + +type api = { + module_: module_, + toctree: node, +} + +type params = {slug: array} +type props = result + +module MarkdownStylize = { + @react.component + let make = (~content, ~rehypePlugins) => { + let components = { + ...MarkdownComponents.default, + h2: MarkdownComponents.default.h3->Obj.magic, + } + content + } +} + +module DeprecatedMessage = { + @react.component + let make = (~deprecated) => { + switch deprecated->Null.toOption { + | Some(content) => + +

{"Deprecated"->React.string}

+ +
+ | None => React.null + } + } +} + +module DocstringsStylize = { + @react.component + let make = (~docstrings, ~slugPrefix) => { + let rehypePlugins = + [Rehype.WithOptions([Plugin(Rehype.slug), SlugOption({prefix: slugPrefix ++ "-"})])]->Some + + let content = switch docstrings->Array.length > 1 { + | true => docstrings->Array.sliceToEnd(~start=1) + | false => docstrings + }->Array.join("\n") + +
+ +
+ } +} + +let default = (props: props) => { + let (isSidebarOpen, setSidebarOpen) = React.useState(_ => false) + let toggleSidebar = () => setSidebarOpen(prev => !prev) + let router = Next.Router.useRouter() + + let title = switch props { + | Ok({module_: {id}}) => id + | _ => "API" + } + + let children = { + open Markdown + switch props { + | Ok({module_: {id, name, docstrings, items}}) => + let valuesAndType = items->Array.map(item => { + switch item { + | Value({name, signature, docstrings, deprecated}) => + let code = String.replaceRegExp(signature, /\\n/g, "\n") + let slugPrefix = "value-" ++ name + <> +

{name->React.string}

+ + + + + | Type({name, signature, docstrings, deprecated}) => + let code = String.replaceRegExp(signature, /\\n/g, "\n") + let slugPrefix = "type-" ++ name + <> +

{name->React.string}

+ + + + + } + }) + + <> +

{name->React.string}

+ + {valuesAndType->React.array} + + | _ => React.null + } + } + + let rightSidebar = switch props { + | Ok({module_: {items}}) if Array.length(items) > 0 => +
+ +
+ | _ => React.null + } + + let version = Url.parse(router.asPath)->Url.getVersionString + + let sidebar = switch props { + | Ok({toctree, module_: {items}}) => + + | Error(_) => React.null + } + + let prefix = { + {Url.name: "API", href: "/docs/manual/" ++ (version ++ "/api")} + } + + let breadcrumbs = ApiLayout.makeBreadcrumbs(~prefix, router.asPath) + + + children + +} + +module Data = { + type t = { + mainModule: Dict.t, + tree: Dict.t, + } + + let dir = Node.Path.resolve("data", "api") + + let getVersion = (~version: string, ~moduleName: string) => { + open Node + + let pathModule = Path.join([dir, version, `${moduleName}.json`]) + + let moduleContent = Fs.readFileSync(pathModule)->JSON.parseExn + + let content = switch moduleContent { + | Object(dict) => dict->Some + | _ => None + } + + let toctree = switch Path.join([dir, version, "toc_tree.json"]) + ->Fs.readFileSync + ->JSON.parseExn { + | Object(dict) => dict->Some + | _ => None + } + + switch (content, toctree) { + | (Some(content), Some(toctree)) => Some({mainModule: content, tree: toctree}) + | _ => None + } + } +} + +let processStaticProps = (~slug: array, ~version: string) => { + let moduleName = slug->Belt.Array.getExn(0) + let content = Data.getVersion(~version, ~moduleName) + + let modulePath = slug->Array.join("/") + + switch content { + | Some({mainModule, tree}) => + switch mainModule->Dict.get(modulePath) { + | Some(json) => + let {items, docstrings, deprecated, name} = Docgen.decodeFromJson(json) + let id = switch json { + | Object(dict) => + switch Dict.get(dict, "id") { + | Some(String(s)) => s + | _ => "" + } + | _ => "" + } + + let items = items->Array.map(item => + switch item { + | Docgen.Value({id, docstrings, signature, name, ?deprecated}) => + Value({ + id, + docstrings, + signature, + name, + deprecated: deprecated->Null.fromOption, + }) + | Type({id, docstrings, signature, name, ?deprecated, ?detail}) => + let detail = switch detail { + | Some(kind) => + switch kind { + | Docgen.Record({items}) => + let items = items->Array.map(({ + name, + docstrings, + signature, + optional, + ?deprecated, + }) => { + { + name, + docstrings, + signature, + optional, + deprecated: deprecated->Null.fromOption, + } + }) + Record({items: items})->Null.make + | Variant({items}) => + let items = items->Array.map(({name, docstrings, signature, ?deprecated}) => { + { + name, + docstrings, + signature, + deprecated: deprecated->Null.fromOption, + } + }) + + Variant({items: items})->Null.make + | Signature(_) => Null.null + } + | None => Null.null + } + Type({ + id, + docstrings, + signature, + name, + deprecated: deprecated->Null.fromOption, + detail, + }) + | _ => assert(false) + } + ) + let module_ = { + id, + name, + docstrings, + deprecated: deprecated->Null.fromOption, + items, + } + + let toctree = tree->Dict.get(moduleName) + + switch toctree { + | Some(toctree) => Ok({module_, toctree: (Obj.magic(toctree): node)}) + | None => Error(`Failed to find toctree to ${modulePath}`) + } + + | None => Error(`Failed to get key for ${modulePath}`) + } + | None => Error(`Failed to get API Data for version ${version} and module ${moduleName}`) + } +} + +let getStaticPropsByVersion = async (ctx: {"params": params, "version": string}) => { + let params = ctx["params"] + let version = ctx["version"] + + let slug = params.slug + + let result = processStaticProps(~slug, ~version) + + {"props": result} +} + +let getStaticPathsByVersion = async (~version: string) => { + open Node + + let pathDir = Path.join([Data.dir, version]) + + let slugs = + pathDir + ->Fs.readdirSync + ->Array.reduce([], (acc, file) => { + switch file == "toc_tree.json" { + | true => acc + | false => + let paths = switch Path.join2(pathDir, file) + ->Fs.readFileSync + ->JSON.parseExn { + | Object(dict) => + dict + ->Dict.keysToArray + ->Array.map(modPath => modPath->String.split("/")) + | _ => acc + } + Array.concat(acc, paths) + } + }) + + let paths = slugs->Array.map(slug => + { + "params": { + "slug": slug, + }, + } + ) + + {"paths": paths, "fallback": false} +} diff --git a/src/ApiDocs.resi b/src/ApiDocs.resi new file mode 100644 index 000000000..7b9092be6 --- /dev/null +++ b/src/ApiDocs.resi @@ -0,0 +1,15 @@ +type params = {slug: array} +type props + +let default: props => React.element + +let getStaticPropsByVersion: {"params": params, "version": string} => promise<{"props": props}> + +let getStaticPathsByVersion: ( + ~version: string, +) => promise<{ + "fallback": bool, + "paths": array<{ + "params": {"slug": array}, + }>, +}> diff --git a/src/Blog.mjs b/src/Blog.mjs deleted file mode 100644 index e2ee30e64..000000000 --- a/src/Blog.mjs +++ /dev/null @@ -1,306 +0,0 @@ -// Generated by ReScript, PLEASE EDIT WITH CARE - -import * as Mdx from "./common/Mdx.mjs"; -import * as Meta from "./components/Meta.mjs"; -import * as Next from "./bindings/Next.mjs"; -import * as Util from "./common/Util.mjs"; -import * as Curry from "rescript/lib/es6/curry.js"; -import * as React from "react"; -import * as Button from "./components/Button.mjs"; -import * as Footer from "./components/Footer.mjs"; -import * as BlogApi from "./common/BlogApi.mjs"; -import * as DateStr from "./common/DateStr.mjs"; -import * as Markdown from "./components/Markdown.mjs"; -import * as Belt_Array from "rescript/lib/es6/belt_Array.js"; -import * as Navigation from "./components/Navigation.mjs"; -import * as Caml_option from "rescript/lib/es6/caml_option.js"; -import * as BlogFrontmatter from "./common/BlogFrontmatter.mjs"; - -var defaultPreviewImg = "/static/Art-3-rescript-launch.jpg"; - -var middleDotSpacer = " " + (String.fromCharCode(183) + " "); - -function Blog$Badge(Props) { - var badge = Props.badge; - var bgColor = badge !== 1 ? "bg-turtle" : "bg-orange"; - var text = BlogFrontmatter.Badge.toString(badge); - return React.createElement("div", { - className: bgColor + " flex items-center h-6 font-medium tracking-tight text-gray-80-tr text-14 px-2 rounded-sm" - }, React.createElement("div", undefined, React.createElement("img", { - className: "h-3 block mr-1", - src: "/static/star.svg" - })), React.createElement("div", undefined, text)); -} - -function Blog$CategorySelector(Props) { - var selected = Props.selected; - var onSelected = Props.onSelected; - var tabs = [ - /* All */0, - /* Archived */1 - ]; - return React.createElement("div", { - className: "text-16 w-full flex items-center justify-between text-gray-60" - }, Belt_Array.map(tabs, (function (tab) { - var onClick = function (evt) { - evt.preventDefault(); - return Curry._1(onSelected, tab); - }; - var isActive = selected === tab; - var text = tab ? "Archived" : "All"; - return React.createElement("div", { - key: text, - className: ( - isActive ? "bg-gray-20 text-gray-80 rounded py-1" : "hover:cursor-pointer bg-white hover:text-gray-80" - ) + " px-4 inline-block", - onClick: onClick - }, text); - }))); -} - -function Blog$BlogCard(Props) { - var previewImg = Props.previewImg; - var titleOpt = Props.title; - var category = Props.category; - var badge = Props.badge; - var date = Props.date; - var slug = Props.slug; - var title = titleOpt !== undefined ? titleOpt : "Unknown Title"; - var className = "absolute top-0 h-full w-full object-cover"; - return React.createElement("section", { - className: "h-full" - }, React.createElement("div", { - className: "relative" - }, badge !== undefined ? React.createElement("div", { - className: "absolute z-10 bottom-0 mb-4 -ml-2" - }, React.createElement(Blog$Badge, { - badge: badge - })) : null, React.createElement(Next.Link.make, { - href: "/blog/[slug]", - as: "/blog/" + slug, - children: React.createElement("a", { - className: "relative hl-title block mb-4 pt-9/16" - }, previewImg !== undefined ? React.createElement("img", { - className: className, - src: previewImg - }) : React.createElement("img", { - className: className, - src: defaultPreviewImg - })) - })), React.createElement("div", { - className: "px-2" - }, React.createElement(Next.Link.make, { - href: "/blog/[slug]", - as: "/blog/" + slug, - children: React.createElement("a", undefined, React.createElement("h2", { - className: "hl-4" - }, title)) - }), React.createElement("div", { - className: "captions text-gray-40 pt-1" - }, category !== undefined ? React.createElement(React.Fragment, undefined, category, " · ") : null, Util.$$Date.toDayMonthYear(date)))); -} - -function Blog$FeatureCard(Props) { - var previewImg = Props.previewImg; - var titleOpt = Props.title; - var author = Props.author; - var badge = Props.badge; - var date = Props.date; - var category = Props.category; - var firstParagraphOpt = Props.firstParagraph; - var slug = Props.slug; - var title = titleOpt !== undefined ? titleOpt : "Unknown Title"; - var firstParagraph = firstParagraphOpt !== undefined ? firstParagraphOpt : ""; - var authorImg = React.createElement("img", { - className: "h-full w-full rounded-full", - src: author.imgUrl - }); - var className = "absolute top-0 h-full w-full object-cover"; - return React.createElement("section", { - className: "flex sm:px-4 md:px-8 lg:px-0 flex-col justify-end lg:flex-row sm:items-center h-full" - }, React.createElement("div", { - className: "w-full h-full sm:self-start md:self-auto", - style: { - maxHeight: "25.4375rem" - } - }, React.createElement(Next.Link.make, { - href: "/blog/[slug]", - as: "/blog/" + slug, - children: React.createElement("a", { - className: "relative block pt-2/3" - }, badge !== undefined ? React.createElement("div", { - className: "absolute z-10 top-0 mt-10 ml-4 lg:-ml-4" - }, React.createElement(Blog$Badge, { - badge: badge - })) : null, previewImg !== undefined ? React.createElement("img", { - className: className, - src: previewImg - }) : React.createElement("img", { - className: className, - src: defaultPreviewImg - })) - })), React.createElement("div", { - className: "relative px-4 lg:self-auto sm:pt-12 md:px-20 sm:self-start md:-mt-20 mt-4 bg-white lg:w-full lg:pt-0 lg:mt-0 lg:px-0 lg:ml-12" - }, React.createElement("div", { - className: "max-w-400 " - }, React.createElement("h2", { - className: "hl-1" - }, title), React.createElement("div", { - className: "mb-6" - }, React.createElement("div", { - className: "flex items-center body-sm text-gray-40 mt-2 mb-5" - }, React.createElement("div", { - className: "inline-block w-4 h-4 mr-2" - }, authorImg), React.createElement("div", undefined, React.createElement("a", { - className: "hover:text-gray-60", - href: "https://twitter.com/" + author.twitter, - rel: "noopener noreferrer" - }, author.fullname), category !== undefined ? React.createElement(React.Fragment, undefined, middleDotSpacer, category, middleDotSpacer) : middleDotSpacer, Util.$$Date.toDayMonthYear(date))), React.createElement("p", { - className: "body-md text-gray-70" - }, firstParagraph))), React.createElement(Next.Link.make, { - href: "/blog/[slug]", - as: "/blog/" + slug, - children: React.createElement("a", undefined, React.createElement(Button.make, { - children: "Read Article" - })) - }))); -} - -function $$default(props) { - var posts = props.posts; - var match = React.useState(function () { - return /* All */0; - }); - var setSelection = match[1]; - var currentSelection = match[0]; - var content; - if (posts.length === 0) { - content = React.createElement("div", { - className: "mt-8" - }, React.createElement(Markdown.H1.make, { - children: "Blog not yet available" - }), React.createElement(Markdown.Warn.make, { - children: "This blog is currently in the works." - })); - } else { - var filtered = currentSelection ? props.archived : posts; - var match$1 = filtered.length; - var result; - if (match$1 !== 0) { - var first = Belt_Array.getExn(filtered, 0); - var rest = filtered.slice(1); - var tmp = { - title: first.frontmatter.title, - author: first.frontmatter.author, - date: DateStr.toDate(first.frontmatter.date), - slug: BlogApi.blogPathToSlug(first.path) - }; - var tmp$1 = Caml_option.null_to_opt(first.frontmatter.previewImg); - if (tmp$1 !== undefined) { - tmp.previewImg = tmp$1; - } - var tmp$2 = Caml_option.null_to_opt(first.frontmatter.badge); - if (tmp$2 !== undefined) { - tmp.badge = Caml_option.valFromOption(tmp$2); - } - var tmp$3 = Caml_option.null_to_opt(first.frontmatter.description); - if (tmp$3 !== undefined) { - tmp.firstParagraph = tmp$3; - } - var featureBox = React.createElement("div", { - className: "w-full mb-24 lg:px-8 xl:px-0" - }, React.createElement(Blog$FeatureCard, tmp)); - var postsBox = rest.length !== 0 ? React.createElement("div", { - className: "px-4 md:px-8 xl:px-0 grid grid-cols-1 xs:grid-cols-2 md:grid-cols-3 gap-20 gap-y-12 md:gap-y-24 w-full" - }, rest.map(function (post) { - var badge = post.frontmatter.badge; - var tmp = { - title: post.frontmatter.title, - author: post.frontmatter.author, - date: DateStr.toDate(post.frontmatter.date), - slug: BlogApi.blogPathToSlug(post.path), - key: post.path - }; - var tmp$1 = Caml_option.null_to_opt(post.frontmatter.previewImg); - if (tmp$1 !== undefined) { - tmp.previewImg = tmp$1; - } - var tmp$2 = badge === null ? undefined : Caml_option.some(badge); - if (tmp$2 !== undefined) { - tmp.badge = Caml_option.valFromOption(tmp$2); - } - return React.createElement(Blog$BlogCard, tmp); - })) : null; - result = React.createElement(React.Fragment, undefined, featureBox, postsBox); - } else { - result = React.createElement("div", undefined, "No posts for this category available..."); - } - content = React.createElement(React.Fragment, undefined, React.createElement("div", { - className: "hidden sm:flex justify-center " - }, React.createElement("div", { - className: "my-16 w-full", - style: { - maxWidth: "12rem" - } - }, React.createElement(Blog$CategorySelector, { - selected: currentSelection, - onSelected: (function (selection) { - return Curry._1(setSelection, (function (param) { - return selection; - })); - }) - }))), result); - } - var overlayState = React.useState(function () { - return false; - }); - return React.createElement(React.Fragment, undefined, React.createElement(Meta.make, { - siteName: "ReScript Blog", - description: "News, Announcements, Release Notes and more", - title: "Blog | ReScript Documentation" - }), React.createElement("div", { - className: "mt-16 pt-2" - }, React.createElement("div", { - className: "text-gray-80 text-18" - }, React.createElement(Navigation.make, { - overlayState: overlayState - }), React.createElement("div", { - className: "flex justify-center overflow-hidden" - }, React.createElement("main", { - className: "min-w-320 lg:align-center w-full lg:px-0 max-w-1280 pb-48" - }, React.createElement(Mdx.Provider.make, { - components: Markdown.$$default, - children: React.createElement("div", { - className: "flex justify-center" - }, React.createElement("div", { - className: "w-full", - style: { - maxWidth: "66.625rem" - } - }, content)) - }))), React.createElement(Footer.make, {})))); -} - -function getStaticProps(_ctx) { - var match = Belt_Array.partition(BlogApi.getAllPosts(undefined), (function (data) { - return data.archived; - })); - var props_posts = match[1]; - var props_archived = match[0]; - var props = { - posts: props_posts, - archived: props_archived - }; - return Promise.resolve({ - props: props - }); -} - -export { - defaultPreviewImg , - $$default , - $$default as default, - getStaticProps , - -} -/* middleDotSpacer Not a pure module */ diff --git a/src/Blog.res b/src/Blog.res index 08bb5541a..262d684ef 100644 --- a/src/Blog.res +++ b/src/Blog.res @@ -18,13 +18,13 @@ module Link = Next.Link let defaultPreviewImg = "/static/Art-3-rescript-launch.jpg" // For encoding reasons, see https://shripadk.github.io/react/docs/jsx-gotchas.html -let middleDotSpacer = " " ++ (Js.String.fromCharCode(183) ++ " ") +let middleDotSpacer = " " ++ (String.fromCharCode(183) ++ " ") module Badge = { @react.component let make = (~badge: BlogFrontmatter.Badge.t) => { let bgColor = switch badge { - | Preview | Roadmap | Release => "bg-turtle" + | Preview | Roadmap | Release | Community => "bg-turtle" | Testing => "bg-orange" } @@ -32,49 +32,41 @@ module Badge = {
-
+
+ +
{React.string(text)}
} } -module CategorySelector = { - type selection = - | All - | Archived - let renderTab = (~text: string, ~isActive: bool, ~onClick) => { - let active = "bg-gray-20 text-gray-80 rounded py-1" -
- {React.string(text)} -
- } +type category = + | /** Actually only unarchived */ All + | Archived +module CategorySelector = { @react.component - let make = (~selected: selection, ~onSelected: selection => unit) => { + let make = (~selected: category) => { let tabs = [All, Archived]
- {Belt.Array.map(tabs, tab => { - let onClick = evt => { - evt->ReactEvent.Mouse.preventDefault - onSelected(tab) - } - + {tabs + ->Array.map(tab => { // Deep comparison here! let isActive = selected == tab - - let text = switch tab { - | All => "All" - | Archived => "Archived" + let text = (tab :> string) + let href = switch tab { + | All => "/blog" + | Archived => "/blog/archived" } - - renderTab(~isActive, ~text, ~onClick) - })->React.array} + let className = + switch isActive { + | true => "bg-gray-20 text-gray-80 rounded py-1" + | false => "hover:cursor-pointer bg-white hover:text-gray-80" + } ++ " px-4 inline-block" + {React.string(text)} + }) + ->React.array}
} } @@ -87,34 +79,39 @@ module BlogCard = { ~author as _: BlogFrontmatter.author, ~category: option=?, ~badge: option=?, - ~date: Js.Date.t, + ~date: Date.t, ~slug: string, ) =>
- -

{React.string(title)}

+ +

{React.string(title)}

{switch category { - | Some(category) => <> {React.string(category)} {React.string(j` · `)} + | Some(category) => + <> + {React.string(category)} + {React.string(` · `)} + | None => React.null }} {React.string(date->Util.Date.toDayMonthYear)} @@ -130,13 +127,18 @@ module FeatureCard = { ~title: string="Unknown Title", ~author: BlogFrontmatter.author, ~badge: option=?, - ~date: Js.Date.t, + ~date: Date.t, ~category: option=?, ~firstParagraph: string="", ~slug: string, ) => { - let authorImg = - + let authorImg = switch author.imgUrl { + | "" => React.null + | imgUrl => +
+ +
+ }
{React.string(title)}
-
authorImg
+ authorImg
"https://x.com/" ++ handle + | Bluesky(handle) => "https://bsky.app/profile/" ++ handle + }} rel="noopener noreferrer"> {React.string(author.fullname)} {switch category { - | Some(category) => <> + | Some(category) => + <> {React.string(middleDotSpacer)} {React.string(category)} {React.string(middleDotSpacer)} @@ -191,8 +197,8 @@ module FeatureCard = {

{React.string(firstParagraph)}

- - + +
@@ -201,42 +207,32 @@ module FeatureCard = { type params = {slug: string} -type props = { - posts: array, - archived: array, -} +type props = {posts: array, category: category} let default = (props: props): React.element => { - let {posts, archived} = props - - let (currentSelection, setSelection) = React.useState(() => CategorySelector.All) + let {posts, category} = props - let content = if Belt.Array.length(posts) === 0 { + let content = if Array.length(posts) === 0 { /*
{React.string("Currently no posts available")}
; */
{React.string("Blog not yet available")} {React.string("This blog is currently in the works.")}
} else { - let filtered = switch currentSelection { - | All => posts - | Archived => archived - } - - let result = switch Belt.Array.length(filtered) { + let result = switch Array.length(posts) { | 0 =>
{React.string("No posts for this category available...")}
| _ => - let first = Belt.Array.getExn(filtered, 0) - let rest = Js.Array2.sliceFrom(filtered, 1) + let first = Belt.Array.getExn(posts, 0) + let rest = Array.sliceToEnd(posts, ~start=1) let featureBox =
Js.Null.toOption} + previewImg=?{first.frontmatter.previewImg->Null.toOption} title=first.frontmatter.title - badge=?{first.frontmatter.badge->Js.Null.toOption} + badge=?{first.frontmatter.badge->Null.toOption} author=first.frontmatter.author - firstParagraph=?{first.frontmatter.description->Js.Null.toOption} + firstParagraph=?{first.frontmatter.description->Null.toOption} date={first.frontmatter.date->DateStr.toDate} slug={BlogApi.blogPathToSlug(first.path)} /> @@ -247,12 +243,12 @@ let default = (props: props): React.element => { | rest =>
- {Js.Array2.map(rest, post => { - let badge = post.frontmatter.badge->Js.Null.toOption + {Array.map(rest, post => { + let badge = post.frontmatter.badge->Null.toOption Js.Null.toOption} + previewImg=?{post.frontmatter.previewImg->Null.toOption} title=post.frontmatter.title author=post.frontmatter.author ?badge @@ -263,22 +259,23 @@ let default = (props: props): React.element => {
} - <> featureBox postsBox + <> + featureBox + postsBox + } <>
- setSelection(_ => selection)} selected=currentSelection - /> +
result } - let overlayState = React.useState(() => false) + let (isOverlayOpen, setOverlayOpen) = React.useState(() => false) let title = "Blog | ReScript Documentation" <> @@ -287,16 +284,16 @@ let default = (props: props): React.element => { />
- +
- +
content
-
+