跳到主要内容

external

CLI支持两种功能:

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

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

wj-fe-bundle 与 wj-fe-bundle-node

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

"bin": {
"wj-fe-bundle": "dist/cli-default.js",
"wj-fe-bundle-node": "dist/cli-node.js"
}

两者区别在于wj-fe-bundle-node传入了以下 option:

skipNodeModulesBundle: true,

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

import { Plugin } from 'esbuild';
import { tsconfigPathsToRegExp, match } 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 const 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)) {
return;
}

过滤 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
*/
const generateExternal = async (external: (string | RegExp)[]) => {
const result: (string | RegExp)[] = [];

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

let 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;
}