📖

Reactコンポーネントのnpmライブラリを作成してみた

2022/01/09に公開

Reactコンポーネントのnpmライブラリを作成する

storybookを使用してreactコンポーネントの開発をするための環境を整備しました。
作成したコンポーネントはnpmパッケージとして保存して、他プロジェクトで使用できるようにします。
https://storybook.js.org/

ライブラリ作成なので、webpackではなく、rollupを使用しています
https://rollupjs.org/guide/en/

最終的な構成
react-component $ tree -a -I "node_modules|.git" 
.
├── .babelrc
├── .gitignore
├── .npmignore
├── .storybook
│   ├── main.js
│   └── preview.js
├── dist
│   └── index.cjs.js
├── package-lock.json
├── package.json
├── rollup.config.js
└── src
    ├── index.js
    └── stories
        ├── Button.jsx
        ├── Button.stories.jsx
        ├── Header.jsx
        ├── Header.stories.jsx
        ├── Introduction.stories.mdx
        ├── Page.jsx
        ├── Page.stories.jsx
        ├── assets
        │   ├── code-brackets.svg
        │   ├── colors.svg
        │   ├── comments.svg
        │   ├── direction.svg
        │   ├── flow.svg
        │   ├── plugin.svg
        │   ├── repo.svg
        │   └── stackalt.svg
        ├── button.css
        ├── header.css
        └── page.css

5 directories, 28 files

mkdir

mkdir react-component
cd react-component 
npm init -y
mkdir src

git init

git init
touch .gitignore
.gitignore
node_modules

rollup install

npm install rollup --save-dev

babel install

npm install @babel/cli @babel/core @babel/preset-env @babel/preset-react @rollup/plugin-babel --save-dev

runtime install

npm install @babel/runtime
npm install @babel/plugin-transform-runtime --save-dev

rollup plugin install

npm install rollup-plugin-styles autoprefixer --save-dev
npm install --save-dev rollup-plugin-delete

sourcemap install

npm install rollup-plugin-sourcemaps --save-dev

.babelrc

.babelrc
{
  "presets": [
      "@babel/preset-react",
      "@babel/preset-env"
  ]
}

rollup.config.js

rollup.config.js
import styles from "rollup-plugin-styles";
import babel from '@rollup/plugin-babel';
import sourcemaps from 'rollup-plugin-sourcemaps';
import del from 'rollup-plugin-delete';

const autoprefixer = require('autoprefixer');

const conf = {
    input: 'src/index.js',
    output: {
        file: `dist/index.cjs.js`,
        format: "cjs",
        exports: "auto"
    },
    // this externelizes react to prevent rollup from compiling it
    external: ["react", /@babel\/runtime/],
    plugins: [
        // these are babel comfigurations
        babel({
            exclude: 'node_modules/**',
            plugins: ['@babel/transform-runtime'],
            babelHelpers: 'runtime'
        }),
        // this adds sourcemaps
        sourcemaps(),
        del({targets:'dist/*'}),
        // this adds support for styles
        styles({
            postcss: {
                plugins: [
                    autoprefixer()
                ]
            }
        })
    ]
}

export default conf;

storybook install

npx -p @storybook/cli sb init --type react

package.json 編集

package.json
  "main": "dist/index.cjs.js",
  "scripts": {
    "build": "rollup -c",
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook"
  },

index.js作成

import { Header } from './stories/Header.jsx'

const returnLibrary = () => {
    return {
        Header: Header
        // you can add here other components that you want to export
    }
}
export default returnLibrary()

build

npm run build

Error対応

[!] Error: Could not resolve './Button' from src/stories/Header.jsx

Button.jsxの拡張子つけると治る

Header.jsx
import { Button } from './Button.jsx';

つけなくていいはずなのに...原因不明??

警告 対策

react-component $ npm run build

> @marumarumeruru/react-component@1.0.10 build
> rollup -c


src/index.js → dist/index.cjs.js...
(!) Unresolved dependencies
https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency
prop-types (imported by src/stories/Header.jsx, src/stories/Button.jsx)
created dist/index.cjs.js in 569ms

下記の情報を参考に対応できた
https://stackoverflow.com/questions/41495596/rollup-js-unresolved-dependencies

npm install @rollup/plugin-commonjs --save-dev  
npm install @rollup/plugin-node-resolve --save-dev
rollup.config.js編集
import commonjs from '@rollup/plugin-commonjs';
import nodeResolve from '@rollup/plugin-node-resolve';


    plugins: [
    
        nodeResolve({preferBuiltins: false }),
        commonjs(),
    ]

storybook run

react-component $ npm run storybook


サンプルとして、Button、そのButtonを使用したHeader、そのHeaderを使用したPageが表示されている
後で、このHeaderコンポーネントを使ってみる

publish

srcなどは対象外にするために.npmignoreファイルを作成する

.npmignore
## the src folder
src
.babelrc
rollup.config.js
## node modules folder
node_modules
## incase you have a git repositiory initiated
.git
.gitignore
CVS
.svn
.hg
.lock-wscript
.wafpickle-N
.DS_Store
npm-debug.log
.npmrc

config.gypi
package-lock.json

.storybook
storybook-static
npm version patch

#事前にnpm https://www.npmjs.com/ にユーザー登録しておく
npm login
#初回のみpublicであることを明示しないとエラーになる
npm publish --access=public
#2回目以降
npm publish
rollup.config.js 最終形
import commonjs from '@rollup/plugin-commonjs';
import nodeResolve from '@rollup/plugin-node-resolve';
import styles from "rollup-plugin-styles";
import babel from '@rollup/plugin-babel';
import sourcemaps from 'rollup-plugin-sourcemaps';
import del from 'rollup-plugin-delete';


const autoprefixer = require('autoprefixer');

const conf = {
    input: 'src/index.js',
    output: {
        file: `dist/index.cjs.js`,
        format: "cjs",
        exports: "auto"
    },
    // this externelizes react to prevent rollup from compiling it
    external: ["react", /@babel\/runtime/],
    plugins: [
        // these are babel comfigurations
        babel({
            exclude: 'node_modules/**',
            plugins: ['@babel/transform-runtime'],
            babelHelpers: 'runtime'
        }),
        // this adds sourcemaps
        nodeResolve({preferBuiltins: false }),
        commonjs(),
        sourcemaps(),
        del({targets:'dist/*'}),
        // this adds support for styles
        styles({
            postcss: {
                plugins: [
                    autoprefixer()
                ]
            }
        })
    ]
}

export default conf;
package.json 最終形
{
  "name": "@marumarumeruru/react-component",
  "version": "1.0.12",
  "description": "",
  "main": "dist/index.cjs.js",
  "scripts": {
    "build": "rollup -c",
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.16.7",
    "@babel/core": "^7.16.7",
    "@babel/plugin-transform-runtime": "^7.16.7",
    "@babel/preset-env": "^7.16.7",
    "@babel/preset-react": "^7.16.7",
    "@rollup/plugin-babel": "^5.3.0",
    "@rollup/plugin-commonjs": "^21.0.1",
    "@rollup/plugin-node-resolve": "^13.1.3",
    "@storybook/addon-actions": "^6.4.9",
    "@storybook/addon-essentials": "^6.4.9",
    "@storybook/addon-links": "^6.4.9",
    "@storybook/react": "^6.4.9",
    "autoprefixer": "^10.4.2",
    "babel-loader": "^8.2.3",
    "rollup": "^2.63.0",
    "rollup-plugin-delete": "^2.0.0",
    "rollup-plugin-sourcemaps": "^0.6.3",
    "rollup-plugin-styles": "^3.14.1"
  },
  "dependencies": {
    "@babel/runtime": "^7.16.7"
  }
}

作成したパッケージを利用する

reactの別プロジェクトを作成して、Headerコンポーネントを使用してみる

create-react-app

npx create-react-app use-component
cd use-component

npm install

npm install @marumarumeruru/react-component

@marumarumeruru/react-componentの部分は、npmにpublishしたパッケージ名

App.jsx編集

App.jsx
import logo from './logo.svg';
import './App.css';
import component from '@marumarumeruru/react-component';

function App() {
  return (
    <div className="App">
      <component.Header/>
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
        <component.AwesomeButton>Suceess!!</component.AwesomeButton>
      </header>
    </div>
  );
}

export default App;

run

npm start

Headerが表示できた!

link

https://dev.to/jimjunior/how-to-create-an-npm-library-from-react-components-2m2

https://rollupjs.org/guide/en/#creating-your-first-bundle

https://github.com/rollup/awesome

https://storybook.js.org/tutorials/intro-to-storybook/react/ja/get-started/

https://www.npmjs.com/

https://hira.page/blog/201712_taskrunner/

Discussion