手写 webpack

70 阅读1分钟

astexplorer.net/ AST 可以做什么? (Babel\LESS、Eslint、Typescript) regex101.com/ 正则表达式

流程图

image.png

直接写代码

代码

image.png

image.png

image.png

image.png

webpack

image.png

image.png

image.png

fc_webpack.js

image.png

image.png

image.png

image.png

image.png

fc_webpack.js 源码

const fs = require('fs');
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default;
const babel = require('@babel/core');
const path = require('path')
let ID=0;
function createAsset(filename){
    const content = fs.readFileSync(filename,'utf-8');
    const ast = parser.parse(content,{
        sourceType:'module'
    })
    const dependencies=[]
    traverse(ast,{
        ImportDeclaration:({node}) =>{
            dependencies.push(node.source.value);
        }
    })

    const { code}  = babel.transformFromAstSync(ast,null,{
        presets:['@babel/preset-env']
    })
    let id=ID++;
    return {
        filename,
        code,
        dependencies,
        id
    }
}

function createGraph(entry){
    const mainAsset = createAsset(entry);
    const queue=[mainAsset];
    for(const asset of queue){
        const dirname = path.dirname(asset.filename);
        asset.mapping={};
        asset.dependencies.forEach((relativePath)=>{
            const absolutePath = path.join(dirname,relativePath);
            const child = createAsset(absolutePath);

            asset.mapping[relativePath] =child.id;
            queue.push(child)

        })
    }
    return queue;

}

function bundle(graph){
    let  modules ='';
    graph.forEach((mod)=>{
        modules+=`
            ${mod.id}:[
                function(require,module,exports){
                    ${mod.code}
                },
                ${JSON.stringify(mod.mapping)}
            ],
        `
    })
    const result = `
             (
                function(modules){
                    function require(id){
                        const [ fn,mapping]=modules[id]
                        function localRequire(relativePath){
                            return require(mapping[relativePath])
                        }
                        const module={
                            exports:{}
                        }
                        
                        fn(localRequire,module,module.exports);
                        return module.exports;
                    }
                    require(0);
                }
             )({${modules}})


    `
    return result;
}


const graph = createGraph('./src/index.js');
const result = bundle(graph);
console.log(result);


打包后的代码

;(function (modules) {
  function require(id) {
    const [fn, mapping] = modules[id]
    function localRequire(relativePath) {
      return require(mapping[relativePath])
    }
    const module = {
      exports: {},
    }

    fn(localRequire, module, module.exports)
    return module.exports
  }
  require(0)
})({
  0: [
    function (require, module, exports) {
      'use strict'

      var _info = _interopRequireDefault(require('./info.js'))
      function _interopRequireDefault(obj) {
        return obj && obj.__esModule ? obj : { default: obj }
      }
      console.log(_info['default'])
    },
    { './info.js': 1 },
  ],

  1: [
    function (require, module, exports) {
      'use strict'

      Object.defineProperty(exports, '__esModule', {
        value: true,
      })
      exports['default'] = void 0
      var _consts = require('./consts.js')
      var _default = (exports['default'] = '\u6B22\u8FCE\u6765\u5230 '.concat(
        _consts.company,
      ))
    },
    { './consts.js': 2 },
  ],

  2: [
    function (require, module, exports) {
      'use strict'

      Object.defineProperty(exports, '__esModule', {
        value: true,
      })
      exports.company = void 0
      var company = (exports.company = '\u9605\u6587')
    },
    {},
  ],
})