导航菜单

  • 0.api
  • 0.Async
  • 0.module
  • 1.ES2015
  • 2.Promise
  • 3.Node
  • 4.NodeInstall
  • 5.REPL
  • 6.NodeCore
  • 7.module&NPM
  • 8.Encoding
  • 9.Buffer
  • 10.fs
  • 11.Stream-1
  • 11.Stream-2
  • 11.Stream-3
  • 11.Stream-4
  • 12-Network-2
  • 12.NetWork-3
  • 12.Network-1
  • 13.tcp
  • 14.http-1
  • 14.http-2
  • 15.compress
  • 16.crypto
  • 17.process
  • 18.yargs
  • 19.cache
  • 20.action
  • 21.https
  • 22.cookie
  • 23.session
  • 24.express-1
  • 24.express-2
  • 24.express-3
  • 24.express-4
  • 25.koa-1
  • 26.webpack-1-basic
  • 26.webpack-2-optimize
  • 26.webpack-3-file
  • 26.webpack-4.tapable
  • 26.webpack-5-AST
  • 26.webpack-6-sources
  • 26.webpack-7-loader
  • 26.webpack-8-plugin
  • 26.webpack-9-hand
  • 26.webpack-10-prepare
  • 28.redux
  • 28.redux-jwt-back
  • 28.redux-jwt-front
  • 29.mongodb-1
  • 29.mongodb-2
  • 29.mongodb-3
  • 29.mongodb-4
  • 29.mongodb-5
  • 29.mongodb-6
  • 30.cms-1-mysql
  • 30.cms-2-mysql
  • 30.cms-3-mysql
  • 30.cms-4-nunjucks
  • 30.cms-5-mock
  • 30.cms-6-egg
  • 30.cms-7-api
  • 30.cms-8-roadhog
  • 30.cms-9-yaml
  • 30.cms-10-umi
  • 30.cms-12-dva
  • 30.cms-13-dva-ant
  • 30.cms-14-front
  • 30.cms-15-deploy
  • 31.dva
  • 31.cms-13-dva-antdesign
  • 33.redis
  • 34.unittest
  • 35.jwt
  • 36.websocket-1
  • 36.websocket-2
  • 38.chat-api-1
  • 38.chat-api-2
  • 38.chat-3
  • 38.chat-api-3
  • 38.chat
  • 38.chat2
  • 38.chat2
  • 39.crawl-0
  • 39.crawl-1
  • 39.crawl-2
  • 40.deploy
  • 41.safe
  • 42.test
  • 43.nginx
  • 44.enzyme
  • 45.docker
  • 46.elastic
  • 47.oauth
  • 48.wxpay
  • index
  • 52.UML
  • 53.design
  • index
  • 54.linux
  • 57.ts
  • 56.react-ssr
  • 58.ts_react
  • 59.ketang
  • 59.ketang2
  • 61.1.devops-linux
  • 61.2.devops-vi
  • 61.3.devops-user
  • 61.4.devops-auth
  • 61.5.devops-shell
  • 61.6.devops-install
  • 61.7.devops-system
  • 61.8.devops-service
  • 61.9.devops-network
  • 61.10.devops-nginx
  • 61.11.devops-docker
  • 61.12.devops-jekins
  • 61.13.devops-groovy
  • 61.14.devops-php
  • 61.15.devops-java
  • 61.16.devops-node
  • 61.17.devops-k8s
  • 62.1.react-basic
  • 62.2.react-state
  • 62.3.react-high
  • 62.4.react-optimize
  • 62.5.react-hooks
  • 62.6.react-immutable
  • 62.7.react-mobx
  • 62.8.react-source
  • 63.1.redux
  • 63.2.redux-middleware
  • 63.3.redux-hooks
  • 63.4.redux-saga
  • 63.5.redux-saga-hand
  • 64.1.router
  • 64.2.router-connected
  • 65.1.typescript
  • 65.2.typescript
  • 65.3.typescript
  • 65.4.antd
  • 65.4.definition
  • 66-1.vue-base
  • 66-2.vue-component
  • 66-3.vue-cli3.0
  • 66-4.$message组件
  • 66-5.Form组件
  • 66-6.tree
  • 66-7.vue-router-apply
  • 66-8.axios-apply
  • 66-9.vuex-apply
  • 66-10.jwt-vue
  • 66-11.vue-ssr
  • 66-12.nuxt-apply
  • 66-13.pwa
  • 66-14.vue单元测试
  • 66-15.权限校验
  • 67-1-network
  • 68-2-wireshark
  • 7.npm2
  • 69-hooks
  • 70-deploy
  • 71-hmr
  • 72.deploy
  • 73.import
  • 74.mobile
  • 75.webpack-1.文件分析
  • 75.webpack-2.loader
  • 75.webpack-3.源码流程
  • 75.webpack-4.tapable
  • 75.webpack-5.prepare
  • 75.webpack-6.resolve
  • 75.webpack-7.loader
  • 75.webpack-8.module
  • 75.webpack-9.chunk
  • 75.webpack-10.asset
  • 75.webpack-11.实现
  • 76.react_optimize
  • 77.ts_ketang_back
  • 77.ts_ketang_front
  • 78.vue-domdiff
  • 79.grammar
  • 80.tree
  • 81.axios
  • 82.1.react
  • 82.2.react-high
  • 82.3.react-router
  • 82.4.redux
  • 82.5.redux_middleware
  • 82.6.connected
  • 82.7.saga
  • 82.8.dva
  • 82.8.dva-source
  • 82.9.roadhog
  • 82.10.umi
  • 82.11.antdesign
  • 82.12.ketang-front
  • 82.12.ketang-back
  • 83.upload
  • 84.graphql
  • 85.antpro
  • 86.1.uml
  • 86.2.design
  • 87.postcss
  • 88.react16-1
  • 89.nextjs
  • 90.react-test
  • 91.react-ts
  • 92.rbac
  • 93.tsnode
  • 94.1.JavaScript
  • 94.2.JavaScript
  • 94.3.MODULE
  • 94.4.EventLoop
  • 94.5.文件上传
  • 94.6.https
  • 94.7. nginx
  • 95.1. react
  • 95.2.react
  • 96.1.react16
  • 96.2.fiber
  • 96.3.fiber
  • 97.serverless
  • 98.websocket
  • 100.1.react-basic
  • 101.1.monitor
  • 101.2.monitor
  • 102.java
  • 103.1.webpack-usage
  • 103.2.webpack-bundle
  • 103.3.webpack-ast
  • 103.4.webpack-flow
  • 103.5.webpack-loader
  • 103.6.webpack-tapable
  • 103.7.webpack-plugin
  • 103.8.webpack-optimize1
  • 103.9.webpack-optimize2
  • 103.10.webpack-hand
  • 103.11.webpack-hmr
  • 103.11.webpack5
  • 103.13.splitChunks
  • 103.14.webpack-sourcemap
  • 103.15.webpack-compiler1
  • 103.15.webpack-compiler2
  • 103.16.rollup.1
  • 103.16.rollup.2
  • 103.16.rollup.3
  • 103.16.vite.basic
  • 103.16.vite.source
  • 103.16.vite.plugin
  • 103.16.vite.1
  • 103.16.vite.2
  • 103.17.polyfill
  • 104.1.binary
  • 104.2.binary
  • 105.skeleton
  • 106.1.react
  • 106.2.react_hooks
  • 106.3.react_router
  • 106.4.redux
  • 106.5.redux_middleware
  • 106.6.connected-react-router
  • 106.6.redux-first-history
  • 106.7.redux-saga
  • 106.8.dva
  • 106.9.umi
  • 106.10.ketang
  • 106.11.antdesign
  • 106.12.antpro
  • 106.13.router-6
  • 106.14.ssr
  • 106.15.nextjs
  • 106.16.1.cms
  • 106.16.2.cms
  • 106.16.3.cms
  • 106.16.4.cms
  • 106.16.mobx
  • 106.17.fomily
  • 107.fiber
  • 108.http
  • 109.1.webpack_usage
  • 109.2.webpack_source
  • 109.3.dll
  • 110.nest.js
  • 111.xstate
  • 112.Form
  • 113.redux-saga
  • 114.react+typescript
  • 115.immer
  • 116.pro5
  • 117.css-loader
  • 118.1.umi-core
  • 119.2.module-federation
  • 119.1.module-federation
  • 120.create-react-app
  • 121.react-scripts
  • 122.react-optimize
  • 123.jsx-runtime
  • 124.next.js
  • 125.1.linux
  • 125.2.linux-vi
  • 125.3.linux-user
  • 125.4.linux-auth
  • 125.5.linux-shell
  • 125.6.linux-install
  • 125.7.linux-system
  • 125.8.linux-service
  • 125.9.linux-network
  • 125.10.nginx
  • 125.11.docker
  • 125.12.ci
  • 125.13.k8s
  • 125.14.k8s
  • 125.15.k8s
  • 125.16.k8s
  • 126.11.react-1
  • 126.12.react-2
  • 126.12.react-3
  • 126.12.react-4
  • 126.12.react-5
  • 126.12.react-6
  • 126.12.react-7
  • 126.12.react-8
  • 127.frontend
  • 128.rollup
  • 129.px2rem-loader
  • 130.health
  • 131.hooks
  • 132.keepalive
  • 133.vue-cli
  • 134.react18
  • 134.2.react18
  • 134.3.react18
  • 135.function
  • 136.toolkit
  • 137.lerna
  • 138.create-vite
  • 139.cli
  • 140.antd
  • 141.react-dnd
  • 142.1.link
  • 143.1.gulp
  • 143.2.stream
  • 143.3.gulp
  • 144.1.closure
  • 144.2.v8
  • 144.3.gc
  • 145.react-router-v6
  • 146.browser
  • 147.lighthouse
  • 148.1.basic
  • 148.2.basic
  • 148.3.basic
  • 148.4.basic
  • 148.5.basic
  • 149.1.vite
  • 149.2.vite
  • 149.3.vite
  • 149.4.vite
  • 150.react-window
  • 151.react-query
  • 152.useRequest
  • 153.transition
  • 154.emotion
  • 155.1.formily
  • 155.2.formily
  • 155.3.formily
  • 155.3.1.mobx.usage
  • 155.3.2.mobx.source
  • 156.vue-loader
  • 103.11.mf
  • 157.1.react18
  • 158.umi4
  • 159.rxjs
  • 159.rxjs2
  • 160.bff
  • 161.zustand
  • 162.vscode
  • 163.emp
  • 164.cors
  • 1.前置知识
    • 1.1. 初始化项目
    • 1.2. magic-string
    • 2.3. AST
      • 2.3.1 AST工作流
      • 2.3.2 acorn
        • 2.3.2.1 walk.js
        • 2.3.2.2 use.js
    • 1.4 作用域
      • 1.4.1 作用域
      • 1.4.2 作用域链
        • 1.4.2.1 scope.js
        • 1.4.2.2 useScope.js
  • 2. 实现rollup
    • 2.1 目录结构
    • 2.2 src\main.js
    • 2.3 debugger.js
    • 2.4 rollup.js
    • 2.5 bundle.js
    • 2.6 module.js
    • 2.7 analyse.js
  • 3. 实现tree-shaking
    • 3.1 基本原理
    • 3.2 基本语句
    • 3.3 main.js
    • 3.4 msg.js
    • 3.5 bundle.js
    • 3.6 utils.js
    • 3.7 module.js
    • 3.8 analyse.js
    • 3.9 scope.js
    • 3.10 lib\ast\walk.js
  • 4.包含修改语句
    • 4.1 main.js
    • 4.2 msg.js
    • 4.3 module.js
    • 4.4 analyse.js
  • 4.支持块级作用域
    • 4.1 src\main.js
    • 5.2 lib\ast\scope.js
    • 5.3 analyse.js
    • 5.4 module.js
  • 6.实现变量名重命名
    • 6.1 src\main.js
    • 6.2 src\age1.js
    • 6.3 src\age2.js
    • 6.4 src\age3.js
    • 6.5 utils.js
    • 6.6 bundle.js
    • 6.7 module.js

1.前置知识 #

1.1. 初始化项目 #

npm install rollup magic-string acorn --save

1.2. magic-string #

  • magic-string是一个操作字符串和生成source-map的工具
var MagicString = require('magic-string');
let sourceCode = `export var name = "zhufeng"`;
let ms = new MagicString(sourceCode);
console.log(ms);
//裁剪出原始字符串开始和结束之间所有的内容
//返回一个克隆后的MagicString的实例
console.log(ms.snip(0, 6).toString());//sourceCode.slice(0,6);
//删除0, 7之间的内容
console.log(ms.remove(0, 7).toString());//sourceCode.slice(7);

//还可以用用来合并代码 //TODO
let bundle = new MagicString.Bundle();
bundle.addSource({
  content: 'var a = 1;',
  separator: '\n'
});
bundle.addSource({
  content: 'var b = 2;',
  separator: '\n'
});
console.log(bundle.toString());

2.3. AST #

  • 通过JavaScript Parser可以把代码转化为一颗抽象语法树AST,这颗树定义了代码的结构,通过操纵这颗树,我们可以精准的定位到声明语句、赋值语句、运算语句等等,实现对代码的分析、优化、变更等操作

868f60df0f5227621151031e261678a9

2.3.1 AST工作流 #

  • Parse(解析) 将源代码转换成抽象语法树,树上有很多的estree节点
  • Transform(转换) 对抽象语法树进行转换
  • Generate(代码生成) 将上一步经过转换过的抽象语法树生成新的代码

1d821a22ff221e924731a6d8c8a654c4

2.3.2 acorn #

  • astexplorer可以把代码转成语法树
  • acorn 解析结果符合The Estree Spec规范

d434186f338a42917a8ef3835df3a28f

2.3.2.1 walk.js #
function walk(astNode, { enter, leave }) {
  visit(astNode, null, enter, leave);
}
function visit(node, parent, enter, leave) {
  if (enter) {
    enter.call(null, node, parent);
  }
  let keys = Object.keys(node).filter(key => typeof node[key] === 'object')
  keys.forEach(key => {
    let value = node[key];
    if (Array.isArray(value)) {
      value.forEach(val => visit(val, node, enter, leave));
    } else if (value && value.type) {
      visit(value, node, enter, leave)
    }
  });
  if (leave) {
    leave.call(null, node, parent);
  }
}


module.exports = walk;
2.3.2.2 use.js #
const acorn = require('acorn');
const walk = require('./walk');
const sourceCode = 'import $ from "jquery"'
const ast = acorn.parse(sourceCode, {
 sourceType: 'module', ecmaVersion: 8
});
let indent = 0;
const padding = () => ' '.repeat(indent)
ast.body.forEach((statement) => {
  walk(statement, {
    enter(node) {
      if (node.type) {
        console.log(padding() + node.type + "进入");
        indent += 2;
      }
    },
    leave(node) {
      if (node.type) {
        indent -= 2;
        console.log(padding() + node.type + "离开");
      }
    }
  });
});

d39f73349c0580b4bfe6aa106ef0b1ae

ImportDeclaration进入
  ImportDefaultSpecifier进入
    Identifier进入
    Identifier离开
  ImportDefaultSpecifier离开
  Literal进入
  Literal离开
ImportDeclaration离开

1.4 作用域 #

1.4.1 作用域 #

  • 在JS中,作用域是用来规定变量访问范围的规则
    function one() {
      var a = 1;
    }
    console.log(a);

1.4.2 作用域链 #

  • 作用域链是由当前执行环境与上层执行环境的一系列变量对象组成的,它保证了当前执行环境对符合访问权限的变量和函数的有序访问
1.4.2.1 scope.js #

scope.js


class Scope {
  constructor(options = {}) {
    //作用域的名称
    this.name = options.name;
    //父作用域
    this.parent = options.parent;
    //此作用域内定义的变量
    this.names = options.names || [];
  }
  add(name) {
    this.names.push(name);
  }
  findDefiningScope(name) {
    if (this.names.includes(name)) {
      return this;
    } else if (this.parent) {
      return this.parent.findDefiningScope(name);
    } else {
      return null;
    }
  }
}
module.exports = Scope;
1.4.2.2 useScope.js #

useScope.js

var a = 1;
function one() {
  var b = 1;
  function two() {
    var c = 2;
     console.log(a, b, c);
  }
}
let Scope = require('./scope');
let globalScope = new Scope({ name: 'global', names: [], parent: null });
let oneScope = new Scope({ name: 'one', names: ['b'], parent: globalScope });
let twoScope = new Scope({ name: 'two', names: ['c'], parent: oneScope });
console.log(
  threeScope.findDefiningScope('a').name,
  threeScope.findDefiningScope('b').name,
  threeScope.findDefiningScope('c').name

2. 实现rollup #

2.1 目录结构 #

  • rollup代码仓库地址
├── package.json
├── README.md
├── src
    ├── ast
    │   ├── analyse.js //分析AST节点的作用域和依赖项
    │   ├── Scope.js //有些语句会创建新的作用域实例
    │   └── walk.js //提供了递归遍历AST语法树的功能
    ├── Bundle//打包工具,在打包的时候会生成一个Bundle实例,并收集其它模块,最后把所有代码打包在一起输出
    │   └── index.js 
    ├── Module//每个文件都是一个模块
    │   └── index.js
    ├── rollup.js //打包的入口模块
    └── utils
        ├── map-helpers.js
        ├── object.js
        └── promise.js

2.2 src\main.js #

src\main.js

console.log('hello');

2.3 debugger.js #

const path = require('path');
const rollup = require('./lib/rollup');
let entry = path.resolve(__dirname, 'src/main.js');
rollup(entry, 'bundle.js');

2.4 rollup.js #

lib\rollup.js

const Bundle = require('./bundle')
function rollup(entry, filename) {
  const bundle = new Bundle({ entry });
  bundle.build(filename);
}
module.exports = rollup;

2.5 bundle.js #

lib\bundle.js

let fs = require('fs');
let path = require('path');
let Module = require('./module');
let MagicString = require('magic-string');
class Bundle {
  constructor(options) {
    //入口文件数据
    this.entryPath = path.resolve(options.entry.replace(/\.js$/, '') + '.js');
    //存放所有的模块
    this.modules = {};
  }
  build(filename) {
    const entryModule = this.fetchModule(this.entryPath);//获取模块代码
    this.statements = entryModule.expandAllStatements(true);//展开所有的语句
    const { code } = this.generate();//生成打包后的代码
    fs.writeFileSync(filename, code);//写入文件系统
  }
  fetchModule(importee) {
    let route = importee;
    if (route) {
      let code = fs.readFileSync(route, 'utf8');
      const module = new Module({
        code,
        path: importee,
        bundle: this
      })
      return module;
    }
  }
  generate() {
    let magicString = new MagicString.Bundle();
    this.statements.forEach(statement => {
      const source = statement._source.clone();
      magicString.addSource({
        content: source,
        separator: '\n'
      })
    })
    return { code: magicString.toString() }
  }
}
module.exports = Bundle;

2.6 module.js #

lib\module.js

const MagicString = require('magic-string');
const { parse } = require('acorn');
let analyse = require('./ast/analyse');
class Module {
  constructor({ code, path, bundle }) {
    this.code = new MagicString(code, { filename: path });
    this.path = path;
    this.bundle = bundle;
    this.ast = parse(code, {
      ecmaVersion: 8,
      sourceType: 'module'
    })
    analyse(this.ast, this.code, this);
  }
  expandAllStatements() {
    let allStatements = [];
    this.ast.body.forEach(statement => {
      let statements = this.expandStatement(statement);
      allStatements.push(...statements);
    });
    return allStatements;
  }
  expandStatement(statement) {
    statement._included = true;
    let result = [];
    result.push(statement);
    return result;
  }
}
module.exports = Module;

2.7 analyse.js #

lib\ast\analyse.js

function analyse(ast, code,module) {
  //给statement定义属性
  ast.body.forEach(statement => {
    Object.defineProperties(statement, {
      _module: { value: module },
      _source: { value: code.snip(statement.start, statement.end) }
    })
  });
}
module.exports = analyse;

3. 实现tree-shaking #

3.1 基本原理 #

  • 第一步
    • 在module里收集imports、exports和definitions
    • 在analyse收集_defines和_dependsOn
  • 第二步
    • 重构expandAllStatements

3.2 基本语句 #

//存放本模块导入了哪些变量
this.imports = {};
// 存放本模块导出了哪些变量
this.exports = {};
//存放本模块的定义变量的语句
this.definitions = {};
//此变量存放所有的变量修改语句,key是变量名,值是一个数组
this.modifications = {};//{name:[name+='jiagou'],age:'age++'}
//记得重命名的变量{key老的变量名:value新的变量名}
this.canonicalNames = {};//{age:'age$1'}
//本模块从哪个模块导入了什么变量,在当前模块内叫什么名字
//this.imports.name = {'./msg','name'};
this.imports[localName] = { source, importName }
//本模块导出了哪个变量,对应哪个本地变量
//this.exports.name = {localName:'name'};
this.exports[exportName] = { localName };
//本顶级语句定义的变量
statement._defines[name] = true;
//定义变量的语句
this.definitions[name] = statement;
//本语句用到的变量
statement._dependsOn[name] = true;
//从模块中获取定义变量的语句
module.define(name);

3.3 main.js #

src\main.js

import { name, age } from './msg';
function say() {
  console.log('hello', name);
}
say();

3.4 msg.js #

src\msg.js

export var name = 'zhufeng';
export var age = 12;

3.5 bundle.js #

lib\bundle.js

let fs = require('fs');
let path = require('path');
let Module = require('./module');
let MagicString = require('magic-string');
class Bundle {
  constructor(options) {
    //入口文件数据
    this.entryPath = path.resolve(options.entry.replace(/\.js$/, '') + '.js');
    //存放所有的模块
    this.modules = {};
  }
  build(filename) {
    const entryModule = this.fetchModule(this.entryPath);//获取模块代码
    this.statements = entryModule.expandAllStatements(true);//展开所有的语句
    const { code } = this.generate();//生成打包后的代码
    fs.writeFileSync(filename, code);//写入文件系统
  }
  fetchModule(importee, importer) {
+   let route;
+   if (!importer) {
+     route = importee;
+   } else {
+     if (path.isAbsolute(importee)) {
+       route = importee;
+     } else {
+       route = path.resolve(path.dirname(importer), importee.replace(/\.js$/, '') + '.js');
+     }
+   }
    if (route) {
      let code = fs.readFileSync(route, 'utf8');
      const module = new Module({
        code,
        path: importee,
        bundle: this
      })
      return module;
    }
  }
  generate() {
    let magicString = new MagicString.Bundle();
    this.statements.forEach(statement => {
      const source = statement._source.clone();
+     if (statement.type === 'ExportNamedDeclaration') {
+       source.remove(statement.start, statement.declaration.start);
+     }
      magicString.addSource({
        content: source,
        separator: '\n'
      })
    })
    return { code: magicString.toString() }
  }
}
module.exports = Bundle;

3.6 utils.js #

lib\utils.js

function hasOwnProperty(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop)
}
exports.hasOwnProperty = hasOwnProperty;

3.7 module.js #

lib\module.js

const MagicString = require('magic-string');
const { parse } = require('acorn');
+const { hasOwnProperty } = require('./utils');
const analyse = require('./ast/analyse');
class Module {
  constructor({ code, path, bundle }) {
    this.code = new MagicString(code, { filename: path });
    this.path = path;
    this.bundle = bundle;
    this.ast = parse(code, {
      ecmaVersion: 8,
      sourceType: 'module'
    })
+   //存放本模块的导入信息
+   this.imports = {};
+   //本模块的导出信息
+   this.exports = {};
+   //存放本模块的定义变量的语句 a=>var a = 1;b =var b =2;
+   this.definitions = {};
    analyse(this.ast, this.code, this);
  }
  expandAllStatements() {
    let allStatements = [];
    this.ast.body.forEach(statement => {
+     //导入的语句默认全部过滤掉 
+     if (statement.type === 'ImportDeclaration') return;
      let statements = this.expandStatement(statement);
      allStatements.push(...statements);
    });
    return allStatements;
  }
  expandStatement(statement) {
    statement._included = true;
    let result = [];
+   //获取此语句依赖的变量
+   let _dependsOn = Object.keys(statement._dependsOn);
+   _dependsOn.forEach(name => {
+     //找到此变量定义的语句,添加到输出数组里
+     let definitions = this.define(name);
+     result.push(...definitions);
+   });
    //把此语句添加到输出列表中
    result.push(statement);
    return result;
  }
+ define(name) {
+   //先判断此变量是外部导入的还是模块内声明的 
+   if (hasOwnProperty(this.imports, name)) {
+     //说明此变量不是模块内声明的,而是外部导入的,获取从哪个模块内导入了哪个变量
+     const { source, importName } = this.imports[name];
+     //获取这个模块
+     const importModule = this.bundle.fetchModule(source, this.path);
+     //从这个模块的导出变量量获得本地变量的名称
+     const { localName } = importModule.exports[importName];
+     //获取本地变量的定义语句
+     return importModule.define(localName);//name
+   } else {//如果是模块的变量的话
+     let statement = this.definitions[name];//name
+     if (statement && !statement._included) {
+       //如果本地变量的话还需要继续展开
+       return this.expandStatement(statement);
+     } else {
+       return []
+     }
+   }
+ }
}
module.exports = Module;

3.8 analyse.js #

  • 第1个循环 找出导入导出的变量
  • 第2个循环 找出定义和依赖的变量

lib\ast\analyse.js

+const Scope = require('./scope');
+const walk = require('./walk');
+const { hasOwnProperty } = require('../utils');
+function analyse(ast, code, module) {
+  //第1个循环,找出导入导出的变量
+  ast.body.forEach(statement => {
+    Object.defineProperties(statement, {
       _module: { value: module }
       _source: { value: code.snip(statement.start, statement.end) },
+      _defines: { value: {} },//此节点上定义的变量say
+      _dependsOn: { value: {} }//此此节点读取了哪些变量
+    })
+    //import { name, age } from './msg';
+    if (statement.type === 'ImportDeclaration') {
+      let source = statement.source.value;// ./msg
+      statement.specifiers.forEach(specifier => {
+        let importName = specifier.imported.name;//导入的变量名
+        let localName = specifier.local.name;//本地的变量名
+        //imports.name = {source:'./msg',importName:'name'};
+        module.imports[localName] = { source, importName }
+      });
+    } else if (statement.type === 'ExportNamedDeclaration') {
+      const declaration = statement.declaration;
+      if (declaration && declaration.type === 'VariableDeclaration') {
+        const declarations = declaration.declarations;
+        declarations.forEach(variableDeclarator => {
+          const localName = variableDeclarator.id.name;//name
+          const exportName = localName;
+          //exports.name = {localName:'name'};
+          module.exports[exportName] = { localName };
+        });
+      }
+    }
+  });
+  //第2次循环创建作用域链
+  let currentScope = new Scope({ name: '全局作用域' });
+  //创建作用域链,为了知道我在此模块中声明哪些变量,这些变量的声明节点是哪个 var name = 1;
+  ast.body.forEach(statement => {
+    function addToScope(name) {
+      currentScope.add(name);//把name变量放入当前的作用域
+      //如果没有父亲,相当 于就是根作用域或者 当前的作用域是一个块级作用域的话
+      if (!currentScope.parent) {//如果没有父作用域,说明这是一个顶级作用域
+        statement._defines[name] = true;//在一级节点定义一个变量name _defines.say=true
+        module.definitions[name] = statement;
+      }
+    }
+    walk(statement, {
+      enter(node) {
+        //收集本节点上使用的变量
+        if (node.type === 'Identifier') {
+          statement._dependsOn[node.name] = true;
+        }
+        let newScope;
+        switch (node.type) {
+          case 'FunctionDeclaration':
+            addToScope(node.id.name);//say
+            const names = node.params.map(param => param.name);
+            newScope = new Scope({ name: node.id.name, parent: currentScope, names });
+            break;
+          case 'VariableDeclaration':
+            node.declarations.forEach(declaration => {
+              addToScope(declaration.id.name);//var
+            });
+            break;
+          default:
+            break;
+        }
+        if (newScope) {
+          Object.defineProperty(node, '_scope', { value: newScope });
+          currentScope = newScope;
+        }
+      },
+      leave(node) {
+        if (hasOwnProperty(node, '_scope')) {
+          currentScope = currentScope.parent;
+        }
+      }
+    });
+  });
+}
module.exports = analyse;

3.9 scope.js #

lib\ast\scope.js

class Scope {
  constructor(options = {}) {
    //作用域的名称
    this.name = options.name;
    //父作用域
    this.parent = options.parent;
    //此作用域内定义的变量
    this.names = options.names || [];
  }
  add(name) {
    this.names.push(name);
  }
  findDefiningScope(name) {
    if (this.names.includes(name)) {
      return this;
    } else if (this.parent) {
      return this.parent.findDefiningScope(name);
    } else {
      return null;
    }
  }
}
module.exports = Scope;

3.10 lib\ast\walk.js #

lib\ast\walk.js

function walk(astNode, { enter, leave }) {
  visit(astNode, null, enter, leave);
}
function visit(node, parent, enter, leave) {
  if (enter) {
    enter.call(null, node, parent);
  }
  let keys = Object.keys(node).filter(key => typeof node[key] === 'object')
  keys.forEach(key => {
    let value = node[key];
    if (Array.isArray(value)) {
      value.forEach(val => visit(val, node, enter, leave));
    } else if (value && value.type) {
      visit(value, node, enter, leave)
    }
  });
  if (leave) {
    leave.call(null, node, parent);
  }
}
module.exports = walk;

4.包含修改语句 #

4.1 main.js #

src\main.js

import { name, age } from './msg';
console.log(name);

4.2 msg.js #

src\msg.js

export var name = 'zhufeng';
name += 'jiagou';
export var age = 12;

4.3 module.js #

lib\module.js

const MagicString = require('magic-string');
const { parse } = require('acorn');
const { hasOwnProperty } = require('./utils');
let analyse = require('./ast/analyse');
class Module {
  constructor({ code, path, bundle }) {
    this.code = new MagicString(code, { filename: path });
    this.path = path;
    this.bundle = bundle;
    this.ast = parse(code, {
      ecmaVersion: 8,
      sourceType: 'module'
    })
    //存放本模块的导入信息
    this.imports = {};
    //本模块的导出信息
    this.exports = {};
    //存放本模块的定义变量的语句 a=>var a = 1;b =var b =2;
    this.definitions = {};
    //存放变量修改语句
+   this.modifications = {};
    analyse(this.ast, this.code, this);
  }
  expandAllStatements() {
    let allStatements = [];
    this.ast.body.forEach(statement => {
      if (statement.type === 'ImportDeclaration') return;
      let statements = this.expandStatement(statement);
      allStatements.push(...statements);
    });
    return allStatements;
  }
  expandStatement(statement) {
    statement._included = true;
    let result = [];
    //获取此语句依赖的变量
    let _dependsOn = Object.keys(statement._dependsOn);
    _dependsOn.forEach(name => {
      //找到此变量定义的语句,添加到输出数组里
      let definitions = this.define(name);
      result.push(...definitions);
    });
    //把此语句添加到输出列表中
    result.push(statement);
+   //找到此语句定义的变量,把定义的变量和修改语句也包括进来
+   //注意要先定义再修改,所以要把这行放在push(statement)的下面
+   const defines = Object.keys(statement._defines);
+   defines.forEach(name => {
+     //找到定义的变量依赖的修改的语句
+     const modifications = hasOwnProperty(this.modifications, name) && this.modifications[name];
+     if (modifications) {
+       //把修改语句也展开放到结果里
+       modifications.forEach(statement => {
+         if (!statement._included) {
+           let statements = this.expandStatement(statement);
+           result.push(...statements);
+         }
+       });
+     }
+   });
    return result;
  }
  define(name) {
    //先判断此变量是外部导入的还是模块内声明的 
    if (hasOwnProperty(this.imports, name)) {
      //说明此变量不是模块内声明的,而是外部导入的,获取从哪个模块内导入了哪个变量
      const { source, importName } = this.imports[name];
      //获取这个模块
      const importModule = this.bundle.fetchModule(source, this.path);
      //从这个模块的导出变量量获得本地变量的名称
      const { localName } = importModule.exports[importName];
      //获取本地变量的定义语句
      return importModule.define(localName);//name
    } else {//如果是模块的变量的话
      let statement = this.definitions[name];//name
      if (statement && !statement._included) {
        //如果本地变量的话还需要继续展开
        return this.expandStatement(statement);
      } else {
        return []
      }
    }
  }
}
module.exports = Module;

4.4 analyse.js #

lib\ast\analyse.js

const Scope = require('./scope');
const walk = require('./walk');
const { hasOwnProperty } = require('../utils');
function analyse(ast, code, module) {
  //第1个循环,找出导入导出的变量
  ast.body.forEach(statement => {
    Object.defineProperties(statement, {
      _module: { value: module },
      _source: { value: code.snip(statement.start, statement.end) },
      _defines: { value: {} },//此节点上定义的变量say
      _dependsOn: { value: {} },//此此节点读取了哪些变量
+     _modifies: { value: {} },//本语句修改的变量
    })
    //import { name, age } from './msg';
    if (statement.type === 'ImportDeclaration') {
      let source = statement.source.value;// ./msg
      statement.specifiers.forEach(specifier => {
        let importName = specifier.imported.name;//导入的变量名
        let localName = specifier.local.name;//本地的变量名
        //imports.name = {source:'./msg',importName:'name'};
        module.imports[localName] = { source, importName }
      });
    } else if (statement.type === 'ExportNamedDeclaration') {
      const declaration = statement.declaration;
      if (declaration && declaration.type === 'VariableDeclaration') {
        const declarations = declaration.declarations;
        declarations.forEach(variableDeclarator => {
          const localName = variableDeclarator.id.name;//name
          const exportName = localName;
          //exports.name = {localName:'name'};
          module.exports[exportName] = { localName };
        });
      }
    }
  });
  //第2次循环创建作用域链
  let currentScope = new Scope({ name: '全局作用域' });
  //创建作用域链,为了知道我在此模块中声明哪些变量,这些变量的声明节点是哪个 var name = 1;
  ast.body.forEach(statement => {
+   function checkForReads(node) {
+     //如果此节点类型是一个标识符话
+     if (node.type === 'Identifier') {
+       statement._dependsOn[node.name] = true;
+     }
+   }
+   function checkForWrites(node) {
+     function addNode(node) {
+       const name = node.name;
+       statement._modifies[name] = true;//statement._modifies.age = true;
+       if (!hasOwnProperty(module.modifications, name)) {
+         module.modifications[name] = [];
+       }
+       module.modifications[name].push(statement);
+     }
+     if (node.type === 'AssignmentExpression') {
+       addNode(node.left, true);
+     } else if (node.type === 'UpdateExpression') {
+       addNode(node.argument);
+     }
+   }
    function addToScope(name) {
      currentScope.add(name);//把name变量放入当前的作用域
      //如果没有父亲,相当 于就是根作用域或者 当前的作用域是一个块级作用域的话
      if (!currentScope.parent) {//如果没有父作用域,说明这是一个顶级作用域
        statement._defines[name] = true;//在一级节点定义一个变量name _defines.say=true
        module.definitions[name] = statement;
      }
    }
    walk(statement, {
      enter(node) {
-        if (node.type === 'Identifier') {
-          statement._dependsOn[node.name] = true;
-        }
+       //收集本节点上使用的变量
+       checkForReads(node);
+       checkForWrites(node);
        let newScope;
        switch (node.type) {
          case 'FunctionDeclaration':
            addToScope(node.id.name);//say
            const names = node.params.map(param => param.name);
            newScope = new Scope({ name: node.id.name, parent: currentScope, names });
            break;
          case 'VariableDeclaration':
            node.declarations.forEach(declaration => {
              addToScope(declaration.id.name);//var
            });
            break;
          default:
            break;
        }
        if (newScope) {
          Object.defineProperty(node, '_scope', { value: newScope });
          currentScope = newScope;
        }
      },
      leave(node) {
        if (hasOwnProperty(node, '_scope')) {
          currentScope = currentScope.parent;
        }
      }
    });
  });
}
module.exports = analyse;

4.支持块级作用域 #

4.1 src\main.js #

src\main.js

var name = 'zhufeng';
if (true) {
  var age = 12;
}
console.log(age);

5.2 lib\ast\scope.js #

lib\ast\scope.js

class Scope {
  constructor(options = {}) {
    //作用域的名称
    this.name = options.name;
    //父作用域
    this.parent = options.parent;
    //此作用域内定义的变量
    this.names = options.names || [];
    // 是否块作用域
+   this.isBlock = !!options.isBlock
  }
+ add(name, isBlockDeclaration) {
+   if (!isBlockDeclaration && this.isBlock) {
      //这是一个var或者函数声明,并且这是一个块级作用域,所以我们需要向上提升
+     this.parent.add(name, isBlockDeclaration)
    } else {
      this.names.push(name);
    }
  }
  findDefiningScope(name) {
    if (this.names.includes(name)) {
      return this;
    } else if (this.parent) {
      return this.parent.findDefiningScope(name);
    } else {
      return null;
    }
  }
}
module.exports = Scope;

5.3 analyse.js #

lib\ast\analyse.js

const Scope = require('./scope');
const walk = require('./walk');
const { hasOwnProperty } = require('../utils');
function analyse(ast, code, module) {
  //第1个循环,找出导入导出的变量
  ast.body.forEach(statement => {
    Object.defineProperties(statement, {
      _module: { value: module },
      _source: { value: code.snip(statement.start, statement.end) },
      _defines: { value: {} },//此节点上定义的变量say
      _dependsOn: { value: {} },//此此节点读取了哪些变量
      _modifies: { value: {} },//本语句修改的变量
    })
    //import { name, age } from './msg';
    if (statement.type === 'ImportDeclaration') {
      let source = statement.source.value;// ./msg
      statement.specifiers.forEach(specifier => {
        let importName = specifier.imported.name;//导入的变量名
        let localName = specifier.local.name;//本地的变量名
        //imports.name = {source:'./msg',importName:'name'};
        module.imports[localName] = { source, importName }
      });
    } else if (statement.type === 'ExportNamedDeclaration') {
      const declaration = statement.declaration;
      if (declaration && declaration.type === 'VariableDeclaration') {
        const declarations = declaration.declarations;
        declarations.forEach(variableDeclarator => {
          const localName = variableDeclarator.id.name;//name
          const exportName = localName;
          //exports.name = {localName:'name'};
          module.exports[exportName] = { localName };
        });
      }
    }
  });
  //第2次循环创建作用域链
  let currentScope = new Scope({ name: '全局作用域' });
  //创建作用域链,为了知道我在此模块中声明哪些变量,这些变量的声明节点是哪个 var name = 1;
  ast.body.forEach(statement => {
    function checkForReads(node) {
      //如果此节点类型是一个标识符话
      if (node.type === 'Identifier') {
        statement._dependsOn[node.name] = true;
      }
    }
    function checkForWrites(node) {
      function addNode(node) {
        const name = node.name;
        statement._modifies[name] = true;//statement._modifies.age = true;
        if (!hasOwnProperty(module.modifications, name)) {
          module.modifications[name] = [];
        }
        module.modifications[name].push(statement);
      }
      if (node.type === 'AssignmentExpression') {
        addNode(node.left, true);
      } else if (node.type === 'UpdateExpression') {
        addNode(node.argument);
      }
    }
+   function addToScope(name, isBlockDeclaration) {
+     currentScope.add(name, isBlockDeclaration);//把name变量放入当前的作用域
      //如果没有父亲,相当 于就是根作用域或者 当前的作用域是一个块级作用域的话
+     if (!currentScope.parent || (currentScope.isBlock && !isBlockDeclaration)) {//如果没有父作用域,说明这是一个顶级作用域
        statement._defines[name] = true;//在一级节点定义一个变量name _defines.say=true
        module.definitions[name] = statement;
      }
    }
    walk(statement, {
      enter(node) {
        //收集本节点上使用的变量
        checkForReads(node);
        checkForWrites(node);
        let newScope;
        switch (node.type) {
          case 'FunctionDeclaration':
            addToScope(node.id.name);//say
            const names = node.params.map(param => param.name);
+           newScope = new Scope({ name: node.id.name, parent: currentScope, names, isBlock: false });
            break;
          case 'VariableDeclaration':
            node.declarations.forEach(declaration => {
+             if (node.kind === 'let' || node.kind === 'const') {
+               addToScope(declaration.id.name, true);//这是是一个块级变量
+             } else {
+               addToScope(declaration.id.name);//var
+             }
            });
            break;
+         case 'BlockStatement':
+           newScope = new Scope({ parent: currentScope, isBlock: true });
+           break;
          default:
            break;
        }
        if (newScope) {
          Object.defineProperty(node, '_scope', { value: newScope });
          currentScope = newScope;
        }
      },
      leave(node) {
        if (hasOwnProperty(node, '_scope')) {
          currentScope = currentScope.parent;
        }
      }
    });
  });

}
module.exports = analyse;

5.4 module.js #

lib\module.js

const MagicString = require('magic-string');
const { parse } = require('acorn');
const { hasOwnProperty } = require('./utils');
const analyse = require('./ast/analyse');
+const SYSTEMS = ['console', 'log'];
class Module {
  constructor({ code, path, bundle }) {
    this.code = new MagicString(code, { filename: path });
    this.path = path;
    this.bundle = bundle;
    this.ast = parse(code, {
      ecmaVersion: 8,
      sourceType: 'module'
    })
    //存放本模块的导入信息
    this.imports = {};
    //本模块的导出信息
    this.exports = {};
    //存放本模块的定义变量的语句 a=>var a = 1;b =var b =2;
    this.definitions = {};
    //存放变量修改语句
    this.modifications = {};
    analyse(this.ast, this.code, this);
  }
  expandAllStatements() {
    let allStatements = [];
    this.ast.body.forEach(statement => {
      if (statement.type === 'ImportDeclaration') return;
+     //默认不包含所有的变量声明语句
+     if (statement.type === 'VariableDeclaration') return;
      let statements = this.expandStatement(statement);
      allStatements.push(...statements);
    });
    return allStatements;
  }
  expandStatement(statement) {
    statement._included = true;
    let result = [];
    //获取此语句依赖的变量
    let _dependsOn = Object.keys(statement._dependsOn);
    _dependsOn.forEach(name => {
      //找到此变量定义的语句,添加到输出数组里
      let definitions = this.define(name);
      result.push(...definitions);
    });
    //把此语句添加到输出列表中
    result.push(statement);
    //找到此语句定义的变量,把定义的变量和修改语句也包括进来
    //注意要先定义再修改,所以要把这行放在push(statement)的下面
    const defines = Object.keys(statement._defines);
    defines.forEach(name => {
      //找到定义的变量依赖的修改的语句
      const modifications = hasOwnProperty(this.modifications, name) && this.modifications[name];
      if (modifications) {
        //把修改语句也展开放到结果里
        modifications.forEach(statement => {
          if (!statement._included) {
            let statements = this.expandStatement(statement);
            result.push(...statements);
          }
        });
      }
    });
    return result;
  }
  define(name) {
    //先判断此变量是外部导入的还是模块内声明的 
    if (hasOwnProperty(this.imports, name)) {
      //说明此变量不是模块内声明的,而是外部导入的,获取从哪个模块内导入了哪个变量
      const { source, importName } = this.imports[name];
      //获取这个模块
      const importModule = this.bundle.fetchModule(source, this.path);
      //从这个模块的导出变量量获得本地变量的名称
      const { localName } = importModule.exports[importName];
      //获取本地变量的定义语句
      return importModule.define(localName);//name
    } else {//如果是模块的变量的话
      let statement = this.definitions[name];//name
+     if (statement) {
+       if (statement._included) {
+         return [];
+       } else {
+         return this.expandStatement(statement);
+       }
+     } else {  
+       if (SYSTEMS.includes(name)) {
+         return [];
+       } else {  //如果找不到定义的变量就报错
+         throw new Error(`变量${name}既没有从外部导入,也没有在当前的模块声明`);
+       }
      }
    }
  }
}
module.exports = Module;

6.实现变量名重命名 #

6.1 src\main.js #

src\main.js

import { age1 } from './age1.js';
import { age2 } from './age2.js';
import { age3 } from './age3.js';
console.log(age1, age2, age3);

/**
const age$2 = '年龄';
const age1 = age$2 + '1';

const age$1 = '年龄';
const age2 = age$1 + '2';

const age = '年龄';
const age3 = age + '3';

console.log(age1, age2, age3);
 */

6.2 src\age1.js #

src\age1.js

const age = '年龄';
export const age1 = age + '1';

6.3 src\age2.js #

src\age2.js

const age = '年龄';
export const age2 = age + '2';

6.4 src\age3.js #

src\age3.js

const age = '年龄';
export const age3 = age + '3';

6.5 utils.js #

lib\utils.js

+const walk = require('./ast/walk');
function hasOwnProperty(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop)
}
exports.hasOwnProperty = hasOwnProperty;
+function replaceIdentifiers(statement, source, replacements) {
+  walk(statement, {
+    enter(node) {
+      if (node.type === 'Identifier') {
+        if (node.name && replacements[node.name]) {
+          source.overwrite(node.start, node.end, replacements[node.name]);
+        }
+      }
+    }
+  })
+}
+exports.replaceIdentifiers = replaceIdentifiers;

6.6 bundle.js #

lib\bundle.js

let fs = require('fs');
let path = require('path');
let Module = require('./module');
let MagicString = require('magic-string');
+const { hasOwnProperty, replaceIdentifiers } = require('./utils');
class Bundle {
  constructor(options) {
    //入口文件数据
    this.entryPath = path.resolve(options.entry.replace(/\.js$/, '') + '.js');
    //存放所有的模块
    this.modules = {};
  }
  build(filename) {
    const entryModule = this.fetchModule(this.entryPath);//获取模块代码
    this.statements = entryModule.expandAllStatements(true);//展开所有的语句
+   this.deconflict();
    const { code } = this.generate();//生成打包后的代码
    fs.writeFileSync(filename, code);//写入文件系统
    console.log('done');
  }
+ deconflict() {
+   const defines = {};//定义的变量
+   const conflicts = {};//变量名冲突的变量
+   this.statements.forEach(statement => {
+     Object.keys(statement._defines).forEach(name => {
+       if (hasOwnProperty(defines, name)) {
+         conflicts[name] = true;
+       } else {
+         defines[name] = [];//defines.age = [];
+       }
+       //把此声明变量的语句,对应的模块添加到数组里
+       defines[name].push(statement._module);
+     });
+   });
+   Object.keys(conflicts).forEach(name => {
+     const modules = defines[name];//获取定义此变量名的模块的数组
+     modules.pop();//最后一个模块不需要重命名,保留 原来的名称即可 [age1,age2]
+     modules.forEach((module, index) => {
+       let replacement = `${name}$${modules.length - index}`;
+       debugger
+       module.rename(name, replacement);//module age=>age$2
+     });
+   });
+ }
  fetchModule(importee, importer) {
    let route;
    if (!importer) {
      route = importee;
    } else {
      if (path.isAbsolute(importee)) {
        route = importee;
      } else {
        route = path.resolve(path.dirname(importer), importee.replace(/\.js$/, '') + '.js');
      }
    }
    if (route) {
      let code = fs.readFileSync(route, 'utf8');
      const module = new Module({
        code,
        path: importee,
        bundle: this
      })
      return module;
    }
  }
  generate() {
    let magicString = new MagicString.Bundle();
    this.statements.forEach(statement => {
+     let replacements = {};
+     Object.keys(statement._dependsOn)
+       .concat(Object.keys(statement._defines))
+       .forEach(name => {
+         const canonicalName = statement._module.getCanonicalName(name);
+         if (name !== canonicalName)
+           replacements[name] = canonicalName;
+       });
      const source = statement._source.clone();
      if (statement.type === 'ExportNamedDeclaration') {
        source.remove(statement.start, statement.declaration.start);
      }
+     replaceIdentifiers(statement, source, replacements);
      magicString.addSource({
        content: source,
        separator: '\n'
      })
    })
    return { code: magicString.toString() }
  }
}
module.exports = Bundle;

6.7 module.js #

lib\module.js

const MagicString = require('magic-string');
const { parse } = require('acorn');
const { hasOwnProperty } = require('./utils');
const analyse = require('./ast/analyse');
const SYSTEMS = ['console', 'log'];
class Module {
  constructor({ code, path, bundle }) {
    this.code = new MagicString(code, { filename: path });
    this.path = path;
    this.bundle = bundle;
    this.ast = parse(code, {
      ecmaVersion: 8,
      sourceType: 'module'
    })
    //存放本模块的导入信息
    this.imports = {};
    //本模块的导出信息
    this.exports = {};
    //存放本模块的定义变量的语句 a=>var a = 1;b =var b =2;
    this.definitions = {};
    //存放变量修改语句
    this.modifications = {};
+   this.canonicalNames = {};
    analyse(this.ast, this.code, this);
  }
  expandAllStatements() {
    let allStatements = [];
    this.ast.body.forEach(statement => {
      if (statement.type === 'ImportDeclaration') return;
      if (statement.type === 'VariableDeclaration') return;
      let statements = this.expandStatement(statement);
      allStatements.push(...statements);
    });
    return allStatements;
  }
  expandStatement(statement) {
    statement._included = true;
    let result = [];
    //获取此语句依赖的变量
    let _dependsOn = Object.keys(statement._dependsOn);
    _dependsOn.forEach(name => {
      //找到此变量定义的语句,添加到输出数组里
      let definitions = this.define(name);
      result.push(...definitions);
    });
    //把此语句添加到输出列表中
    result.push(statement);
    //找到此语句定义的变量,把定义的变量和修改语句也包括进来
    //注意要先定义再修改,所以要把这行放在push(statement)的下面
    const defines = Object.keys(statement._defines);
    defines.forEach(name => {
      //找到定义的变量依赖的修改的语句
      const modifications = hasOwnProperty(this.modifications, name) && this.modifications[name];
      if (modifications) {
        //把修改语句也展开放到结果里
        modifications.forEach(statement => {
          if (!statement._included) {
            let statements = this.expandStatement(statement);
            result.push(...statements);
          }
        });
      }
    });
    return result;
  }
  define(name) {
    //先判断此变量是外部导入的还是模块内声明的 
    if (hasOwnProperty(this.imports, name)) {
      //说明此变量不是模块内声明的,而是外部导入的,获取从哪个模块内导入了哪个变量
      const { source, importName } = this.imports[name];
      //获取这个模块
      const importModule = this.bundle.fetchModule(source, this.path);
      //从这个模块的导出变量量获得本地变量的名称
      const { localName } = importModule.exports[importName];
      //获取本地变量的定义语句
      return importModule.define(localName);//name
    } else {//如果是模块的变量的话
      let statement = this.definitions[name];//name
      if (statement) {
        if (statement._included) {
          return [];
        } else {
          return this.expandStatement(statement);
        }
      } else {
        if (SYSTEMS.includes(name)) {
          return [];
        } else {
          throw new Error(`变量${name}既没有从外部导入,也没有在当前的模块声明`);
        }
      }
    }
  }
+ rename(name, replacement) {
+   this.canonicalNames[name] = replacement;
+ }
+ getCanonicalName(name) {
+   return this.canonicalNames[name] || name;
+ }
}
module.exports = Module;

访问验证

请输入访问令牌

Token不正确,请重新输入