mini版webpack实现

397 阅读1分钟

步骤

  • 读取入口文件 entry
  • 拿到文件依赖, @babel/parser
  • 遍历出所有的引入模块 @babel/traverse
  • 转化为浏览器可以识别的代码 @babel/core @babel/preset-env
  • 分析依赖
  • 生成代码
  • 写入输出文件目录

myWebpack.js

const fs = require('fs')
const parse = require('@babel/parser')
const { transformFromAst } = require('babel-core')
module.exports = class Webpack{
   constructor(options){
      this.entry = options.entry  
      this.output = options.output
   } 
   run(){
    // 递归找到moduleArr
    const info = this.build(this.entry)
    const moduleArr = []
    moduleArr.push(info)
    for(var i=0;i<moduleArr.length;i++){
        const item  = moduleArr[i]
        const {dependencies} = item 
        if(dependencies){
            for(const key in dependencies){
                moduleArr.push(this.build(dependencies[key]))
            }
        }
    }

    // 转成对象
    const obj = {}
    moduleArr.forEach(module=>{
        obj[module.entry] = {
            code:module.code,
            dependencies: module.dependencies
        }    
    })
    this.emitFile(obj)

   }
   // 读取文件 拿到依赖
   build(entry){
    const entry = fs.readFileSync(entry, 'uft-8')
    // 转成ast数
    const ast = parser.parse(entry,{
        sourceType: 'module'
    })
    const dependencies = {}
    // 依赖
    tranverse(ast, {
        importDeclaration({node}){
            const dirname = path.dirname(entry)
            const newPath = './' + path.join(dirname,node.source.value).replace(/\///g,'/' )
            dependencies[node.source.value] = newPath
        }
    })

    // 转化为浏览器可执行的代码
    const {code} = transformFromAst(ast, null, {
        preset:['@babel/preset-env']
    })
    return {
        code,
        dependencies,
        entry
    }
   }
   emitFile(obj){
    // eval code
    // 转成字符串
    const objStr = JSON.stringify(obj)
    const boundle = `
        (function(modules){
            var exports = {}
            funciton localRequire(relativePath){
                return require(modules[moduleId].dependencies[relativePath])
            }
           (function(require, exports, code){
            eval(code)
           })(localRequire, exports, modules[moduleId].code) 
           require('${this.entry}')
        })(${objStr})
    `
    fs.writeFileSync(filename, boundle)
   }
}