webpackをより明確に実装するために、この記事ではwebpackキーワードの代わりにfastpackを使用します。
Fasterpackの実装
#!/usr/bin/env node
const path = require("path");
const fs = require("fs");
const config = require(path.resolve("./fasterpack.config.js"));
class Fasterpack {
constructor(config) {
//キャッシュの設定
this.config = config;
//エントリーを保存する
this.entry = config.entry;
//ルートディレクトリを取得する
this.root = process.cwd();
//モジュールを作成する
this.modules = {};
}
/**
*
* @param {*} code
* @param {*} parent 親ディレクトリ
*/
pares(code, parent) {
// console.log("parent", parent);
const deps = [];
const r = /require\(['"](.*)['"]\)/g;
const r1 = /[
]/g;
//requireを置き換える
code = code.replace(r, function (match, arg) {
const retPath = path.join(parent, arg.replace(/'|"/g, ""));
// 依存パスを追加する
deps.push(retPath);
return `__fasterpack_require__("./${retPath}")`;
});
//改行を置き換える
code = code.replace(r1, function (match, arg) {
return "\
";
});
code = code.replace(/"/g, '"');
code = `"${code}"`;
return { code, deps };
}
/**
*
* @param {*} modulePath モジュールの物理パス
* @param {*} name プロジェクトユニークパスキー
*/
createModule(modulePath, name) {
const modulePathKeys = Object.keys(this.modules);
const fileContent = fs.readFileSync(modulePath, "utf-8");
//プロジェクトが登録されているかどうかを再帰的に判断する
if (!modulePathKeys.includes(name)) {
//解析されたソースコードとその依存関係を返す。
const { code, deps } = this.pares(fileContent, path.dirname(name));
this.modules[name] = `function(module,exports,__fasterpack_require__){
eval(${code})
}`;
// モジュールの再帰的作成
deps.forEach((dep) => {
this.createModule(path.join(this.root, dep), `./${dep}`);
});
}
}
generateModuleStr() {
let fnTemp = "";
Object.keys(this.modules).forEach((name) => {
fnTemp += `"${name}":${this.modules[name]},`;
});
return `{${fnTemp}}`;
}
generateFile() {
//パラメータ・インジェクション・テンプレート
this.tempalate = getTempalate(this.generateModuleStr(), this.entry);
fs.writeFileSync(`./dist/main.js`, this.tempalate);
console.log("書き直す");
}
start() {
console.log("解析開始");
const entryPath = path.resolve(this.root, this.entry);
//キャッシュモジュールを作成する
this.createModule(entryPath, this.entry);
//ファイルを生成する
this.generateFile();
}
}
const getTempalate = (__modules_content__, __entry__) => {
return `(function (modules) {
const installModules = {};
function __faster_require__(moduleId) {
//キャッシュするかどうか
if (installModules[moduleId]) {
console.log(installModules, moduleId, installModules[moduleId].exports);
return installModules[moduleId].exports;
}
var module = (installModules[moduleId] = {
exports: {},
});
modules[moduleId].call(
module.exports,
module,
module.exports,
__faster_require__
);
return module.exports;
}
//
return __faster_require__("${__entry__}");
})(${__modules_content__});`;
};
const fasterpack = new Fasterpack(config);
fasterpack.start();
次のステップは、バベルの助けを借りてそれを変換することです。
知る必要があります。
- 1
- 2
- 3
#!/usr/bin/env node
const path = require("path");
const fs = require("fs");
// ソースコードをastツリーに変換する
const parser = require("@babel/parser");
// ast構文集を解析する
const traverse = require("@babel/traverse").default;
// ソースコードの解析と変換
const { transformFromAst } = require("@babel/core");
const config = require(path.resolve("./fasterpack.config.js"));
class Fasterpack {
constructor(config) {
//キャッシュの設定
this.config = config;
//エントリーを保存する
this.entry = config.entry;
//ルートディレクトリを取得する
this.root = process.cwd();
//モジュールを作成する
this.modules = {};
}
pares(fileContent, parentDirname) {
const deps = [];
const ast = parser.parse(fileContent, {
sourceType: "module",
});
traverse(ast, {
ImportDeclaration({ node }) {
deps.push(path.join(parentDirname, node.source.value));
},
});
let { code } = transformFromAst(ast, null, {
//変換の標準
presets: ["@babel/preset-env"],
});
const r = /require\(['"](.*)['"]\)/g;
code = code.replace(r, function (match, arg) {
const retPath = path.join(parentDirname, arg.replace(/'|"/g, ""));
// 依存パスを追加する
return `__fasterpack_require__("./${retPath}")`;
});
return { code, deps };
}
/**
*
* @param {*} modulePath モジュールの物理パス
* @param {*} name プロジェクトユニークパスキー
*/
createModule(modulePath, name) {
const modulePathKeys = Object.keys(this.modules);
const fileContent = fs.readFileSync(modulePath, "utf-8");
//プロジェクトが登録されているかどうかを再帰的に判断する
if (!modulePathKeys.includes(name)) {
//解析されたソースコードとその依存関係を返す。
const { code, deps } = this.pares(fileContent, path.dirname(name));
this.modules[name] = `function(module,exports,__fasterpack_require__){
eval(${JSON.stringify(code)})
}`;
// モジュールの再帰的作成
deps.forEach((dep) => {
this.createModule(path.join(this.root, dep), `./${dep}`);
});
}
}
generateModuleStr() {
let fnTemp = "";
Object.keys(this.modules).forEach((name) => {
fnTemp += `"${name}":${this.modules[name]},`;
});
return `{${fnTemp}}`;
}
generateFile() {
//パラメータ・インジェクション・テンプレート
this.tempalate = getTempalate(this.generateModuleStr(), this.entry);
fs.writeFileSync(`./dist/main.js`, this.tempalate);
console.log("上書きする ", this.m);
}
start() {
console.log("解析開始");
const entryPath = path.resolve(this.root, this.entry);
//キャッシュモジュールを作成する
this.createModule(entryPath, this.entry);
//ファイルを生成する
this.generateFile();
}
}
const getTempalate = (__modules_content__, __entry__) => {
return `(function (modules) {
const installModules = {};
function __faster_require__(moduleId) {
//キャッシュするかどうか
if (installModules[moduleId]) {
console.log(installModules, moduleId, installModules[moduleId].exports);
return installModules[moduleId].exports;
}
var module = (installModules[moduleId] = {
exports: {},
});
modules[moduleId].call(
module.exports,
module,
module.exports,
__faster_require__
);
return module.exports;
}
//
return __faster_require__("${__entry__}");
})(${__modules_content__});`;
};
const fasterpack = new Fasterpack(config);
fasterpack.start();