Commit Graph

18 Commits

Author SHA1 Message Date
James Anderson 69f03c871b fix(test): prevent recursive deploy log growth (#2693) 2026-07-24 11:21:45 +01:00
James Anderson 645d1a1c74 fix(create): make create-vinext-app work with npm and npx (#2618)
* fix(create): make create-vinext-app work with npx

* fix(ci): run packed creator directly with npx

* fix(cloudflare): support vinext prerelease peers
2026-07-14 20:25:39 +01:00
James Anderson de94652a1d fix(shims): align public API with vendored Next types (#2617)
* fix(shims): align public API with vendored Next types

* fix(shims): preserve revalidation and runtime behavior

* test(shims): align cache revalidation expectations

* fix(ci): package types for deploy suite
2026-07-14 19:32:33 +01:00
James Anderson 0a48f0f7fb feat(cloudflare): move deploy command to cloudflare package (#2405)
* feat(cloudflare): move deploy command to cloudflare package

* fix(cloudflare): expose deploy cli bin
2026-06-29 16:47:28 +00:00
James Anderson 6cbe7cdd18 fix(ci): handle npm-pinned deploy fixtures (#2400) 2026-06-29 11:12:05 +01:00
James Anderson 56772ed310 fix(build): resolve framework-owned SWC helpers (#2357)
* fix(build): resolve framework-owned SWC helpers

* fix(test): preserve shared node_modules during deploy prepare
2026-06-29 00:11:42 +01:00
James Anderson 592a50b76d fix(test): pin deploy suite init to node (#2383) 2026-06-26 20:26:04 +00:00
James Anderson ee551d1e28 fix(config): match deprecation warning parity (#2252)
* fix(config): match deprecation warning output

* fix(config): dedupe deprecation warnings

* fix(test): preserve lifecycle install diagnostics

* fix(check): report unsupported proxy config options
2026-06-23 20:39:39 +01:00
Nathan Nguyen d89f611277 chore(test): package workspace deps in deploy harness (#1786)
The Next.js deploy-suite harness installs vinext into isolated temp apps outside the vinext workspace. That breaks when the packaged vinext manifest still contains workspace dependencies, because pnpm cannot resolve workspace:* specs without the monorepo package graph.

Copy the local @vinext/cloudflare build output into a sibling throwaway package and rewrite workspace dependencies in the generated vinext manifest to point at that package. This preserves the package boundary while keeping deploy-suite fixture installs self-contained.
2026-06-06 09:28:58 +01:00
Nathan Nguyen 88144de251 perf(e2e): preinstall compatible React in deploy harness (#1443)
* perf(e2e): preinstall compatible React in deploy harness

App Router deploy-suite fixtures can start with React versions below the react-server-dom-webpack peer range. The first harness install then resolves that older React pair, and vinext build immediately runs its RSC compatibility upgrade, causing a second package-manager install inside each throwaway app.

Normalize App Router fixture manifests before the initial install by replacing older semver React and React DOM specs with the workspace-compatible specs. Pages-only fixtures and non-semver specs are left untouched.

* chore: rerun ci

* fix(e2e): derive React floor from harness spec

The deploy harness normalized App Router fixture React versions against a hardcoded 19.2.6 floor. That duplicated the compatibility threshold already expressed by vinext's install specs and could go stale when the workspace moves to a newer compatible React pair.

Compare each fixture dependency against the replacement spec that the harness would write. This keeps React and React DOM normalization tied to the package metadata instead of a separate constant.
2026-05-22 14:22:27 +01:00
Nathan Nguyen 3c77b2bb32 ci: avoid killing deploy-suite test runners during cleanup (#1423) 2026-05-22 07:51:24 +01:00
James Anderson 1f2030b8d2 fix(config): load CJS next.config.js under "type": "module" (#1275)
`createRequire` of a `.js` file in a `"type": "module"` package still gets
resolved as ESM by Node (extension + parent type wins over the require()
caller), so the existing CJS fallback in loadNextConfig threw `require is
not defined` for every plain `module.exports = {}` config after `vinext
init`. Worked around in the Nextjs deploy suite by rewriting every
fixture's config from CJS to ESM in-place via scripts/cjs-to-esm-config.mjs.

Copy the source to a sibling temp `.cjs` (extension overrides the parent
type field) and require that instead. Relative imports inside the config
still resolve against the original directory. Drop the deploy-suite
rewrite step and its 145-line shim.
2026-05-16 18:36:22 +01:00
James Anderson 875f314c11 fix(deploy-suite): handle missing optional deps in test fixture configs (#1213)
Three classes of missing-dep build failures in the Next.js deploy harness:

1. `@next/bundle-analyzer` (test/e2e/app-dir/metadata-{font,edge,dynamic-routes})
   These fixtures wrap the require in `if (process.env.ANALYZE)`, but our
   CJS→ESM converter hoisted `require('mod')(args)` to a static top-level
   `import`, which unconditionally tried to resolve the package and failed
   the build even when ANALYZE was unset.

   Fix: rewrite `const X = require('mod')(args)` to an inline
   `(await import('mod')).default(args)` instead of hoisting. This preserves
   CJS lazy semantics — the import only runs when the surrounding code does.

2. `webpack` (test/e2e/app-dir/next-config/next.config.js)
   Top-level `require('webpack').sources.RawSource` — Next.js's own monorepo
   has webpack at the workspace root, but the test fixture's package.json
   doesn't declare it. Inject `webpack@^5.99.0` into the harness-deps block.

3. `ipaddr.js` (test/e2e/type-module-interop, test/e2e/app-dir/app-middleware)
   vinext's image-config shim imports `ipaddr.js`, which is in vinext's
   `dependencies`. But vinext is installed via `file:.vinext-local-package`,
   so pnpm keeps its transitive deps under `.vinext-local-package/node_modules`
   and Node's ESM resolver can't see them from `<test-app>/dist/...` at
   prerender time. Inject `ipaddr.js` into the harness-deps list (spec sourced
   from the vinext catalog).

Affected suites:
- test/e2e/app-dir/metadata-font
- test/e2e/app-dir/metadata-edge
- test/e2e/app-dir/metadata-dynamic-routes
- test/e2e/app-dir/next-config
- test/e2e/type-module-interop
- test/e2e/app-dir/app-middleware (prerender failure)

Verified:
- `bash -n scripts/*.sh`
- `node --check scripts/cjs-to-esm-config.mjs`
- Round-tripped each of the four fixture next.config.js files through the
  patched converter; output is syntactically valid and loads without error
  when ANALYZE is unset (and fails with the expected ERR_MODULE_NOT_FOUND
  when ANALYZE=1, matching CJS lazy semantics).

Refs: workflow run 25870737355
2026-05-14 19:24:06 +00:00
James Anderson 040eef55ae fix(deploy-suite): inject jiti when test app uses TypeScript config files (#1211)
Test apps using `next.config.ts`, `postcss.config.{ts,mts,cts}`, or
`.postcssrc.ts` fail at `vinext build` time with:

  Failed to load PostCSS config: 'tsx' or 'jiti' is required for the
  TypeScript configuration files. Make sure it is installed
  Cannot find package 'jiti' imported from
    .../vite/dist/node/chunks/node.js

Next.js auto-installs one of jiti/tsx as part of its own build pipeline
when it detects a TypeScript config file. Once vinext replaces Next.js
as the deploy target, the test app's package.json no longer lists
either, and Vite's bundled PostCSS / Tailwind / etc. config loaders
fail to load `*.config.ts` files.

Fix: extend the harness-deps injector in scripts/e2e-deploy.sh to scan
the test app root for `*.config.{ts,mts,cts}` files (and the dotfile
`.postcssrc.ts` variants) and inject `jiti` into devDependencies
when present. jiti is the lighter option (no native deps) and matches
what Vite's internal loader prefers.

Pinned to `^2.6.1` to match the version already resolved transitively
in this workspace via `@tailwindcss/node`. Both postcss-load-config and
Vite require `jiti >=1.21.0`; `^2.6.1` satisfies that.

Affected suites (~20 build-time failures in run 25870737355):
- test/e2e/app-dir/next-config-ts/*
- test/e2e/app-dir/next-config-ts-native-mts/*
- test/e2e/app-dir/next-config-ts-native-ts/*
- test/e2e/postcss-config-ts/

Verified locally:
- `bash -n scripts/*.sh` passes
- Manually exercised the embedded Node block against synthetic test
  apps with next.config.ts, postcss.config.mts, .postcssrc.ts, and a
  negative control (next.config.js only). jiti injection fires for all
  three positives and stays out for the negative.

Refs: https://github.com/cloudflare/vinext/actions/runs/25870737355
2026-05-14 18:46:22 +00:00
James Anderson 289ab18b14 fix(deploy-suite): bump pinned sass below ^1.70.0 for Vite 8 (#1214)
The Next.js scss e2e fixtures pin `sass: '1.54.0'` (and some scss-modules
fixtures pin `sass-embedded: '1.75.0'`) via createNextDescribe's
`dependencies` option. Vite 8's built-in `vite:css` preprocessor calls
`sass.initAsyncCompiler()`, an API added in dart-sass 1.70.0 (Feb 2024).
Vite 8 declares `sass@^1.70.0` / `sass-embedded@^1.70.0` as optional
peers, but the fixture's older pin wins during install.

Result: every test/e2e/app-dir/scss/* and test/e2e/app-dir/scss-modules/*
suite fails at vinext build time with:

  TypeError: [sass] sass.initAsyncCompiler is not a function

(see run 25870737355, ~31 deploy-script failures from this cluster).

Fix: in the deploy harness's dependency-injection step, after a test
app's package.json has been augmented with vinext + Vite plugins, check
for a `sass` or `sass-embedded` entry pinned below `^1.70.0` (parsed
from the leading `major.minor` of common semver specs) and rewrite it to
`^1.70.0`. Non-version specs (workspace:, file:, git+, tags) are left
alone. This is a no-op for test apps that don't depend on sass.

Verified locally by running the patched harness against the multi-page
scss fixture: the resolved pnpm path goes from `sass@1.54.0` to
`sass@1.99.0`, and the build advances past the SCSS preprocessor (a
separate JSX-in-Pages-Router error then trips it, tracked as its own
cluster).
2026-05-14 18:32:14 +00:00
James Anderson bc8cee2385 fix(deploy-suite): extract CJS->ESM converter to standalone script (#1209)
* fix(deploy-suite): extract CJS->ESM converter to standalone script

The CJS->ESM next.config converter was inlined into scripts/e2e-deploy.sh
via 'node -e ...' with bash single-quote escapes ('"'"'). The escaping
rebalanced quotes such that later code (a comment containing '(e.g.') sat
outside any single-quoted bash region, causing 'bash -n' to fail with:

  scripts/e2e-deploy.sh: line 490: syntax error near unexpected token `('

The script died before doing anything in every deploy-suite shard, producing
2480 identical 'Custom deploy script failed: undefined (2)' failures and a
misleading 9.5% pass rate (almost all 'passes' were 'should skip next deploy'
no-ops). See run 25869359604.

- Extract the converter to scripts/cjs-to-esm-config.mjs (same regex
  pipeline, no behavioural change).
- Replace the inlined 'node -e' block with a plain 'node ./script.mjs'
  invocation.
- Add a 'bash -n scripts/*.sh' gate to the Check job so a future regression
  here can't ship green.

* Apply suggestion from @james-elicx
2026-05-14 16:04:09 +00:00
James Anderson 3f4e594e1c fix(deploy-suite): create .next/trace before build to avoid ENOENT noise on build failure (#1200)
The Next.js deploy test harness (test/lib/next-modes/base.ts) calls
lstat('.next/trace') during destroy() and throws ENOENT if the file
doesn't exist. vinext doesn't produce a .next/trace (it's a Next.js
telemetry file), so the deploy script creates an empty one as a stub.

That stub was created AFTER 'vinext build' (line ~520), so any run where
the build (or any earlier step like 'pnpm install' / 'vinext init')
failed left .next/trace missing. The test harness then logged ENOENT for
every failed test — 303 lines per deploy-suite workflow run.

Move the stub creation to immediately after the cleanup trap is
installed, before any step that can fail. The cleanup trap runs on EXIT,
so debug artifacts are still preserved; this just ensures .next/trace is
present for the test harness regardless of whether the build succeeded.

Note: VITE_GIT_HOOKS=0 used because vp check --fix errors out when no
JS/TS files are staged (only a .sh file is changed here).
2026-05-14 13:31:08 +00:00
James Anderson 747bb0ec76 ci: add Next.js deploy suite harness (#1189)
* ci: add Next.js Pages Router deploy suite harness

Add the workflow, scripts, and manifest generator for running the
Next.js Pages Router deploy test suite against vinext.

Triggers:
- Nightly cron (06:00 UTC) against main
- Manual dispatch with configurable vinext-ref, next-ref, and concurrency

Extracted from #922.

* refactor: align deploy suite harness with Next.js adapter testing docs

- Rename scripts to match Next.js convention: e2e-deploy.sh, e2e-logs.sh,
  e2e-cleanup.sh
- Split workflow into build + test jobs (build once, shard 16 ways) per
  the documented adapter testing pattern
- Generalize workflow from pages-only to configurable: add suite-filter
  input (pages/app/all, defaults to pages)
- Add --filter flag to manifest generator (replaces the pages-hardcoded
  version)
- Accept ADAPTER_DIR (Next.js docs convention) alongside VINEXT_DIR
- Rename workflow file to nextjs-deploy-suite.yml
- Use matrix.group strings ("1/16") instead of shard integers, matching
  the docs example

* ci: default suite-filter to app

* ci: bump Next.js default to v16.2.6, move cron to 02:00 UTC

* address review: add report job, clean up cache paths, document compat shims

- Add a report job that aggregates .results.json across all shards into
  a GitHub Actions summary (pass/fail/skip counts, failed test details,
  passed suite list) and uploads a JSON report artifact
- Upload test results from every shard (not just on failure)
- Remove redundant next.js/ from cache paths (already under .)
- Add comment explaining why IS_TURBOPACK_TEST=1 is needed
- Clean up stale PID file in e2e-cleanup.sh
- Add header comments to all scripts explaining their purpose and contract
- Document the hardcoded warning messages and .next/trace shim
- Document the YAML catalog parser assumptions
- Fix [[ ]] -> [ ] inconsistency in run-nextjs-deploy-suite.sh

* ci: add temporary PR trigger to test workflow [remove before merge]

* fix: address deploy script failures found in CI and local testing

Bugs found and fixed:

- IMMUTABLE_ASSET_TOKEN marker renamed to NEXT_SUPPORTS_IMMUTABLE_ASSETS
  to match what next-deploy.ts parseIdsFromCliOuput() actually parses.
  Without this, every test fails with 'Failed to get supportsImmutableAssets'.

- Deploy script now injects "type": "module" into test app package.json
  and generates a vite.config.ts. Without type:module, Rolldown emits .mjs
  files but the RSC plugin's cross-environment imports expect .js. This
  mirrors what `vinext init` does (steps 3 and 5).

- Added vp (Vite+) fallback to run_pnpm() for environments where pnpm
  and corepack are not available but vp is.

- Cleanup script now uses process group kills (kill -TERM -PID) and a
  port-based lsof fallback to handle orphaned child processes from
  vp/pnpm exec wrappers.

- Error cleanup trap now dumps the last 80 lines of the build log and
  last 40 lines of the server log to stderr, so failures are visible
  in CI output (previously only showed file sizes).

- Test job uses run-install: false since the workspace is already built
  in the cache from the build job.

Tested locally: deploy script succeeds, curl returns valid HTML, logs
script outputs all three required markers (BUILD_ID, DEPLOYMENT_ID,
NEXT_SUPPORTS_IMMUTABLE_ASSETS), cleanup kills the server.

* fix: use vinext init in deploy script, add next.config.js to CJS rename list

Replace the manual type:module / vite.config.ts / CJS rename logic in
e2e-deploy.sh with a single `vinext init --skip-check --force` call.
This runs after pnpm install (so deps are available) and before
vinext build.

Also add next.config.js to the CJS_CONFIG_FILES list in project.ts.
Without this, vinext init adds type:module but doesn't rename CJS
next.config.js files, causing 'module is not defined in ES module
scope' errors. This was the root cause of the CI failures — Next.js
test fixtures use module.exports in next.config.js.

All 61 init tests pass.

* fix: convert CJS next.config.js to ESM instead of renaming to .cjs

Next.js doesn't support next.config.cjs, so we can't rename it. Instead,
convert module.exports/require() to export default/import in-place after
vinext init adds "type": "module".

Also revert adding next.config.js to CJS_CONFIG_FILES — vinext init
should not rename it since it's not a generic config file.

Tolerate pnpm 10+ ERR_PNPM_IGNORED_BUILDS exit code 1 on install (the
install completes, packages are in node_modules, but pnpm exits non-zero
due to unapproved build scripts). Verify node_modules/vinext exists
as a safety check.

Tested against the real Next.js actions-streaming fixture: CJS
next.config.js converted, app builds, server starts, HTTP 200.

* fix: emit both IMMUTABLE_ASSET_TOKEN and NEXT_SUPPORTS_IMMUTABLE_ASSETS

v16.2.x parses IMMUTABLE_ASSET_TOKEN, canary renamed it to
NEXT_SUPPORTS_IMMUTABLE_ASSETS. Emit both for cross-version compat.

* fix: enable JSX in .js files, convert CJS next.config.ts to ESM

Add vinext:jsx-in-js transform plugin to handle JSX in plain .js files.
Next.js allows JSX in .js files (Babel/SWC handle it transparently),
but Vite 8's built-in vite:oxc plugin excludes .js files by default
(exclude: /\.js$/) AND infers lang: 'js' from the extension (which
disables JSX parsing). Neither can be fixed via config alone.

The plugin runs with enforce: 'pre' before vite:oxc, transforming .js
files with OXC using lang: 'jsx' so JSX is compiled before vite:oxc
sees the code. This unblocks ~60 test suites in the deploy suite.

Also extend the deploy script's CJS-to-ESM config converter to handle
next.config.ts files (not just .js), fixing ~6 more test failures.

Tested: 1284 tests pass (jsx-in-js + shims + routing + app-router),
zero regressions.

* ci: trigger clean workflow run

* fix: improve CJS-to-ESM converter for next.config files

The naive regex converter missed two patterns:
- require('mod')(args) produced invalid ESM (import X from 'mod'(args))
- require() calls inside function bodies were not converted

New converter handles:
- module.exports = X → export default X
- const X = require('mod') → import X from 'mod'
- const X = require('mod')(args) → import _X from 'mod'; const X = _X(args)
- require('mod') in expressions → (await import('mod')).default

Tested against bundle-analyzer, path, and inline require patterns.

* .

* Apply suggestion from @james-elicx

* fix: handle destructured requires in CJS converter, read port from file in cleanup

- Add pattern for const { a, b } = require('mod') → import { a, b } from 'mod'
- Read port from PORT_FILE in cleanup_on_error trap instead of using $PORT
  variable which may be unset if the script fails before port allocation
- Add TODO noting remaining CJS edge cases (dynamic require, require.resolve)

* Apply suggestions from code review

Co-authored-by: James Anderson <james@eli.cx>

* Apply suggestion from @james-elicx
2026-05-14 13:01:01 +00:00