export const word = "hello";
./src/message.js
import { word } from "./word.js";
const message = "say " + word;
export default message;
./src/index.js
import message from "./message.js";
console.log(message);
首先需要读取文件内容,在bundler.js中:
const fs = require("fs");
const parser = require("@babel/parser");
//模块分析
const moduleAnalyser = (filename) => {
// 读取文件内容
const content = fs.readFileSync(filename,"utf-8");
// 并将其转换为AST抽象语法树
//安装@babel/parser
const ast = parser.parse(content,{
sourceType:"module"
})
//打印ast
console.log(ast);
// console.log(ast.program.body);
}
console.log(ast.program.body);
显示:
type为‘ImportDeclaration‘表示为引入模块,所以我们需要提取出该Node中中的value值,将相对路径变成绝对路径,在这里我们使用babel/traverse工具遍历抽象语法树:
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const path = require("path");
//模块分析
const moduleAnalyser = (filename) => {
// 读取文件内容
const content = fs.readFileSync(filename, "utf-8");
// 并将其转换为AST抽象语法树
//安装@babel/parser
const ast = parser.parse(content, {
sourceType: "module"
})
//遍历抽象语法树
// console.log(ast);
// console.log(ast.program.body);
//创建一个对象,收集模块名字
const dependencies = {};
//利用@babel/traverse工具遍历ast
traverse(ast, {
ImportDeclaration({ node }) {
//可找出node.source.value为相对路径
//获取绝对路径
const dirname = path.dirname(filename);
//注意直接使用path.join() 会变成'src\\message‘,所以我用的是path.posix.join()
const newFile = path.posix.join(dirname, node.source.value)
//key为相对路径,value为绝对路径
dependencies[node.source.value] = newFile;
}
})
//打印出收集的模块
console.log(dependencies);
}
moduleAnalyser("./src/index.js");
收集到的模块打印出来:
{ './message': 'src/message' }
利用@babel/core将语法树转换为代码,并将内容返回
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const path = require("path");
const babel = require("@babel/core");
//模块分析
const moduleAnalyser = (filename) => {
// 读取文件内容
const content = fs.readFileSync(filename, "utf-8");
// 并将其转换为AST抽象语法树
//安装@babel/parser
const ast = parser.parse(content, {
sourceType: "module"
})
//遍历抽象语法树
// console.log(ast);
// console.log(ast.program.body);
//创建一个对象,收集模块名字
const dependencies = {};
//利用@babel/traverse工具遍历ast
traverse(ast, {
ImportDeclaration({ node }) {
//可找出node.source.value为相对路径
//获取绝对路径
const dirname = path.dirname(filename);
//注意直接使用path.join() 会变成'src\\message‘,所以我用的是path.posix.join()
const newFile = path.posix.join(dirname, node.source.value)
//key为相对路径,value为绝对路径
dependencies[node.source.value] = newFile;
}
});
//利用@babel/core将语法树转换为代码
const { code } = babel.transformFromAst(ast, null, {
presets: ["@babel/preset-env"]
});
return {
filename, //入口文件
dependencies,//收集模块名字
code//转换好的代码
}
}
moduleAnalyser("./src/index.js");
我们需要利用一个依赖图谱存放所有的模块信息
//依赖图谱存放所有的模块信息
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;
//如果存在依赖的模块,并对其进行分析,存放到graphArray中
//依赖的模块仍有自己依赖的模块并继续深入分析
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);
}
makeDependenciesGraph("./src/index.js");
打印出来:
做好这些之后我们就需要让代码成可以在浏览器中执行
const fs = require("fs");
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const path = require("path");
const babel = require("@babel/core");
//模块分析
const moduleAnalyser = (filename) => {
// 读取文件内容
const content = fs.readFileSync(filename, "utf-8");
// 并将其转换为AST抽象语法树
//安装@babel/parser
const ast = parser.parse(content, {
sourceType: "module"
})
//遍历抽象语法树
// console.log(ast);
// console.log(ast.program.body);
//创建一个对象,收集模块名字
const dependencies = {};
//利用@babel/traverse工具遍历ast
traverse(ast, {
ImportDeclaration({ node }) {
//可找出node.source.value为相对路径
//获取绝对路径
const dirname = path.dirname(filename);
//注意直接使用path.join() 会变成'src\\message‘,所以我用的是path.posix.join()
const newFile = path.posix.join(dirname, node.source.value)
//key为相对路径,value为绝对路径
dependencies[node.source.value] = newFile;
}
});
//利用@babel/core将语法树转换为代码
const { code } = babel.transformFromAst(ast, null, {
presets: ["@babel/preset-env"]
});
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;
//如果存在依赖的模块,并对其进行分析,存放到graphArray中
//依赖的模块仍有自己依赖的模块并继续深入分析
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
}
});
return graph;
}
//让代码变成可以在浏览器中执行
const generateCode = (module) => {
//将其转换成JSON字符串
const graph = JSON.stringify(makeDependenciesGraph(module));
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('${ module}')
})(${ graph})
`
}
console.log(generateCode("./src/index.js"))
注意: