webpack原理

24 阅读2分钟
//1.定义入口函数将 入口文件中涉及的依赖全部进
//2.根据加载的文件内容 定义上下文
var fs = require('fs')
var parser =  require('@babel/parser')
var traverse = require('@babel/traverse').default
const path = require('path');
var babel = require('@babel/core')
//加载模块的方法
/**
 * @param {string} fileUrl 文件路径
 * 
 * 
*/
function ModelInfo(fileUrl){
   //根据文件路径读取文件内容
   var content = fs.readFileSync(fileUrl,'utf-8');

   //将文件的内容通过babel 转成ast树
   var ast = parser.parse(content,{
      sourceType:'module' //已es6模式进行解析文件
   })
   var deps = {}
   //提取特定类型的ast 
   traverse(ast,{
      ImportDeclaration({node}){
    var dirname =  path.dirname(fileUrl) //获取入口文件的路径


    //合并路径
    var  darPath =  './'+path.join(dirname,node.source.value)

       //生成一个路径名称和地址对应的map结构方法查找资源。
       deps[node.source.value] = darPath.replace('\\','/') //将路径中的\替换成/

      }
   })
   //将高级语法转成es5语法
   const {code} = babel.transformFromAst(ast,null,{
    presets:["@babel/preset-env"]
})
const moduleInfo = {fileUrl,deps,code} // 返回文件地址 依赖关系 和 转换后的代码
return moduleInfo

}



//生成文件的依赖关系图
function parseModules (fileUrl){
    var  entry =  ModelInfo(fileUrl) //加载文件

    var temp = [entry] //将入口文件放入数组中
    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(ModelInfo(deps[key]))
                }
            }
        }
     }
     // 文件地址和内容对应关系
     var depsMap = {}
     temp.forEach(moduleInfo =>{
        depsMap[moduleInfo.fileUrl] = {
            deps:moduleInfo.deps,
            code:moduleInfo.code
        }
})
    return depsMap
}

//生成build.js文件  这个文件是一个自执行文件

function bundle(fileUrl){
  var depsMap = parseModules(fileUrl)
return `(function (graph){
console.log(graph)
  function require(file) {
            function absRequire(relPath) {
             //我们再子模块中的代码中会这是require(path) 的文件加载相对路径我们需要根据相对路径获取其对应的map中的绝对路径 然后调用自定义的require方法通过自执行函数进行执行code
                return require(graph[file].deps[relPath])
            }
            var exports = {};
            (function (require,exports,code) { //将上下文传入 用于嵌套文件中的上下文使用
                eval(code)
            })(absRequire,exports,graph[file].code) 
            return exports //将exports对象返回
        }
        require('${fileUrl}')


})(${JSON.stringify(depsMap)})`  //因为我们需要将depsMap 嵌入到字符串的自执行函数中   因为我们需要JSON.stringify


}


const content = bundle('./src/index.js')
// if(fs.existsSync('./dist')){
//     fs.rmdirSync('./dist',{ recursive: true });
// }
//写入到我们的dist目录下
fs.mkdirSync('./dist');
fs.writeFileSync('./dist/bundle.js',content)`