Configuration du projet Remix

Nous configurons un nouveau projet Remix qui va contenir toute la logique frontend de notre application.

3 min read

Configuration de Remix

Création d'un nouveau projet Remix

Nous devons d'abord reculer d'un dossier pour créer le projet Remix, à la racine de stack-remix-nestjs.

Terminal
1
cd .. # Pour remonter d'un dossier
2
npx create-remix@latest # Pour créer un nouveau projet Remix

Nous appelons le projet frontend. Et nous répondons Non à la question Voulez-vous instancier un nouveau repository git ?.

On devrait maintenant avoir un dossier frontend à la racine de stack-remix-nestjs.

Configuration d'un nouveau projet Remix.js

Création d'un nouveau fichier index.cjs et index.d.cts

Attention. Nous allons créer un fichier index.cjs qui sera exécuté et interprété par l'application NestJS. Il utilise la syntaxe CommonJS, qui est compatible avec NestJS.

frontend/index.cjs
1
const path = require('node:path');
2
3
let devServer;
4
const SERVER_DIR = path.join(__dirname, 'build/server/index.js');
5
const PUBLIC_DIR = path.join(__dirname, 'build/client');
6
7
module.exports.getPublicDir = function getPublicDir() {
8
return PUBLIC_DIR;
9
};
10
11
module.exports.getServerBuild = async function getServerBuild() {
12
if (process.env.NODE_ENV === 'production' || devServer === null) {
13
return import(SERVER_DIR);
14
}
15
const ssrModule = await devServer.ssrLoadModule('virtual:remix/server-build');
16
return ssrModule;
17
};
18
19
module.exports.startDevServer = async function startDevServer(app) {
20
if (process.env.NODE_ENV === 'production') return;
21
const vite = await import('vite');
22
devServer = await vite.createServer({
23
server: { middlewareMode: 'true' },
24
root: __dirname,
25
});
26
27
app.use(devServer.middlewares);
28
return devServer;
29
// ...continues
30
};

Ce fichier déclare trois fonctions que nous utiliserons dans l'application NestJS :

  • getPublicDir : retourne le chemin du dossier build/client
  • getServerBuild : retourne le module server/index.js de l'application Remix
  • startDevServer : démarre le serveur de développement de Remix

Pour bénéficier de l'autocomplétion, nous devons créer un fichier index.d.cts qui va déclarer les types de ces fonctions.

frontend/index.d.cts
1
declare module '@virgile/frontend' {
2
// <= Nom du module, à adapter selon le nom de votre projet
3
export function getPublicDir(): string;
4
export function getServerBuild(): Promise<any>;
5
export function startDevServer(app: any): Promise<void>;
6
}

Configurer le point d'entrée de l'application Remix dans le fichier package.json

Après avoir créé ces deux fichiers, nous devons modifier le fichier package.json de Remix pour que NestJS connaisse son point d'entrée (et sa déclaration de types).

C'est seulement après avoir ajouté cette instruction qu'on bénéficiera de l'auto-complétion du module frontend.

Voilà un package.json complet.

frontend/package.json
1
{
2
"name": "@virgile/frontend",
3
"private": true,
4
"sideEffects": false,
5
"type": "module",
6
"main": "./index.cjs",
7
"types": "./index.d.cts",
8
"scripts": {
9
"start": "remix-serve ./build/server/index.js",
10
"old-dev": "remix vite:dev",
11
"build": "remix vite:build",
12
"lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
13
"typecheck": "tsc"
14
},
15
"dependencies": {
16
"@conform-to/react": "^1.1.0",
17
"@conform-to/zod": "^1.1.0",
18
"@radix-ui/react-slot": "^1.0.2",
19
"@remix-run/node": "^2.8.1",
20
"@remix-run/react": "^2.8.1",
21
"@remix-run/serve": "^2.8.1",
22
"class-variance-authority": "^0.7.0",
23
"clsx": "^2.1.0",
24
"isbot": "^5.1.2",
25
"lucide-react": "^0.365.0",
26
"react": "^18.2.0",
27
"react-dom": "^18.2.0",
28
"remix-flat-routes": "^0.6.4",
29
"tailwind-merge": "^2.2.2",
30
"tailwindcss-animate": "^1.0.7",
31
"zod": "^3.22.4"
32
},
33
"devDependencies": {
34
"@remix-run/dev": "^2.8.1",
35
"@types/react": "^18.2.67",
36
"@types/react-dom": "^18.2.22",
37
"@virgile/eslint-config": "*",
38
"@virgile/typescript-config": "*",
39
"autoprefixer": "^10.4.19",
40
"eslint": "^8.57.0",
41
"eslint-import-resolver-typescript": "^3.6.1",
42
"eslint-plugin-import": "^2.29.1",
43
"eslint-plugin-jsx-a11y": "^6.8.0",
44
"eslint-plugin-react": "^7.34.1",
45
"eslint-plugin-react-hooks": "^4.6.0",
46
"eslint-plugin-remix-react-routes": "^1.0.5",
47
"eslint-plugin-tailwindcss": "^3.15.1",
48
"postcss": "^8.4.38",
49
"tailwindcss": "^3.4.3",
50
"typescript": "^5.4.3",
51
"vite": "^5.2.2",
52
"vite-tsconfig-paths": "^4.3.2"
53
},
54
"engines": {
55
"node": ">=18.0.0"
56
}
57
}

Ce qui nous intéresse ici, ce sont les deux premières lignes :

1
"main": "./index.cjs",
2
"types": "./index.d.cts",

Grâce à l'ajout de ces deux lignes, toute application qui importera le module @virgile/frontend bénéficiera de l'autocomplétion des fonctions getPublicDir, getServerBuild et startDevServer.

Nous avons terminé la configuration de ces deux projets pour l'instant. Nous allons maintenant configurer le monorepo pour qu'ils puissent communiquer entre eux.