Printable

Contribute

The people who contribute to webpack do so for the love of open source, our users and ecosystem, and most importantly, pushing the web forward together. Because of our Open Collective model for funding and transparency, we are able to funnel support and funds through contributors, dependent projects, and the contributor and core teams. To make a donation, click the button below...

But what is the return on the investment?

Developers

The biggest core feature we'd like to provide is an enjoyable development experience. Developers like you can help by contributing to rich and vibrant documentation, issuing pull requests to help us cover niche use cases, and to help sustain what you love about webpack.

How Can I Help?

Anybody can help by doing any of the following:

Encouraging Employers

You can ask your employer to improve your workflow by leveraging webpack: an all-in-one tool for fonts, images and image optimization, and json. Explain to them how webpack will attempt to bundle your code and assets the best it can for the smallest file size, leading to speedier sites and applications.

Your Contributions

Contributing to webpack is not contributing to an exclusive club. You as a developer are contributing to the overall health of downstream projects. Hundreds, if not thousands, of projects depend on webpack and contributing will make the ecosystem better for all the users.

The remainder of this section of the site is dedicated to developers such as yourself who would like to become a part of our ever-growing community:

Executives

CTO's, VPs, and owners can help too!

Webpack is an all-in-one tool for bundling your code. It can handle fonts, images, data and more with the help of community-driven plugins and loaders. Having all of your assets be handled by one tool is immensely helpful, as you or your team can spend less time making sure a machine with many moving parts is working correctly and more time building your product.

Sponsorship

Aside from monetary assistance, companies can support webpack by:

  • Providing developers that are not actively working on a project.
  • Contributing computing power for improved CI and regression testing.

You can also encourage your developers to contribute to the ecosystem by open-sourcing webpack loaders, plugins and other utilities. And, as mentioned above, we would greatly appreciate any help in increasing our CI/CD infrastructure.

Anyone Else

To anyone else who is interested in helping our mission – e.g. venture capitalists, government entities, digital agencies, etc. – we would love for you to work with us, one of the top npm packages, to improve your product! Please don't hesitate to reach out with questions.

Community

Looking for help, have questions, or want to connect with fellow webpack contributors?

Join our Discord server for real-time support, discussions, and collaboration.

Pull requests

Documentation of webpack features and changes is now being updated as webpack evolves. An automated issue creation integration was established and proven to be effective in the recent years. When a feature gets merged, an issue with a documentation request is created in our repository and we expect to resolve it in a timely manner. This means that there are features, changes and breaking changes awaiting to be documented, reviewed and released. That said, if Pull Request's author is abandoning it for more than 30 days, we may mark such Pull Request as stale. We may take over the work that is needed to finish it. If Pull Request author grants write access to their fork to the webpack Documentation team we will commit directly to your branch and finish the work. In other cases, we may have to start over by ourselves or by delegating it to willing community members. This may render your Pull Request redundant and it might get closed as a part of the cleanup process.

Governance Overview

webpack Project Governance

webpack is an open source project that depends on contributions from the community. Anyone may contribute to the project at any time by submitting code, participating in discussions, making suggestions, or any other contribution they see fit. This document describes how various types of contributors work within the webpack project.

Roles and Responsibilities

Contributors

Contributors are community members who contribute in concrete ways to the project, most often in the form of code and/or documentation. Anyone can become a Contributor, and contributions can take many forms. There is no expectation of commitment to the project, no specific skill requirements, and no selection process.

Contributors have read-only access to source code and so submit changes via pull requests. Contributor pull requests have their contribution reviewed and merged by a TSC member. TSC members and Committers work with Contributors to review their code and prepare it for merging.

As Contributors gain experience and familiarity with the project, their profile within, and commitment to, the community will increase. At some stage, they may find themselves being nominated as either a Website Team Member or Committer by an existing Website Team Member or Committer.

Committers

Committers are community members who have shown that they are committed to the continued development of the project through ongoing engagement with the community. Committers are given push access to the project's GitHub repos and must abide by the project's Contribution Guidelines

To become a Committer:

  • One must have shown a willingness and ability to participate in the project as a team player. Typically, a potential Committer will need to show that they have an understanding of and alignment with the project, its objectives, and its strategy.
  • Committers are expected to be respectful of every community member and to work collaboratively in the spirit of inclusion.

New Committers can be nominated by any existing Committer. Once they have been nominated, there will be a vote by the TSC members.

It is important to recognize that committership is a privilege, not a right. That privilege must be earned and once earned it can be removed by the TSC members by a standard TSC motion. However, under normal circumstances committership exists for as long as the Committer wishes to continue engaging with the project.

A Committer who shows an above-average level of contribution to the project, particularly with respect to its strategic direction and long-term health, may be nominated to become a reviewer, described below.

Reviewers

Reviewers are community members who have contributed a significant amount of time to the project through triaging of issues, fixing bugs, implementing enhancements/features, and are trusted community leaders.

Reviewers may perform all of the duties of Committers, and also:

  • May merge external pull requests for accepted issues upon reviewing and approving the changes.
  • May merge their own pull requests once they have collected the feedback they deem necessary. (No pull request should be merged without at least one Committer/Reviewer/TSC member comment stating they've looked at the code.)

To become a Reviewer:

  • Work in a helpful and collaborative way with the community.
  • Have given good feedback on others' submissions and displayed an overall understanding of the code quality standards for the project.
  • Commit to being a part of the community for the long-term.

A Committer is invited to become a Reviewer by existing Reviewers and TSC members. A nomination will result in discussion and then a decision by the TSC.

Technical Steering Committee

A subset of the collaborators forms the Technical Steering Committee (TSC). The TSC has final authority over this project, including:

  • Technical direction
  • Project governance and process (including this policy)
  • Contribution policy
  • GitHub repository hosting
  • Conduct guidelines
  • Maintaining the list of collaborators

The current list of TSC members is in the project README.

The TSC Charter governs the operations of the TSC. All changes to the Charter need approval by the OpenJS Foundation Cross-Project Council (CPC).

TSC meetings

The TSC meets in a Discord conference call or Discord thread. Each year, the TSC elects a chair to run the meetings.

Any community member can create a GitHub issue asking that the TSC review something.

The TSC may invite people to take part in a non-voting capacity.

During the meeting, the TSC chair ensures that someone takes minutes. After the meeting, the TSC chair ensures that someone opens a pull request with the minutes.

The TSC seeks to resolve as many issues as possible outside meetings using the webpack's governance repository issue tracker.

The process in the issue tracker is:

  • A TSC member opens an issue explaining the proposal/issue and @-mentions @webpack/tsc.
  • The proposal passes if, after 72 hours, there are two or more TSC voting member approvals and no TSC voting member opposition.
  • If there is an extended impasse, a TSC member may make a motion for a vote.

Consensus Seeking Process

The TSC follows a Consensus Seeking decision making model.

When an agenda item has appeared to reach a consensus, the moderator will ask "Does anyone object?" as a final call for dissent from the consensus.

If an agenda item cannot reach a consensus, a TSC member can call for either a closing vote or a vote to table the issue to the next meeting. The call for a vote must be approved by a majority of the TSC or else the discussion will continue. Simple majority wins.


This document is an adaption of the Node.js project Governance Model and the ESlint project Governance Model

Charter

webpack TSC Charter

Section 0. Guiding Principle

The webpack project is part of the OpenJS Foundation. The project operates transparently, openly, collaboratively, and ethically. Project proposals, timelines, and status must not merely be open, but also easily visible to outsiders.

Section 1. Scope

The webpack project is a highly flexible and efficient module bundler for modern JavaScript applications. Its primary purpose is to transform and bundle various modules (JavaScript, CSS, images, etc.) into a format suitable for web applications. Webpack focuses on enhancing the developer experience, providing optimizations for production environments, and enabling configurations to support a wide range of use cases and edge cases for specific projects.

With this, webpack not only simplifies the process of managing application dependencies, but it also offers tools to optimize performance, maintainability, and scalability. The webpack community contributes solutions to common challenges in modern web development, helping developers streamline their workflows and build robust applications.

1.1: In-scope

  • Bundling JavaScript files and their dependencies into a single output (or multiple outputs) for browser environments
  • Handling of asset files such as CSS, images, fonts, and other static resources
  • Optimization of assets for production, including minification, compression, and tree shaking (removal of unused code)
  • Enabling hot module replacement (HMR) for faster development feedback loops
  • Configuration and extensibility through plugins and loaders
  • Support for code splitting and lazy loading to enhance performance
  • Integrating with modern JavaScript frameworks (e.g., React, Vue, Angular)
  • Providing detailed build reports and debugging tools for development and production
  • Supporting multiple output formats (e.g., AMD, CommonJS, ES Modules) for compatibility with various environments
  • Community-driven tools and best practices provided through plugins and guides
  • Technical help and discussions via community platforms such as GitHub Discussions

In addition to the main repository, webpack (bundler), the organization maintains and manages several other core projects that are integral to the ecosystem such as webpack-dev-server, webpack-cli, and webpack-contrib projects.

These repositories, along with others housed within the github.com/webpack and github.com/webpack-contrib are owned by the webpack project.

1.2: Out-of-Scope

  • Testing frameworks (e.g., unit, integration, or end-to-end testing tools).
  • APIs and tools that uses webpack in any way and are not in the webpack organization.
  • Server-side rendering directly (although it can integrate with SSR frameworks).
  • webpack-related frameworks and libraries that operate in their own capacity.

Section 2. Relationship with OpenJS Foundation CPC.

Most large, complex open source communities have both a business and a technical governance model. Technical leadership for the projects within the OpenJS Foundation is delegated to the projects through their project charters by the OpenJS Cross Project Council (CPC). In the case of the webpack project, it is delegated to the webpack Technical Steering Committee ("TSC"). OpenJS Foundation's business leadership is the Board of Directors (the "Board").

This charter can only be amended with the approval of the CPC.

Section 3. Establishment of the TSC

TSC members can be either regular members or voting members. Regular members can attend meetings and participate in TSC discussions, but do not vote. Voting members can do everything regular members can do, and also have the ability to cast votes when consensus is not reached on an issue.

TSC memberships are not time-limited. There is no maximum size of the TSC. The TSC must have at least four voting members.

The TSC may add additional voting members to the TSC through meeting consensus. The consensus requires at least five TSC members to be present to approve a new voting member. A TSC member can be removed from the TSC by voluntary resignation or by consensus with at least five voting members to be present. A vote in a TSC meeting can be used to change a regular TSC member to a voting TSC member, or to change a voting TSC member to a regular TSC member.

No more than one-fourth of the TSC voting members may be affiliated with the same employer. If a change in TSC voting membership or a change of employment by a TSC voting member creates a situation where more than one-fourth of the TSC voting membership shares an employer, then the situation must be immediately remedied by the removal of voting member status from one or more TSC voting members affiliated with the over-represented employer(s).

The TSC shall meet regularly using tools that enable participation by the community (e.g. weekly on a Google Hangout On Air, or through any other appropriate means selected by the TSC). The meeting shall be directed by the TSC Chairperson. Responsibility for directing individual meetings may be delegated by the TSC Chairperson to any other TSC voting member. Minutes or an appropriate recording shall be taken and made available to the community through accessible public postings.

TSC voting members are expected to regularly participate in TSC meetings and issue triaging.

A TSC voting member is automatically converted to a TSC regular member if they do not participate in three consecutive TSC votes.

Section 4. Roles & Responsibilities of the TSC

Subject to such policies as may be set by the CPC, the TSC voting members are responsible for all technical development within the webpack project, including:

  • Setting release dates.
  • Release quality standards.
  • Technical direction.
  • Project governance and process.
  • GitHub repository management.
  • Conduct and maintain guidelines defined by the OpenJS Foundation Cross Project Council.
  • Maintaining the list of additional collaborators.
  • Development process and any coding standards.
  • Mediating technical conflicts between collaborators or webpack projects.

The TSC voting members will define webpack project's release vehicles.

Section 4.1. webpack Project Operations

The TSC voting members will establish and maintain a development process for the webpack project. The development process will establish guidelines for how the developers and community will operate. It will, for example, establish appropriate timelines for TSC review (e.g. agenda items must be published at least a certain number of hours in advance of a TSC meeting).

Note that project operations remain subject to any relevant policy or process specified by the OpenJS Foundation board or Cross Project Council.

Section 4.2. Decision-making, Voting, and/or Elections

Section 4.2.1. Elections

These processes are described in webpack's GOVERNANCE document.

Section 4.2.2. Decision Making

For webpack projects' decisions, Collaborators and TSC voting members shall operate under Lazy Consensus (consensus seeking). When an agenda item has appeared to reach a consensus, the moderator will ask "Does anyone object?" as a final call for dissent from the consensus. For all votes, a simple majority of all TSC voting members for, or against, the issue wins. A TSC voting member may choose to participate in any vote through abstention. Votes are needed to add new members, remove members from the TSC or deciding in project critical issues, such as roadmapping major versions of webpack core.

Section 5. Definitions

  • Project: a technical collaboration effort, e.g. a subsystem, that is organized through the webpack organization creation.

This work is a derivative of the Node.js Project TSC Charter.

Writer's Guide

The following sections contain all you need to know about editing and formatting the content within this site. Make sure to do some research before starting your edits or additions. Sometimes the toughest part is finding where the content should live and determining whether or not it already exists.

Process

  1. Check related issue if an article links to one.
  2. Hit edit and expand on the structure.
  3. PR changes.

YAML Frontmatter

Each article contains a small section at the top written in YAML Frontmatter:

---
title: My Article
group: My Sub-Section
sort: 3
contributors:
  - [github username]
related:
  - title: Title of Related Article
    url: [url of related article]
---

Let's break these down:

  • title: The name of the article.
  • group: The name of the sub-section
  • sort: The order of the article within its section (or) sub-section if it is present.
  • contributors: A list of GitHub usernames who have contributed to this article.
  • related: Any related reading or useful examples.

Note that related will generate a Further Reading section at the bottom of the page and contributors will yield a Contributors section below it. If you edit an article and would like recognition, don't hesitate to add your GitHub username to the contributors list.

Article Structure

  1. Brief Introduction - a paragraph or two so you get the basic idea about the what and why.
  2. Outline Remaining Content – how the content will be presented.
  3. Main Content - tell what you promised to tell.
  4. Conclusion - tell what you told and recap the main points.

Typesetting

  • Webpack can be written with a capital W at the beginning of a sentence. (source)
  • loaders are enclosed in backticks and kebab-cased: css-loader, ts-loader, …
  • plugins are enclosed in backticks and camel-cased: BannerPlugin, NpmInstallWebpackPlugin, …
  • Use "webpack 2" to refer to a specific webpack version ("webpack v2")
  • Use ES5; ES2015, ES2016, … to refer to the ECMAScript standards (ES6, ES7)

Formatting

Code

Syntax: ```javascript … ```

function foo() {
  return 'bar';
}

foo();

Quotation

Use single quotes in code snippets and project files (.jsx, .scss etc):

- import webpack from "webpack";
+ import webpack from 'webpack';

And in inline backticks:

correct

Set value to 'index.md'...

incorrect

Set value to "index.md"...

Lists

  • Boo
  • Foo
  • Zoo

Lists should be ordered alphabetically.

Tables

ParameterExplanationInput TypeDefault Value
--debugSwitch loaders to debug modebooleanfalse
--devtoolDefine source map type for the bundled resourcesstring-
--progressPrint compilation progress in percentagebooleanfalse

Tables should also be ordered alphabetically.

Configuration Properties

The configuration properties should be ordered alphabetically as well:

  • devServer.compress
  • devServer.hot
  • devServer.static

Quotes

Blockquote

Syntax: >

This is a blockquote.

Tip

Syntax: T>

Syntax: W>

Syntax: ?>

Assumptions and simplicity

Do not make assumptions when writing the documentation.

- You might already know how to optimize bundle for production...
+ As we've learned in [production guide](/guides/production/)...

Please do not assume things are simple. Avoid words like 'just', 'simply'.

- Simply run command...
+ Run the `command-name` command...

Configuration defaults and types

Always provide types and defaults to all of the documentation options in order to keep the documentation accessible and well-written. We are adding types and defaults after entitling the documented option:

configuration.example.option

string = 'none'

Where = 'none' means that the default value is 'none' for the given option.

string = 'none': 'none' | 'development' | 'production'

Where : 'none' | 'development' | 'production' enumerates the possible type values, in this case, three strings are acceptable: 'none', 'development', and 'production'.

Use space between types to list all available types for the given option:

string = 'none': 'none' | 'development' | 'production' boolean

To mark an array, use square brackets:

string [string]

If multiple types are allowed in array, use comma:

string [string, RegExp, function(arg) => string]

To mark a function, also list arguments when they are available:

function (compilation, module, path) => boolean

Where (compilation, module, path) lists the arguments that the provided function will receive and => boolean means that the return value of the function must be a boolean.

To mark a Plugin as an available option value type, use the camel case title of the Plugin:

TerserPlugin [TerserPlugin]

Which means that the option expects one or few TerserPlugin instances.

To mark a number, use number:

number = 15: 5, 15, 30

To mark an object, use object:

object = { prop1 string = 'none': 'none' | 'development' | 'production', prop2 boolean = false, prop3 function (module) => string }

When object's key can have multiple types, use | to list them. Here is an example, where prop1 can be both a string and an array of strings:

object = { prop1 string = 'none': 'none' | 'development' | 'production' | [string]}

This allows us to display the defaults, enumeration and other information.

If the object's key is dynamic, user-defined, use <key> to describe it:

object = { <key> string }

Options shortlists and their typing

Sometimes, we want to describe certain properties of objects and functions in lists. When applicable add typing directly to the list where properties are enlisted:

  • madeUp (boolean = true): short description
  • shortText (string = 'i am text'): another short description

An example can be found on the options section of the EvalSourceMapDevToolPlugin's page.

Adding links

Please use relative URLs (such as /concepts/mode/) to link our own content instead of absolute URLs (such as https://webpack.js.org/concepts/mode/).

Member Expectations

All participants in the webpack project must follow the Code of Conduct. There are further expectations for members of the TSC.

When decisions are made within the established guidelines and policies of the project, those in leadership roles have a responsibility to uphold and respect the decision even if they disagree with it. This is especially important in external communications, for example in social media. Should the member be unwilling or unable to do so, then they should resign their leadership position. This does not mean that decisions cannot be revisited and discussed within the team at a later time.

Everyone participating in the webpack project must conduct themselves in a professional and respectful manner in accordance with our Code of Conduct. In addition, some general guidelines for leadership group members include:

  • Remediate quickly when you realize you made a mistake. Leaders are human, and they will make mistakes. However they should act swiftly to acknowledge mistakes and correct them.
  • Aim to remediate first and then discuss. If other members of the team express concerns about actions, acknowledge their concerns by stopping the actions in question and then discuss within the team to come to a common agreement.
  • Treat all community members with respect, consideration, and highest standards of ethical conduct.
  • Value a diversity of views and opinions. Avoid preferential treatment, and hold everyone (including ourselves) accountable to the same set of standards. Everyone gets to speak up.
  • Deal with issues directly with the person in question. Resist complaining about others in the project in a public sphere.
  • Build trust by keeping your promises.
  • Be the model of accountability and leadership. Provide the example of ownership and stewardship that everyone can follow to success.
  • Commit to ongoing development and learning best practices for governing.
  • Criticize ideas rather than people, discussing any concerns in person whenever possible, and taking responsibility for our statements.

While the guidelines above focus primarily on the spaces where we participate in official foundation work (GitHub, IRC, meetings, conferences), it is important to recognize that the public behavior of members also reflects on the webpack project.

If you're interested in an introduction to diversity, inclusion, and unconscious bias, try this free training offered by our partners at the Linux Foundation.

If it appears that any member of the project leadership is acting outside of the expectations set above please refer to our moderation policy which outlines the process of making an official complaint.


This document is an adaption of the Node.js project Member Expectations

Writing a Loader

A loader is a node module that exports a function. This function is called when a resource should be transformed by this loader. The given function will have access to the Loader API using the this context provided to it.

Setup

Before we dig into the different types of loaders, their usage, and examples, let's take a look at the three ways you can develop and test a loader locally.

To test a single loader, you can use path to resolve a local file within a rule object:

webpack.config.js

const path = require('path');

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: path.resolve('path/to/loader.js'),
            options: {
              /* ... */
            },
          },
        ],
      },
    ],
  },
};

To test multiple, you can utilize the resolveLoader.modules configuration to update where webpack will search for loaders. For example, if you had a local /loaders directory in your project:

webpack.config.js

const path = require('path');

module.exports = {
  //...
  resolveLoader: {
    modules: ['node_modules', path.resolve(__dirname, 'loaders')],
  },
};

By the way, if you've already created a separate repository and package for your loader, you could npm link it to the project in which you'd like to test it out.

Simple Usage

When a single loader is applied to the resource, the loader is called with only one parameter – a string containing the content of the resource file.

Synchronous loaders can return a single value representing the transformed module. In more complex cases, the loader can return any number of values by using the this.callback(err, values...) function. Errors are either passed to the this.callback function or thrown in a sync loader.

The loader is expected to give back one or two values. The first value is a resulting JavaScript code as string or buffer. The second optional value is a SourceMap as JavaScript object.

Complex Usage

When multiple loaders are chained, it is important to remember that they are executed in reverse order – either right to left or bottom to top depending on array format.

  • The last loader, called first, will be passed the contents of the raw resource.
  • The first loader, called last, is expected to return JavaScript and an optional source map.
  • The loaders in between will be executed with the result(s) of the previous loader in the chain.

In the following example, the foo-loader would be passed the raw resource and the bar-loader would receive the output of the foo-loader and return the final transformed module and a source map if necessary.

webpack.config.js

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js/,
        use: ['bar-loader', 'foo-loader'],
      },
    ],
  },
};

Guidelines

The following guidelines should be followed when writing a loader. They are ordered in terms of importance and some only apply in certain scenarios, read the detailed sections that follow for more information.

  • Keep them simple.
  • Utilize chaining.
  • Emit modular output.
  • Make sure they're stateless.
  • Employ loader utilities.
  • Mark loader dependencies.
  • Resolve module dependencies.
  • Extract common code.
  • Avoid absolute paths.
  • Use peer dependencies.

Simple

Loaders should do only a single task. This not only makes the job of maintaining each loader easier, but also allows them to be chained for usage in more scenarios.

Chaining

Take advantage of the fact that loaders can be chained together. Instead of writing a single loader that tackles five tasks, write five simpler loaders that divide this effort. Isolating them not only keeps each individual loader simple, but may allow for them to be used for something you hadn't thought of originally.

Take the case of rendering a template file with data specified via loader options or query parameters. It could be written as a single loader that compiles the template from source, executes it and returns a module that exports a string containing the HTML code. However, in accordance with guidelines, an apply-loader exists that can be chained with other open source loaders:

  • pug-loader: Convert template to a module that exports a function.
  • apply-loader: Executes the function with loader options and returns raw HTML.
  • html-loader: Accepts HTML and outputs a valid JavaScript module.

Modular

Keep the output modular. Loader generated modules should respect the same design principles as normal modules.

Stateless

Make sure the loader does not retain state between module transformations. Each run should always be independent of other compiled modules as well as previous compilations of the same module.

Loader Utilities

Take advantage of the loader-utils package which provides a variety of useful tools. Along with loader-utils, the schema-utils package should be used for consistent JSON Schema based validation of loader options. Here's a brief example that utilizes both:

loader.js

import { urlToRequest } from 'loader-utils';
import { validate } from 'schema-utils';

const schema = {
  type: 'object',
  properties: {
    test: {
      type: 'string',
    },
  },
};

export default function (source) {
  const options = this.getOptions();

  validate(schema, options, {
    name: 'Example Loader',
    baseDataPath: 'options',
  });

  console.log('The request path', urlToRequest(this.resourcePath));

  // Apply some transformations to the source...

  return `export default ${JSON.stringify(source)}`;
}

Data Sharing

In webpack, loaders can be chained together and share data with subsequent loaders in the chain. To achieve this, you can pass data along with the content (source code) using the this.callback method in raw loaders. In the default exported function of a raw loader, you can pass data using the fourth argument of this.callback.

export default function (source) {
  const options = getOptions(this);
  // Pass data using the fourth argument of this.callback
  this.callback(null, `export default ${JSON.stringify(source)}`, null, {
    some: data,
  });
}

In the example above, some property in the fourth argument of this.callback is used to pass data to the next chained loader.

Loader Dependencies

If a loader uses external resources (i.e. by reading from filesystem), they must indicate it. This information is used to invalidate cacheable loaders and recompile in watch mode. Here's a brief example of how to accomplish this using the addDependency method:

loader.js

import path from 'path';

export default function (source) {
  var callback = this.async();
  var headerPath = path.resolve('header.js');

  this.addDependency(headerPath);

  fs.readFile(headerPath, 'utf-8', function (err, header) {
    if (err) return callback(err);
    callback(null, header + '\n' + source);
  });
}

Module Dependencies

Depending on the type of module, there may be a different schema used to specify dependencies. In CSS for example, the @import and url(...) statements are used. These dependencies should be resolved by the module system.

This can be done in one of two ways:

  • By transforming them to require statements.
  • Using the this.resolve function to resolve the path.

The css-loader is a good example of the first approach. It transforms dependencies to requires, by replacing @import statements with a require to the other stylesheet and url(...) with a require to the referenced file.

In the case of the less-loader, it cannot transform each @import to a require because all .less files must be compiled in one pass for variables and mixin tracking. Therefore, the less-loader extends the less compiler with custom path resolving logic. It then takes advantage of the second approach, this.resolve, to resolve the dependency through webpack.

Common Code

Avoid generating common code in every module the loader processes. Instead, create a runtime file in the loader and generate a require to that shared module:

src/loader-runtime.js

const { someOtherModule } = require('./some-other-module');

module.exports = function runtime(params) {
  const x = params.y * 2;

  return someOtherModule(params, x);
};

src/loader.js

import runtime from './loader-runtime.js';

export default function loader(source) {
  // Custom loader logic

  return `${runtime({
    source,
    y: Math.random(),
  })}`;
}

Absolute Paths

Don't insert absolute paths into the module code as they break hashing when the root for the project is moved. You can use below code to convert absolute paths to relative ones.

// `loaderContext` is same as `this` inside loader function
JSON.stringify(
  loaderContext.utils.contextify(
    loaderContext.context || loaderContext.rootContext,
    request
  )
);

Peer Dependencies

If the loader you're working on is a simple wrapper around another package, then you should include the package as a peerDependency. This approach allows the application's developer to specify the exact version in the package.json if desired.

For instance, the sass-loader specifies node-sass as peer dependency like so:

{
  "peerDependencies": {
    "node-sass": "^4.0.0"
  }
}

Testing

So you've written a loader, followed the guidelines above, and have it set up to run locally. What's next? Let's go through a unit testing example to ensure our loader is working the way we expect. We'll be using the Jest framework to do this. We'll also install babel-jest and some presets that will allow us to use the import / export and async / await. Let's start by installing and saving these as a devDependencies:

npm install --save-dev jest babel-jest @babel/core @babel/preset-env

babel.config.js

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        targets: {
          node: 'current',
        },
      },
    ],
  ],
};

Our loader will process .txt files and replace any instance of [name] with the name option given to the loader. Then it will output a valid JavaScript module containing the text as its default export:

src/loader.js

export default function loader(source) {
  const options = this.getOptions();

  source = source.replace(/\[name\]/g, options.name);

  return `export default ${JSON.stringify(source)}`;
}

We'll use this loader to process the following file:

test/example.txt

Hey [name]!

Pay close attention to this next step as we'll be using the Node.js API and memfs to execute webpack. This lets us avoid emitting output to disk and will give us access to the stats data which we can use to grab our transformed module:

npm install --save-dev webpack memfs

test/compiler.js

import path from 'path';
import webpack from 'webpack';
import { createFsFromVolume, Volume } from 'memfs';

export default (fixture, options = {}) => {
  const compiler = webpack({
    context: __dirname,
    entry: `./${fixture}`,
    output: {
      path: path.resolve(__dirname),
      filename: 'bundle.js',
    },
    module: {
      rules: [
        {
          test: /\.txt$/,
          use: {
            loader: path.resolve(__dirname, '../src/loader.js'),
            options,
          },
        },
      ],
    },
  });

  compiler.outputFileSystem = createFsFromVolume(new Volume());
  compiler.outputFileSystem.join = path.join.bind(path);

  return new Promise((resolve, reject) => {
    compiler.run((err, stats) => {
      if (err) reject(err);
      if (stats.hasErrors()) reject(stats.toJson().errors);

      resolve(stats);
    });
  });
};

And now, finally, we can write our test and add an npm script to run it:

test/loader.test.js

/**
 * @jest-environment node
 */
import compiler from './compiler.js';

test('Inserts name and outputs JavaScript', async () => {
  const stats = await compiler('example.txt', { name: 'Alice' });
  const output = stats.toJson({ source: true }).modules[0].source;

  expect(output).toBe('export default "Hey Alice!\\n"');
});

package.json

{
  "scripts": {
    "test": "jest"
  },
  "jest": {
    "testEnvironment": "node"
  }
}

With everything in place, we can run it and see if our new loader passes the test:

 PASS  test/loader.test.js
  ✓ Inserts name and outputs JavaScript (229ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.853s, estimated 2s
Ran all test suites.

It worked! At this point you should be ready to start developing, testing, and deploying your own loaders. We hope that you'll share your creations with the rest of the community!

Moderation Policy

Moderation policy

If you are not a member of the webpack GitHub Organization and wish to submit a moderation request, please see Requesting Moderation

Applicability

This policy applies to all repositories under the webpack and webpack-contrib GitHub Organizations and all webpack Working Groups. This policy also applies to the webpack Slack Community, supported by the Admin team of the Slack organization.

Terms

  • Collaborator refers to any individual with configured triage role or higher in any webpack GitHub organizations repositories. See GitHub's Repository roles documentation for more information.
  • TSC refers to the webpack Technical Steering Committee.
  • Post refers to the content and titles of any issue, pull request, comment, discussion, or wiki page.
  • Moderate means to modify, lock, or delete one or more Posts to correct or address Code of Conduct violations.
  • Remove refers to the act of removing the configured write (commit) permissions for an individual Collaborator's GitHub account from all webpack GitHub Organizations repositories as well as removing the account from the webpack GitHub Organizations membership.
  • Block refers to the act of prohibiting an individual GitHub account from any further participation in the webpack GitHub Organizations. A block may be temporary or indefinite.
  • Requester refers to an individual requesting Moderation on a Post.

Grounds for Moderation

Any Post in violation of the webpack Code of Conduct is subject to Moderation.

The TSC is responsible for deciding what constitutes inappropriate behavior that may be subject to Moderation.

Requesting Moderation

Anyone may request Moderation of a Post. Requesting Moderation of a Post can be accomplished in one of four ways:

  • Via the webpack-security@openjsf.org email address,
  • Via private email to individual TSC members,
  • Via a new Post in the same thread as the Post being requested for Moderation,
  • Via a new Post in the private webpack/moderation repository.

Note that Collaborators may Moderate non-Collaborator Posts at any time without submitting an initial request (see: Non-Collaborator Posts).

Use of the webpack-security@openjsf.org email address -- or private email to individual TSC members -- is appropriate when the individual requesting the Moderation does not feel comfortable directly or publicly making the request. All emails sent to the webpack-security@openjsf.org address are currently forwarded to all members of the TSC.

Requests should contain as much information and context as possible, including the URL and a screenshot of the Post in question. Screenshots may be modified to obscure obscene or offensive content.

External public venues or social media services such as Twitter must never be used to request Moderation.

Collaborators must never discuss the specific details of a Moderation request in any public forum or any social media service outside of the webpack GitHub Organization.

Note that quoting the original content of a Post within a Moderation request or webpack/moderation repository issue is not a violation of the Code of Conduct. However, discretion is advised when including such quotes in requests posted to public repositories.

Requests for Moderation that do not appear to have been submitted in good faith with intent to address a legitimate Code of Conduct violation will be ignored.

Consideration of Intent

Before Moderating a Post, Collaborators should carefully consider the possible intent of the author. It may be that the author has simply made an error or is not yet familiar with the Code of Conduct; or it may be that cultural differences exist, or that the author is unaware that certain content is inappropriate. In such cases, the author should be given an opportunity to correct any error that may have been made.

Note, however, that unfamiliarity with the Code of Conduct does not excuse a Post from Moderation.

Guidelines and Requirements

  • All Posts are expected to respect the webpack Code of Conduct.
  • Any Collaborator with triage permission to a given repository (except webpack/moderation) may Moderate Posts within that repository's issue tracker. Only the TSC is allowed to moderate posts on webpack/moderation.
  • The TSC serves as the final arbiter for all Moderation issues.
  • TSC members may Remove or Block an individual from the webpack GitHub Organizations.
  • For any Removal or Blocking action, an issue describing the reasons for the action, and identifying the Github account being acted upon, must be posted to the Moderation Repository with an explanation provided by the Moderation Team member performing the action.
  • Any individual Blocked from the webpack GitHub Organizations will be recommended for exclusion from any webpack Foundation sponsored event or activity.
  • Minor edits to the formatting of a Post or to correct typographical errors are not "Moderation". Such edits and their intent must still be documented with a short note indicating who made the edit and why.

Collaborator Posts

  • Prior to Moderating any Post authored by a Collaborator, the author must be given a reasonable opportunity to modify or remove the Post on their own.
  • If the author of the Post disagrees that Moderation is required, the matter can be escalated to the TSC for resolution. In such cases, no Moderation action may be taken until a decision by the TSC is made.
  • When Moderating any Post authored by another Collaborator, the moderating Collaborator must:
    • Explain the justification for Moderating the post,
    • Identify all changes made to the Post, and
    • Identify the steps previously taken to resolve the issue.
    • If the Moderation action included Blocking, an indication of whether the Block is temporary or indefinite is required, along with an issue posted to the moderation repository justifying the action.
  • Explanations of Moderation actions on Collaborator Posts must be provided in:
    • A new post within the original thread, or
    • A new issue within the private webpack/moderation repository.
  • Any Collaborator habitually violating the Code of Conduct or this Moderation policy may be Blocked temporarily or, in extreme cases, Removed and Blocked indefinitely.

Non-Collaborator Posts

  • Posts authored by non-Collaborators are always subject to immediate Moderation by any Collaborator if the content is intentionally disruptive or in violation of the Code of Conduct.
  • When moderating non-Collaborator posts, the moderating Collaborator must:
    • Explain the justification for Moderating the post, and
    • Identify all changes made to the Post.
    • If the Moderation action included Blocking, an indication of whether the Block is temporary or indefinite is required, along with a note justifying the action.
  • If an explanation of a Moderation action for a non-Collaborator Post is provided, it must be provided in:
    • The original Post being modified (as replacement or appended content),
    • A new post within the original thread, or
    • A new issue within the private webpack/moderation repository.
  • Moderation of Posts authored by non-Collaborators may result in those non-Collaborators being Blocked temporarily or indefinitely from further participation in the webpack GitHub organizations.
  • If it is clear that there is no intention to collaborate in good faith, it is possible to hide comments of non-Collaborators. In that case there is an exception to the reporting requirement described above.
  • Accounts that are reasonably believed to be bots (other than bots authorized by the TSC) are subject to immediate Blocking.
  • Issues, pull requests, discussions, and comments that are spam (job posting, service advertising, etc.) are subject to immediate moderation.
  • Issues, pull requests, discussions, and comments that are believed to be LLM-generated (e.g a PR coming from a new contributor changing a single file without clear motivation) should be closed with a comment such as "It seems you are using a LLM, please stop, this is not bringing any value and is wasting our time. If you are not using one, please read and follow our contributing guidelines." Report the user to the moderation repository so they get blocked if they do it again.
  • Collaborators may use the Hide feature in the GitHub interface for off-topic posts by non-Collaborators.
  • TSC members can delete any issues or comments posted by accounts that have been deleted by GitHub. These accounts show up in the GitHub interface as user ghost. There is no need to screenshot or document these deletions.

There are a few examples of moderating non-Collaborator posts:

Scenario 1:

  • A non-Collaborator posts a comment that indicates that they are a bot.
  • A collaborator sees the post and hides it.
  • No further action is necessary.

Scenario 2:

  • A non-Collaborator posts a comment that is against the Code of Conduct.
  • A Collaborator sees the comment and asks the author to edit it.
  • The author refuses to edit their comment.
  • The Collaborator deletes the comment and posts an issue in the moderation repository explaining their actions.

Scenario 3:

  • A non-Collaborator opens a pull request with comments indicating they are a bot.
  • A Collaborator sees that pull requests, closes it, deletes the comments and posts an issue in the moderation repository explaining their actions.
  • A TSC member sees the issue and decides to block the user from the organization.

Scenario 4:

  • A non-Collaborator posts a comment on an old commit that is against the Code of Conduct.
  • A Collaborator sees the comment, takes a screenshot, and deletes it.
  • The Collaborator posts an issue in the moderation repository explaining their actions.

Temporary Interaction Limits

The TSC may, at their discretion, choose to enable GitHub's Temporary Interaction Limits on any GitHub repository in the webpack GitHub Organization.

Any Collaborator may request that the TSC enable the Temporary Interaction Limits by posting an issue to the moderation repository. If the TSC chooses not to do so, then a comment explaining why that decision was made must be added to the moderation repository thread.

Temporary and Indefinite Blocks

A Temporary Block is time limited, with the timeframe decided on by the Moderation Team at the time of issuing, depending on the severity of the issue. Recommended default options are 24-hour, 48-hour, and 7-day periods.

An Indefinite Block is set for an unspecified period of time and may only be lifted for an individual through a simple majority vote of the Moderation Team.

Privacy of the webpack/moderation Repository

The webpack/moderation Repository is used to discuss the potentially sensitive details of any specific moderation issue. The repository is private but accessible to all Collaborators. The details of any issue discussed within the webpack/moderation repository are expected to remain confidential and are not to be discussed in any public forum or social media service.

Any Collaborator found to be violating the privacy of the webpack/moderation repository by repeatedly sharing or discussing the details of webpack/moderation issues in any public forum or social media service risks being permanently removed from the webpack GitHub organizations.

Escalation of Issues

Moderation issue disputes not involving a TSC member may be escalated to the TSC for review by tagging the original issue, pull request, or associated webpack/moderation repository. Any such Moderation action may be overturned through a TSC vote.

TSC members directly involved in a Moderation issue (as either the Requester or author of the Post in question) are required to recuse themselves from any decisions required to resolve the issue.

Moderation disputes involving TSC members, including questions of whether a TSC member has violated the Code of Conduct, shall be referred to an Independent Mediator selected by the OpenJS Foundation.

Modifications to This Policy

Modifications to this policy are subject to approval by the TSC. When modifications are proposed, if there are no objections after 72 hours, the modifications are accepted. If there any objections to any proposed change, a TSC vote in favor of the change is required.


This document is an adaption of the Node.js project Moderation Policy

Writing a Plugin

Plugins expose the full potential of the webpack engine to third-party developers. Using staged build callbacks, developers can introduce their own behaviors into the webpack build process. Building plugins is a bit more advanced than building loaders, because you'll need to understand some of the webpack low-level internals to hook into them. Be prepared to read some source code!

Creating a Plugin

A plugin for webpack consists of:

  • A named JavaScript function or a JavaScript class.
  • Defines apply method in its prototype.
  • Specifies an event hook to tap into.
  • Manipulates webpack internal instance specific data.
  • Invokes webpack provided callback after functionality is complete.
// A JavaScript class.
class MyExampleWebpackPlugin {
  // Define `apply` as its prototype method which is supplied with compiler as its argument
  apply(compiler) {
    // Specify the event hook to attach to
    compiler.hooks.emit.tapAsync(
      'MyExampleWebpackPlugin',
      (compilation, callback) => {
        console.log('This is an example plugin!');
        console.log(
          'Here’s the `compilation` object which represents a single build of assets:',
          compilation
        );

        // Manipulate the build using the plugin API provided by webpack
        compilation.addModule(/* ... */);

        callback();
      }
    );
  }
}

Basic plugin architecture

Plugins are instantiated objects with an apply method on their prototype. This apply method is called once by the webpack compiler while installing the plugin. The apply method is given a reference to the underlying webpack compiler, which grants access to compiler callbacks. A plugin is structured as follows:

class HelloWorldPlugin {
  apply(compiler) {
    compiler.hooks.done.tap(
      'Hello World Plugin',
      (
        stats /* stats is passed as an argument when done hook is tapped.  */
      ) => {
        console.log('Hello World!');
      }
    );
  }
}

module.exports = HelloWorldPlugin;

Then to use the plugin, include an instance in your webpack configuration plugins array:

// webpack.config.js
var HelloWorldPlugin = require('hello-world');

module.exports = {
  // ... configuration settings here ...
  plugins: [new HelloWorldPlugin({ options: true })],
};

Use schema-utils in order to validate the options being passed through the plugin options. Here is an example:

import { validate } from 'schema-utils';

// schema for options object
const schema = {
  type: 'object',
  properties: {
    test: {
      type: 'string',
    },
  },
};

export default class HelloWorldPlugin {
  constructor(options = {}) {
    validate(schema, options, {
      name: 'Hello World Plugin',
      baseDataPath: 'options',
    });
  }

  apply(compiler) {}
}

Compiler and Compilation

Among the two most important resources while developing plugins are the compiler and compilation objects. Understanding their roles is an important first step in extending the webpack engine.

class HelloCompilationPlugin {
  apply(compiler) {
    // Tap into compilation hook which gives compilation as argument to the callback function
    compiler.hooks.compilation.tap('HelloCompilationPlugin', (compilation) => {
      // Now we can tap into various hooks available through compilation
      compilation.hooks.optimize.tap('HelloCompilationPlugin', () => {
        console.log('Assets are being optimized.');
      });
    });
  }
}

module.exports = HelloCompilationPlugin;

For the list of hooks available on compiler, compilation, and other important objects, see the plugins API docs.

Async event hooks

Some plugin hooks are asynchronous. To tap into them, we can use tap method which will behave in synchronous manner or use one of tapAsync method or tapPromise method which are asynchronous methods.

tapAsync

When we use tapAsync method to tap into plugins, we need to call the callback function which is supplied as the last argument to our function.

class HelloAsyncPlugin {
  apply(compiler) {
    compiler.hooks.emit.tapAsync(
      'HelloAsyncPlugin',
      (compilation, callback) => {
        // Do something async...
        setTimeout(function () {
          console.log('Done with async work...');
          callback();
        }, 1000);
      }
    );
  }
}

module.exports = HelloAsyncPlugin;

tapPromise

When we use tapPromise method to tap into plugins, we need to return a promise which resolves when our asynchronous task is completed.

class HelloAsyncPlugin {
  apply(compiler) {
    compiler.hooks.emit.tapPromise('HelloAsyncPlugin', (compilation) => {
      // return a Promise that resolves when we are done...
      return new Promise((resolve, reject) => {
        setTimeout(function () {
          console.log('Done with async work...');
          resolve();
        }, 1000);
      });
    });
  }
}

module.exports = HelloAsyncPlugin;

Example

Once we can latch onto the webpack compiler and each individual compilations, the possibilities become endless for what we can do with the engine itself. We can reformat existing files, create derivative files, or fabricate entirely new assets.

Let's write an example plugin that generates a new build file called assets.md, the contents of which will list all of the asset files in our build. This plugin might look something like this:

class FileListPlugin {
  static defaultOptions = {
    outputFile: 'assets.md',
  };

  // Any options should be passed in the constructor of your plugin,
  // (this is a public API of your plugin).
  constructor(options = {}) {
    // Applying user-specified options over the default options
    // and making merged options further available to the plugin methods.
    // You should probably validate all the options here as well.
    this.options = { ...FileListPlugin.defaultOptions, ...options };
  }

  apply(compiler) {
    const pluginName = FileListPlugin.name;

    // webpack module instance can be accessed from the compiler object,
    // this ensures that correct version of the module is used
    // (do not require/import the webpack or any symbols from it directly).
    const { webpack } = compiler;

    // Compilation object gives us reference to some useful constants.
    const { Compilation } = webpack;

    // RawSource is one of the "sources" classes that should be used
    // to represent asset sources in compilation.
    const { RawSource } = webpack.sources;

    // Tapping to the "thisCompilation" hook in order to further tap
    // to the compilation process on an earlier stage.
    compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
      // Tapping to the assets processing pipeline on a specific stage.
      compilation.hooks.processAssets.tap(
        {
          name: pluginName,

          // Using one of the later asset processing stages to ensure
          // that all assets were already added to the compilation by other plugins.
          stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE,
        },
        (assets) => {
          // "assets" is an object that contains all assets
          // in the compilation, the keys of the object are pathnames of the assets
          // and the values are file sources.

          // Iterating over all the assets and
          // generating content for our Markdown file.
          const content =
            '# In this build:\n\n' +
            Object.keys(assets)
              .map((filename) => `- ${filename}`)
              .join('\n');

          // Adding new asset to the compilation, so it would be automatically
          // generated by the webpack in the output directory.
          compilation.emitAsset(
            this.options.outputFile,
            new RawSource(content)
          );
        }
      );
    });
  }
}

module.exports = { FileListPlugin };

webpack.config.js

const { FileListPlugin } = require('./file-list-plugin.js');

// Use the plugin in your webpack configuration:
module.exports = {
  // …

  plugins: [
    // Adding the plugin with the default options
    new FileListPlugin(),

    // OR:

    // You can choose to pass any supported options to it:
    new FileListPlugin({
      outputFile: 'my-assets.md',
    }),
  ],
};

This will generate a markdown file with chosen name that looks like this:

# In this build:

- main.css
- main.js
- index.html

Different Plugin Shapes

A plugin can be classified into types based on the event hooks it taps into. Every event hook is pre-defined as synchronous or asynchronous or waterfall or parallel hook and hook is called internally using call/callAsync method. The list of hooks that are supported or can be tapped into is generally specified in this.hooks property.

For example:

this.hooks = {
  shouldEmit: new SyncBailHook(['compilation']),
};

It represents that the only hook supported is shouldEmit which is a hook of SyncBailHook type and the only parameter which will be passed to any plugin that taps into shouldEmit hook is compilation.

Various types of hooks supported are :

Synchronous Hooks

  • SyncHook

    • Defined as new SyncHook([params])
    • Tapped into using tap method.
    • Called using call(...params) method.
  • Bail Hooks

    • Defined using SyncBailHook[params]
    • Tapped into using tap method.
    • Called using call(...params) method.

    In these types of hooks, each of the plugin callbacks will be invoked one after the other with the specific args. If any value is returned except undefined by any plugin, then that value is returned by hook and no further plugin callback is invoked. Many useful events like optimizeChunks, optimizeChunkModules are SyncBailHooks.

  • Waterfall Hooks

    • Defined using SyncWaterfallHook[params]
    • Tapped into using tap method.
    • Called using call(...params) method

    Here each of the plugins is called one after the other with the arguments from the return value of the previous plugin. The plugin must take the order of its execution into account. It must accept arguments from the previous plugin that was executed. The value for the first plugin is init. Hence at least 1 param must be supplied for waterfall hooks. This pattern is used in the Tapable instances which are related to the webpack templates like ModuleTemplate, ChunkTemplate etc.

Asynchronous Hooks

  • Async Series Hook

    • Defined using AsyncSeriesHook[params]
    • Tapped into using tap/tapAsync/tapPromise method.
    • Called using callAsync(...params) method

    The plugin handler functions are called with all arguments and a callback function with the signature (err?: Error) -> void. The handler functions are called in order of registration. callback is called after all the handlers are called. This is also a commonly used pattern for events like emit, run.

  • Async waterfall The plugins will be applied asynchronously in the waterfall manner.

    • Defined using AsyncWaterfallHook[params]
    • Tapped into using tap/tapAsync/tapPromise method.
    • Called using callAsync(...params) method

    The plugin handler functions are called with the current value and a callback function with the signature (err: Error, nextValue: any) -> void. When called nextValue is the current value for the next handler. The current value for the first handler is init. After all handlers are applied, callback is called with the last value. If any handler passes a value for err, the callback is called with this error and no more handlers are called. This plugin pattern is expected for events like before-resolve and after-resolve.

  • Async Series Bail

    • Defined using AsyncSeriesBailHook[params]
    • Tapped into using tap/tapAsync/tapPromise method.
    • Called using callAsync(...params) method
  • Async Parallel

    • Defined using AsyncParallelHook[params]
    • Tapped into using tap/tapAsync/tapPromise method.
    • Called using callAsync(...params) method

Configuration defaults

Webpack applies configuration defaults after plugins defaults are applied. This allows plugins to feature their own defaults and provides a way to create configuration preset plugins.

Working Groups

webpack Working Groups

webpack Working Groups are autonomous projects created by the Technical Steering Committee (TSC).

Working Groups can be formed at any time but must be ratified by the TSC. Once formed the work defined in the Working Group charter is the responsibility of the WG rather than the TSC.

It is important that Working Groups are not formed prematurely. Working Groups are not formed to begin a set of tasks but instead are formed once that work is already underway and the contributors think it would benefit from being done as an autonomous project.

If the work defined in a Working Group's charter is complete, the charter should be revoked.

A Working Group's charter can be revoked either by consent of the Working Group's members or by a TSC vote. Once revoked, any future work that arises becomes the responsibility of the TSC.

Joining a WG

To find out how to join a working group, consult the [GOVERNANCE.md][] in the working group's repository.

Starting a Working Group

A Working Group is established by first defining a charter that can be ratified by the TSC. A charter is a statement of purpose and a list of responsibilities. When requesting that a working group be chartered, it is also necessary to provide a list of initial membership.

A working group needs 3 initial members. These should be individuals already undertaking the work described in the charter.

The list of responsibilities should be specific. Once established, these responsibilities are no longer governed by the TSC and therefore should not be broad or subjective. The only recourse the TSC has over the working group is to revoke the entire charter.

If the responsibilities described in the charter are currently undertaken by another working group then the charter will additionally have to be ratified by that working group.

You can submit the working group charter for ratification by sending a pull request to this document to add the charter it to the list of current Working Groups. Once ratified, the list of members should be maintained in the Working Group's README.

Bootstrap Governance

Once the TSC ratifies a charter, the working group inherits the following documentation for governance, contribution, conduct and an MIT LICENSE. The working group is free to change these documents through their own governance process, hence the term "bootstrap."

### *[insert WG name]* Working Group

The webpack *[insert WG name]* is governed by a Working Group (WG)
that is responsible for high-level guidance of the project.

The WG has final authority over this project including:

* Technical direction
* Project governance and process (including this policy)
* Contribution policy
* GitHub repository hosting
* Conduct guidelines
* Maintaining the list of additional Collaborators

For the current list of WG members, see the project
[README.md](https://github.com/webpack/webpack/blob/main/README.md#current-project-members).

### Collaborators

The *[insert WG name]* GitHub repository is
maintained by the WG and additional Collaborators who are added by the
WG on an ongoing basis.

Individuals making significant and valuable contributions are made
Collaborators and given commit-access to the project. These
individuals are identified by the WG and their addition as
Collaborators is discussed during the weekly WG meeting.

Modifications of the contents of the *[insert WG repo]* repository are made on
a collaborative basis. Anybody with a GitHub account may propose a
modification via pull request and it will be considered by the project
Collaborators. All pull requests must be reviewed and accepted by a
Collaborator with sufficient expertise who is able to take full
responsibility for the change. In the case of pull requests proposed
by an existing Collaborator, an additional Collaborator is required
for sign-off. Consensus should be sought if additional Collaborators
participate and there is disagreement around a particular
modification. See _Consensus Seeking Process_ below for further detail
on the consensus model used for governance.

For the current list of Collaborators, see the project
[README.md](https://github.com/webpack/webpack/blob/main/README.md#current-project-members).

### WG Membership

WG seats are not time-limited.  There is no fixed size of the WG.
However, the expected target is between 6 and 12, to ensure adequate
coverage of important areas of expertise, balanced with the ability to
make decisions efficiently.

There is no specific set of requirements or qualifications for WG
membership beyond these rules.

The WG may add additional members to the WG by unanimous consensus.

A WG member may be removed from the WG by voluntary resignation, or by
unanimous consensus of all other WG members.

Changes to WG membership should be posted in the agenda, and may be
suggested as any other agenda item (see "WG Meetings" below).

If an addition or removal is proposed during a meeting, and the full
WG is not in attendance to participate, then the addition or removal
is added to the agenda for the subsequent meeting.  This is to ensure
that all members are given the opportunity to participate in all
membership decisions.  If a WG member is unable to attend a meeting
where a planned membership decision is being made, then their consent
is assumed.

No more than 1/3 of the WG members may be affiliated with the same
employer.  If removal or resignation of a WG member, or a change of
employment by a WG member, creates a situation where more than 1/3 of
the WG membership shares an employer, then the situation must be
immediately remedied by the resignation or removal of one or more WG
members affiliated with the over-represented employer(s).

### Consensus Seeking Process

The WG follows a [Consensus Seeking][] decision-making model.

If an agenda item cannot reach a consensus a WG member can call for
either a closing vote or a vote to table the issue to the next
meeting. The call for a vote must be seconded by a majority of the WG
or else the discussion will continue. Simple majority wins.

Note that changes to WG membership require unanimous consensus. See
"WG Membership" above.

<a id="developers-certificate-of-origin"></a>
## Developer's Certificate of Origin 1.1

Use of a CLA or DCO is mandatory for all all OpenJS Foundation projects.
The webpack project has chosen to use the DCO 1.1

By making a contribution to this project, I certify that:

* (a) The contribution was created in whole or in part by me and I
  have the right to submit it under the open source license
  indicated in the file; or

* (b) The contribution is based upon previous work that, to the best
  of my knowledge, is covered under an appropriate open source
  license and I have the right under that license to submit that
  work with modifications, whether created in whole or in part
  by me, under the same open source license (unless I am
  permitted to submit under a different license), as indicated
  in the file; or

* (c) The contribution was provided directly to me by some other
  person who certified (a), (b) or (c) and I have not modified
  it.

* (d) I understand and agree that this project and the contribution
  are public and that a record of the contribution (including all
  personal information I submit with it, including my sign-off) is
  maintained indefinitely and may be redistributed consistent with
  this project or the open source license(s) involved.

### Moderation Policy

The [webpack Moderation Policy] applies to this WG.

### Code of Conduct

The [webpack Code of Conduct][] applies to this WG.

[webpack Code of Conduct]: https://github.com/webpack/webpack/blob/main/CODE_OF_CONDUCT.md
[webpack Moderation Policy]: https://github.com/webpack/governance/blob/main/MODERATION_POLICY.md
[Consensus Seeking]: https://en.wikipedia.org/wiki/Consensus-seeking_decision-making

Current Working Groups

Core

The Core Working Group is responsible for the development and maintenance of the webpack bundler and its technical direction.

Responsibilities include:

  • Maintaining the core of the webpack bundler
  • Reviewing and merging pull requests
  • Managing releases and versioning
  • Ensuring code quality and consistency
  • Addressing issues and bugs reported by the community
  • Developing new features and enhancements
  • Writing and maintaining documentation
  • Coordinating with other working groups and the TSC
  • Ensuring compliance with the project's code of conduct and governance policies

This document is an adaption of the Node.js project Working Groups Charter

Plugin Patterns

Plugins grant unlimited opportunity to perform customizations within the webpack build system. This allows you to create custom asset types, perform unique build modifications, or even enhance the webpack runtime while using middleware. The following are some features of webpack that become useful while writing plugins.

Exploring assets, chunks, modules, and dependencies

After a compilation is sealed, all structures within the compilation may be traversed.

class MyPlugin {
  apply(compiler) {
    compiler.hooks.emit.tapAsync('MyPlugin', (compilation, callback) => {
      // Explore each chunk (build output):
      compilation.chunks.forEach((chunk) => {
        // Explore each module within the chunk (built inputs):
        chunk.getModules().forEach((module) => {
          // Explore each source file path that was included into the module:
          module.buildInfo &&
            module.buildInfo.fileDependencies &&
            module.buildInfo.fileDependencies.forEach((filepath) => {
              // we've learned a lot about the source structure now...
            });
        });

        // Explore each asset filename generated by the chunk:
        chunk.files.forEach((filename) => {
          // Get the asset source for each file generated by the chunk:
          var source = compilation.assets[filename].source();
        });
      });

      callback();
    });
  }
}
module.exports = MyPlugin;
  • compilation.modules: A set of modules (built inputs) in the compilation. Each module manages the build of a raw file from your source library.
  • module.fileDependencies: An array of source file paths included into a module. This includes the source JavaScript file itself (ex: index.js), and all dependency asset files (stylesheets, images, etc) that it has required. Reviewing dependencies is useful for seeing what source files belong to a module.
  • compilation.chunks: A set of chunks (build outputs) in the compilation. Each chunk manages the composition of a final rendered assets.
  • chunk.getModules(): An array of modules that are included into a chunk. By extension, you may look through each module's dependencies to see what raw source files fed into a chunk.
  • chunk.files: A Set of output filenames generated by the chunk. You may access these asset sources from the compilation.assets table.

Monitoring the watch graph

While running webpack middleware, each compilation includes a fileDependencies Set (what files are being watched) and a fileTimestamps Map that maps watched file paths to a timestamp. These are extremely useful for detecting what files have changed within the compilation:

class MyPlugin {
  constructor() {
    this.startTime = Date.now();
    this.prevTimestamps = new Map();
  }
  apply(compiler) {
    compiler.hooks.emit.tapAsync('MyPlugin', (compilation, callback) => {
      const changedFiles = Array.from(compilation.fileTimestamps.keys()).filter(
        (watchfile) => {
          return (
            (this.prevTimestamps.get(watchfile) || this.startTime) <
            (compilation.fileTimestamps.get(watchfile) || Infinity)
          );
        }
      );

      this.prevTimestamps = compilation.fileTimestamps;
      callback();
    });
  }
}

module.exports = MyPlugin;

You may also feed new file paths into the watch graph to receive compilation triggers when those files change. Add valid file paths into the compilation.fileDependencies Set to add them to the watched files.

Changed chunks

Similar to the watch graph, you can monitor changed chunks (or modules, for that matter) within a compilation by tracking their hashes.

class MyPlugin {
  constructor() {
    this.chunkVersions = {};
  }
  apply(compiler) {
    compiler.hooks.emit.tapAsync('MyPlugin', (compilation, callback) => {
      var changedChunks = compilation.chunks.filter((chunk) => {
        var oldVersion = this.chunkVersions[chunk.name];
        this.chunkVersions[chunk.name] = chunk.hash;
        return chunk.hash !== oldVersion;
      });
      callback();
    });
  }
}

module.exports = MyPlugin;

Release Process

The release process for deploying webpack is actually quite painless. Read through the following steps, so you have a clear understanding of how it's done.

Pull Requests

When merging pull requests into the main branch, select the Create Merge Commit option.

Releasing

npm version patch && git push --follow-tags && npm publish
npm version minor && git push --follow-tags && npm publish
npm version major && git push --follow-tags && npm publish

This will increment the package version, commits the changes, cuts a local tag, push to github & publish the npm package.

After that go to the github releases page and write a Changelog for the new tag.

Debugging

When contributing to the core repo, writing a loader/plugin, or even working on a complex project, debugging tools can be central to your workflow. Whether the problem is slow performance on a large project or an unhelpful traceback, the following utilities can make figuring it out less painful.

  • The stats data made available through Node and the CLI.
  • Chrome DevTools and the latest Node.js version.

Stats

Whether you want to sift through this data manually or use a tool to process it, the stats data can be extremely useful when debugging build issues. We won't go in depth here as there's an entire page dedicated to its contents, but know that you can use it to find the following information:

  • The contents of every module.
  • The modules contained within every chunk.
  • Per module compilation and resolving stats.
  • Build errors and warnings.
  • The relationships between modules.
  • And much more...

On top of that, the official analyze tool and various others will accept this data and visualize it in various ways.

DevTools

While console statements may work well in straightforward scenarios, sometimes a more robust solution is needed. As most front-end developers already know, Chrome DevTools are a life saver when debugging web applications, but they don’t have to stop there. As of Node v6.3.0+, developers can use the built-in --inspect flag to debug a node program in DevTools.

Let's start by invoking webpack with the node --inspect.

Note that we cannot run npm scripts, e.g. npm run build, so we'll have to specify the full node_modules path:

node --inspect ./node_modules/webpack/bin/webpack.js

Which should output something like:

Debugger listening on ws://127.0.0.1:9229/c624201a-250f-416e-a018-300bbec7be2c
For help see https://nodejs.org/en/docs/inspector

Now jump to chrome://inspect in the browser and you should see any active scripts you've inspected under the Remote Target header. Click the "inspect" link under each script to open a dedicated debugger or the Open dedicated DevTools for Node link for a session that will connect automatically. You can also check out the NiM extension, a handy Chrome plugin that will automatically open a DevTools tab every time you --inspect a script.

We recommend using the --inspect-brk flag which will break on the first statement of the script allowing you to go through the source to set breakpoints and start/stop the build as you please. Also, don't forget that you can still pass arguments to the script. For example, if you have multiple configuration files you could pass --config webpack.prod.js to specify the configuration you'd like to debug.

1 Contributor

webpack