Frontend Development 12 min read

Build Your Own Mini Webpack: A Step‑by‑Step Guide to Module Bundling

This article walks you through the fundamentals of abstract syntax trees, Babel, and Webpack's build process, then shows how to implement a simple mini‑webpack from scratch, install dependencies, configure the project, run the build, and verify the bundled output.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Build Your Own Mini Webpack: A Step‑by‑Step Guide to Module Bundling

Introduction

While learning Webpack, I found its module‑dependency‑graph approach fascinating and built a simple mini‑webpack that bundles entry files and their dependencies into a single runnable script.

Background Knowledge

Abstract Syntax Tree (AST)

An AST is a tree‑structured representation of source code that enables tools to understand and transform code. It powers IDE features, linting, bundlers like Webpack and Rollup, and transpilers such as Babel.

Online tools like AST Explorer let you visualise the AST of any JavaScript snippet.

Babel

Babel is a toolchain that converts modern ECMAScript (ES2015+) code into backward‑compatible JavaScript. It can:

Transform syntax

Inject missing features via polyfills (e.g.,

core‑js

)

Apply codemods to rewrite source code

In a typical Webpack setup,

babel‑loader

parses files into an AST, transforms them, and emits ES5 code.

<code>// Babel input: ES2015 arrow function
[1, 2, 3].map(n => n + 1);

// Babel output: ES5 equivalent
[1, 2, 3].map(function (n) {
  return n + 1;
});</code>

Webpack Bundling Principle

Webpack builds a project in several steps:

Read the basic configuration (

webpack.config.js

)

Analyze the entry file and its dependencies

Parse each module into an AST

Transform the AST (e.g., via Babel)

Generate a dependency graph object

Wrap the graph in a self‑executing function that provides a

require

implementation

Emit the final bundle file

<code>// mini‑webpack.config.js
const path = require('path');
module.exports = {
  entry: "./src/index.js",
  mode: "development",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "bundle.js"
  }
};</code>
<code>// Core of the generated bundle (simplified)
(function (graph) {
  function require(filename) {
    function localRequire(relativePath) {
      return require(graph[filename].dependencies[relativePath]);
    }
    const exports = {};
    (function (require, exports, code) {
      eval(code);
    })(localRequire, exports, graph[filename].code);
    return exports;
  }
  require('${entry}');
})(${graph});</code>

Implementation Details

1. Install Dependencies

Run:

<code>npm install @babel/parser @babel/traverse @babel/core @babel/preset-env -D</code>

2. Read Configuration

<code>const path = require('path');
module.exports = {
  entry: "./src/index.js",
  mode: "development",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "bundle.js"
  }
};</code>

3. Core MiniWebpack Class

<code>class MiniWebpack {
  constructor(options) {
    this.options = options;
  }

  // Parse a file into AST, collect dependencies, and transform to code
  parse = filename => {
    const fileBuffer = fs.readFileSync(filename, 'utf-8');
    const ast = parser.parse(fileBuffer, { sourceType: 'module' });
    const dependencies = {};
    traverse(ast, {
      ImportDeclaration({ node }) {
        const dirname = path.dirname(filename);
        const newDirname = './' + path.join(dirname, node.source.value).replace('\\', '/');
        dependencies[node.source.value] = newDirname;
      }
    });
    const { code } = babel.transformFromAst(ast, null, { presets: ['@babel/preset-env'] });
    return { filename, dependencies, code };
  };

  // Build the dependency graph starting from the entry
  analyse = entry => {
    const entryModule = this.parse(entry);
    const graphArray = [entryModule];
    for (let i = 0; i < graphArray.length; ++i) {
      const { dependencies } = graphArray[i];
      Object.keys(dependencies).forEach(filename => {
        graphArray.push(this.parse(dependencies[filename]));
      });
    }
    const graph = {};
    graphArray.forEach(({ filename, dependencies, code }) => {
      graph[filename] = { dependencies, code };
    });
    return graph;
  };

  // Generate bundle code from the graph
  generate = (graph, entry) => {
    return `
      (function(graph){
        function require(filename){
          function localRequire(relativePath){
            return require(graph[filename].dependencies[relativePath]);
          }
          const exports = {};
          (function(require, exports, code){
            eval(code);
          })(localRequire, exports, graph[filename].code);
          return exports;
        }
        require('${entry}');
      })(${graph})
    `;
  };

  // Write the bundle to disk
  fileOutput = (output, code) => {
    const { path: dirPath, filename } = output;
    const outputPath = path.join(dirPath, filename);
    if (!fs.existsSync(dirPath)) {
      fs.mkdirSync(dirPath);
    }
    fs.writeFileSync(outputPath, code, 'utf-8');
  };

  // Run the whole process
  run = () => {
    const { entry, output } = this.options;
    const graph = this.analyse(entry);
    const graphStr = JSON.stringify(graph);
    const code = this.generate(graphStr, entry);
    this.fileOutput(output, code);
  };
}
</code>

4. Demonstration

Create

src/a.js

,

src/b.js

, and

src/index.js

:

<code>// a.js
export default 1;

// b.js
export default function () {
  console.log('I am b');
};

// index.js
import a from './a.js';
import b from './b.js';
console.log(a);
console.log(b);
</code>

Add the configuration above to

mini-webpack.config.js

and update

package.json

with a

build

script that runs

node main.js

. Then execute:

<code>npm run build</code>

The command creates

dist/bundle.js

. Running the bundle prints the values from

a.js

and

b.js

, confirming the mini‑webpack works.

Project Repository

The source code is available at: mini-webpack

References

Implement a Simple Webpack

Babel Documentation (Chinese)

Understanding AST

Webpack Build Principles and Simple Implementation

javascriptASTBabelwebpackmodule bundlerMiniWebpack
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.