React : créer une librairie utilisable localement
22 janvier 2023Cela fait un petit moment que je n'ai pas poster d'articles. Ces dernières années j'ai été très occupé. Bref, je pense que je reviendrais dessus un peu plus tard. Revenons à notre sujet principal, à savoir comment créer et utiliser une librairie localement en React ?
Je suis nouveau dans le game React, étant plutôt ciblé Angular à la base. Bien entendu, il y a des avantages et des inconvénients sur les 2 stacks.
L'une des choses qui m'est passé par dessus la tête avec React, c'est la complexité de créer une librairie que l'on peut réutiliser localement. Là ou avec Angular, c'est d'une simplicité.
Bon, je ne dis pas que c'est vraiment compliqué car pour quelqu'un qui maitrise Webpack / Rollup et tous les outils nécessaires à la création d'une librairie, cela doit être assez simple mais à mon niveau, ce n'était pas vraiment le cas.
Je vous donne donc ma solution afin de créer une librairie configurée de manière manuelle.
Configuration
Démarrage
Vous allez devoir initialiser votre package (librairie):
mkdir my-lib
cd my-lib
npm init -y
Organisez ensuite votre package comme suit:
my-lib
|_ src
| |_ components
| |_ index.js
|_ package.json
Installation de Rollup et Babel
Rollup est un packager (comme Webpack) mais très utilisé en React.
Babel est un compiler Javascript.
Exécutez le script suivant pour installer Babel et Rollup:
npm i -D @babel/cli @babel/core @babel/preset-env @babel/preset-react @rollup/plugin-babel @rollup/plugin-typescript rollup rollup-plugin-delete rollup-plugin-dts rollup-plugin-peer-deps-external npm-run-all
npm i -D react react-dom
npm i @testing-library/jest-dom @testing-library/react @testing-library/user-event react-scripts
Dans package.json
, transférez react
et react-dom
de devDependencies
à peerDependencies
:
{
// ...
"devDependencies": {
"@babel/cli": "^7.17.10",
"@babel/core": "^7.17.10",
"@babel/preset-env": "^7.17.10",
"@babel/preset-react": "^7.16.7",
"@rollup/plugin-babel": "^5.3.1",
"@rollup/plugin-typescript": "^8.3.2",
"npm-run-all": "^4.1.5",
"rollup": "^2.72.1",
"rollup-plugin-delete": "^2.0.0",
"rollup-plugin-dts": "^4.2.1",
"rollup-plugin-peer-deps-external": "^2.2.4"
},
"peerDependencies": {
"react": "^18.1.0",
"react-dom": "^18.1.0",
}
}
Ajoutez le fichier .babelrc
:
{
"presets": ["@babel/env", "@babel/preset-react"]
}
Dans package.json
, ajoutez les lignes suivantes:
{
//...
"main": "dist/index.js",
"module": "dist/index.esm.js",
"source": "src/index.ts",
"scripts": {
"build": "rollup -c",
"build-watch": "rollup -c -w",
"start-playground": "cd playground && npm run start",
"i-all": "npm i && cd playground && npm i",
"dev": "npm-run-all --parallel build-watch start-playground"
},
"eslintConfig": {
"extends": [
"react-app"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
//...
}
Ajoutez un fichier rollup.config.js
avec:
import babel from "@rollup/plugin-babel";
import typescript from "@rollup/plugin-typescript";
import external from "rollup-plugin-peer-deps-external";
import del from "rollup-plugin-delete";
import pkg from "./package.json";
import dts from "rollup-plugin-dts";
export default [
{
input: pkg.source,
output: [
{ file: pkg.main, format: "cjs", sourcemap: true },
{ file: pkg.module, format: "esm", sourcemap: true },
],
plugins: [
external(),
babel({
exclude: "node_modules/**",
babelHelpers: "bundled",
}),
del({ targets: ["dist/*"] }),
typescript({ tsconfig: "./tsconfig.json" }),
],
external: Object.keys(pkg.peerDependencies || {}),
},
{
input: "dist/types/index.d.ts",
output: [{ file: "dist/index.d.ts", format: "esm" }],
plugins: [dts(), del({ targets: ["dist/types"], hook: "buildEnd" })],
},
];
Configuration du projet pour Typescript et SCSS
Aujourd'hui, il est courant d'utiliser Typescript et SCSS pour le développement Frontend.
Exécutez les lignes suivante:
npx tsc --init
Dans le fichier tsconfig.json
, ajoutez les configuration suivantes:
{
// ...
"jsx": "react",
"module": "es2022",
"declaration": true,
"sourceMap": true,
"outDir": "./types",
"declarationDir": "./types",
// ...
}
Il ne manque plus qu'a configurer SCSS. Pour cela, exécutez les lignes suivantes:
npm i -D rollup-plugin-scss sass
Dans rollup.config.js
, modifiez comme suit:
// ...
import scss from "rollup-plugin-scss";
// ...
typescript({ tsconfig: "./tsconfig.json" }),
// New
scss()
// ...
plugins: [dts(), del({ targets: ["dist/types"], hook: "buildEnd" })],
// New
external: [/\.css$/, /\.scss$/],
Développement du composant de la librairie
Dans src/components
, ajoutez un fichier Button.tsx
avec:
import React from "react";
export interface ButtonProps {
label: string;
}
const Button = (props: ButtonProps) => {
return <button>{props.label}</button>;
};
export default Button;
Dans src
, ajoutez un fichier index.ts
avec :
export { default as Button } from "./components/Button";
Vous pouvez maintenant compiler votre module avec npm run build
.
Installer et configurer Storybook
Storybook est un outil puissant qui vous permet de prévisualiser le rendu et d'interagir avec votre composant, sans pour autant devoir l'installer dans une application React.
Exécutez les lignes suivante:
npx sb init --builder webpack5
npm i -D storybook-addon-sass-postcss
Il est peut--être nécessaire de forcer l'installation du plugin SASS pour Storybook:
npm i -D storybook-addon-sass-postcss -f
Dans le fichier .storybook/main.js
, ajoutez les lignes suivante:
addons: [
// ...
{
name: "storybook-addon-sass-postcss",
options: {
sassLoaderOptions: {
implementation: require("sass"),
},
},
},
],
Supprimez tout le contenu de my-lib/src/stories
,puis dans ce même dossier, ajoutez le fichier Button.stories.jsx
avec:
import React from "react";
import { default as Button } from "../components/Button";
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
export default {
title: "Components/Button",
component: Button,
// More on argTypes: https://storybook.js.org/docs/react/api/argtypes
argTypes: {
label: {},
},
};
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template = (args) => <Button {...args} />;
export const Main = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
Main.args = {
label: "Button",
};
Vous pouvez maintenant lancer la commande npm run storybook
.
Pour ajouter un peu de style à notre bouton, vous pouvez ajouter le fichier my-lib/src/components/Button.scss
:
.my-button {
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 700;
border: 0;
border-radius: 3em;
cursor: pointer;
display: inline-block;
line-height: 1;
color: white;
background-color: #1ea7fd;
font-size: 14px;
padding: 11px 20px;
}
Puis dans my-lib/src/components/Button.tsx
, modifiez les lignes suivantes:
// ...
// new
import "./Button.scss";
// ...
const Button = (props: ButtonProps) => {
// className is added
return <button className="my-button">{props.label}</button>;
};
Vous pouvez maintenant voir le style modifié dans le storybook.
Utilisation de la librairie dans une Application
Lorsque vous avez développer votre librairie en exposant ce que vous souhaitez utiliser dans votre app, il suffit dans compiler votre lib npm run build
puis dans package.json
de votre app, ajouter les ligne suivante:
{
// ...
"dependencies": {
// ...
"my-lib": "file:../my-lib/dist",
}
}
Et dans votre app, importer votre bouton:
import { Button } from 'my-lib';
Conclusion
C'est un peu long et laborieux surtout pour une personne qui ne maitrise pas tous les outils utilisés mais avec ce guide, vous saurez dorénavant:
- créer une librairie local
- prévisualiser les composants de votre librairie
- utiliser votre librairie
Si vous souhaitez publier votre librairie sur NPM, vous pouvez consulter les guides dans le chapitre Crédits.
J'espère que cet article vous a été utile, et je vous dis à plus pour le prochain !
Crédits
How to Create and Publish a React Component Library
Build a React Component Library
Article précédent Article suivant
Ajouter un commentaire