Appearance
Rollup.js 源码摘要 
调试命令:
bash
# 先构建
pnpm run build
# 进入调试流程
./dist/bin/rollup -c zhi.config.ts --configPlugin typescript看源码技能 get ✅
- 1、注意 class 类,尤其是属性;
 - 2、注意方法名称,代表功能;
 - 3、看函数返回了什么,返回值才是目的嘛;
 
rollup(命令) 源码摘要 
- 获取参数。
 - 解析配置。
 - 唤起打包核心函数。
 
ts
// 获取参数 cli/cli.ts
const command = argParser(process.argv.slice(2), {
  alias: commandAliases,
  configuration: { "camel-case-expansion": false },
});
if (command.help || (process.argv.length <= 2 && process.stdin.isTTY)) {
  console.log(`\n${help.replace("__VERSION__", version)}\n`);
} else if (command.version) {
  console.log(`rollup v${version}`);
} else {
  run(command);
}
// 即 run()
export default async function runRollup(
  command: Record<string, any>
): Promise<void> {
  try {
    // 解析配置
    const { options, warnings } = await getConfigs(command);
    try {
      for (const inputOptions of options) {
        // 开启打包流程
        await build(inputOptions, warnings, command.silent);
      }
    } catch (error: any) {
      warnings.flush();
      handleError(error);
    }
  } catch (error: any) {
    handleError(error);
  }
}
export default async function build(
  inputOptions: MergedRollupOptions,
  warnings: BatchWarnings,
  silent = false
): Promise<unknown> {
  // 唤起打包核心函数
  const bundle = await rollup(inputOptions as any);
  // 输出打包产物、写入目标文件
  await Promise.all(outputOptions.map(bundle.write));
  // 执行 closeBundle 钩子
  await bundle.close();
}
export default function rollup(
  rawInputOptions: RollupOptions
): Promise<RollupBuild> {
  return rollupInternal(rawInputOptions, null);
}打包流程简介 
主要分为 2 大阶段:
- 1、构建(
build) 阶段。 - 2、输出(
write/generate)阶段。generate意思是,只输出在内存中,比如ts配置文件,会被先打包成.mjs文件,再读取配置内容,随后立即删除这个临时文件。write就是真正写入磁盘,变成看得见的实体文件。所有目标模块,都写入成实体文件。 
流程函数集锦 
build谱系函数集锦 
ts
runRollup(command)
  getConfigs(command)
    const configFile = await getConfigPath(command.config)
    // 加载配置文件
    const { options, warnings } = await loadConfigFile(configFile, command)
      getConfigList(fileName, commandOptions)
        getConfigFileExport(fileName, commandOptions)
          loadTranspiledConfigFile(fileName, commandOptions)
            const inputOptions = {..., plugins: [] }
            addPluginsFromCommandOption(configPlugin, inputOptions)
            // 解析配置文件
            const bundle = await rollup.rollup(inputOptions) {
              rollupInternal(rawInputOptions, null) {
                // 获取配置文件入口配置
                const { options: inputOptions } = await getInputOptions(rawInputOptions) {
                  const rawPlugins = getSortedValidatedPlugins('options')
                  const { options } = await normalizeInputOptions(
                    rawPlugins.reduce(..., rawInputOptions)) {
                    const options = {..., input: getInput(config)}
                    return { options }
                  }
                  return { options }
                }
                // 创建图谱实例
                const graph = new Graph(inputOptions, watcher) {
                  readonly modulesById = new Map()
                  this.pluginDriver = new PluginDriver(this, options, options.plugins)
                  this.moduleLoader = new ModuleLoader(this, this.modulesById)
                }
                // 进入核心构建流程
                await catchUnfinishedHookActions() {
                  try {
                    await graph.pluginDriver.hookParallel('buildStart')
                    // 构建
                    await graph.build() {
                      // 深度优先递归解析模块内容、依赖等信息,生成关系图谱
                      await this.generateModuleGraph() {
                        await this.moduleLoader.addEntryModules(
                            normalizeEntryModules(this.options.input),
                            true
                          ) {
                          const newEntryModules = await this.extendLoadModulesPromise() {
                            Promise.all(
                              unresolvedEntryModules.map(({ id, importer }) => {
                                this.loadEntryModule(id) {
                                  const resolveIdResult = await resolveId(unresolvedId) {
                                    const pluginResult = await resolveIdViaPlugins(source) {
                                      // return pluginDriver.hookFirstAndGetPlugin('resolveId')
                                    }
                                    return addJsExtensionIfNecessary(source)
                                  }
                                  return this.fetchModule(this.getResolvedIdWithDefaults(id)) {
                                    // 创建模块实例
                                    const module = new Module(id, ...)
                                    this.modulesById.set(id, module)
                                    // 添加模块源信息
                                    const loadPromise = this.addModuleSource(id, module) {
                                      try {
                                        source = await this.pluginDriver.hookFirst('load', [id])
                                      }
                                      // 更新模块相关信息
                                      module.updateOptions(sourceDescription)
                                      // 设置模块相关信息
                                      module.setSource(transform(module) {
                                        code = await pluginDriver.hookReduceArg0('transform', module.id)
                                        return { code, ... }
                                      }) {
                                        this.info.code = code
                                        const moduleAst = ast ?? this.tryParse()
                                        this.astContext = {code, ...}
                                        this.scope = new ModuleScope()
                                        this.namespace = new NamespaceVariable()
                                        this.ast = new Program()
                                        this.info.ast = moduleAst;
                                      }
                                    }.then(() => {
                                      this.getResolveStaticDependencyPromises(module),
                                      this.getResolveDynamicImportPromises(module),
                                      loadAndResolveDependenciesPromise
                                    })
                                    this.pluginDriver.hookParallel('moduleParsed')
                                    const resolveDependencyPromises = await loadPromise;
                                    await this.fetchModuleDependencies(module, ...resolveDependencyPromises)
                                    return module;
                                  }
                                }
                              }
                            ).then(entryModules => {
                              for (const [index, entryModule] of entryModules.entries()) {
                                addChunkNamesToModule(entryModule)
                                this.indexedEntryModules.push({index: xxx, module: entryModule})
                                this.indexedEntryModules.sort(({index: indexA}, {index: indexB}) => {
                                  indexA > indexB ? 1 : -1
                                })
                              }
                              return entryModules;
                            })
                          }
                          await this.awaitLoadModulesPromise()
                          return {
                            entryModules: this.indexedEntryModules.map(({ module }) => module),
                            newEntryModules
                          }
                        }
                        // 标记内部、外部模块
                        for (const module of this.modulesById.values()) {
                          if (module instanceof Module) {
                            this.modules.push(module);
                          } else {
                            this.externalModules.push(module);
                          }
                        }
                      }
                      // 按模块及依赖执行先后排序,ast按照各自类型绑定引用
                      this.sortModules() {
                        // 排序:递归分析模块,并标记执行顺序,返回排序后的模块数组
                        const { orderedModules, cyclePaths } = analyseModuleExecution(this.entryModules) {
                          const analyseModule = (module) => {
                            if (module instanceof Module) {
                              for (const dependency of module.dependencies) {
                                analyseModule(dependency);
                              }
                            }
                            module.execIndex = nextExecIndex++;
                            analysedModules.add(module);
                          }
                          return { cyclePaths, orderedModules };
                        }
                        this.modules = orderedModules;
                        // ast绑定引用
                        for (const module of this.modules) {
                          module.bindReferences() {
                            this.ast!.bind() {
                              for (const key of this.keys) {
                                child?.bind()
                              }
                            }
                          }
                        }
                      }
                      // 运行node.hasEffects(),标记ast属性included是否含true,决定该节点是否入包
                      this.includeStatements() {
                        for (const module of entryModules) {
                          markModuleAndImpureDependenciesAsExecuted(module) { 
                            baseModule.isExecuted = true;
                          }
                        }
                        for (const module of this.modules) {
                          if (module.info.moduleSideEffects === 'no-treeshake') {
                            module.includeAllInBundle() {
                              this.ast!.include(createInclusionContext(), true) {
                                // Program.ts
                                include() {
                                  this.included = true
                                  for (const node of this.body) {
                                    if(...) node.include(context, includeChildrenRecursively)
                                  }
                                }
                              }
                              this.includeAllExports(false)
                            } else {
                              module.include() {
                                const context = createInclusionContext()
                                if (this.ast!.shouldBeIncluded(context) {
                                  return this.included || (!context.brokenFlow && this.hasEffects(createHasEffectsContext()))
                                }) {
                                  this.ast!.include(context, false)
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                    // 执行 buildEnd 钩子
                    await graph.pluginDriver.hookParallel('buildEnd')
                  }
                }
                const result = {
                  // 只生成。不写入
                  async generate() {
                    return handleGenerateWrite(false, ...)
                  }
                }
                return result;
              }
            }write/generate谱系函数集锦 
ts
runRollup() {
  const { options, warnings } = await getConfigs(command)
  try {
    for (const inputOptions of options) {
      await build(inputOptions, warnings, command.silent) {
        const outputOptions = inputOptions.output
        // 声明 build 结果
        const bundle = await rollup(inputOptions as any) {
          return rollupInternal(rawInputOptions, null) {
            const { options: inputOptions } = await getInputOptions(rawInputOptions)
            const graph = new Graph(inputOptions, watcher)
            await catchUnfinishedHookActions(graph.pluginDriver, async () => {
              await graph.build(); // build细节参见 build谱系函数集锦
            });
            const result = {
              // 写入、生成文件
              async write() {
                return handleGenerateWrite(true, inputOptions, unsetInputOptions, rawOutputOptions, graph) {
                  const { outputOptions, outputPluginDriver } = await getOutputOptionsAndPluginDriver(rawOutputOptions) {
                    const rawPlugins = await normalizePluginOption(rawOutputOptions.plugins)
                    const outputPluginDriver = inputPluginDriver.createOutputPluginDriver(rawPlugins)
                    return {
                      ...(await getOutputOptions(rawOutputOptions) {
                        return normalizeOutputOptions(outputPluginDriver.hookReduceArg0Sync('outputOptions', [rawOutputOptions], ...))
                      }),
                      outputPluginDriver
                    }
                  }
                  return catchUnfinishedHookActions() {
                    const bundle = new Bundle(outputOptions, outputPluginDriver, graph) {
                      constructor() {
                        private readonly outputOptions
                      }
                      async generate() {
                        // 声明返回数据
                        const outputBundleBase: OutputBundle = Object.create(null)
                        // 创建返回数据转发代理
                        const outputBundle = getOutputBundle(outputBundleBase)
                        this.pluginDriver.setOutputBundle(outputBundle, this.outputOptions)
                        try {
                          await this.pluginDriver.hookParallel('renderStart')
                          // 生成chunks
                          const chunks = await this.generateChunks(outputBundle) {
                            const snippets = getGenerateCodeSnippets(this.outputOptions)
                            const chunks: Chunk[] = []
                            for(const {alias, modules} of getChunkAssignments(this.graph.entryModules)) {
                              const chunk = new Chunk(modules, this.inputOptions, this.outputOptions) {
                                constructor(
                                  // 即第一个参数: modules
                                  private readonly orderedModules: readonly Module[]
                                ) {}
                              }
                              chunks.push(chunk)
                            }
                            for (const chunk of chunks) {
                              // 设置chunk依赖、导入、导出等
                              chunk.link() {
                                this.dependencies = getStaticDependencies(this)
                                for (const module of this.orderedModules) {
                                  this.addImplicitlyLoadedBeforeFromModule(module)
                                  this.setUpChunkImportsAndExportsForModule(module)
                                }
                              }
                            }
                            return [...chunks, ...facades]
                          }
                          // 生成chunk导出变量、模式等
                          for (const chunk of chunks) {
                            chunk.generateExports() {
                              const exportNamesByVariable = this.facadeModule.getExportNamesByVariable()
                              this.exportMode = getExportMode(...)
                            }
                          }
                          // 渲染chunk
                          await renderChunks(chunks, outputBundle = bundle) {
                            const renderedChunks = await Promise.all(chunks.map(chunk => chunk.render()) {
                              // 预备文件名
                              const preliminaryFileName = this.getPreliminaryFileName()
                              const { magicString, usedModules } = this.renderModules(preliminaryFileName.fileName) {
                                const { orderedModules } = this
                                const magicString = new MagicStringBundle({ separator: `${n}${n}` })
                                const usedModules: Module[] = []
                                const renderOptions = {...}
                                for (const module of orderedModules) {
                                  const rendered = module.render(renderOptions) {
                                    const source = this.magicString.clone()
                                    this.ast!.render(source = code, options) {
                                      if (code.original.startsWith('#!')) code.remove(0, start)
                                      if (this.body.length > 0) {
                                        renderStatementList(code, ..., options)
                                      } else {
                                        super.render(code, options)
                                      }
                                    }
                                    return { source, ... }
                                  }
                                  ({ source } = rendered)
                                  magicString.addSource(source)
                                  usedModules.push(module)
                                }
                                return { magicString, renderedSource, usedModules, ... }
                              }
                              const { intro, outro, banner, footer } = await createAddons()
                              return { chunk: this, magicString, preliminaryFileName, usedModules }
                            })
                            // 创建 chunk 图谱
                            const chunkGraph = getChunkGraph(chunks) {
                              return Object.fromEntries(
                                chunks.map(chunk => {
                                  const renderedChunkInfo = chunk.getRenderedChunkInfo()
                                  return [renderedChunkInfo.fileName, renderedChunkInfo]
                                }
                              )
                            }
                            // 生成chunk哈希
                            const { nonHashedChunksWithPlaceholders } = await transformChunksAndGenerateContentHashes(renderedChunks, chunkGraph)
                            const hashesByPlaceholder = generateFinalHashes(..., bundle, )
                            // 整合chunk
                            addChunksToBundle(..., bundle, nonHashedChunksWithPlaceholders) {
                              for (const { chunk, code, fileName, map } of nonHashedChunksWithPlaceholders) {
                                bundle[fileName] = chunk.finalizeChunk(...)
                              }
                            }
                          }
                          // 移除未引用的物料
                          removeUnreferencedAssets(outputBundle) {
                            for (const file of unreferencedAssets) {
                              delete outputBundle[file]
                            }
                          }
                          await this.pluginDriver.hookSeq('generateBundle')
                          // 返回数据
                          return outputBundleBase;
                        }
                      }
                    }
                    const generated = await bundle.generate(isWrite)
                    if (isWrite) {
                      await Promise.all(
                        Object.values(generated).map(chunk => {
                          graph.fileOperationQueue.run(() => writeOutputFile(chunk = outputFile, outputOptions) {
                            const fileName = resolve(outputOptions.dir || dirname(outputOptions.file!), outputFile.fileName)
                            await mkdir(dirname(fileName), { recursive: true })
                            return writeFile(fileName, outputFile.type === 'asset' ? outputFile.source : outputFile.code)
                          })
                        })
                      )
                      await outputPluginDriver.hookParallel('writeBundle')
                    }
                    return createOutput(generated) {
                      return { output: xxx }
                    }
                  }
                }
              }
            }
            return result
          }
        }
        // 唤起写入功能
        await Promise.all(outputOptions.map(bundle.write))
        await bundle.close()
      }
    }
  }
}