实现一个简易版webpack

现实 webpack 的打包产物

大概长这样(只把核心代码留下来):

实现一个简版的webpack

依葫芦画瓢,实现思路分2步:

1. 分析入口文件,把所有的依赖找出来(包括所有后代的依赖)

2. 拼接出类似上面的立即执行函数

找依赖

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

// 分析一个文件,转成CommonJS Module,并找出它的依赖
function readCode(filePath) {
    // 读取文件字符串
    const content = fs.readFileSync(filePath, 'utf-8');
    // 语法解析成 AST
    const ast = parser(content, {
        sourceType: 'module'
    })
    // 获取本文件的依赖
    const dependiences = [];
    // 遍历 AST,每当触发依赖钩子,就往依赖数组添加
    traverse(ast, {
        ImportDeclaration({node}) {
            // 把对应的以来路径存起来
            dependiences.push(node.source.value)
        }
    })
    // 把 es6 转成 es5 字符串
    // 最重要的是把 esModule 的 import export,转成 es5 能认识的 commonJs写法
    const { code } = transformFromAST(ast, null, {
        presets: ['@babel/preset-env']
    })
    return {
        filePath,
        code,
        dependiences
    }
}

// 广度优先算法,深入找出所有的依赖
function getAllDependencies(filePath) {
    const entryObj = readCode(filePath);
    const dependencies = [entryObj];
    for (const dependency of dependencies) {
        const curDirname = path.dirname(dependency.filePath)
        for (const relativePath dependency.dependencies) {
            const absolutePath = path.join(curDirname, relativePath);
            const child = readCode(absolutePath);
            child.relativePath = relativePath;
            dependencies.push(child);
        }
    }
    return dependencies;
}

ps: 我们用的是babel的配套工具来做语法分析和转化,但是真正的webpack用的是webassemblyjs的配套工具

拼写立即执行函数

function bundle(fileName) {
    const dependencies = getAllDependencies(fileName);
    const modulesStr = '';
    dependencies.forEach(dependency => {
        const key = dependency.relativePath || dependency.filePath;
        modulesStr += `'${key}': function(module, exports, require) {
            ${ dependency.code }
        }`
    })
    return `(function(modules) {
        const installedModules = {};
        function require(id) {
            // 解决循环依赖
            if (installedModules[id]) {
                return installedModules[id].exports;
            }
            var module = installedModules[id] = {exports: {}};
            modules[id].call(module.exports, module, module.exports, require);
            return module.exports;
        }
        return require('${fileName}')
    })({${modulesStr}})`
}

猜你喜欢

转载自www.cnblogs.com/amiezhang/p/11517294.html