Reactコンポーネントをnpmで公開するときにやること

React コンポーネントを webpack、babel等を使ってnpmとして公開するまでの手順。

環境準備

まずはプロジェクトディレクトリを作成して、必要なファイルを作成する(ドットファイルだったりREADMEだったり)。

$ mkdir react-hoge
$ cd react-hoge
$ touch README.md .editorconfig .babelrc .eslintrc webpack.config.babel.js

必要なファイルを作ったらpackage.jsonを作って必要なnpmモジュールをインストールする。

$ yarn init
$ yarn add babel-runtime prop-types
$ yarn add --dev autoprefixer ava babel-cli babel-core babel-eslint .... webpack webpack-dev-server

npmモジュールのインストールが完了したらwebpackの設定をする。

import webpack from 'webpack'
import path from 'path'
import HtmlWebpackPlguin from 'html-webpack-plugin'

export default {
  entry: {
    app: [
      './src/js/app.js',
    ],
  },
  resolve: {
    extensions: ['', 'js', 'jsx'],
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'js/[name].js',
    publicPath: '/',
  },
  resolve: {
    modules: [
      'node_modules',
      path.join(__dirname, 'src'),
    ],
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules)/,
        include: [
          path.resolve(__dirname, 'src/js'),
        ],
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: false,
            },
          },
        ],
      },
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules)/,
        include: [
          path.resolve(__dirname, 'src/js'),
        ],
        use: [
          {
            loader: 'eslint-loader',
            options: {
            },
          },
        ],
      },
      {
        test: /\.scss$/,
        include: path.resolve(__dirname, 'src/scss'),
        use: [
          {
            loader: 'style-loader',
            options: {
              singleton: true,
            },
          },
          {
            loader: 'css-loader',
            options: {
              minimize: true,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: (loader) => [
                require('autoprefixer')(),
              ],
            },
          },
          {
            loader: 'sass-loader',
          },
        ],
      },
    ],
  },
  devServer: {
    contentBase: './dist',
    publicPath: '/',
    port: 8080,
  },
  plugins: [
    new HtmlWebpackPlguin({
      filename: 'index.html',
      inject: false,
      template: './src/templates/index.ejs',
    }),
  ],
}

.babelrcの設定もする。

{
  "presets": [
    [
      "env", {
        "targets": {
          "browsers": ["last 3 versions"],
        },
      }
    ],
    "react"
  ],
  "plugins": [
    "transform-object-assign",
    "transform-class-properties",
    "transform-runtime"
  ],
  "env": {
    "development": {
      "presets": ["react-hmre"],
      "plugins": [
        [
          "react-transform",
        {
          "transforms": [
          {
            "transform": "react-transform-hmr",
            "imports": ["react"],
            "locals": ["module"]
          }
          ]
        }
        ]
      ]
    }
  }
}

原因が良く分かってないけど、postcss.config.jsを用意しないとwebpackでエラーになるので、postcss.config.jsを用意する。

module.exports = {}

srcディレクトリにJSファイル、CSSファイル、ejsファイルを用意する。テスト用にのファイルも作る。

$ mkdir -p src/js/lib src/scss src/templates 
$ touch src/js/app.js
$ touch src/js/lib/index.js
$ touch src/scss/app.scss
$ touch src/templates/index.ejs
$ mkdir __test__
$ touch __test__/index.js

あとはpackage.jsonscriptsを用意して環境の準備は完了。

  "scripts": {
    "start": "NODE_ENV=development webpack-dev-server --inline --hot --color",
    "build": "NODE_ENV=production ./node_modules/.bin/babel ./src/js/lib --out-dir ./lib",
    "docs": "NODE_ENV=production webpack",
    "test": "NODE_ENV=test ava __test__/*.js --verbose",
    "prepublish": "yarn run build"
  },

デモ画面作成 + コンポーネント作成

コンポーネントを確認する用 + github pagesで公開する用のテンプレートを用意して、src/js/app.jsを読み込むようにする。 コンポーネントsrc/js/app.jsimportする。

テンプレートになるsrc/templates/index.ejsに適当なHTMLを用意してsrc/js/app.jsを読み込む。

    <div id="js-app"></div>
    <script src="./js/app.js"></script>

src/js/app.jsの方でsrc/js/lib/配下に用意するコンポーネントimportして使う。

import React from 'react'
import ReactDOM from 'react-dom'
import HogeComponent from './lib'
import '../scss/app.scss'

class App extends React.Component {

  render () {
    return (
       <div>
         <HogeComponent />
       </div>
    )
   }
}

ReactDOM.render(<App />, document.getElementById('js-app'))

あとはコンポーネント./lib/index.jsの方でコンポーネントを作る。

github pagesにデモページを公開

コンポーネントが出来上がったら、github pagesでデモを公開する。 デモページ用のファイルの生成は自動化したいので、travisからビルドしてcommitするようにする。

最初にgithubでアクセストークンを作っておく。 https://github.com/settings/tokens

作ったアクセストークンを暗号化するためのgemをインストールしておく。

$ sudo gem install travis

プロジェクトのルートディレクトリに.travis.ymlを作成する。

$ touch .travis.yml

先ほど入れたgem、travisでアクセストークンを暗号化する。

$ travis encrypt GH_TOKEN="アクセストークン"
Please add the following to your .travis.yml file:

  secure: "..."

Please add the following to your .travis.yml file:の下に表示されるsecure以降を.travis.ymlに貼り付ける。

env:
  secure: "..."

travisからgh-pagesブランチにデモページ用のファイルをコミットするように、shellscriptdocs.shを用意する。

#!/bin/sh

rm -rf gh_pages || exit 0;
mkdir gh_pages
ls -la
cd gh_pages

cp -r ../dist/* .

git init
git config user.name "${GIT_COMITTER_NAME}"
git config user.email "${GIT_COMITTER_EMAIL}"

ls -la
git add .
git commit -m "Deploy to gh-pages"
git push -fq "https://${GH_TOKEN}@github.com/${GH_REPO}.git" master:gh-pages > /dev/null 2>&1

内容としては、./dist配下に生成済みのデモページ用ファイルをgh_pagesディレクトリにコピーして、gh_pagesディレクトリでgit initして、gh_pagesブランチにpushするもの。

最終的に用意する.travis.ymlは以下のような形。after_successscriptが通ったら、yarn run docsでデモページ用ファイルを生成してgh_pagesブランチにコミットするようになる。

language: node_js
env:
  global:
  - GH_REPO="user/reponame"
  - GIT_COMMITTER_NAME=username
  - GIT_COMMITTER_EMAIL=user@example.com
  - GIT_AUTHOR_NAME=username
  - GIT_AUTHOR_EMAIL=user@example.com
  - secure: [暗号化されたtoken]
node_js:
- '6'
- '7'
- '8'
script: yarn run test
after_success:
- npm rebuild node-sass
- test ${TRAVIS_TAG} && yarn run build
- yarn run docs && ./docs.sh
cache:
  directories:
    - node_modules

npm publish

npm scriptsでnpmにpublishする直前にビルドするようにしておく。

  "build": "NODE_ENV=production ./node_modules/.bin/babel ./src/js/lib --out-dir ./lib",
  "prepublish": "yarn run build"

package.jsonのバージョンを任意のバージョンにしたら、gitでtagつけておく。

$ git tag x.x.x
$ git push origin master --tags

tagつけたものがCIで問題なければ、npmに公開する。

$ npm publish

これが全体的な流れ。 README、テスト、changelogなどについては省略してる。