webpack构建流程解析

114 阅读1分钟

解析webpack核心代码:

1,getModuleInfo函数获取模块的信息,包括文件相对路径,依赖,和模块代码

  • @babel/parser将模块转化为ast结构语法树。
  • @babel/traverse解析ast,获取所有的依赖
  • @babel/core用于将代码中的es6语法转化为浏览器能够解析的es5语法

2,parseModules通过递归获取所有依赖模块的信息,保存在depsGraph数组中
3,bundle通过depsGraph的信息循环代码,通过require递归,将所有依赖的代码整合,最终输出一个自执行函数字符串

4,最后使用fs将最后打包数据输出到打包文件。

const fs = require('fs');
const path = require('path');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const babel = require('@babel/core');
const getModuleInfo = (file) => {
  const body = fs.readFileSync(file, 'utf-8');
  //使用@bab1el/parser将文件转化为ast
  const ast = parser.parse(body, {
    sourceType: 'module', //表示我们要解析的是ES模块
  });

  const deps = {};
  //遍历ast,获得所有的依赖
  traverse(ast, {
    ImportDeclaration({ node }) {
      const dirname = path.dirname(file);
      const abspath = './' + path.join(dirname, node.source.value);
      deps[node.source.value] = abspath;
    },
  });
  //将es6语法转为es5
  const { code } = babel.transformFromAst(ast, null, {
    presets: ['@babel/preset-env'],
  });

  const moduleInfo = { file, deps, code };
  return moduleInfo;
};

const parseModules = (file) => {
  const entry = getModuleInfo(file);
  const temp = [entry];
  const depsGraph = {};
  for (let i = 0; i < temp.length; i++) {
    const deps = temp[i].deps;
    if (deps) {
      for (const key in deps) {
        if (deps.hasOwnProperty(key)) {
          temp.push(getModuleInfo(deps[key]));
        }
      }
    }
  }
  temp.forEach((moduleInfo) => {
    depsGraph[moduleInfo.file] = {
      deps: moduleInfo.deps,
      code: moduleInfo.code,
    };
  });
  return depsGraph;
};
const bundle = (file) => {
  const depsGraph = JSON.stringify(parseModules(file));
  return `(function (graph) {
          function require(file) {
              function absRequire(relPath) {
                  return require(graph[file].deps[relPath])
              }
              var exports = {};
              (function (require,exports,code) {
                  eval(code)
              })(absRequire,exports,graph[file].code)
              return exports
          }
          require('${file}')
      })(${depsGraph})`;
};
const content = bundle('./src/index.js');

// console.log(content);

// 写入到我们的dist目录下;
// fs.mkdirSync('./dist');
// fs.writeFileSync('./dist/bundle.js', content);