rollup.js
는 자바스크립트 모듈을번들링
해주는 번들러이다.
번들링
은 여러 파일로 나누어져 있는 모듈을 하나의 파일로 합치는 것
이다.
rollup.js
는 ES6에서 자바스크립트에 추가된 ES Module
, node.js에서 사용되는 CommonJS
, 그리고 AMD
등의 모듈에 대해 번들링을 지원한다.
기본적으로는 ES Module
을 번들링한다.
rollup은 CLI나 API로도 사용 가능하고, npm scripts로 사용할 수도 있다.
npm i -D rollup
// package.json
{
"scripts": {
"build": "rollup -c" // or "rollup --config"
}
}
보통은 이런 식으로 프로젝트 폴더에 설치하고 config 파일을 작성하는 식으로 사용하는 듯하다.
우선 ES Module 파일들을 하나의 ES Module 파일로 합치거나, CommonJS 파일들을 하나의 CommonJS 파일로 합치는 건 당연히 가능하다.
그리고 ES Module로 작성된 여러 개의 파일들을 하나의 CommonJS 파일로 번들링할 수도 있고, 그 반대도 가능하다.
심지어 여러 종류의 모듈들이 섞인 파일들을 하나의 ES Module로 번들링하는 것도 가능하므로 유연하게 번들링하게 해준다.
유연하게 번들링 가능한 경우들을 예시로 알아보자.
ES Module -> CommonJS
// package.json
{
"type": "module",
"scripts": {
"build": "rollup -c"
},
...
}
// rollup.config.js
export default {
input: "main.js",
output: {
file: "dist.js",
format: "cjs",
},
}
output
의 format
에 CommonJS를 뜻하는 cjs
를 적어줬다.
// main.js | ES Module
import { giveOne } from "./es"
const a = giveOne()
export { a }
// es.js | ES Module
export function giveOne() {
return 1
}
이런 상황일 때 npm run build
를 실행해주면 다음과 같은 파일이 생성된다.
// dist.js | CommonJS
"use strict"
function giveOne() {
return 1
}
const a = giveOne()
exports.a = a
CommonJS -> ES Module
rollup은 기본적으로 ES Module만 번들링할 수 있다.
다른 종류의 모듈을 번들링하려면 플러그인
을 써야 한다.
플러그인은 Awesome Rollup에서 주로 찾을 수 있다고 한다.
그 중에는 CommonJS 모듈을 ES Module로 변환해주는 공식 플러그인 @rollup/plugin-commonjs
도 있다.
그리고 node_modules
에 있는 third party 모듈의 위치를 resolve 해주는 @rollup/plugin-node-resolve
플러그인도 사용해줘야 한다.
npm i -D @rollup/plugin-commonjs @rollup/plugin-node-resolve
// package.json
{
"type": "module",
"scripts": {
"build": "rollup -c"
},
...
}
// rollup.config.js
import commonjs from "@rollup/plugin-commonjs"
import resolve from "@rollup/plugin-node-resolve"
export default {
input: "main.js",
output: {
file: "dist.js",
format: "es",
},
plugins: [resolve(), commonjs()],
}
// main.js | CommonJS
const common = require("./common")
console.log(common)
// common.js | CommonJS
module.exports = {
message: "hi from common",
}
여기서 npm run build
를 실행해주면 다음과 같은 파일이 생성된다.
// dist.js | ES Module
var main = {}
var common$1 = {
message: "hi from common",
}
const common = common$1
console.log(common)
export { main as default }
ES Module & CommonJS -> ES Module
ES Module 파일과 CommonJS 파일을 둘 다 포함시키고,
main.js
에서는 공통적으로 ES Module 형태로 import 해보자.
// package.json
{
"type": "module",
"scripts": {
"build": "rollup -c"
},
...
}
// rollup.config.js
import commonjs from "@rollup/plugin-commonjs"
import resolve from "@rollup/plugin-node-resolve"
export default {
input: "main.js",
output: {
file: "dist.js",
format: "es",
},
plugins: [resolve(), commonjs()],
}
// main.js | ES Module
import { esMsg } from "./es"
import common from "./common"
console.log(common.msg)
console.log(esMsg)
// es.js | ES Module
export const esMsg = "hi from es"
// common.js | CommonJS
module.exports = {
msg: "hi from common",
}
이 상태에서 npm run build
를 실행하면 다음과 같은 파일을 생성한다.
// dist.js | ES Module
const esMsg = "hi from es"
function getDefaultExportFromCjs(x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default")
? x["default"]
: x
}
var common = {
msg: "hi from common",
}
var common$1 = /*@__PURE__*/ getDefaultExportFromCjs(common)
console.log(common$1.msg)
console.log(esMsg)
여기서는 ES Module과 CommonJS 파일이 섞여 있어도 제대로 번들링 된다는 걸 알 수 있다.
config 파일에서 내보내는 객체
의 프로퍼티
와 값
에 어떤 게 올 수 있는지는 이 문서를 보면 자세히 알 수 있다.
그리고 그 중에 output.format
부분을 보면, 다음과 같은 포맷의 파일로 번들링 할 수 있다는 걸 알 수 있다.
amd, cjs, es, iife, umd, system
실제로 사용되지 않는 코드를 제외
시키는 걸 자바스크립트 쪽에서 Tree Shaking
이라고 부른다고 한다.
rollup
은 Tree Shaking을 지원한다.
즉, import 하는 코드들을 정적으로(static) 분석해서 실제로 사용되지 않는 코드를 제외시킨다.
간단한 예시를 짜보면 다음과 같다.
// rollup.config.js
export default {
input: "main.js",
output: {
file: "dist.js",
format: "es",
},
}
// main.js
import { sayHi, notUsedA } from "./a"
import { sayBye, notUsedB } from "./b"
sayHi()
sayBye()
// a.js
export function sayHi() {
console.log("hi")
}
export function notUsedA() {
console.log("not used")
}
// b.js
export function sayBye() {
console.log("bye")
}
export function notUsedB() {
console.log("not used")
}
여기서 npm run build
(rollup -c
)를 실행해주면 다음과 같은 파일이 생성된다.
// dist.js
function sayHi() {
console.log("hi")
}
function sayBye() {
console.log("bye")
}
sayHi()
sayBye()
사용되지 않은 코드(
notUsedA
,notUsedB
)들은 번들링 될 때 모두 제외되었다는 걸 알 수 있다.
entry point가 여러 개인 config 파일은 다음처럼 작성할 수 있다고 한다.
// rollup.config.js
export default [
{
input: "a.js",
output: {
file: "dist/a.js",
format: "es",
},
},
{
input: "b.js",
output: {
file: "dist/b.js",
format: "es",
},
},
]
번들이 크고 복잡해질수록 더욱 다양하고 유연한 기능들이 요구된다.
npm으로 설치한 모듈 사용하기, Babel로 transpile 하기, JSON 파일 사용하기 등등의 확장된 기능들은 플러그인
을 통해 얻을 수 있다.
플러그인은 Awesome Rollup
에서 주로 찾을 수 있다고 한다.
사용하는 방법은 config 파일에서 plugins
프로퍼티의 값에 배열로 담아주면 되는듯하다.
import json from "@rollup/plugin-json"
export default {
input: "main.js",
output: {
file: "dist.js",
format: "es",
},
plugins: [json()],
}
이제 이 문서를 바탕으로 실제 플러그인이 어떤 식으로 구성되는지 조금 알아보자.
Rollup plugin은 객체이며, 프로퍼티
, 빌드 hook
, output generation hook
중에 하나 이상을 갖는다고 한다.
그리고 그 중 핵심은 빌드 hook
인듯하다.
Hooks는 번들링
의 여러 스테이지에서 실행되는 함수들
이라고 한다.
플러그인 객체
에 지정된 이름의 함수를 작성하면, 번들링의 해당 스테이지에서 함수를 실행해주는 방식인듯하다.
options
->buildStart
->resolveId
-> ... ->transform
-> ...
구체적인 예시로, @rollup/plugin-json
의 src/index.js
코드를 보면 대충 이런 식의 틀로 구성되어 있다 :
export default function json() {
return {
name: "json",
transform() {},
}
}
transform
은 빌드 hook 이름이다.
config 파일에서는 plugins: [json()]
이런 식으로 사용되고, json
함수가 돌려주는 게 플러그인 객체인듯하다.