🎁 Module bundler/🧻 Rollup

icon를 rollup으로 번들링해서 npm publish하자

DarrenKwonDev 2021. 12. 20. 21:39
많은 부분을 velopert의 글을 참고 했다. 사실상 베끼는 것과 다름 없었기에..

https://velog.io/@velopert/bundle-with-rollup-and-publish-to-npm#typescript-declaration-%ED%8C%8C%EC%9D%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0

 

초반 세팅

esm만 지원해도 괜찮은가

https://antfu.me/posts/publish-esm-and-cjs

 

알아둬야 하는 것들

By default, Rollup will expect config files to be ES modules and bundle and transpile them and their relative imports to CommonJS before requiring them. - https://rollupjs.org/guide/en/#using-untranspiled-config-files

 

config-intellisense 세팅

https://rollupjs.org/guide/en/#config-intellisense

 

다음과 같이 자동완성이 되는 것을 확인할 수 있습니다.

 

 

각종 옵션들

 

preserveModules

 

preserveModules : true로 설정된 경우 모듈의 구조를 유지하며 빌드합니다. 아래는 preserveModules에 설정을 하지 않은 default 케이스입니다. 이 경우 모든 파일을 하나의 파일에 몰아서 번들링하기 때문에 tree shaking 등에 불리합니다. (그러나 저는 굳이 이 속성까지 지 할 정도로 크지 않다고 생각해서 그냥 두었습니다.) 

preserveModules를 활성화한 경우 아래와 같이 모듈 구조를 유지하면서 빌드되는 것을 확인할 수 있습니다.

 

 

plugins 구성과 그 사용례들

모든 플러그인은 다음에서 확인 가능하다 https://github.com/rollup/plugins

 

🚨 주의사항

 - 몇 몇 플러그인들은 배치 순서가 중요합니다. 유의미한 차이가 있기 때문입니다. 예를 들어 @rollup/plugin-commonjs와 @rollup/plugin-babel가 동시에 사용될 때, commonjs() 플러그인이 babel() 플러그인보다 상단에 위치해야 합니다.

 

@rollup/plugin-babel

rollup과 babel을 쓰기 위한 플러그인입니다. 

https://github.com/rollup/plugins/tree/master/packages/babel

import { babel } from '@rollup/plugin-babel';

const config = {
  input: 'src/index.js',
  output: {
    dir: 'output',
    format: 'esm'
  },
  plugins: [ 
    babel({ babelHelpers: 'bundled' })
  ]
};

export default config;

 

@rollup/plugin-node-resolve

외부 패키지에 대한 일종의 트리 쉐이킹(이런 용어가 적절한지는 모르겠지만)을 적용하여 우리 모듈을 설치한 이용자들이 패키지 전부를 설치하지 않아도 되도록 조치.

추가로, 대개 node_modules 폴더에 있는 대부분의 패키지는 ESM이 아닌 CJS일 수 있어서 보통 @rollup/plugin-commonjs와 함께 사용됨.

https://github.com/rollup/plugins/tree/master/packages/node-resolve

import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';

export default {
  input: 'main.js',
  output: {
    file: 'bundle.js',
    format: 'iife',
    name: 'MyModule'
  },
  plugins: [nodeResolve(), commonjs()]
};

 

@rollup/plugin-commonjs

CJS 모듈을 ES6로 변환하여 롤업 번들링 과정에 포함될 수 있게 도와줍니다. 앞서 언급하였듯 작성된 파일이 ESM을 따를 것으로 예상하고 있습니다. 

https://github.com/rollup/plugins/tree/master/packages/commonjs

 

 

@rollup/plugin-typescript

https://github.com/rollup/plugins/tree/master/packages/typescript

 

 

@rollup/plugin-url

파일을 데이터 data-URL 형태로 가져오는 롤업 플러그인.

설명이 감이 잘 안올 수 있는데 다음과 같은 꼴로 사용할 수 있게 된다고 보면 된다.

// src/index.js
import svg from './image.svg';
console.log(`svg contents: ${svg}`);
<img src={svg} />

https://github.com/rollup/plugins/tree/master/packages/url/#readme

 

 

공식 plugin은 아니지만 사용하는 것들

rollup-plugin-terser

terser를 활용하여 번들을 minify합니다.

https://github.com/TrySound/rollup-plugin-terser

import { rollup } from "rollup";
import { terser } from "rollup-plugin-terser";

rollup({
  input: "main.js",
  plugins: [terser()],
});

 

rollup-plugin-peer-deps-external

peerDependencies들을 번들링 결과에 포함하지 않고 externailize합니다. 왜 이런 짓을 하느냐? 우리가 만든 라이브러리를 사용하는 사람이 일반적으로 설치할 것으로 예상되는 peerDependencies는 라이브러리에 포함시키지 않는 것이 번들 크기를 줄이고 중복적인 종속성을 방지할 수 있기 때문이다.

https://github.com/pmowrer/rollup-plugin-peer-deps-external#readme

 

일단 아래처럼 짰다.

import { defineConfig } from "rollup";
import commonjs from '@rollup/plugin-commonjs';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import babel from 'rollup-plugin-babel';
import url from '@rollup/plugin-url';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import svgr from '@svgr/rollup';

process.env.BABEL_ENV = 'production'; 

const extensions = ['.js', '.jsx', '.ts', '.tsx']; 

export default defineConfig({
    input: './src/index.ts',
    plugins: [
      peerDepsExternal(),
      nodeResolve({ extensions }),
      commonjs({
        include: 'node_modules/**'
      }),
      babel({ extensions, include: ['src/**/*'], runtimeHelpers: true }), 
      url(),
      svgr() 
    ],
    output: [
      {
        file: "dist/index.js",
        format: 'cjs' 
      },
      {
        file: "dist/index.mjs",
        format: 'es' 
      }
    ]
})

 

 

배포

 

1. tsconfig 수정과 typing

 

tsconfig.json 중 다음 내역을 신경써주자

 

allowJS: false -> true면 d.ts 생성이 안된다고 함. default가 false니까 선언을 안하면 됨

noEmit: false -> true면 출력물을 내보내지 않겠다는 것이다. default가 false니까 선언을 안하면 됨

isolatedModules: false -> true면 export로 독립된 external 모듈을 허용하지 않습니다. default가 false니까 선언을 안하면 됨

"declaration": true -> true를 명시해주자. 
"declarationDir": "프로젝트 빌드 파일의 타이핑 경로", -> 여기에 지정해준 곳에 d.ts 파일이 생기게 됩니다.

tsc --emitDeclarationOnly // 타입 생성

 

2. package.json 수정

 

우리가 application을 만들고 있다면 main을 설정해주지만 우리는 모듈을 만들고 있으므로 module을 설정해주자.

프로젝트의 entry point를 등록해두면 된다.

"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
  ".": {
    "require": "./dist/index.js",
    "import": "./dist/index.mjs",
    "types": "./dist/index.d.ts"
  }
},

 

 

 

3. 배포

npm publish나 yarn publish를 사용할테지만 

좀 더 안전하고 체계적으로 배포를 진행하기 위한 패키지가 존재한다. 

https://github.com/sindresorhus/np

 

GitHub - sindresorhus/np: A better `npm publish`

A better `npm publish`. Contribute to sindresorhus/np development by creating an account on GitHub.

github.com

 

 

 

 

etc

 

 

color가 바뀌려면 아래 처럼 fill이 props.color를 받도록 수정해야 한다.

https://react-svgr.com/docs/options/#replace-attribute-value

svgr({
  replaceAttrValues: {
    "#121211": "{props.color}",
    "black": "{props.color}",
  }
})