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/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..a76fa3ef7 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: ryyppy +custom: https://rescript-association.org/donate 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 387c112ac..58df60cb2 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -2,14 +2,16 @@ name: Test on: [pull_request] jobs: build: - name: Test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: '12.x' - registry-url: 'https://registry.npmjs.org/' - - run: yarn install - - run: yarn run bs:build - - run: yarn test + 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 de05ce5b4..d364e12a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -out - .DS_Store *.swp *.swo @@ -11,7 +9,14 @@ node_modules/ .next/ index_data/*.json -yarn-error.log +# 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/ .bsb.lock .merlin @@ -20,5 +25,10 @@ lib/ .vercel -# for IDE tooling tests -bin.exe +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/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..b84fef4c6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog + + +This changelog documents significant changes that caused a new version in the manual, or other related resources. + +We don't create a version fork for every minor release, and try to make docs "append only" as much as possible. Usually when we introduce a new feature we add a `since 9.0` annotation to a specific section, and be done with it. + +For bigger substantial changes (e.g. when we removed the `bs.` prefix, or introduced the `rescript` package in 9.1.0), we fork the version to have a cleaner look on the actual state. + +Here are the notes on major changes we did, and how the version corresponds to specific version ranges. + +## Manual + +### latest + +- 9.1 related + - + +### v9.0 (v8.3 - v9.0) + +**Related PR:** (https://github.com/rescript-association/rescript-lang.org/pull/293) + +- 8.3 related + - Removes `bs.` prefixes from attributes + - Allow more character sets in module names +- 8.4 related + - Pinned deps feature page + - New 109 warning toplevel expression is expected to have type unit It is turned on as warn-error by default. This warning is introduced to avoid partial application errors in a curried language +- 9.0 related + - Lightweight polyvar syntax + - New external-stdlib docs + - `when` -> `if` transformation + +### v8.0.0 (v6 - v8.3) + +- Docs with Reason / OCaml syntax before the new syntax + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5e4b59477..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,15 +16,15 @@ 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. **Always make sure to get feedback from the core maintainers before starting any work** -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! +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). @@ -84,6 +84,6 @@ Most of the stuff we want to build can be built as components with Tailwind clas - Clone the project and follow the [README](README.md) instructions - Always run the page locally and verify your changes (especially when working on code examples) -- When writing markdown with code examples, always run `yarn test` to prevent broken code +- When writing markdown with code examples, always run `npm test` to prevent broken code - Feel free to open `Draft PRs` when you are working on bigger features (good for visibility and asking for feedback) - Improve code based on last feedback until the code is ready to be merged diff --git a/LICENSE b/LICENSE index 1d88c548a..f051ed2ba 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Reason Association +Copyright (c) 2019 ReScript Association Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal 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 5e72e1e3c..48b2e84bd 100644 --- a/README.md +++ b/README.md @@ -6,37 +6,41 @@ 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@20` or higher +- `npm@10` or higher + ## Setup ```sh # For first time clone / build (install dependencies) -yarn +npm i # Initial build -yarn bs:build +npx rescript -# Build the index data -yarn run update-index +# Build the index data. Only needed for initial clone (or content H2 changes) +npm run update-index # In a new tab -yarn dev +npm run dev -# then open localhost:3000 +open localhost:3000 ``` -In case you want to run BuckleScript in watchmode: +In case you want to run ReScript in watchmode: ```sh -yarn run bs:start +npx rescript -w ``` ## Build Index Data @@ -46,7 +50,7 @@ search terms we need for searching inside the `Belt` docs). You can create your index by running following command: ```sh -yarn run update-index +npm run update-index ``` All the index data is stored in `index_data`, but will not be tracked by git. @@ -76,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 @@ -109,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 @@ -143,14 +148,16 @@ NODE_ENV=production npx postcss styles/main.css -o test.css ## Writing Blog Posts -In case you are a blog author, please refer to our [guide on writing blog -posts](https://rescript-lang.org/blogpost-guide). +In case you are a blog author, please refer to our [guide on writing blog posts](https://rescript-lang.org/blogpost-guide). + +## How to Add Your Company Logo to Our Front Page -**Quick-takeaways:** +In case your company is a user of ReScript and wants to be displayed on our front page ("Trusted by our users" section), do the following: -- Blogposts are located in `_blogposts` -- Author metadata is located in `data/blog_authors.json` -- Make sure to follow the file naming rules +- Get your logo as a black / white `.svg` version and use `#979AAD` as a fill color (check out the existing logos on our front page). +- Put your logo into the [`public/static/lp`](./public/static/lp) folder; the file should be named after your company. +- Open [src/common/OurUsers.res](./src/common/OurUsers.res) and add your info +- Commit, push, and open a PR. ### Contributing diff --git a/_blogposts/2020-08-10-bucklescript-is-rebranding.mdx b/_blogposts/2020-08-10-bucklescript-is-rebranding.mdx new file mode 100644 index 000000000..168c3abf5 --- /dev/null +++ b/_blogposts/2020-08-10-bucklescript-is-rebranding.mdx @@ -0,0 +1,110 @@ +--- +author: rescript-team +date: "2020-08-10" +badge: roadmap +title: "BuckleScript & Reason Rebranding" +description: A new unified experience for the platform +--- + +We're pleased to announce that BuckleScript is getting a brand new name: **ReScript**. + +## History & Summary + +- **OCaml** is a typed FP language compiling to bytecode and native code. +- **Js_of_ocaml** is based on OCaml and compiles to JavaScript for OCaml users. +- **BuckleScript** is a fork of OCaml that also outputs JavaScript, optimized (features, JS interoperability, output, build tools) for JS developers rather than OCaml developers. +- **Reason** is an alternative, JS-looking syntax layer over OCaml, plus extra tools. Reason used 1. BuckleScript to produce JavaScript output and 2. OCaml to produce native output. Most of the community focused on the former usage. +- Reason and BuckleScript shared most teammates, who wanted to double down on the JS use-case. +- **ReScript**, thus born, is the new branding for BuckleScript that reimplements or cleans up Reason's syntax, tools, ecosystem & docs into a vertically integrated experience. +- Reason project will continue serving its purpose of a syntax layer for native OCaml. Some folks might use Reason with Js_of_ocaml to output JS code. + +## Community Situation + +BuckleScript started with the idea that **JavaScript programmers deserved a great typed language with a fast and lean toolchain**. This idea took root and, over the years, we've gradually accomplished feats such as: +- a state of the art compiled JavaScript output that rivals hand-written JS, +- a fast & reliable toolchain much needed in front-end and Node development, +- various JS interop features that spawned an ecosystem of well typed libraries, +- a production-ready standard library, +- [and recently](/blog/bucklescript-8-1-new-syntax), a fresh syntax made by a major contributor of Reason's old syntax. + +These developments have attracted many people into our community. But one bigger challenge persisted: newcomers dropped out at the sheer amount of extra incongruent tools and learning overhead from having to understand OCaml concepts, Reason concepts, and BuckleScript's concepts. Take, for example, what's required to make a React app using BuckleScript: +- Knowledge of React. +- Knowledge of JS. +- Knowledge of BuckleScript's specific bindings to React (that we've tried hard to keep to a minimum). +- Knowledge of OCaml idioms, which leaked through BuckleScript. +- Knowledge of BuckleScript's JS interop and the build system. +- Knowledge of the Reason syntax. +- Avoiding the distractions of OCaml and Reason's unrelated, native-oriented build tools, package manager, etc. + +Ironically, the more documentation we pile up, the more mental overhead newcomers suffered. During the meetups, we've frequently seen folks' enthusiasm crushed at the sheer prospect of making a web app while keeping 5+ tabs' worth of documentations open. This was discouraging for everyone. + +The adoption barrier is real, and it's about time we finally solve it. + +## The Rebranding + +Today, we'll start to truly unify the various BuckleScript-related projects under the ReScript umbrella. This includes: +- The compiler, build system and the new syntax unified under a single installation. +- Doubling down on editor tooling for ReScript usage. +- A single documentation site (this one), which unifies all the docs and greatly trims down on redundant and stale info. +- Streamlined communication through said website, a [forum](https://forum.rescript-lang.org) and a new [Twitter](http://twitter.com/rescriptlang). +- The renaming of various tools, always in a backward-compatible way. + +In short, all JS-related concepts previously under disparate Reason and BuckleScript ecosystems are now called ReScript. Previously, due to the messy situation, most real-world BuckleScript adoption came from a few heroic community members suffering through the technical and social risk of spreading our tech to their coworkers. From today onward, you can simply tell to your coworkers: **"this is ReScript"**, and point to them a clear starting point. + +It's worth emphasizing that while this rebranding seems disruptive, it's mostly a bunch of name changes for the same tech. The Q&A below addresses some of the worries on existing code. + +## Q & A + +**What's that recently released BuckleScript syntax called?** + +There's no dedicated name for the syntax anymore. It's simply called the ReScript syntax. The extension is `.res` and `.resi`. Existing tools operating on `.ml`, `.mli`, `.re` and `.rei` continue working. + +**Will there be a migration script to gradually convert our code to the new syntax?** + +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?** + +No. The new syntax & tools sit alongside the existing code. We **won't** remove OCaml and Reason support from ReScript for a long time. + +**What's the editor tooling story?** + +Much more streamlined now! See our [Editor Plugins](/docs/manual/latest/editor-plugins) page. + +Reason-language-server will continue working as-is for existing usage. We as first party don't work on ocaml's language server. + +**Will I be able to continue writing OCaml/Reason and compile to JS using BuckleScript in the future?** + +It follows from our previous answer that yes, you will still be able to. Though community-wise, we're encouraging the new syntax and tools. + +The compiler will also continue to acquire upstream OCaml features when relevant. + +**Will the new syntax prompt the move to a non-OCaml AST?** + +No, since that'd break existing ppxes (e.g. internationalization, graphql). If we feel the need to adopt a new AST in the future, it'll again be purely additive. + +**Will we have 3 syntaxes to worry about?** + +The plan is to emphasize the new syntax and focus our tooling around it. It'll be confusing to temporarily have different syntaxes in the same codebase, but that's the cost of a proper migration support. We hope this is transient; it's darkest before the dawn. + +**How do we address the fragmentation of the community by the new syntax?** + +Folks who have been in the community for a while know that there have always been opposing philosophies regarding newcomer funneling, tooling emphasis, library preferences, etc., due to differing cultures. + +Rather, it's more accurate to say a few pieces of awkwardly shared infrastructure held opposing forces together. This is true when Reason spun off from OCaml's engineering, and true when BuckleScript entered the picture. + +We can't realistically always release new features that are a compromise of various philosophies, but that's exactly what we've been trying to do for the past few years. BuckleScript's reimplementation of the Reason syntax is a departure from that (much tighter integration with the compiler, more JavaScript/TypeScript-friendly philosophy, fewer compromises, faster, less buggy). We believe that its quality speaks for said departure. + +The ReScript community will comprise of the majority of the old Reason community, most of which focused on the BuckleScript part already. The remainder of the Reason community still exists and will now focus on their existing use-case better. + +**The new changes make me worried about the future support I'm going to receive from the team.** + +_Retrospective edit, in 2021_: suffice to say that we've been doing well =). + +## Conclusion + +Thank you for your support and keep shipping. A brighter future awaits us! + +Stay safe, + +The ReScript team (Hongbo, Cheng, Cristiano, Maxim, Patrick, Ricky). diff --git a/_blogposts/2020-08-28-new-rescript-logo.mdx b/_blogposts/2020-08-28-new-rescript-logo.mdx index 12b084e51..0d40e3903 100644 --- a/_blogposts/2020-08-28-new-rescript-logo.mdx +++ b/_blogposts/2020-08-28-new-rescript-logo.mdx @@ -1,9 +1,8 @@ --- author: made_by_betty -date: "2020-08-27" +date: "2020-08-27" title: "A New Logo for ReScript" -previewImg: https://res.cloudinary.com/dmm9n7v9f/image/upload/v1598616442/Reason%20Association/rescript-lang.org/Art-3-rescript-launch_ovoibg.jpg -articleImg: https://res.cloudinary.com/dmm9n7v9f/image/upload/v1598616442/Reason%20Association/rescript-lang.org/ReScript-3_by4q2u.jpg +articleImg: /static/blog/rescript-launch/ReScript-3.jpg description: | Today, our resident designer Bettina is unveiling to us the fresh new ReScript branding we've been long waiting for. We hope you're as excited about the result as us! --- @@ -17,7 +16,7 @@ ReScript is the evolution and fusion of Reason and BuckleScript. As we're a desi Here it is! - + The old Reason and BuckleScript logo had many limitations: @@ -28,14 +27,14 @@ The old Reason and BuckleScript logo had many limitations: The new logo addresses all these and more: - + ## Creating Meaning Through Simple Shapes A minimal logo design is achieved by putting aside all distractions while focusing on legibility, meaning and small details like clear lines, interesting perspective and depth. - + **The letter "r"**, composed of two shapes, is inspired by Albers, a german-born American Bauhaus artist and typographer. An object should be simple, beautiful, functional and accessible for everyone - a statement which applies to ReScript's principles perfectly. @@ -56,6 +55,6 @@ We hope you enjoyed our new design article. In the future, we're hoping to inter ## About the Designer / Creator -[Bettina Steinbrecher](https://bettystein.com/) is a freelance designer and brand consultant, specialising in digital products and brands, based in Vienna. Previously she has been a Digital Designer and later on Head of Brand Design at [adidas runtastic](https://runtastic.com/), where she led major design efforts in the visual brand developing processes before and during the Runtastic / adidas acquisition. She is now working as a freelance designer. +[Bettina Steinbrecher](https://bettystein.com/) is a freelance designer and brand consultant, specialising in digital products and brands, based in Vienna. Previously she has been a Digital Designer and later on Head of Brand Design at [Adidas Runtastic](https://runtastic.com/), where she led major design efforts in the visual brand developing processes before and during the Runtastic / Adidas acquisition. She is now working as a freelance designer. -She has been involved in our design processes since the inception of the [Reason Association](https://reason-association.org) in 2018, and was responsible for all the user-facing websites such as `reasonml.org` (now `rescript-lang.org`), reason-association.org. She also created the new logo of the [OCaml Software Foundation](https://ocaml-sf.org/). +She has been involved in our design processes since the inception of the [ReScript Association](https://rescript-association.org) in 2018, and was responsible for all the user-facing websites such as `reasonml.org` (now `rescript-lang.org`) and rescript-association.org. She also created the new logo of the [OCaml Software Foundation](https://ocaml-sf.org/). diff --git a/_blogposts/2020-09-25-release-8-3-2.mdx b/_blogposts/2020-09-25-release-8-3-2.mdx index 0d708808a..a0ba49a92 100644 --- a/_blogposts/2020-09-25-release-8-3-2.mdx +++ b/_blogposts/2020-09-25-release-8-3-2.mdx @@ -1,15 +1,14 @@ --- author: hongbo -date: "2020-09-26" +date: "2020-09-26" previewImg: -category: compiler title: What's new in ReScript 8.3 (Part 2) description: | --- ## Introduction -ReScript is a soundly typed language with an optimizing compiler focused on the JS platform. +ReScript is a soundly typed language with an optimizing compiler focused on the JS platform. It's focused on type safety, performance and JS interop. It used to be called BuckleScript. [ReScript@8.3](https://www.npmjs.com/package/bs-platform/v/8.3.0) is now available for testing, you can try it via @@ -18,47 +17,47 @@ It's focused on type safety, performance and JS interop. It used to be called Bu npm i bs-platform@8.3.1 ``` -Following the [previous post](/blog/release-8-3-pt1), in this post we will go through +Following the [previous post](/blog/release-8-3), in this post we will go through the enhancement over the build system. ## Performance enhancement -The underlying build engine for `bsb` is [ninja](https://ninja-build.org/), it is famous for +The underlying build engine for `bsb` is [ninja](https://ninja-build.org/), it is famous for [being fast](https://www.aosabook.org/en/posa/ninja.html) to build large C++ repos. -In the last releases, we did lots of work for vertical integration into the bsb build chain. +In the last releases, we did lots of work for vertical integration into the bsb build chain. For example, we replaced the dynamic dependency parser with a minimal specialized one for bsb. we also removed the static dependencies parser which is only used for parsing C++ compiler output. Thanks to various other low-level improvements, the final outcome is quite impressive. For example, The binary size for [Mac platform](https://github.com/ninja-build/ninja/releases/tag/v1.10.1) is 270 KB -while our vendored version is only 136KB. This is a non-trivial gain given that ninja is minimalist and already +while our vendored version is only 136KB. This is a non-trivial gain given that ninja is minimalist and already optimized by top-level C++ experts. -Note such vertical integration not only brings better performance, smaller sizes, it also brings new features +Note such vertical integration not only brings better performance, smaller sizes, it also brings new features ## Build system enhancements for editor diagnostics When people are coding in their favorite editors, they expect to see syntax and type errors in real-time. There are multiple ways to achieve this. The most reliable way is to always invoke the build system whenever the user saves a file. Due to not having an in-memory cache, our build system is very reliable. However we didn't yet optimize the build system for live feedback in editors. -what syntax errors, type checking errors do they have when editing? There are multiple ways to achieve this, the most easy -and reliable way is to always invoke the build system whenever the user saves the files, since it's the same build system +what syntax errors, type checking errors do they have when editing? There are multiple ways to achieve this, the most easy +and reliable way is to always invoke the build system whenever the user saves the files, since it's the same build system without any in-memory cache, the reliability is very high, however, there's several challenges to use the build system output as editor diagnostics. ### The build system/compiler has to be fast to deliver real-time feedback Our build system is fast enough to deliver feedback for reasonable sized projects in less than 100ms. -thanks to our previous [hard work](/blog/scalable). We continue improving +thanks to our previous [hard work](/blog/scalable). We continue improving Pushing the limits of performance in the build system allows us to provide real-time feedback in editors. ### The warnings for each file should not be flushed during a rebuild For a typical file based build system, if the file A is compiled successfully with some warnings, the rebuild will not build A anymore. -This is problematic if we use the build system output for editor diagnostics. Since the second build will not capture those warnings, we +This is problematic if we use the build system output for editor diagnostics. Since the second build will not capture those warnings, we could use some caching mechanism to cache previous build output. But… stateful systems are not reliable and come with a whole range of different problems. -To solve such a challenging problem, we did some innovations to co-ordinate the compiler and build system. When the file A is compiled with warnings, +To solve such a challenging problem, we did some innovations to co-ordinate the compiler and build system. When the file A is compiled with warnings, the compiler will produce some marks to the build system, the build system will keep building but such marks are encoded in the build rules so that the second build will do the rebuild. @@ -80,15 +79,15 @@ We write the output to `.compiler.log` per each build, allowing clients to read ## A better algorithm for removing stale outputs -Whenever we rename a file, e.g. `a.res` to `b.res`, will lead to the output of `a.res` being stale. Thanks to the deeper integration of the build system and compiler, +Whenever we rename a file, e.g. `a.res` to `b.res`, will lead to the output of `a.res` being stale. Thanks to the deeper integration of the build system and compiler, we employ a more advanced strategy to remove stale outputs in this release. Pruning stale outputs is done in the beginning of each build. There are two ways of removing staled artifacts, the second one is introduced in this release: - 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 -the current build set, it is considered stale artifacts. If it is `cmt` file, it would trigger some hooks of `genType`, notably -cmt-rm. +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 We store previous compilation stats. If a file is in the previous compiler output, but no longer in the output of the new build, it is considered stale and can be removed. diff --git a/_blogposts/2020-09-25-release-8-3.md.raw b/_blogposts/2020-09-25-release-8-3.md.raw deleted file mode 100644 index bf1f5ac23..000000000 --- a/_blogposts/2020-09-25-release-8-3.md.raw +++ /dev/null @@ -1,121 +0,0 @@ ---- -author: hongbo -date: "2020-09-25" -previewImg: -category: compiler -title: What's new in ReScript 8.3 (Part 1) -description: | ---- - -## Introduction - -ReScript is a soundly typed language with an optimizing compiler focused on the JS platform. -It's focused on type safety, performance and JS interop. It used to be called BuckleScript. - -[ReScript@8.3](https://www.npmjs.com/package/bs-platform/v/8.3.0) is now available for testing, you can try it via - -``` -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. - - - -## Lightweight FFI attributes without `bs.` prefix - -In this release, we make the `bs.` prefix optional, this will make the FFI less verbose. - -For example, the old externals for `readFileAsUtf8Sync` used to be written like this - - -```ocaml -external readFileAsUtf8Sync : string -> (_[@bs.as "utf8"]) -> string = "readFileSync" [@@bs.val] [@@bs.module "fs"] -``` - -It can now be simplified as -```ocaml -external readFileAsUtf8Sync : string -> (_[@as "utf8"]) -> string = "readFileSync" -[@@val] [@@module "fs"] -``` - -Note almost all previous attributes with `bs.xx` can be simplified as `xx` -with the exception of the following two that don't have abbreviations: - -- `bs.send.pipe` : this attribute was deprecated in favor of `bs.send`; you can still use the existing one for backward compatibility. - -- `bs.splice` : this attribute was deprecated in favor of `bs.variadic`; you can still use the existing one for -backward compatibility. - - -## default import in Es6 support - -If you use es6 module output, the default bindings will be compiled properly now: - -```ocaml -external input : string -> string = "default" [@@module "hello"] - -let a = input "hello" -``` - -Will now be compiled properly under es6 format as below: - -```js -import Hello from "hello"; -var a = Hello("hello"); -``` - - -## Customized js file extension support - -Now user can pick up their js file extension support per module format: - -```json - "package-specs": [{ - "module": "es6", - "suffix": ".mjs" - },{ - "module": "commonjs", - "suffix": ".cjs" - }], - -``` - -## More flexible filename support - -To have better integration with other [JS infrastructures](https://github.com/rescript-lang/rescript-compiler/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 - - - -## Better type based inference for pattern `let {a,b,c} = value` - -Previously, for code like this: - -```ocaml -module N = struct - type t = { - x : int - } -end - -let f (u : N.t) = - let {x } = u in x + 1 (* type error *) -``` - -You will get a type error - -``` -Error: Unbound record field x -``` - -However, since the compiler already knows the type of `u`, it is capable of looking up the label `x` properly. -In this release, we make the original code style work out of the box without a work-around such as adding a module prefix -like `let {N.x} = ..` - -## Build system enhancement - -A lot of work is put in improving the build system, we will expand on this topic in the next post! - -Happy Hacking! diff --git a/_blogposts/2020-09-25-release-8-3.mdx b/_blogposts/2020-09-25-release-8-3.mdx index 39835b859..5975e6678 100644 --- a/_blogposts/2020-09-25-release-8-3.mdx +++ b/_blogposts/2020-09-25-release-8-3.mdx @@ -1,15 +1,14 @@ --- author: hongbo -date: "2020-09-25" +date: "2020-09-25" previewImg: -category: compiler title: What's new in ReScript 8.3 (Part 1) description: | --- ## Introduction -ReScript is a soundly typed language with an optimizing compiler focused on the JS platform. +ReScript is a soundly typed language with an optimizing compiler focused on the JS platform. It's focused on type safety, performance and JS interop. It used to be called BuckleScript. [ReScript@8.3](https://www.npmjs.com/package/bs-platform/v/8.3.0) is now available for testing, you can try it via @@ -18,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. @@ -46,7 +45,7 @@ external readFileAsUtf8Sync : string -> (_[@bs.as "utf8"]) -> string = "readFile -It can now be simplified as +It can now be simplified as @@ -59,18 +58,18 @@ external readFileAsUtf8Sync: (string, [@as "utf8"] _) => string = "readFileSync"; ``` ```ocaml -external readFileAsUtf8Sync : string -> (_[@as "utf8"]) -> string = "readFileSync" +external readFileAsUtf8Sync : string -> (_[@as "utf8"]) -> string = "readFileSync" [@@val] [@@module "fs"] ``` -Note almost all previous attributes with `bs.xx` can be simplified as `xx` +Note almost all previous attributes with `bs.xx` can be simplified as `xx` with the exception of the following two that don't have abbreviations: - `bs.send.pipe` : this attribute was deprecated in favor of `bs.send`; you can still use the existing one for backward compatibility. -- `bs.splice` : this attribute was deprecated in favor of `bs.variadic`; you can still use the existing one for +- `bs.splice` : this attribute was deprecated in favor of `bs.variadic`; you can still use the existing one for backward compatibility. @@ -118,13 +117,13 @@ Now user can pick up their js file extension support per module format: "module": "commonjs", "suffix": ".cjs" }], - + ``` ## More flexible filename support -To have better integration with other [JS infrastructures](https://github.com/rescript-lang/rescript-compiler/issues/4624), -for example, Next.js/React Native, we allow file names like `404.res`, +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 @@ -156,13 +155,13 @@ let f = (u: N.t) => { }; /* type error */ ``` ```ocaml -module N = struct +module N = struct type t = { - x : int + x : int } end -let f (u : N.t) = +let f (u : N.t) = let {x } = u in x + 1 (* type error *) ``` @@ -174,11 +173,11 @@ You will get a type error Error: Unbound record field x ``` -However, since the compiler already knows the type of `u`, it is capable of looking up the label `x` properly. -In this release, we make the original code style work out of the box without a work-around such as adding a module prefix +However, since the compiler already knows the type of `u`, it is capable of looking up the label `x` properly. +In this release, we make the original code style work out of the box without a work-around such as adding a module prefix like `let {N.x} = ..` -## Build system enhancement +## Build system enhancement A lot of work is put in improving the build system, we will expand on this topic in the next post! 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 e3de515bb..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 @@ -2,15 +2,13 @@ author: maxim date: "2020-11-17" previewImg: -category: syntax badge: roadmap title: "Editor Support, Custom Operators and More" description: | Update on what we're doing around the end of 2020 and early next year. -canonical: https://rescript-lang.org/blog/rescript-the-road-ahead --- -import Video from "src/components/Video" +import Video from "src/components/Video"; ## Upcoming Improvements @@ -30,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-11-26-editor-support-release-1-0.mdx b/_blogposts/2020-11-26-editor-support-release-1-0.mdx index 70428e106..090f101b6 100644 --- a/_blogposts/2020-11-26-editor-support-release-1-0.mdx +++ b/_blogposts/2020-11-26-editor-support-release-1-0.mdx @@ -1,14 +1,12 @@ --- author: rescript-team date: "2020-11-26" -previewImg: https://res.cloudinary.com/dmm9n7v9f/image/upload/v1606399719/Reason%20Association/rescript-lang.org/editor_support_preview_mcgpfo.jpg -articleImg: https://res.cloudinary.com/dmm9n7v9f/image/upload/v1606399722/Reason%20Association/rescript-lang.org/editor_support_article_rnlmxj.jpg -category: syntax +previewImg: /static/blog/editor_support_preview.jpg +articleImg: /static/blog/editor_support_article.jpg badge: release title: "Editor Plugin for VSCode and Vim Officially Released!" description: | Type hints, jump to definition, error diagnostics, and more. -canonical: https://rescript-lang.org/blog/editor-support-release-1-0 --- @@ -34,4 +32,4 @@ We'll keep on iterating on the polish of the plugins, and release better [Sublim Happy thanksgiving! - + diff --git a/_blogposts/2020-12-07-release-8-4.mdx b/_blogposts/2020-12-07-release-8-4.mdx index caef8f3e6..bc7484feb 100644 --- a/_blogposts/2020-12-07-release-8-4.mdx +++ b/_blogposts/2020-12-07-release-8-4.mdx @@ -1,15 +1,16 @@ --- author: hongbo -date: "2020-12-07" +date: "2020-12-07" previewImg: -category: compiler +badge: release title: ReScript 8.4 description: | + bsb improvements --- ## Introduction -ReScript is a soundly typed language with an optimizing compiler focused on the JS platform. +ReScript is a soundly typed language with an optimizing compiler focused on the JS platform. It's focused on type safety, performance and JS interop. It used to be called BuckleScript. [ReScript@8.4](https://www.npmjs.com/package/bs-platform/v/8.4.2) is now available for testing, you can try it via @@ -18,77 +19,54 @@ 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. ### The integrity of `bsb -make-world` -When we introduced `bsb` as a build system around four years ago, we made the assumption that dependencies are immutable, -so that once it's built for the first time, we don't need to rebuild it any more. The integrity of `bsb -make-world` will be broken +When we introduced `bsb` as a build system around four years ago, we made the assumption that dependencies are immutable, +so that once it's built for the first time, we don't need to rebuild it any more. The integrity of `bsb -make-world` will be broken when the assumption does not hold. -In this release, we fix the integrity of `bsb -make-world` which allows user to change the dependencies. -The fix is well implementend that people who don't do such modifications will not pay for it. +In this release, we fix the integrity of `bsb -make-world` which allows user to change the dependencies. +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. +This is one of the highest desired feature request based on the +[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 blackbox, -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. -To make things worse, each package comes with an installation process which is a shell script, so re-installation will make the +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 +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. +To make things worse, each package comes with an installation process which is a shell script, so re-installation will make the package look like freshly built. -In this release, we track the installation in the build graph as well, and we calculate the hash of the installation of dependencies and put it in the -dependent's command line flags for building binary artifacts. Such strategy benefits in such aspects: +In this release, we track the installation in the build graph as well, and we calculate the hash of the installation of dependencies and put it in the +dependent's command line flags for building binary artifacts. Such strategy benefits in such aspects: - The calculation of the hash of installation is almost free since it is just one stat, we don't need track all dependencies' artifacts. -- The introducion of such hashing does not appear in parsing, so that the changes of dependencies will never trigger re-parsing. -- Once the package installation is a nop, the transitive rebuild graph will be cut off so that we can save some unneeded rebuild. -- When people make changes to the depdendencies, if such changes don't change the package interface, it will not trigger the build of its dependents. +- The introduction of such hashing does not appear in parsing, so that the changes of dependencies will never trigger re-parsing. +- Once the package installation is a no-op, the transitive rebuild graph will be cut off so that we can save some unneeded rebuild. +- When people make changes to the dependencies, if such changes don't change the package interface, it will not trigger the build of its dependents. ### Introducing `pinned-dependencies` -To make `bsb -make-world` more pratical, we also introduce a new concept called: `pinned-dependencies`. -In general, packages are classified as three categories: - -- toplevel - - warnings reported - - warn-error respected - - build dev dependency - - run custom rules - - package-specs like es6/commonjs overrides all its dependencies -- pinned dependencies - - warnings reported - - warn-error respected - - build dev dependency - - run custom rules -- normal dependencies - - warnings, warn-error ignored - - ignore dev directories - - ignore custom generator rules - -Previously, we only had `toplevel` and `normal dependencies`, by introducing `pinned-dependencies`, -such package will be built mostly like `toplevel` packages. - -The usage is quite simple, add `pinned-dependencies` in the toplevel package's `bsconfig.json`. -Note such field only make sense in toplevel's configuration. +To make `bsb -make-world` more practical for e.g. multi-package setups (`lerna`, `yarn workspaces`, etc.), we introduced a new concept called `pinned-dependencies`. A pinned dependency allows you to automatically rebuild packages that are pinned by a toplevel package, like your final webapp. +Please refer to our [pinned dependencies](/docs/manual/latest/build-pinned-dependencies) docs for more details. ### More robust handling of removal of staled output -When people delete or rename rescript files, it will introduce dangling staled output, for example, renaming `src/A.res` -into `src/B.res` will bring `src/A.cmi` (and more intemediate outputs) stale. This is worse than it sounds like, suppose you have a +When people delete or rename ReScript files, it will introduce dangling staled output, for example, renaming `src/A.res` +into `src/B.res` will bring `src/A.cmi` (and more intermediate outputs) stale. This is worse than it sounds like, suppose you have a local `src/List.res` which shadows the stdlib's List. Now we remove it, it will introduce stale `List.cmi` file, without the proper removal of such stale outptu, such stale `List.cmi` file will break the integrity of the build. -In this release, we introduced a more robust algorithm that will always remove stale output before the build so +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 710460fef..0ae1f049c 100644 --- a/_blogposts/2021-02-09-release-9-0.mdx +++ b/_blogposts/2021-02-09-release-9-0.mdx @@ -1,8 +1,7 @@ --- author: hongbo -date: "2021-02-09" -previewImg: https://res.cloudinary.com/dmm9n7v9f/image/upload/v1612974395/Reason%20Association/rescript-lang.org/compiler_release_9_0_szd11o.jpg -category: compiler +date: "2021-02-09" +previewImg: /static/blog/compiler_release_9_0.jpg title: ReScript 9.0 badge: release description: | @@ -17,68 +16,28 @@ 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. In previous versions, users couldn't ship their compiled JS code without defining a `package.json` dependency on `bs-platform`. Whenever a ReScript developer wanted to publish a package just for pure JS consumption / lean container deployment, they were required to use a bundler to bundle up their library / stdlib code, which made things way more complex and harder to understand. -To fix this problem, we now publish our pre-compiled stdlib JS files as a separate npm package called [`@rescript/std`](https://www.npmjs.com/package/@rescript/std). Each new `bs-platform` release has a matching `@rescript/std` release for runtime compatibility. - -We also introduced a new configuration within our `bsconfig.json` file to tell the compiler to use our pre-compiled package instead: - -```json -{ - /* ... */ - "external-stdlib" : "@rescript/std" -} -``` - -With this configuration set, compiled JS code will now point to the defined `external-stdlib` path: - - - -```res -Belt.Array.forEach([1, 2, 3], (num) => Js.log(num)) -``` - -```js -// Note the import path starting with "@rescript/std" -import * as Array from "@rescript/std/lib/es6/belt_Array.js"; - -Belt_Array.forEach([ - 1, - 2, - 3 - ], (function (num) { - console.log(num); - - })); -``` - +To fix this problem, we introduced an `external-stdlib` configuration that allows specifying a pre-compiled stdlib npm package (`@rescript/std`). More details on how to use that feature can be found in our [External Stdlib](/docs/manual/latest/build-external-stdlib) documentation. -The JavaScript output above was compiled with an `es6` target, but will also work with `commonjs`. - -**Important:** When using this option, you need to make sure that the version number of `bs-platform` and `@rescript/std` matches with the same version number in your `package.json` file, otherwise you'll eventually run into runtime problems due to mismatching stdlib behavior! - -To prevent unnecessary complications, only use this feature when... -- You want to ship a library for JS / TS consumers without making them depend on `bs-platform` -- You can't depend on `bs-platform` due to toolchain size (docker containers, low-storage deployment devices, etc) - -### Less Bundle Bloat when Adding ReScript +### Less Bundle Bloat when Adding ReScript With each release we keep a close eye on generating code that is optimized for tree-shaking. We also believe that we reached a milestone where ReScript reliably produces output that has almost no impact on our final JS bundle-sizes (this is what we call our "zero-cost" philosophy). @@ -88,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 = @@ -128,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. @@ -138,13 +97,13 @@ For those interested in the details, here is a representative diff resulting fro function is_space(param){ - var switcher = param - 9 | 0; - if (switcher > 4 || switcher < 0) { -- return switcher == 23 ; +- return switcher == 23 ; + if (param > 13 || param < 9) { + return param === 32; } else { -- return switcher !== 2; +- return switcher !== 2; + return param != 11; - } + } } ``` @@ -209,12 +168,12 @@ Here is a code example before and after the change. Note how the `user` record s ```res type user = { - age: int + age: int } let data = { "user": { - age: 1 + age: 1 } } @@ -224,12 +183,12 @@ let age = data["user"].age ```res type user = { - age: int + age: int } let data = { "user": { - age: 1 + age: 1 } } diff --git a/_blogposts/2021-03-03-rescript-association-rebranding.mdx b/_blogposts/2021-03-03-rescript-association-rebranding.mdx new file mode 100644 index 000000000..e216cb619 --- /dev/null +++ b/_blogposts/2021-03-03-rescript-association-rebranding.mdx @@ -0,0 +1,74 @@ +--- +author: rescript-association +date: "2021-03-03" +previewImg: /static/blog/rescript_assoc_rename_preview.jpg +title: The ReScript Association +description: | + After the rebranding of ReScript, its Reason Association has now followed through to become the ReScript Association. +--- + +## A Small Change for our Association + +Last year, our platform rebranded from BuckleScript and Reason to [ReScript](/blog/bucklescript-is-rebranding). The Reason Association however, due to administrative reasons, lagged behind in name change until recently. + +Today, we are glad to announce that our organization is now known as the [**ReScript Association**](https://rescript-association.org). + +We will continue on our mission to support the development of the ReScript language while leveraging OCaml as its underlying foundation. + +## Relation to ReScript the Project + +Founded in 2018, the ReScript Association provides a legal and financial foundation for many parts of ReScript. We’re non-profit and independently funded by industrial partners, research institutions and individuals. Our board consists of three members, two of which are also on the ReScript core team (Patrick & Cristiano). + +**Here is some of our work you might recognize:** + +- Reason Conf 2018 & 2019. Post-covid, we’ll restart prioritizing these. +- [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-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. +- Experimental tools like the upcoming doc generator. +- Yearly reports for our donors and partners. +- Setting up legal contracts, hiring contractors, paying invoices, doing accounting, etc. + +## Benefits of an Association + +The Association provides a much needed layer of protection so that we can put liabilities onto an organization instead of individuals (a crucial safeguard when things go wrong and money is at risk). As an example, organizing the past conferences or any event past meetup sizes wouldn’t have been realistic for single, legally unaccountable individuals. + +We also take non-partisan ownership of some of ReScript’s essential infrastructures listed above, so that ReScript can live on past individual bus factors. + +Furthermore, we serve as a communication channel for our partners and donors. + +## Donate + +High quality, long-term Open Source work doesn’t come from some good words and pats on the back. While we do try to be efficient, past a certain point, big amounts of time and financial resources are needed to keep up the momentum for maintaining and developing the platform. To this end, we regularly raise donations to make sure that the running costs are covered, and Open Source contributors paid. + +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) + +Your support allowed us to accomplish many of the work listed earlier. Thank you! + +## Extra: Why did this change take so long? + +We are an established non-profit organization under Austrian law, located in Vienna. We have legal obligations and can't just rename our organization as we please. + +**Here are a few things we had to consider:** + +- We wrapped up our yearly donation reports and checked in with our partners regarding our new ReScript brand and roadmap. Our partners are satisfied with our plan! +- Contrary to our compiler, Austrian bureaucracy is not known for its speed. Thankfully, in parallel, we took this time to reach a couple of major milestones on rescript-lang.org. +- We had to refactor our brand assets, rework the association site, hook up new domains, rename things while making sure nothing breaks, and communicate the goals within the ReScript team. + +Luckily, most of the challenging work is done! We only need to wait for legal authorities to verify the new amendments of our statutes. + +## Conclusion + +The association rebranding and mission alignment took longer than expected, but was a worthwhile investment. The ReScript Association is now an integral part of the ReScript project, with all the aforementioned benefits. + +For further updates, make sure to join our [Forum](https://forum.rescript-lang.org), or follow [@ReScriptAssoc](https://twitter.com/ReScriptAssoc) & [@rescriptlang](https://twitter.com/rescriptlang) on Twitter! diff --git a/_blogposts/2021-05-07-release-9-1.mdx b/_blogposts/2021-05-07-release-9-1.mdx new file mode 100644 index 000000000..75f021180 --- /dev/null +++ b/_blogposts/2021-05-07-release-9-1.mdx @@ -0,0 +1,164 @@ +--- +author: rescript-team +date: "2021-05-07" +previewImg: /static/blog/compiler_release_9_1.jpg +title: ReScript 9.1 +badge: release +description: | + Featuring a new npm package, a CLI revamp, polymorphic variant interop and object cleanup. +--- + +## Exciting Improvements in ReScript 9.1 + +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 + +We've finally moved from [bs-platform](https://www.npmjs.com/package/bs-platform) to [rescript](https://www.npmjs.com/package/rescript)! + +This is mostly just a long overdue name change; the package's virtually identical. Apart from the renaming of our CLI. + +### CLI Cleanup + +We took the occasion of the NPM package move to also unify the binaries `bsc`, `bsb` and their various commands into a single `rescript` command: + +```sh +❯ rescript -help +Available flags +-v, -version display version number +-h, -help display help +Subcommands: + build + clean + format + convert + help +Run rescript subcommand -h for more details, +For example: + rescript build -h + rescript format -h +The default `rescript` is equivalent to `rescript build` subcommand +``` + +Here's a table of translation, if you're upgrading your script that is currently using `bsc` and `bsb`: +- `bsc -format myFile.res`: `rescript format myFile.res` +- `bsb`: `rescript build` \* +- `bsb -make-world`: `rescript build -with-deps` \* +- `bsb -w`: `rescript build -w` +- `bsb -w -make-world`: `rescript build -w -with-deps` \* + +\* **However**, we've gone even further to improve your experience; in most cases you won't need to invoke `build`, nor `-with-deps` anymore! Not only is `rescript` an alias to `rescript build`, it also smartly detects whether your dependencies are already built; if not, it builds them automatically. + +This means that you can ditch your old `-make-world` (now the explicit `-with-deps` flag, for edge-case explicit usages). Just call `rescript` and everything including dependencies will always be built! As performance is our highest priority, we've ensured that such extra detections does not slow down the build. + +### Polymorphic Variants for Numbers and Strings + +*Drumrolls* +- Poly variants like `#1`, `#42` compile to JavaScript numbers. +- Poly variants like `#hello`, `#world` compile to JavaScript Strings. + +This is a feature many of you were probably waiting for. Now you can interop with a JavaScript value that's a limited set of numbers or strings: + + + +```res +let secret = #42 + +// optional type annotation, for documentation +type t = [#1 | #3 | #5 ] + +// enjoy the pattern matching +let test = (arg: t) => { + switch arg { + | #1 | #3 => "hello" + | #5 => "world" + } +} +``` + +```js +var secret = 42; + +function test(arg) { + if (arg === 5) { + return "world"; + } else { + return "hello"; + } +} +``` + + + +But wait, **there's more**. We allow safely coercing these poly variants to `int`s or `string`s even at the ReScript side: + + + +```res +let test2 = (arg: [#1 | #3 | #5]) => { + (arg :> int) +} + +let test3 = (arg: option<[#1 | #3 | #5]>) => { + (arg :> option) +} + +Js.log(test2(#1)) +Js.log(test3(Some(#3))) +``` + +```js +function test2(arg) { + return arg; +} + +function test3(arg) { + return arg; +} + +console.log(1); +console.log(3); +``` + + + +As usual, check the output tabs: there's no runtime cost. Time to upgrade some interop! + +### Object Cleanup + +Our objects had various constraints due to legacy reasons; we've managed to clean them up, and expose the UX that they deserve: + +- `Js.t<{"x": int}>` is now simply `{"x": int}`. Existing code using `Js.t` still work; it's now a no-op. Our `rescript format` will also format them away. +- You can now use object type spread: + ``` + type point2d = { + "x": float, + "y": float, + } + type point3d = { + ...point2d, + "z": float, + } + ``` + +The cleanup also allowed us to unlock very exciting ideas. For example, [this one](https://forum.rescript-lang.org/t/rfc-more-general-type-checking-for-structural-typings/1485). + +## What's Next? + +First class unicode support! Expect being able to write the following: + +```res +let helloUnicode = (x) =>{ + switch x { + | '❤️' => "ReScript is awesome" + | 'Σ' => "Math is fun" + | _ => "Lots of unicode" + } +} +``` + +## Conclusion + +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 new file mode 100644 index 000000000..a1da83db4 --- /dev/null +++ b/_blogposts/2021-06-25-roadmap-2021-and-new-landing-page.mdx @@ -0,0 +1,59 @@ +--- +author: rescript-team +date: "2021-06-25" +title: Roadmap 2021 & New Landing Page +badge: roadmap +description: | + Announcing our roadmap for 2021 / 2022, release cycle plans and new landing page. +--- + +import Image from "src/components/Image.mjs" + +## Team Update + +It has almost been a year since we originally [launched our new ReScript brand](/blog/bucklescript-is-rebranding), and we think it has been a pretty successful journey so far: + +- Brand new documentation experience +- Relaunch of `rescript-react` and its new docs +- Release of our re-imagined VSCode & Vim plugins +- New syntax, compiler and build system improvements, including incremental build performance at scale +- Improved interop with polymorphic variants for numbers and strings +- 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. + +Stay tuned, this is just the beginning! + +### New Roadmap and Release Cycle Plans + +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 +- Help the community / companies align on major stable versions + +The detailed roadmap with all our planned changes (and definition of our release cycle plan) can be found in our [community / roadmap](/community/roadmap) section. + +### New Landing Page + +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: + +- Highlighted Case Studies from production users such as Beop or Rohea +- 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-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.js b/src/Blog.js deleted file mode 100644 index fa3a44dab..000000000 --- a/src/Blog.js +++ /dev/null @@ -1,441 +0,0 @@ -// Generated by ReScript, PLEASE EDIT WITH CARE - -import * as Mdx from "./common/Mdx.js"; -import * as Meta from "./components/Meta.js"; -import * as Next from "./bindings/Next.js"; -import * as $$Text from "./components/Text.js"; -import * as Util from "./common/Util.js"; -import * as Curry from "bs-platform/lib/es6/curry.js"; -import * as React from "react"; -import * as Button from "./components/Button.js"; -import * as Footer from "./components/Footer.js"; -import * as BlogApi from "./common/BlogApi.js"; -import * as DateStr from "./common/DateStr.js"; -import * as Caml_obj from "bs-platform/lib/es6/caml_obj.js"; -import * as Markdown from "./components/Markdown.js"; -import * as Belt_Array from "bs-platform/lib/es6/belt_Array.js"; -import * as Navigation from "./components/Navigation.js"; -import * as ProcessEnv from "./common/ProcessEnv.js"; -import * as Belt_Option from "bs-platform/lib/es6/belt_Option.js"; -import * as Caml_option from "bs-platform/lib/es6/caml_option.js"; -import * as BlogFrontmatter from "./common/BlogFrontmatter.js"; -import * as NameInitialsAvatar from "./components/NameInitialsAvatar.js"; - -var defaultPreviewImg = "https://res.cloudinary.com/dmm9n7v9f/image/upload/v1598616442/Reason%20Association/rescript-lang.org/Art-3-rescript-launch_ovoibg.jpg"; - -var middleDotSpacer = " " + (String.fromCharCode(183) + " "); - -function Blog$Badge(Props) { - var badge = Props.badge; - var bgColor = badge !== 1 ? "bg-turtle" : "bg-code-1"; - var text = BlogFrontmatter.Badge.toString(badge); - return React.createElement("div", { - className: bgColor + " flex items-center h-6 font-medium tracking-tight text-onyx-80 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 categories = Props.categories; - var selected = Props.selected; - var onSelected = Props.onSelected; - var tabs = [ - /* All */0, - /* Archived */1 - ].concat(Belt_Array.map(categories, (function (cat) { - return { - _0: cat, - [Symbol.for("name")]: "Category" - }; - }))); - return React.createElement("div", { - className: "text-16 w-full flex items-center justify-between text-onyx-50" - }, Belt_Array.map(tabs, (function (tab) { - var onClick = function (evt) { - evt.preventDefault(); - return Curry._1(onSelected, tab); - }; - var isActive = Caml_obj.caml_equal(selected, tab); - var text = typeof tab === "number" ? ( - tab !== 0 ? "Archived" : "All" - ) : BlogFrontmatter.Category.toString(tab._0); - return React.createElement("div", { - key: text, - className: ( - isActive ? "bg-snow-dark text-onyx rounded py-1" : "hover:cursor-pointer hover:text-onyx" - ) + " 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 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: $$Text.H3.$$default - }, title)) - }), React.createElement("div", { - className: "text-night-light text-sm" - }, 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 displayName = BlogFrontmatter.Author.getDisplayName(author); - var src = author.imgUrl; - var authorImg = src !== null ? React.createElement("img", { - className: "h-full w-full rounded-full", - src: src - }) : React.createElement(NameInitialsAvatar.make, { - displayName: displayName - }); - var className = "absolute top-0 h-full w-full object-cover"; - var handle = author.twitter; - 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: $$Text.H2.$$default - }, title), React.createElement("div", { - className: "mb-6" - }, React.createElement("div", { - className: "flex items-center font-medium text-onyx-50 text-sm my-2" - }, React.createElement("div", { - className: "inline-block w-4 h-4 mr-2" - }, authorImg), React.createElement("div", undefined, handle !== null ? React.createElement("a", { - className: "hover:text-onyx-80", - href: "https://twitter.com/" + handle, - rel: "noopener noreferrer", - target: "_blank" - }, displayName) : displayName, category !== undefined ? React.createElement(React.Fragment, undefined, middleDotSpacer, category, middleDotSpacer) : middleDotSpacer, Util.$$Date.toDayMonthYear(date))), React.createElement("p", { - className: "text-night-dark text-16" - }, 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 orderByDate(posts) { - return posts.slice().sort(function (a, b) { - var aV = DateStr.toDate(a.frontmatter.date).valueOf(); - var bV = DateStr.toDate(b.frontmatter.date).valueOf(); - if (aV === bV) { - return 0; - } else if (aV > bV) { - return -1; - } else { - return 1; - } - }); -} - -var Malformed = {}; - -function $$default(props) { - var malformed = props.malformed; - var posts = props.posts; - var match = React.useState(function () { - return /* All */0; - }); - var setSelection = match[1]; - var currentSelection = match[0]; - var errorBox = ProcessEnv.env === ProcessEnv.development && malformed.length !== 0 ? React.createElement("div", { - className: "mb-12" - }, React.createElement(Markdown.Warn.make, { - children: null - }, React.createElement("h2", { - className: "font-bold text-night-dark text-2xl mb-2" - }, "Some Blog Posts are Malformed!"), React.createElement("p", undefined, "Any blog post with invalid data will not be displayed in production."), React.createElement("div", undefined, React.createElement("p", { - className: "font-bold mt-4" - }, "Errors:"), React.createElement("ul", undefined, Belt_Array.mapWithIndex(malformed, (function (i, m) { - return React.createElement("li", { - key: String(i), - className: "list-disc ml-5" - }, "pages/blog/" + (m.id + (".mdx: " + m.message))); - })))))) : null; - 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; - if (typeof currentSelection === "number") { - filtered = currentSelection !== 0 ? props.archived : posts; - } else { - var selected = currentSelection._0; - filtered = Belt_Array.keep(posts, (function (param) { - var category = param.frontmatter.category; - if (category !== null) { - return category === selected; - } else { - return false; - } - })); - } - var match$1 = filtered.length; - var result; - if (match$1 !== 0) { - var first = Belt_Array.getExn(filtered, 0); - var rest = filtered.slice(1); - var category = Belt_Option.map(Caml_option.null_to_opt(first.frontmatter.category), (function (category) { - return BlogFrontmatter.Category.toString(category); - })); - var tmp = { - title: first.frontmatter.title, - author: first.frontmatter.author, - date: DateStr.toDate(first.frontmatter.date), - slug: first.id - }; - var tmp$1 = Caml_option.null_to_opt(first.frontmatter.previewImg); - if (tmp$1 !== undefined) { - tmp.previewImg = Caml_option.valFromOption(tmp$1); - } - var tmp$2 = Caml_option.null_to_opt(first.frontmatter.badge); - if (tmp$2 !== undefined) { - tmp.badge = Caml_option.valFromOption(tmp$2); - } - if (category !== undefined) { - tmp.category = Caml_option.valFromOption(category); - } - var tmp$3 = Caml_option.null_to_opt(first.frontmatter.description); - if (tmp$3 !== undefined) { - tmp.firstParagraph = Caml_option.valFromOption(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 row-gap-12 md:row-gap-24 w-full" - }, Belt_Array.mapWithIndex(rest, (function (i, post) { - var badge = post.frontmatter.badge; - var category = Belt_Option.map(Caml_option.null_to_opt(first.frontmatter.category), (function (category) { - return BlogFrontmatter.Category.toString(category); - })); - var tmp = { - title: post.frontmatter.title, - author: post.frontmatter.author, - date: DateStr.toDate(post.frontmatter.date), - slug: post.id, - key: post.id + String(i) - }; - var tmp$1 = Caml_option.null_to_opt(post.frontmatter.previewImg); - if (tmp$1 !== undefined) { - tmp.previewImg = Caml_option.valFromOption(tmp$1); - } - if (category !== undefined) { - tmp.category = Caml_option.valFromOption(category); - } - 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, { - categories: props.availableCategories, - 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, { - description: "News, Announcements, Release Notes and more", - title: "Blog | ReScript Documentation" - }), React.createElement("div", { - className: "mt-16 pt-2" - }, React.createElement("div", { - className: "text-night text-lg" - }, 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" - } - }, errorBox, content)) - }))), React.createElement(Footer.make, {})))); -} - -function getStaticProps(_ctx) { - var authors = BlogFrontmatter.Author.getAllAuthors(undefined); - var match = Belt_Array.reduce(BlogApi.getAllPosts(undefined), [ - [], - [], - [], - [] - ], (function (acc, postData) { - var availableCategories = acc[3]; - var archived = acc[2]; - var malformed = acc[1]; - var posts = acc[0]; - var id = postData.slug; - var decoded = BlogFrontmatter.decode(authors, postData.frontmatter); - if (decoded.TAG === /* Ok */0) { - var frontmatter = decoded._0; - if (postData.archived) { - archived.push({ - id: id, - frontmatter: frontmatter - }); - } else { - posts.push({ - id: id, - frontmatter: frontmatter - }); - } - var category = frontmatter.category; - var hasCategory = availableCategories.some(function (c) { - if (category !== null) { - return c === category; - } else { - return false; - } - }); - var newAvailableCat = true || postData.archived || !(category !== null && !hasCategory) ? availableCategories : Belt_Array.concat(availableCategories, [category]); - return [ - posts, - malformed, - archived, - newAvailableCat - ]; - } - var m_message = decoded._0; - var m = { - id: id, - message: m_message - }; - var malformed$1 = Belt_Array.concat(malformed, [m]); - return [ - posts, - malformed$1, - archived, - availableCategories - ]; - })); - var props_posts = orderByDate(match[0]); - var props_archived = orderByDate(match[2]); - var props_malformed = match[1]; - var props_availableCategories = match[3]; - var props = { - posts: props_posts, - archived: props_archived, - malformed: props_malformed, - availableCategories: props_availableCategories - }; - return Promise.resolve({ - props: props - }); -} - -var Post = {}; - -export { - Post , - Malformed , - defaultPreviewImg , - $$default , - $$default as default, - getStaticProps , - -} -/* middleDotSpacer Not a pure module */ diff --git a/src/Blog.res b/src/Blog.res index a7b96aa04..262d684ef 100644 --- a/src/Blog.res +++ b/src/Blog.res @@ -15,75 +15,58 @@ module Link = Next.Link -let _rescriptDefaultImg = "https://res.cloudinary.com/dmm9n7v9f/image/upload/v1598616442/reason%20association/rescript-lang.org/art-3-rescript-launch_ovoibg.jpg" -let _planetPreviewImg = "https://res.cloudinary.com/dmm9n7v9f/image/upload/v1587479463/Reason%20Association/reasonml.org/reasonml_art2_1280_vhzxnz.png" - -let defaultPreviewImg = "https://res.cloudinary.com/dmm9n7v9f/image/upload/v1598616442/Reason%20Association/rescript-lang.org/Art-3-rescript-launch_ovoibg.jpg" +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" - | Testing => "bg-code-1" + | Preview | Roadmap | Release | Community => "bg-turtle" + | Testing => "bg-orange" } let text = badge->BlogFrontmatter.Badge.toString
-
+ className={bgColor ++ " flex items-center h-6 font-medium tracking-tight text-gray-80-tr text-14 px-2 rounded-sm"}> +
+ +
{React.string(text)}
} } -module CategorySelector = { - type selection = - | All - | Archived - | Category(BlogFrontmatter.Category.t) - let renderTab = (~text: string, ~isActive: bool, ~onClick) => { - let active = "bg-snow-dark text-onyx rounded py-1" -
- {React.string(text)} -
- } +type category = + | /** Actually only unarchived */ All + | Archived +module CategorySelector = { @react.component - let make = ( - ~categories: array, - ~selected: selection, - ~onSelected: selection => unit, - ) => { - let tabs = [All, Archived]->Js.Array2.concat(Belt.Array.map(categories, cat => Category(cat))) - -
- {Belt.Array.map(tabs, tab => { - let onClick = evt => { - evt->ReactEvent.Mouse.preventDefault - onSelected(tab) - } + let make = (~selected: category) => { + let tabs = [All, Archived] +
+ {tabs + ->Array.map(tab => { // Deep comparison here! let isActive = selected == tab - - let text = switch tab { - | All => "All" - | Archived => "Archived" - | Category(cat) => BlogFrontmatter.Category.toString(cat) + 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}
} } @@ -93,37 +76,42 @@ module BlogCard = { let make = ( ~previewImg: option=?, ~title: string="Unknown Title", - ~author as _: BlogFrontmatter.Author.t, + ~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)} @@ -137,20 +125,20 @@ module FeatureCard = { let make = ( ~previewImg: option=?, ~title: string="Unknown Title", - ~author: BlogFrontmatter.Author.t, + ~author: BlogFrontmatter.author, ~badge: option=?, - ~date: Js.Date.t, + ~date: Date.t, ~category: option=?, ~firstParagraph: string="", ~slug: string, ) => { - let displayName = BlogFrontmatter.Author.getDisplayName(author) - - let authorImg = switch author.imgUrl->Js.Null.toOption { - | Some(src) => - | None => + let authorImg = switch author.imgUrl { + | "" => React.null + | imgUrl => +
+ +
} -
-

{React.string(title)}

+

{React.string(title)}

-
-
authorImg
+
+ authorImg
- {switch author.twitter->Js.Null.toOption { - | Some(handle) => - - {React.string(displayName)} - - | None => React.string(displayName) - }} + "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)} @@ -207,11 +194,11 @@ module FeatureCard = { {date->Util.Date.toDayMonthYear->React.string}
-

{React.string(firstParagraph)}

+

{React.string(firstParagraph)}

- - + +
@@ -220,113 +207,34 @@ module FeatureCard = { type params = {slug: string} -module Post = { - type t = { - id: string, - frontmatter: BlogFrontmatter.t, - } - - let orderByDate = (posts: array): array => - posts - ->Js.Array.copy - ->Js.Array2.sortInPlaceWith((a, b) => { - let aV = a.frontmatter.date->DateStr.toDate->Js.Date.valueOf - let bV = b.frontmatter.date->DateStr.toDate->Js.Date.valueOf - if aV === bV { - 0 - } else if aV > bV { - -1 - } else { - 1 - } - }) -} - -module Malformed = { - type t = { - id: string, - message: string, - } -} - -type props = { - posts: array, - archived: array, - malformed: array, - availableCategories: array, -} +type props = {posts: array, category: category} let default = (props: props): React.element => { - let {availableCategories, posts, malformed, archived} = props - - let (currentSelection, setSelection) = React.useState(() => CategorySelector.All) + let {posts, category} = props - let errorBox = if ProcessEnv.env === ProcessEnv.development && Belt.Array.length(malformed) > 0 { -
- -

- {React.string("Some Blog Posts are Malformed!")} -

-

- {React.string("Any blog post with invalid data will not be displayed in production.")} -

-
-

{React.string("Errors:")}

-
    - {Belt.Array.mapWithIndex(malformed, (i, m) => -
  • Belt.Int.toString} className="list-disc ml-5"> - {React.string("pages/blog/" ++ (m.id ++ (".mdx: " ++ m.message)))} -
  • - )->React.array} -
-
-
-
- } else { - React.null - } - - 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 - | Category(selected) => - Belt.Array.keep(posts, ({frontmatter}) => - switch Js.Null.toOption(frontmatter.category) { - | Some(category) => category === selected - | None => false - } - ) - } - - 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 category = - first.frontmatter.category - ->Js.Null.toOption - ->Belt.Option.map(category => category->BlogFrontmatter.Category.toString) + 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} - ?category - slug=first.id + slug={BlogApi.blogPathToSlug(first.path)} />
@@ -334,64 +242,58 @@ let default = (props: props): React.element => { | [] => React.null | rest =>
- {Belt.Array.mapWithIndex(rest, (i, post) => { - let badge = post.frontmatter.badge->Js.Null.toOption - let category = - first.frontmatter.category - ->Js.Null.toOption - ->Belt.Option.map(category => category->BlogFrontmatter.Category.toString) + 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"> + {Array.map(rest, post => { + let badge = post.frontmatter.badge->Null.toOption Js.Null.toOption} + key={post.path} + previewImg=?{post.frontmatter.previewImg->Null.toOption} title=post.frontmatter.title author=post.frontmatter.author ?badge - ?category date={post.frontmatter.date->DateStr.toDate} - slug=post.id + slug={BlogApi.blogPathToSlug(post.path)} /> })->React.array}
} - <> 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" <>
-
- +
+
- +
- errorBox content + content
-
+