Skip to content

feat!: upgrade to Next.js 16 and replace webpack with Turbopack - #958

Draft
pdavies88 wants to merge 5 commits into
chore/dep-upgrade-2026-09from
chore/nextjs-16-upgrade
Draft

pdavies88 wants to merge 5 commits into
chore/dep-upgrade-2026-09from
chore/nextjs-16-upgrade

Conversation

@pdavies88

Copy link
Copy Markdown
Contributor

Next.js 16 builds with Turbopack by default and hard-fails when a webpack config is present, so the config-injection mechanism has been rewritten.

  • remove the webpack ModifySourcePlugin and inject the headstartwp config through a Turbopack loader rule emitted by withHeadstartWPConfig
  • add injectHeadstartWPConfigLoader, a dependency-free loader that inserts the bootstrap after the directive prologue so 'use client'/'use server' is preserved, and imports the config by a path relative to the module (Turbopack resolves a leading / against the project root)
  • split client/server config via Turbopack's built-in browser condition
  • drop the linaria/wyw-in-js integration and the schema-utils, loader-utils and copy-webpack-plugin dependencies
  • always emit images.remotePatterns; drop the Next < 14 domains fallback
  • rename middleware.* to proxy.* across the example projects
  • remove the eslint next config option (removed in Next.js 16)
  • replace @next/bundle-analyzer with next experimental-analyze

React stays on 18.3.1 so the Pages Router remains supported.

Claude-Session: https://claude.ai/code/session_01VMatBaQUrPS9AhSJmkF2vF

Description of the Change

Closes #

How to test the Change

Changelog Entry

Added - New feature
Changed - Existing functionality
Deprecated - Soon-to-be removed feature
Removed - Feature
Fixed - Bug fix
Security - Vulnerability
Developer - Non-functional update

Credits

Props @username, ...

Checklist:

Next.js 16 builds with Turbopack by default and hard-fails when a webpack
config is present, so the config-injection mechanism has been rewritten.

- remove the webpack ModifySourcePlugin and inject the headstartwp config
  through a Turbopack loader rule emitted by withHeadstartWPConfig
- add injectHeadstartWPConfigLoader, a dependency-free loader that inserts
  the bootstrap after the directive prologue so 'use client'/'use server'
  is preserved, and imports the config by a path relative to the module
  (Turbopack resolves a leading / against the project root)
- split client/server config via Turbopack's built-in `browser` condition
- drop the linaria/wyw-in-js integration and the schema-utils,
  loader-utils and copy-webpack-plugin dependencies
- always emit images.remotePatterns; drop the Next < 14 domains fallback
- rename middleware.* to proxy.* across the example projects
- remove the `eslint` next config option (removed in Next.js 16)
- replace @next/bundle-analyzer with `next experimental-analyze`

React stays on 18.3.1 so the Pages Router remains supported.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VMatBaQUrPS9AhSJmkF2vF
@changeset-bot

changeset-bot Bot commented Sep 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f12d5d8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@headstartwp/next Major
@10up/next-redis-cache-provider Major
@headstartwp/block-primitives Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
headstartwp-app-router Ready Ready Preview Sep 18, 2026 3:44pm UTC
headstarwp Ready Ready Preview Sep 18, 2026 3:44pm UTC

Request Review

pdavies88 and others added 3 commits September 18, 2026 15:04
The lockfile still carried @next/bundle-analyzer and its tree for the three
projects it was removed from, so npm install in CI would rewrite it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VMatBaQUrPS9AhSJmkF2vF
Two pre-existing CI problems, both unrelated to the Next.js 16 migration.

The repo requires npm >= 11.6.0 via engines.npm and devEngines with
onFail: error, but Node 22 ships npm 10.x and always will, so every Node 22
job aborted at install with EBADDEVENGINES. engines.node advertised support
for ^20.19.0 || ^22.18.0 which could never satisfy that npm floor. Node 24
is what .nvmrc pins and the only version the lint workflow ran, so drop the
lower versions from engines and from the build-test and unit-test matrices.

The bundle analysis workflows downloaded the base branch's bundle stats with
if_no_artifact_found: fail. Baselines are only produced by pushes to develop,
so any PR stacked on a feature branch had no baseline and the job failed even
though the build and the report both succeeded. Make that download
non-fatal and skip the compare and comment steps when no baseline exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VMatBaQUrPS9AhSJmkF2vF
PHPUnit failed at install for the same reason the Node 22 jobs did: it runs
npm install with no setup-node step, so it used the runner's system npm,
which is below the npm >= 11.6.0 floor that devEngines enforces with
onFail: error. PHPCS is the same workflow without an npm install step and it
passed. Pin Node 24 there so npm 11 is used.

The app-router bundle analysis workflow reported
{"/_app":{"raw":0,"gzip":0},"__global":{"raw":0,"gzip":0}} because
nextjs-bundle-analysis reads buildMeta.pages['/_app'], which only exists in a
pages-router build manifest. It has never measured anything on that project,
so remove it rather than let it report zeros.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VMatBaQUrPS9AhSJmkF2vF
…d step

The previous attempt guarded the compare and comment steps on the download
step's outcome, but if_no_artifact_found: warn makes that step exit 0, so its
outcome is always 'success'. The guard never fired and the compare step still
ran, failing on `ls` against a directory that was never created.

Check whether the baseline directory actually has contents instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VMatBaQUrPS9AhSJmkF2vF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant