├── .editorconfig ├── .github └── workflows │ ├── autofix.yml │ └── ci.yml ├── .gitignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.config.ts ├── eslint.config.mjs ├── examples ├── 1.zero-config │ ├── README.md │ ├── package.json │ └── src │ │ ├── index.ts │ │ └── utils.ts ├── 2.mkdist │ ├── README.md │ ├── build.config.ts │ ├── package.json │ └── src │ │ ├── index.ts │ │ └── plugins │ │ ├── vite.ts │ │ └── webpack.ts ├── 3.untyped │ ├── .gitignore │ ├── README.md │ ├── build.config.ts │ ├── package.json │ └── src │ │ └── index.ts └── README.md ├── package.json ├── pnpm-lock.yaml ├── renovate.json ├── src ├── auto.ts ├── build.ts ├── builders │ ├── copy │ │ ├── index.ts │ │ └── types.ts │ ├── mkdist │ │ ├── index.ts │ │ └── types.ts │ ├── rollup │ │ ├── build.ts │ │ ├── config.ts │ │ ├── index.ts │ │ ├── plugins │ │ │ ├── cjs.ts │ │ │ ├── esbuild.ts │ │ │ ├── json.ts │ │ │ ├── raw.ts │ │ │ └── shebang.ts │ │ ├── stub.ts │ │ ├── types.ts │ │ ├── utils.ts │ │ └── watch.ts │ └── untyped │ │ ├── index.ts │ │ └── types.ts ├── cli.ts ├── index.ts ├── types.ts ├── utils.ts └── validate.ts ├── test ├── auto.test.ts ├── fixture │ ├── bin │ │ └── cli.mjs │ ├── build.config.ts │ ├── build.preset.ts │ ├── package.json │ └── src │ │ ├── index.mts │ │ ├── nested │ │ └── subpath.ts │ │ ├── runtime │ │ └── foo.ts │ │ ├── schema.ts │ │ └── test.json ├── utils.test.ts └── validate.test.ts ├── tsconfig.json └── vitest.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | charset = utf-8 8 | 9 | [*.js] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [{package.json,*.yml,*.cjson}] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.github/workflows/autofix.yml: -------------------------------------------------------------------------------- 1 | name: autofix.ci # needed to securely identify the workflow 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: ["main"] 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | autofix: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - run: npm i -g -f corepack && corepack enable 17 | - uses: actions/setup-node@v4 18 | with: 19 | node-version: 22 20 | cache: "pnpm" 21 | - run: pnpm install 22 | - name: Fix lint issues 23 | run: pnpm run lint:fix 24 | - uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef 25 | with: 26 | commit-message: "chore: apply automated updates" 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | ci: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - run: npm i -g -f corepack && corepack enable 17 | - uses: actions/setup-node@v4 18 | with: 19 | node-version: 22 20 | cache: "pnpm" 21 | - run: pnpm install 22 | - run: pnpm lint 23 | - run: pnpm test:types 24 | - run: pnpm build 25 | - run: pnpm vitest run --coverage 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | *.log 4 | .DS_Store 5 | coverage 6 | dist 7 | types 8 | .gen 9 | .nyc_output 10 | .idea/ 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## v3.5.0 6 | 7 | [compare changes](https://github.com/unjs/unbuild/compare/v3.4.1...v3.5.0) 8 | 9 | ### 🚀 Enhancements 10 | 11 | - Use `fix-dts-default-cjs-exports` to transform cjs types ([#513](https://github.com/unjs/unbuild/pull/513)) 12 | 13 | ### 🏡 Chore 14 | 15 | - **release:** V3.4.1 ([b408ac0](https://github.com/unjs/unbuild/commit/b408ac0)) 16 | - Updated untyped to v2 ([24997cc](https://github.com/unjs/unbuild/commit/24997cc)) 17 | - **release:** V3.4.2 ([045ebf2](https://github.com/unjs/unbuild/commit/045ebf2)) 18 | 19 | ### ❤️ Contributors 20 | 21 | - Joaquín Sánchez ([@userquin](https://github.com/userquin)) 22 | - Pooya Parsa ([@pi0](https://github.com/pi0)) 23 | 24 | ## v3.4.2 25 | 26 | [compare changes](https://github.com/unjs/unbuild/compare/v3.4.1...v3.4.2) 27 | 28 | ### 🏡 Chore 29 | 30 | - Updated untyped to v2 ([24997cc](https://github.com/unjs/unbuild/commit/24997cc)) 31 | 32 | ### ❤️ Contributors 33 | 34 | - Pooya Parsa ([@pi0](https://github.com/pi0)) 35 | 36 | ## v3.4.1 37 | 38 | [compare changes](https://github.com/unjs/unbuild/compare/v3.4.0...v3.4.1) 39 | 40 | ### 🩹 Fixes 41 | 42 | - Filter commonjs plugin when generating declaration ([#495](https://github.com/unjs/unbuild/pull/495)) 43 | 44 | ### ❤️ Contributors 45 | 46 | - Kricsleo ([@kricsleo](https://github.com/kricsleo)) 47 | 48 | ## v3.4.0 49 | 50 | [compare changes](https://github.com/unjs/unbuild/compare/v3.3.1...v3.4.0) 51 | 52 | ### 🚀 Enhancements 53 | 54 | - Prefer `publishConfig` from `package.json` when defined ([#506](https://github.com/unjs/unbuild/pull/506)) 55 | 56 | ### 🩹 Fixes 57 | 58 | - Workaroud the `composite` in `tsconfig.json` ([#504](https://github.com/unjs/unbuild/pull/504)) 59 | 60 | ### 📦 Build 61 | 62 | - Remove extra dist files ([c0c00ad](https://github.com/unjs/unbuild/commit/c0c00ad)) 63 | 64 | ### 🏡 Chore 65 | 66 | - Update deps ([60154b1](https://github.com/unjs/unbuild/commit/60154b1)) 67 | - Update dependencies ([2448af1](https://github.com/unjs/unbuild/commit/2448af1)) 68 | - Update pnpm ([b43bdf1](https://github.com/unjs/unbuild/commit/b43bdf1)) 69 | - Update `JavaScript` case in pkg description ([#493](https://github.com/unjs/unbuild/pull/493)) 70 | - Update exports field ([0b0d90c](https://github.com/unjs/unbuild/commit/0b0d90c)) 71 | - Update build config ([61054fe](https://github.com/unjs/unbuild/commit/61054fe)) 72 | 73 | ### ❤️ Contributors 74 | 75 | - Pooya Parsa ([@pi0](https://github.com/pi0)) 76 | - Kricsleo ([@kricsleo](https://github.com/kricsleo)) 77 | - @beer ([@iiio2](https://github.com/iiio2)) 78 | 79 | ## v3.3.1 80 | 81 | [compare changes](https://github.com/unjs/unbuild/compare/v3.3.0...v3.3.1) 82 | 83 | ### 🩹 Fixes 84 | 85 | - **rollup:** Improve external detection ([#488](https://github.com/unjs/unbuild/pull/488)) 86 | 87 | ### ❤️ Contributors 88 | 89 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 90 | 91 | ## v3.3.0 92 | 93 | [compare changes](https://github.com/unjs/unbuild/compare/v3.2.0...v3.3.0) 94 | 95 | ### 🚀 Enhancements 96 | 97 | - Allow specifying dependencies to inline in `inlineDependencies` ([#480](https://github.com/unjs/unbuild/pull/480)) 98 | 99 | ### 🩹 Fixes 100 | 101 | - **rollup:** Resolve aliases using pathe utils ([#483](https://github.com/unjs/unbuild/pull/483)) 102 | 103 | ### 💅 Refactors 104 | 105 | - Inline `withTrailingSlash` util ([#482](https://github.com/unjs/unbuild/pull/482)) 106 | 107 | ### 🏡 Chore 108 | 109 | - Update deps ([edc8784](https://github.com/unjs/unbuild/commit/edc8784)) 110 | 111 | ### ❤️ Contributors 112 | 113 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 114 | - Kricsleo ([@kricsleo](http://github.com/kricsleo)) 115 | - Daniel Roe ([@danielroe](http://github.com/danielroe)) 116 | 117 | ## v3.2.0 118 | 119 | [compare changes](https://github.com/unjs/unbuild/compare/v3.1.0...v3.2.0) 120 | 121 | ### 🚀 Enhancements 122 | 123 | - **mkdist:** Fail build if `mkdist` errors in creating declarations ([#473](https://github.com/unjs/unbuild/pull/473)) 124 | 125 | ### 🩹 Fixes 126 | 127 | - Correct `.d.cts` default export type ([#458](https://github.com/unjs/unbuild/pull/458)) 128 | 129 | ### 🏡 Chore 130 | 131 | - Update deps ([4536320](https://github.com/unjs/unbuild/commit/4536320)) 132 | 133 | ### ❤️ Contributors 134 | 135 | - Kricsleo ([@kricsleo](http://github.com/kricsleo)) 136 | - Daniel Roe ([@danielroe](http://github.com/danielroe)) 137 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 138 | 139 | ## v3.1.0 140 | 141 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.1...v3.1.0) 142 | 143 | ### 🚀 Enhancements 144 | 145 | - Support parallel builds ([af19b1b](https://github.com/unjs/unbuild/commit/af19b1b)) 146 | - Infer externals from package `name`, `exports` and `imports` ([#469](https://github.com/unjs/unbuild/pull/469)) 147 | - **rollup:** Resolve with `production` condition ([#470](https://github.com/unjs/unbuild/pull/470)) 148 | 149 | ### 🩹 Fixes 150 | 151 | - Resolve preset on directory ([#465](https://github.com/unjs/unbuild/pull/465)) 152 | - Only externalize `@types/` from `devDependencies` ([#471](https://github.com/unjs/unbuild/pull/471)) 153 | 154 | ### 🏡 Chore 155 | 156 | - Remove unused imports ([#463](https://github.com/unjs/unbuild/pull/463)) 157 | - Apply automated updates ([f724382](https://github.com/unjs/unbuild/commit/f724382)) 158 | 159 | ### ❤️ Contributors 160 | 161 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 162 | - Sunny-117 163 | - Christian Preston ([@cpreston321](http://github.com/cpreston321)) 164 | - Sunny ([@Sunny-117](http://github.com/Sunny-117)) 165 | 166 | ## v3.0.1 167 | 168 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.0...v3.0.1) 169 | 170 | ### 🩹 Fixes 171 | 172 | - **rollup:** De-default default export from stub ([#455](https://github.com/unjs/unbuild/pull/455)) 173 | 174 | ### 🏡 Chore 175 | 176 | - Update changeloge.md ([577b841](https://github.com/unjs/unbuild/commit/577b841)) 177 | 178 | ### ❤️ Contributors 179 | 180 | - Daniel Roe ([@danielroe](http://github.com/danielroe)) 181 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 182 | 183 | ## v3.0.0 184 | 185 | See [github release notes](https://github.com/unjs/unbuild/releases/edit/v3.0.0). 186 | 187 | ## v3.0.0-rc.11 188 | 189 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.0-rc.10...v3.0.0-rc.11) 190 | 191 | ### 🩹 Fixes 192 | 193 | - **untyped:** Use schema module default export if is the only export ([cc26726](https://github.com/unjs/unbuild/commit/cc26726)) 194 | 195 | ### ❤️ Contributors 196 | 197 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 198 | 199 | ## v3.0.0-rc.10 200 | 201 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.0-rc.9...v3.0.0-rc.10) 202 | 203 | ### 🚀 Enhancements 204 | 205 | - Add `--config` to the CLI ([#440](https://github.com/unjs/unbuild/pull/440)) 206 | 207 | ### 🩹 Fixes 208 | 209 | - Update to jiti 2.3 ([9147c3e](https://github.com/unjs/unbuild/commit/9147c3e)) 210 | - Untyped declaration output is optional ([5820182](https://github.com/unjs/unbuild/commit/5820182)) 211 | 212 | ### 📖 Documentation 213 | 214 | - Add more usage info ([#401](https://github.com/unjs/unbuild/pull/401)) 215 | 216 | ### 🏡 Chore 217 | 218 | - Update deps ([c5e0b89](https://github.com/unjs/unbuild/commit/c5e0b89)) 219 | - Remove extra default check ([b380758](https://github.com/unjs/unbuild/commit/b380758)) 220 | - Enable automd ([2ea153a](https://github.com/unjs/unbuild/commit/2ea153a)) 221 | - Update mkdist ([877906f](https://github.com/unjs/unbuild/commit/877906f)) 222 | 223 | ### ❤️ Contributors 224 | 225 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 226 | - 阿菜 Cai <1064425721@qq.com> 227 | - Joaquín Sánchez ([@userquin](http://github.com/userquin)) 228 | 229 | ## v3.0.0-rc.9 230 | 231 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.0-rc.8...v3.0.0-rc.9) 232 | 233 | ### 🩹 Fixes 234 | 235 | - **stub:** Enable `interopDefault` by default ([8e6f7e4](https://github.com/unjs/unbuild/commit/8e6f7e4)) 236 | - **config:** Only take default export ([fefafec](https://github.com/unjs/unbuild/commit/fefafec)) 237 | 238 | ### 🏡 Chore 239 | 240 | - Remove unused dep ([1a65aef](https://github.com/unjs/unbuild/commit/1a65aef)) 241 | - Update dependencies ([f7ab6ce](https://github.com/unjs/unbuild/commit/f7ab6ce)) 242 | - Lint ([262a35a](https://github.com/unjs/unbuild/commit/262a35a)) 243 | 244 | ### ❤️ Contributors 245 | 246 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 247 | 248 | ## v3.0.0-rc.8 249 | 250 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.0-rc.7...v3.0.0-rc.8) 251 | 252 | ### 🩹 Fixes 253 | 254 | - Normalize resolved path ([2640083](https://github.com/unjs/unbuild/commit/2640083)) 255 | 256 | ### 💅 Refactors 257 | 258 | - Replace fast-glob with tinyglobby ([#426](https://github.com/unjs/unbuild/pull/426)) 259 | 260 | ### 📖 Documentation 261 | 262 | - Fix tiny typo ([#414](https://github.com/unjs/unbuild/pull/414)) 263 | 264 | ### 🏡 Chore 265 | 266 | - Update dependencies ([2f815ef](https://github.com/unjs/unbuild/commit/2f815ef)) 267 | 268 | ### ❤️ Contributors 269 | 270 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 271 | - Superchupu ([@SuperchupuDev](http://github.com/SuperchupuDev)) 272 | - @beer ([@iiio2](http://github.com/iiio2)) 273 | 274 | ## v3.0.0-rc.7 275 | 276 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.0-rc.6...v3.0.0-rc.7) 277 | 278 | ### 💅 Refactors 279 | 280 | - Replace `globby` w/ `fast-glob` ([#418](https://github.com/unjs/unbuild/pull/418)) 281 | 282 | ### 🏡 Chore 283 | 284 | - Removed resolved @ts-expect-error ([733a914](https://github.com/unjs/unbuild/commit/733a914)) 285 | - Update deps ([6e3be15](https://github.com/unjs/unbuild/commit/6e3be15)) 286 | 287 | ### ❤️ Contributors 288 | 289 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 290 | - Sukka 291 | 292 | ## v3.0.0-rc.6 293 | 294 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.0-rc.5...v3.0.0-rc.6) 295 | 296 | ### 🩹 Fixes 297 | 298 | - **untyped:** Use custom jiti instance ([00ded57](https://github.com/unjs/unbuild/commit/00ded57)) 299 | 300 | ### 🏡 Chore 301 | 302 | - Eslint ignore `test/fixture/dist` ([66a8db3](https://github.com/unjs/unbuild/commit/66a8db3)) 303 | 304 | ### ❤️ Contributors 305 | 306 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 307 | 308 | ## v3.0.0-rc.5 309 | 310 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.0-rc.4...v3.0.0-rc.5) 311 | 312 | ### 🩹 Fixes 313 | 314 | - **rollup:** Keep empty type-only modules ([7a6469b](https://github.com/unjs/unbuild/commit/7a6469b)) 315 | 316 | ### ❤️ Contributors 317 | 318 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 319 | 320 | ## v3.0.0-rc.4 321 | 322 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.0-rc.3...v3.0.0-rc.4) 323 | 324 | ### 🩹 Fixes 325 | 326 | - **rollup:** Keep empty (type-only) modules ([a9158e2](https://github.com/unjs/unbuild/commit/a9158e2)) 327 | 328 | ### ❤️ Contributors 329 | 330 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 331 | 332 | ## v3.0.0-rc.3 333 | 334 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.0-rc.2...v3.0.0-rc.3) 335 | 336 | ### 🚀 Enhancements 337 | 338 | - Upgrade to jiti v2 beta ([#409](https://github.com/unjs/unbuild/pull/409)) 339 | 340 | ### 🩹 Fixes 341 | 342 | - Type `RollupOptions.plugins` as array ([62fa930](https://github.com/unjs/unbuild/commit/62fa930)) 343 | - Add all loader extensions to esbuild include ([8ab22ff](https://github.com/unjs/unbuild/commit/8ab22ff)) 344 | - Enable jiti `interopDefault` for config loader and internals (ref: #409) ([#409](https://github.com/unjs/unbuild/issues/409)) 345 | 346 | ### 💅 Refactors 347 | 348 | - Overhaul builders structure ([#410](https://github.com/unjs/unbuild/pull/410)) 349 | - Add explicit return types ([#412](https://github.com/unjs/unbuild/pull/412)) 350 | - Use `colors` from `consola/utils` ([6a8f36d](https://github.com/unjs/unbuild/commit/6a8f36d)) 351 | 352 | ### 🏡 Chore 353 | 354 | - Upgrade `@rollup/plugin-commonjs` to 26 ([9392ec3](https://github.com/unjs/unbuild/commit/9392ec3)) 355 | - Update deps ([d886ad5](https://github.com/unjs/unbuild/commit/d886ad5)) 356 | - Update release script ([8ce4090](https://github.com/unjs/unbuild/commit/8ce4090)) 357 | - Remove unused imports ([04c2b5c](https://github.com/unjs/unbuild/commit/04c2b5c)) 358 | - Add explicit type for var exports ([af26e40](https://github.com/unjs/unbuild/commit/af26e40)) 359 | - Stricter type checks ([#413](https://github.com/unjs/unbuild/pull/413)) 360 | - Add publishTag to release script ([577484c](https://github.com/unjs/unbuild/commit/577484c)) 361 | 362 | ### ❤️ Contributors 363 | 364 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 365 | 366 | ## v3.0.0-rc.2 367 | 368 | [compare changes](https://github.com/unjs/unbuild/compare/v3.0.0-rc.1...v3.0.0-rc.2) 369 | 370 | ### 🚀 Enhancements 371 | 372 | - Support `copy` builder ([#389](https://github.com/unjs/unbuild/pull/389)) 373 | - Experimental active watcher for rollup ([#364](https://github.com/unjs/unbuild/pull/364)) 374 | - **rollup:** Add `.mts` and `.cts` to supported extensions ([633ffe9](https://github.com/unjs/unbuild/commit/633ffe9)) 375 | - Support custom jiti plugins for stub mode ([#368](https://github.com/unjs/unbuild/pull/368)) 376 | 377 | ### 🩹 Fixes 378 | 379 | - Generate stub types of with explicit extension import if pkg type is `module` ([#371](https://github.com/unjs/unbuild/pull/371)) 380 | - Correct dts generation for stub mode ([#314](https://github.com/unjs/unbuild/pull/314)) 381 | - **rollup:** Handle aliases when checking for externals ([#384](https://github.com/unjs/unbuild/pull/384)) 382 | - **rollup:** Update `importAttributesKey` to `with` ([27bcba8](https://github.com/unjs/unbuild/commit/27bcba8)) 383 | 384 | ### 📖 Documentation 385 | 386 | - Add more jsdocs ([#363](https://github.com/unjs/unbuild/pull/363)) 387 | - Add examples ([#334](https://github.com/unjs/unbuild/pull/334)) 388 | 389 | ### 🏡 Chore 390 | 391 | - **release:** V3.0.0-rc.1 ([0419efa](https://github.com/unjs/unbuild/commit/0419efa)) 392 | - Update prerelease script ([94615cf](https://github.com/unjs/unbuild/commit/94615cf)) 393 | - Fix typo in comments ([#393](https://github.com/unjs/unbuild/pull/393)) 394 | - Update eslint to v9 ([f085607](https://github.com/unjs/unbuild/commit/f085607)) 395 | - Update dependencies ([5de86d7](https://github.com/unjs/unbuild/commit/5de86d7)) 396 | - Apply automated lint fixes ([225338e](https://github.com/unjs/unbuild/commit/225338e)) 397 | - Update ci ([8b39cfd](https://github.com/unjs/unbuild/commit/8b39cfd)) 398 | 399 | ### 🤖 CI 400 | 401 | - Test against node v18 ([7bc5321](https://github.com/unjs/unbuild/commit/7bc5321)) 402 | 403 | ### ❤️ Contributors 404 | 405 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 406 | - S3xysteak ([@s3xysteak](http://github.com/s3xysteak)) 407 | - Weng <157215725@qq.com> 408 | - ZHAO Jin-Xiang 409 | - Joe McKenney ([@joemckenney](http://github.com/joemckenney)) 410 | - Estéban 411 | - Yasuhiro SHIMIZU 412 | - 阿菜 Cai <1064425721@qq.com> 413 | - Marco Solazzi 414 | - Shoma-mano ([@shoma-mano](http://github.com/shoma-mano)) 415 | - Daniel Roe ([@danielroe](http://github.com/danielroe)) 416 | 417 | ## v3.0.0-rc.1 418 | 419 | [compare changes](https://github.com/unjs/unbuild/compare/v2.0.0...v3.0.0-rc.1) 420 | 421 | ### 🚀 Enhancements 422 | 423 | - ⚠️ Upgrade to rollup v4 ([#327](https://github.com/unjs/unbuild/pull/327)) 424 | - Support disabling `preserveDynamicImports` ([#322](https://github.com/unjs/unbuild/pull/322)) 425 | - **rollup:** ⚠️ Default to `esnext` build target ([#335](https://github.com/unjs/unbuild/pull/335)) 426 | 427 | ### 🩹 Fixes 428 | 429 | - Don't clean root directory if set as `outDir` ([#343](https://github.com/unjs/unbuild/pull/343)) 430 | 431 | ### 💅 Refactors 432 | 433 | - Use `jiti.import` for esm stubs and improve templates ([#300](https://github.com/unjs/unbuild/pull/300)) 434 | 435 | ### 📖 Documentation 436 | 437 | - Update jsdocs for `inferEntries` ([#310](https://github.com/unjs/unbuild/pull/310)) 438 | 439 | ### 🏡 Chore 440 | 441 | - Update dependencies ([83ea4f0](https://github.com/unjs/unbuild/commit/83ea4f0)) 442 | - Update dependencies ([4d69ebe](https://github.com/unjs/unbuild/commit/4d69ebe)) 443 | - Add prerelease script ([9c48ca4](https://github.com/unjs/unbuild/commit/9c48ca4)) 444 | 445 | #### ⚠️ Breaking Changes 446 | 447 | - ⚠️ Upgrade to rollup v4 ([#327](https://github.com/unjs/unbuild/pull/327)) 448 | - **rollup:** ⚠️ Default to `esnext` build target ([#335](https://github.com/unjs/unbuild/pull/335)) 449 | 450 | ### ❤️ Contributors 451 | 452 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 453 | - Daniel Roe 454 | - Anthony Fu 455 | - Brendon Matos 456 | - Estéban ([@Barbapapazes](http://github.com/Barbapapazes)) 457 | - 翠 / Green 458 | 459 | ## v2.0.0 460 | 461 | [compare changes](https://github.com/unjs/unbuild/compare/v2.0.0-rc.0...v2.0.0) 462 | 463 | ### 🏡 Chore 464 | 465 | - Update dependencies ([33b20a4](https://github.com/unjs/unbuild/commit/33b20a4)) 466 | - Update depedencies ([8d8a3d7](https://github.com/unjs/unbuild/commit/8d8a3d7)) 467 | 468 | ### 🤖 CI 469 | 470 | - Use conventional commit for autofix ([#294](https://github.com/unjs/unbuild/pull/294)) 471 | 472 | ### ❤️ Contributors 473 | 474 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 475 | - Daniel Roe 476 | 477 | ## v2.0.0-rc.0 478 | 479 | [compare changes](https://github.com/unjs/unbuild/compare/v1.2.1...v2.0.0-rc.0) 480 | 481 | ### 🚀 Enhancements 482 | 483 | - Generate `.d.mts` and `.d.cts` declarations ([#273](https://github.com/unjs/unbuild/pull/273)) 484 | - Support multiple build configs ([#275](https://github.com/unjs/unbuild/pull/275)) 485 | - Support nested subpaths ([#280](https://github.com/unjs/unbuild/pull/280)) 486 | - Allow customizing jiti options for stub entries ([#282](https://github.com/unjs/unbuild/pull/282)) 487 | - Support aliases for stub mode ([#283](https://github.com/unjs/unbuild/pull/283)) 488 | - Migrate to `unjs/citty` ([#284](https://github.com/unjs/unbuild/pull/284)) 489 | - **cli:** Support `--minify` ([#285](https://github.com/unjs/unbuild/pull/285)) 490 | - Sourcemap support ([#286](https://github.com/unjs/unbuild/pull/286)) 491 | 492 | ### 🩹 Fixes 493 | 494 | - Respect directory separator when matching entries ([#214](https://github.com/unjs/unbuild/pull/214)) 495 | - **esbuild:** Typo in `sourcemap` option ([#259](https://github.com/unjs/unbuild/pull/259)) 496 | - Remove extensions from default output name ([#279](https://github.com/unjs/unbuild/pull/279)) 497 | - **rollup:** Check module id against externals as well ([#270](https://github.com/unjs/unbuild/pull/270)) 498 | - **esbuild:** Apply minify with corresponding loader ([#254](https://github.com/unjs/unbuild/pull/254)) 499 | - **esbuild:** Do not minify declarations ([83b3ed2](https://github.com/unjs/unbuild/commit/83b3ed2)) 500 | - **mkdist:** Allow passing all possible options ([f40a889](https://github.com/unjs/unbuild/commit/f40a889)) 501 | - Clean dist directories only once for multi builds ([11c47c8](https://github.com/unjs/unbuild/commit/11c47c8)) 502 | - **auto:** Avoid warning for existing files ([#287](https://github.com/unjs/unbuild/pull/287)) 503 | 504 | ### 💅 Refactors 505 | 506 | - **rollup:** ⚠️ Improve esbuild options handling ([#278](https://github.com/unjs/unbuild/pull/278)) 507 | 508 | ### 📦 Build 509 | 510 | - ⚠️ Make typescript a peer dependency ([f31c6a4](https://github.com/unjs/unbuild/commit/f31c6a4)) 511 | 512 | ### 🏡 Chore 513 | 514 | - **release:** V1.2.1 ([968612b](https://github.com/unjs/unbuild/commit/968612b)) 515 | - Update badges ([#260](https://github.com/unjs/unbuild/pull/260)) 516 | - Update all non major dependencies ([9518aa8](https://github.com/unjs/unbuild/commit/9518aa8)) 517 | - Update dev dependencies ([a446556](https://github.com/unjs/unbuild/commit/a446556)) 518 | - Lint code ([87e5035](https://github.com/unjs/unbuild/commit/87e5035)) 519 | - Fix lint issue ([b711242](https://github.com/unjs/unbuild/commit/b711242)) 520 | - Lint code ([6ac2e9f](https://github.com/unjs/unbuild/commit/6ac2e9f)) 521 | - Add autofix ci ([075b7ad](https://github.com/unjs/unbuild/commit/075b7ad)) 522 | - Format with prettier v3 ([40ec1d9](https://github.com/unjs/unbuild/commit/40ec1d9)) 523 | - Update mkdist ([1234f75](https://github.com/unjs/unbuild/commit/1234f75)) 524 | - Enable declaration for min + sourcemap fixture ([7dcda3f](https://github.com/unjs/unbuild/commit/7dcda3f)) 525 | 526 | #### ⚠️ Breaking Changes 527 | 528 | - **rollup:** ⚠️ Improve esbuild options handling ([#278](https://github.com/unjs/unbuild/pull/278)) 529 | - ⚠️ Make typescript a peer dependency ([f31c6a4](https://github.com/unjs/unbuild/commit/f31c6a4)) 530 | 531 | ### ❤️ Contributors 532 | 533 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 534 | - Sukka 535 | - Wang Zhi ([@yfwz100](http://github.com/yfwz100)) 536 | - Patryk Tomczyk 537 | - JounQin ([@JounQin](http://github.com/JounQin)) 538 | - ZHAO Jin-Xiang 539 | - LitoMore 540 | - 科科 541 | 542 | ## v1.2.1 543 | 544 | [compare changes](https://github.com/unjs/unbuild/compare/v1.2.0...v1.2.1) 545 | 546 | ### 💅 Refactors 547 | 548 | - Update to consola v3 ([3bb25b2](https://github.com/unjs/unbuild/commit/3bb25b2)) 549 | 550 | ### ❤️ Contributors 551 | 552 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 553 | 554 | ## v1.2.0 555 | 556 | [compare changes](https://github.com/unjs/unbuild/compare/v1.1.2...v1.2.0) 557 | 558 | ### 🚀 Enhancements 559 | 560 | - **rollup:** Add `jsx` and `tsx` to esbuild loader defaults ([#198](https://github.com/unjs/unbuild/pull/198)) 561 | - **rollup:** Allow regular expressions in `externals` array ([#145](https://github.com/unjs/unbuild/pull/145)) 562 | - **mkdist:** Add new `pattern` option ([#139](https://github.com/unjs/unbuild/pull/139)) 563 | - Support esbuild `charset` option ([#190](https://github.com/unjs/unbuild/pull/190)) 564 | - **rollup:** Allow passing any all common esbuild options ([8e81e2a](https://github.com/unjs/unbuild/commit/8e81e2a)) 565 | - **rollup:** Show size of bundled npm packages in cli output ([#243](https://github.com/unjs/unbuild/pull/243)) 566 | 567 | ### 🩹 Fixes 568 | 569 | - Pass missing esbuild jsx factory options ([#224](https://github.com/unjs/unbuild/pull/224)) 570 | - **rollup:** Handle array format for `rollup.alias.entries` option ([#220](https://github.com/unjs/unbuild/pull/220)) 571 | - **rollup:** Enable `interop: compat` for cjs compatibility ([#215](https://github.com/unjs/unbuild/pull/215)) 572 | 573 | ### 📖 Documentation 574 | 575 | - Add `types` to default `exports` ([#226](https://github.com/unjs/unbuild/pull/226)) 576 | - Remove `types` field suggestion for now ([e8988ae](https://github.com/unjs/unbuild/commit/e8988ae)) 577 | 578 | ### 🏡 Chore 579 | 580 | - Update lockfile ([cc99946](https://github.com/unjs/unbuild/commit/cc99946)) 581 | - Fix lint issues ([ee1ced8](https://github.com/unjs/unbuild/commit/ee1ced8)) 582 | - Recreate lockfile with pnpm 8 ([06d0044](https://github.com/unjs/unbuild/commit/06d0044)) 583 | 584 | ### ❤️ Contributors 585 | 586 | - Pooya Parsa ([@pi0](http://github.com/pi0)) 587 | - Kid 588 | - Ntnyq ([@ntnyq](http://github.com/ntnyq)) 589 | - Marco Solazzi 590 | - Zoeyzhao19 591 | - Dunqing ([@Dunqing](http://github.com/Dunqing)) 592 | - XLor 593 | 594 | ## v1.1.2 595 | 596 | [compare changes](https://github.com/unjs/unbuild/compare/v1.1.1...v1.1.2) 597 | 598 | ### 💅 Refactors 599 | 600 | - Use `fs.mkdir` instead of `mkdirp` ([6ee6384](https://github.com/unjs/unbuild/commit/6ee6384)) 601 | - Use `fs.mkdir` instead of `mkdirp` ([8e6962e](https://github.com/unjs/unbuild/commit/8e6962e)) 602 | 603 | ### 🏡 Chore 604 | 605 | - Update dependencies ([87bea65](https://github.com/unjs/unbuild/commit/87bea65)) 606 | 607 | ### ❤️ Contributors 608 | 609 | - Pooya Parsa 610 | 611 | ## v1.1.1 612 | 613 | [compare changes](https://github.com/unjs/unbuild/compare/v1.1.0...v1.1.1) 614 | 615 | ### 🩹 Fixes 616 | 617 | - Use fs instead of rimraf ([bbd374d](https://github.com/unjs/unbuild/commit/bbd374d)) 618 | 619 | ### ❤️ Contributors 620 | 621 | - Pooya Parsa 622 | 623 | ## v1.1.0 624 | 625 | [compare changes](https://github.com/unjs/unbuild/compare/v1.0.2...v1.1.0) 626 | 627 | ### 🚀 Enhancements 628 | 629 | - Update all rollup dependencies ([3d1b976](https://github.com/unjs/unbuild/commit/3d1b976)) 630 | 631 | ### 🩹 Fixes 632 | 633 | - Prevent minification of `.d.ts` files ([#185](https://github.com/unjs/unbuild/pull/185)) 634 | 635 | ### 🎨 Styles 636 | 637 | - Format with prettier ([818ced7](https://github.com/unjs/unbuild/commit/818ced7)) 638 | 639 | ### ❤️ Contributors 640 | 641 | - Pooya Parsa 642 | - Marco Solazzi 643 | 644 | ## v1.0.2 645 | 646 | [compare changes](https://github.com/unjs/unbuild/compare/v1.0.1...v1.0.2) 647 | 648 | ### 🩹 Fixes 649 | 650 | - Do not throw error on absolute windows paths ([#166](https://github.com/unjs/unbuild/pull/166)) 651 | 652 | ### 🏡 Chore 653 | 654 | - Remove unused dependency ([79cc03d](https://github.com/unjs/unbuild/commit/79cc03d)) 655 | 656 | ### ❤️ Contributors 657 | 658 | - Pooya Parsa 659 | - Daniel Roe 660 | 661 | ### [1.0.1](https://github.com/unjs/unbuild/compare/v1.0.0...v1.0.1) (2022-11-16) 662 | 663 | ## [1.0.0](https://github.com/unjs/unbuild/compare/v0.9.4...v1.0.0) (2022-11-16) 664 | 665 | ### [0.8.9](https://github.com/unjs/unbuild/compare/v0.8.8...v0.8.9) (2022-08-18) 666 | 667 | ### Bug Fixes 668 | 669 | - **rollup:** remove wrong context ([67d1d38](https://github.com/unjs/unbuild/commit/67d1d38e976e5e72c79ac49155f0047c6a8ff0ee)) 670 | 671 | ### [0.8.8](https://github.com/unjs/unbuild/compare/v0.8.7...v0.8.8) (2022-08-11) 672 | 673 | ### Bug Fixes 674 | 675 | - **rollup:** seperate dynamic chunks from shared chunk names ([aaf8227](https://github.com/unjs/unbuild/commit/aaf8227692f5c11b93ed034d5528fb69f255601f)) 676 | 677 | ### [0.8.7](https://github.com/unjs/unbuild/compare/v0.8.6...v0.8.7) (2022-08-10) 678 | 679 | ### Bug Fixes 680 | 681 | - include all files for total size report ([7bce76d](https://github.com/unjs/unbuild/commit/7bce76ddbc52151452c0bbc59b5d809850ed024c)) 682 | 683 | ### [0.8.6](https://github.com/unjs/unbuild/compare/v0.8.5...v0.8.6) (2022-08-10) 684 | 685 | ### [0.8.5](https://github.com/unjs/unbuild/compare/v0.8.4...v0.8.5) (2022-08-10) 686 | 687 | ### Features 688 | 689 | - `name` option defaulting to package name ([c3979ab](https://github.com/unjs/unbuild/commit/c3979abde77cd7393d4c4e7fa231a3496923e4aa)) 690 | - **rollup:** use hashed chunk names ([850b013](https://github.com/unjs/unbuild/commit/850b013da4da6463be84b552889a507f9d6e1085)) 691 | 692 | ### Bug Fixes 693 | 694 | - **rollup:** unmark chunk names as external imports ([59debad](https://github.com/unjs/unbuild/commit/59debad1cfb5a6fcb31f032b11d82ff11eafa519)) 695 | 696 | ### [0.8.4](https://github.com/unjs/unbuild/compare/v0.8.3...v0.8.4) (2022-08-10) 697 | 698 | ### Bug Fixes 699 | 700 | - **rollup:** handle stubbing multiple exports ([f215525](https://github.com/unjs/unbuild/commit/f2155253e9a2cd9d33e552006d31acc9355b72e7)) 701 | 702 | ### [0.8.3](https://github.com/unjs/unbuild/compare/v0.8.2...v0.8.3) (2022-08-10) 703 | 704 | ### Bug Fixes 705 | 706 | - **rollup:** escape stub import paths ([63a3c11](https://github.com/unjs/unbuild/commit/63a3c1163fa7bdd26cfbd9ea0cc06acf27919d3c)) 707 | - **rollup:** fix stub type hints ([38d95be](https://github.com/unjs/unbuild/commit/38d95be7c9295052553c441a746ed78385d164fd)) 708 | - **rollup:** normalize stub entry path ([f7272e0](https://github.com/unjs/unbuild/commit/f7272e09f866bf02372597a151c1b2f686f2cd5b)) 709 | 710 | ### [0.8.2](https://github.com/unjs/unbuild/compare/v0.8.1...v0.8.2) (2022-08-10) 711 | 712 | ### Bug Fixes 713 | 714 | - **rollup:** fix stub export resolution issues ([63961e4](https://github.com/unjs/unbuild/commit/63961e4009264d73763bea402728509e82bf0c13)) 715 | 716 | ### [0.8.1](https://github.com/unjs/unbuild/compare/v0.8.0...v0.8.1) (2022-08-10) 717 | 718 | ### Bug Fixes 719 | 720 | - update mlly ([26bc33c](https://github.com/unjs/unbuild/commit/26bc33c8bbcff5a3e12a197deeb70acdf7c379ec)) 721 | 722 | ## [0.8.0](https://github.com/unjs/unbuild/compare/v0.7.6...v0.8.0) (2022-08-10) 723 | 724 | ### ⚠ BREAKING CHANGES 725 | 726 | - always enable `esmResolve` for `jiti` (stub and config) 727 | - exit with code (1) on build warnings (#98) 728 | 729 | ### Features 730 | 731 | - always enable `esmResolve` for `jiti` (stub and config) ([b87c8df](https://github.com/unjs/unbuild/commit/b87c8df9bbd27081bba00dfb43f9e75cb540990c)) 732 | - exit with code (1) on build warnings ([#98](https://github.com/unjs/unbuild/issues/98)) ([ffc0d7c](https://github.com/unjs/unbuild/commit/ffc0d7c63f53e531eb66c6ea3d32cfe144ecd988)) 733 | - **rollup:** generated named exports in esm stub ([c9fce24](https://github.com/unjs/unbuild/commit/c9fce24d5ad1c987d469846715b668c855435280)) 734 | 735 | ### [0.7.6](https://github.com/unjs/unbuild/compare/v0.7.5...v0.7.6) (2022-07-20) 736 | 737 | ### Bug Fixes 738 | 739 | - **pkg:** use `default` export condition to avoid breaking change ([858d35d](https://github.com/unjs/unbuild/commit/858d35de754dcc76f55e3ba8d01945be9c6e9bee)), closes [#89](https://github.com/unjs/unbuild/issues/89) 740 | 741 | ### [0.7.5](https://github.com/unjs/unbuild/compare/v0.7.4...v0.7.5) (2022-07-20) 742 | 743 | ### Features 744 | 745 | - enable `esmResolve` when loading `build.config` ([#93](https://github.com/unjs/unbuild/issues/93)) ([c856812](https://github.com/unjs/unbuild/commit/c856812ef28ba2aac3d57030b4db087d0369cd6c)) 746 | 747 | ### Bug Fixes 748 | 749 | - **pkg:** add types field for exports ([#89](https://github.com/unjs/unbuild/issues/89)) ([457f043](https://github.com/unjs/unbuild/commit/457f0434b43b9e16cbbe1b54c61bfc5dcaf666a6)) 750 | - properly calculate bytes of output size ([#82](https://github.com/unjs/unbuild/issues/82)) ([1888978](https://github.com/unjs/unbuild/commit/1888978944ae3384585e7da54ba0a4c0f55e9eb2)) 751 | 752 | ### [0.7.4](https://github.com/unjs/unbuild/compare/v0.7.3...v0.7.4) (2022-04-13) 753 | 754 | ### Bug Fixes 755 | 756 | - **deps:** update mlly ([c935845](https://github.com/unjs/unbuild/commit/c935845905cac9db007c6c3442557c6c84057460)) 757 | 758 | ### [0.7.3](https://github.com/unjs/unbuild/compare/v0.7.2...v0.7.3) (2022-04-12) 759 | 760 | ### Bug Fixes 761 | 762 | - resolve asbolute path to jiti for pnpm support ([#58](https://github.com/unjs/unbuild/issues/58)) ([81d6da7](https://github.com/unjs/unbuild/commit/81d6da7894533b020c6a2c4a5991eecce1824b18)) 763 | - **stub:** use `file://` protocol for windows compatibility ([12a99c1](https://github.com/unjs/unbuild/commit/12a99c1fa2bb8ce9341cb9a62bd1faf8b8f265e0)) 764 | - work around issue building with pnpm ([#57](https://github.com/unjs/unbuild/issues/57)) ([eb7da84](https://github.com/unjs/unbuild/commit/eb7da84b5665ef11d1942ec7c70820d46d20f905)) 765 | 766 | ### [0.7.2](https://github.com/unjs/unbuild/compare/v0.7.1...v0.7.2) (2022-03-25) 767 | 768 | ### Bug Fixes 769 | 770 | - revert builtins from default externals ([0b808c6](https://github.com/unjs/unbuild/commit/0b808c603d1686afe8352a7e279d6c254b62a29c)) 771 | 772 | ### [0.7.1](https://github.com/unjs/unbuild/compare/v0.7.0...v0.7.1) (2022-03-25) 773 | 774 | ### Bug Fixes 775 | 776 | - add `builtins` and `node:` prefixes to externals ([2af233d](https://github.com/unjs/unbuild/commit/2af233d16797ffdf331c3c311b874d41ab75dac8)) 777 | 778 | ## [0.7.0](https://github.com/unjs/unbuild/compare/v0.6.9...v0.7.0) (2022-03-09) 779 | 780 | ### ⚠ BREAKING CHANGES 781 | 782 | - update all dependencies 783 | 784 | ### Features 785 | 786 | - make rollup-plugin-dts options configurable ([#52](https://github.com/unjs/unbuild/issues/52)) ([e710503](https://github.com/unjs/unbuild/commit/e710503cda58a2691a6ed04abe91c346dae66d21)) 787 | - update all dependencies ([fc7c164](https://github.com/unjs/unbuild/commit/fc7c1646f13adbc3fcaa9397e25951d39fb5dc21)) 788 | 789 | ### [0.6.9](https://github.com/unjs/unbuild/compare/v0.6.8...v0.6.9) (2022-01-27) 790 | 791 | ### Features 792 | 793 | - allow peerDeps as import entry points ([#43](https://github.com/unjs/unbuild/issues/43)) ([755981d](https://github.com/unjs/unbuild/commit/755981dc98a17898ab45d6d794eac3023b7d0298)) 794 | 795 | ### [0.6.8](https://github.com/unjs/unbuild/compare/v0.6.7...v0.6.8) (2022-01-21) 796 | 797 | ### Bug Fixes 798 | 799 | - **rollup:** ensure output subdirectory exists ([#40](https://github.com/unjs/unbuild/issues/40)) ([b964655](https://github.com/unjs/unbuild/commit/b96465550f89b9d0a1354b7ed315659dfa653917)) 800 | 801 | ### [0.6.7](https://github.com/unjs/unbuild/compare/v0.6.6...v0.6.7) (2021-12-14) 802 | 803 | ### Features 804 | 805 | - **rollup:** replace and per-plugin configuration ([6d185b2](https://github.com/unjs/unbuild/commit/6d185b260ce40099380fed5f1dd9d123c2459cf0)) 806 | 807 | ### [0.6.6](https://github.com/unjs/unbuild/compare/v0.6.5...v0.6.6) (2021-12-14) 808 | 809 | ### Bug Fixes 810 | 811 | - update mkdist ([46272a1](https://github.com/unjs/unbuild/commit/46272a1339763116cdb8366b655f5a0c3421dd4e)) 812 | 813 | ### [0.6.5](https://github.com/unjs/unbuild/compare/v0.6.4...v0.6.5) (2021-12-14) 814 | 815 | ### Bug Fixes 816 | 817 | - fix nested config types to be optional ([d48e0ac](https://github.com/unjs/unbuild/commit/d48e0ac48eb460daacb51843078cee73993c8243)) 818 | 819 | ### [0.6.4](https://github.com/unjs/unbuild/compare/v0.6.3...v0.6.4) (2021-12-14) 820 | 821 | ### Features 822 | 823 | - auto config preset ([#30](https://github.com/unjs/unbuild/issues/30)) ([fe1ac94](https://github.com/unjs/unbuild/commit/fe1ac940d6c3617e8d9679a7f458c991c69eaafa)) 824 | - resolve tsx and jsx files ([#31](https://github.com/unjs/unbuild/issues/31)) ([03d7056](https://github.com/unjs/unbuild/commit/03d705617177ef0efbab0f0b6a5c6b7bb3f062b5)) 825 | - support alias and esbuild config ([#29](https://github.com/unjs/unbuild/issues/29)) ([b0b09e0](https://github.com/unjs/unbuild/commit/b0b09e04b35487090109e2a76e6b9b3ef3e449ff)) 826 | - validate build outputs against `package.json` ([#33](https://github.com/unjs/unbuild/issues/33)) ([c9ce0b0](https://github.com/unjs/unbuild/commit/c9ce0b0bc5e1e38734c72393f15cd28516703e19)) 827 | 828 | ### Bug Fixes 829 | 830 | - external peerDependencies ([#32](https://github.com/unjs/unbuild/issues/32)) ([ba82fcb](https://github.com/unjs/unbuild/commit/ba82fcb5dd84dfdf3b3ab5e435e0c269c8659e0c)) 831 | 832 | ### [0.6.3](https://github.com/unjs/unbuild/compare/v0.6.2...v0.6.3) (2021-12-03) 833 | 834 | ### Bug Fixes 835 | 836 | - shebang support ([#28](https://github.com/unjs/unbuild/issues/28)) ([cb8c4a2](https://github.com/unjs/unbuild/commit/cb8c4a2b65f5eecdb8cef6fd8dab092e3e4a72ab)) 837 | 838 | ### [0.6.2](https://github.com/unjs/unbuild/compare/v0.6.1...v0.6.2) (2021-12-03) 839 | 840 | ### Features 841 | 842 | - shebang support ([12ccc15](https://github.com/unjs/unbuild/commit/12ccc15d42a360995280aec057e44cb1bfba0c87)), closes [#27](https://github.com/unjs/unbuild/issues/27) 843 | 844 | ### [0.6.1](https://github.com/unjs/unbuild/compare/v0.6.0...v0.6.1) (2021-12-03) 845 | 846 | ### Features 847 | 848 | - allow programmatic inputConfig ([9493837](https://github.com/unjs/unbuild/commit/9493837636f203657d5166eba277eee83e959e21)) 849 | 850 | ### Bug Fixes 851 | 852 | - `hooks` type in config should be partial ([62fd953](https://github.com/unjs/unbuild/commit/62fd9534bb82b69bd97091ff45a9947546e18d9a)) 853 | - register programmatic hooks ([867ebc5](https://github.com/unjs/unbuild/commit/867ebc52dd7654cb0e41a61d6833461c178d2a28)) 854 | - resolve outDir relative to rootDir ([ba18055](https://github.com/unjs/unbuild/commit/ba18055a42a939a692e063cb95ad18f559bbb5c4)) 855 | - show error if code is not `MODULE_NOT_FOUND` ([82d9432](https://github.com/unjs/unbuild/commit/82d9432ae29bc61459b71b3809868c8b6af26946)) 856 | - use tryRequire for package.json ([31ab840](https://github.com/unjs/unbuild/commit/31ab840c56ca807577082d8216bee87ef23d977f)) 857 | 858 | ## [0.6.0](https://github.com/unjs/unbuild/compare/v0.5.13...v0.6.0) (2021-12-03) 859 | 860 | ### ⚠ BREAKING CHANGES 861 | 862 | - extract rollup specific global options 863 | 864 | ### Features 865 | 866 | - `mkdist:` hooks ([46f8b96](https://github.com/unjs/unbuild/commit/46f8b966242f06a9fc8f321795c4dc9c8ea32b5c)) 867 | - `rollup:` hooks ([43cb0da](https://github.com/unjs/unbuild/commit/43cb0da484380d9208bd28a0f39bbcb6ae8ae113)) 868 | - auto external peerDeps ([#25](https://github.com/unjs/unbuild/issues/25)) ([f629d74](https://github.com/unjs/unbuild/commit/f629d747c2e525cc21416fe177dd243417276be0)) 869 | - basic preset support ([1c0f772](https://github.com/unjs/unbuild/commit/1c0f772259a4216e5f3e922144312b3d3cc5b7aa)) 870 | - support `build:before` and `build:after` hooks ([c834e56](https://github.com/unjs/unbuild/commit/c834e56765510346f2c2901176cf09a0e3b1a39f)) 871 | - support `unbuild` key from `package.json` ([ee16f56](https://github.com/unjs/unbuild/commit/ee16f56e9927c2c61fbee8db8e8c369b9e066902)) 872 | - untyped hooks ([e5ddb8e](https://github.com/unjs/unbuild/commit/e5ddb8ed0542e3a18be21090993392a200d529dc)) 873 | 874 | ### Bug Fixes 875 | 876 | - **stub:** re-export `default` in dts stub ([#26](https://github.com/unjs/unbuild/issues/26)) ([468347f](https://github.com/unjs/unbuild/commit/468347f39e9c270c865975b3bde46dbd753d94f2)) 877 | 878 | - extract rollup specific global options ([0dfb39f](https://github.com/unjs/unbuild/commit/0dfb39fcdf90a5842ffd56440860ef25b058cdb4)) 879 | 880 | ### [0.5.13](https://github.com/unjs/unbuild/compare/v0.5.12...v0.5.13) (2021-11-18) 881 | 882 | ### Features 883 | 884 | - upgrade to untyped 0.3.x ([a7f50b8](https://github.com/unjs/unbuild/commit/a7f50b8e292a06998197b739de1af67dfc061c40)) 885 | 886 | ### [0.5.12](https://github.com/unjs/unbuild/compare/v0.5.11...v0.5.12) (2021-11-18) 887 | 888 | ### Features 889 | 890 | - raw import support ([0af3032](https://github.com/unjs/unbuild/commit/0af30328571e4d9864211e52fa0bb2f4542571f3)) 891 | 892 | ### Bug Fixes 893 | 894 | - de-default rollup-plugin-esbuild (resolves [#23](https://github.com/unjs/unbuild/issues/23)) ([e88fc72](https://github.com/unjs/unbuild/commit/e88fc72b0b18f2041ec0501db5402625079e62cf)) 895 | 896 | ### [0.5.11](https://github.com/unjs/unbuild/compare/v0.5.10...v0.5.11) (2021-10-22) 897 | 898 | ### Bug Fixes 899 | 900 | - allow cjs for ext in types ([e878d1d](https://github.com/unjs/unbuild/commit/e878d1d4fb8ab808367df52e2a536db1da104c68)) 901 | 902 | ### [0.5.10](https://github.com/unjs/unbuild/compare/v0.5.9...v0.5.10) (2021-10-21) 903 | 904 | ### Bug Fixes 905 | 906 | - **rollup:** use cjs compatible json exports ([06d7b9d](https://github.com/unjs/unbuild/commit/06d7b9d2aedbb75d56a9e11bc31fd2ee0551a09d)), closes [nuxt/framework#1335](https://github.com/nuxt/framework/issues/1335) 907 | 908 | ### [0.5.9](https://github.com/unjs/unbuild/compare/v0.5.8...v0.5.9) (2021-10-21) 909 | 910 | ### Features 911 | 912 | - use json plugin to allow treeshaking ([562e4d1](https://github.com/unjs/unbuild/commit/562e4d1110d3d0675f5205874f5088cbaa3dc3d6)) 913 | 914 | ### [0.5.8](https://github.com/unjs/unbuild/compare/v0.5.7...v0.5.8) (2021-10-20) 915 | 916 | ### [0.5.7](https://github.com/unjs/unbuild/compare/v0.5.6...v0.5.7) (2021-10-13) 917 | 918 | ### [0.5.6](https://github.com/unjs/unbuild/compare/v0.5.5...v0.5.6) (2021-10-01) 919 | 920 | ### Features 921 | 922 | - cjsBridge and emitCJS ([74a64c1](https://github.com/unjs/unbuild/commit/74a64c100a983ba5965c3177194261fd5d595b7d)) 923 | 924 | ### [0.5.5](https://github.com/unjs/unbuild/compare/v0.5.4...v0.5.5) (2021-09-29) 925 | 926 | ### Bug Fixes 927 | 928 | - **rollup:** don't ignore built-in requires to transform into import ([00c6b21](https://github.com/unjs/unbuild/commit/00c6b2103168793c359e2e1fcf18e45ce064982c)) 929 | 930 | ### [0.5.4](https://github.com/unjs/unbuild/compare/v0.5.3...v0.5.4) (2021-09-21) 931 | 932 | ### [0.5.3](https://github.com/unjs/unbuild/compare/v0.5.2...v0.5.3) (2021-09-21) 933 | 934 | ### Bug Fixes 935 | 936 | - enable back interopDefault for esm stub for now ([079bb51](https://github.com/unjs/unbuild/commit/079bb51b6e985e989f61ca58234142cdef45bca8)) 937 | 938 | ### [0.5.2](https://github.com/unjs/unbuild/compare/v0.5.1...v0.5.2) (2021-09-21) 939 | 940 | ### Bug Fixes 941 | 942 | - avoid interopDefault for esm stub ([6a36080](https://github.com/unjs/unbuild/commit/6a360801afbf04d3889d2abe8acfac6b709fc094)) 943 | 944 | ### [0.5.1](https://github.com/unjs/unbuild/compare/v0.5.0...v0.5.1) (2021-09-21) 945 | 946 | ### Bug Fixes 947 | 948 | - **rollup:** add `interopDefault` for jiti stub ([b1bf926](https://github.com/unjs/unbuild/commit/b1bf9266413bed041ef38693ef735ce754955895)) 949 | 950 | ## [0.5.0](https://github.com/unjs/unbuild/compare/v0.4.2...v0.5.0) (2021-09-20) 951 | 952 | ### ⚠ BREAKING CHANGES 953 | 954 | - use `.cjs` extension 955 | 956 | ### Features 957 | 958 | - use `.cjs` extension ([a1c8c0f](https://github.com/unjs/unbuild/commit/a1c8c0fb9d93dad57e876e3876e91854cb321612)) 959 | 960 | ### Bug Fixes 961 | 962 | - **pkg:** use explicit .cjs extension ([909a539](https://github.com/unjs/unbuild/commit/909a5395714525a883cf1d9f91b9f7c4b3f1621a)) 963 | - **rollup:** stub esm with jiti for typescript support ([6bab21f](https://github.com/unjs/unbuild/commit/6bab21f366600d071a0a182d88968023380ff3ec)) 964 | 965 | ### [0.4.2](https://github.com/unjs/unbuild/compare/v0.4.1...v0.4.2) (2021-08-09) 966 | 967 | ### Bug Fixes 968 | 969 | - **stub:** use junction links for windows support ([56aaf4b](https://github.com/unjs/unbuild/commit/56aaf4b9101b708015cc5c38cdaf11c598c5bf77)) 970 | 971 | ### [0.4.1](https://github.com/unjs/unbuild/compare/v0.4.0...v0.4.1) (2021-07-29) 972 | 973 | ## [0.4.0](https://github.com/unjs/unbuild/compare/v0.3.2...v0.4.0) (2021-07-22) 974 | 975 | ### ⚠ BREAKING CHANGES 976 | 977 | - upgrade `untyped` (#14) 978 | 979 | - upgrade `untyped` ([#14](https://github.com/unjs/unbuild/issues/14)) ([caa57c9](https://github.com/unjs/unbuild/commit/caa57c931e652ac60c8cf8aa0f46739c6749e549)) 980 | 981 | ### [0.3.2](https://github.com/unjs/unbuild/compare/v0.3.1...v0.3.2) (2021-07-08) 982 | 983 | ### Bug Fixes 984 | 985 | - don't warn for every src file in windows ([#10](https://github.com/unjs/unbuild/issues/10)) ([e4d18aa](https://github.com/unjs/unbuild/commit/e4d18aa020533810dc297acda923a4edcfce9114)) 986 | 987 | ### [0.3.1](https://github.com/unjs/unbuild/compare/v0.3.0...v0.3.1) (2021-06-16) 988 | 989 | ### Bug Fixes 990 | 991 | - change esbuild target to es2019 ([a91ec26](https://github.com/unjs/unbuild/commit/a91ec26abef272a30e94f0568b0004cf9351c19c)) 992 | 993 | ## [0.3.0](https://github.com/unjs/unbuild/compare/v0.2.3...v0.3.0) (2021-05-24) 994 | 995 | ### ⚠ BREAKING CHANGES 996 | 997 | - update dependencies 998 | 999 | - update dependencies ([293dd7f](https://github.com/unjs/unbuild/commit/293dd7fc4383b5608dc09a20359840369030142e)) 1000 | 1001 | ### [0.2.3](https://github.com/unjs/unbuild/compare/v0.2.2...v0.2.3) (2021-04-23) 1002 | 1003 | ### Features 1004 | 1005 | - pass ext option to mkdist ([2c9a513](https://github.com/unjs/unbuild/commit/2c9a513014bab7d5b0cab67e6952647c085edf11)) 1006 | 1007 | ### [0.2.2](https://github.com/unjs/unbuild/compare/v0.2.1...v0.2.2) (2021-04-21) 1008 | 1009 | ### Bug Fixes 1010 | 1011 | - **mkdist:** rmdir before stubbing ([1ca50a4](https://github.com/unjs/unbuild/commit/1ca50a499f692f9d0cfdac4b6384836fb292af93)) 1012 | 1013 | ### [0.2.1](https://github.com/unjs/unbuild/compare/v0.2.0...v0.2.1) (2021-04-21) 1014 | 1015 | ### Bug Fixes 1016 | 1017 | - avoid duplicate name in dist ([cbc8f55](https://github.com/unjs/unbuild/commit/cbc8f55cbdd2df29d328a44b773118ec6e00a079)) 1018 | 1019 | ## [0.2.0](https://github.com/unjs/unbuild/compare/v0.1.13...v0.2.0) (2021-04-21) 1020 | 1021 | ### ⚠ BREAKING CHANGES 1022 | 1023 | - support per-entry outDir 1024 | 1025 | ### Features 1026 | 1027 | - support per-entry outDir ([5bb7ac3](https://github.com/unjs/unbuild/commit/5bb7ac384381c08b896c346fde94ca69fe704412)) 1028 | 1029 | ### Bug Fixes 1030 | 1031 | - enable declarations and export defineBuildConfig ([0e7d62b](https://github.com/unjs/unbuild/commit/0e7d62ba1fcf8dffa7932e0df66ccfee658ea069)) 1032 | 1033 | ### [0.1.13](https://github.com/unjs/unbuild/compare/v0.1.12...v0.1.13) (2021-04-16) 1034 | 1035 | ### Bug Fixes 1036 | 1037 | - rollup config fixes (inline related) ([ae8a3d3](https://github.com/unjs/unbuild/commit/ae8a3d31438f1ea5b1f006a050e94fe335646cf3)) 1038 | 1039 | ### [0.1.12](https://github.com/unjs/unbuild/compare/v0.1.11...v0.1.12) (2021-04-09) 1040 | 1041 | ### Features 1042 | 1043 | - inlineDependencies option ([9f320a1](https://github.com/unjs/unbuild/commit/9f320a17b567c18d3b9212481ae3da4fd06ea1c9)) 1044 | 1045 | ### [0.1.11](https://github.com/unjs/unbuild/compare/v0.1.10...v0.1.11) (2021-04-09) 1046 | 1047 | ### Features 1048 | 1049 | - declaration option to generate types ([936478a](https://github.com/unjs/unbuild/commit/936478a0c6c36373a111a01b9c6e4eb2438e6ed6)) 1050 | 1051 | ### [0.1.10](https://github.com/unjs/unbuild/compare/v0.1.9...v0.1.10) (2021-04-09) 1052 | 1053 | ### Bug Fixes 1054 | 1055 | - exclude package itself from externals ([617a9f9](https://github.com/unjs/unbuild/commit/617a9f98f233e5a5c9764aa3e7802ef374c74944)) 1056 | 1057 | ### [0.1.9](https://github.com/unjs/unbuild/compare/v0.1.8...v0.1.9) (2021-04-09) 1058 | 1059 | ### Bug Fixes 1060 | 1061 | - **pkg:** move typescript to dependencies ([51cd53b](https://github.com/unjs/unbuild/commit/51cd53b836947e35da2639a2ee1602b6aa122499)) 1062 | 1063 | ### [0.1.8](https://github.com/unjs/unbuild/compare/v0.1.7...v0.1.8) (2021-04-09) 1064 | 1065 | ### Bug Fixes 1066 | 1067 | - **rollup:** set respectExternal option for dts ([a596fa3](https://github.com/unjs/unbuild/commit/a596fa333d78178d41a4e7ad0cd0114f3af9cedb)) 1068 | 1069 | ### [0.1.7](https://github.com/unjs/unbuild/compare/v0.1.6...v0.1.7) (2021-04-09) 1070 | 1071 | ### Bug Fixes 1072 | 1073 | - **pkg:** directly add typescript dependency for standalone usage ([adeda2f](https://github.com/unjs/unbuild/commit/adeda2f7b1b6627359316f1fb47afcb1a6119827)) 1074 | 1075 | ### [0.1.6](https://github.com/unjs/unbuild/compare/v0.1.5...v0.1.6) (2021-04-09) 1076 | 1077 | ### Bug Fixes 1078 | 1079 | - **pkg:** specify peerDependencies ([71e8994](https://github.com/unjs/unbuild/commit/71e8994cbb5d17693e94d9d93073b2d2b9011a89)) 1080 | 1081 | ### [0.1.5](https://github.com/unjs/unbuild/compare/v0.1.4...v0.1.5) (2021-04-09) 1082 | 1083 | ### Bug Fixes 1084 | 1085 | - ensure parent dir for link ([87e75b3](https://github.com/unjs/unbuild/commit/87e75b35c380a65eaa05e30adce2d2a675340216)) 1086 | 1087 | ### [0.1.4](https://github.com/unjs/unbuild/compare/v0.1.3...v0.1.4) (2021-04-09) 1088 | 1089 | ### Bug Fixes 1090 | 1091 | - use mkdirp ([c54e393](https://github.com/unjs/unbuild/commit/c54e393ea3512b837f64dcf7c55507e0df9d3de1)) 1092 | 1093 | ### [0.1.3](https://github.com/unjs/unbuild/compare/v0.1.2...v0.1.3) (2021-04-09) 1094 | 1095 | ### Bug Fixes 1096 | 1097 | - update deps and use interopDefault ([6b0f39a](https://github.com/unjs/unbuild/commit/6b0f39a8270ed1927d806ab04ce9fc160c3353f6)) 1098 | 1099 | ### [0.1.2](https://github.com/unjs/unbuild/compare/v0.1.1...v0.1.2) (2021-04-09) 1100 | 1101 | ### Features 1102 | 1103 | - link module to itself during stub ([5a1f9cd](https://github.com/unjs/unbuild/commit/5a1f9cd7b574eaf0f8ca503ecf70b0ff35bfe23b)) 1104 | - schema loader ([e2388bb](https://github.com/unjs/unbuild/commit/e2388bb3b3d23b8fc9a9d9a8a2a4d62e981602c8)) 1105 | 1106 | ### 0.1.1 (2021-04-07) 1107 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Pooya Parsa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # unbuild 2 | 3 | 4 | 5 | [![npm version](https://img.shields.io/npm/v/unbuild)](https://npmjs.com/package/unbuild) 6 | [![npm downloads](https://img.shields.io/npm/dm/unbuild)](https://npm.chart.dev/unbuild) 7 | 8 | 9 | 10 | > A unified JavaScript build system 11 | 12 | > [!NOTE] 13 | > We are experimenting with [obuild](https://github.com/unjs/obuild) as the next next-gen successor based on [rolldown](https://github.com/rolldown/rolldown). 14 | > 15 | > If you mainly need faster build speeds and don't mind trying beta software, give it a try! 16 | 17 | ### 📦 Optimized bundler 18 | 19 | Robust [rollup](https://rollupjs.org) based bundler that supports TypeScript and generates CommonJS and module formats + type declarations. 20 | 21 | ### 🪄 Automated config 22 | 23 | Automagically infer build config and entries from `package.json`. 24 | 25 | ### 📁 Bundleless build 26 | 27 | Integration with [mkdist](https://github.com/unjs/mkdist) for generating bundleless dists with file-to-file transpilation. 28 | 29 | ### ✨ Passive watcher 30 | 31 | Stub `dist` once using `unbuild --stub` (powered by [jiti](https://github.com/unjs/jiti)) and you can try and link your project without needing to watch and rebuild during development. 32 | 33 | ### ✍ Untype Generator 34 | 35 | Integration with [untyped](https://github.com/unjs/untyped). 36 | 37 | ### ✔️ Secure builds 38 | 39 | Automatically check for various build issues such as potential **missing** and **unused** [dependencies](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#dependencies) and fail CI. 40 | 41 | CLI output also includes output size and exports for quick inspection. 42 | 43 | ## Usage 44 | 45 | Create `src/index.ts`: 46 | 47 | ```js 48 | export const log = (...args) => { 49 | console.log(...args); 50 | }; 51 | ``` 52 | 53 | Update `package.json`: 54 | 55 | ```json 56 | { 57 | "type": "module", 58 | "scripts": { 59 | "build": "unbuild", 60 | "prepack": "unbuild" 61 | }, 62 | "exports": { 63 | ".": { 64 | "import": "./dist/index.mjs", 65 | "require": "./dist/index.cjs" 66 | } 67 | }, 68 | "main": "./dist/index.cjs", 69 | "types": "./dist/index.d.ts", 70 | "files": ["dist"] 71 | } 72 | ``` 73 | 74 | > **Note** 75 | > You can find a more complete example in [unjs/template](https://github.com/unjs/template) for project setup. 76 | 77 | Build with `unbuild`: 78 | 79 | ```sh 80 | npx unbuild 81 | ``` 82 | 83 | Configuration is automatically inferred from fields in `package.json` mapped to `src/` directory. For more control, continue with next section. 84 | 85 | ## Configuration 86 | 87 | Create `build.config.ts`: 88 | 89 | ```js 90 | export default { 91 | entries: ["./src/index"], 92 | }; 93 | ``` 94 | 95 | You can either use `unbuild` key in `package.json` or `build.config.{js,cjs,mjs,ts,mts,cts,json}` to specify configuration. 96 | 97 | See options [here](./src/types.ts). 98 | 99 | Example: 100 | 101 | ```js 102 | import { defineBuildConfig } from "unbuild"; 103 | 104 | export default defineBuildConfig({ 105 | // If entries is not provided, will be automatically inferred from package.json 106 | entries: [ 107 | // default 108 | "./src/index", 109 | // mkdist builder transpiles file-to-file keeping original sources structure 110 | { 111 | builder: "mkdist", 112 | input: "./src/package/components/", 113 | outDir: "./build/components", 114 | }, 115 | ], 116 | 117 | // Change outDir, default is 'dist' 118 | outDir: "build", 119 | 120 | // Generates .d.ts declaration file 121 | declaration: true, 122 | }); 123 | ``` 124 | 125 | Or with multiple builds you can declare an array of configs: 126 | 127 | ```js 128 | import { defineBuildConfig } from "unbuild"; 129 | 130 | export default defineBuildConfig([ 131 | { 132 | // If entries is not provided, will be automatically inferred from package.json 133 | entries: [ 134 | // default 135 | "./src/index", 136 | // mkdist builder transpiles file-to-file keeping original sources structure 137 | { 138 | builder: "mkdist", 139 | input: "./src/package/components/", 140 | outDir: "./build/components", 141 | }, 142 | ], 143 | 144 | // Change outDir, default is 'dist' 145 | outDir: "build", 146 | 147 | /** 148 | * * `compatible` means "src/index.ts" will generate "dist/index.d.mts", "dist/index.d.cts" and "dist/index.d.ts". 149 | * * `node16` means "src/index.ts" will generate "dist/index.d.mts" and "dist/index.d.cts". 150 | * * `true` is equivalent to `compatible`. 151 | * * `false` will disable declaration generation. 152 | * * `undefined` will auto detect based on "package.json". If "package.json" has "types" field, it will be `"compatible"`, otherwise `false`. 153 | */ 154 | declaration: "compatible", 155 | }, 156 | { 157 | name: "minified", 158 | entries: ["./src/index"], 159 | outDir: "build/min", 160 | rollup: { 161 | esbuild: { 162 | minify: true, 163 | }, 164 | }, 165 | }, 166 | ]); 167 | ``` 168 | 169 | ## Recipes 170 | 171 | ### Decorators support 172 | 173 | In `build.config.ts` 174 | 175 | ```ts 176 | import { defineBuildConfig } from "unbuild"; 177 | 178 | export default defineBuildConfig({ 179 | rollup: { 180 | esbuild: { 181 | tsconfigRaw: { 182 | compilerOptions: { 183 | experimentalDecorators: true, 184 | }, 185 | }, 186 | }, 187 | }, 188 | }); 189 | ``` 190 | 191 | ### Generate sourcemaps 192 | 193 | ```ts 194 | import { defineBuildConfig } from "unbuild"; 195 | 196 | export default defineBuildConfig({ 197 | sourcemap: true, 198 | }); 199 | ``` 200 | 201 | ## 💻 Development 202 | 203 | - Clone this repository 204 | - Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable` (use `npm i -g corepack` for Node.js < 16.10) 205 | - Install dependencies using `pnpm install` 206 | - Run interactive tests using `pnpm dev` 207 | 208 | ## License 209 | 210 | [MIT](./LICENSE) 211 | 212 | 213 | 214 | [npm-version-src]: https://img.shields.io/npm/v/unbuild?style=flat-square 215 | [npm-version-href]: https://npmjs.com/package/unbuild 216 | [npm-downloads-src]: https://img.shields.io/npm/dm/unbuild?style=flat-square 217 | [npm-downloads-href]: https://npmjs.com/package/unbuild 218 | [github-actions-src]: https://img.shields.io/github/actions/workflow/status/unjs/unbuild/ci.yml?style=flat-square 219 | [github-actions-href]: https://github.com/unjs/unbuild/actions?query=workflow%3Aci 220 | [codecov-src]: https://img.shields.io/codecov/c/gh/unjs/unbuild/main?style=flat-square 221 | [codecov-href]: https://codecov.io/gh/unjs/unbuild 222 | -------------------------------------------------------------------------------- /build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from "./src"; 2 | import { rm } from "node:fs/promises"; 3 | 4 | export default defineBuildConfig({ 5 | hooks: { 6 | async "build:done"() { 7 | await rm("dist/index.d.ts"); 8 | await rm("dist/cli.d.ts"); 9 | await rm("dist/cli.d.mts"); 10 | }, 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import unjs from "eslint-config-unjs"; 2 | 3 | // https://github.com/unjs/eslint-config 4 | export default unjs({ 5 | ignores: [ 6 | ".git", 7 | "test/fixture/dist" 8 | ], 9 | rules: { 10 | "unicorn/no-null": 0, 11 | "unicorn/prevent-abbreviations": 0, 12 | "unicorn/prefer-module": 0, 13 | "unicorn/prefer-top-level-await": 0, 14 | "@typescript-eslint/no-non-null-assertion": 0, 15 | "@typescript-eslint/no-unused-vars": 0, 16 | }, 17 | }, { 18 | files: ["**/*.ts"], 19 | rules: { 20 | "@typescript-eslint/explicit-function-return-type": "error", 21 | "@typescript-eslint/no-inferrable-types": "error", 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /examples/1.zero-config/README.md: -------------------------------------------------------------------------------- 1 | # Unbuild zero config example 2 | 3 | Unbuild automatically infers the build configuration from `exports` field in [`package.json`](./package.json). 4 | 5 | Since 3 `types`, `import` and `require` fields are set, build automatically includes them. 6 | 7 | Unbuild also supports building multiple entries. 8 | -------------------------------------------------------------------------------- /examples/1.zero-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unbuild-example-zero-config", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "exports": { 6 | ".": { 7 | "import": { 8 | "types": "./dist/index.d.mts", 9 | "default": "./dist/index.mjs" 10 | }, 11 | "require": { 12 | "types": "./dist/index.d.cts", 13 | "default": "./dist/index.cjs" 14 | } 15 | }, 16 | "./utils": { 17 | "import": { 18 | "types": "./dist/utils.d.mts", 19 | "default": "./dist/utils.mjs" 20 | }, 21 | "require": { 22 | "types": "./dist/utils.d.cts", 23 | "default": "./dist/utils.cjs" 24 | } 25 | } 26 | }, 27 | "files": [ 28 | "dist" 29 | ], 30 | "scripts": { 31 | "build": "unbuild", 32 | "build:stub": "unbuild --stub" 33 | }, 34 | "devDependencies": { 35 | "unbuild": "^2.0.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/1.zero-config/src/index.ts: -------------------------------------------------------------------------------- 1 | export function main(data: string): string { 2 | return `Hello ${data}!`; 3 | } 4 | -------------------------------------------------------------------------------- /examples/1.zero-config/src/utils.ts: -------------------------------------------------------------------------------- 1 | export default function sum(a: number, b: number): number { 2 | return a + b; 3 | } 4 | -------------------------------------------------------------------------------- /examples/2.mkdist/README.md: -------------------------------------------------------------------------------- 1 | # Unbuild mkdist example 2 | 3 | A simple example of how to generate ESM, CJS and DTS from TypeScript using a folder as entry point. 4 | -------------------------------------------------------------------------------- /examples/2.mkdist/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from "unbuild"; 2 | 3 | export default defineBuildConfig({ 4 | entries: [ 5 | "src/index.ts", 6 | { 7 | input: "src/plugins/", 8 | outDir: "dist/plugins/", 9 | format: "esm", 10 | }, 11 | { 12 | input: "src/plugins/", 13 | outDir: "dist/plugins/", 14 | format: "cjs", 15 | ext: "cjs", 16 | declaration: false, 17 | }, 18 | ], 19 | declaration: true, 20 | rollup: { 21 | emitCJS: true, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /examples/2.mkdist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unbuild-example-mkdist", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "exports": { 6 | ".": { 7 | "import": { 8 | "types": "./dist/index.d.mts", 9 | "default": "./dist/index.mjs" 10 | }, 11 | "require": { 12 | "types": "./dist/index.d.cts", 13 | "default": "./dist/index.cjs" 14 | } 15 | }, 16 | "./plugins/*": { 17 | "import": { 18 | "types": "./dist/plugins/*.d.mts", 19 | "default": "./dist/plugins/*.mjs" 20 | }, 21 | "require": { 22 | "types": "./dist/plugins/*.d.cts", 23 | "default": "./dist/plugins/*.cjs" 24 | } 25 | } 26 | }, 27 | "files": [ 28 | "dist" 29 | ], 30 | "scripts": { 31 | "build": "unbuild", 32 | "build:stub": "unbuild --stub" 33 | }, 34 | "devDependencies": { 35 | "unbuild": "^2.0.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/2.mkdist/src/index.ts: -------------------------------------------------------------------------------- 1 | export function main(data: string): string { 2 | return `Hello ${data}!`; 3 | } 4 | -------------------------------------------------------------------------------- /examples/2.mkdist/src/plugins/vite.ts: -------------------------------------------------------------------------------- 1 | export default function vitePlugin(): string { 2 | return "Hello Vite!"; 3 | } 4 | -------------------------------------------------------------------------------- /examples/2.mkdist/src/plugins/webpack.ts: -------------------------------------------------------------------------------- 1 | export default function webpackPlugin(): string { 2 | return "Hello Webpack!"; 3 | } 4 | -------------------------------------------------------------------------------- /examples/3.untyped/.gitignore: -------------------------------------------------------------------------------- 1 | schema 2 | -------------------------------------------------------------------------------- /examples/3.untyped/README.md: -------------------------------------------------------------------------------- 1 | # Unbuild untyped example 2 | -------------------------------------------------------------------------------- /examples/3.untyped/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from "unbuild"; 2 | 3 | export default defineBuildConfig({ 4 | entries: [ 5 | "src/index.ts", 6 | { 7 | builder: "untyped", 8 | input: "src/index.ts", 9 | outDir: "schema", 10 | name: "schema", 11 | }, 12 | ], 13 | declaration: true, 14 | rollup: { 15 | emitCJS: true, 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /examples/3.untyped/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unbuild-example-untyped", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "exports": { 6 | ".": { 7 | "import": { 8 | "types": "./dist/index.d.mts", 9 | "default": "./dist/index.mjs" 10 | }, 11 | "require": { 12 | "types": "./dist/index.d.cts", 13 | "default": "./dist/index.cjs" 14 | } 15 | } 16 | }, 17 | "files": [ 18 | "dist" 19 | ], 20 | "scripts": { 21 | "build": "unbuild" 22 | }, 23 | "devDependencies": { 24 | "unbuild": "^2.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/3.untyped/src/index.ts: -------------------------------------------------------------------------------- 1 | export function sendMessage( 2 | message: string, 3 | date = new Date(), 4 | flash?: boolean, 5 | ): string { 6 | return "OK"; 7 | } 8 | 9 | export const config = { 10 | name: "default", 11 | price: 12.5, 12 | /** 13 | * checked state 14 | */ 15 | checked: false, 16 | dimensions: { 17 | /** width in px */ 18 | width: 10, 19 | /** height in px */ 20 | height: 10, 21 | }, 22 | tags: { 23 | $resolve: (val?: string[]): string[] => 24 | ["tag1", ...(val || [])].filter(Boolean), 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # unbuild examples 2 | 3 | In this directory you can find some examples of how to use unbuild. 4 | 5 | - [Zero Config](./1.zero-config/) 6 | - [mkdist](./2.mkdist/) 7 | - [untyped](./3.untyped/) 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unbuild", 3 | "version": "3.5.0", 4 | "description": "A unified JavaScript build system", 5 | "repository": "unjs/unbuild", 6 | "license": "MIT", 7 | "type": "module", 8 | "exports": { 9 | "types": "./dist/index.d.mts", 10 | "default": "./dist/index.mjs" 11 | }, 12 | "types": "./dist/index.d.mts", 13 | "bin": { 14 | "unbuild": "./dist/cli.mjs" 15 | }, 16 | "files": [ 17 | "dist" 18 | ], 19 | "scripts": { 20 | "build": "pnpm unbuild", 21 | "dev": "pnpm unbuild test/fixture", 22 | "lint": "eslint . && prettier -c src test examples", 23 | "lint:fix": "automd && eslint --fix . && prettier -w src test examples", 24 | "prepack": "pnpm unbuild", 25 | "release": "pnpm test && changelogen --release --publish && git push --follow-tags", 26 | "stub": "pnpm unbuild --stub", 27 | "test": "pnpm lint && pnpm test:types && vitest run --coverage", 28 | "test:types": "tsc --noEmit", 29 | "unbuild": "jiti ./src/cli" 30 | }, 31 | "dependencies": { 32 | "@rollup/plugin-alias": "^5.1.1", 33 | "@rollup/plugin-commonjs": "^28.0.2", 34 | "@rollup/plugin-json": "^6.1.0", 35 | "@rollup/plugin-node-resolve": "^16.0.0", 36 | "@rollup/plugin-replace": "^6.0.2", 37 | "@rollup/pluginutils": "^5.1.4", 38 | "citty": "^0.1.6", 39 | "consola": "^3.4.0", 40 | "defu": "^6.1.4", 41 | "esbuild": "^0.25.0", 42 | "fix-dts-default-cjs-exports": "^1.0.0", 43 | "hookable": "^5.5.3", 44 | "jiti": "^2.4.2", 45 | "magic-string": "^0.30.17", 46 | "mkdist": "^2.2.0", 47 | "mlly": "^1.7.4", 48 | "pathe": "^2.0.3", 49 | "pkg-types": "^2.0.0", 50 | "pretty-bytes": "^6.1.1", 51 | "rollup": "^4.34.8", 52 | "rollup-plugin-dts": "^6.1.1", 53 | "scule": "^1.3.0", 54 | "tinyglobby": "^0.2.12", 55 | "untyped": "^2.0.0" 56 | }, 57 | "devDependencies": { 58 | "@babel/plugin-transform-class-properties": "^7.25.9", 59 | "@types/node": "^22.13.5", 60 | "@vitest/coverage-v8": "^3.0.7", 61 | "automd": "^0.4.0", 62 | "changelogen": "^0.6.0", 63 | "eslint": "^9.21.0", 64 | "eslint-config-unjs": "^0.4.2", 65 | "prettier": "^3.5.2", 66 | "typescript": "^5.7.3", 67 | "vitest": "^3.0.7" 68 | }, 69 | "peerDependencies": { 70 | "typescript": "^5.7.3" 71 | }, 72 | "peerDependenciesMeta": { 73 | "typescript": { 74 | "optional": true 75 | } 76 | }, 77 | "packageManager": "pnpm@10.5.0" 78 | } 79 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "github>unjs/renovate-config" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/auto.ts: -------------------------------------------------------------------------------- 1 | import type { PackageJson } from "pkg-types"; 2 | import type { BuildEntry, BuildPreset, MkdistBuildEntry } from "./types"; 3 | import { existsSync } from "node:fs"; 4 | import { normalize, join, resolve } from "pathe"; 5 | import { consola } from "consola"; 6 | import { colors } from "consola/utils"; 7 | import { definePreset } from "./types"; 8 | import { extractExportFilenames, listRecursively, warn } from "./utils"; 9 | 10 | type InferEntriesResult = { 11 | entries: BuildEntry[]; 12 | cjs?: boolean; 13 | dts?: boolean; 14 | warnings: string[]; 15 | }; 16 | 17 | export const autoPreset: BuildPreset = definePreset(() => { 18 | return { 19 | hooks: { 20 | "build:prepare"(ctx): void { 21 | // Disable auto if entries already provided or pkg not available 22 | if (!ctx.pkg || ctx.options.entries.length > 0) { 23 | return; 24 | } 25 | const sourceFiles = listRecursively(join(ctx.options.rootDir, "src")); 26 | const res = inferEntries(ctx.pkg, sourceFiles, ctx.options.rootDir); 27 | for (const message of res.warnings) { 28 | warn(ctx, message); 29 | } 30 | ctx.options.entries.push(...res.entries); 31 | if (res.cjs) { 32 | ctx.options.rollup.emitCJS = true; 33 | } 34 | if (ctx.options.declaration === undefined) { 35 | // Enable auto detect based on "package.json" 36 | // If "package.json" has "types" field, it will be "compatible", otherwise false. 37 | ctx.options.declaration = res.dts ? "compatible" : false; 38 | } 39 | consola.info( 40 | "Automatically detected entries:", 41 | colors.cyan( 42 | ctx.options.entries 43 | .map((e) => 44 | colors.bold( 45 | e.input 46 | .replace(ctx.options.rootDir + "/", "") 47 | .replace(/\/$/, "/*"), 48 | ), 49 | ) 50 | .join(", "), 51 | ), 52 | colors.gray( 53 | ["esm", res.cjs && "cjs", res.dts && "dts"] 54 | .filter(Boolean) 55 | .map((tag) => `[${tag}]`) 56 | .join(" "), 57 | ), 58 | ); 59 | }, 60 | }, 61 | }; 62 | }); 63 | 64 | /** 65 | * @param {PackageJson} pkg The contents of a package.json file to serve as the source for inferred entries. 66 | * @param {string[]} sourceFiles A list of source files to use for inferring entries. 67 | * @param {string | undefined} rootDir The root directory of the project. 68 | */ 69 | export function inferEntries( 70 | pkg: PackageJson, 71 | sourceFiles: string[], 72 | rootDir?: string, 73 | ): InferEntriesResult { 74 | const warnings = []; 75 | 76 | // Sort files so least-nested files are first 77 | sourceFiles.sort((a, b) => a.split("/").length - b.split("/").length); 78 | 79 | // Come up with a list of all output files & their formats 80 | const outputs = extractExportFilenames(pkg.exports); 81 | 82 | if (pkg.bin) { 83 | const binaries = 84 | typeof pkg.bin === "string" ? [pkg.bin] : Object.values(pkg.bin); 85 | for (const file of binaries) { 86 | outputs.push({ file }); 87 | } 88 | } 89 | if (pkg.main) { 90 | outputs.push({ file: pkg.main }); 91 | } 92 | if (pkg.module) { 93 | outputs.push({ type: "esm", file: pkg.module }); 94 | } 95 | if (pkg.types || pkg.typings) { 96 | outputs.push({ file: pkg.types || pkg.typings! }); 97 | } 98 | 99 | // Try to detect output types 100 | const isESMPkg = pkg.type === "module"; 101 | for (const output of outputs.filter((o) => !o.type)) { 102 | const isJS = output.file.endsWith(".js"); 103 | if ((isESMPkg && isJS) || output.file.endsWith(".mjs")) { 104 | output.type = "esm"; 105 | } else if ((!isESMPkg && isJS) || output.file.endsWith(".cjs")) { 106 | output.type = "cjs"; 107 | } 108 | } 109 | 110 | let cjs = false; 111 | let dts = false; 112 | 113 | // Infer entries from package files 114 | const entries: BuildEntry[] = []; 115 | for (const output of outputs) { 116 | // Supported output file extensions are `.d.ts`, `.cjs` and `.mjs` 117 | // But we support any file extension here in case user has extended rollup options 118 | const outputSlug = output.file.replace( 119 | /(\*[^/\\]*|\.d\.(m|c)?ts|\.\w+)$/, 120 | "", 121 | ); 122 | const isDir = outputSlug.endsWith("/"); 123 | 124 | // Skip top level directory 125 | if (isDir && ["./", "/"].includes(outputSlug)) { 126 | continue; 127 | } 128 | 129 | const possiblePaths = getEntrypointPaths(outputSlug); 130 | // eslint-disable-next-line unicorn/no-array-reduce 131 | const input = possiblePaths.reduce((source, d) => { 132 | if (source) { 133 | return source; 134 | } 135 | const SOURCE_RE = new RegExp( 136 | `(?<=/|$)${d}${isDir ? "" : String.raw`\.\w+`}$`, 137 | ); 138 | return sourceFiles 139 | .find((i) => SOURCE_RE.test(i)) 140 | ?.replace(/(\.d\.(m|c)?ts|\.\w+)$/, ""); 141 | }, undefined as any); 142 | 143 | if (!input) { 144 | if (!existsSync(resolve(rootDir || ".", output.file))) { 145 | warnings.push(`Could not find entrypoint for \`${output.file}\``); 146 | } 147 | continue; 148 | } 149 | 150 | if (output.type === "cjs") { 151 | cjs = true; 152 | } 153 | 154 | const entry = 155 | entries.find((i) => i.input === input) || 156 | entries[entries.push({ input }) - 1]; 157 | 158 | if (/\.d\.(m|c)?ts$/.test(output.file)) { 159 | dts = true; 160 | } 161 | 162 | if (isDir) { 163 | entry.outDir = outputSlug; 164 | (entry as MkdistBuildEntry).format = output.type; 165 | } 166 | } 167 | 168 | return { entries, cjs, dts, warnings }; 169 | } 170 | 171 | export const getEntrypointPaths = (path: string): string[] => { 172 | const segments = normalize(path).split("/"); 173 | return segments 174 | .map((_, index) => segments.slice(index).join("/")) 175 | .filter(Boolean); 176 | }; 177 | -------------------------------------------------------------------------------- /src/build.ts: -------------------------------------------------------------------------------- 1 | import Module from "node:module"; 2 | import { promises as fsp } from "node:fs"; 3 | import { resolve, relative, isAbsolute, normalize } from "pathe"; 4 | import type { PackageJson } from "pkg-types"; 5 | import { colors } from "consola/utils"; 6 | import { consola } from "consola"; 7 | import { defu } from "defu"; 8 | import { createHooks } from "hookable"; 9 | import prettyBytes from "pretty-bytes"; 10 | import { glob } from "tinyglobby"; 11 | import { 12 | dumpObject, 13 | rmdir, 14 | resolvePreset, 15 | removeExtension, 16 | inferPkgExternals, 17 | withTrailingSlash, 18 | } from "./utils"; 19 | import type { BuildContext, BuildConfig, BuildOptions } from "./types"; 20 | import { validatePackage, validateDependencies } from "./validate"; 21 | import { rollupBuild } from "./builders/rollup"; 22 | import { typesBuild } from "./builders/untyped"; 23 | import { mkdistBuild } from "./builders/mkdist"; 24 | import { copyBuild } from "./builders/copy"; 25 | import { createJiti } from "jiti"; 26 | 27 | export async function build( 28 | rootDir: string, 29 | stub: boolean, 30 | inputConfig: BuildConfig & { config?: string } = {}, 31 | ): Promise { 32 | // Determine rootDir 33 | rootDir = resolve(process.cwd(), rootDir || "."); 34 | 35 | // Create jiti instance for loading initial config 36 | const jiti = createJiti(rootDir); 37 | 38 | const _buildConfig: BuildConfig | BuildConfig[] = 39 | (await jiti.import(inputConfig?.config || "./build.config", { 40 | try: !inputConfig.config, 41 | default: true, 42 | })) || {}; 43 | 44 | const buildConfigs = ( 45 | Array.isArray(_buildConfig) ? _buildConfig : [_buildConfig] 46 | ).filter(Boolean); 47 | 48 | const pkg: PackageJson & Partial> = 49 | ((await jiti.import("./package.json", { 50 | try: true, 51 | default: true, 52 | })) as PackageJson) || ({} as PackageJson); 53 | 54 | // Invoke build for every build config defined in build.config.ts 55 | const cleanedDirs: string[] = []; 56 | 57 | const _watchMode = inputConfig.watch === true; 58 | const _stubMode = !_watchMode && (stub || inputConfig.stub === true); 59 | 60 | if (!_watchMode && !_stubMode) { 61 | // Prefer `publishConfig` when defined 62 | Object.assign(pkg, pkg.publishConfig); 63 | } 64 | 65 | for (const buildConfig of buildConfigs) { 66 | await _build( 67 | rootDir, 68 | inputConfig, 69 | buildConfig, 70 | pkg, 71 | cleanedDirs, 72 | _stubMode, 73 | _watchMode, 74 | ); 75 | } 76 | } 77 | 78 | async function _build( 79 | rootDir: string, 80 | inputConfig: BuildConfig = {}, 81 | buildConfig: BuildConfig, 82 | pkg: PackageJson & Partial>, 83 | cleanedDirs: string[], 84 | _stubMode: boolean, 85 | _watchMode: boolean, 86 | ): Promise { 87 | // Resolve preset 88 | const preset = await resolvePreset( 89 | buildConfig.preset || 90 | pkg.unbuild?.preset || 91 | pkg.build?.preset || 92 | inputConfig.preset || 93 | "auto", 94 | rootDir, 95 | ); 96 | 97 | // Merge options 98 | const options = defu( 99 | buildConfig, 100 | pkg.unbuild || pkg.build, 101 | inputConfig, 102 | preset, 103 | { 104 | name: (pkg?.name || "").split("/").pop() || "default", 105 | rootDir, 106 | entries: [], 107 | clean: true, 108 | declaration: undefined, 109 | outDir: "dist", 110 | stub: _stubMode, 111 | stubOptions: { 112 | /** 113 | * See https://github.com/unjs/jiti#%EF%B8%8F-options 114 | */ 115 | jiti: { 116 | interopDefault: true, 117 | alias: {}, 118 | }, 119 | }, 120 | watch: _watchMode, 121 | watchOptions: _watchMode 122 | ? { 123 | exclude: "node_modules/**", 124 | include: "src/**", 125 | } 126 | : undefined, 127 | externals: [ 128 | ...Module.builtinModules, 129 | ...Module.builtinModules.map((m) => "node:" + m), 130 | ], 131 | dependencies: [], 132 | devDependencies: [], 133 | peerDependencies: [], 134 | alias: {}, 135 | replace: {}, 136 | failOnWarn: true, 137 | sourcemap: false, 138 | rollup: { 139 | emitCJS: false, 140 | watch: false, 141 | cjsBridge: false, 142 | inlineDependencies: false, 143 | preserveDynamicImports: true, 144 | output: { 145 | // https://v8.dev/features/import-attributes 146 | importAttributesKey: "with", 147 | }, 148 | // Plugins 149 | replace: { 150 | preventAssignment: true, 151 | }, 152 | alias: {}, 153 | resolve: { 154 | preferBuiltins: true, 155 | }, 156 | json: { 157 | preferConst: true, 158 | }, 159 | commonjs: { 160 | ignoreTryCatch: true, 161 | }, 162 | esbuild: { target: "esnext" }, 163 | dts: { 164 | compilerOptions: { 165 | // https://github.com/Swatinem/rollup-plugin-dts/issues/143 166 | preserveSymlinks: false, 167 | // https://github.com/Swatinem/rollup-plugin-dts/issues/127 168 | composite: false, 169 | }, 170 | respectExternal: true, 171 | }, 172 | }, 173 | parallel: false, 174 | } satisfies BuildOptions, 175 | ) as BuildOptions; 176 | 177 | // Resolve dirs relative to rootDir 178 | options.outDir = resolve(options.rootDir, options.outDir); 179 | 180 | // Create shared jiti instance for context 181 | const jiti = createJiti(options.rootDir, { interopDefault: true }); 182 | 183 | // Build context 184 | const ctx: BuildContext = { 185 | options, 186 | jiti, 187 | warnings: new Set(), 188 | pkg, 189 | buildEntries: [], 190 | usedImports: new Set(), 191 | hooks: createHooks(), 192 | }; 193 | 194 | // Register hooks 195 | if (preset.hooks) { 196 | ctx.hooks.addHooks(preset.hooks); 197 | } 198 | if (inputConfig.hooks) { 199 | ctx.hooks.addHooks(inputConfig.hooks); 200 | } 201 | if (buildConfig.hooks) { 202 | ctx.hooks.addHooks(buildConfig.hooks); 203 | } 204 | 205 | // Allow prepare and extending context 206 | await ctx.hooks.callHook("build:prepare", ctx); 207 | 208 | // Normalize entries 209 | options.entries = options.entries.map((entry) => 210 | typeof entry === "string" ? { input: entry } : entry, 211 | ); 212 | 213 | for (const entry of options.entries) { 214 | if (typeof entry.name !== "string") { 215 | let relativeInput = isAbsolute(entry.input) 216 | ? relative(rootDir, entry.input) 217 | : normalize(entry.input); 218 | if (relativeInput.startsWith("./")) { 219 | relativeInput = relativeInput.slice(2); 220 | } 221 | entry.name = removeExtension(relativeInput.replace(/^src\//, "")); 222 | } 223 | 224 | if (!entry.input) { 225 | throw new Error("Missing entry input: " + dumpObject(entry)); 226 | } 227 | 228 | if (!entry.builder) { 229 | entry.builder = entry.input.endsWith("/") ? "mkdist" : "rollup"; 230 | } 231 | 232 | if (options.declaration !== undefined && entry.declaration === undefined) { 233 | entry.declaration = options.declaration; 234 | } 235 | 236 | entry.input = resolve(options.rootDir, entry.input); 237 | entry.outDir = resolve(options.rootDir, entry.outDir || options.outDir); 238 | } 239 | 240 | // Infer dependencies from pkg 241 | options.dependencies = Object.keys(pkg.dependencies || {}); 242 | options.peerDependencies = Object.keys(pkg.peerDependencies || {}); 243 | options.devDependencies = Object.keys(pkg.devDependencies || {}); 244 | 245 | // Add all dependencies as externals 246 | options.externals.push(...inferPkgExternals(pkg)); 247 | options.externals = [...new Set(options.externals)]; 248 | 249 | // Call build:before 250 | await ctx.hooks.callHook("build:before", ctx); 251 | 252 | // Start info 253 | consola.info( 254 | colors.cyan(`${options.stub ? "Stubbing" : "Building"} ${options.name}`), 255 | ); 256 | if (process.env.DEBUG) { 257 | consola.info(`${colors.bold("Root dir:")} ${options.rootDir} 258 | ${colors.bold("Entries:")} 259 | ${options.entries.map((entry) => " " + dumpObject(entry)).join("\n ")} 260 | `); 261 | } 262 | 263 | // Clean dist dirs 264 | if (options.clean) { 265 | for (const dir of new Set( 266 | options.entries 267 | .map((e) => e.outDir) 268 | .filter((p): p is NonNullable => !!p) 269 | .sort(), 270 | )) { 271 | if ( 272 | dir === options.rootDir || 273 | options.rootDir.startsWith(withTrailingSlash(dir)) || 274 | cleanedDirs.some((c) => dir.startsWith(c)) 275 | ) { 276 | continue; 277 | } 278 | cleanedDirs.push(dir); 279 | consola.info( 280 | `Cleaning dist directory: \`./${relative(process.cwd(), dir)}\``, 281 | ); 282 | await rmdir(dir); 283 | await fsp.mkdir(dir, { recursive: true }); 284 | } 285 | } 286 | 287 | // Try to selflink 288 | // if (ctx.stub && ctx.pkg.name) { 289 | // const nodemodulesDir = resolve(ctx.rootDir, 'node_modules', ctx.pkg.name) 290 | // await symlink(resolve(ctx.rootDir), nodemodulesDir).catch(() => {}) 291 | // } 292 | 293 | const buildTasks = [ 294 | typesBuild, // untyped 295 | mkdistBuild, // mkdist 296 | rollupBuild, // rollup 297 | copyBuild, // copy 298 | ] as const; 299 | 300 | if (options.parallel) { 301 | await Promise.all(buildTasks.map((task) => task(ctx))); 302 | } else { 303 | for (const task of buildTasks) { 304 | await task(ctx); 305 | } 306 | } 307 | 308 | // Skip rest for stub and watch mode 309 | if (options.stub || options.watch) { 310 | await ctx.hooks.callHook("build:done", ctx); 311 | return; 312 | } 313 | 314 | // Done info 315 | consola.success(colors.green("Build succeeded for " + options.name)); 316 | 317 | // Find all dist files and add missing entries as chunks 318 | const outFiles = await glob(["**"], { cwd: options.outDir }); 319 | for (const file of outFiles) { 320 | let entry = ctx.buildEntries.find((e) => e.path === file); 321 | if (!entry) { 322 | entry = { 323 | path: file, 324 | chunk: true, 325 | }; 326 | ctx.buildEntries.push(entry); 327 | } 328 | if (!entry.bytes) { 329 | const stat = await fsp.stat(resolve(options.outDir, file)); 330 | entry.bytes = stat.size; 331 | } 332 | } 333 | 334 | const rPath = (p: string): string => 335 | relative(process.cwd(), resolve(options.outDir, p)); 336 | for (const entry of ctx.buildEntries.filter((e) => !e.chunk)) { 337 | let totalBytes = entry.bytes || 0; 338 | for (const chunk of entry.chunks || []) { 339 | totalBytes += ctx.buildEntries.find((e) => e.path === chunk)?.bytes || 0; 340 | } 341 | let line = 342 | ` ${colors.bold(rPath(entry.path))} (` + 343 | [ 344 | totalBytes && `total size: ${colors.cyan(prettyBytes(totalBytes))}`, 345 | entry.bytes && `chunk size: ${colors.cyan(prettyBytes(entry.bytes))}`, 346 | entry.exports?.length && 347 | `exports: ${colors.gray(entry.exports.join(", "))}`, 348 | ] 349 | .filter(Boolean) 350 | .join(", ") + 351 | ")"; 352 | if (entry.chunks?.length) { 353 | line += 354 | "\n" + 355 | entry.chunks 356 | .map((p) => { 357 | const chunk = 358 | ctx.buildEntries.find((e) => e.path === p) || ({} as any); 359 | return colors.gray( 360 | " └─ " + 361 | rPath(p) + 362 | colors.bold( 363 | chunk.bytes ? ` (${prettyBytes(chunk?.bytes)})` : "", 364 | ), 365 | ); 366 | }) 367 | .join("\n"); 368 | } 369 | if (entry.modules?.length) { 370 | line += 371 | "\n" + 372 | entry.modules 373 | .filter((m) => m.id.includes("node_modules")) 374 | .sort((a, b) => (b.bytes || 0) - (a.bytes || 0)) 375 | .map((m) => { 376 | return colors.gray( 377 | " 📦 " + 378 | rPath(m.id) + 379 | colors.bold(m.bytes ? ` (${prettyBytes(m.bytes)})` : ""), 380 | ); 381 | }) 382 | .join("\n"); 383 | } 384 | consola.log(entry.chunk ? colors.gray(line) : line); 385 | } 386 | console.log( 387 | "Σ Total dist size (byte size):", 388 | colors.cyan( 389 | prettyBytes(ctx.buildEntries.reduce((a, e) => a + (e.bytes || 0), 0)), 390 | ), 391 | ); 392 | 393 | // Validate 394 | validateDependencies(ctx); 395 | validatePackage(pkg, rootDir, ctx); 396 | 397 | // Call build:done 398 | await ctx.hooks.callHook("build:done", ctx); 399 | 400 | consola.log(""); 401 | 402 | if (ctx.warnings.size > 0) { 403 | consola.warn( 404 | "Build is done with some warnings:\n\n" + 405 | [...ctx.warnings].map((msg) => "- " + msg).join("\n"), 406 | ); 407 | if (ctx.options.failOnWarn) { 408 | consola.error( 409 | "Exiting with code (1). You can change this behavior by setting `failOnWarn: false` .", 410 | ); 411 | // eslint-disable-next-line unicorn/no-process-exit 412 | process.exit(1); 413 | } 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /src/builders/copy/index.ts: -------------------------------------------------------------------------------- 1 | import { promises as fsp } from "node:fs"; 2 | import { relative, resolve } from "pathe"; 3 | import { glob } from "tinyglobby"; 4 | import { symlink, rmdir, warn } from "../../utils"; 5 | import type { CopyBuildEntry, BuildContext } from "../../types"; 6 | import consola from "consola"; 7 | 8 | const copy = fsp.cp || fsp.copyFile; 9 | 10 | export async function copyBuild(ctx: BuildContext): Promise { 11 | const entries = ctx.options.entries.filter( 12 | (e) => e.builder === "copy", 13 | ) as CopyBuildEntry[]; 14 | await ctx.hooks.callHook("copy:entries", ctx, entries); 15 | for (const entry of entries) { 16 | const distDir = entry.outDir!; 17 | if (ctx.options.stub) { 18 | await rmdir(distDir); 19 | await symlink(entry.input, distDir); 20 | } else { 21 | const patterns = Array.isArray(entry.pattern) 22 | ? entry.pattern 23 | : [entry.pattern || "**"]; 24 | const paths = await glob(patterns, { 25 | cwd: resolve(ctx.options.rootDir, entry.input), 26 | absolute: false, 27 | }); 28 | 29 | const outputList = await Promise.allSettled( 30 | paths.map(async (path) => { 31 | const src = resolve(ctx.options.rootDir, entry.input, path); 32 | const dist = resolve(ctx.options.rootDir, distDir, path); 33 | await copy(src, dist); 34 | return dist; 35 | }), 36 | ); 37 | 38 | for (const output of outputList) { 39 | if (output.status === "rejected") { 40 | warn(ctx, output.reason); 41 | } 42 | } 43 | 44 | ctx.buildEntries.push({ 45 | path: distDir, 46 | chunks: outputList 47 | .filter(({ status }) => status === "fulfilled") 48 | .map((p) => 49 | relative( 50 | ctx.options.outDir, 51 | (p as PromiseFulfilledResult).value, 52 | ), 53 | ), 54 | }); 55 | } 56 | } 57 | await ctx.hooks.callHook("copy:done", ctx); 58 | 59 | if (entries.length > 0 && ctx.options.watch) { 60 | consola.warn("`untyped` builder does not support watch mode yet."); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/builders/copy/types.ts: -------------------------------------------------------------------------------- 1 | import type { BaseBuildEntry, BuildContext } from "../../types"; 2 | 3 | export interface CopyBuildEntry extends BaseBuildEntry { 4 | builder: "copy"; 5 | pattern?: string | string[]; 6 | } 7 | 8 | export interface CopyHooks { 9 | "copy:entries": ( 10 | ctx: BuildContext, 11 | entries: CopyBuildEntry[], 12 | ) => void | Promise; 13 | "copy:done": (ctx: BuildContext) => void | Promise; 14 | } 15 | -------------------------------------------------------------------------------- /src/builders/mkdist/index.ts: -------------------------------------------------------------------------------- 1 | import { relative } from "pathe"; 2 | import { mkdist, type MkdistOptions } from "mkdist"; 3 | import { symlink, rmdir, warn } from "../../utils"; 4 | import type { MkdistBuildEntry, BuildContext } from "../../types"; 5 | import consola from "consola"; 6 | 7 | export async function mkdistBuild(ctx: BuildContext): Promise { 8 | const entries = ctx.options.entries.filter( 9 | (e) => e.builder === "mkdist", 10 | ) as MkdistBuildEntry[]; 11 | await ctx.hooks.callHook("mkdist:entries", ctx, entries); 12 | for (const entry of entries) { 13 | const distDir = entry.outDir!; 14 | if (ctx.options.stub) { 15 | await rmdir(distDir); 16 | await symlink(entry.input, distDir); 17 | } else { 18 | const mkdistOptions: MkdistOptions = { 19 | rootDir: ctx.options.rootDir, 20 | srcDir: entry.input, 21 | distDir, 22 | cleanDist: false, 23 | ...entry, 24 | }; 25 | await ctx.hooks.callHook( 26 | "mkdist:entry:options", 27 | ctx, 28 | entry, 29 | mkdistOptions, 30 | ); 31 | const output = await mkdist(mkdistOptions); 32 | ctx.buildEntries.push({ 33 | path: distDir, 34 | chunks: output.writtenFiles.map((p) => relative(ctx.options.outDir, p)), 35 | }); 36 | await ctx.hooks.callHook("mkdist:entry:build", ctx, entry, output); 37 | if (output.errors) { 38 | for (const error of output.errors) { 39 | warn( 40 | ctx, 41 | `mkdist build failed for \`${relative(ctx.options.rootDir, error.filename)}\`:\n${error.errors.map((e) => ` - ${e}`).join("\n")}`, 42 | ); 43 | } 44 | } 45 | } 46 | } 47 | await ctx.hooks.callHook("mkdist:done", ctx); 48 | 49 | if (entries.length > 0 && ctx.options.watch) { 50 | consola.warn("`mkdist` builder does not support watch mode yet."); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/builders/mkdist/types.ts: -------------------------------------------------------------------------------- 1 | import type { MkdistOptions } from "mkdist"; 2 | import type { BuildContext, BaseBuildEntry } from "../../types"; 3 | 4 | type _BaseAndMkdist = BaseBuildEntry & MkdistOptions; 5 | export interface MkdistBuildEntry extends _BaseAndMkdist { 6 | builder: "mkdist"; 7 | } 8 | 9 | export interface MkdistHooks { 10 | "mkdist:entries": ( 11 | ctx: BuildContext, 12 | entries: MkdistBuildEntry[], 13 | ) => void | Promise; 14 | "mkdist:entry:options": ( 15 | ctx: BuildContext, 16 | entry: MkdistBuildEntry, 17 | options: MkdistOptions, 18 | ) => void | Promise; 19 | "mkdist:entry:build": ( 20 | ctx: BuildContext, 21 | entry: MkdistBuildEntry, 22 | output: { writtenFiles: string[] }, 23 | ) => void | Promise; 24 | "mkdist:done": (ctx: BuildContext) => void | Promise; 25 | } 26 | -------------------------------------------------------------------------------- /src/builders/rollup/build.ts: -------------------------------------------------------------------------------- 1 | import type { OutputOptions, OutputChunk } from "rollup"; 2 | import { rollup } from "rollup"; 3 | import dts from "rollup-plugin-dts"; 4 | import { resolve } from "pathe"; 5 | import type { BuildContext } from "../../types"; 6 | import { removeShebangPlugin } from "./plugins/shebang"; 7 | import consola from "consola"; 8 | import { getRollupOptions } from "./config"; 9 | import { getChunkFilename } from "./utils"; 10 | import { rollupStub } from "./stub"; 11 | import { rollupWatch } from "./watch"; 12 | import { fixCJSExportTypePlugin } from "./plugins/cjs"; 13 | 14 | export async function rollupBuild(ctx: BuildContext): Promise { 15 | // Stub mode 16 | if (ctx.options.stub) { 17 | await rollupStub(ctx); 18 | await ctx.hooks.callHook("rollup:done", ctx); 19 | return; 20 | } 21 | 22 | // Resolve options 23 | const rollupOptions = getRollupOptions(ctx); 24 | await ctx.hooks.callHook("rollup:options", ctx, rollupOptions); 25 | 26 | // Skip build if no input entries defined 27 | if (Object.keys(rollupOptions.input as any).length === 0) { 28 | await ctx.hooks.callHook("rollup:done", ctx); 29 | return; 30 | } 31 | 32 | // Do rollup build 33 | const buildResult = await rollup(rollupOptions); 34 | await ctx.hooks.callHook("rollup:build", ctx, buildResult); 35 | 36 | // Collect info about output entries 37 | const allOutputOptions = rollupOptions.output! as OutputOptions[]; 38 | for (const outputOptions of allOutputOptions) { 39 | const { output } = await buildResult.write(outputOptions); 40 | const chunkFileNames = new Set(); 41 | const outputChunks = output.filter( 42 | (e) => e.type === "chunk", 43 | ) as OutputChunk[]; 44 | for (const entry of outputChunks) { 45 | chunkFileNames.add(entry.fileName); 46 | for (const id of entry.imports) { 47 | ctx.usedImports.add(id); 48 | } 49 | if (entry.isEntry) { 50 | ctx.buildEntries.push({ 51 | chunks: entry.imports.filter((i) => 52 | outputChunks.find((c) => c.fileName === i), 53 | ), 54 | modules: Object.entries(entry.modules).map(([id, mod]) => ({ 55 | id, 56 | bytes: mod.renderedLength, 57 | })), 58 | path: entry.fileName, 59 | bytes: Buffer.byteLength(entry.code, "utf8"), 60 | exports: entry.exports, 61 | }); 62 | } 63 | } 64 | for (const chunkFileName of chunkFileNames) { 65 | ctx.usedImports.delete(chunkFileName); 66 | } 67 | } 68 | 69 | // Watch 70 | if (ctx.options.watch) { 71 | rollupWatch(rollupOptions); 72 | // TODO: Clone rollup options to continue types watching 73 | if (ctx.options.declaration && ctx.options.watch) { 74 | consola.warn("`rollup` DTS builder does not support watch mode yet."); 75 | } 76 | return; 77 | } 78 | 79 | // Types 80 | if (ctx.options.declaration) { 81 | rollupOptions.plugins = [ 82 | ...rollupOptions.plugins, 83 | dts(ctx.options.rollup.dts), 84 | removeShebangPlugin(), 85 | ctx.options.rollup.emitCJS && fixCJSExportTypePlugin(ctx), 86 | ].filter( 87 | (plugin): plugin is NonNullable> => 88 | /** 89 | * Issue: #396 90 | * rollup-plugin-dts conflicts with rollup-plugin-commonjs: 91 | * https://github.com/Swatinem/rollup-plugin-dts?tab=readme-ov-file#what-to-expect 92 | */ 93 | !!plugin && (!("name" in plugin) || plugin.name !== "commonjs"), 94 | ); 95 | 96 | await ctx.hooks.callHook("rollup:dts:options", ctx, rollupOptions); 97 | const typesBuild = await rollup(rollupOptions); 98 | await ctx.hooks.callHook("rollup:dts:build", ctx, typesBuild); 99 | // #region cjs 100 | if (ctx.options.rollup.emitCJS) { 101 | await typesBuild.write({ 102 | dir: resolve(ctx.options.rootDir, ctx.options.outDir), 103 | entryFileNames: "[name].d.cts", 104 | chunkFileNames: (chunk) => getChunkFilename(ctx, chunk, "d.cts"), 105 | }); 106 | } 107 | // #endregion 108 | // #region mjs 109 | await typesBuild.write({ 110 | dir: resolve(ctx.options.rootDir, ctx.options.outDir), 111 | entryFileNames: "[name].d.mts", 112 | chunkFileNames: (chunk) => getChunkFilename(ctx, chunk, "d.mts"), 113 | }); 114 | // #endregion 115 | // #region .d.ts for node10 compatibility (TypeScript version < 4.7) 116 | if ( 117 | ctx.options.declaration === true || 118 | ctx.options.declaration === "compatible" 119 | ) { 120 | await typesBuild.write({ 121 | dir: resolve(ctx.options.rootDir, ctx.options.outDir), 122 | entryFileNames: "[name].d.ts", 123 | chunkFileNames: (chunk) => getChunkFilename(ctx, chunk, "d.ts"), 124 | }); 125 | } 126 | // #endregion 127 | } 128 | 129 | await ctx.hooks.callHook("rollup:done", ctx); 130 | } 131 | -------------------------------------------------------------------------------- /src/builders/rollup/config.ts: -------------------------------------------------------------------------------- 1 | import type { OutputOptions, PreRenderedChunk } from "rollup"; 2 | import commonjs from "@rollup/plugin-commonjs"; 3 | import { nodeResolve } from "@rollup/plugin-node-resolve"; 4 | import alias from "@rollup/plugin-alias"; 5 | import replace from "@rollup/plugin-replace"; 6 | import { resolve, isAbsolute } from "pathe"; 7 | import { resolveAlias } from "pathe/utils"; 8 | import { parseNodeModulePath } from "mlly"; 9 | import { arrayIncludes, getpkg, warn } from "../../utils"; 10 | import type { BuildContext, RollupOptions } from "../../types"; 11 | import { esbuild } from "./plugins/esbuild"; 12 | import { JSONPlugin } from "./plugins/json"; 13 | import { rawPlugin } from "./plugins/raw"; 14 | import { cjsPlugin } from "./plugins/cjs"; 15 | import { shebangPlugin } from "./plugins/shebang"; 16 | import { DEFAULT_EXTENSIONS, getChunkFilename, resolveAliases } from "./utils"; 17 | 18 | export function getRollupOptions(ctx: BuildContext): RollupOptions { 19 | const _aliases = resolveAliases(ctx); 20 | return { 21 | input: Object.fromEntries( 22 | ctx.options.entries 23 | .filter((entry) => entry.builder === "rollup") 24 | .map((entry) => [ 25 | entry.name, 26 | resolve(ctx.options.rootDir, entry.input), 27 | ]), 28 | ), 29 | 30 | output: [ 31 | ctx.options.rollup.emitCJS && 32 | ({ 33 | dir: resolve(ctx.options.rootDir, ctx.options.outDir), 34 | entryFileNames: "[name].cjs", 35 | chunkFileNames: (chunk: PreRenderedChunk) => 36 | getChunkFilename(ctx, chunk, "cjs"), 37 | format: "cjs", 38 | exports: "auto", 39 | interop: "compat", 40 | generatedCode: { constBindings: true }, 41 | externalLiveBindings: false, 42 | freeze: false, 43 | sourcemap: ctx.options.sourcemap, 44 | ...ctx.options.rollup.output, 45 | } satisfies OutputOptions), 46 | { 47 | dir: resolve(ctx.options.rootDir, ctx.options.outDir), 48 | entryFileNames: "[name].mjs", 49 | chunkFileNames: (chunk: PreRenderedChunk) => 50 | getChunkFilename(ctx, chunk, "mjs"), 51 | format: "esm", 52 | exports: "auto", 53 | generatedCode: { constBindings: true }, 54 | externalLiveBindings: false, 55 | freeze: false, 56 | sourcemap: ctx.options.sourcemap, 57 | ...ctx.options.rollup.output, 58 | } satisfies OutputOptions, 59 | ].filter(Boolean) as OutputOptions[], 60 | 61 | external(originalId): boolean { 62 | // Resolve aliases 63 | const resolvedId = resolveAlias(originalId, _aliases); 64 | 65 | // Try to guess package name of id 66 | const pkgName = 67 | parseNodeModulePath(resolvedId)?.name || 68 | parseNodeModulePath(originalId)?.name || 69 | getpkg(originalId); 70 | 71 | // Check for explicit external rules 72 | if ( 73 | arrayIncludes(ctx.options.externals, pkgName) || 74 | arrayIncludes(ctx.options.externals, originalId) || 75 | arrayIncludes(ctx.options.externals, resolvedId) 76 | ) { 77 | return true; 78 | } 79 | 80 | // Source is always bundled 81 | for (const id of [originalId, resolvedId]) { 82 | if ( 83 | id[0] === "." || 84 | isAbsolute(id) || 85 | /src[/\\]/.test(id) || 86 | id.startsWith(ctx.pkg.name!) 87 | ) { 88 | return false; 89 | } 90 | } 91 | 92 | // Check for other explicit inline rules 93 | if ( 94 | ctx.options.rollup.inlineDependencies === true || 95 | (Array.isArray(ctx.options.rollup.inlineDependencies) && 96 | (arrayIncludes(ctx.options.rollup.inlineDependencies, pkgName) || 97 | arrayIncludes(ctx.options.rollup.inlineDependencies, originalId) || 98 | arrayIncludes(ctx.options.rollup.inlineDependencies, resolvedId))) 99 | ) { 100 | return false; 101 | } 102 | 103 | // Inline by default, but also show a warning, since it is an implicit behavior 104 | warn(ctx, `Implicitly bundling "${originalId}"`); 105 | return false; 106 | }, 107 | 108 | onwarn(warning, rollupWarn): void { 109 | if (!warning.code || !["CIRCULAR_DEPENDENCY"].includes(warning.code)) { 110 | rollupWarn(warning); 111 | } 112 | }, 113 | 114 | plugins: [ 115 | ctx.options.rollup.replace && 116 | replace({ 117 | ...ctx.options.rollup.replace, 118 | values: { 119 | ...ctx.options.replace, 120 | ...ctx.options.rollup.replace.values, 121 | }, 122 | }), 123 | 124 | ctx.options.rollup.alias && 125 | alias({ 126 | ...ctx.options.rollup.alias, 127 | entries: _aliases, 128 | }), 129 | 130 | ctx.options.rollup.resolve && 131 | nodeResolve({ 132 | extensions: DEFAULT_EXTENSIONS, 133 | exportConditions: ["production"], 134 | ...ctx.options.rollup.resolve, 135 | }), 136 | 137 | ctx.options.rollup.json && 138 | JSONPlugin({ 139 | ...ctx.options.rollup.json, 140 | }), 141 | 142 | shebangPlugin(), 143 | 144 | ctx.options.rollup.esbuild && 145 | esbuild({ 146 | sourcemap: ctx.options.sourcemap, 147 | ...ctx.options.rollup.esbuild, 148 | }), 149 | 150 | ctx.options.rollup.commonjs && 151 | commonjs({ 152 | extensions: DEFAULT_EXTENSIONS, 153 | ...ctx.options.rollup.commonjs, 154 | }), 155 | 156 | ctx.options.rollup.preserveDynamicImports && { 157 | name: "unbuild=preserve-dynamic-imports", 158 | renderDynamicImport(): { left: string; right: string } { 159 | return { left: "import(", right: ")" }; 160 | }, 161 | }, 162 | 163 | ctx.options.rollup.cjsBridge && cjsPlugin({}), 164 | 165 | rawPlugin(), 166 | ].filter((p): p is NonNullable> => !!p), 167 | } satisfies RollupOptions; 168 | } 169 | -------------------------------------------------------------------------------- /src/builders/rollup/index.ts: -------------------------------------------------------------------------------- 1 | export { rollupBuild } from "./build"; 2 | -------------------------------------------------------------------------------- /src/builders/rollup/plugins/cjs.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from "rollup"; 2 | import { findStaticImports } from "mlly"; 3 | import MagicString from "magic-string"; 4 | import type { BuildContext } from "../../../types.ts"; 5 | import { FixDtsDefaultCjsExportsPlugin } from "fix-dts-default-cjs-exports/rollup"; 6 | 7 | export function cjsPlugin(_opts?: any): Plugin { 8 | return { 9 | name: "unbuild-cjs", 10 | renderChunk(code, _chunk, opts) { 11 | if (opts.format === "es") { 12 | return CJSToESM(code); 13 | } 14 | return null; 15 | }, 16 | } as Plugin; 17 | } 18 | 19 | export function fixCJSExportTypePlugin(ctx: BuildContext): Plugin { 20 | const regexp = 21 | ctx.options.declaration === "node16" 22 | ? /\.d\.cts$/ // d.cts only 23 | : /\.d\.c?ts$/; // d.ts and d.cts 24 | return FixDtsDefaultCjsExportsPlugin({ 25 | warn: (msg) => ctx.warnings.add(msg), 26 | matcher: (info) => { 27 | return ( 28 | info.type === "chunk" && 29 | info.exports?.length > 0 && 30 | info.exports.includes("default") && 31 | regexp.test(info.fileName) && 32 | info.isEntry 33 | ); 34 | }, 35 | }); 36 | } 37 | 38 | const CJSyntaxRe = /__filename|__dirname|require\(|require\.resolve\(/; 39 | 40 | const CJSShim = ` 41 | 42 | // -- Unbuild CommonJS Shims -- 43 | import __cjs_url__ from 'url'; 44 | import __cjs_path__ from 'path'; 45 | import __cjs_mod__ from 'module'; 46 | const __filename = __cjs_url__.fileURLToPath(import.meta.url); 47 | const __dirname = __cjs_path__.dirname(__filename); 48 | const require = __cjs_mod__.createRequire(import.meta.url); 49 | `; 50 | 51 | // Shim __dirname, __filename and require 52 | function CJSToESM(code: string): { code: string; map: any } | null { 53 | if (code.includes(CJSShim) || !CJSyntaxRe.test(code)) { 54 | return null; 55 | } 56 | 57 | const lastESMImport = findStaticImports(code).pop(); 58 | const indexToAppend = lastESMImport ? lastESMImport.end : 0; 59 | const s = new MagicString(code); 60 | s.appendRight(indexToAppend, CJSShim); 61 | 62 | return { 63 | code: s.toString(), 64 | map: s.generateMap(), 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /src/builders/rollup/plugins/esbuild.ts: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/egoist/rollup-plugin-esbuild and nitropack fork (MIT) 2 | import type { Plugin, PluginContext } from "rollup"; 3 | import type { FilterPattern } from "@rollup/pluginutils"; 4 | import type { Loader, TransformResult, CommonOptions } from "esbuild"; 5 | import { transform } from "esbuild"; 6 | import { extname, relative } from "pathe"; 7 | import { createFilter } from "@rollup/pluginutils"; 8 | 9 | const DefaultLoaders: { [ext: string]: Loader } = { 10 | ".js": "js", 11 | ".mjs": "js", 12 | ".cjs": "js", 13 | 14 | ".ts": "ts", 15 | ".mts": "ts", 16 | ".cts": "ts", 17 | 18 | ".tsx": "tsx", 19 | ".jsx": "jsx", 20 | }; 21 | 22 | export type EsbuildOptions = CommonOptions & { 23 | include?: FilterPattern; 24 | exclude?: FilterPattern; 25 | 26 | /** 27 | * Map extension to esbuild loader 28 | * Note that each entry (the extension) needs to start with a dot 29 | */ 30 | loaders?: { 31 | [ext: string]: Loader | false; 32 | }; 33 | }; 34 | 35 | export function esbuild(options: EsbuildOptions): Plugin { 36 | // Extract esBuild options from additional options and apply defaults 37 | const { 38 | include = new RegExp(Object.keys(DefaultLoaders).join("|")), 39 | exclude = /node_modules/, 40 | loaders: loaderOptions, 41 | ...esbuildOptions 42 | } = options; 43 | 44 | // Rsolve loaders 45 | const loaders = { ...DefaultLoaders }; 46 | if (loaderOptions) { 47 | for (const [key, value] of Object.entries(loaderOptions)) { 48 | if (typeof value === "string") { 49 | loaders[key] = value; 50 | } else if (value === false) { 51 | delete loaders[key]; 52 | } 53 | } 54 | } 55 | const getLoader = (id = ""): Loader | undefined => { 56 | return loaders[extname(id)]; 57 | }; 58 | 59 | const filter = createFilter(include, exclude); 60 | 61 | return { 62 | name: "esbuild", 63 | 64 | async transform(code, id): Promise { 65 | if (!filter(id)) { 66 | return null; 67 | } 68 | 69 | const loader = getLoader(id); 70 | if (!loader) { 71 | return null; 72 | } 73 | 74 | const result = await transform(code, { 75 | ...esbuildOptions, 76 | loader, 77 | sourcefile: id, 78 | }); 79 | 80 | printWarnings(id, result, this); 81 | 82 | return { 83 | code: result.code || "", 84 | map: result.map || null, 85 | }; 86 | }, 87 | 88 | async renderChunk( 89 | code, 90 | { fileName }, 91 | ): Promise { 92 | if (!options.minify) { 93 | return null; 94 | } 95 | if (/\.d\.(c|m)?tsx?$/.test(fileName)) { 96 | return null; 97 | } 98 | const loader = getLoader(fileName); 99 | if (!loader) { 100 | return null; 101 | } 102 | const result = await transform(code, { 103 | ...esbuildOptions, 104 | loader, 105 | sourcefile: fileName, 106 | minify: true, 107 | }); 108 | return { 109 | code: result.code || "", 110 | map: result.map || null, 111 | }; 112 | }, 113 | }; 114 | } 115 | 116 | function printWarnings( 117 | id: string, 118 | result: TransformResult, 119 | plugin: PluginContext, 120 | ): void { 121 | if (result.warnings) { 122 | for (const warning of result.warnings) { 123 | let message = "[esbuild]"; 124 | if (warning.location) { 125 | message += ` (${relative(process.cwd(), id)}:${warning.location.line}:${ 126 | warning.location.column 127 | })`; 128 | } 129 | message += ` ${warning.text}`; 130 | plugin.warn(message); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/builders/rollup/plugins/json.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin, TransformHook, TransformResult } from "rollup"; 2 | import type { RollupJsonOptions } from "@rollup/plugin-json"; 3 | import rollupJSONPlugin from "@rollup/plugin-json"; 4 | 5 | const EXPORT_DEFAULT = "export default "; 6 | 7 | export function JSONPlugin(options: RollupJsonOptions): Plugin { 8 | const plugin = rollupJSONPlugin(options); 9 | return { 10 | ...plugin, 11 | name: "unbuild-json", 12 | transform(code, id): TransformResult { 13 | const res = (plugin.transform as TransformHook)!.call(this, code, id); 14 | if ( 15 | res && 16 | typeof res !== "string" && 17 | "code" in res && 18 | res.code && 19 | res.code.startsWith(EXPORT_DEFAULT) 20 | ) { 21 | res.code = res.code.replace(EXPORT_DEFAULT, "module.exports = "); 22 | } 23 | return res; 24 | }, 25 | } satisfies Plugin; 26 | } 27 | -------------------------------------------------------------------------------- /src/builders/rollup/plugins/raw.ts: -------------------------------------------------------------------------------- 1 | import { createFilter } from "@rollup/pluginutils"; 2 | import type { FilterPattern } from "@rollup/pluginutils"; 3 | import type { Plugin } from "rollup"; 4 | 5 | export interface RawLoaderOptions { 6 | include?: FilterPattern; 7 | exclude?: FilterPattern; 8 | } 9 | 10 | const defaults: RawLoaderOptions = { 11 | include: [/\.(md|txt|css|htm|html)$/], 12 | exclude: [], 13 | }; 14 | 15 | export function rawPlugin(opts: RawLoaderOptions = {}): Plugin { 16 | opts = { ...opts, ...defaults }; 17 | const filter = createFilter(opts.include, opts.exclude); 18 | return { 19 | name: "unbuild-raw", 20 | transform(code, id): { code: string; map: any } | undefined { 21 | if (filter(id)) { 22 | return { 23 | code: `export default ${JSON.stringify(code)}`, 24 | map: null, 25 | }; 26 | } 27 | }, 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/builders/rollup/plugins/shebang.ts: -------------------------------------------------------------------------------- 1 | import { promises as fsp } from "node:fs"; 2 | import { resolve } from "pathe"; 3 | import type { Plugin } from "rollup"; 4 | 5 | // Forked from https://github.com/developit/rollup-plugin-preserve-shebang (1.0.1 @ MIT) 6 | 7 | const SHEBANG_RE = /^#![^\n]*/; 8 | 9 | export function shebangPlugin(): Plugin { 10 | return { 11 | name: "unbuild-shebang", 12 | async writeBundle(options, bundle): Promise { 13 | for (const [fileName, output] of Object.entries(bundle)) { 14 | if (output.type !== "chunk") { 15 | continue; 16 | } 17 | if (output.code?.match(SHEBANG_RE)) { 18 | const outFile = resolve(options.dir!, fileName); 19 | await makeExecutable(outFile); 20 | } 21 | } 22 | }, 23 | }; 24 | } 25 | 26 | export function removeShebangPlugin(): Plugin { 27 | return { 28 | name: "unbuild-remove-shebang", 29 | renderChunk(code): string { 30 | return code.replace(SHEBANG_RE, ""); 31 | }, 32 | }; 33 | } 34 | 35 | export async function makeExecutable(filePath: string): Promise { 36 | await fsp.chmod(filePath, 0o755 /* rwx r-x r-x */).catch(() => {}); 37 | } 38 | 39 | export function getShebang(code: string, append = "\n"): string { 40 | const m = code.match(SHEBANG_RE); 41 | return m ? m + append : ""; 42 | } 43 | -------------------------------------------------------------------------------- /src/builders/rollup/stub.ts: -------------------------------------------------------------------------------- 1 | import { writeFile, mkdir } from "node:fs/promises"; 2 | import { promises as fsp } from "node:fs"; 3 | import { resolve, dirname, extname, relative } from "pathe"; 4 | import { resolvePath, resolveModuleExportNames, fileURLToPath } from "mlly"; 5 | import { warn } from "../../utils"; 6 | import type { BuildContext } from "../../types"; 7 | import { makeExecutable, getShebang } from "./plugins/shebang"; 8 | import { DEFAULT_EXTENSIONS, resolveAliases } from "./utils"; 9 | 10 | export async function rollupStub(ctx: BuildContext): Promise { 11 | const babelPlugins = ctx.options.stubOptions.jiti.transformOptions?.babel 12 | ?.plugins as any; 13 | const importedBabelPlugins: Array = []; 14 | const serializedJitiOptions = JSON.stringify( 15 | { 16 | ...ctx.options.stubOptions.jiti, 17 | alias: { 18 | ...resolveAliases(ctx), 19 | ...ctx.options.stubOptions.jiti.alias, 20 | }, 21 | transformOptions: { 22 | ...ctx.options.stubOptions.jiti.transformOptions, 23 | babel: { 24 | ...ctx.options.stubOptions.jiti.transformOptions?.babel, 25 | plugins: "__$BABEL_PLUGINS", 26 | }, 27 | }, 28 | }, 29 | null, 30 | 2, 31 | ).replace( 32 | '"__$BABEL_PLUGINS"', 33 | Array.isArray(babelPlugins) 34 | ? "[" + 35 | babelPlugins 36 | .map((plugin: string | Array, i) => { 37 | if (Array.isArray(plugin)) { 38 | const [name, ...args] = plugin; 39 | importedBabelPlugins.push(name); 40 | return ( 41 | `[` + 42 | [ 43 | `plugin${i}`, 44 | ...args.map((val) => JSON.stringify(val)), 45 | ].join(", ") + 46 | "]" 47 | ); 48 | } else { 49 | importedBabelPlugins.push(plugin); 50 | return `plugin${i}`; 51 | } 52 | }) 53 | .join(",") + 54 | "]" 55 | : "[]", 56 | ); 57 | 58 | for (const entry of ctx.options.entries.filter( 59 | (entry) => entry.builder === "rollup", 60 | )) { 61 | const output = resolve( 62 | ctx.options.rootDir, 63 | ctx.options.outDir, 64 | entry.name!, 65 | ); 66 | 67 | const isESM = ctx.pkg.type === "module"; 68 | const resolvedEntry = fileURLToPath(ctx.jiti.esmResolve(entry.input)!); 69 | const resolvedEntryWithoutExt = resolvedEntry.slice( 70 | 0, 71 | Math.max(0, resolvedEntry.length - extname(resolvedEntry).length), 72 | ); 73 | const resolvedEntryForTypeImport = isESM 74 | ? `${resolvedEntry.replace(/(\.m?)(ts)$/, "$1js")}` 75 | : resolvedEntryWithoutExt; 76 | const code = await fsp.readFile(resolvedEntry, "utf8"); 77 | const shebang = getShebang(code); 78 | 79 | await mkdir(dirname(output), { recursive: true }); 80 | 81 | // CJS Stub 82 | if (ctx.options.rollup.emitCJS) { 83 | const jitiCJSPath = relative( 84 | dirname(output), 85 | await resolvePath("jiti", { 86 | url: import.meta.url, 87 | conditions: ["node", "require"], 88 | }), 89 | ); 90 | await writeFile( 91 | output + ".cjs", 92 | shebang + 93 | [ 94 | `const { createJiti } = require(${JSON.stringify(jitiCJSPath)})`, 95 | ...importedBabelPlugins.map( 96 | (plugin, i) => 97 | `const plugin${i} = require(${JSON.stringify(plugin)})`, 98 | ), 99 | "", 100 | `const jiti = createJiti(__filename, ${serializedJitiOptions})`, 101 | "", 102 | `/** @type {import(${JSON.stringify( 103 | resolvedEntryForTypeImport, 104 | )})} */`, 105 | `module.exports = jiti(${JSON.stringify(resolvedEntry)})`, 106 | ].join("\n"), 107 | ); 108 | } 109 | 110 | // MJS Stub 111 | // Try to analyze exports 112 | const namedExports: string[] = await resolveModuleExportNames( 113 | resolvedEntry, 114 | { 115 | extensions: DEFAULT_EXTENSIONS, 116 | }, 117 | ).catch((error) => { 118 | warn(ctx, `Cannot analyze ${resolvedEntry} for exports:` + error); 119 | return []; 120 | }); 121 | const hasDefaultExport = 122 | namedExports.includes("default") || namedExports.length === 0; 123 | 124 | const jitiESMPath = relative( 125 | dirname(output), 126 | await resolvePath("jiti", { 127 | url: import.meta.url, 128 | conditions: ["node", "import"], 129 | }), 130 | ); 131 | 132 | await writeFile( 133 | output + ".mjs", 134 | shebang + 135 | [ 136 | `import { createJiti } from ${JSON.stringify(jitiESMPath)};`, 137 | ...importedBabelPlugins.map( 138 | (plugin, i) => `import plugin${i} from ${JSON.stringify(plugin)}`, 139 | ), 140 | "", 141 | `const jiti = createJiti(import.meta.url, ${serializedJitiOptions})`, 142 | "", 143 | `/** @type {import(${JSON.stringify(resolvedEntryForTypeImport)})} */`, 144 | `const _module = await jiti.import(${JSON.stringify( 145 | resolvedEntry, 146 | )});`, 147 | hasDefaultExport 148 | ? "\nexport default _module?.default ?? _module;" 149 | : "", 150 | ...namedExports 151 | .filter((name) => name !== "default") 152 | .map((name) => `export const ${name} = _module.${name};`), 153 | ].join("\n"), 154 | ); 155 | 156 | // DTS Stub 157 | if (ctx.options.declaration) { 158 | const dtsContent = [ 159 | `export * from ${JSON.stringify(resolvedEntryForTypeImport)};`, 160 | hasDefaultExport 161 | ? `export { default } from ${JSON.stringify(resolvedEntryForTypeImport)};` 162 | : "", 163 | ].join("\n"); 164 | await writeFile(output + ".d.cts", dtsContent); 165 | await writeFile(output + ".d.mts", dtsContent); 166 | if ( 167 | ctx.options.declaration === "compatible" || 168 | ctx.options.declaration === true 169 | ) { 170 | await writeFile(output + ".d.ts", dtsContent); 171 | } 172 | } 173 | 174 | if (shebang) { 175 | await makeExecutable(output + ".cjs"); 176 | await makeExecutable(output + ".mjs"); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/builders/rollup/types.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | RollupOptions as _RollupOptions, 3 | RollupBuild, 4 | OutputOptions, 5 | InputPluginOption, 6 | Plugin, 7 | } from "rollup"; 8 | import type { RollupReplaceOptions } from "@rollup/plugin-replace"; 9 | import type { RollupAliasOptions } from "@rollup/plugin-alias"; 10 | import type { RollupNodeResolveOptions } from "@rollup/plugin-node-resolve"; 11 | import type { RollupJsonOptions } from "@rollup/plugin-json"; 12 | import type { Options as RollupDtsOptions } from "rollup-plugin-dts"; 13 | import type commonjs from "@rollup/plugin-commonjs"; 14 | import type { BaseBuildEntry } from "../../types"; 15 | import type { BuildContext } from "../../types"; 16 | import type { EsbuildOptions } from "./plugins/esbuild"; 17 | 18 | export type RollupCommonJSOptions = Parameters[0] & {}; 19 | 20 | export interface RollupBuildEntry extends BaseBuildEntry { 21 | builder: "rollup"; 22 | } 23 | 24 | export interface RollupBuildOptions { 25 | /** 26 | * If enabled, unbuild generates a CommonJS build in addition to the ESM build. 27 | */ 28 | emitCJS?: boolean; 29 | 30 | /** 31 | * Enable experimental active watcher 32 | * 33 | * @experimental 34 | */ 35 | watch?: boolean; 36 | 37 | /** 38 | * If enabled, unbuild generates CommonJS polyfills for ESM builds. 39 | */ 40 | cjsBridge?: boolean; 41 | 42 | /** 43 | * Preserve dynamic imports as-is 44 | */ 45 | preserveDynamicImports?: boolean; 46 | 47 | /** 48 | * Whether to inline dependencies not explicitly set in "dependencies" or "peerDependencies" or as marked externals to the bundle. 49 | * 50 | * If set to true, all such dependencies will be inlined. 51 | * If an array of string or regular expressions is passed, these will be used to determine whether to inline such a dependency. 52 | */ 53 | inlineDependencies?: boolean | Array; 54 | 55 | /** 56 | * Rollup [Output Options](https://rollupjs.org/configuration-options) 57 | */ 58 | output?: OutputOptions; 59 | 60 | /** 61 | * Replace plugin options 62 | * Set to `false` to disable the plugin. 63 | * Read more: [@rollup/plugin-replace](https://www.npmjs.com/package/@rollup/plugin-replace) 64 | */ 65 | replace: RollupReplaceOptions | false; 66 | 67 | /** 68 | * Alias plugin options 69 | * Set to `false` to disable the plugin. 70 | * Read more: [@rollup/plugin-alias](https://www.npmjs.com/package/@rollup/plugin-alias) 71 | */ 72 | alias: RollupAliasOptions | false; 73 | 74 | /** 75 | * Resolve plugin options 76 | * Set to `false` to disable the plugin. 77 | * Read more: [@rollup/plugin-node-resolve](https://www.npmjs.com/package/@rollup/plugin-node-resolve) 78 | */ 79 | resolve: RollupNodeResolveOptions | false; 80 | 81 | /** 82 | * JSON plugin options 83 | * Set to `false` to disable the plugin. 84 | * Read more: [@rollup/plugin-json](https://www.npmjs.com/package/@rollup/plugin-json) 85 | */ 86 | json: RollupJsonOptions | false; 87 | 88 | /** 89 | * ESBuild plugin options 90 | * Set to `false` to disable the plugin. 91 | * Read more: [esbuild](https://www.npmjs.com/package/esbuild) 92 | */ 93 | esbuild: EsbuildOptions | false; 94 | 95 | /** 96 | * CommonJS plugin options 97 | * Set to `false` to disable the plugin. 98 | * Read more: [@rollup/plugin-commonjs](https://www.npmjs.com/package/@rollup/plugin-commonjs) 99 | */ 100 | commonjs: RollupCommonJSOptions | false; 101 | 102 | /** 103 | * DTS plugin options 104 | * Set to `false` to disable the plugin. 105 | * Read more: [rollup-plugin-dts](https://www.npmjs.com/package/rollup-plugin-dts) 106 | */ 107 | dts: RollupDtsOptions; 108 | } 109 | 110 | export interface RollupOptions extends _RollupOptions { 111 | plugins: Plugin[]; 112 | } 113 | 114 | export interface RollupHooks { 115 | "rollup:options": ( 116 | ctx: BuildContext, 117 | options: RollupOptions, 118 | ) => void | Promise; 119 | "rollup:build": ( 120 | ctx: BuildContext, 121 | build: RollupBuild, 122 | ) => void | Promise; 123 | "rollup:dts:options": ( 124 | ctx: BuildContext, 125 | options: RollupOptions, 126 | ) => void | Promise; 127 | "rollup:dts:build": ( 128 | ctx: BuildContext, 129 | build: RollupBuild, 130 | ) => void | Promise; 131 | "rollup:done": (ctx: BuildContext) => void | Promise; 132 | } 133 | -------------------------------------------------------------------------------- /src/builders/rollup/utils.ts: -------------------------------------------------------------------------------- 1 | import type { PreRenderedChunk } from "rollup"; 2 | import type { BuildContext } from "../../types"; 3 | 4 | export const DEFAULT_EXTENSIONS: string[] = [ 5 | ".ts", 6 | ".tsx", 7 | ".mts", 8 | ".cts", 9 | ".mjs", 10 | ".cjs", 11 | ".js", 12 | ".jsx", 13 | ".json", 14 | ]; 15 | 16 | export function resolveAliases(ctx: BuildContext): Record { 17 | const aliases: Record = { 18 | [ctx.pkg.name!]: ctx.options.rootDir, 19 | ...ctx.options.alias, 20 | }; 21 | 22 | if (ctx.options.rollup.alias) { 23 | if (Array.isArray(ctx.options.rollup.alias.entries)) { 24 | Object.assign( 25 | aliases, 26 | Object.fromEntries( 27 | ctx.options.rollup.alias.entries.map((entry) => { 28 | return [entry.find, entry.replacement]; 29 | }), 30 | ), 31 | ); 32 | } else { 33 | Object.assign( 34 | aliases, 35 | ctx.options.rollup.alias.entries || ctx.options.rollup.alias, 36 | ); 37 | } 38 | } 39 | 40 | return aliases; 41 | } 42 | 43 | export function getChunkFilename( 44 | ctx: BuildContext, 45 | chunk: PreRenderedChunk, 46 | ext: string, 47 | ): string { 48 | if (chunk.isDynamicEntry) { 49 | return `chunks/[name].${ext}`; 50 | } 51 | // TODO: Find a way to generate human friendly hash for short groups 52 | return `shared/${ctx.options.name}.[hash].${ext}`; 53 | } 54 | -------------------------------------------------------------------------------- /src/builders/rollup/watch.ts: -------------------------------------------------------------------------------- 1 | import { relative } from "pathe"; 2 | import { watch as _rollupWatch } from "rollup"; 3 | import type { RollupOptions } from "../../types"; 4 | import consola from "consola"; 5 | import { colors } from "consola/utils"; 6 | 7 | export function rollupWatch(rollupOptions: RollupOptions): void { 8 | const watcher = _rollupWatch(rollupOptions); 9 | 10 | let inputs: string[]; 11 | if (Array.isArray(rollupOptions.input)) { 12 | inputs = rollupOptions.input; 13 | } else if (typeof rollupOptions.input === "string") { 14 | inputs = [rollupOptions.input]; 15 | } else { 16 | inputs = Object.keys(rollupOptions.input || {}); 17 | } 18 | consola.info( 19 | `[unbuild] [rollup] Starting watchers for entries: ${inputs.map((input) => "./" + relative(process.cwd(), input)).join(", ")}`, 20 | ); 21 | 22 | consola.warn( 23 | "[unbuild] [rollup] Watch mode is experimental and may be unstable", 24 | ); 25 | 26 | watcher.on("change", (id, { event }) => { 27 | consola.info(`${colors.cyan(relative(".", id))} was ${event}d`); 28 | }); 29 | 30 | watcher.on("restart", () => { 31 | consola.info(colors.gray("[unbuild] [rollup] Rebuilding bundle")); 32 | }); 33 | 34 | watcher.on("event", (event) => { 35 | if (event.code === "END") { 36 | consola.success(colors.green("[unbuild] [rollup] Rebuild finished\n")); 37 | } 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /src/builders/untyped/index.ts: -------------------------------------------------------------------------------- 1 | import { writeFile } from "node:fs/promises"; 2 | import { resolve } from "pathe"; 3 | import { 4 | resolveSchema, 5 | generateTypes, 6 | generateMarkdown, 7 | type InputObject, 8 | } from "untyped"; 9 | import untypedPlugin from "untyped/babel-plugin"; 10 | import { pascalCase } from "scule"; 11 | import type { 12 | BuildContext, 13 | UntypedBuildEntry, 14 | UntypedOutputs, 15 | } from "../../types"; 16 | import consola from "consola"; 17 | import { createJiti } from "jiti"; 18 | 19 | export async function typesBuild(ctx: BuildContext): Promise { 20 | const entries = ctx.options.entries.filter( 21 | (entry) => entry.builder === "untyped", 22 | ) as UntypedBuildEntry[]; 23 | await ctx.hooks.callHook("untyped:entries", ctx, entries); 24 | 25 | for (const entry of entries) { 26 | const options = { 27 | jiti: { 28 | interopDefault: true, 29 | transformOptions: { 30 | babel: { 31 | plugins: [untypedPlugin], 32 | }, 33 | }, 34 | }, 35 | }; 36 | await ctx.hooks.callHook("untyped:entry:options", ctx, entry, options); 37 | 38 | const untypedJiti = createJiti(ctx.options.rootDir, options.jiti); 39 | 40 | const distDir = entry.outDir!; 41 | 42 | let rawSchema = 43 | ((await untypedJiti.import(resolve(ctx.options.rootDir, entry.input), { 44 | try: true, 45 | })) as InputObject) || ({} as InputObject); 46 | 47 | const rawSchemaKeys = Object.keys(rawSchema); 48 | if (rawSchemaKeys.length === 1 && rawSchemaKeys[0] === "default") { 49 | rawSchema = (rawSchema as any).default; 50 | } 51 | 52 | const defaults = entry.defaults || {}; 53 | const schema = await resolveSchema(rawSchema, defaults); 54 | 55 | await ctx.hooks.callHook("untyped:entry:schema", ctx, entry, schema); 56 | 57 | const outputs: UntypedOutputs = { 58 | markdown: { 59 | fileName: resolve(distDir, `${entry.name}.md`), 60 | contents: generateMarkdown(schema), 61 | }, 62 | schema: { 63 | fileName: `${entry.name}.schema.json`, 64 | contents: JSON.stringify(schema, null, 2), 65 | }, 66 | defaults: { 67 | fileName: `${entry.name}.defaults.json`, 68 | contents: JSON.stringify(defaults, null, 2), 69 | }, 70 | declaration: entry.declaration 71 | ? { 72 | fileName: `${entry.name}.d.ts`, 73 | contents: generateTypes(schema, { 74 | interfaceName: pascalCase(entry.name + "-schema"), 75 | }), 76 | } 77 | : undefined, 78 | }; 79 | await ctx.hooks.callHook("untyped:entry:outputs", ctx, entry, outputs); 80 | for (const output of Object.values(outputs)) { 81 | if (!output) continue; // declaration is optional 82 | await writeFile( 83 | resolve(distDir, output.fileName), 84 | output.contents, 85 | "utf8", 86 | ); 87 | } 88 | } 89 | await ctx.hooks.callHook("untyped:done", ctx); 90 | 91 | if (entries.length > 0 && ctx.options.watch) { 92 | consola.warn("`untyped` builder does not support watch mode yet."); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/builders/untyped/types.ts: -------------------------------------------------------------------------------- 1 | import type { Schema } from "untyped"; 2 | import type { BaseBuildEntry, BuildContext } from "../../types"; 3 | 4 | export interface UntypedBuildEntry extends BaseBuildEntry { 5 | builder: "untyped"; 6 | defaults?: Record; 7 | } 8 | 9 | export interface UntypedOutput { 10 | fileName: string; 11 | contents: string; 12 | } 13 | 14 | export interface UntypedOutputs { 15 | markdown: UntypedOutput; 16 | schema: UntypedOutput; 17 | defaults: UntypedOutput; 18 | declaration?: UntypedOutput; 19 | } 20 | 21 | export interface UntypedHooks { 22 | "untyped:entries": ( 23 | ctx: BuildContext, 24 | entries: UntypedBuildEntry[], 25 | ) => void | Promise; 26 | "untyped:entry:options": ( 27 | ctx: BuildContext, 28 | entry: UntypedBuildEntry, 29 | options: any, 30 | ) => void | Promise; 31 | "untyped:entry:schema": ( 32 | ctx: BuildContext, 33 | entry: UntypedBuildEntry, 34 | schema: Schema, 35 | ) => void | Promise; 36 | "untyped:entry:outputs": ( 37 | ctx: BuildContext, 38 | entry: UntypedBuildEntry, 39 | outputs: UntypedOutputs, 40 | ) => void | Promise; 41 | "untyped:done": (ctx: BuildContext) => void | Promise; 42 | } 43 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { defineCommand, runMain } from "citty"; 3 | import consola from "consola"; 4 | import { resolve } from "pathe"; 5 | import { name, version, description } from "../package.json"; 6 | import { build } from "./build"; 7 | 8 | const main = defineCommand({ 9 | meta: { 10 | name, 11 | version, 12 | description, 13 | }, 14 | args: { 15 | dir: { 16 | type: "positional", 17 | description: "The directory to build", 18 | required: false, 19 | }, 20 | config: { 21 | type: "string", 22 | description: [ 23 | "The configuration file to use relative to the current working directory.", 24 | " Unbuild tries to read `build.config` from the build `DIR` by default.", 25 | "", 26 | ].join("\n"), 27 | }, 28 | watch: { 29 | type: "boolean", 30 | description: "Watch the src dir and rebuild on change (experimental)", 31 | }, 32 | stub: { 33 | type: "boolean", 34 | description: "Stub the package for JIT compilation", 35 | }, 36 | minify: { 37 | type: "boolean", 38 | description: "Minify build", 39 | }, 40 | sourcemap: { 41 | type: "boolean", 42 | description: "Generate sourcemaps (experimental)", 43 | }, 44 | parallel: { 45 | type: "boolean", 46 | description: 47 | "Run different types of builds (untyped, mkdist, Rollup, copy) simultaneously.", 48 | }, 49 | }, 50 | async run({ args }) { 51 | const rootDir = resolve(process.cwd(), args.dir || "."); 52 | await build(rootDir, args.stub, { 53 | sourcemap: args.sourcemap, 54 | config: args.config ? resolve(args.config) : undefined, 55 | stub: args.stub, 56 | watch: args.watch, 57 | rollup: { 58 | esbuild: { 59 | minify: args.minify, 60 | }, 61 | }, 62 | }).catch((error) => { 63 | consola.error(`Error building ${rootDir}: ${error}`); 64 | throw error; 65 | }); 66 | }, 67 | }); 68 | 69 | runMain(main); 70 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./build"; 2 | export * from "./types"; 3 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { PackageJson } from "pkg-types"; 2 | import type { Hookable } from "hookable"; 3 | import type { RollupOptions as _RollupOptions, WatcherOptions } from "rollup"; 4 | import type { Jiti, JitiOptions } from "jiti"; 5 | import type { 6 | RollupBuildEntry, 7 | RollupBuildOptions, 8 | RollupHooks, 9 | } from "./builders/rollup/types"; 10 | import type { MkdistBuildEntry, MkdistHooks } from "./builders/mkdist/types"; 11 | import type { CopyBuildEntry, CopyHooks } from "./builders/copy/types"; 12 | import type { UntypedBuildEntry, UntypedHooks } from "./builders/untyped/types"; 13 | 14 | export interface BaseBuildEntry { 15 | builder?: "untyped" | "rollup" | "mkdist" | "copy"; 16 | input: string; 17 | name?: string; 18 | outDir?: string; 19 | declaration?: "compatible" | "node16" | boolean; 20 | } 21 | 22 | /** Bundler types */ 23 | export type { 24 | RollupBuildEntry, 25 | RollupBuildOptions, 26 | RollupOptions, 27 | } from "./builders/rollup/types"; 28 | export type { MkdistBuildEntry } from "./builders/mkdist/types"; 29 | export type { CopyBuildEntry } from "./builders/copy/types"; 30 | export type { 31 | UntypedBuildEntry, 32 | UntypedOutput, 33 | UntypedOutputs, 34 | } from "./builders/untyped/types"; 35 | 36 | export type BuildEntry = 37 | | BaseBuildEntry 38 | | RollupBuildEntry 39 | | UntypedBuildEntry 40 | | MkdistBuildEntry 41 | | CopyBuildEntry; 42 | 43 | export interface BuildOptions { 44 | /** 45 | * The name of the project. 46 | */ 47 | name: string; 48 | 49 | /** 50 | * The root directory of the project. 51 | */ 52 | rootDir: string; 53 | 54 | /** 55 | * Build entries. 56 | */ 57 | entries: BuildEntry[]; 58 | 59 | /** 60 | * Clean the output directory before building. 61 | */ 62 | clean: boolean; 63 | 64 | /** 65 | * @experimental 66 | * Generate source mapping file. 67 | */ 68 | sourcemap: boolean; 69 | 70 | /** 71 | * Whether to generate declaration files. 72 | * * `compatible` means "src/index.ts" will generate "dist/index.d.mts", "dist/index.d.cts" and "dist/index.d.ts". 73 | * * `node16` means "src/index.ts" will generate "dist/index.d.mts" and "dist/index.d.cts". 74 | * * `true` is equivalent to `compatible`. 75 | * * `false` will disable declaration generation. 76 | * * `undefined` will auto detect based on "package.json". If "package.json" has "types" field, it will be `"compatible"`, otherwise `false`. 77 | */ 78 | declaration?: "compatible" | "node16" | boolean; 79 | 80 | /** 81 | * Output directory. 82 | */ 83 | outDir: string; 84 | 85 | /** 86 | * Whether to build with JIT stubs. 87 | * Read more: [stubbing](https://antfu.me/posts/publish-esm-and-cjs#stubbing) 88 | */ 89 | stub: boolean; 90 | 91 | /** 92 | * Whether to build and actively watch the file changes. 93 | * 94 | * @experimental This feature is experimental and incomplete. 95 | */ 96 | watch: boolean; 97 | 98 | /** 99 | * Watch mode options. 100 | */ 101 | watchOptions: WatcherOptions | undefined; 102 | 103 | /** 104 | * Stub options, where [jiti](https://github.com/unjs/jiti) 105 | * is an object of type `Omit`. 106 | */ 107 | stubOptions: { jiti: Omit }; 108 | 109 | /** 110 | * Used to specify which modules or libraries should be considered external dependencies 111 | * and not included in the final build product. 112 | */ 113 | externals: (string | RegExp)[]; 114 | 115 | dependencies: string[]; 116 | 117 | peerDependencies: string[]; 118 | 119 | devDependencies: string[]; 120 | 121 | /** 122 | * Create aliases for module imports to reference modules in code using more concise paths. 123 | * Allow you to specify an alias for the module path. 124 | */ 125 | alias: { [find: string]: string }; 126 | 127 | /** 128 | * Replace the text in the source code with rules. 129 | */ 130 | replace: { [find: string]: string }; 131 | 132 | /** 133 | * Terminate the build process when a warning appears 134 | */ 135 | failOnWarn?: boolean; 136 | 137 | /** 138 | * [Rollup](https://rollupjs.org/configuration-options) Build Options 139 | */ 140 | rollup: RollupBuildOptions; 141 | 142 | /** 143 | * Run different types of builds (untyped, mkdist, Rollup, copy) simultaneously. 144 | */ 145 | parallel: boolean; 146 | } 147 | 148 | export interface BuildContext { 149 | options: BuildOptions; 150 | pkg: PackageJson; 151 | jiti: Jiti; 152 | buildEntries: { 153 | path: string; 154 | bytes?: number; 155 | exports?: string[]; 156 | chunks?: string[]; 157 | chunk?: boolean; 158 | modules?: { id: string; bytes: number }[]; 159 | }[]; 160 | 161 | usedImports: Set; 162 | warnings: Set; 163 | hooks: Hookable; 164 | } 165 | 166 | export type BuildPreset = BuildConfig | (() => BuildConfig); 167 | 168 | type DeepPartial = { [P in keyof T]?: DeepPartial }; 169 | 170 | /** 171 | * In addition to basic `entries`, `presets`, and `hooks`, 172 | * there are also all the properties of `BuildOptions` except for BuildOptions's `entries`. 173 | */ 174 | export interface BuildConfig 175 | extends DeepPartial> { 176 | /** 177 | * Specify the entry file or entry module during the construction process. 178 | */ 179 | entries?: (BuildEntry | string)[]; 180 | 181 | /** 182 | * Used to specify the preset build configuration. 183 | */ 184 | preset?: string | BuildPreset; 185 | 186 | /** 187 | * Used to define hook functions during the construction process to perform custom operations during specific construction stages. 188 | * This configuration allows you to insert custom logic during the build process to meet specific requirements or perform additional operations. 189 | */ 190 | hooks?: Partial; 191 | } 192 | 193 | export interface BuildHooks 194 | extends CopyHooks, 195 | UntypedHooks, 196 | MkdistHooks, 197 | RollupHooks { 198 | "build:prepare": (ctx: BuildContext) => void | Promise; 199 | "build:before": (ctx: BuildContext) => void | Promise; 200 | "build:done": (ctx: BuildContext) => void | Promise; 201 | } 202 | 203 | export function defineBuildConfig( 204 | config: BuildConfig | BuildConfig[], 205 | ): BuildConfig[] { 206 | return (Array.isArray(config) ? config : [config]).filter(Boolean); 207 | } 208 | 209 | export function definePreset(preset: BuildPreset): BuildPreset { 210 | return preset; 211 | } 212 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import fsp from "node:fs/promises"; 2 | import { readdirSync, statSync } from "node:fs"; 3 | import { dirname, resolve } from "pathe"; 4 | import { createJiti } from "jiti"; 5 | import { consola } from "consola"; 6 | import type { PackageJson } from "pkg-types"; 7 | import { autoPreset } from "./auto"; 8 | import type { BuildPreset, BuildConfig, BuildContext } from "./types"; 9 | 10 | export async function ensuredir(path: string): Promise { 11 | await fsp.mkdir(dirname(path), { recursive: true }); 12 | } 13 | 14 | export function warn(ctx: BuildContext, message: string): void { 15 | if (ctx.warnings.has(message)) { 16 | return; 17 | } 18 | consola.debug("[unbuild] [warn]", message); 19 | ctx.warnings.add(message); 20 | } 21 | 22 | export async function symlink( 23 | from: string, 24 | to: string, 25 | force = true, 26 | ): Promise { 27 | await ensuredir(to); 28 | if (force) { 29 | await fsp.unlink(to).catch(() => {}); 30 | } 31 | await fsp.symlink(from, to, "junction"); 32 | } 33 | 34 | export function dumpObject(obj: Record): string { 35 | return ( 36 | "{ " + 37 | Object.keys(obj) 38 | .map((key) => `${key}: ${JSON.stringify(obj[key])}`) 39 | .join(", ") + 40 | " }" 41 | ); 42 | } 43 | 44 | export function getpkg(id = ""): string { 45 | const s = id.split("/"); 46 | return s[0][0] === "@" ? `${s[0]}/${s[1]}` : s[0]; 47 | } 48 | 49 | export async function rmdir(dir: string): Promise { 50 | await fsp.unlink(dir).catch(() => {}); 51 | await fsp.rm(dir, { recursive: true, force: true }).catch(() => {}); 52 | } 53 | 54 | export function listRecursively(path: string): string[] { 55 | const filenames = new Set(); 56 | const walk = (path: string): void => { 57 | const files = readdirSync(path); 58 | for (const file of files) { 59 | const fullPath = resolve(path, file); 60 | if (statSync(fullPath).isDirectory()) { 61 | filenames.add(fullPath + "/"); 62 | walk(fullPath); 63 | } else { 64 | filenames.add(fullPath); 65 | } 66 | } 67 | }; 68 | walk(path); 69 | return [...filenames]; 70 | } 71 | 72 | export async function resolvePreset( 73 | preset: string | BuildPreset, 74 | rootDir: string, 75 | ): Promise { 76 | if (preset === "auto") { 77 | preset = autoPreset; 78 | } else if (typeof preset === "string") { 79 | preset = 80 | (await createJiti(rootDir, { interopDefault: true }).import(preset, { 81 | default: true, 82 | })) || {}; 83 | } 84 | if (typeof preset === "function") { 85 | preset = preset(); 86 | } 87 | return preset as BuildConfig; 88 | } 89 | 90 | export function inferExportType( 91 | condition: string, 92 | previousConditions: string[] = [], 93 | filename = "", 94 | ): "esm" | "cjs" { 95 | if (filename) { 96 | if (filename.endsWith(".d.ts")) { 97 | return "esm"; 98 | } 99 | if (filename.endsWith(".mjs")) { 100 | return "esm"; 101 | } 102 | if (filename.endsWith(".cjs")) { 103 | return "cjs"; 104 | } 105 | } 106 | switch (condition) { 107 | case "import": { 108 | return "esm"; 109 | } 110 | case "require": { 111 | return "cjs"; 112 | } 113 | default: { 114 | if (previousConditions.length === 0) { 115 | // TODO: Check against type:module for default 116 | return "esm"; 117 | } 118 | const [newCondition, ...rest] = previousConditions; 119 | return inferExportType(newCondition, rest, filename); 120 | } 121 | } 122 | } 123 | 124 | export type OutputDescriptor = { file: string; type?: "esm" | "cjs" }; 125 | 126 | export function extractExportFilenames( 127 | exports: PackageJson["exports"], 128 | conditions: string[] = [], 129 | ): OutputDescriptor[] { 130 | if (!exports) { 131 | return []; 132 | } 133 | if (typeof exports === "string") { 134 | return [{ file: exports, type: "esm" }]; 135 | } 136 | return ( 137 | Object.entries(exports) 138 | // Filter out .json subpaths such as package.json 139 | .filter(([subpath]) => !subpath.endsWith(".json")) 140 | .flatMap(([condition, exports]) => 141 | typeof exports === "string" 142 | ? { 143 | file: exports, 144 | type: inferExportType(condition, conditions, exports), 145 | } 146 | : extractExportFilenames(exports, [...conditions, condition]), 147 | ) 148 | ); 149 | } 150 | 151 | export function arrayIncludes( 152 | arr: (string | RegExp)[], 153 | searchElement: string, 154 | ): boolean { 155 | return arr.some((entry) => 156 | entry instanceof RegExp 157 | ? entry.test(searchElement) 158 | : entry === searchElement, 159 | ); 160 | } 161 | 162 | export function removeExtension(filename: string): string { 163 | return filename.replace(/\.(js|mjs|cjs|ts|mts|cts|json|jsx|tsx)$/, ""); 164 | } 165 | 166 | export function inferPkgExternals(pkg: PackageJson): (string | RegExp)[] { 167 | const externals: (string | RegExp)[] = [ 168 | ...Object.keys(pkg.dependencies || {}), 169 | ...Object.keys(pkg.peerDependencies || {}), 170 | ...Object.keys(pkg.devDependencies || {}).filter((dep) => 171 | dep.startsWith("@types/"), 172 | ), 173 | ...Object.keys(pkg.optionalDependencies || {}), 174 | ]; 175 | 176 | if (pkg.name) { 177 | externals.push(pkg.name); 178 | if (pkg.exports) { 179 | for (const subpath of Object.keys(pkg.exports)) { 180 | if (subpath.startsWith("./")) { 181 | externals.push(pathToRegex(`${pkg.name}/${subpath.slice(2)}`)); 182 | } 183 | } 184 | } 185 | } 186 | 187 | if (pkg.imports) { 188 | for (const importName of Object.keys(pkg.imports)) { 189 | if (importName.startsWith("#")) { 190 | externals.push(pathToRegex(importName)); 191 | } 192 | } 193 | } 194 | 195 | return [...new Set(externals)]; 196 | } 197 | 198 | function pathToRegex(path: string): string | RegExp { 199 | return path.includes("*") 200 | ? new RegExp( 201 | `^${path.replace(/\./g, String.raw`\.`).replace(/\*/g, ".*")}$`, 202 | ) 203 | : path; 204 | } 205 | 206 | export function withTrailingSlash(path: string): string { 207 | return path.endsWith("/") ? path : `${path}/`; 208 | } 209 | -------------------------------------------------------------------------------- /src/validate.ts: -------------------------------------------------------------------------------- 1 | import type { PackageJson } from "pkg-types"; 2 | import type { BuildContext } from "./types"; 3 | import { existsSync } from "node:fs"; 4 | import { colors } from "consola/utils"; 5 | import { resolve } from "pathe"; 6 | import { arrayIncludes, extractExportFilenames, getpkg, warn } from "./utils"; 7 | 8 | export function validateDependencies(ctx: BuildContext): void { 9 | const usedDependencies = new Set(); 10 | const unusedDependencies = new Set( 11 | Object.keys(ctx.pkg.dependencies || {}), 12 | ); 13 | const implicitDependencies = new Set(); 14 | for (const id of ctx.usedImports) { 15 | unusedDependencies.delete(id); 16 | usedDependencies.add(id); 17 | } 18 | if (Array.isArray(ctx.options.dependencies)) { 19 | for (const id of ctx.options.dependencies) { 20 | unusedDependencies.delete(id); 21 | } 22 | } 23 | for (const id of usedDependencies) { 24 | if ( 25 | !arrayIncludes(ctx.options.externals, id) && 26 | !id.startsWith("chunks/") && 27 | !ctx.options.dependencies.includes(getpkg(id)) && 28 | !ctx.options.peerDependencies.includes(getpkg(id)) 29 | ) { 30 | implicitDependencies.add(id); 31 | } 32 | } 33 | if (unusedDependencies.size > 0) { 34 | warn( 35 | ctx, 36 | "Potential unused dependencies found: " + 37 | [...unusedDependencies].map((id) => colors.cyan(id)).join(", "), 38 | ); 39 | } 40 | if (implicitDependencies.size > 0 && !ctx.options.rollup.inlineDependencies) { 41 | warn( 42 | ctx, 43 | "Potential implicit dependencies found: " + 44 | [...implicitDependencies].map((id) => colors.cyan(id)).join(", "), 45 | ); 46 | } 47 | } 48 | 49 | export function validatePackage( 50 | pkg: PackageJson, 51 | rootDir: string, 52 | ctx: BuildContext, 53 | ): void { 54 | if (!pkg) { 55 | return; 56 | } 57 | 58 | const filenames = new Set( 59 | [ 60 | ...(typeof pkg.bin === "string" 61 | ? [pkg.bin] 62 | : Object.values(pkg.bin || {})), 63 | pkg.main, 64 | pkg.module, 65 | pkg.types, 66 | pkg.typings, 67 | ...extractExportFilenames(pkg.exports).map((i) => i.file), 68 | ].map((i) => i && resolve(rootDir, i.replace(/\/[^/]*\*.*$/, ""))), 69 | ); 70 | 71 | const missingOutputs = []; 72 | 73 | for (const filename of filenames) { 74 | if (filename && !filename.includes("*") && !existsSync(filename)) { 75 | missingOutputs.push(filename.replace(rootDir + "/", "")); 76 | } 77 | } 78 | if (missingOutputs.length > 0) { 79 | warn( 80 | ctx, 81 | `Potential missing package.json files: ${missingOutputs 82 | .map((o) => colors.cyan(o)) 83 | .join(", ")}`, 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /test/auto.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from "vitest"; 2 | import { inferEntries, getEntrypointPaths } from "../src/auto"; 3 | 4 | describe("inferEntries", () => { 5 | it("recognises main and module outputs", () => { 6 | const result = inferEntries( 7 | { main: "dist/test.cjs", module: "dist/test.mjs" }, 8 | ["src/", "src/test.ts"], 9 | ); 10 | expect(result).to.deep.equal({ 11 | cjs: true, 12 | dts: false, 13 | entries: [{ input: "src/test" }], 14 | warnings: [], 15 | }); 16 | }); 17 | 18 | it("handles nested indexes", () => { 19 | const result = inferEntries({ module: "dist/index.mjs" }, [ 20 | "src/", 21 | "src/event/index.ts", 22 | "src/index.mts", 23 | ]); 24 | expect(result).to.deep.equal({ 25 | cjs: false, 26 | dts: false, 27 | entries: [{ input: "src/index" }], 28 | warnings: [], 29 | }); 30 | }); 31 | 32 | it("handles binary outputs", () => { 33 | expect( 34 | inferEntries({ bin: "dist/cli.cjs" }, ["src/", "src/cli.ts"]), 35 | ).to.deep.equal({ 36 | cjs: true, 37 | dts: false, 38 | entries: [{ input: "src/cli" }], 39 | warnings: [], 40 | }); 41 | expect( 42 | inferEntries({ bin: { nuxt: "dist/cli.js" } }, ["src/", "src/cli.ts"]), 43 | ).to.deep.equal({ 44 | cjs: true, 45 | dts: false, 46 | entries: [{ input: "src/cli" }], 47 | warnings: [], 48 | }); 49 | expect( 50 | inferEntries({ bin: { nuxt: "dist/cli.js" }, type: "module" }, [ 51 | "src/", 52 | "src/cli.ts", 53 | ]), 54 | ).to.deep.equal({ 55 | cjs: false, 56 | dts: false, 57 | entries: [{ input: "src/cli" }], 58 | warnings: [], 59 | }); 60 | }); 61 | 62 | it("recognises `type: module` projects", () => { 63 | const result = inferEntries({ main: "dist/test.js", type: "module" }, [ 64 | "src/", 65 | "src/test.ts", 66 | ]); 67 | expect(result).to.deep.equal({ 68 | cjs: false, 69 | dts: false, 70 | entries: [{ input: "src/test" }], 71 | warnings: [], 72 | }); 73 | }); 74 | 75 | it("matches nested entrypoint paths", () => { 76 | const result = inferEntries({ exports: "dist/runtime/index.js" }, [ 77 | "src/", 78 | "src/other/runtime/index.ts", 79 | ]); 80 | expect(result).to.deep.equal({ 81 | cjs: false, 82 | dts: false, 83 | entries: [{ input: "src/other/runtime/index" }], 84 | warnings: [], 85 | }); 86 | }); 87 | 88 | it("handles declarations from `types`", () => { 89 | expect( 90 | inferEntries( 91 | { main: "dist/test.cjs", types: "custom/handwritten.d.ts" }, 92 | ["src/", "src/test.ts"], 93 | ), 94 | ).to.deep.equal({ 95 | cjs: true, 96 | dts: false, 97 | entries: [{ input: "src/test" }], 98 | warnings: ["Could not find entrypoint for `custom/handwritten.d.ts`"], 99 | }); 100 | expect( 101 | inferEntries( 102 | { 103 | main: "dist/test.cjs", 104 | module: "dist/test.mjs", 105 | types: "dist/test.d.ts", 106 | }, 107 | ["src/", "src/test.ts"], 108 | ), 109 | ).to.deep.equal({ 110 | cjs: true, 111 | dts: true, 112 | entries: [{ input: "src/test" }], 113 | warnings: [], 114 | }); 115 | expect( 116 | inferEntries( 117 | { 118 | main: "dist/test.cjs", 119 | module: "dist/test.mjs", 120 | typings: "dist/test.d.ts", 121 | }, 122 | ["src/", "src/test.ts"], 123 | ), 124 | ).to.deep.equal({ 125 | cjs: true, 126 | dts: true, 127 | entries: [{ input: "src/test" }], 128 | warnings: [], 129 | }); 130 | }); 131 | 132 | it("handles types within exports`", () => { 133 | const result = inferEntries( 134 | { 135 | exports: { 136 | import: { 137 | types: "dist/test.d.mts", 138 | default: "dist/test.mjs", 139 | }, 140 | require: { 141 | types: "dist/test.d.cts", 142 | default: "dist/test.cjs", 143 | }, 144 | }, 145 | }, 146 | ["src/", "src/test.ts"], 147 | ); 148 | expect(result).to.deep.equal({ 149 | cjs: true, 150 | dts: true, 151 | entries: [{ input: "src/test" }], 152 | warnings: [], 153 | }); 154 | }); 155 | 156 | it("gracefully handles unknown entries", () => { 157 | expect( 158 | inferEntries({ exports: "dist/test.js" }, ["src/", "src/index.mts"]), 159 | ).to.deep.equal({ 160 | cjs: false, 161 | entries: [], 162 | dts: false, 163 | warnings: ["Could not find entrypoint for `dist/test.js`"], 164 | }); 165 | }); 166 | 167 | it("ignores top-level exports", () => { 168 | expect( 169 | inferEntries({ exports: { "./*": "./*" } }, [ 170 | "src/", 171 | "src/", 172 | "src/index.mts", 173 | ]), 174 | ).to.deep.equal({ 175 | cjs: false, 176 | entries: [], 177 | dts: false, 178 | warnings: [], 179 | }); 180 | }); 181 | 182 | it("handles multiple entries", () => { 183 | expect( 184 | inferEntries( 185 | { 186 | exports: { 187 | ".": "./dist/index.cjs", 188 | "first-test": "./dist/first-test.cjs", 189 | "./test": "./dist/test.cjs", 190 | }, 191 | }, 192 | ["src/", "src/", "src/index.mts", "src/first-test.ts", "src/test.mjs"], 193 | ), 194 | ).to.deep.equal({ 195 | cjs: true, 196 | dts: false, 197 | entries: [ 198 | { input: "src/index" }, 199 | { input: "src/first-test" }, 200 | { input: "src/test" }, 201 | ], 202 | warnings: [], 203 | }); 204 | }); 205 | 206 | it("recognises directory mappings", () => { 207 | expect( 208 | inferEntries({ exports: "./dist/runtime/*" }, [ 209 | "src/", 210 | "src/runtime/", 211 | "src/runtime/test.js", 212 | ]), 213 | ).to.deep.equal({ 214 | cjs: false, 215 | dts: false, 216 | entries: [ 217 | { format: "esm", input: "src/runtime/", outDir: "./dist/runtime/" }, 218 | ], 219 | warnings: [], 220 | }); 221 | expect( 222 | inferEntries({ exports: { "./runtime/*": "./dist/runtime/*.mjs," } }, [ 223 | "src/", 224 | "src/runtime/", 225 | ]), 226 | ).to.deep.equal({ 227 | cjs: false, 228 | dts: false, 229 | entries: [ 230 | { format: "esm", input: "src/runtime/", outDir: "./dist/runtime/" }, 231 | ], 232 | warnings: [], 233 | }); 234 | expect( 235 | inferEntries( 236 | { exports: { "./runtime/*": { require: "./dist/runtime/*" } } }, 237 | ["src/", "src/runtime/"], 238 | ), 239 | ).to.deep.equal({ 240 | cjs: true, 241 | dts: false, 242 | entries: [ 243 | { format: "cjs", input: "src/runtime/", outDir: "./dist/runtime/" }, 244 | ], 245 | warnings: [], 246 | }); 247 | }); 248 | }); 249 | 250 | describe("getEntrypointPaths", () => { 251 | it("produces a list of possible paths", () => { 252 | expect(getEntrypointPaths("./dist/foo/bar.js")).to.deep.equal([ 253 | "dist/foo/bar.js", 254 | "foo/bar.js", 255 | "bar.js", 256 | ]); 257 | expect(getEntrypointPaths("./dist/foo/")).to.deep.equal([ 258 | "dist/foo/", 259 | "foo/", 260 | ]); 261 | }); 262 | }); 263 | -------------------------------------------------------------------------------- /test/fixture/bin/cli.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | console.log("hello"); 4 | -------------------------------------------------------------------------------- /test/fixture/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from "../../src"; 2 | 3 | export default defineBuildConfig([ 4 | // Auto preset 5 | {}, 6 | // Custom preset 7 | { 8 | preset: "./build.preset", 9 | rollup: { 10 | emitCJS: true, 11 | }, 12 | entries: [ 13 | "./src/index.mts", 14 | "./src/nested/subpath.ts", 15 | { input: "src/runtime/", outDir: "dist/runtime" }, 16 | { 17 | input: "src/", 18 | outDir: "dist/json/", 19 | builder: "copy", 20 | pattern: "**/*.json", 21 | }, 22 | { input: "src/schema", builder: "untyped" }, 23 | ], 24 | stubOptions: { 25 | jiti: { 26 | transformOptions: { 27 | babel: { 28 | // @ts-expect-error - type complexity 29 | plugins: [["@babel/plugin-transform-class-properties"]], 30 | }, 31 | }, 32 | }, 33 | }, 34 | }, 35 | // Minified with sourcemaps 36 | { 37 | name: "minified", 38 | entries: ["src/index"], 39 | outDir: "dist/min", 40 | sourcemap: true, 41 | declaration: "compatible", 42 | rollup: { 43 | esbuild: { 44 | minify: true, 45 | }, 46 | }, 47 | }, 48 | ]); 49 | -------------------------------------------------------------------------------- /test/fixture/build.preset.ts: -------------------------------------------------------------------------------- 1 | import { definePreset } from "../../src"; 2 | 3 | export default definePreset({ 4 | declaration: "compatible", 5 | rollup: { 6 | cjsBridge: true, 7 | }, 8 | hooks: { 9 | "build:before": () => { 10 | console.log("Before build"); 11 | }, 12 | "build:done": () => { 13 | console.log("After build"); 14 | }, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /test/fixture/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture", 3 | "private": "true", 4 | "type": "module", 5 | "bin": { 6 | "fixture": "./bin/cli.mjs" 7 | }, 8 | "exports": { 9 | ".": "./dist/index.mjs", 10 | "./nested/subpath": "./dist/nested/subpath.mjs" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/fixture/src/index.mts: -------------------------------------------------------------------------------- 1 | import { arch } from "node:os"; 2 | import testJSON from "./test.json"; 3 | 4 | console.log("__filename", __filename); 5 | console.log("__dirname", __dirname); 6 | console.log("import.meta.url", import.meta.url); 7 | 8 | console.log(arch()); 9 | // eslint-disable-next-line @typescript-eslint/no-require-imports 10 | console.log(require("node:os").arch()); 11 | console.log(require.resolve("rollup")); 12 | console.log(testJSON); 13 | import("node:os").then((os) => console.log(os.arch())); 14 | 15 | // @ts-ignore 16 | import("./runtime/foo.ts").then(console.log); 17 | 18 | export const foo = "bar"; 19 | export const baz = "123"; 20 | export default "default"; 21 | 22 | // Failing test 23 | // export * from 'defu' 24 | -------------------------------------------------------------------------------- /test/fixture/src/nested/subpath.ts: -------------------------------------------------------------------------------- 1 | export default "nested/subpath"; 2 | -------------------------------------------------------------------------------- /test/fixture/src/runtime/foo.ts: -------------------------------------------------------------------------------- 1 | export default "123"; 2 | -------------------------------------------------------------------------------- /test/fixture/src/schema.ts: -------------------------------------------------------------------------------- 1 | export const config = { 2 | foo: "bar", 3 | /** 4 | * @type {typeof import('untyped').BuildConfig} 5 | */ 6 | build: {}, 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixture/src/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "works": "yes" 3 | } 4 | -------------------------------------------------------------------------------- /test/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from "vitest"; 2 | import { 3 | arrayIncludes, 4 | extractExportFilenames, 5 | inferExportType, 6 | inferPkgExternals, 7 | } from "../src/utils"; 8 | 9 | describe("inferExportType", () => { 10 | it("infers export type by condition", () => { 11 | expect(inferExportType("import")).to.equal("esm"); 12 | expect(inferExportType("require")).to.equal("cjs"); 13 | expect(inferExportType("node")).to.equal("esm"); 14 | expect(inferExportType("some_unknown_condition")).to.equal("esm"); 15 | }); 16 | it("infers export type based on previous conditions", () => { 17 | expect(inferExportType("import", ["require"])).to.equal("esm"); 18 | expect(inferExportType("node", ["require"])).to.equal("cjs"); 19 | expect(inferExportType("node", ["import"])).to.equal("esm"); 20 | expect(inferExportType("node", ["unknown", "require"])).to.equal("cjs"); 21 | }); 22 | }); 23 | 24 | describe("extractExportFilenames", () => { 25 | it("handles strings", () => { 26 | expect(extractExportFilenames("test")).to.deep.equal([ 27 | { file: "test", type: "esm" }, 28 | ]); 29 | }); 30 | it("handles nested objects", () => { 31 | expect(extractExportFilenames({ require: "test" })).to.deep.equal([ 32 | { file: "test", type: "cjs" }, 33 | ]); 34 | expect( 35 | extractExportFilenames({ 36 | require: { node: "test", other: { import: "this", require: "that" } }, 37 | }), 38 | ).to.deep.equal([ 39 | { file: "test", type: "cjs" }, 40 | { file: "this", type: "esm" }, 41 | { file: "that", type: "cjs" }, 42 | ]); 43 | }); 44 | }); 45 | 46 | describe("arrayIncludes", () => { 47 | it("handles strings", () => { 48 | expect(arrayIncludes(["test1", "test2"], "test1")).to.eq(true); 49 | expect(arrayIncludes(["test1", "test2"], "test3")).to.eq(false); 50 | }); 51 | it("handles regular expressions", () => { 52 | expect(arrayIncludes([/t1$/, "test2"], "test1")).to.eq(true); 53 | expect(arrayIncludes([/t3$/, "test2"], "test1")).to.eq(false); 54 | }); 55 | }); 56 | 57 | describe("inferPkgExternals", () => { 58 | it("infers externals from package.json", () => { 59 | expect( 60 | inferPkgExternals({ 61 | name: "test", 62 | dependencies: { react: "17.0.0" }, 63 | peerDependencies: { "react-dom": "17.0.0" }, 64 | devDependencies: { "@types/react": "17.0.0", webpack: "*" }, 65 | optionalDependencies: { test: "1.0.0", optional: "1.0.0" }, 66 | exports: { 67 | ".": "index.js", 68 | "./extra/utils": "utils.js", 69 | "./drivers/*.js": "drivers/*.js", 70 | invalid: "invalid.js", 71 | }, 72 | imports: { 73 | "#*": "src/*", 74 | "#test": "test.js", 75 | invalid: "invalid.js", 76 | }, 77 | }), 78 | ).to.deep.equal([ 79 | "react", 80 | "react-dom", 81 | "@types/react", 82 | "test", 83 | "optional", 84 | "test/extra/utils", 85 | /^test\/drivers\/.*\.js$/, 86 | /^#.*$/, 87 | "#test", 88 | ]); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /test/validate.test.ts: -------------------------------------------------------------------------------- 1 | import type { BuildEntry } from "../src/types.ts"; 2 | import { fileURLToPath } from "node:url"; 3 | import { consola } from "consola"; 4 | import { join } from "pathe"; 5 | import { describe, it, expect } from "vitest"; 6 | import { validateDependencies, validatePackage } from "../src/validate"; 7 | 8 | describe("validatePackage", () => { 9 | it("detects missing files", () => { 10 | const buildContext = { 11 | warnings: new Set(), 12 | } as any; 13 | 14 | validatePackage( 15 | { 16 | main: "./dist/test", 17 | bin: { 18 | "./cli": "./dist/cli", 19 | }, 20 | module: "dist/mod", 21 | exports: { 22 | "./runtime/*": "./runtime/*.mjs", 23 | ".": { node: "./src/index.mts" }, 24 | }, 25 | }, 26 | join(fileURLToPath(import.meta.url), "../fixture"), 27 | buildContext, 28 | ); 29 | 30 | const warnings = [...buildContext.warnings]; 31 | 32 | expect(warnings[0]).to.include("Potential missing"); 33 | expect(warnings[0]).not.to.include("src/index.mts"); 34 | 35 | for (const file of ["dist/test", "dist/cli", "dist/mod", "runtime"]) { 36 | expect(warnings[0]).to.include(file); 37 | } 38 | }); 39 | }); 40 | 41 | describe("validateDependencies", () => { 42 | it("detects implicit deps", () => { 43 | const warnings = new Set(); 44 | 45 | validateDependencies({ 46 | warnings, 47 | pkg: {}, 48 | buildEntries: [], 49 | hooks: [] as any, 50 | usedImports: new Set(["pkg-a/core"]), 51 | options: { 52 | externals: [], 53 | dependencies: ["react"], 54 | peerDependencies: [], 55 | devDependencies: [], 56 | rootDir: ".", 57 | entries: [] as BuildEntry[], 58 | clean: false, 59 | outDir: "dist", 60 | stub: false, 61 | alias: {}, 62 | replace: {}, 63 | // @ts-expect-error 64 | rollup: { 65 | replace: false, 66 | alias: false, 67 | resolve: false, 68 | json: false, 69 | esbuild: false, 70 | commonjs: false, 71 | }, 72 | }, 73 | }); 74 | 75 | expect([...warnings][0]).to.include( 76 | "Potential implicit dependencies found:", 77 | ); 78 | }); 79 | 80 | it("does not print implicit deps warning for peerDependencies", () => { 81 | const logs: string[] = []; 82 | consola.mockTypes((type) => 83 | type === "warn" 84 | ? (str: string): void => { 85 | logs.push(str); 86 | } 87 | : (): void => {}, 88 | ); 89 | 90 | validateDependencies({ 91 | pkg: {}, 92 | buildEntries: [], 93 | hooks: [] as any, 94 | usedImports: new Set(["pkg-a/core"]), 95 | options: { 96 | externals: [], 97 | dependencies: ["react"], 98 | peerDependencies: ["pkg-a"], 99 | devDependencies: [], 100 | rootDir: ".", 101 | entries: [] as BuildEntry[], 102 | clean: false, 103 | outDir: "dist", 104 | stub: false, 105 | alias: {}, 106 | replace: {}, 107 | // @ts-expect-error 108 | rollup: { 109 | replace: false, 110 | alias: false, 111 | resolve: false, 112 | json: false, 113 | esbuild: false, 114 | commonjs: false, 115 | }, 116 | }, 117 | }); 118 | 119 | expect(logs.length).to.eq(0); 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "Preserve", 5 | "moduleResolution": "Bundler", 6 | "resolveJsonModule": true, 7 | "esModuleInterop": false, 8 | "allowSyntheticDefaultImports": true, 9 | "skipLibCheck": true, 10 | "allowJs": true, 11 | "checkJs": true, 12 | "strict": true, 13 | "verbatimModuleSyntax": true, 14 | "isolatedModules": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "allowImportingTsExtensions": true, 17 | "noImplicitOverride": true, 18 | "noEmit": true 19 | }, 20 | "include": ["src", "test"] 21 | } 22 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | coverage: { 6 | reporter: ["text", "clover", "json"] 7 | } 8 | } 9 | }); 10 | --------------------------------------------------------------------------------