tsup
tsup 是@egois是我们最常用的工具之一,typescript项目的特性是零配置构建。就可以直接打包dist/index.js
tsup src/index.ts
//支持双重格式
tsup src/index.ts --format cjs,esm
//package.json中使用tsup
{
"name": "my-cool-package",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
"watch": "npm run build -- --watch src",
"prepublishOnly": "npm run build"
}
}
unbuild
如果说tsup是typescript的一个最小的打包工具,那么unbuild则是一个更通用的,可自定义的,但功能强大,unbuild被用来打包Nuxt3及其子包。要使用他,我们需要在根目录下创建build.config.ts文件
// build.config.ts
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
entries: [
'./src/index'
],
declaration: true, // generate .d.ts files
})
//执行命令 默认会生成ESM和CJS两种格式
unbuild
在next3代码库中发现unbuild引入了一个叫stub的新概念,每次修改源代码启动一个监视程序重新触发打包不同,stubbing在unbuild中的根本不需要您有另一个程序处理它。只需要调用命令
unbuild --stub
生成的文件内容
// dist/index.mjs
import jiti from 'jiti'
export default jiti(null, { interopDefault: true })('/Users/antfu/unbuild-test/src/index')
// dist/index.cjs
module.exports = require('jiti')(null, { interopDefault: true })('/Users/antfu/unbuild-test/src/index')
现在dist文件不在发布你的代码包,而是通过jit
重定向到你的源代码。jiti通过动态编译模块,为typescript和esm提供了运行时支持,因为它直接指向你的源文件,所以在你的源代码和bundle dist之间不会有错位。因此不需要观察者进程!
Bundleless 的构建
unbuild由mkdist(另一个@unjs包 )提供支持,他还可以处理静态资源和文件到文件的编译。无需绑定构建允许您保持源代码的结构,便于按需导入子模块以优化性能
配置
// build.config.ts
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
entries: [
// bundling
'src/index',
// bundleless, or just copy assets
{ input: 'src/components/', outDir: 'dist/components' },
],
declaration: true,
})
他最酷的一个特性是他可以直接处理.vue 例如src/components下有一个组件MyComponent.vue 例如
<!-- src/components/MyComponent.vue -->
<template>
<div>{{ count }}</div>
</template>
<script lang="ts">
const count: number | string = 0
export default {
data: () => ({ count }),
}
</script>
请注意,我们在Vue文件中使用TypeScript,当我们进行构建时,组件将被复制,但TypeScript注释将被删除,同时生成MyComponent.Vue.d.ts。
<!-- dist/components/MyComponent.vue -->
<template>
<div>{{ count }}</div>
</template>
<script>
const count = 0
export default {
data: () => ({ count })
}
</script>
// dist/components/MyComponent.vue.d.ts
declare const _default: {
data: () => {
count: number | string
}
}
export default _default
使用上述任何一种工具,现在我们都可以将typescript编写为单一的试试来源,病史整个代码库变得更易于维护。但是,您仍然需要注意一些注意事项
在ESM中,没有__dirname
,__filename
,require
,require.resolve
.相反,您需要使用import.meta,url
并进行一些转化来获取文件路径字符串。因此我们的代码被编译成CJS和ESM,因此最好尽可能避免使用这些特定于环境的上下文。如果却行使用可以参考[参考地址]
(https://antfu.me/notes#isomorphic-dirname)
import { dirname } from 'path'
import { fileURLToPath } from 'url'
const _dirname = typeof __dirname !== 'undefined'
? __dirname
: dirname(fileURLToPath(import.meta.url))
对于requireand require.resolve,您可以使用
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
注意:好消息是,如果正在使用‘unbuild’,我们可以直接打开‘cjsBridge’标志并‘unbuild’自动为我们填充ESM中的那些CJS上下文
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
cjsBridge: true, // <--
})
相反,如果您使用的是tsup,则轻松import.meta.url在 CJS 中为您填充ESM 。
对于tsup的使用我在utils的开发中有详细的搭建和配置过程可以参考utils公共组件独立开发