[webpack]手写一个mvp版本的webpack
let fs = require('fs');
let path = require('path'); let babylon = require('babylon'); // Babylon 把源码转换为AST
let t = require('@babel/types'); // @babel-types 替换节点
let traverse = require('@babel/traverse').default; // @babel-traverse 遍历节点
let generator = require('@babel/generator').default; // @babel/generator 生成 let ejs = require('ejs'); // js模版 class Compiler {
// 构造函数
constructor(config) {
// entry out
this.config = config;
// 1、保存入口文件的路径
this.entryId;
// 2、博阿村所有的模块依赖
this.modules = {};
// 入口路径
this.entry = config.entry;
// 工作路径
this.root = process.cwd();
}
// 获取文件源码
getSource(modulePath){
let content = fs.readFileSync(modulePath,'utf8');
return content;
}
// 解析源码
parse(source,parentPath){
// AST解析语法树
let ast = babylon.parse(source);
let dependencies = [];
traverse(ast,{
CallExpression(p){
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);
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));
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 tempateStr = this.getSource(path.join(__dirname,'main.ejs'));
let code = ejs.render(tempateStr,{
entryId:this.entryId,
modules: this.modules
}); // this.assets = {};
// // 路径对应的代码
// this.assets[main] = code;
// fs.writeFileSync(main,this.assets[main]); fs.writeFileSync(main,code);
}
run() {
// 执行并创建模块依赖关系
this.buildModule(path.resolve(this.root, this.entry),true);
// 发射一个打包后的文件
this.emitFile();
}
} module.exports = Compiler;
[webpack]手写一个mvp版本的webpack的更多相关文章
- java24 手写服务器最终版本
手写服务器最终版本; <?xml version="1.0" encoding="UTF-8"?> <web-app> <serv ...
- 『练手』手写一个独立Json算法 JsonHelper
背景: > 一直使用 Newtonsoft.Json.dll 也算挺稳定的. > 但这个框架也挺闹心的: > 1.影响编译失败:https://www.cnblogs.com/zih ...
- 放弃antd table,基于React手写一个虚拟滚动的表格
缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反 ...
- 手写一个简单的ElasticSearch SQL转换器(一)
一.前言 之前有个需求,是使ElasticSearch支持使用SQL进行简单查询,较新版本的ES已经支持该特性(不过貌似还是实验性质的?) ,而且git上也有elasticsearch-sql 插件, ...
- webview的简单介绍和手写一个H5套壳的webview
1.webview是什么?作用是什么?和浏览器有什么关系? Webview 是一个基于webkit引擎,可以解析DOM 元素,展示html页面的控件,它和浏览器展示页面的原理是相同的,所以可以把它当做 ...
- -手写Spring注解版本&事务传播行为
视频参考C:\Users\Administrator\Desktop\蚂蚁3期\[www.zxit8.com] 0018-(每特教育&每特学院&蚂蚁课堂)-3期-源码分析-手写Spri ...
- 教你如何使用Java手写一个基于链表的队列
在上一篇博客[教你如何使用Java手写一个基于数组的队列]中已经介绍了队列,以及Java语言中对队列的实现,对队列不是很了解的可以我上一篇文章.那么,现在就直接进入主题吧. 这篇博客主要讲解的是如何使 ...
- 【spring】-- 手写一个最简单的IOC框架
1.什么是springIOC IOC就是把每一个bean(实体类)与bean(实体了)之间的关系交给第三方容器进行管理. 如果我们手写一个最最简单的IOC,最终效果是怎样呢? xml配置: <b ...
- 只会用就out了,手写一个符合规范的Promise
Promise是什么 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息.Prom ...
随机推荐
- iveiw DatePicker 只能选择本月之前的日期,本月包括之后都不能选择
日期判断只能选择本月之前的日期 <DatePicker type="date" :options="options3" format="yyyy ...
- VMnet、VMnet1和 VMnet8的区别是什么
1.经常使用虚拟机的人在配置网络的时候在疑惑 虚拟的 网卡 VMnet1 和 VMnet8的差别是什么 如上面的图, 是安装了一台虚拟机,下图是安装了多个网卡以及虚拟机产生的虚拟网卡 VMware N ...
- 【OF框架】在Visual Studio中启用Docker支持,编译生成,并在容器运行项目
准备 本地已经安装Docker 一.添加Docker支持 第一步:查看本地Docker服务状态 第二步:项目添加Docker支持 第三步:选择Linux容器 第四步:点击启动 第五步:确认Docker ...
- 用js刷剑指offer(复杂链表的复制)
题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用,否 ...
- 【邻接矩阵+基础BFS】PAT-L3-008. 喊山
L3-008. 喊山 一个山头呼喊的声音可以被临近的山头同时听到.题目假设每个山头最多有两个能听到它的临近山头.给定任意一个发出原始信号的山头,本题请你找出这个信号最远能传达到的地方.[何为最远?大致 ...
- dos中查找端口的PID,并在任务管理器中处理端口
本文来源https://www.cnblogs.com/lsyf/p/8979012.html 1.查看所有端口进程 首先点击开始菜单选择运行,接着在运行对话框中输入“cmd”,回车打开命令提示符窗口 ...
- STM32F10XX学习笔记的石墨连接
https://shimo.im/docs/QHGRrWxbeb0NiBm9/ <STM32F10X系列笔记>,可复制链接后用石墨文档 App 打开
- RMQ问题【模板】
概念 RMQ 是英文 Range Maximum/Minimum Query 的缩写,表示区间最大(最小)值. 解决 RMQ 问题的主要方法有两种,分别是 ST 表和线段树.本文主要讲 ST 表. S ...
- 到spring官网创建第一个springboot工程
登录到spring的官网,直接生成一个,然后倒入本地工程就可以了. https://start.spring.io/ 点击创建的时候. 就等于下载了这个工程. 下载后,倒入到我们的maven工程可以直 ...
- 原生JS实现九宫格拼图
实现这个案例,需要考虑到鼠标的拖拽效果(onmousedown/onmousemove/mouseup) 拖拽分解: 按下鼠标---->移动鼠标----->松开鼠标 1.给目标元素添加on ...