深入webpack

102 阅读1分钟

实现步骤:

0、读取webpack.config.js

1、解析文件依赖

2、替换require为__webpack_require__

3、本地使用{}存储所有的文件,然后通过使用为__webpack_require__获取文件内容,执行函数方式为evel的包裹代码字符串

代码实现

index.js

#! /usr/bin/env node

// 读取config
const path = require('path')
const fs = require('fs')
// 默认配置
const defaultConfig = {
  entry: './src/index.js',
  output: {
    filename: 'build.js'
  }
}
// 最终的配置
const config = {
  ...defaultConfig,
  ...require(path.resolve('./mywebpack.config.js'))
}

class MyWebPack {
  constructor(config) {
    this.config = config
    this.entry = config.entry
    // 根目录
    this.root = process.cwd()
    // 存储所有代码
    this.modules = {}
  }
  start() {
    console.log('解析依赖')
    const entryPath = path.resolve(this.root, this.entry)
    this.createModule(entryPath, this.entry)
    // 生成新文件
    this.generaleFile()
  }
  // 代码字符串
  generaleModuleString() {
    let fnTmp = ''
    for (const name in this.modules) {
      fnTmp += `"${name}":${this.modules[name].replace(/\r|\n/g, "")},`
    }
    fnTmp = fnTmp.substring(0, fnTmp.length - 1)
    return `{${fnTmp}}`
  }
  // 生成新文件
  generaleFile() {
    let template = fs.readFileSync(path.resolve(__dirname, './template.js'), 'utf-8')
    this.template = template.replace('__entry__', this.entry).replace('__modules_content__', this.generaleModuleString())
    fs.mkdirSync("./mydist");
    fs.writeFileSync('./mydist/' + this.config.output.filename, this.template)
    console.log('写入文件完毕');
  }
  // 创建依赖
  createModule(modulePath, name) {
    // 出现了循环依赖
    // if (this.modules[modulePath]) return
    const fileContent = fs.readFileSync(modulePath, 'utf-8')
    // 替换后的代码和依赖数组
    const { code, deps } = this.parse(fileContent, path.dirname(name))
    this.modules[name] = `function (module, module.exports, __mywebpack_require__) {
      eval(\`${code}\`)
    }`
    // 循环获取所有依赖数组的内容
    deps.forEach(dep => {
      this.createModule(path.join(this.root, dep), './' + dep)
    })

    console.log(this.modules)
  }
  // 文件解析
  parse(code, parent) {
    let deps = []
    let r = /require\('(.*)'\)/g
    // require('xxx') 将require替换__mywebpack_require__
    code = code.replace(r, function (match, arg) {
      const retPath = path.join(parent, arg.replace(/'|"/g, ''))
      deps.push(retPath)
      return `__mywebpack_require__("./${retPath}")`
    })
    return { code, deps }
  }
}

const mywebpack = new MyWebPack(config)

mywebpack.start()

template.js

!function (modules) {
  // 缓存
  const installModules = {}
  // 替换后的require
  function __mywebpack_require__(moduleId) {
    // 是否缓存
    if (installModules[moduleId]) {
      return installModules[moduleId].exports
    }
    let modules = installModules[moduleId] = {
      exports: {}
    }
    modules[moduleId].call(modules.exports, module, exports, __mywebpack_require__)

    return module.exports
  }
  // 入口
  return __mywebpack_require__("__entry__")
}(__modules_content__)