美文网首页
创建自己的打包工具

创建自己的打包工具

作者: Victor_818 | 来源:发表于2019-05-15 12:38 被阅读0次
  1. 创建package.json文件
npm init -y
  1. 设置配置文件
{
  "name": "my-webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "bin": {  // 运行哪个命令,执行哪个文件
      "my-pack": "./bin/my-pack.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
  1. 创建bin/my-pack.js文件
#! /usr/bin/env node
console.log('start');
  1. 把package.json里面bin对象中的my-pack命令链接到全局
npm link
image.png
  1. 把my-pack命令从全局链接到本地
npm link my-pack
image.png

测试一下:

npx webpack
image.png

打印出start表示测试成功

  1. 编译my-pack.js文件
#! /usr/bin/env node

/**
 * 1)  需要找到当前执行的路径  拿到webpack.config.js
 */
let path = require('path');
let Compiler = require('../lib/Compiler')
// 引用当前运行的目录  config表示配置文件
let config = require(path.resolve(__dirname));

let compiler = new Compiler(config);
// 标识运行编译
compiler.run();
  1. 创建./lib/Compiler.js目录
let fs = require('fs');
let path = require('path');
let babylon = require('babylon');
let t = require('@babel/types');
let traverse = require('@babel/traverse').default;
let generator = require('@babel/generator').default;
let ejs = require('ejs');
let {SyncHook} = require('tapable')
// babylon 主要就是把源码 转换成ast
// @babel/traverse
// @babel/types
// @babel/generator
class Compiler {
  constructor(config) {
    // entry output
    this.config = config;
    // 需要保存入口文件的路径
    this.entryId; // './src/index.js'
    // 需要保存所有的模块依赖
    this.modules = {};
    this.entry = config.entry; // 入口路径
    // 工作路径
    this.root = process.cwd();
    this.hooks = {
      entryOption:new SyncHook(),
      compile:new SyncHook(),
      afterCompile:new SyncHook(),
      afterPulgins:new SyncHook(),
      run:new SyncHook(),
      emit:new SyncHook(),
      done:new SyncHook()
    }
    // 如果传递了plugins参数
    let plugins = this.config.plugins;
    if(Array.isArray(plugins)){
      plugins.forEach(plugin => {
        plugin.apply(this);
      });
    }
    this.hooks.afterPulgins.call();
  }
  getSource(modulePath) { // ./index.less
    let rules = this.config.module.rules;
    let content = fs.readFileSync(modulePath, 'utf8');
    // 拿到每个规则来处理
    for (let i = 0; i < rules.length; i++) {
      let rule = rules[i];
      let { test, use } = rule;
      let len = use.length - 1;
      if (test.test(modulePath)) { // 这个模块需要通过loader来转化
        // loader获取对应的loader函数
        function normalLoader() {
          let loader = require(use[len--]);
          // 递归调用loader 实现转化功能
          content = loader(content);
          if(len>=0){
            normalLoader();
          }
        }
        normalLoader();
      }
    }
    return content
  }
  // 解析源码 
  parse(source, parentPath) { // AST解析语法树
    let ast = babylon.parse(source);
    let dependencies = [];// 依赖的数组
    traverse(ast, {
      CallExpression(p) { //  a() require()
        let node = p.node; // 对应的节点
        if (node.callee.name === 'require') {
          node.callee.name = '__webpack_require__';
          let moduleName = node.arguments[0].value; // 取到的就是模块的引用名字
          moduleName = moduleName + (path.extname(moduleName) ? '' : '.js');
          moduleName = './' + path.join(parentPath, moduleName); 'src/a.js'
          dependencies.push(moduleName);
          node.arguments = [t.stringLiteral(moduleName)];
        }
      }
    });
    let sourceCode = generator(ast).code;
    return { sourceCode, dependencies }
  }
  // 构建模块
  buildModule(modulePath, isEntry) {
    // 拿到模块的内容
    let source = this.getSource(modulePath);
    // 模块id modulePath  = modulePath- this.root  src/index.js
    let moduleName = './' + path.relative(this.root, modulePath);
    if (isEntry) {
      this.entryId = moduleName; // 保存入口的名字
    }
    // 解析需要把source源码进行改造 返回一个依赖列表
    let { sourceCode, dependencies } = this.parse(source, path.dirname(moduleName)); // ./src

    // 把相对路径和模块中的内容 对应起来
    this.modules[moduleName] = sourceCode;

    dependencies.forEach(dep => { // 附模块的加载 递归加载
      this.buildModule(path.join(this.root, dep), false);
    });
  }
  emitFile() { // 发射文件
    // 用数据 渲染我们的
    // 拿到输出到哪个目录下 输出路径
    let main = path.join(this.config.output.path, this.config.output.filename);
    // 模板的理解
    let templateStr = this.getSource(path.join(__dirname, 'main.ejs'));
    let code = ejs.render(templateStr, { entryId: this.entryId, modules: this.modules });
    this.assets = {}
    // 资源中 路径对应的代码
    this.assets[main] = code;
    fs.writeFileSync(main, this.assets[main]);
  }
  run() {
    this.hooks.run.call();
    // 执行 并且创建模块的依赖关系
    this.hooks.compile.call();
    this.buildModule(path.resolve(this.root, this.entry), true);
    this.hooks.afterCompile.call();
    // 发射一个文件 打包后的文件
    this.emitFile();
    this.hooks.emit.call();
    this.hooks.done.call();
  }
}
module.exports = Compiler
  1. 创建./lib/main.ejs模板
(function (modules) {
var installedModules = {};
function __webpack_require__(moduleId) {
  if (installedModules[moduleId]) {
    return installedModules[moduleId].exports;
  }
  var module = installedModules[moduleId] = {
    i: moduleId,
    l: false,
    exports: {}
  };
  modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  module.l = true;
  return module.exports;
}
return __webpack_require__(__webpack_require__.s = "<%-entryId%>");
})
({
  <%for(let key in modules){%>
  "<%-key%>":
  (function (module, exports, __webpack_require__) {
    eval(`<%-modules[key]%>`);
  }),
  <%}%>
});

相关文章

  • 创建自己的打包工具

    创建package.json文件 设置配置文件 创建bin/my-pack.js文件 把package.json里...

  • Python搭建私服代码库

    一、代码打包 Python代码打包,使用的是打包工具setuptool 1 创建文件结构 crawlerCore/...

  • 纯命令行打APK实操全记录

    介绍 本文基于ant命令创建工程使用android sdk提供的工具进行手动打包 打包基本流程 手动命令打包实操 ...

  • Webpack 入门教程

    Webpack 是一个前端资源加载/打包工具。 安装 Webpack 使用 cnpm 安装 webpack: 创建...

  • Snabbdom 基本使用

    1. 创建项目 打包工具为了方便使用 parcel[https://parceljs.org/getting_s...

  • Unreal4移动补丁工具节点0046bate1

    移动补丁工具节点 介绍不同类型的移动补丁工具蓝图节点。 使用 移动打包向导 为移动项目创建小型初始下载后,还需要以...

  • shell命令整理(七)

    打包压缩 window打包压缩工具: linux打包压缩工具: 打包 解包 案例 压缩 解压缩 案例 打包压缩一起...

  • vue 中webpack学习

    webpack模块化打包机 或者 模块打包工具。它是一个工具。打包工具“打包”,帮助我们将不同的资源和文件,进行打...

  • 《创建自己的工具类 》

    什么是工具类? 学习开发已经有一段时间了,有时发现别人总是用封装、工具类等高大档的词语!其实,这些都很简单的!好吧...

  • Vue轮子笔记

    1.创建仓库2.组件3.打包工具4.报错总结5.git 新建分支 创建仓库 轱辘 - 一个 Vue UI 组件作者...

网友评论

      本文标题:创建自己的打包工具

      本文链接:https://www.haomeiwen.com/subject/lzmuaqtx.html