Ajouter Typescript et Eslint

Nous ajoutons Typescript et Eslint pour détecter les améliorations à faire sur notre base de code.

5 min read

Ajout de Typescript et Eslint

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.

Terminal
1
mkdir packages # Pour créer un dossier nommé packages

Il va contenir nos configurations Typescript et Eslint.

Configurer Typescript

Pour ajouter notre configuration Typescript, nous allons créer un nouveau dossier typescript-config à l'intérieur de packages.

Terminal
1
mkdir 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.

Terminal
1
cd packages/typescript-config # Pour se déplacer dans le dossier typescript-config
2
npm 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.

packages/typescript-config/base.json
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.

backend/package.json
1
"devDependencies": {
2
"@virgile/typescript-config": "*"
3
}
frontend/package.json
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.

backend/tsconfig.json
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
}
frontend/tsconfig.json
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": true
30
}
31
}

Configurer Eslint

Nous allons reproduire la même opération pour Eslint.

Créons un nouveau dossier packages/eslint-config à la racine de notre monorepo.

Terminal
1
mkdir 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.

Terminal
1
cd packages/eslint-config # Pour se déplacer dans le dossier eslint-config
2
npm init -y # Pour créer un fichier package.json

Nous devons ensuite installer toutes les librairies relatives à nos règles Eslint.

packages/eslint-config/package.Jso
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.

packages/eslint-config/base.js
1
/** @type {import('@types/eslint').Linter.BaseConfig} */
2
module.exports = {
3
extends: [
4
"@remix-run/eslint-config",
5
"@remix-run/eslint-config/node",
6
"prettier",
7
"plugin:import/recommended",
8
],
9
parser: "@typescript-eslint/parser",
10
rules: {
11
"import/no-duplicates": ["warn", { "prefer-inline": true }],
12
"import/consistent-type-specifier-style": ["warn", "prefer-inline"],
13
"import/order": [
14
"warn",
15
{
16
alphabetize: { order: "asc", caseInsensitive: true },
17
groups: [
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.

backend/package.json
1
"devDependencies": {
2
"@virgile/eslint-config": "*"
3
}
frontend/package.json
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.

backend/.eslintrc.js
1
module.exports = {
2
parser: '@typescript-eslint/parser',
3
parserOptions: {
4
project: 'tsconfig.json',
5
tsconfigRootDir: __dirname,
6
sourceType: 'module',
7
},
8
extends: [
9
'plugin:@typescript-eslint/recommended'
10
],
11
root: true,
12
env: {
13
node: true,
14
jest: true
15
},
16
ignorePatterns: ['.eslintrc.js', 'dist/', 'node_modules/', '**.d.ts'],
17
rules: {
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 !

frontend/.eslintrc.cjs
1
/** @type {import("eslint").Linter.Config} */
2
module.exports = {
3
root: true,
4
extends: [
5
'@virgile/eslint-config/base.js',
6
'@remix-run/eslint-config',
7
'@remix-run/eslint-config/node',
8
'plugin:remix-react-routes/recommended',
9
],
10
settings: {
11
'import/resolver': {
12
node: {
13
extensions: ['.js', '.jsx', '.ts', '.tsx'],
14
},
15
},
16
},
17
18
overrides: [
19
{
20
extends: ['@remix-run/eslint-config/jest-testing-library'],
21
files: ['app/**/__tests__/**/*', 'app/**/*.{spec,test}.*'],
22
rules: {
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 jest
27
// (so the linting plugins work nicely), but it means we have to explicitly
28
// set the jest version.
29
settings: {
30
jest: {
31
version: 28,
32
},
33
},
34
},
35
],
36
};

Ajouter ces packages en tant que workspaces

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.

package.json
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.