webpack原理分析之编写一个打包器

一、引入依赖

const fs = require('fs')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default;
const path = require('path')
const babel = require('@babel/core')

二、形成AST抽象语法树,分析模块的代码和依赖路径

function moduleAnalyser(filename) {
    const content = fs.readFileSync(filename, 'utf-8');
    const ast = parser.parse(content, {
        // parse in strict mode and allow module declarations
        sourceType: "module",
    });
    const dependencies = {};
    traverse(ast, {
        ImportDeclaration({ node }) {
            // dependencies.push(node.source.value)
            const dirname = path.dirname(filename)
            const newFile = './' + path.join(dirname, node.source.value)
            dependencies[node.source.value] = newFile
        }
    })
    // console.log(ast.program)
    // console.log(dependencies)
    const { code } = babel.transformFromAst(ast, null, {
        presets: ["@babel/preset-env"]
    });
    return {
        filename,
        dependencies,
        code
    }
}

三、形成模块间的依赖图谱

const makeDependenciesGraph = (entry) => {
    const entryModule = moduleAnalyser(entry)
    const graphArry = [entryModule]
    for (let i = 0; i < graphArry.length; i++) {
        const item = graphArry[i];
        // console.log(item,123)
        const { dependencies } = item;
        if (dependencies) {
            for (let j in dependencies) {
                graphArry.push(
                    moduleAnalyser(dependencies[j])
                )
            }
        }
    }
    const graph = {}
    graphArry.forEach(item => {
        graph[item.filename] = {
            dependencies: item.dependencies,
            code: item.code
        }
    })
    return graph
}

四、代码生成

const generateCode = (entry) => {
    const graph = JSON.stringify(makeDependenciesGraph(entry));
    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/main.js')  //这个就是你项目的入口文件
console.log(code)

最后生成的code代码就可以在浏览器上执行