实现小型打包工具

2020-02-17 14:35:47 浏览数 (1)

如果能挺过去,要更加珍惜活着的时间~


hey,各位宝宝,最近的疫情很严重,大家尽量就不要到外面浪了,好好在家做个安静的宝宝吧。不得不出门时也一定要戴口罩哦!照顾好自己,望平安... ...

为了解webpack的原理,我们来试着实现一个小型的打包工具。以下代码实现了两个功能:

  1. 将ES6转为ES5
  2. 支持在js中通过import引用CSS文件

实现


因为涉及ES6转ES5所以需要引用babel相关工具

代码语言:javascript复制
yarn add babylon babel-traverse babel-core babel-preset-env

使用babel转换代码

代码语言:javascript复制
const fs = require('fs')
const path = require('path')
const babylon = require('babylon')
const traverse = require('babel-traverse').default
const { transformFromAst } = require('babel-core')
function readCode(filePath){
  // 读取文件内容
  const centent = fs.readFilesync(filePath,'utf-8')
  // 生成ast 
  const ast = babylon.parse(content,{
    sourceType : 'module'
  })
  // 寻找当前文件的依赖关系
  const dependencies = []
  traverse(ast,{
    ImportDeclaration:({node})=>{
      dependencies.push(node.source.value)
    }
  })
  // 通过AST将代码转为ES5
  const {code} = trasnformFromAst(ast, null, {
    presets: ['env']
  })  

  return {
    filePath,
    dependencies,
    code
  }
}
// 接下来我们实现一个函数,有如下功能:
// 调用 readCode 函数,传入入口文件
// 分析入口文件的依赖
// 识别 JS 和 CSS 文件
function getDependencies(entry){
  const entryObject = readCode(entry)
  const dependencies  = [entryObject]
  // 遍历所有文件依赖关系
  for(const asset of dependencies){
    // 获得文件路径  
    const dirname = path.dirname(asset.filepath)
    // 遍历当前文件依赖关系
    asset.dependencied.forEach(relativePath=>{
      // 获取绝对路径
      const absolutePath = path.join(dirname, relativePath);
      // CSS文件逻辑是插入style标签,JS文件需要查看是否有依赖关系
      if(/.css$/.test(absolutePath)){
        const content = fs.readFileSync(absolutePath, 'utf-8');
        const code = `
          const style = document.createElement('style')
          style.innerText = ${JSON.stringfy(content).replace(/\r\n/g)}
          document.head.appendChild(style)
        `
        dependencies.push({
          filePath: absolutePath,
          relativePath,
          dependencied:[],
          code
        })        
      }else{
        const child = readCode(absolutePath)
        child.relativePath = relativePath
        dependencies.push(child)
      }
    })
  }
  retrun dependencies
}
// 首先我们读取入口文件,然后创建一个数组,该数组的目的是存储代买中涉及到的所有文件
// 然后遍历这个数组,开始这个数组中只有入口文件,遍历过程中,如果入口文件依赖其他文件就会被push到数组中

// 现在我们已经获取了所有的依赖,接下来实现打包功能
function bundle(dependencies, entry){
  let moudles = ''
  dependencies.forEach(dep =>{
    const filePath = dep.relativePath || entry;
    modules  = `'${filePath}'`:(
      function (module, exports, require){ ${dep.code} }
    )
  })
  const result= `
    (function(modules{
    // 构建require函数,目的是为了获取模块暴露出来的内容
      function require(id){
        const module = {exports :{}}
        modules[id](module, module.exports, require)
        return module.exports
      }
      require('${entry}')
   })({${modules}})
  `
  // 将生成的内容写入文件中
  fs.writeFileSync('./bundle.js', result)
}

到这里就实现了一个简单的打包工具,主要功能:

  1. 找出入口文件所有的依赖关系
  2. 然后通过构建 CommonJS 代码来获取 exports 导出的内容

每张故作坚强的笑脸背后,是怎样风雨漂泊的一生---Lin

0 人点赞