經過babylon將文件解析成AST
在線解析器:
node
代碼實現:
bundle.jsgit
const fs = require("fs"); const babylon = require("babylon"); const traverse = require("babel-traverse").default; let ID = 0; function createAsset(filename) { const content = fs.readFileSync(filename, "utf-8"); // 解析文件成AST const ast = babylon.parse(content, { sourceType: "module", }); const dependencies = []; // 根據AST獲取相關依賴 traverse(ast, { ImportDeclaration: ({ node }) => { dependencies.push(node.source.value); }, }); const id = ID++; return { id, filename, dependencies, }; } const mainAssets = createAsset("./example/entry.js"); console.log(mainAssets)
輸出結果:
github
// 構建一個依賴關係圖 function createGraph(entry) { const mainAssets = createAsset(entry); const queue = [mainAssets]; for (const asset of queue) { const dirname = path.dirname(asset.filename); asset.mapping = {}; asset.dependencies.forEach((relativePath) => { const absolutePath = path.join(dirname, relativePath); const child = createAsset(absolutePath); asset.mapping[relativePath] = child.id; queue.push(child); }); } return queue; } const graph = createGraph("./example/entry.js"); console.log(graph);
輸出結果:
npm
在解析文件時,使用babel對代碼進行轉譯瀏覽器
// 解析一個文件及其依賴 function createAsset(filename) { const content = fs.readFileSync(filename, "utf-8"); const ast = babylon.parse(content, { sourceType: "module", }); const dependencies = []; traverse(ast, { ImportDeclaration: ({ node }) => { dependencies.push(node.source.value); }, }); const id = ID++; // 使用babel對代碼進行轉譯 const { code } = babel.transformFromAst(ast, null, { presets: ["env"], }); return { id, filename, dependencies, code, }; }
// 將全部東西打包成一個單文件 function bundle(graph) { let modules = ""; graph.forEach((mod) => { modules += `${mod.id}:[ function(require,module,exports){ ${mod.code} }, ${JSON.stringify(mod.mapping)} ],`; }); const result = ` (function(modules){ function require(id){ const [fn, mapping] = modules[id]; // 由於代碼引入文件時根據相對路徑,因此須要把相對路徑跟id進行一個映射 function localRequire(relativePath){ return require(mapping[relativePath]) } const module = {exports:{}}; fn(localRequire,module,module.exports) return module.exports; } // 執行入口模塊 require(0); })({${modules}}) `; return result; } const graph = createGraph("./example/entry.js"); const result = bundle(graph); console.log(result);
輸出結果:babel
(function(modules) { function require(id) { const [fn, mapping] = modules[id]; function localRequire(relativePath) { return require(mapping[relativePath]) } const module = { exports: {} }; fn(localRequire, module, module.exports) return module.exports; } require(0); })({ 0: [ function(require, module, exports) { "use strict"; var _message = require("./message.js"); var _message2 = _interopRequireDefault(_message); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } console.log(_message2.default); }, { "./message.js": 1 } ], 1: [ function(require, module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _name = require("./name.js"); exports.default = "hello " + _name.name + "!"; }, { "./name.js": 2 } ], 2: [ function(require, module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var name = exports.name = 'Aaron'; }, {} ], })
把代碼複製到瀏覽器運行,執行成功!
app
一個簡易版的Webapck完成了。ui