跳到主要内容

external

CLI支持两种功能:

  1. tsbuild: 构建除了dependenciespeerDependencies外的依赖;
  2. tsbuild-node: 除了不构建dependenciespeerDependencies的依赖外,还可以跳过所有的 Node.js 依赖包;

除了以上默认的使用外,还可以使用--external避免打特定的包。

tsbuild 与 tsbuild-node

其中,package.json中指定特定 CLI 入口:

"bin": {
"tsbuild": "dist/cli-default.js",
"tsbuild-node": "dist/cli-node.js"
}

两者区别在于tsbuild-node传入了以下 option:

skipNodeModulesBundle: true,

此处因为esbuildexternal不支持正则,创建externalPlugin

import { Plugin } from 'esbuild'
import { match, tsconfigPathsToRegExp } from 'bundle-require'

// Must not start with "/" or "./" or "../" or "C:\" or be the exact strings ".." or "."
const NON_NODE_MODULE_RE = /^[A-Z]:[\\/]|^\.{0,2}\/|^\.{1,2}$/

export function externalPlugin({
external,
noExternal,
skipNodeModulesBundle,
tsconfigResolvePaths,
}: {
external?: (string | RegExp)[]
noExternal?: (string | RegExp)[]
skipNodeModulesBundle?: boolean
tsconfigResolvePaths?: Record<string, string[]>
}): Plugin {
return {
name: `external`,

setup(build) {
if (skipNodeModulesBundle) {
const resolvePatterns = tsconfigPathsToRegExp(tsconfigResolvePaths || {})
build.onResolve({ filter: /.*/ }, (args) => {
// Resolve `paths` from tsconfig
if (match(args.path, resolvePatterns)) {
return
}
// Respect explicit external/noExternal conditions
if (match(args.path, noExternal)) {
return
}
if (match(args.path, external)) {
return { external: true }
}
// Exclude any other import that looks like a Node module
if (!NON_NODE_MODULE_RE.test(args.path)) {
return {
path: args.path,
external: true,
}
}
})
}
else {
build.onResolve({ filter: /.*/ }, (args) => {
// Respect explicit external/noExternal conditions
if (match(args.path, noExternal)) {
return
}
if (match(args.path, external)) {
return { external: true }
}
})
}
},
}
}

其中,针对skipNodeModulesBundletrue的情况下,额外过滤tsconfig中所有path的文件

const resolvePatterns = tsconfigPathsToRegExp(tsconfigResolvePaths || {})

if (match(args.path, resolvePatterns)) {

}

过滤 dependencies/peerDependencies 与 external 依赖

  1. 获取对应依赖
const external = [
// Exclude dependencies, e.g. `lodash`, `lodash/get`
...deps.map((dep) => new RegExp(`^${dep}($|\\/|\\\\)`)),
...(await generateExternal(options.external || [])),
  1. 获取要external的依赖项,先处理options.external选项中的依赖,处理完后默认过滤线上依赖项
/**
* Support to exclude special package.json
*/
async function generateExternal(external: (string | RegExp)[]) {
const result: (string | RegExp)[] = []

for (const item of external) {
if (typeof item !== 'string' || !item.endsWith('package.json')) {
result.push(item)
continue
}

const pkgPath: string = path.isAbsolute(item)
? path.dirname(item)
: path.dirname(path.resolve(process.cwd(), item))

const deps = await getProductionDeps(pkgPath)
result.push(...deps)
}

return result
}
  1. 获取dependenciespeerDependencies
/*
* Production deps should be excluded from the bundle
*/
export async function getProductionDeps(cwd: string, clearCache: boolean = false) {
const data = await loadPkg(cwd, clearCache)

const deps = Array.from(
new Set([...Object.keys(data.dependencies || {}), ...Object.keys(data.peerDependencies || {})]),
)

return deps
}