icon를 rollup으로 번들링해서 npm publish하자
많은 부분을 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
etc
color가 바뀌려면 아래 처럼 fill이 props.color를 받도록 수정해야 한다.
https://react-svgr.com/docs/options/#replace-attribute-value
svgr({
replaceAttrValues: {
"#121211": "{props.color}",
"black": "{props.color}",
}
})