Hey! I’m not using the plugin, but happy to share our set-up as comparison which just calls ReScript and then Vite.
Going to be a bit rude and just paste some code here since this isn’t in a public repo, sorry.
Package.json
We use nodemon
for automatic reloading of changed server code. Vite builds a client bundle and a server bundle for SSR. The app also uses ReScript Relay though I’ve removed that from the examples here. It’s mostly and extra build and dev script. We use npm-run-all
to run scripts serially or in parallel (that library is named npm
but works with yarn
too which we use).
Of note is type: module
to make things work with mjs
files.
{
"name": "my-app",
"version": "0.1.0",
"type": "module",
"scripts": {
"postinstall": "rescript clean",
"build": "cross-env NODE_ENV=production run-s build:*",
"build:rescript": "rescript build",
"build:vite": "run-s build:vite:*",
"build:vite:client": "vite build --outDir dist/client --ssrManifest --manifest",
"build:vite:server": "vite build --outDir dist/server --ssr src/entry.server.mjs",
"start": "cross-env NODE_ENV=production node -r dotenv-expand/config Server.mjs",
"dev": "run-s build:rescript && run-p dev:*",
"dev:rescript": "rescript build -w",
"dev:vite": "nodemon -r dotenv-expand/config -w Server.mjs -w .env Server.mjs"
},
"dependencies": {
"@rescript/react": "^0.10.3",
"@ryyppy/rescript-promise": "^2.1.0",
"cross-env": "^7.0.3",
"dotenv": "^16.0.1",
"dotenv-expand": "^8.0.3",
"express": "^4.17.3",
"node-fetch": "^3.2.4",
"patch-package": "^6.4.7",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"rescript": "^9.1.4",
"rescript-webapi": "^0.6.1"
},
"devDependencies": {
"@vitejs/plugin-react": "^1.3.1",
"binode": "^1.0.5",
"nodemon": "^2.0.16",
"npm-run-all": "^4.1.5",
"vite": "^2.9.5"
}
}
bsconfig.json
I think the bsconfig.json
file is pretty standard. Do note we’re compiling to mjs
. I’d have liked to compile to .bs.mjs
to be able to differentiate between custom JS modules and ones that are generated by ReScript. Unfortunately the compiler has extensions as enum and not free-form string.
{
"name": "my-app",
"namespace": false,
"reason": { "react-jsx": 3 },
"refmt": 3,
"bs-dependencies": [
"@rescript/react",
"rescript-webapi",
"@ryyppy/rescript-promise"
],
"ppx-flags": [],
"sources": [
{ "dir": "src", "subdirs": true },
{ "dir": ".", "files": ["Server.res"] }
],
"package-specs": {
"module": "es6",
"in-source": true
},
"suffix": ".mjs",
"warnings": {
"number": "-3",
"error": "+101+8"
}
}
vite.config.js
To make things work here I had to change the rollup
and esbuild
output to also be es6. I believe this was specifically needed to make Node.js happy. The client doesn’t care, but there was an unbundled file (Server.res
) which was invoked by Node.js directly, compiled by ReScript, which would then try to import CJS files, which broke. So making everything ESM fixed that.
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
],
build: {
target: 'esnext',
rollupOptions: {
output: {
format: "esm"
}
}
},
// Prevent ReScript messages from being lost when we run all things at the same time.
clearScreen: false,
})
Hope that helps anyone trying to get this working (perhaps you can spot the difference between this and a set-up using the ReScript plugin).