webpack中bundler源码编写2
const fs = require('fs'); // 帮助我们获取一些文件的信息
const path = require('path'); // 打包的时候需要绝对路径,借助path这个模块
const parser = require('@babel/parser'); // 帮助我们分析代码,引入的文件
const traverse = require('@babel/traverse').default;// 因为是export出来的内容,必须加一个default属性才可以
const babel = require('@babel/core'); // babel的核心模块,转化代码,转化成浏览器认识的代码 // 分析模块
const moduleAnalyser = (filename) => {
// 读取文件内容
const content = fs.readFileSync(filename, 'utf-8');
// 利用parser.parse获取到ast,抽象语法树
const ast = parser.parse(content, {
sourceType: 'module' // 说明是es module的引入方式
});
// 利用traverse对代码进行一个分析
const dependencies = {};
traverse(ast, {
// 只要抽象语法树有ImportDeclaration就会进入这个方法,node是节点
ImportDeclaration({ node }){
// 拿到filename对应的文件夹路径
const dirname = path.dirname(filename);
// 对这个文件夹的路径进行一个转化,将引入的模块转化成相对于bundler的相对路径
const newFile = './' + path.join(dirname, node.source.value);
// 为了方便,把相对路径,绝对路径都存上,key是相对路径,value是绝对路径
dependencies[node.source.value] = newFile;
}
});
// 这个方法可以将抽象语法树转化成浏览器可以运行代码。
const { code } = babel.transformFromAst(ast, null, {
presets: ['@babel/preset-env'] // 把es6语法翻译成es5语法
});
// 返回入口文件和相对应的依赖,都可以分析出来了。
return {
filename,
dependencies,
code
}
} // 依赖图谱函数,将所有分析好的模块放在这里
const makeDependenciesGraph = (entry) => {
const entryModule = moduleAnalyser(entry);
const graphArray = [entryModule];
// 循环入口文件
for(let i = 0; i < graphArray.length; i++) {
const item = graphArray[i];
const { dependencies } = item;
// 如果该模块有依赖文件,那么就对相应的依赖文件继续进行分析
if(dependencies) {
for(let j in dependencies) {
graphArray.push(moduleAnalyser(dependencies[j]));
}
}
}
// 打印出来,发现所有模块都分析好了。
console.log(graphArray);
} const graphInfo = makeDependenciesGraph('./src/index.js');
运行node bundler.js | highlight。发现所有模块的文件,依赖和翻译的代码都分析好了打印出来。这个就是我们的依赖图谱。
// 依赖图谱函数,将所有分析好的模块放在这里
const makeDependenciesGraph = (entry) => {
const entryModule = moduleAnalyser(entry);
const graphArray = [entryModule];
// 循环入口文件
for(let i = 0; i < graphArray.length; i++) {
const item = graphArray[i];
const { dependencies } = item;
// 如果该模块有依赖文件,那么就对相应的依赖文件继续进行分析
if(dependencies) {
for(let j in dependencies) {
graphArray.push(moduleAnalyser(dependencies[j]));
}
}
}
const graph = {};
graphArray.forEach(item => {
graph[item.filename] = {
dependencies: item.dependencies,
code: item.code
}
})
console.log(graph);
}
这个时候数组就编程了一个对象。
const fs = require('fs'); // 帮助我们获取一些文件的信息
const path = require('path'); // 打包的时候需要绝对路径,借助path这个模块
const parser = require('@babel/parser'); // 帮助我们分析代码,引入的文件
const traverse = require('@babel/traverse').default;// 因为是export出来的内容,必须加一个default属性才可以
const babel = require('@babel/core'); // babel的核心模块,转化代码,转化成浏览器认识的代码 // 分析模块
const moduleAnalyser = (filename) => {
// 读取文件内容
const content = fs.readFileSync(filename, 'utf-8');
// 利用parser.parse获取到ast,抽象语法树
const ast = parser.parse(content, {
sourceType: 'module' // 说明是es module的引入方式
});
// 利用traverse对代码进行一个分析
const dependencies = {};
traverse(ast, {
// 只要抽象语法树有ImportDeclaration就会进入这个方法,node是节点
ImportDeclaration({ node }){
// 拿到filename对应的文件夹路径
const dirname = path.dirname(filename);
// 对这个文件夹的路径进行一个转化,将引入的模块转化成相对于bundler的相对路径
const newFile = './' + path.join(dirname, node.source.value);
// 为了方便,把相对路径,绝对路径都存上,key是相对路径,value是绝对路径
dependencies[node.source.value] = newFile;
}
});
// 这个方法可以将抽象语法树转化成浏览器可以运行代码。
const { code } = babel.transformFromAst(ast, null, {
presets: ['@babel/preset-env'] // 把es6语法翻译成es5语法
});
// 返回入口文件和相对应的依赖,都可以分析出来了。
return {
filename,
dependencies,
code
}
} // 依赖图谱函数,将所有分析好的模块放在这里
const makeDependenciesGraph = (entry) => {
const entryModule = moduleAnalyser(entry);
const graphArray = [entryModule];
// 循环入口文件
for(let i = 0; i < graphArray.length; i++) {
const item = graphArray[i];
const { dependencies } = item;
// 如果该模块有依赖文件,那么就对相应的依赖文件继续进行分析
if(Object.getOwnPropertyNames(dependencies).length) {
for(let j in dependencies) {
graphArray.push(moduleAnalyser(dependencies[j]));
}
}
}
const graph = {};
graphArray.forEach(item => {
graph[item.filename] = {
dependencies: item.dependencies,
code: item.code
}
}) return graph;
} const graphInfo = makeDependenciesGraph('./src/index.js');
console.log(graphInfo);
接下来我们就只要借助dependenciesGraph来生成真正可以在浏览器运行的代码。
const fs = require('fs'); // 帮助我们获取一些文件的信息
const path = require('path'); // 打包的时候需要绝对路径,借助path这个模块
const parser = require('@babel/parser'); // 帮助我们分析代码,引入的文件
const traverse = require('@babel/traverse').default;// 因为是export出来的内容,必须加一个default属性才可以
const babel = require('@babel/core'); // babel的核心模块,转化代码,转化成浏览器认识的代码 // 分析模块
const moduleAnalyser = (filename) => {
// 读取文件内容
const content = fs.readFileSync(filename, 'utf-8');
// 利用parser.parse获取到ast,抽象语法树
const ast = parser.parse(content, {
sourceType: 'module' // 说明是es module的引入方式
});
// 利用traverse对代码进行一个分析
const dependencies = {};
traverse(ast, {
// 只要抽象语法树有ImportDeclaration就会进入这个方法,node是节点
ImportDeclaration({ node }){
// 拿到filename对应的文件夹路径
const dirname = path.dirname(filename);
// 对这个文件夹的路径进行一个转化,将引入的模块转化成相对于bundler的相对路径
const newFile = './' + path.join(dirname, node.source.value);
// 为了方便,把相对路径,绝对路径都存上,key是相对路径,value是绝对路径
dependencies[node.source.value] = newFile;
}
});
// 这个方法可以将抽象语法树转化成浏览器可以运行代码。
const { code } = babel.transformFromAst(ast, null, {
presets: ['@babel/preset-env'] // 把es6语法翻译成es5语法
});
// 返回入口文件和相对应的依赖,都可以分析出来了。
return {
filename,
dependencies,
code
}
} // 依赖图谱函数,将所有分析好的模块放在这里
const makeDependenciesGraph = (entry) => {
const entryModule = moduleAnalyser(entry);
const graphArray = [entryModule];
// 循环入口文件
for(let i = 0; i < graphArray.length; i++) {
const item = graphArray[i];
const { dependencies } = item;
// 如果该模块有依赖文件,那么就对相应的依赖文件继续进行分析
if(Object.getOwnPropertyNames(dependencies).length) {
for(let j in dependencies) {
graphArray.push(moduleAnalyser(dependencies[j]));
}
}
}
const graph = {};
graphArray.forEach(item => {
graph[item.filename] = {
dependencies: item.dependencies,
code: item.code
}
}) return graph;
} // 这个函数结合dependenciesGraph来生成最后的代码
const generateCode = (entry) => {
// 拿到生成的 graph对象
const graph = JSON.stringify(makeDependenciesGraph(entry));
/**
* 1、避免污染全局,放在大的闭包里面
* 2、我们看到graph里面的源码有require,export这样的关键字,这个浏览器也是看不懂的,
* 所以如果想去直接去执行每个模块的代码,会报错的。所以首先需要在里面构建require
* 3、localRequire是相对路径转化的函数
*/
return `
(function(graph){
function require(module){
function localRequire(relativePath){
return require(graph[module].dependencies[relativePath])
}
var exports = {};
(function(require, exports, code){
eval(code);
})(localRequire, exports, graph[module].code);
return exports;
};
require('${entry}')
})(${graph});
`;
}
const code = generateCode('./src/index.js');
console.log(code);
webpack中bundler源码编写2的更多相关文章
- webpack中bundler源码编写
新建一个项目,再新建一个src文件夹,里面有三个文件,word.js,message.js,index.js word.js export const word = 'hello'; message. ...
- php中foreach源码分析(编译原理)
php中foreach源码分析(编译原理) 一.总结 编译原理(lex and yacc)的知识 二.php中foreach源码分析 foreach是PHP中很常用的一个用作数组循环的控制语句.因为它 ...
- vue打包时,assets目录 和static目录下文件的处理区别(nodeModule中插件源码修改后,打包后的文件应放在static目录)
为了回答这个问题,我们首先需要了解Webpack如何处理静态资产.在 *.vue 组件中,所有模板和CSS都会被 vue-html-loader 及 css-loader 解析,并查找资源URL.例如 ...
- 【原】Spark中Client源码分析(二)
继续前一篇的内容.前一篇内容为: Spark中Client源码分析(一)http://www.cnblogs.com/yourarebest/p/5313006.html DriverClient中的 ...
- 【原】Spark中Master源码分析(二)
继续上一篇的内容.上一篇的内容为: Spark中Master源码分析(一) http://www.cnblogs.com/yourarebest/p/5312965.html 4.receive方法, ...
- 【原】 Spark中Worker源码分析(二)
继续前一篇的内容.前一篇内容为: Spark中Worker源码分析(一)http://www.cnblogs.com/yourarebest/p/5300202.html 4.receive方法, r ...
- Django缓存机制--rest_framework中节流源码使用的就是django提供的缓存api
一.配置缓存 https://www.jb51.net/article/124434.htm 二.缓存全站.页面.局部 三.自我控制的简单缓存API API 接口为:django.core.c ...
- 深入理解 Node.js 中 EventEmitter源码分析(3.0.0版本)
events模块对外提供了一个 EventEmitter 对象,即:events.EventEmitter. EventEmitter 是NodeJS的核心模块events中的类,用于对NodeJS中 ...
- 从 sourcemap 中获取源码
使用 paazmaya/shuji: Reverse engineering JavaScript and CSS sources from sourcemaps 可以从 sourcemap 中获取源 ...
随机推荐
- Union All/Union/Intersect操作
Union All/Union/Intersect操作 适用场景:对两个集合的处理,例如追加.合并.取相同项.相交项等等. Concat(连接) 说明:连接不同的集合,不会自动过滤相同项:延迟. 1. ...
- ecshop数据表结构说明
深深地看看这个吧,会很有帮助的... ecs_account_log 用户帐号情况记录表,包括资金和积分等 log_id mediumint 自增ID号user_id mediumint 用户登录后保 ...
- 原生JavaScript常用本地浏览器存储方法二(cookie)
JavsScript Cookie概述 cookie是浏览器提供的一种机制,它将document对象的cookie属性提供给JavaScript.可以由JavaScript对其进行控制,而并不是Jav ...
- 通过iis启动服务,会产生C:/inetpub/logs/logsFile产生大量的日志,定期清理
[转]https://www.cnblogs.com/Martianhh/p/5312495.html bat文件内容如下,加入到计划任务执行 :: 清理IIS日志文件 :: 备份MySql数据库 @ ...
- JavaScript控制浏览器全屏及各种浏览器全屏模式的方法、属性和事件
实现全屏 个人版:function isFullScreen() { var fullscreenElement = document.fullscreenElement || document.we ...
- c# 基础类型探索
一.前言 本章节主要是探索 C# 的基本类型,一直以来我本人常用都是 int .double.bool.decimal.string 这五个类型,其对其它类型没有认真了解过.只是以前在学习的时候背了些 ...
- webpack config to use plugin and webpack-dev-server
Demo3操作手册 本Demo演示如何配合各种plugin进行偏复杂的使用 准备环境 初始化环境, cd到demo1目录之后, 执行如下命令: npm init -y npm install webp ...
- 023 Android 自定义Toast控件
1.Toast自定义控件工具类 package com.example.administrator.test62360safeguard.Utils; import android.content.C ...
- golang日志库之glog使用问题总结
1. 日志默认输出路径为临时路径,可通过执行命令时带上 -log_dir="路径",指定输出,但路径必须已存在,源码如下,日志文件会生成两个 .INFO等后缀是符号链接文件,另一个 ...
- IOS微信浏览器返回事件监听问题
业务需求:从主页进入A订单页面,然后经过各种刷新或点标签加载后点左上角的返回直接返回到主页 采取方法:采用onpopstate事件监听url改变,从而跳转到主页 遇到的问题:安卓上测试没问题:苹果手机 ...