# 四.build

# lib/Complier.js

let fs = require("fs")
let path = require("path")
let babylon = require("babylon")
let traverse = require("@babel/traverse").default
let t = require("@babel/types")
let generator = require("@babel/generator").default
let ejs = require("ejs")
//babylon 主要把源码转换成ast
//@babel/traverse
//@babel/types
//@babel/generator
class Complier {
  constructor(config) {
    //entry output
    this.config = config
    //需要保存入口文件的路径
    this.entryId
    //需要保存所有的模块依赖
    this.modules = {}
    this.entry = config.entry
    this.root = process.cwd()
  }
  getSource(modulePath) {
    let content = fs.readFileSync(modulePath, "utf8")
    return content
  }
  //解析源码 AST解析语法树
  parse(source, parentPath) {
    let ast = babylon.parse(source)
    let dependencies = []
    traverse(ast, {
      CallExpression(p) {
        let node = p.node
        if (node.callee.name === "require") {
          node.callee.name = "__webpack_require__"
          let moduleName = node.arguments[0].value
          moduleName = moduleName + (path.extname(moduleName) ? "" : ".js")

          moduleName = "./" + path.join(parentPath, moduleName)
          ;("src/a.js")
          dependencies.push(moduleName)
          node.arguments = [t.stringLiteral(moduleName)]
        }
      },
    })
    let sourceCode = generator(ast).code
    return { sourceCode, dependencies }
  }
  buildModule(modulePath, isEntry) {
    let source = this.getSource(modulePath)
    //模块id modulePath = modulePath-this.root
    let moduleName = "./" + path.relative(this.root, modulePath)
    if (isEntry) {
      this.entryId = moduleName
    }
    //解析需要把source源码进行改造 返回一个依赖列表
    let { sourceCode, dependencies } = this.parse(
      source,
      path.dirname(moduleName)
    )
    //把相对路径和模块中的内容 对应起来
    this.modules[moduleName] = sourceCode

    dependencies.forEach((dep) => {
      this.buildModule(path.join(this.root, dep), false)
    })
  }
  emitFile() {
    //发射文件
    //用数据 渲染我们的
    // 拿到输出到那个目录下 输出路径
    let main = path.join(this.config.output.path, this.config.output.filename)

    //模板路径
    let templateStr = this.getSource(path.join(__dirname, "main.ejs"))
    let code = ejs.render(templateStr, {
      entryId: this.entryId,
      modules: this.modules,
    })
    this.assets = {}
    //资源中 路径对应的代码
    this.assets[main] = code
    fs.writeFileSync(main, this.assets[main])
  }
  run() {
    //执行
    this.buildModule(path.resolve(this.root, this.entry), true)
    console.log(this.modules, this.entryId)
    this.emitFile()
  }
}
module.exports = Complier
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

# lib/main.ejs

(function(modules) {
  var installedModules = {};

  function __webpack_require__(moduleId) {
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    var module = (installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    });

    modules[moduleId].call(
      module.exports,
      module,
      module.exports,
      __webpack_require__
    );

    module.l = true;

    return module.exports;
  }

  __webpack_require__.m = modules;

  __webpack_require__.c = installedModules;

  __webpack_require__.d = function(exports, name, getter) {
    if (!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, { enumerable: true, get: getter });
    }
  };

  __webpack_require__.r = function(exports) {
    if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
      Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
    }
    Object.defineProperty(exports, "__esModule", { value: true });
  };

  __webpack_require__.t = function(value, mode) {
    if (mode & 1) value = __webpack_require__(value);
    if (mode & 8) return value;
    if (mode & 4 && typeof value === "object" && value && value.__esModule)
      return value;
    var ns = Object.create(null);
    __webpack_require__.r(ns);
    Object.defineProperty(ns, "default", { enumerable: true, value: value });
    if (mode & 2 && typeof value != "string")
      for (var key in value)
        __webpack_require__.d(
          ns,
          key,
          function(key) {
            return value[key];
          }.bind(null, key)
        );
    return ns;
  };

  __webpack_require__.n = function(module) {
    var getter =
      module && module.__esModule
        ? function getDefault() {
            return module["default"];
          }
        : function getModuleExports() {
            return module;
          };
    __webpack_require__.d(getter, "a", getter);
    return getter;
  };

  __webpack_require__.o = function(object, property) {
    return Object.prototype.hasOwnProperty.call(object, property);
  };
  __webpack_require__.p = "";
  return __webpack_require__(__webpack_require__.s = "<%-entryId%>");
})
({
    <%for(let key in modules){%>
        "<%-key%>": (function(module, exports, __webpack_require__) {
            eval(`<%-modules[key]%>`);
        }),
    <%}%>
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

# 运行

npx mypack
1

得到:ab

包完整代码 (opens new window)