Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b0bb618
refactor: migrate to ES module syntax
bjohansebas Apr 26, 2026
a59fdba
refactor: migrate ports-map.js to ES module syntax
bjohansebas May 11, 2026
d109bb9
refactor: migrate helper files to ES module syntax
bjohansebas May 11, 2026
d7e222b
refactor: migrate fixtures configs to ES module syntax
bjohansebas May 11, 2026
250c87b
refactor: migrate test files to ES module syntax
bjohansebas May 11, 2026
e6e0957
refactor: migrate test files to ES module syntax
bjohansebas May 11, 2026
9f1b3e9
refactor: migrate test files to use fileURLToPath for __dirname
bjohansebas May 11, 2026
2dfb6d4
refactor: remove duplicate import of http in cross-origin request tests
bjohansebas May 11, 2026
899db46
refactor: update library type to module and enable outputModule exper…
bjohansebas May 11, 2026
dd31f4f
refactor: update client transport implementation checks and adjust mo…
bjohansebas May 12, 2026
5daa8f7
refactor: enhance client transport handling with pathToFileURL for ab…
bjohansebas May 12, 2026
43dfed2
fixup!
bjohansebas May 15, 2026
010b741
fixup!
bjohansebas May 15, 2026
3b30cf4
refactor: improve lazy loading of webpack peer dependency and update …
bjohansebas May 15, 2026
05a27ec
fixup!
bjohansebas May 15, 2026
4aaecfc
refactor: update lazy initialization of webpack dev middleware to async
bjohansebas May 15, 2026
c705bef
refactor: update TypeScript configuration to target ES2024 and improv…
bjohansebas May 15, 2026
1f97c88
fixup!
bjohansebas May 15, 2026
fdc4216
refactor: simplify package installation check using require.resolve
bjohansebas May 15, 2026
a6cd73f
fix: update Node.js target version in Babel configuration to 22.15.0
bjohansebas May 15, 2026
37abbf3
fixup!
bjohansebas May 16, 2026
a157165
fixup!
bjohansebas May 16, 2026
0178ed9
refactor: dynamically import 'node:net' in Server class for improved …
bjohansebas May 16, 2026
381a5e5
refactor: update type definition for onLoadQueue to ensure correct fu…
bjohansebas May 16, 2026
6c50433
fixup!
bjohansebas May 16, 2026
b7df6e5
refactor: migrate examples to ES module syntax and remove "use strict…
bjohansebas May 16, 2026
0f42478
refactor: update webpack configuration and asset handling for improve…
bjohansebas May 16, 2026
82c9150
refactor: update webSocketServer configuration to use object syntax f…
bjohansebas May 16, 2026
46d0148
refactor: update test coverage script to include additional directori…
bjohansebas May 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ npm-debug.log
client
!/examples/client
!/test/client

examples/**/dist

coverage
node_modules
.vscode
Expand Down
6 changes: 2 additions & 4 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"use strict";

module.exports = (api) => {
export default (api) => {
api.cache(true);

return {
Expand All @@ -24,7 +22,7 @@ module.exports = (api) => {
"@babel/preset-env",
{
targets: {
node: "20.9.0",
node: "22.15.0",
},
},
],
Expand Down
88 changes: 29 additions & 59 deletions bin/webpack-dev-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@
/* Based on webpack/bin/webpack.js */
/* eslint-disable no-console */

"use strict";
import cp from "node:child_process";
import { createRequire } from "node:module";
import path from "node:path";
import readLine from "node:readline";
import { fileURLToPath, pathToFileURL } from "node:url";
import fs from "graceful-fs";

const require = createRequire(import.meta.url);

/**
* @param {string} command process to run
* @param {string[]} args command line arguments
* @returns {Promise<void>} promise
*/
const runCommand = (command, args) => {
const cp = require("node:child_process");

return new Promise((resolve, reject) => {
const runCommand = (command, args) =>
new Promise((resolve, reject) => {
const executedCommand = cp.spawn(command, args, {
stdio: "inherit",
shell: true,
Expand All @@ -30,7 +35,6 @@ const runCommand = (command, args) => {
}
});
});
};

/**
* @param {string} packageName name of the package
Expand All @@ -41,63 +45,30 @@ const isInstalled = (packageName) => {
return true;
}

const path = require("node:path");
const fs = require("graceful-fs");

let dir = __dirname;

do {
try {
if (
fs.statSync(path.join(dir, "node_modules", packageName)).isDirectory()
) {
return true;
}
} catch {
// Nothing
}
} while (dir !== (dir = path.dirname(dir)));

// https://github.com/nodejs/node/blob/v18.9.1/lib/internal/modules/cjs/loader.js#L1274
// @ts-expect-error
for (const internalPath of require("node:module").globalPaths) {
try {
if (fs.statSync(path.join(internalPath, packageName)).isDirectory()) {
return true;
}
} catch {
// Nothing
}
try {
require.resolve(packageName);
return true;
} catch {
return false;
}

return false;
};

/**
* @param {CliOption} cli options
* @returns {void}
* @returns {Promise<void>}
*/
const runCli = (cli) => {
const runCli = async (cli) => {
if (cli.preprocess) {
cli.preprocess();
}

const path = require("node:path");

const pkgPath = require.resolve(`${cli.package}/package.json`);
const pkgUrl = import.meta.resolve(`${cli.package}/package.json`);
const pkgPath = fileURLToPath(pkgUrl);
const pkg = (await import(pkgUrl, { with: { type: "json" } })).default;

const pkg = require(pkgPath);
const binPath = path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName]);

if (pkg.type === "module" || /\.mjs/i.test(pkg.bin[cli.binName])) {
import(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName])).catch(
(error) => {
console.error(error);
process.exitCode = 1;
},
);
} else {
require(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName]));
}
await import(pathToFileURL(binPath).href);
};

/**
Expand All @@ -123,10 +94,6 @@ const cli = {
};

if (!cli.installed) {
const path = require("node:path");
const fs = require("graceful-fs");
const readLine = require("node:readline");

const notify = `CLI for webpack must be installed.\n ${cli.name} (${cli.url})\n`;

console.error(notify);
Expand Down Expand Up @@ -187,14 +154,17 @@ if (!cli.installed) {
);

runCommand(packageManager, [...installOptions, cli.package])
.then(() => {
runCli(cli);
})
.then(() => runCli(cli))
.catch((error) => {
console.error(error);
process.exitCode = 1;
});
});
} else {
runCli(cli);
try {
await runCli(cli);
} catch (error) {
console.error(error);
process.exitCode = 1;
}
}
2 changes: 1 addition & 1 deletion client-src/clients/WebSocketClient.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { log } from "../utils/log.js";

/** @typedef {import("../index").EXPECTED_ANY} EXPECTED_ANY */
/** @typedef {import("../index.js").EXPECTED_ANY} EXPECTED_ANY */

/**
* @implements {CommunicationClient}
Expand Down
2 changes: 1 addition & 1 deletion client-src/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ declare module "ansi-html-community" {
function setColors(colors: Record<string, string | string[]>): void;
}

export = ansiHtmlCommunity;
export default ansiHtmlCommunity;
}
2 changes: 1 addition & 1 deletion client-src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import socket from "./socket.js";
import { log, setLogLevel } from "./utils/log.js";
import sendMessage from "./utils/sendMessage.js";

// eslint-disable-next-line jsdoc/no-restricted-syntax
// eslint-disable-next-line jsdoc/reject-any-type
/** @typedef {any} EXPECTED_ANY */

/**
Expand Down
22 changes: 11 additions & 11 deletions client-src/overlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import ansiHTML from "ansi-html-community";

/** @typedef {import("./index").EXPECTED_ANY} EXPECTED_ANY */
/** @typedef {import("./index.js").EXPECTED_ANY} EXPECTED_ANY */

/**
* @type {(input: string, position: number) => number | undefined}
Expand Down Expand Up @@ -79,16 +79,16 @@ function encode(text) {

/**
* @typedef {object} Context
* @property {'warning' | 'error'} level level
* @property {(string | Message)[]} messages messages
* @property {'build' | 'runtime'} messageSource message source
* @property {"warning" | "error"} level level
* @property {(string | Message)[]} messages messages
* @property {"build" | "runtime"} messageSource message source
*/

/** @typedef {{ type: string } & Record<string, EXPECTED_ANY>} Event */

/**
* @typedef {object} Options
* @property {{ [state: string]: { on: Record<string, { target: string; actions?: Array<string> }> } }} states states
* @property {{ [state: string]: { on: Record<string, { target: string, actions?: string[] }> } }} states states
* @property {Context} context context
* @property {string} initial initial
*/
Expand Down Expand Up @@ -149,9 +149,9 @@ function createMachine({ states, context, initial }, { actions }) {

/**
* @typedef {object} ShowOverlayData
* @property {'warning' | 'error'} level level
* @property {(string | Message)[]} messages messages
* @property {'build' | 'runtime'} messageSource message source
* @property {"warning" | "error"} level level
* @property {(string | Message)[]} messages messages
* @property {"build" | "runtime"} messageSource message source
*/

/**
Expand Down Expand Up @@ -390,7 +390,7 @@ const colors = {

ansiHTML.setColors(colors);

/** @typedef {Error & { file?: string, moduleName?: string, moduleIdentifier?: string, loc?: string, message?: string; stack?: string | string[] }} Message */
/** @typedef {Error & { file?: string, moduleName?: string, moduleIdentifier?: string, loc?: string, message?: string, stack?: string | string[] }} Message */

/**
* @param {string} type type
Expand Down Expand Up @@ -450,7 +450,7 @@ const createOverlay = (options) => {
let containerElement;
/** @type {HTMLDivElement | null | undefined} */
let headerElement;
/** @type {Array<(element: HTMLDivElement) => void>} */
/** @type {((element: HTMLDivElement) => void)[]} */
let onLoadQueue = [];
/** @type {Omit<TrustedTypePolicy, "createScript" | "createScriptURL"> | undefined} */
let overlayTrustedTypesPolicy;
Expand Down Expand Up @@ -589,7 +589,7 @@ const createOverlay = (options) => {
* @param {string} type type
* @param {(string | Message)[]} messages messages
* @param {undefined | false | string} trustedTypesPolicyName trusted types policy name
* @param {'build' | 'runtime'} messageSource message source
* @param {"build" | "runtime"} messageSource message source
*/
function show(type, messages, trustedTypesPolicyName, messageSource) {
ensureOverlayExists(() => {
Expand Down
2 changes: 1 addition & 1 deletion client-src/utils/sendMessage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* global WorkerGlobalScope */

/** @typedef {import("../index").EXPECTED_ANY} EXPECTED_ANY */
/** @typedef {import("../index.js").EXPECTED_ANY} EXPECTED_ANY */

// Send messages to the outside, so plugins can consume it.
/**
Expand Down
21 changes: 10 additions & 11 deletions client-src/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
"use strict";
import path from "node:path";
import { fileURLToPath } from "node:url";
import webpack from "webpack";
import { merge } from "webpack-merge";

const path = require("node:path");
const webpack = require("webpack");
const { merge } = require("webpack-merge");
const __dirname = path.dirname(fileURLToPath(import.meta.url));

const library = {
library: {
// type: "module",
type: "commonjs",
type: "module",
},
};

const baseForModules = {
devtool: false,
mode: "development",
// TODO enable this in future after fix bug with `eval` in webpack
// experiments: {
// outputModule: true,
// },
experiments: {
outputModule: true,
},
output: {
path: path.resolve(__dirname, "../client/modules"),
...library,
Expand All @@ -37,7 +36,7 @@ const baseForModules = {
},
};

module.exports = [
export default [
merge(baseForModules, {
entry: path.join(__dirname, "modules/logger/index.js"),
output: {
Expand Down
4 changes: 1 addition & 3 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"use strict";

module.exports = {
export default {
extends: ["@commitlint/config-conventional"],
rules: {
"header-max-length": [0],
Expand Down
4 changes: 0 additions & 4 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ export default defineConfig([
files: ["test/**/*"],
extends: [configs["universal-recommended"]],
rules: {
// Tests use experimental node:test APIs intentionally
// (mock.module, mock.timers, snapshot.*). The package's engines field
// is wider than where these are stable, so silence the linter here.
"n/no-unsupported-features/node-builtins": "off",
// Test callbacks (it/test/subtest arrow functions) don't need JSDoc.
"jsdoc/require-jsdoc": "off",
// Tests legitimately log diagnostics (retry attempts, etc.).
Expand Down
21 changes: 21 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@ An example should be as minimal as possible and consists of at least:
API examples can be found in the `api` directory. These examples demonstrate how
to access and run `webpack-dev-server` directly in your application / script.

## Module format

Examples are written as ES modules (`import` / `export`) because the project's
`package.json` sets `"type": "module"`. The shared helper `util.js` exports a
`setup` function that each `webpack.config.js` calls like this:

```js
import { setup } from "../util.js";

export default setup(
{
context: import.meta.dirname,
entry: "./app.js",
},
import.meta.url,
);
```

If you still need a CommonJS configuration, see [`default-cjs/`](./default-cjs)
for a minimal CJS example using a `.cjs` extension.

## Notes

- Each example's `webpack` config is wrapped with `util.setup`; a helper function
Expand Down
4 changes: 1 addition & 3 deletions examples/api/find-ip/app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"use strict";

const WebpackDevServer = require("../../../lib/Server");
import WebpackDevServer from "../../../lib/Server.js";

const logInternalIPs = async () => {
const localIPv4 = WebpackDevServer.findIp("v4", false);
Expand Down
2 changes: 0 additions & 2 deletions examples/api/middleware/app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
"use strict";

const target = document.querySelector("#target");

target.classList.add("pass");
Expand Down
8 changes: 3 additions & 5 deletions examples/api/middleware/server.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"use strict";

const Webpack = require("webpack");
const WebpackDevServer = require("../../../lib/Server");
const webpackConfig = require("./webpack.config");
import Webpack from "webpack";
import WebpackDevServer from "../../../lib/Server.js";
import webpackConfig from "./webpack.config.js";

const compiler = Webpack(webpackConfig);
const devServerOptions = webpackConfig.devServer;
Expand Down
Loading
Loading