webpack学习9-手写webpack

1. 编写的目的

自己写一个mini版本的webpck ,从而感受webpack内部的原理

2. 需要的工具

1. @babel/parser 把文件编译成抽象语法树ast

2. @babel/traverse 获取依赖

3. @babel/core transformFromAst 方法解析ast的es6 为es5

4. @babel/preset-env 设置babel环境

3. 代码

1. webpack.config.js

const path = require('path');
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename:'main.js'
    }

}

2. package.json 的script 部分

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "debug": "node --inspect-brk ./myWebpack/index.js",
    "build": "node ./myWebpack/index.js"
  },

3. 主代码

 //  index.js
const Compiler = require('./Compile.js');
const config = require('../webpack.config.js');

 function myWebpack(config){
     return new Compiler(config);
 };

 
 myWebpack(config).run();
 //  compile.js
const fs = require('fs');
const parser = require('./paser.js');
const {getEntrtFile, getAst, getDeps,getCode} = parser;
const path = require('path');
const { file } = require('@babel/types');

class Compiler{
    constructor(config){
        this.options = config; 
        this.modules = {};  // 存储各个模块
    }
    run(){
        const filePath = path.resolve(process.cwd(),this.options.entry);
        // 拿到文件
        this.build(filePath, this.modules);

        this.generate(this.modules, filePath); // 打包到一个bundle中去
        
    }
    build(filePath,container){
       
        const entryFile = getEntrtFile(filePath);
        // 获得ast
        const ast = getAst(entryFile); 
        // 收集依赖
        const deps = getDeps(ast, filePath);
        // 收集代码
        const code = getCode(ast);
    
        const fileInfo = {
            deps,
            code,
            filePath
        }
        container[filePath] = fileInfo;
        if(Object.keys(deps).length){
            Object.keys(deps).forEach(item=>{
                !container[deps[item]] && this.build(deps[item],container)
            });
        };
    }
    generate(modules){
        const distPath = path.resolve(this.options.output.path, this.options.output.filename);
        let str = '';
        // 伪代码 可能需要把ast的解析 想办法解析相对路径才行 
        Object.keys(modules).forEach(item=>{
            str += modules[item].code;
        });
        // 最后插入代码
        fs.writeFileSync(distPath, str, 'utf-8');
    }
}

module.exports = Compiler;

 //  paser.js

 const fs = require('fs');
const path = require('path');
const babelParser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const {transformFromAst} = require('@babel/core');


const parser = {
      // 拿到文件
    getEntrtFile(filePath){
        return fs.readFileSync(filePath, 'utf-8');
    },
    // 获得ast
    getAst(entryFile){
        // 在ast的program的body属性中有四个node 代表有几行代码
        return babelParser.parse(entryFile,{sourceType: 'module'})
    }, 
    //收集依赖
    getDeps(ast,filePath){
         // 获得文件目录相对路径
         const dirName = path.dirname(filePath);
         // 收集依赖 type: "ImportDeclaration" 就是引入的依赖
         const deps = {}
         traverse(ast, {
            ImportDeclaration({node}){
             const raletivePath = node.source.value;
             const absolutePath = path.resolve(dirName,raletivePath);
             deps[raletivePath] = absolutePath;
            }
         });
         return deps;
    },
    //获取astes6编译后的es5代码
     getCode(ast){
        const {code} = transformFromAst(ast,null,{
            presets: ['@babel/preset-env']
        })
        return code
     }

}
module.exports = parser;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值