Nous ajoutons Typescript et Eslint pour détecter les améliorations à faire sur notre base de code.
Nous allons maintenant ajouter deux librairies à notre monorepo : Typescript
et Eslint
. Pour ce faire, nous allons créer un fichier de configuration pour chaque librairie, qu'on va ensuite réutiliser dans nos applications NestJS et Remix.js.
Nous allons nous inspirer de la configuration de Raphaël Moreau, un développeur Remix qui a créé un monorepo nommé Remix Galaxy
Nous allons donc créer un nouveau dossier nommé packages
à la racine de notre monorepo.
1mkdir packages # Pour créer un dossier nommé packages
Il va contenir nos configurations Typescript et Eslint.
Pour ajouter notre configuration Typescript, nous allons créer un nouveau dossier typescript-config
à l'intérieur de packages
.
1mkdir packages/typescript-config # Pour créer un dossier nommé typescript-config
Nous allons ensuite générer un fichier package.json
, pour télécharger les dépendances nécessaires.
1cd packages/typescript-config # Pour se déplacer dans le dossier typescript-config2npm init -y # Pour créer un fichier package.json
Nous allons changer le nom pour qu'il ressemble au code ci-dessus :
1{2"name": "@virgile/typescript-config",3"version": "0.0.0",4"private": true,5"license": "MIT",6"publishConfig": {7"access": "public"8},9"prettier": {}10}
Nous allons ensuite créer un fichier base.json
pour y coller notre configuration globale Typescript.
1{2"compilerOptions": {3"module": "CommonJS",4"declaration": true,5"removeComments": true,6"allowSyntheticDefaultImports": true,7"target": "ES2022",8"sourceMap": true,9"strict": true,10"skipLibCheck": true,11"forceConsistentCasingInFileNames": true,12"esModuleInterop": true,13"isolatedModules": false,14"moduleResolution": "Node",15"resolveJsonModule": false,16"allowJs": false,17"lib": ["ES2023"]18}19}
Et voilà ! Nous pouvons maintenant importer cette configuration Typescript dans nos projet NestJS et Remix.js.
Il nous suffit d'ajouter la dépendance @virgile/typescript-config
dans les fichiers backend/package.json
et frontend/package.json
.
1"devDependencies": {2"@virgile/typescript-config": "*"3}
1"devDependencies": {2"@virgile/typescript-config": "*"3}
Pourquoi utiliser Typescript comme ça ? Pour nous éviter de répéter la même configuration dans chaque projet. Nous avons besoin de faire un dernier changement avant que la configuration soit détectée.
Nous devons déclarer cette configuration globale dans le fichier tsconfig.json
de chaque projet. Il nous suffit de rajouter l'instruction extends
avec le chemin relatif vers la configuration.
1{2"extends": "@virgile/typescript-config/base.json",3"compilerOptions": {4"emitDecoratorMetadata": true,5"experimentalDecorators": true,6"strict": true,7"outDir": "./dist",8"rootDir": "./src",9"noEmit": false,10"lib": ["DOM", "ES2023"]11},12"include": ["src/**/*.ts", "src/exports.js"]13}
1{2"extends": "@virgile/typescript-config/base.json",3"include": [4"env.d.ts",5"**/*.ts",6"**/*.tsx",7"../playwright.config.ts",8"tailwind.config.cjs"9],10"exclude": ["index.d.cts"],11"compilerOptions": {12"module": "ESNext",13"skipLibCheck": true,14"lib": ["DOM", "DOM.Iterable", "ES2019"],15"isolatedModules": true,16"esModuleInterop": false,17"jsx": "react-jsx",18"noImplicitAny": false,19"moduleResolution": "bundler",20"resolveJsonModule": true,21"target": "ES2019",22"strict": true,23"allowJs": true,24"forceConsistentCasingInFileNames": true,25"baseUrl": ".",26"paths": {27"~/*": ["./app/*"]28},29"noEmit": true30}31}
Nous allons reproduire la même opération pour Eslint.
Créons un nouveau dossier packages/eslint-config
à la racine de notre monorepo.
1mkdir packages/eslint-config # Pour créer un dossier nommé eslint-config
Nous allons ensuite générer un fichier package.json
, pour télécharger les dépendances nécessaires.
1cd packages/eslint-config # Pour se déplacer dans le dossier eslint-config2npm init -y # Pour créer un fichier package.json
Nous devons ensuite installer toutes les librairies relatives à nos règles Eslint.
1{2"name": "@virgile/eslint-config",3"version": "0.0.0",4"private": true,5"license": "MIT",6"files": ["base.js"],7"devDependencies": {8"@remix-run/eslint-config": "^2.8.1",9"@types/eslint": "^8.56.5",10"@typescript-eslint/eslint-plugin": "^7.2.0",11"@typescript-eslint/parser": "^7.2.0",12"eslint": "^8.57.0",13"eslint-config-prettier": "^9.1.0",14"eslint-plugin-import": "^2.29.1",15"eslint-plugin-prettier": "^5.1.3",16"typescript": "^5.4.2"17},18"prettier": {},19"scripts": {20"lint:parent": "cd ../../ && npm run lint"21}22}
Et nous allons également copier sa configuration globale.
1/** @type {import('@types/eslint').Linter.BaseConfig} */2module.exports = {3extends: [4"@remix-run/eslint-config",5"@remix-run/eslint-config/node",6"prettier",7"plugin:import/recommended",8],9parser: "@typescript-eslint/parser",10rules: {11"import/no-duplicates": ["warn", { "prefer-inline": true }],12"import/consistent-type-specifier-style": ["warn", "prefer-inline"],13"import/order": [14"warn",15{16alphabetize: { order: "asc", caseInsensitive: true },17groups: [18"builtin",19"external",20"internal",21"parent",22"sibling",23"index",24],25},26],27},28};
Maintenant, ajoutons cette librairie comme dépendance de nos projets.
1"devDependencies": {2"@virgile/eslint-config": "*"3}
1"devDependencies": {2"@virgile/eslint-config": "*"3}
Nous devons ensuite extends
cette configuration dans les configurations Eslint de chaque projet.
J'ai finalement décidé de ne pas l'utiliser dans la configuration Eslint du backend. Je vous partage quand même la configuration.
1module.exports = {2parser: '@typescript-eslint/parser',3parserOptions: {4project: 'tsconfig.json',5tsconfigRootDir: __dirname,6sourceType: 'module',7},8extends: [9'plugin:@typescript-eslint/recommended'10],11root: true,12env: {13node: true,14jest: true15},16ignorePatterns: ['.eslintrc.js', 'dist/', 'node_modules/', '**.d.ts'],17rules: {18'@typescript-eslint/interface-name-prefix': 'off',19'@typescript-eslint/explicit-function-return-type': 'off',20'@typescript-eslint/explicit-module-boundary-types': 'off',21'@typescript-eslint/no-explicit-any': 'off'22}23};
Nous l'utilisons essentiellement pour notre application Remix !
1/** @type {import("eslint").Linter.Config} */2module.exports = {3root: true,4extends: [5'@virgile/eslint-config/base.js',6'@remix-run/eslint-config',7'@remix-run/eslint-config/node',8'plugin:remix-react-routes/recommended',9],10settings: {11'import/resolver': {12node: {13extensions: ['.js', '.jsx', '.ts', '.tsx'],14},15},16},1718overrides: [19{20extends: ['@remix-run/eslint-config/jest-testing-library'],21files: ['app/**/__tests__/**/*', 'app/**/*.{spec,test}.*'],22rules: {23'testing-library/no-await-sync-events': 'off',24'jest-dom/prefer-in-document': 'off',25},26// we're using vitest which has a very similar API to jest27// (so the linting plugins work nicely), but it means we have to explicitly28// set the jest version.29settings: {30jest: {31version: 28,32},33},34},35],36};
Nous avons créé ces deux packages pour éviter de répéter la même configuration dans chaque projet. Nous devons maintenant les ajouter dans le fichier package.json
principal.
C'est l'instruction packages/*
qui va informer NodeJS et Turbo que chaque dossier à l'intérieur du dossier packages
(contenant un fichier packages.json
) est à considérer comme workspace.
1{2"name": "nestjs-remix-monorepo",3"version": "1.0.0",4"description": "",5"main": "index.js",6"scripts": {7"test": "echo \"Error: no test specified\" && exit 1",8"dev": "turbo dev"9},10"keywords": [],11"author": "",12"license": "ISC",13"packageManager": "npm@10.2.3",14"workspaces": ["frontend", "backend", "packages/*"]15}
Nous avons terminé la configuration de Typescript et Eslint. Avant de commencer à coder, il nous reste plus qu'à intégrer notre service NestJS dans notre application Remix.js.