从一个简单的demo学习rollup的流程

demo

// main.js
import { name, age } from "./msg.js";
let msg = 123;
console.log(name);
// msg.js
export const name = "name";
export const age = "age";
// 通过 https://astexplorer.net/ 查看 ast语法树
// rollup.config.js
import babel from "@rollup/plugin-babel";
export default {
  input: "./main.js",
  output: {
    file: "dist/bundle.js",
    format: "es",
  },
  plugins: [
    babel({
      babelHelpers: "bundled",
      exclude: "node_modules/**",
      presets: ["@babel/preset-env"],
    }),
  ],
};
// npm script
"build": "rollup -c"
复制代码

ast.png

1. 从入口出发

  1. rollup -c 命令
// package.json中bin和main字段
"bin": {
  "rollup": "dist/bin/rollup"
}
"main": "dist/rollup.js",
复制代码
  1. bin/rollup
const rollup = require('../shared/rollup.js')
// 参数
const command = yargsParser(process.argv.slice(2))
// 执行rollup
runRollup(command);
async function runRollup(command) {
  const { options } = await getConfigs(command);
  for (const inputOptions of options) {
    await build(inputOptions, warnings, command.silent);
  }
}
// build执行打包
async function build(inputOptions) {
  // rollup函数返回的是bundle 里面几个方法
  const bundle = await rollup.rollup(inputOptions);
  // 调用rollup函数返回的 write方法
  await Promise.all(outputOptions.map(bundle.write));
}
复制代码

2.rollup函数

// shared/rollup.js
function rollup(rawInputOptions) {
  return rollupInternal(rawInputOptions, null);
}

// 只关系主流程 不 care watcher
function rollupInternal(rawInputOptions, watcher) {
  // 1. 处理参数 标准化参数 会有一些默认的参数
  const { options: inputOptions, unsetOptions: unsetInputOptions } = await getInputOptions(rawInputOptions, watcher !== null);
  // 2. 初始化 Graph(rollup的核心) 类比 webpack 的 compiler 编译对象?
  const graph = new Graph(inputOptions, watcher);
  // 3. 执行 build 方法 生成 module
  // rollup也是基础插件的 有很多钩子的
  await graph.pluginDriver.hookParallel('buildStart', [inputOptions]);
  await graph.build();
  await graph.pluginDriver.hookParallel('buildEnd', []);
  // 4. 定义write函数 返回结果
  const result = {
    async close() {},
    async generate() {},
    watchFiles: Object.keys(graph.watchFiles),
    // 执行rollup生成bundle之后 会调用这个write方法
    async write(rawOutputOptions) {
      return handleGenerateWrite(true, inputOptions, unsetInputOptions, rawOutputOptions, graph);
    }
  }
}
复制代码

2.1.getInputOptions

function getInputOptions(rawInputOptions, watchMode) {
  // 确保plugins是数组
  const rawPlugins = ensureArray(rawInputOptions.plugins);
  // 格式化配置 rollup.config.js
  const { options, unsetOptions } = normalizeInputOptions(
    await rawPlugins.reduce(
      // 处理插件的参数 options 包装成promise数组
      applyOptionHook(watchMode),
      Promise.resolve(rawInputOptions)
    )
  );
  // 格式化插件 设置默认插件名 index
  normalizePlugins(options.plugins);
  return { options, unsetOptions };
}
复制代码

2.2 Graph

// 初始化 这里面很多概念
class Graph {
  constructor(options, watcher) {
    this.modulesById = {}
    this.modules =[]
    this.options = options
    // 1. 路径追踪系统 先跳过
    this.deoptimizationTracker = new PathTracker();
    // 缓存
    this.cachedModules = new Map();
    if(options.cache !== false) {}
    // 监听模式
    if(watcher) {}
    // 2.插件驱动器 注入文件操作方法 插件环境上下文等操作 basePluginDriver
    this.pluginDriver = new PluginDriver()
    // 3. 全局作用域 作用域 分很多种 全局 模块 块级 等等
    this.scope = new GlobalScope();
    // 4. js parser acorn
    this.acornParser = acorn.Parser.extend(...options.acornInjectPlugins);
    // 5. 模块加载器 用于模块的解析和加载
    this.moduleLoader = new ModuleLoader();
  }
}
复制代码

2.3 PluginDriver

// 插件驱动器 使用 pluginDriver 来执行插件中的hook
class PluginDriver {
  constructor(graph, options, userPlugins, pluginCache, basePluginDriver) {
    this.basePluginDriver = basePluginDriver; // 根插件驱动器
    // 为插件设置上下文
    this.pluginContexts.set()
  }
  hookFirst(){}
  hookParallel(){}
  // 执行插件 hook.apply()
  runHook(){}
}
复制代码

2.4 GlobalScope

// 全局作用域 对应的 GlobalVariable
class GlobalScope extends Scope {}
class Scope{
  // 添加到作用域中
  addDeclaration() {}
}
复制代码

2.5 ModuleLoader

class ModuleLoader {
  constructor(graph, modulesById, options, pluginDriver) {
    this.modulesById = modulesById;
  }
  // 类比 webpack 中 的 compilation addEntry ?
  async addEntryModules(unresolvedEntryModules, isUserDefined) {

  } 
}
复制代码

3. build

// 执行 graph的build方法 主要用来生成 module
function build() {
  // 1.构建模块图
  await this.generateModuleGraph();
  // 2.排序
  // this.sortModules();
  // 3. 标记为包含
  this.includeStatements();
}
复制代码

3.1 generateModuleGraph

async generateModuleGraph() {
  await this.moduleLoader.addEntryModules(normalizeEntryModules(this.options.input))
}

function normalizeEntryModules(entryModules) {
  return Object.entries(entryModules).map(([name, id]) => ({
    fileName: null,
    id,
    implicitlyLoadedAfter: [],
    importer: undefined,
    name,
  }));
}
复制代码

3.2 addEntryModules

// name id filename
function addEntryModules(unresolvedEntryModules) {
  unresolvedEntryModules.map(({ id, importer }) =>
    // 对文件进行解析分析依赖 id是文件的路径 从入口开始 这里就是 main.js
    this.loadEntryModule(id, true, importer, null)
  )
}
复制代码

3.3 loadEntryModule

function loadEntryModule() {
  // 找到绝对路径 
  const resolveIdResult = await resolveId();
  return this.fetchModule(this.addDefaultsToResolvedId(resolveIdResult), undefined);
}

function addDefaultsToResolvedId(resolvedId) {
  return {
    id: resolvedId.id
  }
}
复制代码

3.4 fetchModule

function fetchModule({ id }, importer, isEntry) {
  // 1. 生成模块
  const module = new Module()
  // 2. 建立对应关系
  this.modulesById.set(id, module);
  // 3. 为每个模块添加监听
  this.graph.watchFiles[id] = true;
  // 4. 获取文件的内容
  await this.addModuleSource(id, importer, module);
  // 5. 执行钩子
  await this.pluginDriver.hookParallel("moduleParsed", [module.info]);
  // 6. 处理依赖 递归处理
  await this.fetchStaticDependencies(module),
  await this.fetchDynamicDependencies(module),
  // 7. linkImports
  module.linkImports();
  // 8. 返回模块
  return module
}
复制代码

3.4.1 Module

// 简单理解为每个文件都是一个module
class Module {
  constructor(graph, id, options, isEntry) {
    this.context = options.moduleContext(id);
    this.info = {}
  }
}
复制代码

3.4.2 addModuleSource

function addModuleSource(id, importer, module) {
  // 1. load modules 执行load方法或者直接读取文件内容
  let source =  
    (await this.pluginDriver.hookFirst("load", [id])) ?? (await readFile(id))
  // 2. 添加到模块内容上
  const sourceDescription = { code: source };
  module.setSource(
    await transform(
      sourceDescription,
      module,
      this.pluginDriver,
      this.options.onwarn
    )
  )
}
复制代码

3.4.3 transform

function transform() {
  return pluginDriver.hookReduceArg0('transform').then(code => {
    return {ast, code}
  })
}
复制代码

3.4.4 setSource

import { nodeConstructors } from "./ast/nodes/index";
function setSource(ast, code) {
  // 1. acorn.Parser.parse(code) 生成ast语法树
  if (!ast) ast = this.graph.contextParse(this.info.code);
  // 2. magicString
  this.magicString = new MagicString(code, {});
  // 3.ast上下文
  this.astContext = {
    addExport: this.addExport.bind(this),
    addImport: this.addImport.bind(this),
    nodeConstructors,
  };
  // 4. 模块作用域
  this.scope = new ModuleScope(this.graph.scope, this.astContext);
  // 5. 分析ast
  this.ast = new Program(ast, { context: this.astContext, type: 'Module' }, this.scope)
  this.info.ast = ast
}

function addImport() {
  const source = node.source.value;
  this.sources.add(source);
}
复制代码

3.4.5 nodeConstructors

// 不同ast节点的构造函数 在 Program中会使用
import ImportDeclaration from "./ImportDeclaration";
import Program from "./Program";
import Identifier from "./Identifier";

export const nodeConstructors = {
  ImportDeclaration,
  Program,
  Identifier,
};
复制代码

3.4.6 Program

class Program extends NodeBase {
  render(code, options) {
    if (this.boy.length) {
      renderStatementList(this.body, code, this.start, this.end, options);
    } else {
      super.render(code, options);
    }
  }
}
复制代码

3.4.7 NodeBase

class NodeBase extends ExpressionEntity {
  constructor(esTreeNode, parent, parentScope) {
    // 创建作用域
    this.createScope(parentScope);
    // 解析节点
    this.parseNode(esTreeNode);
    this.initialise();
  }
  // 我们的 esTreeNode 结果大概是 {start: end: body: []} 这样的
  parseNode(esTreeNode) {
    // 分析body中的调用对应的构造器
    for (const [key, value] of Object.entries(esTreeNode)) {
      if (Array.isArray(value)) {
        // body属性 [statement]
        for (const child of value) {
          this[key].push(
            child === null
              ? null
              : // ImportDeclaration class ImportDeclaration
                new this.context.nodeConstructors[child.type]()
          );
        }
      }
    }
  }
}

// ImportDeclaration
class ImportDeclaration extends NodeBase {
  // 调用上下文中的 addImport
  initialise() {
    // this.sources.add(source); './msg'在这里被添加到 sources中了
    this.context.addImport(this);
  }
}
复制代码

3.4.8 fetchStaticDependencies

// 处理依赖 处理source属性 我们import中添加了一个msg 递归生成 msg对应的module
function fetchStaticDependencies() {
  Array.from(module.sources, async (source) =>
    // this.fetchModule(resolvedId, importer, false); 会区分是否为外部依赖
    this.fetchResolvedDependency(source, module.id, module.resolvedIds)
  )
}
复制代码

3.5 result

// build函数完成 我们得到 main和msg对应的两个module
// graph 的结构大致如下
{
  modules: [mainModule, msgModule],
  modulesById: {main: mainModule, msg: msgModule},
  moduleLoader:ModuleLoader,
  pluginDriver:PluginDriver,
  scope:GlobalScope: {children: [moduleScope, moduleScope]}
}
// module 中的属性
{
  graph,
  id,
  name,
  filename,
  imports: [],
  exports: [],
  scope,
  ast,
  info
}
复制代码

4. bundle.write

// 经过rollup的过程 我们得到了modules 然后我们 实例话bundle 然后根据module生成chunk
// 将 outputBundle 写入到文件系统中
// rollup 函数返回 write
async write(rawOutputOptions) {
  return handleGenerateWrite(true, inputOptions, unsetInputOptions, rawOutputOptions, graph);
}
async function handleGenerateWrite() {
  // 1. 标准化配置 创建插件驱动器 createOutputPluginDriver
  // outputOptions: {file:'dist/bundle.js'}
  const { options: outputOptions, outputPluginDriver } =
    getOutputOptionsAndPluginDriver();
  // 2. 创建 bundle
  const bundle = new Bundle(outputOptions, outputPluginDriver, graph);
  // 3.调用bundle的generate方法
  const generated = await bundle.generate();
  // 4. 写入到文件系统中
  await Promise.all(
    Object.values(generated).map((chunk) =>
      // fs.writeFile 判断sourcemap
      writeOutputFile(chunk, outputOptions)
    )
  );
  // 5. 返回
  return createOutput(generated);
}
复制代码

4.1 getOutputOptionsAndPluginDriver

function getOutputOptionsAndPluginDriver() {
  // new PluginDriver(); fileEmitter 为什么要 new 一个
  const outputPluginDriver = inputPluginDriver.createOutputPluginDriver(rawPlugins);
  return {
    ...getOutputOptions(),
    outputPluginDriver
  }
}
复制代码

4.2

class Bundle {
  constructor(outputOptions, unsetOptions, inputOptions, pluginDriver, graph) {
    this.outputOptions = outputOptions;
    this.unsetOptions = unsetOptions;
    this.inputOptions = inputOptions;
    this.pluginDriver = pluginDriver;
    this.graph = graph;
  }
}
复制代码

4.3 bundle.generate

function generate() {
  const outputBundle = Object.create(null);
  // 1. renderStart 插件
  await this.pluginDriver.hookParallel('renderStart', [this.outputOptions, this.inputOptions]);
  // 2. 生成chunks new Chunk()
  const chunks = await this.generateChunks()
  // 3. 获取公共的路径
  const inputBase = commondir(getAbsoluteEntryModulePaths(chunks));
  // 4. 预渲染 chunks 
  // 我们需要在预渲染之前创建插件 预渲染和渲染之间没有异步代码
  // {banner: '', footer: '', intro: '', outro: ''}
  const addons = await createAddons(this.outputOptions, this.pluginDriver);
  // 调用 chunk 的 preRender 方法
  this.prerenderChunks(chunks, inputBase);
  // 5. 将chunk加入到bundle中 调用chunk 的 render方法  
  // Object.assign(outputChunk, await chunk.render()) 
  await this.addFinalizedChunksToBundle(chunks, inputBase, addons, outputBundle);
  this.finaliseAssets(outputBundle);
  // code在哪里处理的 tree-shaking如何处理的
  // bundle.js: { code:'const name = "name";\n\nconsole.log(name);\n', fileName:'bundle.js'}
  return outputBundle
}
复制代码

4.3.1 generateChunks

function generateChunks() {
  const chunks = []
  const chunkByModule = new Map()
  // {alias: null, modules: [main.js, msg.js]} 我们上一步生成的两个 module
  const chunkDefinitions = getChunkAssignments(this.graph.entryModules)
  for(const { alias, modules } of chunkDefinitions) {
    // 生成chunk 和webpack有点类似 一般是一个入口对应一个chunk (动态导入的怎么处理的?)
    const chunk = new Chunk(modules, this.inputOptions)
    chunks.push(chunk)
  }
  return [...chunks]
}

function addManualChunks(manualChunks) {
  addModuleToManualChunk(alias, entry, manualChunkAliasByEntry);
}
function getChunkAssignments() {
  for(const entry of entryModules) {
    assignEntryToStaticDependencies(entry, null);
  }
  // dynamicEntryModules
  // assignEntryToStaticDependencies()
  // {alias: null, modules}
  chunkDefinitions.push(...createChunks([...entryModules, ...dynamicEntryModules]));
  return chunkDefinitions;
}
复制代码

4.3.2 prerenderChunks

function prerenderChunks() {
  for (const chunk of chunks) {
    chunk.generateExports();
  }
  for (const chunk of chunks) {
    chunk.preRender(this.outputOptions, inputBase);
  }
}
复制代码

4.3.3 addFinalizedChunksToBundle

function addFinalizedChunksToBundle() {
  for(const chunk of chunks) {
    // 为 outputBundle 添加chunk
    outputBundle[chunk.id] = chunk.getChunkInfoWithFileNames();
  }
  await Promise.all(chunks.map(async (chunk) => {
    const outputChunk = outputBundle[chunk.id];
    // 调用 chunk的render方法 得到code
    Object.assign(outputChunk, await chunk.render(this.outputOptions, addons, outputChunk));
  }))
}
复制代码

4.3.4 finaliseAssets

function finaliseAssets(outputBundle) {
  for(const file of Object.values(outputBundle)) {
    if (this.outputOptions.validate && typeof file.code == 'string') {
      this.graph.contextParse(file.code)
    }
  }
  this.pluginDriver.finaliseAssets();
}
复制代码

5. chunk

class Chunk {
  constructor(orderedModules, modulesById) {
    this.orderedModules = orderedModules
    this.modulesById = modulesById;
    this.imports = new Set();
    const chunkModules = new Set(orderedModules);
    // 我们生成的 main msg两个module
    for(const module of orderedModules) {

    }
  }
}
复制代码

5.1 preRender

function preRender(options, inputBase) {
	const magicString = new MagicStringBundle()
  const renderOptions = {}
  for(const module of this.orderedModules) {
    // 调用module的render方法
    const source = module.render(renderOptions)
    // 我们最终得到的资源还是module render生成的内容
    this.renderedModuleSources.set(module, source);
    magicString.addSource(source);
    // [name] [age] 没有用到的就需要 removed
    const { renderedExports, removedExports } = module.getRenderedExports();
    const { renderedModuleSources } = this;
    // 渲染的模块 最终生成的 code
    renderedModules[module.id] = {
      get code() {
        var _a, _b;
        return (_b =
          (_a = renderedModuleSources.get(module)) === null || _a === void 0
            ? void 0
            : _a.toString()) !== null && _b !== void 0
          ? _b
          : null;
      },
      originalLength: module.originalCode.length,
      removedExports,
      renderedExports,
    };
  }
}

复制代码

5.2 render

function render(options, addons, outputChunk) {
 // 1. this.dependencies
 // 2. finaliseDynamicImports
 // 3. finaliseImportMetas
 // 4. scope 
 const format = options.format;
 // 不同格式的 我们这里是 es
 const finalise = finalisers[format];
 // renderedSource 在 preRender中处理
 const magicString = finalize(this.renderedSource, {})
 const prevCode = magicString.toString();
 let code = await renderChunk({code: prevCode})
  if (options.sourcemap) {}
 return {code, map}
}
复制代码

5.3 renderChunk

function renderChunk({code, options, renderChunk}) {
  return outputPluginDriver.hookReduceArg0('renderChunk', [code, renderChunk, options], renderChunkReducer);
}
复制代码

5.4 es

// source: {}
function es(magicString) {
  const importBlock = getImportBlock(dependencies, _)
  const exportBlock = getExportBlock(exports, _, varOrConst)
  return magicString.trim();
}
复制代码

6. module

// 我们在chunk中得到的source是module生成的
for (const module of this.orderedModules) {
  // 遍历我们得到的两个module  [main, msg] 
  // 先处理msg 得到 const name = 'name'
  // 在处理 main.js 得到 console.log(name)
  // 我们得到source 然后根据资源生成对应的 code 写入到文件系统中
  const source = module.render(renderOptions).trim();
}

// module的render 我们最终得到的内容是 module生成的
function render() {
  const magicString = this.magicString.clone();
  this.ast.render(magicString, options)
  return magicString;
}

// Program 从这里开始处理 ast节点
function render(code, options) {
  renderStatementsList(this.body,code)
}
复制代码

6.1 Program

function render(code, options) {
  // 开始处理ast树
  renderStatementList(this.body, code, this.start, this.end, options);
}
复制代码

6.2 renderStatementList

function renderStatementList() {
  for (let nextIndex = 1; nextIndex <= statements.length; nextIndex++) {
    // 不同语句调用自己的render方法
    // 我们先处理msg的时候会调用 ExportNamedDeclaration 的render方法
    if(currentNode.included) {
      currentNode.render(code, options, {})
    } else {
      // export const age 没有用到需要 treeShaking掉的 included属性何时添加的?
      treeshakeNode(currentNode, code)
    }
  }
}
复制代码

6.2.1 ExportNamedDeclaration

function render(code, options) {
  // 将export关键字去掉
  code.remove(this.start, this.declaration.start);
  // VariableDeclaration
  this.declaration.render(code, options, { end, start });
}
复制代码

6.2.2 VariableDeclaration

function render() {
  // VariableDeclarator const name = 'name'
  for (const declarator of this.declarations) {
    declarator.render(code, options);
  }
}
复制代码

6.2.3 VariableDeclarator

function render(code, options) {
  this.id.render(code, options); // Identifier name
  this.init.render(code, options) // Literal 'name'
}
复制代码

6.2.4 ExpressionStatement

// main.js 中 第一个ImportDeclaration 直接 treeshakeNode
// 第二个 let msg  VariableDeclaration 也直接treeshake掉
// 第三个是 ExpressionStatement 会被多包装一层 ExpressionStatement
function render(code, options) {
  super.render(code, options)
  this.insertSemicolon(code);
}
复制代码

6.2.5 NodeBase

function render() {
  // ['expression']
  for (const key of this.keys) {
    value.render(code, options)
  }
}
复制代码

6.2.6 CallExpression

function render() {
 // 1. callee MemberExpression
 this.callee.render(code, options, {})
 // 2. arguments name Identifier
  for (const arg of this.arguments) {
    arg.render(code, options);
  }
}
复制代码

6.2.7 MemberExpression

function render() {
  // object, property Identifier
  this.object.render();
  this.property.render(code, options);
}
复制代码

6.2.8 Identifier

function render() {
  const name = this.variable.getName();
  // 不想等就replace
}
复制代码

6.3 treeshakeNode

function treeshakeNode(node, code, start, end) {
  code.remove(start, end)
}
复制代码

6.4 include

// 我们处理ast节点的时候是根据 included 属性来判断是否需要 tree-shaking的
// 我们是什么时间添加的这个属性? 我们之前通过rollup得到我们的module 在module上有ast属性
// 回到rollup函数

// graph.build() 
//   => generateModuleGraph() 
//   => addEntryModules()
//   => addEntryModules 
//   => loadEntryModule 
//   => fetchModule 
//   => new Module() addModuleSource() 
//     ==> module.setSource(await transform())

// code: main.js msg.js源代码
function setSource({ast, code}) {
  this.astContext = {}
  this.scope = new ModuleScope()
  this.ast = new Program(ast, { context: this.astContext, type: 'Module' }, this.scope)
}
// 经过处理 ast中的 included:false 都为false 那么什么时候发生变化的?
// 我们会递归的处理 依赖 main中的就是msg
await this.fetchStaticDependencies()
// build完成之后 我们会执行 在这里修改的 included属性
this.includeStatements()
复制代码

6.4.1 includeStatements

function includeStatements() {
  module.includeAllExports(false);
  for (const module of this.modules) {
    // 先处理msg 执行include shouldBeIncluded 为 false
    // 在处理main 执行include this.ast.include(content,false)
    // 当处理到main的console时候会访问到name变量 然后会递归的去处理msg模块
    module.include()
  }
}
// 不同的类型不同的只 简单理解为我们访问到的变量就会被包含进行
function include() {
  const context = createInclusionContext();
  // this.ast = new Program
  // NodeBase中 return this.included || (!content.brokenFlow)
  // 调用 include 方法就会将 	this.included = true;
  if(this.ast.shouldBeIncluded(context)) {
    this.ast.include(context, false)
  }
}

// Program
function include(context, includeChildrenRecursively) {
  this.included = true;
  // 执行node的include
  for (const node of this.body) {
    // console.log() 才会执行到 
    // msg中 name的会执行 但是age不会执行
    // 这个属性又是如何确定的?
    if(includeChildrenRecursively || node.shouldBeIncluded(context)) {
      node.include(context)
    }
  }
}
// brokenFlow 属性如何确定的
function shouldBeIncluded(context) {
  return this.included || (!context.brokenFlow && this.hasEffects(createHasEffectsContext()));
}
// CallExpression 
function include() {
  this.included = true;
  this.callee.include(context, false);
  // 处理arguments
  this.callee.includeCallArguments(context, this.arguments);
}
// callee MemberExpression
function include() {
  this.included = true;
  this.object.include(context, includeChildrenRecursively);
  this.property.include(context, includeChildrenRecursively);
}
// Identifier
function include() {
  this.included = true;
  // 调用module的方法 当变量name的时候
  this.context.includeVariableInModule(this.variable);
}
function includeVariableInModule() {
  // this.graph.needsTreeshakingPass = true; 下次遍历 msg
  // 递归处理msg的
  this.includeVariable(variable);
  this.imports.add(variable); // main模块的 imports中有一个 name变量
}
function includeVariable() {
  variable.include(); // LocalVariable
}
// GlobalVariable console log
// VariableDeclaration 
function include() {
  this.include = true
  // name就会被include
  for (const declarator of this.declarations) {
    if (includeChildrenRecursively || declarator.shouldBeIncluded(context))
      declarator.include(context, includeChildrenRecursively);
  }
}
// VariableDeclarator id init
复制代码

6.4.2 shouldBeIncluded

// context.brokenFlow
function shouldBeIncluded(context) {
  return this.included || (!context.brokenFlow && this.hasEffects())
}

function include() {
  const context = createInclusionContext();
}

function createInclusionContext() {
  return {
    brokenFlow: BROKEN_FLOW_NONE, // 0
  }
}

function hasEffects() {
  for (const node of this.body) {
    if (node.hasEffects(context)) {
      return (this.hasCachedEffect = true);
    }
  }
  return false;
}

// ExportNamedDeclaration
function hasEffects(context) {
  return this.declaration !== null && this.declaration.hasEffects(context);
}
// NodeBase
function hasEffects(context) {
  child.hasEffects(context)
}
// VariableDeclarator
function hasEffects(context) {
  // id是变量名 init是值
  // class LocalVariable
  return this.id.hasEffects(context) || (this.init !== null && this.init.hasEffects(context));
}
// Identifier
function hasEffects() {
  // console就会到这里 然后include
  return this.variable instanceof GlobalVariable && this.variable.hasEffectsWhenAccessedAtPath(EMPTY_PATH)
}
// ImportDeclaration
function hasEffects() return false
// ExpressionStatement
function hasEffects() {
  child.hasEffects()
}
// CallExpression
function hasEffects() {
  argument.hasEffects(context)
  this.callee.hasEffects(context)
}
// MemberExpression object property

// LocalVariable
function include() {
  this.included = true;
  // 这里将 export 的name变成 include
  for(const declaration of this.declarations) {
    declaration.include(createInclusionContext(), false);
  }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享