Publier une librairie avec plusieurs packages à l'aide de Turborepo Type: blog post Language: fr-FR Canonical URL: https://simonboisset.com/blog/share-packages-monorepo Published: 10 février 2022 Summary: Plusieurs packages ? Maintenant que nous savons publier un package sur npm, nous allons voir comment gérer une librairie qui comporte plusieurs packages. Vou... Plusieurs packages ? Maintenant que nous savons publier un package sur npm, nous allons voir comment gérer une librairie qui comporte plusieurs packages. Vous pouvez consulter cet exemple: https://github.com/simonboisset/validest pour voir une application concrète. Configuration Pour gérer plusieurs modules ensemble nous allons utiliser Turborepo que vous avez déjà pu voir dans d'un article précédent. npx create-turbo@latest Vous pouvez supprimer les différents exemples de packages de base, nous allons créer les nôtres par la suite. Builder On va commencer par créer un package pour gérer les builds de nos librairies. Celui-ci ne sera pas publié mais uniquement partagé dans notre monorepo. Créez un dossier builder dans packages puis créez les fichiers suivants : package.json { "name": "builder", "private": true, "version": "0.1.0", "bin": "index.js", "dependencies": { "esbuild": "^0.14.42", "esbuild-node-externals": "^1.4.1" } } index.js #!/usr/bin/env node const esbuild = require("esbuild"); const { nodeExternalsPlugin } = require("esbuild-node-externals"); esbuild .build({ entryPoints: ["./src/index.ts"], outfile: "dist/index.js", bundle: true, minify: true, treeShaking: true, platform: "node", format: "cjs", target: "node14", plugins: [nodeExternalsPlugin()], }) .catch(() => process.exit(1)); Nos packages Maintenant nous pouvons créer nos différents packages. Pour l'exemple nous allons en créer deux. Core Toujours dans le dossier packages on va créer un dossier core et les fichiers suivants : package.json { "name": "@example/core", "version": "0.1.0", "description": "A description", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": ["dist"], "scripts": { "build": "rm -rf dist && builder && tsc" }, "author": "Your name", "license": "MIT", "devDependencies": { "builder": "", "typescript": "^4.7.4" } } Vous remarquerez que nous avons utilisé un scope pour notre nom de package @example. Pour utiliser le votre vous devez créer une organisation du même nom sur npm. src/index.ts export const hello = (name: string) => { return "Hello " + name; }; tsconfig.json { "extends": "builder/ts.json", "include": ["src"], "exclude": ["src//.test.ts"], "compilerOptions": { "outDir": "dist", "rootDir": "src", "module": "esnext", "target": "esnext", "lib": ["dom", "dom.iterable", "esnext"], "declaration": true, "strict": true, "moduleResolution": "node", "jsx": "react", "skipLibCheck": true, "esModuleInterop": true, "emitDeclarationOnly": true } } Age Même chose pour notre deuxième package @example/age package.json On ajoute une dependance à core : "dependencies": { "@example/core": "0.1.0" }, index.ts import { hello } from "@example/core"; export const age = (name: string, age: number) => { return hello(name) + " you are " + age; }; Turbo build Nos deux packages sont prêts, il ne reste plus qu'à les builder ensemble. Il faut tout de même paramétrer Turborepo pour builder core avant age. A la racine du repo : turbo.json { "pipeline": { "build": { "dependsOn": ["^build"], "outputs": ["dist/"] } } } package.json { "name": "example", "version": "0.1.0", "private": true, "workspaces": ["packages/"], "scripts": { "build": "turbo run build --filter=@example/" }, "devDependencies": { "prettier": "latest", "turbo": "latest" }, "engines": { "npm": ">=7.0.0", "node": ">=14.0.0" }, "prettier": { "singleQuote": true, "tabWidth": 2, "printWidth": 120 }, "packageManager": "yarn@1.22.5" } On peut déjà exécuter les commandes suivantes pour un premier build : yarn yarn build Test Si vous souhaitez ajouter des tests je vous conseille Vitest: https://vitest.dev/. Vous pouvez suivre la docs de Turborepo: https://turbo.build/repo/docs/handbook/testing à ce sujet. Versionning Une étape un peu complexe est de gérer les bonnes versions des dépendances. Le plus simple est de considérer que tous nos packages possèdent le même numéro de version. On va tout de même devoir utiliser un script pour incrémenter notre numéro de versions sur toutes nos dépendances avant chaque publication. Pour ça on utilise turboversion : yarn add -W -D turboversion Le script sera exécuté avant chaque publication dans notre CI. Pour expliquer rapidement ce qu'il fait, il va scanner tout notre monorepo et incrémenter le numéro de version (patch, minor ou major) des dépendances et packages de notre scope example. Publication On va reprendre notre Github Action de l'article précédent pour l'adapter à notre monorepo : .github/workflows/publish.yml name: Publish on: workflowdispatch: inputs: release: description: "major | minor | patch" required: true default: "patch" type: choice options: major minor patch jobs: publish-new-version: runs-on: ubuntu-latest steps: name: 🔌 Checkout uses: actions/checkout@v3 name: 🏗 Setup Node uses: actions/setup-node@v3 with: node-version: 16.x cache: "yarn" registry-url: https://registry.npmjs.org/ scope: "@example" name: ⏳ Yarn install run: yarn name: 🚀 Publish New Version env: NODEAUTHTOKEN: ${{secrets.NPMTOKEN}} run: | git config --local user.email "youremail" git config --local user.name "yourname" yarn turboversion example ${{github.event.inputs.release}} yarn publish:lib PACKAGEVERSION=$(node -p "require('./package.json').version") git commit -a -m "v${PACKAGEVERSION}" git push Voilà, vous pouvez maintenant publier votre librairie et l'ensemble de ses packages avec un numéro de version cohérent. --- Je suis Simon Boisset, développeur fullstack freelance. Je travaille principalement avec React, React Native et Node.js. Je suis disponible pour des missions de développement ou de conseil. N'hésitez pas à me contacter sur mon site: https://simonboisset.com/. Related links Blog index: https://simonboisset.com/blog Website: https://simonboisset.com/