Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
8 changes: 8 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changesets

Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)

We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
133 changes: 133 additions & 0 deletions .changeset/changelog-generator.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { getInfo, getInfoFromPullRequest } from "@changesets/get-github-info";

/** @typedef {import("@changesets/types").ChangelogFunctions} ChangelogFunctions */

/**
* @returns {{ GITHUB_SERVER_URL: string }} value
*/
function readEnv() {
const GITHUB_SERVER_URL =
process.env.GITHUB_SERVER_URL || "https://github.com";
return { GITHUB_SERVER_URL };
}

/** @type {ChangelogFunctions} */
const changelogFunctions = {
getDependencyReleaseLine: async (
changesets,
dependenciesUpdated,
options,
) => {
if (!options.repo) {
throw new Error(
'Please provide a repo to this changelog generator like this:\n"changelog": ["@changesets/changelog-github", { "repo": "org/repo" }]',
);
}
if (dependenciesUpdated.length === 0) return "";

const changesetLink = `- Updated dependencies [${(
await Promise.all(
changesets.map(async (cs) => {
if (cs.commit) {
const { links } = await getInfo({
repo: options.repo,
commit: cs.commit,
});
return links.commit;
}
}),
)
)
.filter(Boolean)
.join(", ")}]:`;

const updatedDependenciesList = dependenciesUpdated.map(
(dependency) => ` - ${dependency.name}@${dependency.newVersion}`,
);

return [changesetLink, ...updatedDependenciesList].join("\n");
},
getReleaseLine: async (changeset, type, options) => {
const { GITHUB_SERVER_URL } = readEnv();
if (!options || !options.repo) {
throw new Error(
'Please provide a repo to this changelog generator like this:\n"changelog": ["@changesets/changelog-github", { "repo": "org/repo" }]',
);
}

/** @type {number | undefined} */
let prFromSummary;
/** @type {string | undefined} */
let commitFromSummary;
/** @type {string[]} */
const usersFromSummary = [];

const replacedChangelog = changeset.summary
.replace(/^\s*(?:pr|pull|pull\s+request):\s*#?(\d+)/im, (_, pr) => {
const num = Number(pr);
if (!Number.isNaN(num)) prFromSummary = num;
return "";
})
.replace(/^\s*commit:\s*([^\s]+)/im, (_, commit) => {
commitFromSummary = commit;
return "";
})
.replaceAll(/^\s*(?:author|user):\s*@?([^\s]+)/gim, (_, user) => {
usersFromSummary.push(user);
return "";
})
.trim();

const [firstLine, ...futureLines] = replacedChangelog
.split("\n")
.map((l) => l.trimEnd());

const links = await (async () => {
if (prFromSummary !== undefined) {
let { links } = await getInfoFromPullRequest({
repo: options.repo,
pull: prFromSummary,
});
if (commitFromSummary) {
const shortCommitId = commitFromSummary.slice(0, 7);
links = {
...links,
commit: `[\`${shortCommitId}\`](${GITHUB_SERVER_URL}/${options.repo}/commit/${commitFromSummary})`,
};
}
return links;
}
const commitToFetchFrom = commitFromSummary || changeset.commit;
if (commitToFetchFrom) {
const { links } = await getInfo({
repo: options.repo,
commit: commitToFetchFrom,
});
return links;
}
return {
commit: null,
pull: null,
user: null,
};
})();

const users = usersFromSummary.length
? usersFromSummary
.map(
(userFromSummary) =>
`[@${userFromSummary}](${GITHUB_SERVER_URL}/${userFromSummary})`,
)
.join(", ")
: links.user;

let suffix = "";
if (links.pull || links.commit || users) {
suffix = `(${users ? `by ${users} ` : ""}in ${links.pull || links.commit})`;
}

return `\n\n- ${firstLine} ${suffix}\n${futureLines.map((l) => ` ${l}`).join("\n")}`;
},
};

export default changelogFunctions;
137 changes: 137 additions & 0 deletions .changeset/changeset-validate.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/* eslint-disable no-console */
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { simpleGit } from "simple-git";
import pkgJson from "../package.json" with { type: "json" };

Comment thread
bjohansebas marked this conversation as resolved.
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const rootPath = path.join(__dirname, "..");
const git = simpleGit(rootPath);

const VALID_BUMPS = new Set(["major", "minor", "patch"]);
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
const ENTRY_RE = /^"([^"]+)"\s*:\s*([a-zA-Z]+)\s*$/;

const toLines = (output) =>
output
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean);

const isChangeset = (filePath) => {
const normalized = filePath.replaceAll("\\", "/");
return (
normalized.startsWith(".changeset/") &&
normalized.endsWith(".md") &&
normalized !== ".changeset/README.md"
);
};

const gitDiff = async (more = []) => {
const args = [
"diff",
"--name-only",
// cspell:ignore ACMR
"--diff-filter=ACMR",
...more,
"--",
".changeset/*.md",
].filter(Boolean);

return toLines(await git.raw(args));
};

const getChangedFiles = async () => {
const files = new Set();
const baseRef = process.env.GITHUB_BASE_REF;

// GitHub Actions base diff
if (baseRef) {
for (const file of await gitDiff([`origin/${baseRef}...HEAD`])) {
if (isChangeset(file)) files.add(file);
}
}
// Local working tree changes
else {
const _files = [
// Unstaged changes
...(await gitDiff()),
// Staged but uncommitted changes
...(await gitDiff(["--cached"])),
// Untracked files
...(await git.status()).not_added,
];
for (const file of _files) {
if (isChangeset(file)) files.add(file);
}
}
return files;
};

const validate = async (filePath) => {
const absoluteFilePath = path.join(rootPath, filePath);
const content = await fs.readFile(absoluteFilePath, "utf8");
const frontmatterMatch = content.match(FRONTMATTER_RE);
const errors = [];

if (!frontmatterMatch) {
errors.push("missing YAML frontmatter block");
return errors;
}

const entries = frontmatterMatch[1]
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean);

if (entries.length === 0) {
errors.push("frontmatter does not contain package bump entries");
return errors;
}

for (const entry of entries) {
const match = entry.match(ENTRY_RE);
if (!match) {
errors.push(`invalid frontmatter entry: ${entry}`);
continue;
}

const [, pkgName, bumpType] = match;
if (pkgName !== pkgJson.name) {
errors.push(
`invalid package name "${pkgName}", expected "${pkgJson.name}"`,
);
}

if (!VALID_BUMPS.has(bumpType)) {
errors.push(
`invalid bump type "${bumpType}", expected one of: major, minor, patch`,
);
}
}

return errors;
};

const changedFiles = await getChangedFiles();

if (changedFiles.size === 0) {
console.log("No changed changeset files found.");
} else {
const failures = [];
for (const filePath of changedFiles) {
const errors = await validate(filePath);
for (const error of errors) {
failures.push(`${filePath}: ${error}`);
}
}

if (failures.length > 0) {
console.error("Changeset validation failed:");
for (const failure of failures) {
console.error(`- ${failure}`);
}
process.exitCode = 1;
}
}
13 changes: 13 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json",
"changelog": [
"./changelog-generator.mjs",
{ "repo": "webpack/webpack-dev-server" }
],
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
5 changes: 5 additions & 0 deletions .changeset/fix-hmr-skip-user-proxy-upgrade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"webpack-dev-server": patch
---

Skip the HMR WebSocket path when forwarding upgrade requests to user-defined proxies, so custom proxy WebSocket upgrades are no longer intercepted by the dev server.
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,14 @@ updates:
dependencies:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 20
labels:
- dependencies
groups:
dependencies:
patterns:
- "*"
7 changes: 4 additions & 3 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,14 @@ jobs:
- name: Check types
run: if [ -n "$(git status types --porcelain)" ]; then echo "Missing types. Update types by running 'npm run build:types'"; exit 1; else echo "All types are valid"; fi

- name: Security audit
run: npm audit --production

- name: Validate PR commits with commitlint
if: github.event_name == 'pull_request'
run: npx commitlint --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose

- name: Validate changeset format
if: github.event_name == 'pull_request'
run: npm run validate:changeset

test:
name: Test - ${{ matrix.os }} - Node v${{ matrix.node-version == '24.15' && '24.x' || matrix.node-version }}, Webpack ${{ matrix.webpack-version }} (${{ matrix.shard }})

Expand Down
42 changes: 42 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Release

on:
push:
branches:
- main

concurrency: ${{ github.workflow }}-${{ github.ref }}

permissions:
id-token: write # Required for OIDC
contents: write
pull-requests: write

jobs:
release:
if: github.repository == 'webpack/webpack-dev-server'
name: Release
runs-on: ubuntu-latest
outputs:
published: ${{ steps.changesets.outputs.published }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- name: Use Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: lts/*
cache: npm

- run: npm ci

- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0
with:
publish: node ./node_modules/.bin/changeset publish
commit: "chore(release): new release"
title: "chore(release): new release"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: "" # https://github.com/changesets/changesets/issues/1152#issuecomment-3190884868
Comment thread
bjohansebas marked this conversation as resolved.
Loading
Loading