npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gregorygrant/nextjs-build-adapter

v0.1.2

Published

Pantheon Next.js build adapter (NextAdapter) — normalizes Next.js 16.2+ build output into the canonical STA build manifest.

Readme

@pantheon-systems/nextjs-build-adapter

The Pantheon Next.js build adapter. A Node module implementing Next.js 16.2+'s stable NextAdapter API, loaded zero-touch via the NEXT_ADAPTER_PATH env var (no client next.config edit, no dependency install on their side). It tunes the build for Pantheon's Single Tenant Architecture (STA) and normalizes the typed Next.js build output into a canonical build manifest the orchestration layer consumes.

What it does

The adapter has two runtime pieces that run in sequence around next build:

  1. The NextAdapter (src/index.ts) — loaded by Next.js during the build.
    • modifyConfig (production build only): forces output: 'standalone' (STA serves the app from the standalone Node output behind sta-caddy-proxy), injects the Pantheon cache handlers for ISR/route/fetch + 'use cache' (backed by the GCS shared cache, zero-touch), and pins a deterministic build id when PANTHEON_BUILD_ID is set.
    • onBuildComplete: writes the canonical manifest.json from ctx.outputs / ctx.routing, and warns (never fails) if the build still uses the unsupported edge runtime (see Edge runtime below). The adapter stays stateless — the builder/orchestration layer uploads the manifest.
  2. The pantheon-stage CLI (src/stage-cli.ts) — run after next build returns. Next writes the standalone/ directory only after onBuildComplete finishes, so staging cannot happen in the hook. This step copies .next/static and public/ into the standalone tree (monorepo-aware) so the standalone output is self-contained and runnable.

Builder order: next build (adapter injected) → pantheon-stage → package image.

Requirements

  • Node.js >= 20 (developed on 26; tests use native TypeScript type-stripping — no tsx/ts-node).
  • next >= 16.2 (peer dependency; provided by the build environment).
  • pnpm (matches the Next.js adapter test harness). sharp's native build is ignored; .npmrc sets verify-deps-before-run=false.

Commands

| Command | What it does | |---|---| | pnpm install | Install dependencies. | | pnpm build | Compile src/dist/ (ESM). Entry: dist/index.js (matches NEXT_ADAPTER_PATH). | | pnpm dev | tsc --watch. | | pnpm typecheck | Type-check without emitting. | | pnpm test | Run all tests: node --test "test/**/*.test.ts" (native TS, no transpile step). | | pnpm test:one "<name>" | Run a single test by name (--test-name-pattern). | | pnpm lint / pnpm lint:fix | ESLint (flat config). | | pnpm e2e:smoke | Build the fixture app through the real adapter + harness and boot the staged server. | | pnpm e2e:fixtures | Build the Pantheon fixture matrix (e2e/fixtures/*) through the adapter and assert each manifest.json against its expected.json. PR-gating. Append a name to run one, e.g. pnpm e2e:fixtures pages-router. | | pnpm clean | Remove dist/. |

The Pantheon fixture suite (pnpm e2e:fixtures) gates every PR; the official Next.js deploy-mode e2e suite runs via bash scripts/run-suite.sh nightly (the heavyweight CI correctness bar). See e2e/README.md.

Usage (zero-touch injection)

Building the adapter itself from this repo (local dev):

pnpm build
export NEXT_ADAPTER_PATH="$(pwd)/dist/index.js"
next build            # Next.js loads the adapter; no next.config change required
pantheon-stage        # post-build: stage static + public into the standalone tree

Adding it to a client app (verified against the greg-site3 sample)

The build environment injects the adapter — the client's repo stays clean. The portable pattern resolves NEXT_ADAPTER_PATH from the installed package, so it works on any host regardless of where node_modules lives (this is exactly what the greg-site3 MediSupplyTech sample uses):

// package.json — the build script the deploy environment runs
"build": "NEXT_ADAPTER_PATH=\"$(node --input-type=module -e \"import{fileURLToPath}from'node:url';process.stdout.write(fileURLToPath(import.meta.resolve('@pantheon-systems/nextjs-build-adapter')))\")\" next build && pantheon-stage"

What the sample integration confirmed:

  • Coexist with an app's own cache handler. If the app already sets cacheHandler in next.config, the adapter detects it and leaves the cache config untouched — it only adds the deployment concerns (standalone output, build-id pinning, manifest emit, staging). It never clobbers an explicit choice; it only removes the requirement to configure one. See src/cache/config.ts.
  • pnpm 11 native-build gotcha. A consumer app on pnpm 11 needs its pnpm-workspace.yaml to opt out of optional native builds so install/tracing is deterministic, mirroring this repo's own .npmrc:
    # pnpm-workspace.yaml
    verifyDepsBeforeRun: false
    allowBuilds:
      sharp: false
      unrs-resolver: false
  • Every Node rendering mode maps cleanly. The sample exercises static, 'use cache' + cacheLife, PPR, generateStaticParams, catch-alls, Node route handlers, metadata routes, and a coexisting Pages Router (appType: hybrid) — all nodejs, no edge.

Edge runtime (unsupported)

Pantheon does not support the Next.js edge runtime. STA serves the standalone Node server behind sta-caddy-proxy, with a Fastly CDN edge in front for caching and invalidation — there is no edge-compute tier. The edge runtime is also deprecated in Next.js, so this is where the platform is heading anyway.

The adapter does not fail a build that still declares the edge runtime — that would break existing apps for no gain. Next executes edge middleware and edge route handlers inside the standalone Node server at origin, so they still run. Instead, onBuildComplete emits a warning nudging the app to migrate to Node, and the emitted manifest.json carries no edge-runtime vocabulary (no runtime, edgeRuntime, hasEdgeRuntime, or compute block) — every route is treated as a Node route.

Migrating off the edge runtime (Next.js 16.2 rules):

| App file | Runtime | Migration | |---|---|---| | middleware.ts | edge by default | Add export const config = { runtime: 'nodejs' }, or move to proxy.ts. | | proxy.ts (the v16 replacement) | Node.js always | Already Node — nothing to do. Setting runtime in a proxy file is a build error. | | Route with export const runtime = 'edge' | edge | Remove the runtime export so the route uses the Node runtime. |

The adapter's cache handler is deliberately left externalized (not in transpilePackages) so that apps which still contain edge code compile instead of failing with The edge runtime does not support Node.js 'fs' module — see src/cache/config.ts.

Environment variables

| Var | Read by | Purpose | |---|---|---| | NEXT_ADAPTER_PATH | Next.js | Path to dist/index.js — the zero-touch load mechanism. | | PANTHEON_BUILD_ID | modifyConfig | Pin a deterministic build id (stable /_next/data/<id>/ URLs on unchanged-source rebuilds). Unset ⇒ Next.js default. | | PANTHEON_DEPLOYMENT_ID | modifyConfig | Bake config.deploymentId for skew protection: the server emits the x-nextjs-deployment-id header and suffixes assets with ?dpl=<id>, so clients on a stale deployment hard-navigate instead of fetching mismatched data. Set a value unique per deployment. Unset ⇒ off (no header/suffix). | | PANTHEON_ADAPTER_MANIFEST_PATH | onBuildComplete, pantheon-stage | Override manifest location (default <distDir>/manifest.json). | | PANTHEON_DIST_DIR | pantheon-stage | Dist dir for staging (default .next, resolved from cwd). | | CACHE_BUCKET | injected cache handler (runtime) | Selects the GCS-backed shared cache (else file-based). Late-bound by tenant-secrets-agent; surfaced in the manifest's image.envRequired. |

Diagnostics go to stderr — the e2e-deploy harness reserves stdout for the deployment URL.

Publishing to npm

The adapter ships to the public npm registry as @pantheon-systems/nextjs-build-adapter (scoped; publishConfig.access is public, so no --access public flag is needed). dist/ is git-ignored, but prepublishOnly (and prepack) run clean && build, so publish always packs a freshly compiled dist/. Releases are cut manually.

Prerequisites: membership in the @pantheon-systems npm org with publish rights, and a clean, tested tree (pnpm test, pnpm lint, pnpm typecheck green).

1. Log in to npm

npm login                       # interactive: username, password/2FA — one-time per machine
npm whoami                      # confirm you're authenticated

For CI/automation, skip npm login and use an npm automation token instead:

npm config set //registry.npmjs.org/:_authToken=$NPM_TOKEN

2. Set the version

package.json is already at 0.1.0 — publish it as-is for the first release. For later releases, bump the version (this writes package.json and creates a git commit + tag):

npm version patch               # 0.1.0 -> 0.1.1  (use minor / major as appropriate)

3. Publish (push to npm)

Inspect the tarball first, then publish:

npm pack --dry-run              # lists what ships: dist/, README.md, LICENSE, package.json
npm publish                     # prepublishOnly rebuilds dist/ automatically
# add --otp=<code> if your npm account enforces 2FA for publishing

pnpm equivalent (also runs prepublishOnly): pnpm publish — add --no-git-checks when publishing from a tree that isn't a clean, tagged commit.

4. Verify

npm view @pantheon-systems/nextjs-build-adapter version

Consumers then add it like any dependency — pnpm add @pantheon-systems/nextjs-build-adapter — and the build environment points NEXT_ADAPTER_PATH at the installed dist/index.js. To rehearse the whole inject + install path against the local package before publishing, use scripts/run-against-sample.sh, which packs the tarball and installs it into a sample app exactly as npm would.

Layout

├── src/
│   ├── index.ts          # the NextAdapter (name, modifyConfig, onBuildComplete)
│   ├── manifest.ts       # BuildManifest type + buildManifest(ctx) (schema-aligned)
│   ├── emit.ts           # writeManifest(ctx) — resolve path + write manifest.json
│   ├── stage.ts          # stageAssets() — copy static/public into standalone tree
│   ├── stage-cli.ts      # `pantheon-stage` bin — post-build staging entry point
│   ├── layout.ts         # standalone output layout (shared by manifest + staging)
│   ├── build-id.ts       # generateBuildId composition from PANTHEON_BUILD_ID
│   ├── deployment-id.ts  # config.deploymentId (skew protection) from PANTHEON_DEPLOYMENT_ID
│   └── cache/
│       ├── config.ts     # cache-handler injection into next.config
│       └── handlers/     # cache-handler + use-cache-handler entry files
├── test/                 # node:test suites (native .ts)
├── e2e/                  # fixtures/ (Pantheon matrix; app-router doubles as the smoke app) + e2e docs
├── scripts/              # e2e harness (e2e-deploy/logs/cleanup) + suite runners
│                         #   run-fixtures.sh + check-manifest.mjs
├── eslint.config.mjs
├── tsconfig.json
└── package.json          # bin: pantheon-stage → dist/stage-cli.js

Status

Adapter implemented: config injection (standalone + Pantheon cache handlers + build-id pinning), manifest emit (schemaVersion 1.1: Node routes/middleware + CDN caching hints), and the post-build pantheon-stage staging CLI, with an e2e smoke test and the official Next.js deploy-mode suite wired in CI. The edge runtime is not supported — builds that use it still succeed (the output runs in the standalone Node server) but the adapter warns and emits no edge-runtime metadata; see Edge runtime. Known follow-ups: emit an experimental.immutableAssetToken so the immutable-asset e2e tests can verify long-TTL caching (see e2e/README.md). The Go orchestration/ layer is not yet scaffolded.