handleGenerateWrite 方法定义
执行完 "build" 之后就是 "generate" 阶段了。
先看下 handleGenerateWrite 的方法定义:
src/rollup/rollup.ts
async function handleGenerateWrite(
isWrite: boolean,
inputOptions: NormalizedInputOptions,
unsetInputOptions: ReadonlySet<string>,
rawOutputOptions: OutputOptions,
graph: Graph
): Promise<RollupOutput> {
const {
options: outputOptions,
outputPluginDriver,
unsetOptions
} = await getOutputOptionsAndPluginDriver(
rawOutputOptions,
graph.pluginDriver,
inputOptions,
unsetInputOptions
);
return catchUnfinishedHookActions(outputPluginDriver, async () => {
const bundle = new Bundle(outputOptions, unsetOptions, inputOptions, outputPluginDriver, graph);
const generated = await bundle.generate(isWrite);
if (isWrite) {
timeStart('WRITE', 1);
if (!outputOptions.dir && !outputOptions.file) {
return error(errorMissingFileOrDirOption());
}
await Promise.all(
Object.values(generated).map(chunk =>
graph.fileOperationQueue.run(() => writeOutputFile(chunk, outputOptions))
)
);
await outputPluginDriver.hookParallel('writeBundle', [outputOptions, generated]);
timeEnd('WRITE', 1);
}
return createOutput(generated);
});
}
handleGenerateWrite 接收的参数:
- isWrite 表示是否需要将打包后的代码写入文件
- inputOptions 是 rollup 的 inputOptions 配置
- unsetInputOptions 是一个空的 Set
- rawOutputOptions 是我们传入的打包输出配置
- graph 就是 rollup 的模块图,它保存了所有的源码信息和模块相关的信息
handleGenerateWrite 方法内部首先通过 const { options: outputOptions, outputPluginDriver, unsetOptions } = getOutputOptionsAndPluginDriver(rawOutputOptions, graph.pluginDriver, inputOptions, unsetInputOptions) 得到经过 merge 后的 outputOptions,outputPluginDriver, unsetOption。然后执行了 return catchUnfinishedHookActions() 操作。
catchUnfinishedHookActions(outputPluginDriver, callback) 方法内部调用了 Promise.race([callback(), emptyEventLoopPromise]) 并将结果返回。此处我们主要分析它的 callback 函数逻辑:
async () => {
const bundle = new Bundle(outputOptions, unsetOptions, inputOptions, outputPluginDriver, graph);
/**
* bundle.generate(isWrite)会得到一个 outputBundleBase 对象。(类似 {index.js: {…}, acorn-bf6b1c54.js: {…}})
*/
const generated = await bundle.generate(isWrite);
if (isWrite) {
if (!outputOptions.dir && !outputOptions.file) {
return error(errorMissingFileOrDirOption());
}
// graph.fileOperationQueue.run() 会从队列中取出一个任务执行,它的作用主要是保证任务的执行顺序(先进先执行)
await Promise.all(
Object.values(generated).map(chunk =>
graph.fileOperationQueue.run(() => writeOutputFile(chunk, outputOptions))
)
);
//...
}
//createOutput(generated) 会得到一个 output[...chunks]
return createOutput(generated);
};
callback 中第一步执行了 new Bundle 生成了 bundle 实例。然后执行 const generated = await bundle.generate(isWrite) 。这个 generated 就是最终的 outputBundle, 即以文件名为 key 以打包的区块为 value 组成的对象。它的接口定义如下:
interface OutputBundle {
[fileName: string]: OutputAsset | OutputChunk;
}
isWrite 代表是否需要将打包后的 code 写入文件。并且 if 语句中第一步有判断, 如果既没有设置 outputOptions.dir 又没有设置 outputOptions.file 则会抛出一个错误提示。然后执行 Promise.all 方法将所有的 chunks 执行 writeOutputFile(chunk, outputOptions) 操作。
writeOutputFile 方法的作用很明确,即将 chunk 写入到文件中。它的代码定义在 src/rollup/rollup.ts 中:
async function writeOutputFile(
outputFile: OutputAsset | OutputChunk,
outputOptions: NormalizedOutputOptions
): Promise<unknown> {
// 将 outputFile.fileName 参数解析为绝对路径
const fileName = resolve(outputOptions.dir || dirname(outputOptions.file!), outputFile.fileName);
// 创建文件
await fs.mkdir(dirname(fileName), { recursive: true });
// 写文件
return fs.writeFile(fileName, outputFile.type === 'asset' ? outputFile.source : outputFile.code);
}
handleGenerateWrite 函数逻辑总结
- 首先通过 getOutputOptionsAndPluginDriver 方法获取 outputOptions, outputPluginDriver, unsetOptions
- 执行 new Bundle(outputOptions, unsetOptions, inputOptions, outputPluginDriver, graph).generate(isWrite) 得到 outputBundle 对象。
- 如果 isWrite 为 true 则循环 chunks 执行 writeOutputFile(chunk, outputOptions) 将最终的代码写入到指定文件中。
- 执行 createOutput(generated) 并最终将 output[...chunks] 返回。
下一节我们继续分析 bundle.generate 的详细过程。
rollup 揭秘相关文章
- rollup 技术揭秘系列一 准备篇(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列二 源码目录结构及打包入口分析(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列三 rollup 函数(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列四 graph.build(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列五 构建依赖图谱(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列六 模块排序(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列七 includeStatements(可能是全网最系统性的 rollup 源码分析文章
- rollup 技术揭秘系列八 node.hasEffects(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列九 module.include()(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列十 includeStatements 总结(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列十一 rollup 打包配置选项整理(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列十二 handleGenerateWrite(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列十三 bundle.generate(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列十四 renderChunks(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列十五 renderModules(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列十六 Rollup 插件开发指南(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列十七 rollup-cli 的开发(可能是全网最系统性的 rollup 源码分析文章)
- rollup 技术揭秘系列十八 Rollup 打包流程示意图(可能是全网最系统性的 rollup 源码分析文章)