Rollup을 사용해서 npm 배포하기

시작하기

제작중인 라이브러리를 npm에 배포하는 방법입니다.
webpack 도구와는 조금 다른 rollup을 사용해서 번들링 하도록 하려고 합니다.
webpack을 쓸 수도 있는데 rollup을 쓰는 이유는 webpack은 ES Module 형태로 번들을 할 수 없습니다.
webpack을 사용 할 때에는 일반적으로 commonjs 형태로 번들링을 하게 됩니다
commonjs로 번들링한 라이브러리를 나중에 다른 프로젝트에서 사용하게 되면 Tree-shaking이 지원되지 않습니다.

간단요약

webpack은 웹 어플리케이션을 제작시 주로 사용하고
rollup은 라이브러리 제작시 사용합니다.
lerna는 라이브러리를 패키지 분리할때 사용합니다.
lerna예시로는 @name/blabl, @name/blabla1 와 같이 사용합니다.

설치하기

rollup 패키지를 설치하겠습니다.

1
npm install -D rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup-plugin-terser @rollup/plugin-url @rollup/plugin-image rollup-plugin-typescript2 rollup-plugin-peer-deps-external rollup-plugin-sourcemaps @rollup/plugin-babel @svgr/rollup
  • rollup: rollup 패키지 입니다.
  • @rollup/plugin-node-resolve: node_modules에서 써드파티 모듈을 사용하는 용도로 사용합니다.
  • @rollup/plugin-commonjs: CommonJS 모듈을 ES6으로 변환하는 롤업 플러그인입니다.
  • rollup-plugin-terser: 생성 된 es 번들을 최소화하기위한 롤업 플러그인 후드 아래에서 terser 를 사용합니다.
  • @rollup/plugin-url: 파일을 데이터 URI 또는 ​​ES모듈로 가져오는 롤업 플러그인입니다.
  • @rollup/plugin-image: JPG, PNG, GIF, SVG 및 WebP 파일을 가져 오는 롤업 플러그인입니다.
  • rollup-plugin-typescript2: 타입스크립트를 지원합니다.
  • rollup-plugin-peer-deps-external: peerDependency로 설치된 라이브러리의 코드가 번들링된 결과에 포함되지 않고, import 구문으로 불러와서 사용할 수 있게 해주는 플러그인입니다.
  • rollup-plugin-sourcemaps: rollup으로 번들하기 전에 소스 맵으로 파일을 변환합니다.
  • @rollup/plugin-babel: rollup에서 babel 을 사용 할 수 있게 해주는 플러그인입니다. rollup-plugin-babel은 deprecated되었습니다.

설정하기

패키지가 모두 설치가 완료되었습니다.
이제 rollup 설정을 해보도록 하겠습니다.

peerDependency 설정하기

1
npm i --peer react react-dom styled-components

react, react-dom, styled-components을 peer로 설치해주도록 합니다.

package.json
1
2
3
4
5
"peerDependencies": {
"react": "^16.12.0",
"react-dom": "^16.12.0",
"styled-components": "^5.0.1"
}

rollup.config.js 설정하기

rollup.config.js 파일을 생성후 설정해주도록 합니다

rollup.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import commonjs from "@rollup/plugin-commonjs";
import typescript from "rollup-plugin-typescript2";
import resolve from "@rollup/plugin-node-resolve";
import svgr from "@svgr/rollup";
import image from "@rollup/plugin-image";
import url from "@rollup/plugin-url";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
import sourcemaps from "rollup-plugin-sourcemaps";
import babel from "@rollup/plugin-babel";
import pkg from "./package.json";

const extensions = [".js", ".jsx", ".ts", ".tsx"];
const external = ["react", "react-dom", "styled-components"];
const config = {
input: pkg.source,
output: [
{
sourcemap: true,
file: pkg.main,
format: "cjs",
},
{
sourcemap: true,
file: pkg.module,
format: "esm",
},
external,
plugins: [
resolve({ extensions }),
babel({ exclude: "node_modules/**" }),
commonjs({ include: "node_modules/**" }),
typescript({ tsconfig: "./tsconfig.json", clean: true }),
svgr(),
image(),
url(),
peerDepsExternal(),
sourcemaps(),
del({ targets: ["dist/*"] }),
],
],
};
export default config;

tsconfig.json 설정

tsconfig.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"lib": ["dom", "dom.iterable", "esnext"],
"jsx": "react",
"declaration": true,
"declarationDir": "./dist",
"sourceMap": true,
"outDir": "./dist",
"strict": true,
"noImplicitThis": false /* Raise error on 'this' expressions with an implied 'any' type. */,
"baseUrl": "src",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"],
"exclude": ["node_modules", "dist", "**/*.stories.tsx", "cypress"]
}

package.json 및 모듈 설정

package.json 과 내보낼 모듈을 설정해주어야합니다.

src/index.ts
1
2
3
4
5
6
7
8
import Rfdd from "./view/Rfdd";
import RfddOption from "./view/RfddOption";

export { Rfdd, RfddOption };

// 혹은
export { default as Rfdd } from "./view.Rfdd";
export { default as RfddOption } from "./view/RfddOption";

package.json파일을 수정 하겠습니다.

package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"name": "react-free-dropdown",
"version": "1.0.0",
"description": "Easy custom dropdown list with React",
"main": "dist/react-free-dropdown.js",
"types": "dist/index.d.ts",
"sideEffects": false,
"scripts": {
"build": "rollup -c"
//...
},
"repository": {
"type": "git",
"url": "git+https://github.com/flamingotiger/react-free-dropdown.git"
},
"keywords": ["free", "dropdown", "custom", "react.js", "react"],
"license": "MIT",
"author": "flamingotiger <hhbhong@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/flamingotiger/react-free-dropdown/issues"
},
"files": ["src", "dist"]
//...
}
  • name: 프로젝트(패키지) 이름을 설정합니다.
  • version: 프로젝트(패키지)의 버전을 설정해주도록 합니다.
  • description: 프로젝트(패키지)의 설명을 지정합니다.
  • main: 프로젝트(패키지)의 기본 진입점(entry point)를 지정합니다.
  • module: 프로젝트(패키지)의 기본 진입점(entry point)를 지정합니다.
  • keywords: 프로젝트(패키지)의 키워드를 배열로 지정합니다.
  • types: 타입스크립트 사용시 기본 진입점(entry point)를 지정합니다.
  • license: 프로젝트(패키지) 사용을 허용하는 방법과 제한 사항을 알 수 있도록 라이센스를 지정합니다.

.npmignore

파일 최상단 루트 폴더에 .gitignore처럼 .npmignore파일을 생성해줍니다.
npm에 배포할 필요없는 폴더 혹은 파일을 지정해줍니다.

.npmignore
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
node_modules
yarn-error.log
npm-debug.log
npm-debug.log.*
yarn-error.log
yarn.lock
.DS_Store
.vscode
.idea
.github
stories
storybook-static
.storybook
cypress
cypress.json
.npmignore
.babelrc
.eslintrc
jest.config.js
tsconfig.json
.prettierrc
rollup.config.js
rollup.min.config.js

NPM 배포

이것으로 배포 준비가 완료되었습니다.

npm 로그인

npm 공식 홈페이지로 접속해서 로그인 해주도록 합니다.
로그인 계정이 없다면 회원가입을 해줍니다.
회원가입시 이메일 인증을 필수로 해야합니다.
로그인

npm 배포하기

프로젝트로 돌아와서 npm cli 몇가지를 사용합니다.

1
cd project
1
npm login

npm adduser 혹은 npm login을 입력하고 유저이름 비밀번호 그리고 이메일 인증한 이메일도 입력해야합니다.

로그인이 성공했다면 npm publish 를 입력해줍니다.

1
npm publish

배포에서 다음과 같은 에러가 발생시
npm 배포 에러

1
npm publish --access=public

커맨드를 입력해줍니다.
관련 Github Issue

추가로 로그인이 되어있는지 확인하려면
npm whoami를 입력해주면 확인할 수 있습니다.

로그아웃은 npm logout을 입력하면 됩니다.

이후 사이트를 확인하면 등록이 된 것을 확인할 수 있습니다.
npm 배포완료

Share