From cda1e89d3edf8a1941d0a0e6a307223bca7a25f0 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Tue, 28 Jul 2026 12:32:22 -0700 Subject: [PATCH 1/8] fixup! --- .github/CODEOWNERS | 6 +- .../playwright-cloudflare-open-next.yml | 9 +- .../tmp-cloudflare-open-next-deploy.yml | 6 +- .gitignore | 2 +- apps/site/app/[locale]/layout.tsx | 11 +- apps/site/eslint.config.js | 10 +- apps/site/instrumentation.ts | 16 +- apps/site/mdx/plugins.mjs | 38 ++--- apps/site/next.config.mjs | 54 +++++-- apps/site/next.constants.cloudflare.mjs | 12 -- apps/site/next.constants.mjs | 26 +--- apps/site/next.image.config.mjs | 33 ---- apps/site/package.json | 40 +++-- apps/site/platform/analytics.tsx | 17 ++ ...wright.config.ts => playwright.config.mjs} | 34 ++-- apps/site/tsconfig.json | 11 +- apps/site/turbo.json | 64 ++------ apps/site/vercel.json | 5 - docs/cloudflare-build-and-deployment.md | 15 +- docs/getting-started.md | 2 +- docs/technologies.md | 4 +- package.json | 2 - platforms/README.md | 55 +++++++ .../cloudflare}/create-vfs-twoslasher.mjs | 2 +- .../cloudflare/image-loader.ts | 0 platforms/cloudflare/next.config.mjs | 24 +++ .../cloudflare}/open-next.config.ts | 0 platforms/cloudflare/package.json | 41 +++++ platforms/cloudflare/playwright.config.mjs | 16 ++ .../scripts/twoslash-fsmap/generate.mjs | 0 .../scripts/twoslash-fsmap/index.mjs | 0 platforms/cloudflare/shiki.mjs | 19 +++ platforms/cloudflare/tsconfig.json | 23 +++ platforms/cloudflare/turbo.json | 9 ++ .../cloudflare/worker-entrypoint.ts | 4 +- .../cloudflare}/wrangler.jsonc | 15 +- platforms/default/next.config.mjs | 9 ++ platforms/default/package.json | 17 ++ platforms/default/playwright.config.mjs | 8 + platforms/vercel/analytics.tsx | 13 ++ platforms/vercel/instrumentation.ts | 3 + platforms/vercel/next.config.mjs | 15 ++ platforms/vercel/package.json | 33 ++++ platforms/vercel/playwright.config.mjs | 8 + platforms/vercel/tsconfig.json | 16 ++ platforms/vercel/vercel.json | 7 + pnpm-lock.yaml | 145 ++++++++++++------ pnpm-workspace.yaml | 2 + 48 files changed, 583 insertions(+), 318 deletions(-) delete mode 100644 apps/site/next.constants.cloudflare.mjs delete mode 100644 apps/site/next.image.config.mjs create mode 100644 apps/site/platform/analytics.tsx rename apps/site/{playwright.config.ts => playwright.config.mjs} (51%) delete mode 100644 apps/site/vercel.json create mode 100644 platforms/README.md rename {apps/site/mdx => platforms/cloudflare}/create-vfs-twoslasher.mjs (91%) rename {apps/site => platforms}/cloudflare/image-loader.ts (100%) create mode 100644 platforms/cloudflare/next.config.mjs rename {apps/site => platforms/cloudflare}/open-next.config.ts (100%) create mode 100644 platforms/cloudflare/package.json create mode 100644 platforms/cloudflare/playwright.config.mjs rename {apps/site => platforms/cloudflare}/scripts/twoslash-fsmap/generate.mjs (100%) rename {apps/site => platforms/cloudflare}/scripts/twoslash-fsmap/index.mjs (100%) create mode 100644 platforms/cloudflare/shiki.mjs create mode 100644 platforms/cloudflare/tsconfig.json create mode 100644 platforms/cloudflare/turbo.json rename {apps/site => platforms}/cloudflare/worker-entrypoint.ts (91%) rename {apps/site => platforms/cloudflare}/wrangler.jsonc (69%) create mode 100644 platforms/default/next.config.mjs create mode 100644 platforms/default/package.json create mode 100644 platforms/default/playwright.config.mjs create mode 100644 platforms/vercel/analytics.tsx create mode 100644 platforms/vercel/instrumentation.ts create mode 100644 platforms/vercel/next.config.mjs create mode 100644 platforms/vercel/package.json create mode 100644 platforms/vercel/playwright.config.mjs create mode 100644 platforms/vercel/tsconfig.json create mode 100644 platforms/vercel/vercel.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 57165054469f8..158da6920f6c7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -17,7 +17,7 @@ apps/site/next.config.mjs @nodejs/web-infra apps/site/next.dynamic.mjs @nodejs/web-infra apps/site/middleware.ts @nodejs/web-infra apps/site/navigation.mjs @nodejs/web-infra -apps/site/playwright.config.ts @nodejs/web-infra +apps/site/playwright.config.mjs @nodejs/web-infra # Package Ecosystem package.json @nodejs/nodejs-website @@ -27,9 +27,7 @@ turbo.json @nodejs/nodejs-website @nodejs/web-infra crowdin.yml @nodejs/web-infra apps/site/redirects.json @nodejs/web-infra apps/site/site.json @nodejs/web-infra -apps/site/wrangler.jsonc @nodejs/web-infra -apps/site/open-next.config.ts @nodejs/web-infra -apps/site/redirects.json @nodejs/web-infra +platforms @nodejs/web-infra # Critical Documents LICENSE @nodejs/tsc diff --git a/.github/workflows/playwright-cloudflare-open-next.yml b/.github/workflows/playwright-cloudflare-open-next.yml index 2af8c17e340b4..d56eed85f9064 100644 --- a/.github/workflows/playwright-cloudflare-open-next.yml +++ b/.github/workflows/playwright-cloudflare-open-next.yml @@ -50,12 +50,11 @@ jobs: working-directory: apps/site run: node_modules/.bin/playwright install --with-deps + - name: Build OpenNext worker + run: pnpm --filter=@node-core/platform-cloudflare build:worker + - name: Run Playwright tests - working-directory: apps/site - run: node --run playwright - env: - PLAYWRIGHT_RUN_CLOUDFLARE_PREVIEW: true - PLAYWRIGHT_BASE_URL: http://127.0.0.1:8787 + run: pnpm --filter=@node-core/platform-cloudflare test:e2e - name: Upload Playwright test results if: always() diff --git a/.github/workflows/tmp-cloudflare-open-next-deploy.yml b/.github/workflows/tmp-cloudflare-open-next-deploy.yml index 758595e6f836a..2a4c118494997 100644 --- a/.github/workflows/tmp-cloudflare-open-next-deploy.yml +++ b/.github/workflows/tmp-cloudflare-open-next-deploy.yml @@ -57,12 +57,10 @@ jobs: run: node --run build:blog-data - name: Build open-next site - working-directory: apps/site - run: node --run cloudflare:build:worker + run: pnpm --filter=@node-core/platform-cloudflare build:worker - name: Deploy open-next site - working-directory: apps/site - run: node --run cloudflare:deploy + run: pnpm --filter=@node-core/platform-cloudflare deploy:worker env: CF_WORKERS_SCRIPTS_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/.gitignore b/.gitignore index d5f3106348e6b..a756afeb5c8ef 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ apps/site/public/blog-data.json apps/site/next-env.d.ts # Generated Build Artifacts -apps/site/generated +platforms/cloudflare/generated # Test Runner junit.xml diff --git a/apps/site/app/[locale]/layout.tsx b/apps/site/app/[locale]/layout.tsx index 5e1e440c5b974..b8ecd4cda027e 100644 --- a/apps/site/app/[locale]/layout.tsx +++ b/apps/site/app/[locale]/layout.tsx @@ -1,12 +1,10 @@ import { availableLocales, defaultLocale } from '@node-core/website-i18n'; -import { Analytics } from '@vercel/analytics/react'; -import { SpeedInsights } from '@vercel/speed-insights/next'; import classNames from 'classnames'; import { NextIntlClientProvider } from 'next-intl'; import BaseLayout from '#site/layouts/Base'; -import { VERCEL_ENV } from '#site/next.constants.mjs'; import { IBM_PLEX_MONO, OPEN_SANS } from '#site/next.fonts'; +import PlatformAnalytics from '#site/platform/analytics'; import { ThemeProvider } from '#site/providers/themeProvider'; import type { FC, PropsWithChildren } from 'react'; @@ -46,12 +44,7 @@ const RootLayout: FC = async ({ children, params }) => { href="https://social.lfx.dev/@nodejs" /> - {VERCEL_ENV && ( - <> - - - - )} + ); diff --git a/apps/site/eslint.config.js b/apps/site/eslint.config.js index 7971c2c082de1..2d4ee148ad97a 100644 --- a/apps/site/eslint.config.js +++ b/apps/site/eslint.config.js @@ -6,15 +6,7 @@ import baseConfig from '../../eslint.config.js'; export default baseConfig.concat([ { - ignores: [ - 'pages/en/blog/**/*.{md,mdx}/**', - 'public', - 'next-env.d.ts', - // The worker entrypoint is bundled by wrangler, not tsc. Its imports - // trigger a tsc crash (see tsconfig.json), so it is excluded from both - // type checking and ESLint's type-aware linting. - 'cloudflare/worker-entrypoint.ts', - ], + ignores: ['pages/en/blog/**/*.{md,mdx}/**', 'public', 'next-env.d.ts'], }, eslintReact.configs['recommended-typescript'], diff --git a/apps/site/instrumentation.ts b/apps/site/instrumentation.ts index 86097a48800b1..96075886a0d44 100644 --- a/apps/site/instrumentation.ts +++ b/apps/site/instrumentation.ts @@ -1,7 +1,11 @@ -export async function register() { - if (!('Cloudflare' in globalThis)) { - // Note: we don't need to set up the Vercel OTEL if the application is running on Cloudflare - const { registerOTel } = await import('@vercel/otel'); - registerOTel({ serviceName: 'nodejs-org' }); +export const register = async () => { + // `NEXT_PUBLIC_DEPLOYMENT_PLATFORM` is a build-time constant (see + // `next.config.mjs`), so the platforms we are not building for are never + // bundled. Only Vercel currently provides instrumentation. + if (process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM === 'vercel') { + const { register } = + await import('@node-core/platform-vercel/instrumentation'); + + register(); } -} +}; diff --git a/apps/site/mdx/plugins.mjs b/apps/site/mdx/plugins.mjs index 15efe01c45c4a..a5f375a5d0fa4 100644 --- a/apps/site/mdx/plugins.mjs +++ b/apps/site/mdx/plugins.mjs @@ -7,35 +7,19 @@ import rehypeSlug from 'rehype-slug'; import remarkGfm from 'remark-gfm'; import readingTime from 'remark-reading-time'; -import { createVfsTwoslasher } from './create-vfs-twoslasher.mjs'; import remarkTableTitles from '../util/table'; -// TODO(@avivkeller): When available, use `OPEN_NEXT_CLOUDFLARE` environment -// variable for detection instead of current method, which will enable better -// tree-shaking. -// Reference: https://github.com/nodejs/nodejs.org/pull/7896#issuecomment-3009480615 -const OPEN_NEXT_CLOUDFLARE = 'Cloudflare' in global; - -// Shiki is created out here to avoid an async rehype plugin -const singletonShiki = await rehypeShikiji({ - // We use the faster WASM engine on the server instead of the web-optimized version. - // - // Currently we fall back to the JavaScript RegEx engine - // on Cloudflare workers because `shiki/wasm` requires loading via - // `WebAssembly.instantiate` with custom imports, which Cloudflare doesn't support - // for security reasons. - wasm: !OPEN_NEXT_CLOUDFLARE, - - twoslash: true, - - // On Cloudflare Workers, the default filesystem-backed Twoslash cannot work - // because there is no real filesystem. Instead, we provide a custom twoslasher - // backed by an in-memory VFS pre-populated at build time with TypeScript - // lib declarations and @types/node. - twoslashOptions: OPEN_NEXT_CLOUDFLARE - ? { twoslasher: await createVfsTwoslasher() } - : undefined, -}); +// Shiki is created out here to avoid an async rehype plugin. +// +// `NEXT_PUBLIC_DEPLOYMENT_PLATFORM` is a build-time constant (see +// `next.config.mjs`), so the Cloudflare-specific options (which cannot use +// the WASM engine nor a filesystem-backed Twoslash) are only bundled on +// Cloudflare builds; everywhere else we use the faster WASM engine. +const singletonShiki = await rehypeShikiji( + process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM === 'cloudflare' + ? (await import('@node-core/platform-cloudflare/shiki.mjs')).shikiOptions + : { wasm: true, twoslash: true } +); /** * Provides all our Rehype Plugins that are used within MDX diff --git a/apps/site/next.config.mjs b/apps/site/next.config.mjs index 40d1f89e87cd3..7ea29629ffef0 100644 --- a/apps/site/next.config.mjs +++ b/apps/site/next.config.mjs @@ -1,22 +1,23 @@ 'use strict'; import createNextIntlPlugin from 'next-intl/plugin'; -import { OPEN_NEXT_CLOUDFLARE } from './next.constants.cloudflare.mjs'; import { BASE_PATH, ENABLE_STATIC_EXPORT } from './next.constants.mjs'; -import { getImagesConfig } from './next.image.config.mjs'; import { redirects, rewrites } from './next.rewrites.mjs'; -const getDeploymentId = async () => { - if (OPEN_NEXT_CLOUDFLARE) { - // If we're building for the Cloudflare deployment we want to set - // an appropriate deploymentId (needed for skew protection) - const openNextAdapter = await import('@opennextjs/cloudflare'); +/** + * The deployment platform this build targets. All platform-specific behavior + * lives in the `platforms/*` workspace packages; see `platforms/README.md`. + * + * Vercel deployments are detected automatically; the Cloudflare build scripts + * set the variable themselves. + */ +const DEPLOYMENT_PLATFORM = + process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM || + (process.env.VERCEL ? 'vercel' : 'default'); - return openNextAdapter.getDeploymentId(); - } - - return undefined; -}; +const { default: platformConfig } = await import( + `@node-core/platform-${DEPLOYMENT_PLATFORM}/next.config.mjs` +); /** @type {import('next').NextConfig} */ const nextConfig = { @@ -27,8 +28,19 @@ const nextConfig = { // We allow the BASE_PATH to be overridden in case that the Website // is being built on a subdirectory (e.g. /nodejs-website) basePath: BASE_PATH, - // Vercel/Next.js Image Optimization Settings - images: getImagesConfig(), + images: { + // We disable image optimisation during static export builds + unoptimized: ENABLE_STATIC_EXPORT, + // We add it to the remote pattern for the static images we use from multiple sources + // to be marked as safe sources (these come from Markdown files) + remotePatterns: [ + 'https://avatars.githubusercontent.com/**', + 'https://bestpractices.coreinfrastructure.org/**', + 'https://raw.githubusercontent.com/nodejs/**', + 'https://user-images.githubusercontent.com/**', + 'https://website-assets.oramasearch.com/**', + ].map(url => new URL(url)), + }, serverExternalPackages: ['twoslash'], outputFileTracingIncludes: { // Twoslash needs TypeScript declarations to function, and, by default, Next.js @@ -81,8 +93,18 @@ const nextConfig = { // Faster Development Servers with Turbopack turbopackFileSystemCacheForDev: true, }, - deploymentId: await getDeploymentId(), }; const withNextIntl = createNextIntlPlugin('./i18n.tsx'); -export default withNextIntl(nextConfig); + +export default withNextIntl({ + ...nextConfig, + ...platformConfig, + env: { + // Inlined into the bundles at build time so runtime code (e.g. + // `#site/platform/analytics`) can branch on the platform, letting + // bundlers drop the branches (and packages) of the other platforms. + NEXT_PUBLIC_DEPLOYMENT_PLATFORM: DEPLOYMENT_PLATFORM, + ...platformConfig.env, + }, +}); diff --git a/apps/site/next.constants.cloudflare.mjs b/apps/site/next.constants.cloudflare.mjs deleted file mode 100644 index a87b3ed2a5214..0000000000000 --- a/apps/site/next.constants.cloudflare.mjs +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -/** - * Whether the build process is targeting the Cloudflare open-next build or not. - * - * TODO: The `OPEN_NEXT_CLOUDFLARE` environment variable is being - * defined in the worker building script, ideally the open-next - * adapter should set it itself when it invokes the Next.js build - * process, once it does that remove the manual `OPEN_NEXT_CLOUDFLARE` - * definition in the package.json script. - */ -export const OPEN_NEXT_CLOUDFLARE = process.env.OPEN_NEXT_CLOUDFLARE; diff --git a/apps/site/next.constants.mjs b/apps/site/next.constants.mjs index 1be9ba0ced14c..3906589b02a35 100644 --- a/apps/site/next.constants.mjs +++ b/apps/site/next.constants.mjs @@ -5,15 +5,6 @@ */ export const IS_DEV_ENV = process.env.NODE_ENV === 'development'; -/** - * This is used for telling Next.js if the Website is deployed on Vercel - * - * Can be used for conditionally enabling features that we know are Vercel only - * - * @see https://vercel.com/docs/projects/environment-variables/system-environment-variables#VERCEL_ENV - */ -export const VERCEL_ENV = process.env.VERCEL_ENV || undefined; - /** * This is used for telling Next.js to do a Static Export Build of the Website * @@ -40,18 +31,13 @@ export const ENABLE_STATIC_EXPORT_LOCALE = /** * This is used for any place that requires the full canonical URL path for the Node.js Website (and its deployment), such as for example, the Node.js RSS Feed. * - * This variable can either come from the Vercel Deployment as `NEXT_PUBLIC_VERCEL_URL` or from the `NEXT_PUBLIC_BASE_URL` Environment Variable that is manually defined - * by us if necessary. Otherwise it will fallback to the default Node.js Website URL. - * - * @TODO: We should get rid of needing to rely on `VERCEL_URL` for deployment URL. - * - * @see https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#framework-environment-variables + * Platform-specific base URLs (such as Vercel's `VERCEL_URL` on preview + * deployments) are inlined into `NEXT_PUBLIC_BASE_URL` at build time by the + * platform's `next.config.mjs` fragment (see `platforms/README.md`), keeping + * this module free of platform-specific branches. */ -export const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL - ? process.env.NEXT_PUBLIC_BASE_URL - : process.env.VERCEL_URL - ? `https://${process.env.VERCEL_URL}` - : 'https://nodejs.org'; +export const BASE_URL = + process.env.NEXT_PUBLIC_BASE_URL || 'https://nodejs.org'; /** * This is used for any place that requires the Node.js distribution URL (which by default is nodejs.org/dist) diff --git a/apps/site/next.image.config.mjs b/apps/site/next.image.config.mjs deleted file mode 100644 index 519523ca2aafe..0000000000000 --- a/apps/site/next.image.config.mjs +++ /dev/null @@ -1,33 +0,0 @@ -import { OPEN_NEXT_CLOUDFLARE } from './next.constants.cloudflare.mjs'; -import { ENABLE_STATIC_EXPORT } from './next.constants.mjs'; - -const remotePatterns = [ - 'https://avatars.githubusercontent.com/**', - 'https://bestpractices.coreinfrastructure.org/**', - 'https://raw.githubusercontent.com/nodejs/**', - 'https://user-images.githubusercontent.com/**', - 'https://website-assets.oramasearch.com/**', -]; - -export const getImagesConfig = () => { - if (OPEN_NEXT_CLOUDFLARE) { - // If we're building for the Cloudflare deployment we want to use the custom cloudflare image loader - // - // Important: The custom loader ignores `remotePatterns` as those are configured as allowed source origins - // (https://developers.cloudflare.com/images/transform-images/sources/) - // in the Cloudflare dashboard itself instead (to the exact same values present in `remotePatterns` above). - // - return { - loader: 'custom', - loaderFile: './cloudflare/image-loader.ts', - }; - } - - return { - // We disable image optimisation during static export builds - unoptimized: ENABLE_STATIC_EXPORT, - // We add it to the remote pattern for the static images we use from multiple sources - // to be marked as safe sources (these come from Markdown files) - remotePatterns: remotePatterns.map(url => new URL(url)), - }; -}; diff --git a/apps/site/package.json b/apps/site/package.json index 35b1ab64fafaf..c25174317e098 100644 --- a/apps/site/package.json +++ b/apps/site/package.json @@ -2,14 +2,10 @@ "name": "@node-core/website", "type": "module", "scripts": { - "prebuild": "node --run build:blog-data && node --run build:twoslash-fsmap", + "prebuild": "node --run build:blog-data", "build": "cross-env NODE_NO_WARNINGS=1 next build", "build:blog-data": "cross-env NODE_NO_WARNINGS=1 node ./scripts/blog-data/index.mjs", "build:blog-data:watch": "node --watch --watch-path=pages/en/blog ./scripts/blog-data/index.mjs", - "build:twoslash-fsmap": "node ./scripts/twoslash-fsmap/index.mjs", - "cloudflare:build:worker": "OPEN_NEXT_CLOUDFLARE=true opennextjs-cloudflare build", - "cloudflare:deploy": "opennextjs-cloudflare deploy", - "cloudflare:preview": "wrangler dev", "predeploy": "node --run build:blog-data", "deploy": "cross-env NEXT_PUBLIC_STATIC_EXPORT=true node --run build", "predev": "node --run build:blog-data", @@ -38,28 +34,20 @@ "@node-core/ui-components": "workspace:*", "@node-core/website-i18n": "workspace:*", "@nodevu/core": "0.3.0", - "@opentelemetry/api-logs": "~0.218.0", - "@opentelemetry/instrumentation": "~0.218.0", - "@opentelemetry/resources": "~1.30.1", - "@opentelemetry/sdk-logs": "~0.218.0", "@orama/orama": "^3.1.18", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", "@tailwindcss/postcss": "~4.3.0", "@types/node": "catalog:", "@types/react": "catalog:", - "@typescript/vfs": "^1.6.4", "@vcarl/remark-headings": "~0.1.0", - "@vercel/analytics": "~2.0.1", - "@vercel/otel": "~2.1.2", - "@vercel/speed-insights": "~2.0.0", "classnames": "catalog:", "cross-env": "catalog:", "feed": "~5.2.0", "github-slugger": "~2.0.0", "gray-matter": "~4.0.3", "mdast-util-to-string": "^4.0.0", - "next": "16.2.6", + "next": "catalog:", "next-intl": "~4.13.0", "next-themes": "~0.4.6", "postcss-calc": "~10.1.1", @@ -73,22 +61,17 @@ "semver": "~7.8.1", "sval": "^0.6.12", "tailwindcss": "catalog:", - "twoslash": "^0.3.8", "typescript": "catalog:", "unist-util-visit": "^5.1.0", "vfile": "~6.0.3", "vfile-matter": "~5.0.1" }, "devDependencies": { - "@cloudflare/workers-types": "^4.20260418.1", "@eslint-react/eslint-plugin": "~5.8.6", - "@flarelabs-net/wrangler-build-time-fs-assets-polyfilling": "^0.0.1", "@next/eslint-plugin-next": "16.2.6", "@node-core/remark-lint": "workspace:*", - "@opennextjs/cloudflare": "^1.19.3", "@orama/core": "^1.2.19", "@playwright/test": "^1.59.1", - "@sentry/cloudflare": "^10.49.0", "@testing-library/user-event": "~14.6.1", "@types/mdast": "^4.0.4", "@types/mdx": "^2.0.13", @@ -109,8 +92,23 @@ "tsx": "^4.22.3", "typescript": "catalog:", "typescript-eslint": "~8.59.1", - "user-agent-data-types": "0.4.2", - "wrangler": "^4.77.0" + "user-agent-data-types": "0.4.2" + }, + "peerDependencies": { + "@node-core/platform-cloudflare": "workspace:*", + "@node-core/platform-default": "workspace:*", + "@node-core/platform-vercel": "workspace:*" + }, + "peerDependenciesMeta": { + "@node-core/platform-cloudflare": { + "optional": true + }, + "@node-core/platform-default": { + "optional": true + }, + "@node-core/platform-vercel": { + "optional": true + } }, "imports": { "#site/*": [ diff --git a/apps/site/platform/analytics.tsx b/apps/site/platform/analytics.tsx new file mode 100644 index 0000000000000..737476b8c5428 --- /dev/null +++ b/apps/site/platform/analytics.tsx @@ -0,0 +1,17 @@ +import type { FC } from 'react'; + +// `NEXT_PUBLIC_DEPLOYMENT_PLATFORM` is a build-time constant (see +// `next.config.mjs`), so the platforms we are not building for are never +// bundled. Only Vercel currently provides analytics. +const PlatformAnalytics: FC = async () => { + if (process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM === 'vercel') { + const { default: VercelAnalytics } = + await import('@node-core/platform-vercel/analytics'); + + return ; + } + + return null; +}; + +export default PlatformAnalytics; diff --git a/apps/site/playwright.config.ts b/apps/site/playwright.config.mjs similarity index 51% rename from apps/site/playwright.config.ts rename to apps/site/playwright.config.mjs index 523f40f644582..5e86aa6ec0025 100644 --- a/apps/site/playwright.config.ts +++ b/apps/site/playwright.config.mjs @@ -1,9 +1,15 @@ -import { defineConfig, devices, type Config } from '@playwright/test'; - -import json from './package.json' with { type: 'json' }; +import { defineConfig, devices } from '@playwright/test'; const isCI = !!process.env.CI; +// The active deployment platform can contribute configuration (e.g. the +// Cloudflare preview web server); see `platforms/README.md`. +const platform = process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM || 'default'; + +const { default: platformConfig } = await import( + `@node-core/platform-${platform}/playwright.config.mjs` +); + // https://playwright.dev/docs/test-configuration export default defineConfig({ testDir: './tests/e2e', @@ -12,10 +18,11 @@ export default defineConfig({ retries: isCI ? 2 : 0, workers: isCI ? 1 : undefined, reporter: isCI ? [['html'], ['github']] : [['html']], - ...getWebServerConfig(), + ...platformConfig, use: { baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://127.0.0.1:3000', trace: 'on-first-retry', + ...platformConfig.use, }, projects: [ { @@ -32,22 +39,3 @@ export default defineConfig({ }, ], }); - -function getWebServerConfig(): Pick { - if (!json.scripts['cloudflare:preview']) { - throw new Error('cloudflare:preview script not defined'); - } - - if (process.env.PLAYWRIGHT_RUN_CLOUDFLARE_PREVIEW) { - return { - webServer: { - stdout: 'pipe', - command: '../../node_modules/.bin/turbo cloudflare:preview', - url: process.env.PLAYWRIGHT_BASE_URL || 'http://127.0.0.1:3000', - timeout: 60_000 * 3, - }, - }; - } - - return {}; -} diff --git a/apps/site/tsconfig.json b/apps/site/tsconfig.json index 02158236da303..334433e743485 100644 --- a/apps/site/tsconfig.json +++ b/apps/site/tsconfig.json @@ -37,14 +37,5 @@ ".next/types/**/*.ts", ".next/dev/types/**/*.ts" ], - "exclude": [ - "node_modules", - ".next", - ".open-next", - // The worker entrypoint is bundled by wrangler (not tsc). Its imports of - // @sentry/cloudflare and .open-next/worker.js trigger an infinite-recursion - // crash in the TypeScript compiler (v5.9) during type resolution of - // @cloudflare/workers-types, so we exclude it from type checking. - "cloudflare/worker-entrypoint.ts" - ] + "exclude": ["node_modules", ".next", ".open-next"] } diff --git a/apps/site/turbo.json b/apps/site/turbo.json index a4977e9cb8273..5ff017a617ba0 100644 --- a/apps/site/turbo.json +++ b/apps/site/turbo.json @@ -3,11 +3,12 @@ "extends": ["//"], "tasks": { "dev": { - "dependsOn": ["build:blog-data", "build:twoslash-fsmap", "^build"], + "dependsOn": ["build:blog-data", "^build"], "cache": false, "persistent": true, "env": [ - "VERCEL_ENV", + "NEXT_PUBLIC_DEPLOYMENT_PLATFORM", + "VERCEL", "VERCEL_URL", "NEXT_PUBLIC_STATIC_EXPORT", "NEXT_PUBLIC_STATIC_EXPORT_LOCALE", @@ -22,9 +23,9 @@ ] }, "build": { - "dependsOn": ["build:blog-data", "build:twoslash-fsmap", "^build"], + "dependsOn": ["build:blog-data", "^build"], "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx}", + "{app,components,hooks,i18n,layouts,middlewares,pages,platform,providers,types,util}/**/*.{ts,tsx}", "{app,components,layouts,pages,styles}/**/*.css", "{next-data,scripts,i18n}/**/*.{mjs,json}", "{app,pages}/**/*.{mdx,md}", @@ -32,7 +33,8 @@ ], "outputs": [".next/**", "!.next/cache/**"], "env": [ - "VERCEL_ENV", + "NEXT_PUBLIC_DEPLOYMENT_PLATFORM", + "VERCEL", "VERCEL_URL", "NEXT_PUBLIC_STATIC_EXPORT", "NEXT_PUBLIC_STATIC_EXPORT_LOCALE", @@ -51,7 +53,8 @@ "cache": false, "persistent": true, "env": [ - "VERCEL_ENV", + "NEXT_PUBLIC_DEPLOYMENT_PLATFORM", + "VERCEL", "VERCEL_URL", "NEXT_PUBLIC_STATIC_EXPORT", "NEXT_PUBLIC_STATIC_EXPORT_LOCALE", @@ -67,7 +70,7 @@ }, "deploy": { "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx}", + "{app,components,hooks,i18n,layouts,middlewares,pages,platform,providers,types,util}/**/*.{ts,tsx}", "{app,components,layouts,pages,styles}/**/*.css", "{next-data,scripts,i18n}/**/*.{mjs,json}", "{app,pages}/**/*.{mdx,md}", @@ -75,7 +78,8 @@ ], "outputs": [".next/**", "!.next/cache/**"], "env": [ - "VERCEL_ENV", + "NEXT_PUBLIC_DEPLOYMENT_PLATFORM", + "VERCEL", "VERCEL_URL", "NEXT_PUBLIC_STATIC_EXPORT", "NEXT_PUBLIC_STATIC_EXPORT_LOCALE", @@ -91,7 +95,7 @@ }, "lint:js": { "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx,mjs}", + "{app,components,hooks,i18n,layouts,middlewares,pages,platform,providers,types,util}/**/*.{ts,tsx,mjs}", "{next-data,scripts,i18n}/**/*.{mjs,json}", "public/**/*.{ts,js,json}", "*.{json,ts,tsx}" @@ -115,9 +119,9 @@ "cache": false }, "test:unit": { - "dependsOn": ["build:blog-data", "build:twoslash-fsmap", "^build"], + "dependsOn": ["build:blog-data", "^build"], "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx,mjs}", + "{app,components,hooks,i18n,layouts,middlewares,pages,platform,providers,types,util}/**/*.{ts,tsx,mjs}", "{app,components,layouts,pages,styles}/**/*.css", "{next-data,scripts,i18n}/**/*.{mjs,json}", "{app,pages}/**/*.{mdx,md}", @@ -129,7 +133,6 @@ "inputs": ["{pages}/**/*.{mdx,md}"], "outputs": ["public/blog-data.json"], "env": [ - "VERCEL_ENV", "VERCEL_URL", "TURBO_CACHE", "TURBO_TELEMETRY_DISABLED", @@ -137,43 +140,6 @@ "ENABLE_EXPERIMENTAL_COREPACK" ] }, - "build:twoslash-fsmap": { - "inputs": ["scripts/twoslash-fsmap/**", "../../pnpm-lock.yaml"], - "outputs": ["generated/twoslash-fsmap.json"] - }, - "cloudflare:build:worker": { - "dependsOn": ["build:blog-data", "build:twoslash-fsmap", "^build"], - "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx}", - "{app,components,layouts,pages,styles}/**/*.css", - "{next-data,scripts,i18n}/**/*.{mjs,json}", - "{app,pages}/**/*.{mdx,md}", - "*.{md,mdx,json,ts,tsx,mjs,yml}" - ], - "outputs": [".open-next/**"] - }, - "cloudflare:preview": { - "dependsOn": ["cloudflare:build:worker"], - "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx}", - "{app,components,layouts,pages,styles}/**/*.css", - "{next-data,scripts,i18n}/**/*.{mjs,json}", - "{app,pages}/**/*.{mdx,md}", - "*.{md,mdx,json,ts,tsx,mjs,yml}" - ], - "outputs": [".open-next/**"] - }, - "cloudflare:deploy": { - "dependsOn": ["cloudflare:build:worker"], - "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx}", - "{app,components,layouts,pages,styles}/**/*.css", - "{next-data,scripts,i18n}/**/*.{mjs,json}", - "{app,pages}/**/*.{mdx,md}", - "*.{md,mdx,json,ts,tsx,mjs,yml}" - ], - "outputs": [".open-next/**"] - }, "scripts:release-post": { "cache": false } diff --git a/apps/site/vercel.json b/apps/site/vercel.json deleted file mode 100644 index b767ff0b425cd..0000000000000 --- a/apps/site/vercel.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://openapi.vercel.sh/vercel.json", - "installCommand": "pnpm install --frozen-lockfile", - "ignoreCommand": "[[ \"$VERCEL_GIT_COMMIT_REF\" =~ \"^dependabot/.*\" || \"$VERCEL_GIT_COMMIT_REF\" =~ \"^gh-readonly-queue/.*\" ]]" -} diff --git a/docs/cloudflare-build-and-deployment.md b/docs/cloudflare-build-and-deployment.md index 4ef60cac192a7..282e24c6b2260 100644 --- a/docs/cloudflare-build-and-deployment.md +++ b/docs/cloudflare-build-and-deployment.md @@ -14,7 +14,7 @@ For more details, refer to the [Wrangler documentation](https://developers.cloud Key configurations include: -- `main`: Points to a custom worker entry point ([`site/cloudflare/worker-entrypoint.ts`](../apps/site/cloudflare/worker-entrypoint.ts)) that wraps the OpenNext-generated worker (see [Custom Worker Entry Point](#custom-worker-entry-point) and [Sentry](#sentry) below). +- `main`: Points to a custom worker entry point ([`platforms/cloudflare/worker-entrypoint.ts`](../platforms/cloudflare/worker-entrypoint.ts)) that wraps the OpenNext-generated worker (see [Custom Worker Entry Point](#custom-worker-entry-point) and [Sentry](#sentry) below). - `account_id`: Specifies the Cloudflare account ID. This is not required for local previews but is necessary for deployments. You can obtain an account ID for free by signing up at [dash.cloudflare.com](https://dash.cloudflare.com/login). - This is set to `07be8d2fbc940503ca1be344714cb0d1`, which is the ID of a Cloudflare account controlled by Node.js. - `build`: Defines the build command to generate the Node.js filesystem polyfills required for the application to run on Cloudflare Workers. This uses the [`@flarelabs/wrangler-build-time-fs-assets-polyfilling`](https://github.com/flarelabs-net/wrangler-build-time-fs-assets-polyfilling) package. @@ -49,15 +49,15 @@ Additionally, when deploying, an extra `CF_WORKERS_SCRIPTS_API_TOKEN` environmen ### Image loader -When deployed on the Cloudflare network a custom image loader is required. We set such loader in the Next.js config file when the `OPEN_NEXT_CLOUDFLARE` environment variable is set (which indicates that we're building the application for the Cloudflare deployment). +When deployed on the Cloudflare network a custom image loader is required. The Cloudflare platform's Next.js configuration fragment ([`platforms/cloudflare/next.config.mjs`](../platforms/cloudflare/next.config.mjs)) sets such loader when building for the Cloudflare deployment (see [`platforms/README.md`](../platforms/README.md)). -The custom loader can be found at [`site/cloudflare/image-loader.ts`](../apps/site/cloudflare/image-loader.ts). +The custom loader can be found at [`platforms/cloudflare/image-loader.ts`](../platforms/cloudflare/image-loader.ts). For more details on this see: https://developers.cloudflare.com/images/transform-images/integrate-with-frameworks/#global-loader ### Custom Worker Entry Point -Instead of directly using the OpenNext-generated worker (`.open-next/worker.js`), the application uses a custom worker entry point at [`site/cloudflare/worker-entrypoint.ts`](../apps/site/cloudflare/worker-entrypoint.ts). This allows customizing the worker's behavior before requests are handled (currently used to integrate [Sentry](#sentry) error monitoring). +Instead of directly using the OpenNext-generated worker (`.open-next/worker.js`), the application uses a custom worker entry point at [`platforms/cloudflare/worker-entrypoint.ts`](../platforms/cloudflare/worker-entrypoint.ts). This allows customizing the worker's behavior before requests are handled (currently used to integrate [Sentry](#sentry) error monitoring). The custom entry point imports the OpenNext-generated handler from `.open-next/worker.js` and re-exports the `DOQueueHandler` Durable Object needed by the application. @@ -75,7 +75,8 @@ For more details, refer to the [Sentry Cloudflare guide](https://docs.sentry.io/ ## Scripts -Preview and deployment of the website targeting the Cloudflare network is implemented via the following two commands: +Building, previewing, and deploying the website targeting the Cloudflare network is implemented via the following commands (all part of the [`@node-core/platform-cloudflare`](../platforms/cloudflare/package.json) package): -- `pnpm cloudflare:preview` builds the website using the OpenNext Cloudflare adapter and runs the website locally in a server simulating the Cloudflare hosting (using the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)) -- `pnpm cloudflare:deploy` builds the website using the OpenNext Cloudflare adapter and deploys the website to the Cloudflare network (using the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)) +- `pnpm --filter=@node-core/platform-cloudflare build:worker` builds the website using the OpenNext Cloudflare adapter +- `pnpm --filter=@node-core/platform-cloudflare preview` runs a previously built worker locally in a server simulating the Cloudflare hosting (using the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)) +- `pnpm --filter=@node-core/platform-cloudflare deploy:worker` builds the website using the OpenNext Cloudflare adapter and deploys it to the Cloudflare network (using the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)) diff --git a/docs/getting-started.md b/docs/getting-started.md index 731ff649b5b1d..273deef787d25 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -108,7 +108,7 @@ This guide will help you set up your development environment and make your first ### Development Commands - `pnpm dev` - Start local development server -- `pnpm build` - Build for production (Vercel deployments) +- `pnpm build` - Build for production (default platform, see [`platforms/README.md`](../platforms/README.md)) - `pnpm deploy` - Build for export (Legacy server) - `pnpm start` - Start server with built content diff --git a/docs/technologies.md b/docs/technologies.md index e54940426bc72..9d15d9f50a961 100644 --- a/docs/technologies.md +++ b/docs/technologies.md @@ -293,14 +293,14 @@ Benefits: #### Multiple Build Targets -- **`pnpm build`**: Production build for Vercel +- **`pnpm build`**: Production build for the default platform (see [`platforms/README.md`](../platforms/README.md)) - **`pnpm deploy`**: Export build for legacy servers - **`pnpm dev`**: Development server #### Vercel Integration - Automatic deployments for branches (ignoring automated branches) -- Custom install + ignore scripts ([see `vercel.json`](../apps/site/vercel.json)) +- Custom install + ignore scripts ([see `vercel.json`](../platforms/vercel/vercel.json)) - Build-time dependencies must be in `dependencies`, not `devDependencies` - Sponsorship maintained by OpenJS Foundation diff --git a/package.json b/package.json index 245c26d4e752e..31eeaee9d445c 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,6 @@ "build": "turbo build", "changeset": "changeset", "changeset:version": "changeset version", - "cloudflare:deploy": "turbo cloudflare:deploy", - "cloudflare:preview": "turbo cloudflare:preview", "deploy": "turbo deploy", "dev": "turbo dev", "format": "turbo //#prettier:fix prettier:fix lint:fix", diff --git a/platforms/README.md b/platforms/README.md new file mode 100644 index 0000000000000..60a26c122d9ed --- /dev/null +++ b/platforms/README.md @@ -0,0 +1,55 @@ +# Deployment Platforms + +The Node.js Website can be deployed to multiple hosting platforms. Everything +that is specific to one platform (analytics, instrumentation, image loaders, +deployment configuration and their dependencies) lives in one of the workspace +packages in this directory, keeping `apps/site` platform-agnostic: + +- [`default`](./default) — self-hosted deployments: plain `next build` / + `next start` and the legacy static export (`pnpm deploy`). This is also what + local development uses. +- [`vercel`](./vercel) — the Vercel deployment of . +- [`cloudflare`](./cloudflare) — the Cloudflare Workers deployment built with + the [OpenNext Cloudflare adapter](https://opennext.js.org/cloudflare) (see + [`docs/cloudflare-build-and-deployment.md`](../docs/cloudflare-build-and-deployment.md)). + +## Platform selection + +The active platform is selected at build time through the +`NEXT_PUBLIC_DEPLOYMENT_PLATFORM` environment variable (`vercel`, +`cloudflare`, or unset for `default`). Vercel deployments are detected +automatically through the `VERCEL` environment variable, and the Cloudflare +build scripts in [`cloudflare/package.json`](./cloudflare/package.json) set the +variable themselves, so it only needs to be set manually for unusual setups. + +`apps/site/next.config.mjs` resolves the variable once, merges the platform's +`next.config.mjs` fragment into the shared configuration, and inlines the +resolved value back into the bundles via the Next.js `env` config. Runtime +code then branches on `process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM`: since +the value is a build-time constant, bundlers eliminate the branches (and the +platform packages they import) that belong to other platforms. + +> [!NOTE] +> This is intentionally **not** implemented with conditional +> [subpath imports](https://nodejs.org/api/packages.html#subpath-imports) +> selected via Node.js `--conditions`: custom resolution conditions are only +> honored by Node.js itself, not by webpack or Turbopack, so bundled code +> would silently fall back to the `default` condition. + +## Platform interface + +Each platform package provides: + +- `next.config.mjs` — a partial Next.js configuration merged over the shared + configuration in `apps/site/next.config.mjs`. +- `playwright.config.mjs` — a partial Playwright configuration merged over the + shared configuration in `apps/site/playwright.config.mjs`. + +Additional platform-specific modules (for example `vercel/analytics.tsx` or +`cloudflare/shiki.mjs`) are imported from the platform branches in +`apps/site/platform/` and `apps/site/mdx/plugins.mjs`. + +The platform packages are declared as optional `peerDependencies` of +`@node-core/website` so that a deployment can install only the platform it +needs (see the `installCommand` in [`vercel/vercel.json`](./vercel/vercel.json)), +while a regular workspace install makes all of them available. diff --git a/apps/site/mdx/create-vfs-twoslasher.mjs b/platforms/cloudflare/create-vfs-twoslasher.mjs similarity index 91% rename from apps/site/mdx/create-vfs-twoslasher.mjs rename to platforms/cloudflare/create-vfs-twoslasher.mjs index d4c34d1655a0c..264b18696ae86 100644 --- a/apps/site/mdx/create-vfs-twoslasher.mjs +++ b/platforms/cloudflare/create-vfs-twoslasher.mjs @@ -11,7 +11,7 @@ export async function createVfsTwoslasher() { const { createTwoslasher } = await import('twoslash/core'); const ts = (await import('typescript')).default; const fsMapJson = ( - await import('../generated/twoslash-fsmap.json', { with: { type: 'json' } }) + await import('./generated/twoslash-fsmap.json', { with: { type: 'json' } }) ).default; const fsMap = new Map(Object.entries(fsMapJson)); diff --git a/apps/site/cloudflare/image-loader.ts b/platforms/cloudflare/image-loader.ts similarity index 100% rename from apps/site/cloudflare/image-loader.ts rename to platforms/cloudflare/image-loader.ts diff --git a/platforms/cloudflare/next.config.mjs b/platforms/cloudflare/next.config.mjs new file mode 100644 index 0000000000000..44d81f0147389 --- /dev/null +++ b/platforms/cloudflare/next.config.mjs @@ -0,0 +1,24 @@ +import { getDeploymentId } from '@opennextjs/cloudflare'; + +/** + * Next.js configuration for the Cloudflare deployment. See + * `platforms/README.md`. + * + * @type {import('next').NextConfig} + */ +export default { + // Skew protection: Cloudflare routes requests by deploymentId so a client + // and the worker stay in sync across rolling deploys. + deploymentId: await getDeploymentId(), + images: { + // Cloudflare image optimization requires a custom loader. Note that this + // replaces the shared `images` configuration: `remotePatterns` do not + // apply, as allowed source origins are enforced at the edge instead + // (configured in the Cloudflare dashboard to the same values). + // https://developers.cloudflare.com/images/transform-images/sources/ + loader: 'custom', + // Relative to the Next.js app directory (`apps/site`), where the build + // runs. + loaderFile: '../../platforms/cloudflare/image-loader.ts', + }, +}; diff --git a/apps/site/open-next.config.ts b/platforms/cloudflare/open-next.config.ts similarity index 100% rename from apps/site/open-next.config.ts rename to platforms/cloudflare/open-next.config.ts diff --git a/platforms/cloudflare/package.json b/platforms/cloudflare/package.json new file mode 100644 index 0000000000000..4336de8d98bc0 --- /dev/null +++ b/platforms/cloudflare/package.json @@ -0,0 +1,41 @@ +{ + "name": "@node-core/platform-cloudflare", + "private": true, + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/nodejs/nodejs.org", + "directory": "platforms/cloudflare" + }, + "scripts": { + "prebuild:worker": "node ./scripts/twoslash-fsmap/index.mjs", + "build:worker": "cross-env NEXT_PUBLIC_DEPLOYMENT_PLATFORM=cloudflare pnpm --filter=@node-core/website exec opennextjs-cloudflare build --openNextConfigPath ../../platforms/cloudflare/open-next.config.ts --config ../../platforms/cloudflare/wrangler.jsonc", + "predeploy:worker": "node ./scripts/twoslash-fsmap/index.mjs", + "deploy:worker": "cross-env NEXT_PUBLIC_DEPLOYMENT_PLATFORM=cloudflare pnpm --filter=@node-core/website exec opennextjs-cloudflare deploy --openNextConfigPath ../../platforms/cloudflare/open-next.config.ts --config ../../platforms/cloudflare/wrangler.jsonc", + "preview": "pnpm --filter=@node-core/website exec wrangler dev --config ../../platforms/cloudflare/wrangler.jsonc", + "test:e2e": "cross-env NEXT_PUBLIC_DEPLOYMENT_PLATFORM=cloudflare pnpm --filter=@node-core/website playwright", + "lint:types": "tsc --noEmit" + }, + "dependencies": { + "@flarelabs-net/wrangler-build-time-fs-assets-polyfilling": "^0.0.1", + "@opennextjs/cloudflare": "^1.19.3", + "@sentry/cloudflare": "^10.49.0", + "@typescript/vfs": "^1.6.4", + "twoslash": "^0.3.8", + "typescript": "catalog:", + "wrangler": "^4.77.0" + }, + "peerDependencies": { + "next": "catalog:" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20260418.1", + "@playwright/test": "^1.59.1", + "@types/node": "catalog:", + "cross-env": "catalog:", + "next": "catalog:" + }, + "engines": { + "node": "24.x" + } +} diff --git a/platforms/cloudflare/playwright.config.mjs b/platforms/cloudflare/playwright.config.mjs new file mode 100644 index 0000000000000..ad608083814e6 --- /dev/null +++ b/platforms/cloudflare/playwright.config.mjs @@ -0,0 +1,16 @@ +/** + * Playwright configuration for the Cloudflare platform: tests run against a + * local `wrangler dev` server simulating the Cloudflare hosting. The worker + * must be built first (`pnpm --filter=@node-core/platform-cloudflare build:worker`). + * + * @type {import('@playwright/test').PlaywrightTestConfig} + */ +export default { + use: { baseURL: 'http://127.0.0.1:8787' }, + webServer: { + stdout: 'pipe', + command: 'pnpm --filter=@node-core/platform-cloudflare preview', + url: 'http://127.0.0.1:8787', + timeout: 60_000 * 3, + }, +}; diff --git a/apps/site/scripts/twoslash-fsmap/generate.mjs b/platforms/cloudflare/scripts/twoslash-fsmap/generate.mjs similarity index 100% rename from apps/site/scripts/twoslash-fsmap/generate.mjs rename to platforms/cloudflare/scripts/twoslash-fsmap/generate.mjs diff --git a/apps/site/scripts/twoslash-fsmap/index.mjs b/platforms/cloudflare/scripts/twoslash-fsmap/index.mjs similarity index 100% rename from apps/site/scripts/twoslash-fsmap/index.mjs rename to platforms/cloudflare/scripts/twoslash-fsmap/index.mjs diff --git a/platforms/cloudflare/shiki.mjs b/platforms/cloudflare/shiki.mjs new file mode 100644 index 0000000000000..edacb64ae66d8 --- /dev/null +++ b/platforms/cloudflare/shiki.mjs @@ -0,0 +1,19 @@ +import { createVfsTwoslasher } from './create-vfs-twoslasher.mjs'; + +/** + * `@node-core/rehype-shiki` options for Cloudflare Workers, where two of the + * defaults cannot work: + * + * - `shiki/wasm` requires loading via `WebAssembly.instantiate` with custom + * imports, which Cloudflare doesn't support for security reasons, so we + * fall back to the JavaScript RegExp engine. + * - The default filesystem-backed Twoslash cannot work because there is no + * real filesystem. Instead, we provide a custom twoslasher backed by an + * in-memory VFS pre-populated at build time with TypeScript lib + * declarations and `@types/node` (see `scripts/twoslash-fsmap`). + */ +export const shikiOptions = { + wasm: false, + twoslash: true, + twoslashOptions: { twoslasher: await createVfsTwoslasher() }, +}; diff --git a/platforms/cloudflare/tsconfig.json b/platforms/cloudflare/tsconfig.json new file mode 100644 index 0000000000000..ef084b5bb396f --- /dev/null +++ b/platforms/cloudflare/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["esnext"], + "module": "esnext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "noEmit": true, + "esModuleInterop": true, + "isolatedModules": true, + "types": ["node"] + }, + "include": ["**/*.ts"], + "exclude": [ + "node_modules", + // The worker entrypoint is bundled by wrangler, not tsc. Its imports of + // @sentry/cloudflare and .open-next/worker.js trigger an infinite-recursion + // crash in the TypeScript compiler (v5.9) during type resolution of + // @cloudflare/workers-types, so we exclude it from type checking. + "worker-entrypoint.ts" + ] +} diff --git a/platforms/cloudflare/turbo.json b/platforms/cloudflare/turbo.json new file mode 100644 index 0000000000000..bd6e869301068 --- /dev/null +++ b/platforms/cloudflare/turbo.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "lint:types": { + "cache": false + } + } +} diff --git a/apps/site/cloudflare/worker-entrypoint.ts b/platforms/cloudflare/worker-entrypoint.ts similarity index 91% rename from apps/site/cloudflare/worker-entrypoint.ts rename to platforms/cloudflare/worker-entrypoint.ts index bd40543b4b9dd..9ce832bf58f15 100644 --- a/apps/site/cloudflare/worker-entrypoint.ts +++ b/platforms/cloudflare/worker-entrypoint.ts @@ -11,7 +11,7 @@ import type { Request, } from '@cloudflare/workers-types'; -import { default as handler } from '../.open-next/worker.js'; +import { default as handler } from '../../apps/site/.open-next/worker.js'; export default withSentry( (env: { @@ -50,4 +50,4 @@ export default withSentry( } ); -export { DOQueueHandler } from '../.open-next/worker.js'; +export { DOQueueHandler } from '../../apps/site/.open-next/worker.js'; diff --git a/apps/site/wrangler.jsonc b/platforms/cloudflare/wrangler.jsonc similarity index 69% rename from apps/site/wrangler.jsonc rename to platforms/cloudflare/wrangler.jsonc index 4cb72688a59af..a989bf26667bc 100644 --- a/apps/site/wrangler.jsonc +++ b/platforms/cloudflare/wrangler.jsonc @@ -1,6 +1,6 @@ { "$schema": "./node_modules/wrangler/config-schema.json", - "main": "./cloudflare/worker-entrypoint.ts", + "main": "./worker-entrypoint.ts", "name": "nodejs-website", "compatibility_date": "2024-11-07", "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"], @@ -8,7 +8,7 @@ "minify": true, "keep_names": false, "assets": { - "directory": ".open-next/assets", + "directory": "../../apps/site/.open-next/assets", "binding": "ASSETS", "run_worker_first": true, }, @@ -31,13 +31,16 @@ "head_sampling_rate": 1, }, "build": { + // The polyfilled filesystem assets are read from (and written next to) + // the Next.js app, not this package. + "cwd": "../../apps/site", "command": "wrangler-build-time-fs-assets-polyfilling --assets pages --assets snippets --assets-output-dir .open-next/assets", }, "alias": { - "node:fs": "./.wrangler/fs-assets-polyfilling/polyfills/node/fs.ts", - "node:fs/promises": "./.wrangler/fs-assets-polyfilling/polyfills/node/fs/promises.ts", - "fs": "./.wrangler/fs-assets-polyfilling/polyfills/node/fs.ts", - "fs/promises": "./.wrangler/fs-assets-polyfilling/polyfills/node/fs/promises.ts", + "node:fs": "../../apps/site/.wrangler/fs-assets-polyfilling/polyfills/node/fs.ts", + "node:fs/promises": "../../apps/site/.wrangler/fs-assets-polyfilling/polyfills/node/fs/promises.ts", + "fs": "../../apps/site/.wrangler/fs-assets-polyfilling/polyfills/node/fs.ts", + "fs/promises": "../../apps/site/.wrangler/fs-assets-polyfilling/polyfills/node/fs/promises.ts", }, "r2_buckets": [ { diff --git a/platforms/default/next.config.mjs b/platforms/default/next.config.mjs new file mode 100644 index 0000000000000..b711ab4da6a15 --- /dev/null +++ b/platforms/default/next.config.mjs @@ -0,0 +1,9 @@ +/** + * Next.js configuration for self-hosted deployments (plain `next build` / + * `next start`, the legacy static export, and local development). The shared + * configuration in `apps/site/next.config.mjs` already covers this platform + * entirely. See `platforms/README.md`. + * + * @type {import('next').NextConfig} + */ +export default {}; diff --git a/platforms/default/package.json b/platforms/default/package.json new file mode 100644 index 0000000000000..159ef2bc4e2b9 --- /dev/null +++ b/platforms/default/package.json @@ -0,0 +1,17 @@ +{ + "name": "@node-core/platform-default", + "private": true, + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/nodejs/nodejs.org", + "directory": "platforms/default" + }, + "devDependencies": { + "@playwright/test": "^1.59.1", + "next": "catalog:" + }, + "engines": { + "node": "24.x" + } +} diff --git a/platforms/default/playwright.config.mjs b/platforms/default/playwright.config.mjs new file mode 100644 index 0000000000000..759afb9011516 --- /dev/null +++ b/platforms/default/playwright.config.mjs @@ -0,0 +1,8 @@ +/** + * Playwright configuration for the default platform: tests run against an + * externally started server (see `PLAYWRIGHT_BASE_URL`), so there is nothing + * to add to the shared configuration in `apps/site/playwright.config.mjs`. + * + * @type {import('@playwright/test').PlaywrightTestConfig} + */ +export default {}; diff --git a/platforms/vercel/analytics.tsx b/platforms/vercel/analytics.tsx new file mode 100644 index 0000000000000..682e13a5ed9f1 --- /dev/null +++ b/platforms/vercel/analytics.tsx @@ -0,0 +1,13 @@ +import { Analytics } from '@vercel/analytics/react'; +import { SpeedInsights } from '@vercel/speed-insights/next'; + +import type { FC } from 'react'; + +const VercelAnalytics: FC = () => ( + <> + + + +); + +export default VercelAnalytics; diff --git a/platforms/vercel/instrumentation.ts b/platforms/vercel/instrumentation.ts new file mode 100644 index 0000000000000..3e7a06a7d37d2 --- /dev/null +++ b/platforms/vercel/instrumentation.ts @@ -0,0 +1,3 @@ +import { registerOTel } from '@vercel/otel'; + +export const register = () => registerOTel({ serviceName: 'nodejs-org' }); diff --git a/platforms/vercel/next.config.mjs b/platforms/vercel/next.config.mjs new file mode 100644 index 0000000000000..d5ef43be78525 --- /dev/null +++ b/platforms/vercel/next.config.mjs @@ -0,0 +1,15 @@ +/** + * Next.js configuration for the Vercel deployment. See `platforms/README.md`. + * + * @type {import('next').NextConfig} + */ +export default { + // `BASE_URL` (see `apps/site/next.constants.mjs`) reads + // `NEXT_PUBLIC_BASE_URL` and falls back to the canonical + // `https://nodejs.org`. On Vercel deployments where it isn't set explicitly + // (e.g. previews) we point it at the URL Vercel generated instead. + env: + !process.env.NEXT_PUBLIC_BASE_URL && process.env.VERCEL_URL + ? { NEXT_PUBLIC_BASE_URL: `https://${process.env.VERCEL_URL}` } + : {}, +}; diff --git a/platforms/vercel/package.json b/platforms/vercel/package.json new file mode 100644 index 0000000000000..e4559aa37a58c --- /dev/null +++ b/platforms/vercel/package.json @@ -0,0 +1,33 @@ +{ + "name": "@node-core/platform-vercel", + "private": true, + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/nodejs/nodejs.org", + "directory": "platforms/vercel" + }, + "dependencies": { + "@opentelemetry/api-logs": "~0.218.0", + "@opentelemetry/instrumentation": "~0.218.0", + "@opentelemetry/resources": "~1.30.1", + "@opentelemetry/sdk-logs": "~0.218.0", + "@vercel/analytics": "~2.0.1", + "@vercel/otel": "~2.1.2", + "@vercel/speed-insights": "~2.0.0" + }, + "peerDependencies": { + "next": "catalog:", + "react": "catalog:" + }, + "devDependencies": { + "@playwright/test": "^1.59.1", + "@types/react": "catalog:", + "next": "catalog:", + "react": "catalog:", + "typescript": "catalog:" + }, + "engines": { + "node": "24.x" + } +} diff --git a/platforms/vercel/playwright.config.mjs b/platforms/vercel/playwright.config.mjs new file mode 100644 index 0000000000000..7689383051bba --- /dev/null +++ b/platforms/vercel/playwright.config.mjs @@ -0,0 +1,8 @@ +/** + * Playwright configuration for the Vercel platform: tests run against a + * deployed preview (see `.github/workflows/playwright.yml`), so there is + * nothing to add to the shared configuration. + * + * @type {import('@playwright/test').PlaywrightTestConfig} + */ +export default {}; diff --git a/platforms/vercel/tsconfig.json b/platforms/vercel/tsconfig.json new file mode 100644 index 0000000000000..eab8850d156de --- /dev/null +++ b/platforms/vercel/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["dom", "dom.iterable", "esnext"], + "module": "esnext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "noEmit": true, + "esModuleInterop": true, + "isolatedModules": true, + "jsx": "react-jsx" + }, + "include": ["**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/platforms/vercel/vercel.json b/platforms/vercel/vercel.json new file mode 100644 index 0000000000000..b522d5e0edb09 --- /dev/null +++ b/platforms/vercel/vercel.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "installCommand": "pnpm install --frozen-lockfile --filter=@node-core/website... --filter=@node-core/platform-vercel...", + "buildCommand": "pnpm --filter=@node-core/website build", + "outputDirectory": "../../apps/site/.next", + "ignoreCommand": "[[ \"$VERCEL_GIT_COMMIT_REF\" =~ \"^dependabot/.*\" || \"$VERCEL_GIT_COMMIT_REF\" =~ \"^gh-readonly-queue/.*\" ]]" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16c8a44666061..3916b530c44df 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -217,6 +217,9 @@ catalogs: cross-env: specifier: ^10.0.0 version: 10.1.0 + next: + specifier: 16.2.6 + version: 16.2.6 react: specifier: ^19.2.6 version: 19.2.6 @@ -292,6 +295,15 @@ importers: '@mdx-js/mdx': specifier: ^3.1.1 version: 3.1.1 + '@node-core/platform-cloudflare': + specifier: workspace:* + version: link:../../platforms/cloudflare + '@node-core/platform-default': + specifier: workspace:* + version: link:../../platforms/default + '@node-core/platform-vercel': + specifier: workspace:* + version: link:../../platforms/vercel '@node-core/rehype-shiki': specifier: workspace:* version: link:../../packages/rehype-shiki @@ -304,18 +316,6 @@ importers: '@nodevu/core': specifier: 0.3.0 version: 0.3.0 - '@opentelemetry/api-logs': - specifier: ~0.218.0 - version: 0.218.0 - '@opentelemetry/instrumentation': - specifier: ~0.218.0 - version: 0.218.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': - specifier: ~1.30.1 - version: 1.30.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs': - specifier: ~0.218.0 - version: 0.218.0(@opentelemetry/api@1.9.1) '@orama/orama': specifier: ^3.1.18 version: 3.1.18 @@ -334,21 +334,9 @@ importers: '@types/react': specifier: 'catalog:' version: 19.2.16 - '@typescript/vfs': - specifier: ^1.6.4 - version: 1.6.4(typescript@5.9.3) '@vcarl/remark-headings': specifier: ~0.1.0 version: 0.1.0 - '@vercel/analytics': - specifier: ~2.0.1 - version: 2.0.1(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) - '@vercel/otel': - specifier: ~2.1.2 - version: 2.1.2(@opentelemetry/api-logs@0.218.0)(@opentelemetry/api@1.9.1)(@opentelemetry/instrumentation@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-logs@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1)) - '@vercel/speed-insights': - specifier: ~2.0.0 - version: 2.0.0(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) classnames: specifier: 'catalog:' version: 2.5.1 @@ -368,7 +356,7 @@ importers: specifier: ^4.0.0 version: 4.0.0 next: - specifier: 16.2.6 + specifier: 'catalog:' version: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) next-intl: specifier: ~4.13.0 @@ -409,9 +397,6 @@ importers: tailwindcss: specifier: 'catalog:' version: 4.1.18 - twoslash: - specifier: ^0.3.8 - version: 0.3.8(typescript@5.9.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -425,33 +410,21 @@ importers: specifier: ~5.0.1 version: 5.0.1 devDependencies: - '@cloudflare/workers-types': - specifier: ^4.20260418.1 - version: 4.20260422.1 '@eslint-react/eslint-plugin': specifier: ~5.8.6 version: 5.8.6(eslint@10.2.1(jiti@2.7.0))(typescript@5.9.3) - '@flarelabs-net/wrangler-build-time-fs-assets-polyfilling': - specifier: ^0.0.1 - version: 0.0.1 '@next/eslint-plugin-next': specifier: 16.2.6 version: 16.2.6 '@node-core/remark-lint': specifier: workspace:* version: link:../../packages/remark-lint - '@opennextjs/cloudflare': - specifier: ^1.19.3 - version: 1.19.3(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(wrangler@4.77.0(@cloudflare/workers-types@4.20260422.1)) '@orama/core': specifier: ^1.2.19 version: 1.2.19 '@playwright/test': specifier: ^1.59.1 version: 1.59.1 - '@sentry/cloudflare': - specifier: ^10.49.0 - version: 10.49.0(@cloudflare/workers-types@4.20260422.1) '@testing-library/user-event': specifier: ~14.6.1 version: 14.6.1(@testing-library/dom@10.4.0) @@ -512,9 +485,6 @@ importers: user-agent-data-types: specifier: 0.4.2 version: 0.4.2 - wrangler: - specifier: ^4.77.0 - version: 4.77.0(@cloudflare/workers-types@4.20260422.1) packages/i18n: devDependencies: @@ -810,6 +780,95 @@ importers: specifier: 'catalog:' version: 5.9.3 + platforms/cloudflare: + dependencies: + '@flarelabs-net/wrangler-build-time-fs-assets-polyfilling': + specifier: ^0.0.1 + version: 0.0.1 + '@opennextjs/cloudflare': + specifier: ^1.19.3 + version: 1.19.3(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(wrangler@4.77.0(@cloudflare/workers-types@4.20260422.1)) + '@sentry/cloudflare': + specifier: ^10.49.0 + version: 10.49.0(@cloudflare/workers-types@4.20260422.1) + '@typescript/vfs': + specifier: ^1.6.4 + version: 1.6.4(typescript@5.9.3) + twoslash: + specifier: ^0.3.8 + version: 0.3.8(typescript@5.9.3) + typescript: + specifier: 'catalog:' + version: 5.9.3 + wrangler: + specifier: ^4.77.0 + version: 4.77.0(@cloudflare/workers-types@4.20260422.1) + devDependencies: + '@cloudflare/workers-types': + specifier: ^4.20260418.1 + version: 4.20260422.1 + '@playwright/test': + specifier: ^1.59.1 + version: 1.59.1 + '@types/node': + specifier: 'catalog:' + version: 24.10.1 + cross-env: + specifier: 'catalog:' + version: 10.1.0 + next: + specifier: 'catalog:' + version: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + + platforms/default: + devDependencies: + '@playwright/test': + specifier: ^1.59.1 + version: 1.59.1 + next: + specifier: 'catalog:' + version: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + + platforms/vercel: + dependencies: + '@opentelemetry/api-logs': + specifier: ~0.218.0 + version: 0.218.0 + '@opentelemetry/instrumentation': + specifier: ~0.218.0 + version: 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': + specifier: ~1.30.1 + version: 1.30.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': + specifier: ~0.218.0 + version: 0.218.0(@opentelemetry/api@1.9.1) + '@vercel/analytics': + specifier: ~2.0.1 + version: 2.0.1(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) + '@vercel/otel': + specifier: ~2.1.2 + version: 2.1.2(@opentelemetry/api-logs@0.218.0)(@opentelemetry/api@1.9.1)(@opentelemetry/instrumentation@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-logs@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1)) + '@vercel/speed-insights': + specifier: ~2.0.0 + version: 2.0.0(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) + devDependencies: + '@playwright/test': + specifier: ^1.59.1 + version: 1.59.1 + '@types/react': + specifier: 'catalog:' + version: 19.2.16 + next: + specifier: 'catalog:' + version: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: + specifier: 'catalog:' + version: 19.2.6 + typescript: + specifier: 'catalog:' + version: 5.9.3 + packages: '@actions/core@3.0.1': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2de78bf7999ac..2c804b9619d26 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,7 @@ packages: - packages/* - apps/* + - platforms/* allowBuilds: "@parcel/watcher": true @@ -15,6 +16,7 @@ catalog: '@types/react': ^19.2.15 classnames: ~2.5.1 cross-env: ^10.0.0 + next: 16.2.6 react: ^19.2.6 tailwindcss: ~4.1.17 typescript: 5.9.3 From 57beecb5f5c6c66170f0e571307cbac4de8755a2 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Tue, 28 Jul 2026 13:03:19 -0700 Subject: [PATCH 2/8] fixup! --- apps/site/app/[locale]/layout.tsx | 2 +- apps/site/instrumentation.ts | 14 ++---- apps/site/mdx/plugins.mjs | 17 +++----- apps/site/next.config.mjs | 31 +++---------- apps/site/package.json | 15 +++++++ apps/site/platform/analytics.tsx | 17 -------- apps/site/playwright.config.mjs | 8 +--- apps/site/turbo.json | 12 ++---- platforms/README.md | 55 ------------------------ platforms/cloudflare/analytics.mjs | 12 ++++++ platforms/cloudflare/instrumentation.mjs | 6 +++ platforms/cloudflare/package.json | 6 +-- platforms/default/analytics.mjs | 12 ++++++ platforms/default/instrumentation.mjs | 5 +++ platforms/default/shiki.mjs | 7 +++ platforms/vercel/shiki.mjs | 9 ++++ platforms/vercel/vercel.json | 5 +++ 17 files changed, 95 insertions(+), 138 deletions(-) delete mode 100644 apps/site/platform/analytics.tsx delete mode 100644 platforms/README.md create mode 100644 platforms/cloudflare/analytics.mjs create mode 100644 platforms/cloudflare/instrumentation.mjs create mode 100644 platforms/default/analytics.mjs create mode 100644 platforms/default/instrumentation.mjs create mode 100644 platforms/default/shiki.mjs create mode 100644 platforms/vercel/shiki.mjs diff --git a/apps/site/app/[locale]/layout.tsx b/apps/site/app/[locale]/layout.tsx index b8ecd4cda027e..7b905a0fd03a3 100644 --- a/apps/site/app/[locale]/layout.tsx +++ b/apps/site/app/[locale]/layout.tsx @@ -1,10 +1,10 @@ +import PlatformAnalytics from '#platform/analytics'; import { availableLocales, defaultLocale } from '@node-core/website-i18n'; import classNames from 'classnames'; import { NextIntlClientProvider } from 'next-intl'; import BaseLayout from '#site/layouts/Base'; import { IBM_PLEX_MONO, OPEN_SANS } from '#site/next.fonts'; -import PlatformAnalytics from '#site/platform/analytics'; import { ThemeProvider } from '#site/providers/themeProvider'; import type { FC, PropsWithChildren } from 'react'; diff --git a/apps/site/instrumentation.ts b/apps/site/instrumentation.ts index 96075886a0d44..44cbaf3de920d 100644 --- a/apps/site/instrumentation.ts +++ b/apps/site/instrumentation.ts @@ -1,11 +1,3 @@ -export const register = async () => { - // `NEXT_PUBLIC_DEPLOYMENT_PLATFORM` is a build-time constant (see - // `next.config.mjs`), so the platforms we are not building for are never - // bundled. Only Vercel currently provides instrumentation. - if (process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM === 'vercel') { - const { register } = - await import('@node-core/platform-vercel/instrumentation'); - - register(); - } -}; +// Resolved through the `#platform/*` import conditions; only Vercel currently +// registers anything. See `platforms/README.md`. +export { register } from '#platform/instrumentation'; diff --git a/apps/site/mdx/plugins.mjs b/apps/site/mdx/plugins.mjs index a5f375a5d0fa4..9dc928c74f18e 100644 --- a/apps/site/mdx/plugins.mjs +++ b/apps/site/mdx/plugins.mjs @@ -1,5 +1,6 @@ 'use strict'; +import { shikiOptions } from '#platform/shiki.mjs'; import rehypeShikiji from '@node-core/rehype-shiki/plugin'; import remarkHeadings from '@vcarl/remark-headings'; import rehypeAutolinkHeadings from 'rehype-autolink-headings'; @@ -7,19 +8,13 @@ import rehypeSlug from 'rehype-slug'; import remarkGfm from 'remark-gfm'; import readingTime from 'remark-reading-time'; + import remarkTableTitles from '../util/table'; -// Shiki is created out here to avoid an async rehype plugin. -// -// `NEXT_PUBLIC_DEPLOYMENT_PLATFORM` is a build-time constant (see -// `next.config.mjs`), so the Cloudflare-specific options (which cannot use -// the WASM engine nor a filesystem-backed Twoslash) are only bundled on -// Cloudflare builds; everywhere else we use the faster WASM engine. -const singletonShiki = await rehypeShikiji( - process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM === 'cloudflare' - ? (await import('@node-core/platform-cloudflare/shiki.mjs')).shikiOptions - : { wasm: true, twoslash: true } -); +// Shiki is created out here to avoid an async rehype plugin. The options come +// from the active platform, since Cloudflare can use neither the WASM engine +// nor a filesystem-backed Twoslash; see `platforms/README.md`. +const singletonShiki = await rehypeShikiji(shikiOptions); /** * Provides all our Rehype Plugins that are used within MDX diff --git a/apps/site/next.config.mjs b/apps/site/next.config.mjs index 7ea29629ffef0..f2de9ebff7cbd 100644 --- a/apps/site/next.config.mjs +++ b/apps/site/next.config.mjs @@ -1,24 +1,13 @@ 'use strict'; +import platformConfig from '#platform/next.config.mjs'; import createNextIntlPlugin from 'next-intl/plugin'; +// Everything platform-specific is resolved through the `#platform/*` import +// conditions; see `platforms/README.md`. + import { BASE_PATH, ENABLE_STATIC_EXPORT } from './next.constants.mjs'; import { redirects, rewrites } from './next.rewrites.mjs'; -/** - * The deployment platform this build targets. All platform-specific behavior - * lives in the `platforms/*` workspace packages; see `platforms/README.md`. - * - * Vercel deployments are detected automatically; the Cloudflare build scripts - * set the variable themselves. - */ -const DEPLOYMENT_PLATFORM = - process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM || - (process.env.VERCEL ? 'vercel' : 'default'); - -const { default: platformConfig } = await import( - `@node-core/platform-${DEPLOYMENT_PLATFORM}/next.config.mjs` -); - /** @type {import('next').NextConfig} */ const nextConfig = { // Full Support of React 18 SSR and Streaming @@ -97,14 +86,4 @@ const nextConfig = { const withNextIntl = createNextIntlPlugin('./i18n.tsx'); -export default withNextIntl({ - ...nextConfig, - ...platformConfig, - env: { - // Inlined into the bundles at build time so runtime code (e.g. - // `#site/platform/analytics`) can branch on the platform, letting - // bundlers drop the branches (and packages) of the other platforms. - NEXT_PUBLIC_DEPLOYMENT_PLATFORM: DEPLOYMENT_PLATFORM, - ...platformConfig.env, - }, -}); +export default withNextIntl({ ...nextConfig, ...platformConfig }); diff --git a/apps/site/package.json b/apps/site/package.json index c25174317e098..1e2c4672c4b1d 100644 --- a/apps/site/package.json +++ b/apps/site/package.json @@ -111,6 +111,21 @@ } }, "imports": { + "#platform/analytics": { + "cloudflare": "@node-core/platform-cloudflare/analytics.mjs", + "vercel": "@node-core/platform-vercel/analytics.tsx", + "default": "@node-core/platform-default/analytics.mjs" + }, + "#platform/instrumentation": { + "cloudflare": "@node-core/platform-cloudflare/instrumentation.mjs", + "vercel": "@node-core/platform-vercel/instrumentation.ts", + "default": "@node-core/platform-default/instrumentation.mjs" + }, + "#platform/*": { + "cloudflare": "@node-core/platform-cloudflare/*", + "vercel": "@node-core/platform-vercel/*", + "default": "@node-core/platform-default/*" + }, "#site/*": [ "./*", "./*.tsx", diff --git a/apps/site/platform/analytics.tsx b/apps/site/platform/analytics.tsx deleted file mode 100644 index 737476b8c5428..0000000000000 --- a/apps/site/platform/analytics.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { FC } from 'react'; - -// `NEXT_PUBLIC_DEPLOYMENT_PLATFORM` is a build-time constant (see -// `next.config.mjs`), so the platforms we are not building for are never -// bundled. Only Vercel currently provides analytics. -const PlatformAnalytics: FC = async () => { - if (process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM === 'vercel') { - const { default: VercelAnalytics } = - await import('@node-core/platform-vercel/analytics'); - - return ; - } - - return null; -}; - -export default PlatformAnalytics; diff --git a/apps/site/playwright.config.mjs b/apps/site/playwright.config.mjs index 5e86aa6ec0025..1e6a260852e1f 100644 --- a/apps/site/playwright.config.mjs +++ b/apps/site/playwright.config.mjs @@ -1,14 +1,10 @@ +import platformConfig from '#platform/playwright.config.mjs'; import { defineConfig, devices } from '@playwright/test'; -const isCI = !!process.env.CI; - // The active deployment platform can contribute configuration (e.g. the // Cloudflare preview web server); see `platforms/README.md`. -const platform = process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM || 'default'; -const { default: platformConfig } = await import( - `@node-core/platform-${platform}/playwright.config.mjs` -); +const isCI = !!process.env.CI; // https://playwright.dev/docs/test-configuration export default defineConfig({ diff --git a/apps/site/turbo.json b/apps/site/turbo.json index 5ff017a617ba0..0dc8b6586db99 100644 --- a/apps/site/turbo.json +++ b/apps/site/turbo.json @@ -7,7 +7,6 @@ "cache": false, "persistent": true, "env": [ - "NEXT_PUBLIC_DEPLOYMENT_PLATFORM", "VERCEL", "VERCEL_URL", "NEXT_PUBLIC_STATIC_EXPORT", @@ -25,7 +24,7 @@ "build": { "dependsOn": ["build:blog-data", "^build"], "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,platform,providers,types,util}/**/*.{ts,tsx}", + "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx}", "{app,components,layouts,pages,styles}/**/*.css", "{next-data,scripts,i18n}/**/*.{mjs,json}", "{app,pages}/**/*.{mdx,md}", @@ -33,7 +32,6 @@ ], "outputs": [".next/**", "!.next/cache/**"], "env": [ - "NEXT_PUBLIC_DEPLOYMENT_PLATFORM", "VERCEL", "VERCEL_URL", "NEXT_PUBLIC_STATIC_EXPORT", @@ -53,7 +51,6 @@ "cache": false, "persistent": true, "env": [ - "NEXT_PUBLIC_DEPLOYMENT_PLATFORM", "VERCEL", "VERCEL_URL", "NEXT_PUBLIC_STATIC_EXPORT", @@ -70,7 +67,7 @@ }, "deploy": { "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,platform,providers,types,util}/**/*.{ts,tsx}", + "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx}", "{app,components,layouts,pages,styles}/**/*.css", "{next-data,scripts,i18n}/**/*.{mjs,json}", "{app,pages}/**/*.{mdx,md}", @@ -78,7 +75,6 @@ ], "outputs": [".next/**", "!.next/cache/**"], "env": [ - "NEXT_PUBLIC_DEPLOYMENT_PLATFORM", "VERCEL", "VERCEL_URL", "NEXT_PUBLIC_STATIC_EXPORT", @@ -95,7 +91,7 @@ }, "lint:js": { "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,platform,providers,types,util}/**/*.{ts,tsx,mjs}", + "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx,mjs}", "{next-data,scripts,i18n}/**/*.{mjs,json}", "public/**/*.{ts,js,json}", "*.{json,ts,tsx}" @@ -121,7 +117,7 @@ "test:unit": { "dependsOn": ["build:blog-data", "^build"], "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,platform,providers,types,util}/**/*.{ts,tsx,mjs}", + "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx,mjs}", "{app,components,layouts,pages,styles}/**/*.css", "{next-data,scripts,i18n}/**/*.{mjs,json}", "{app,pages}/**/*.{mdx,md}", diff --git a/platforms/README.md b/platforms/README.md deleted file mode 100644 index 60a26c122d9ed..0000000000000 --- a/platforms/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Deployment Platforms - -The Node.js Website can be deployed to multiple hosting platforms. Everything -that is specific to one platform (analytics, instrumentation, image loaders, -deployment configuration and their dependencies) lives in one of the workspace -packages in this directory, keeping `apps/site` platform-agnostic: - -- [`default`](./default) — self-hosted deployments: plain `next build` / - `next start` and the legacy static export (`pnpm deploy`). This is also what - local development uses. -- [`vercel`](./vercel) — the Vercel deployment of . -- [`cloudflare`](./cloudflare) — the Cloudflare Workers deployment built with - the [OpenNext Cloudflare adapter](https://opennext.js.org/cloudflare) (see - [`docs/cloudflare-build-and-deployment.md`](../docs/cloudflare-build-and-deployment.md)). - -## Platform selection - -The active platform is selected at build time through the -`NEXT_PUBLIC_DEPLOYMENT_PLATFORM` environment variable (`vercel`, -`cloudflare`, or unset for `default`). Vercel deployments are detected -automatically through the `VERCEL` environment variable, and the Cloudflare -build scripts in [`cloudflare/package.json`](./cloudflare/package.json) set the -variable themselves, so it only needs to be set manually for unusual setups. - -`apps/site/next.config.mjs` resolves the variable once, merges the platform's -`next.config.mjs` fragment into the shared configuration, and inlines the -resolved value back into the bundles via the Next.js `env` config. Runtime -code then branches on `process.env.NEXT_PUBLIC_DEPLOYMENT_PLATFORM`: since -the value is a build-time constant, bundlers eliminate the branches (and the -platform packages they import) that belong to other platforms. - -> [!NOTE] -> This is intentionally **not** implemented with conditional -> [subpath imports](https://nodejs.org/api/packages.html#subpath-imports) -> selected via Node.js `--conditions`: custom resolution conditions are only -> honored by Node.js itself, not by webpack or Turbopack, so bundled code -> would silently fall back to the `default` condition. - -## Platform interface - -Each platform package provides: - -- `next.config.mjs` — a partial Next.js configuration merged over the shared - configuration in `apps/site/next.config.mjs`. -- `playwright.config.mjs` — a partial Playwright configuration merged over the - shared configuration in `apps/site/playwright.config.mjs`. - -Additional platform-specific modules (for example `vercel/analytics.tsx` or -`cloudflare/shiki.mjs`) are imported from the platform branches in -`apps/site/platform/` and `apps/site/mdx/plugins.mjs`. - -The platform packages are declared as optional `peerDependencies` of -`@node-core/website` so that a deployment can install only the platform it -needs (see the `installCommand` in [`vercel/vercel.json`](./vercel/vercel.json)), -while a regular workspace install makes all of them available. diff --git a/platforms/cloudflare/analytics.mjs b/platforms/cloudflare/analytics.mjs new file mode 100644 index 0000000000000..a9df8597f4ccd --- /dev/null +++ b/platforms/cloudflare/analytics.mjs @@ -0,0 +1,12 @@ +/** + * Analytics for the Cloudflare deployment: there are none, so this renders + * nothing. Only Vercel currently provides analytics. + * + * Deliberately not a `.tsx` file — keeping it JSX-free means this package + * needs no React dependency to satisfy the `#platform/*` interface. + * + * @type {() => null} + */ +const PlatformAnalytics = () => null; + +export default PlatformAnalytics; diff --git a/platforms/cloudflare/instrumentation.mjs b/platforms/cloudflare/instrumentation.mjs new file mode 100644 index 0000000000000..b5b9d7a41904d --- /dev/null +++ b/platforms/cloudflare/instrumentation.mjs @@ -0,0 +1,6 @@ +/** + * Instrumentation for the Cloudflare deployment: the Worker is instrumented + * through the Sentry integration in `worker-entrypoint.ts` instead, so the + * Next.js hook is a no-op here. + */ +export const register = () => {}; diff --git a/platforms/cloudflare/package.json b/platforms/cloudflare/package.json index 4336de8d98bc0..5fe483d8bc136 100644 --- a/platforms/cloudflare/package.json +++ b/platforms/cloudflare/package.json @@ -9,11 +9,11 @@ }, "scripts": { "prebuild:worker": "node ./scripts/twoslash-fsmap/index.mjs", - "build:worker": "cross-env NEXT_PUBLIC_DEPLOYMENT_PLATFORM=cloudflare pnpm --filter=@node-core/website exec opennextjs-cloudflare build --openNextConfigPath ../../platforms/cloudflare/open-next.config.ts --config ../../platforms/cloudflare/wrangler.jsonc", + "build:worker": "cross-env NODE_OPTIONS=--conditions=cloudflare pnpm --filter=@node-core/website exec opennextjs-cloudflare build --openNextConfigPath ../../platforms/cloudflare/open-next.config.ts --config ../../platforms/cloudflare/wrangler.jsonc", "predeploy:worker": "node ./scripts/twoslash-fsmap/index.mjs", - "deploy:worker": "cross-env NEXT_PUBLIC_DEPLOYMENT_PLATFORM=cloudflare pnpm --filter=@node-core/website exec opennextjs-cloudflare deploy --openNextConfigPath ../../platforms/cloudflare/open-next.config.ts --config ../../platforms/cloudflare/wrangler.jsonc", + "deploy:worker": "cross-env NODE_OPTIONS=--conditions=cloudflare pnpm --filter=@node-core/website exec opennextjs-cloudflare deploy --openNextConfigPath ../../platforms/cloudflare/open-next.config.ts --config ../../platforms/cloudflare/wrangler.jsonc", "preview": "pnpm --filter=@node-core/website exec wrangler dev --config ../../platforms/cloudflare/wrangler.jsonc", - "test:e2e": "cross-env NEXT_PUBLIC_DEPLOYMENT_PLATFORM=cloudflare pnpm --filter=@node-core/website playwright", + "test:e2e": "cross-env NODE_OPTIONS=--conditions=cloudflare pnpm --filter=@node-core/website playwright", "lint:types": "tsc --noEmit" }, "dependencies": { diff --git a/platforms/default/analytics.mjs b/platforms/default/analytics.mjs new file mode 100644 index 0000000000000..4ac017b5ca5e3 --- /dev/null +++ b/platforms/default/analytics.mjs @@ -0,0 +1,12 @@ +/** + * Analytics for self-hosted deployments: there are none, so this renders + * nothing. Only Vercel currently provides analytics. + * + * Deliberately not a `.tsx` file — keeping it JSX-free means this package + * needs no React dependency to satisfy the `#platform/*` interface. + * + * @type {() => null} + */ +const PlatformAnalytics = () => null; + +export default PlatformAnalytics; diff --git a/platforms/default/instrumentation.mjs b/platforms/default/instrumentation.mjs new file mode 100644 index 0000000000000..144369cf75f83 --- /dev/null +++ b/platforms/default/instrumentation.mjs @@ -0,0 +1,5 @@ +/** + * Instrumentation for self-hosted deployments: there is none, so registering + * is a no-op. Only Vercel currently provides instrumentation. + */ +export const register = () => {}; diff --git a/platforms/default/shiki.mjs b/platforms/default/shiki.mjs new file mode 100644 index 0000000000000..cf054fcc5ba0b --- /dev/null +++ b/platforms/default/shiki.mjs @@ -0,0 +1,7 @@ +/** + * `@node-core/rehype-shiki` options for self-hosted deployments, where both + * defaults work: the WASM engine is the faster of the two, and Twoslash can + * use the real filesystem. Cloudflare overrides both (see + * `platforms/cloudflare/shiki.mjs`). + */ +export const shikiOptions = { wasm: true, twoslash: true }; diff --git a/platforms/vercel/shiki.mjs b/platforms/vercel/shiki.mjs new file mode 100644 index 0000000000000..710b7b129c600 --- /dev/null +++ b/platforms/vercel/shiki.mjs @@ -0,0 +1,9 @@ +/** + * `@node-core/rehype-shiki` options for the Vercel deployment. Vercel runs a + * regular Node.js server, so both defaults work and these match the + * self-hosted options (see `platforms/default/shiki.mjs`). They are repeated + * rather than re-exported so the platform packages stay independent of one + * another — a deployment installs only its own (see the `installCommand` in + * `vercel.json`). + */ +export const shikiOptions = { wasm: true, twoslash: true }; diff --git a/platforms/vercel/vercel.json b/platforms/vercel/vercel.json index b522d5e0edb09..d03ff82500a92 100644 --- a/platforms/vercel/vercel.json +++ b/platforms/vercel/vercel.json @@ -2,6 +2,11 @@ "$schema": "https://openapi.vercel.sh/vercel.json", "installCommand": "pnpm install --frozen-lockfile --filter=@node-core/website... --filter=@node-core/platform-vercel...", "buildCommand": "pnpm --filter=@node-core/website build", + "build": { + "env": { + "NODE_OPTIONS": "--conditions=vercel" + } + }, "outputDirectory": "../../apps/site/.next", "ignoreCommand": "[[ \"$VERCEL_GIT_COMMIT_REF\" =~ \"^dependabot/.*\" || \"$VERCEL_GIT_COMMIT_REF\" =~ \"^gh-readonly-queue/.*\" ]]" } From 3702b463de00e4d5e7d5f464c319f470c1a4fd8f Mon Sep 17 00:00:00 2001 From: avivkeller Date: Tue, 28 Jul 2026 13:08:09 -0700 Subject: [PATCH 3/8] fixup! --- apps/site/instrumentation.ts | 2 -- apps/site/mdx/plugins.mjs | 5 +---- apps/site/next.config.mjs | 3 --- apps/site/next.constants.mjs | 5 ----- apps/site/playwright.config.mjs | 3 --- docs/cloudflare-build-and-deployment.md | 2 +- docs/getting-started.md | 2 +- docs/technologies.md | 2 +- platforms/cloudflare/next.config.mjs | 3 --- platforms/default/next.config.mjs | 5 ----- platforms/vercel/next.config.mjs | 2 -- 11 files changed, 4 insertions(+), 30 deletions(-) diff --git a/apps/site/instrumentation.ts b/apps/site/instrumentation.ts index 44cbaf3de920d..cb1ffbbeb0be6 100644 --- a/apps/site/instrumentation.ts +++ b/apps/site/instrumentation.ts @@ -1,3 +1 @@ -// Resolved through the `#platform/*` import conditions; only Vercel currently -// registers anything. See `platforms/README.md`. export { register } from '#platform/instrumentation'; diff --git a/apps/site/mdx/plugins.mjs b/apps/site/mdx/plugins.mjs index 9dc928c74f18e..a220d13ec19ce 100644 --- a/apps/site/mdx/plugins.mjs +++ b/apps/site/mdx/plugins.mjs @@ -8,12 +8,9 @@ import rehypeSlug from 'rehype-slug'; import remarkGfm from 'remark-gfm'; import readingTime from 'remark-reading-time'; - import remarkTableTitles from '../util/table'; -// Shiki is created out here to avoid an async rehype plugin. The options come -// from the active platform, since Cloudflare can use neither the WASM engine -// nor a filesystem-backed Twoslash; see `platforms/README.md`. +// Shiki is created out here to avoid an async rehype plugin. const singletonShiki = await rehypeShikiji(shikiOptions); /** diff --git a/apps/site/next.config.mjs b/apps/site/next.config.mjs index f2de9ebff7cbd..6f392d51b492d 100644 --- a/apps/site/next.config.mjs +++ b/apps/site/next.config.mjs @@ -2,9 +2,6 @@ import platformConfig from '#platform/next.config.mjs'; import createNextIntlPlugin from 'next-intl/plugin'; -// Everything platform-specific is resolved through the `#platform/*` import -// conditions; see `platforms/README.md`. - import { BASE_PATH, ENABLE_STATIC_EXPORT } from './next.constants.mjs'; import { redirects, rewrites } from './next.rewrites.mjs'; diff --git a/apps/site/next.constants.mjs b/apps/site/next.constants.mjs index 3906589b02a35..abc3e4da2a87d 100644 --- a/apps/site/next.constants.mjs +++ b/apps/site/next.constants.mjs @@ -30,11 +30,6 @@ export const ENABLE_STATIC_EXPORT_LOCALE = /** * This is used for any place that requires the full canonical URL path for the Node.js Website (and its deployment), such as for example, the Node.js RSS Feed. - * - * Platform-specific base URLs (such as Vercel's `VERCEL_URL` on preview - * deployments) are inlined into `NEXT_PUBLIC_BASE_URL` at build time by the - * platform's `next.config.mjs` fragment (see `platforms/README.md`), keeping - * this module free of platform-specific branches. */ export const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || 'https://nodejs.org'; diff --git a/apps/site/playwright.config.mjs b/apps/site/playwright.config.mjs index 1e6a260852e1f..b4f4f29ce1b1c 100644 --- a/apps/site/playwright.config.mjs +++ b/apps/site/playwright.config.mjs @@ -1,9 +1,6 @@ import platformConfig from '#platform/playwright.config.mjs'; import { defineConfig, devices } from '@playwright/test'; -// The active deployment platform can contribute configuration (e.g. the -// Cloudflare preview web server); see `platforms/README.md`. - const isCI = !!process.env.CI; // https://playwright.dev/docs/test-configuration diff --git a/docs/cloudflare-build-and-deployment.md b/docs/cloudflare-build-and-deployment.md index 282e24c6b2260..d4485971d8f35 100644 --- a/docs/cloudflare-build-and-deployment.md +++ b/docs/cloudflare-build-and-deployment.md @@ -49,7 +49,7 @@ Additionally, when deploying, an extra `CF_WORKERS_SCRIPTS_API_TOKEN` environmen ### Image loader -When deployed on the Cloudflare network a custom image loader is required. The Cloudflare platform's Next.js configuration fragment ([`platforms/cloudflare/next.config.mjs`](../platforms/cloudflare/next.config.mjs)) sets such loader when building for the Cloudflare deployment (see [`platforms/README.md`](../platforms/README.md)). +When deployed on the Cloudflare network a custom image loader is required. The Cloudflare platform's Next.js configuration fragment ([`platforms/cloudflare/next.config.mjs`](../platforms/cloudflare/next.config.mjs)) sets such loader when building for the Cloudflare deployment. The custom loader can be found at [`platforms/cloudflare/image-loader.ts`](../platforms/cloudflare/image-loader.ts). diff --git a/docs/getting-started.md b/docs/getting-started.md index 273deef787d25..7a6aef3fb9a6d 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -108,7 +108,7 @@ This guide will help you set up your development environment and make your first ### Development Commands - `pnpm dev` - Start local development server -- `pnpm build` - Build for production (default platform, see [`platforms/README.md`](../platforms/README.md)) +- `pnpm build` - Build for production - `pnpm deploy` - Build for export (Legacy server) - `pnpm start` - Start server with built content diff --git a/docs/technologies.md b/docs/technologies.md index 9d15d9f50a961..c2070160578f2 100644 --- a/docs/technologies.md +++ b/docs/technologies.md @@ -293,7 +293,7 @@ Benefits: #### Multiple Build Targets -- **`pnpm build`**: Production build for the default platform (see [`platforms/README.md`](../platforms/README.md)) +- **`pnpm build`**: Production build for the default platform - **`pnpm deploy`**: Export build for legacy servers - **`pnpm dev`**: Development server diff --git a/platforms/cloudflare/next.config.mjs b/platforms/cloudflare/next.config.mjs index 44d81f0147389..621f905dd1ab4 100644 --- a/platforms/cloudflare/next.config.mjs +++ b/platforms/cloudflare/next.config.mjs @@ -1,9 +1,6 @@ import { getDeploymentId } from '@opennextjs/cloudflare'; /** - * Next.js configuration for the Cloudflare deployment. See - * `platforms/README.md`. - * * @type {import('next').NextConfig} */ export default { diff --git a/platforms/default/next.config.mjs b/platforms/default/next.config.mjs index b711ab4da6a15..33503298867e2 100644 --- a/platforms/default/next.config.mjs +++ b/platforms/default/next.config.mjs @@ -1,9 +1,4 @@ /** - * Next.js configuration for self-hosted deployments (plain `next build` / - * `next start`, the legacy static export, and local development). The shared - * configuration in `apps/site/next.config.mjs` already covers this platform - * entirely. See `platforms/README.md`. - * * @type {import('next').NextConfig} */ export default {}; diff --git a/platforms/vercel/next.config.mjs b/platforms/vercel/next.config.mjs index d5ef43be78525..06fe148d109ed 100644 --- a/platforms/vercel/next.config.mjs +++ b/platforms/vercel/next.config.mjs @@ -1,6 +1,4 @@ /** - * Next.js configuration for the Vercel deployment. See `platforms/README.md`. - * * @type {import('next').NextConfig} */ export default { From 7b48b117fef586a31c03f7d77cf6ed4bd9a95bab Mon Sep 17 00:00:00 2001 From: avivkeller Date: Tue, 28 Jul 2026 13:15:09 -0700 Subject: [PATCH 4/8] fixup! --- pnpm-lock.yaml | 467 +++++++++++++++++++++++++++++++++++++------- pnpm-workspace.yaml | 7 +- 2 files changed, 398 insertions(+), 76 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3916b530c44df..c38fc388a6387 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -218,8 +218,8 @@ catalogs: specifier: ^10.0.0 version: 10.1.0 next: - specifier: 16.2.6 - version: 16.2.6 + specifier: 16.3.0-preview.9 + version: 16.3.0-preview.9 react: specifier: ^19.2.6 version: 19.2.6 @@ -357,10 +357,10 @@ importers: version: 4.0.0 next: specifier: 'catalog:' - version: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) next-intl: specifier: ~4.13.0 - version: 4.13.0(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + version: 4.13.0(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(typescript@5.9.3) next-themes: specifier: ~0.4.6 version: 0.4.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -787,7 +787,7 @@ importers: version: 0.0.1 '@opennextjs/cloudflare': specifier: ^1.19.3 - version: 1.19.3(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(wrangler@4.77.0(@cloudflare/workers-types@4.20260422.1)) + version: 1.19.3(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(wrangler@4.77.0(@cloudflare/workers-types@4.20260422.1)) '@sentry/cloudflare': specifier: ^10.49.0 version: 10.49.0(@cloudflare/workers-types@4.20260422.1) @@ -818,7 +818,7 @@ importers: version: 10.1.0 next: specifier: 'catalog:' - version: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) platforms/default: devDependencies: @@ -827,7 +827,7 @@ importers: version: 1.59.1 next: specifier: 'catalog:' - version: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) platforms/vercel: dependencies: @@ -845,13 +845,13 @@ importers: version: 0.218.0(@opentelemetry/api@1.9.1) '@vercel/analytics': specifier: ~2.0.1 - version: 2.0.1(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) + version: 2.0.1(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) '@vercel/otel': specifier: ~2.1.2 version: 2.1.2(@opentelemetry/api-logs@0.218.0)(@opentelemetry/api@1.9.1)(@opentelemetry/instrumentation@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-logs@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1)) '@vercel/speed-insights': specifier: ~2.0.0 - version: 2.0.0(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) + version: 2.0.0(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) devDependencies: '@playwright/test': specifier: ^1.59.1 @@ -861,7 +861,7 @@ importers: version: 19.2.16 next: specifier: 'catalog:' - version: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react: specifier: 'catalog:' version: 19.2.6 @@ -1431,6 +1431,9 @@ packages: '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@emnapi/runtime@1.11.2': + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + '@emnapi/runtime@1.9.1': resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} @@ -2227,70 +2230,145 @@ packages: cpu: [arm64] os: [darwin] + '@img/sharp-darwin-arm64@0.35.3': + resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + '@img/sharp-darwin-x64@0.34.5': resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] + '@img/sharp-darwin-x64@0.35.3': + resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.3': + resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} + engines: {node: '>=20.9.0'} + os: [freebsd] + '@img/sharp-libvips-darwin-arm64@1.2.4': resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.3.2': + resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} + cpu: [arm64] + os: [darwin] + '@img/sharp-libvips-darwin-x64@1.2.4': resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] + '@img/sharp-libvips-darwin-x64@1.3.2': + resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} + cpu: [x64] + os: [darwin] + '@img/sharp-libvips-linux-arm64@1.2.4': resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-arm64@1.3.2': + resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-arm@1.3.2': + resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-ppc64@1.3.2': + resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-riscv64@1.3.2': + resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-s390x@1.3.2': + resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-x64@1.3.2': + resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} + cpu: [x64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] libc: [musl] + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} + cpu: [arm64] + os: [linux] + libc: [musl] + '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] libc: [musl] + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} + cpu: [x64] + os: [linux] + libc: [musl] + '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2298,6 +2376,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-arm64@0.35.3': + resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2305,6 +2390,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-arm@0.35.3': + resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2312,6 +2404,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-ppc64@0.35.3': + resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2319,6 +2418,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-riscv64@0.35.3': + resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2326,6 +2432,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-s390x@0.35.3': + resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2333,6 +2446,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-x64@0.35.3': + resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2340,6 +2460,13 @@ packages: os: [linux] libc: [musl] + '@img/sharp-linuxmusl-arm64@0.35.3': + resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2347,29 +2474,63 @@ packages: os: [linux] libc: [musl] + '@img/sharp-linuxmusl-x64@0.35.3': + resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] + '@img/sharp-wasm32@0.35.3': + resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.3': + resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + '@img/sharp-win32-arm64@0.34.5': resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] + '@img/sharp-win32-arm64@0.35.3': + resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + '@img/sharp-win32-ia32@0.34.5': resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] + '@img/sharp-win32-ia32@0.35.3': + resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + '@img/sharp-win32-x64@0.34.5': resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] + '@img/sharp-win32-x64@0.35.3': + resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + '@inquirer/external-editor@1.0.3': resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} @@ -2556,60 +2717,60 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@next/env@16.2.6': - resolution: {integrity: sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==} + '@next/env@16.3.0-preview.9': + resolution: {integrity: sha512-BcD+/sVDfoHckHcEx6e4RaLfWrUb+DpXgL9jOc6f29RTyvNwVVtnAaPljp3ok4zFQan6RhFE7q32lSXAFjjV9A==} '@next/eslint-plugin-next@16.2.6': resolution: {integrity: sha512-Z8l6o4JWKUl755x4R+wogD86KPeU+Ckw4K+SYG4kHeOJtRenDeK+OSbGcqZpDtbwn9DsJVdir2UxmwXuinUbUw==} - '@next/swc-darwin-arm64@16.2.6': - resolution: {integrity: sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==} + '@next/swc-darwin-arm64@16.3.0-preview.9': + resolution: {integrity: sha512-42OV/jDhHoKuqq+kp0RYrW1uksykLhXQeX+zx4SYFEG45ec9HnLHnsGgG1vy5W+LaSDvj3BPMBPTKMCh1vl1Ew==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.2.6': - resolution: {integrity: sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==} + '@next/swc-darwin-x64@16.3.0-preview.9': + resolution: {integrity: sha512-j6dvZHjhjB3l+uDQiBmuajzzCyCetWdQw+eouwR0mE455b+OvPY32dUNIY+D33oTOe3f0My9wSw4Um5hxywUlA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.2.6': - resolution: {integrity: sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==} + '@next/swc-linux-arm64-gnu@16.3.0-preview.9': + resolution: {integrity: sha512-2+1WzP2+ngzCkXiyzBwowgBiYaUycjAB/9b5ah5TjP0icBHOHf7oaPnjSZlZ8iq6x2G6QuR9S840jkOKNInUCw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.2.6': - resolution: {integrity: sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==} + '@next/swc-linux-arm64-musl@16.3.0-preview.9': + resolution: {integrity: sha512-2Tq6H0LtObGQj2s7u56TYakTJ3NmDOWnNRaT2TI17oQEUfPv3O4a22jt4xIDVkxvaYfB2L4y2hl8DGAEw1WV6g==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.2.6': - resolution: {integrity: sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==} + '@next/swc-linux-x64-gnu@16.3.0-preview.9': + resolution: {integrity: sha512-H2bCT2w2H/W6xRLRw4oWgOEMIyWc1tn65c46u0zC60omKig9hPWj7vrhMBqwOMTmZcFZwNqBTpJTF0LuopEfHA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.2.6': - resolution: {integrity: sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==} + '@next/swc-linux-x64-musl@16.3.0-preview.9': + resolution: {integrity: sha512-WK0CI8ky1oDGwqzi8oJjtC5rfMNOUx4DMBiVr1o9mvsMwkCUkVh7n6+AAx2Qxk8llRJxpaGlBZv1fhHytsENVw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.2.6': - resolution: {integrity: sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==} + '@next/swc-win32-arm64-msvc@16.3.0-preview.9': + resolution: {integrity: sha512-COFwXsMQsStyBv4qBn6E3tKHuPVifMkpVvF2k2/I0HsLAwuelgj+5uO8jS4e+sfTcdvczdvuiG8BjJz3aAa0IQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.2.6': - resolution: {integrity: sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==} + '@next/swc-win32-x64-msvc@16.3.0-preview.9': + resolution: {integrity: sha512-RG4rEpg3ynGwTP6mnsDZHLuFbKksXkQCd9A5MDkgPY9+wYawIrc23pJYHZxyBxisuV/t+dwCgbpZoclW8icE7A==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -7315,8 +7476,8 @@ packages: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - next@16.2.6: - resolution: {integrity: sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==} + next@16.3.0-preview.9: + resolution: {integrity: sha512-ruev+K1No0Cm/05hSrgKLtzaBdUIT+2I/Bj8x+K8/vk3j/dW+9YJOqGdDEkqNBOXtQlLxuEgeTH71jgsKTfWiQ==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -7745,8 +7906,8 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + postcss@8.5.10: + resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} engines: {node: ^10 || ^12 || >=14} postcss@8.5.15: @@ -8324,6 +8485,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -8351,6 +8517,15 @@ packages: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.35.3: + resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -10452,6 +10627,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.2': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.9.1': dependencies: tslib: 2.8.1 @@ -10966,95 +11146,199 @@ snapshots: '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true + '@img/sharp-darwin-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.2 + optional: true + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.2.4 optional: true + '@img/sharp-darwin-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.2 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true + '@img/sharp-libvips-darwin-arm64@1.3.2': + optional: true + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true + '@img/sharp-libvips-darwin-x64@1.3.2': + optional: true + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true + '@img/sharp-libvips-linux-arm64@1.3.2': + optional: true + '@img/sharp-libvips-linux-arm@1.2.4': optional: true + '@img/sharp-libvips-linux-arm@1.3.2': + optional: true + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true + '@img/sharp-libvips-linux-ppc64@1.3.2': + optional: true + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true + '@img/sharp-libvips-linux-riscv64@1.3.2': + optional: true + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true + '@img/sharp-libvips-linux-s390x@1.3.2': + optional: true + '@img/sharp-libvips-linux-x64@1.2.4': optional: true + '@img/sharp-libvips-linux-x64@1.3.2': + optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + optional: true + '@img/sharp-libvips-linuxmusl-x64@1.2.4': optional: true + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + optional: true + '@img/sharp-linux-arm64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true + '@img/sharp-linux-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.2 + optional: true + '@img/sharp-linux-arm@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.2.4 optional: true + '@img/sharp-linux-arm@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.2 + optional: true + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true + '@img/sharp-linux-ppc64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.2 + optional: true + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true + '@img/sharp-linux-riscv64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.2 + optional: true + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true + '@img/sharp-linux-s390x@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.2 + optional: true + '@img/sharp-linux-x64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.2.4 optional: true + '@img/sharp-linux-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.2 + optional: true + '@img/sharp-linuxmusl-arm64@0.34.5': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 optional: true + '@img/sharp-linuxmusl-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + optional: true + '@img/sharp-linuxmusl-x64@0.34.5': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.2.4 optional: true + '@img/sharp-linuxmusl-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + optional: true + '@img/sharp-wasm32@0.34.5': dependencies: '@emnapi/runtime': 1.10.0 optional: true + '@img/sharp-wasm32@0.35.3': + dependencies: + '@emnapi/runtime': 1.11.2 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + '@img/sharp-win32-arm64@0.34.5': optional: true + '@img/sharp-win32-arm64@0.35.3': + optional: true + '@img/sharp-win32-ia32@0.34.5': optional: true + '@img/sharp-win32-ia32@0.35.3': + optional: true + '@img/sharp-win32-x64@0.34.5': optional: true + '@img/sharp-win32-x64@0.35.3': + optional: true + '@inquirer/external-editor@1.0.3(@types/node@24.10.1)': dependencies: chardet: 2.2.0 @@ -11267,34 +11551,34 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@next/env@16.2.6': {} + '@next/env@16.3.0-preview.9': {} '@next/eslint-plugin-next@16.2.6': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@16.2.6': + '@next/swc-darwin-arm64@16.3.0-preview.9': optional: true - '@next/swc-darwin-x64@16.2.6': + '@next/swc-darwin-x64@16.3.0-preview.9': optional: true - '@next/swc-linux-arm64-gnu@16.2.6': + '@next/swc-linux-arm64-gnu@16.3.0-preview.9': optional: true - '@next/swc-linux-arm64-musl@16.2.6': + '@next/swc-linux-arm64-musl@16.3.0-preview.9': optional: true - '@next/swc-linux-x64-gnu@16.2.6': + '@next/swc-linux-x64-gnu@16.3.0-preview.9': optional: true - '@next/swc-linux-x64-musl@16.2.6': + '@next/swc-linux-x64-musl@16.3.0-preview.9': optional: true - '@next/swc-win32-arm64-msvc@16.2.6': + '@next/swc-win32-arm64-msvc@16.3.0-preview.9': optional: true - '@next/swc-win32-x64-msvc@16.2.6': + '@next/swc-win32-x64-msvc@16.3.0-preview.9': optional: true '@noble/ciphers@1.3.0': {} @@ -11452,7 +11736,7 @@ snapshots: ini: 4.1.3 nopt: 7.2.1 proc-log: 4.2.0 - semver: 7.8.2 + semver: 7.8.5 walk-up-path: 3.0.1 transitivePeerDependencies: - bluebird @@ -11466,7 +11750,7 @@ snapshots: proc-log: 4.2.0 promise-inflight: 1.0.1 promise-retry: 2.0.1 - semver: 7.8.2 + semver: 7.8.5 which: 4.0.0 transitivePeerDependencies: - bluebird @@ -11488,7 +11772,7 @@ snapshots: json-parse-even-better-errors: 3.0.2 normalize-package-data: 6.0.2 proc-log: 4.2.0 - semver: 7.8.2 + semver: 7.8.5 transitivePeerDependencies: - bluebird @@ -11505,7 +11789,7 @@ snapshots: '@open-draft/until@2.1.0': {} - '@opennextjs/aws@3.10.2(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': + '@opennextjs/aws@3.10.2(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': dependencies: '@ast-grep/napi': 0.40.5 '@aws-sdk/client-cloudfront': 3.984.0 @@ -11521,7 +11805,7 @@ snapshots: cookie: 1.1.1 esbuild: 0.25.4 express: 5.2.1 - next: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + next: 16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) path-to-regexp: 6.3.0 urlpattern-polyfill: 10.1.0 yaml: 2.9.0 @@ -11529,17 +11813,17 @@ snapshots: - aws-crt - supports-color - '@opennextjs/cloudflare@1.19.3(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(wrangler@4.77.0(@cloudflare/workers-types@4.20260422.1))': + '@opennextjs/cloudflare@1.19.3(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(wrangler@4.77.0(@cloudflare/workers-types@4.20260422.1))': dependencies: '@ast-grep/napi': 0.40.5 '@dotenvx/dotenvx': 1.31.0 - '@opennextjs/aws': 3.10.2(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) + '@opennextjs/aws': 3.10.2(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) ci-info: 4.3.0 cloudflare: 4.5.0 comment-json: 4.6.2 enquirer: 2.4.1 glob: 12.0.0 - next: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + next: 16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) ts-tqdm: 0.8.6 wrangler: 4.77.0(@cloudflare/workers-types@4.20260422.1) yargs: 18.0.0 @@ -13404,7 +13688,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.60.0 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.2 + semver: 7.8.5 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 @@ -13516,9 +13800,9 @@ snapshots: mdast-util-to-string: 3.2.0 unist-util-visit: 4.1.2 - '@vercel/analytics@2.0.1(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)': + '@vercel/analytics@2.0.1(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)': optionalDependencies: - next: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + next: 16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react: 19.2.6 '@vercel/otel@2.1.2(@opentelemetry/api-logs@0.218.0)(@opentelemetry/api@1.9.1)(@opentelemetry/instrumentation@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-logs@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))': @@ -13531,9 +13815,9 @@ snapshots: '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.1) - '@vercel/speed-insights@2.0.0(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)': + '@vercel/speed-insights@2.0.0(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)': optionalDependencies: - next: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + next: 16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react: 19.2.6 '@vitest/expect@3.2.4': @@ -16721,14 +17005,14 @@ snapshots: next-intl-swc-plugin-extractor@4.13.0: {} - next-intl@4.13.0(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(typescript@5.9.3): + next-intl@4.13.0(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(typescript@5.9.3): dependencies: '@formatjs/intl-localematcher': 0.8.10 '@parcel/watcher': 2.5.6 '@swc/core': 1.15.40 icu-minify: 4.13.0 negotiator: 1.0.0 - next: 16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + next: 16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) next-intl-swc-plugin-extractor: 4.13.0 po-parser: 2.1.1 react: 19.2.6 @@ -16743,31 +17027,32 @@ snapshots: react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@24.10.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: - '@next/env': 16.2.6 + '@next/env': 16.3.0-preview.9 '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.10.33 caniuse-lite: 1.0.30001793 - postcss: 8.4.31 + postcss: 8.5.10 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) styled-jsx: 5.1.6(react@19.2.6) optionalDependencies: - '@next/swc-darwin-arm64': 16.2.6 - '@next/swc-darwin-x64': 16.2.6 - '@next/swc-linux-arm64-gnu': 16.2.6 - '@next/swc-linux-arm64-musl': 16.2.6 - '@next/swc-linux-x64-gnu': 16.2.6 - '@next/swc-linux-x64-musl': 16.2.6 - '@next/swc-win32-arm64-msvc': 16.2.6 - '@next/swc-win32-x64-msvc': 16.2.6 + '@next/swc-darwin-arm64': 16.3.0-preview.9 + '@next/swc-darwin-x64': 16.3.0-preview.9 + '@next/swc-linux-arm64-gnu': 16.3.0-preview.9 + '@next/swc-linux-arm64-musl': 16.3.0-preview.9 + '@next/swc-linux-x64-gnu': 16.3.0-preview.9 + '@next/swc-linux-x64-musl': 16.3.0-preview.9 + '@next/swc-win32-arm64-msvc': 16.3.0-preview.9 + '@next/swc-win32-x64-msvc': 16.3.0-preview.9 '@opentelemetry/api': 1.9.1 '@playwright/test': 1.59.1 babel-plugin-react-compiler: 1.0.0 - sharp: 0.34.5 + sharp: 0.35.3(@types/node@24.10.1) transitivePeerDependencies: - '@babel/core' + - '@types/node' - babel-plugin-macros no-case@3.0.4: @@ -16800,14 +17085,14 @@ snapshots: normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 - semver: 7.8.2 + semver: 7.8.5 validate-npm-package-license: 3.0.4 normalize-path@3.0.0: {} npm-install-checks@6.3.0: dependencies: - semver: 7.8.2 + semver: 7.8.5 npm-normalize-package-bin@3.0.1: {} @@ -16815,7 +17100,7 @@ snapshots: dependencies: hosted-git-info: 7.0.2 proc-log: 4.2.0 - semver: 7.8.2 + semver: 7.8.5 validate-npm-package-name: 5.0.1 npm-pick-manifest@9.1.0: @@ -16823,7 +17108,7 @@ snapshots: npm-install-checks: 6.3.0 npm-normalize-package-bin: 3.0.1 npm-package-arg: 11.0.3 - semver: 7.8.2 + semver: 7.8.5 npm-run-path@4.0.1: dependencies: @@ -17214,7 +17499,7 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.4.31: + postcss@8.5.10: dependencies: nanoid: 3.3.12 picocolors: 1.1.1 @@ -18123,6 +18408,8 @@ snapshots: semver@7.8.2: {} + semver@7.8.5: {} + send@1.2.1: dependencies: debug: 4.4.3 @@ -18203,6 +18490,40 @@ snapshots: '@img/sharp-win32-ia32': 0.34.5 '@img/sharp-win32-x64': 0.34.5 + sharp@0.35.3(@types/node@24.10.1): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.3 + '@img/sharp-darwin-x64': 0.35.3 + '@img/sharp-freebsd-wasm32': 0.35.3 + '@img/sharp-libvips-darwin-arm64': 1.3.2 + '@img/sharp-libvips-darwin-x64': 1.3.2 + '@img/sharp-libvips-linux-arm': 1.3.2 + '@img/sharp-libvips-linux-arm64': 1.3.2 + '@img/sharp-libvips-linux-ppc64': 1.3.2 + '@img/sharp-libvips-linux-riscv64': 1.3.2 + '@img/sharp-libvips-linux-s390x': 1.3.2 + '@img/sharp-libvips-linux-x64': 1.3.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + '@img/sharp-linux-arm': 0.35.3 + '@img/sharp-linux-arm64': 0.35.3 + '@img/sharp-linux-ppc64': 0.35.3 + '@img/sharp-linux-riscv64': 0.35.3 + '@img/sharp-linux-s390x': 0.35.3 + '@img/sharp-linux-x64': 0.35.3 + '@img/sharp-linuxmusl-arm64': 0.35.3 + '@img/sharp-linuxmusl-x64': 0.35.3 + '@img/sharp-webcontainers-wasm32': 0.35.3 + '@img/sharp-win32-arm64': 0.35.3 + '@img/sharp-win32-ia32': 0.35.3 + '@img/sharp-win32-x64': 0.35.3 + '@types/node': 24.10.1 + optional: true + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2c804b9619d26..472ef57773d86 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,8 +4,8 @@ packages: - platforms/* allowBuilds: - "@parcel/watcher": true - "@swc/core": true + '@parcel/watcher': true + '@swc/core': true esbuild: true sharp: true unrs-resolver: true @@ -16,7 +16,8 @@ catalog: '@types/react': ^19.2.15 classnames: ~2.5.1 cross-env: ^10.0.0 - next: 16.2.6 + # Running on the preview to incorperate https://github.com/vercel/next.js/pull/93308 + next: 16.3.0-preview.9 react: ^19.2.6 tailwindcss: ~4.1.17 typescript: 5.9.3 From 93e829e734f99b970df2f2043c0a15a9e68fe0d6 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Tue, 28 Jul 2026 13:22:05 -0700 Subject: [PATCH 5/8] fixup! --- pnpm-lock.yaml | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c38fc388a6387..6d04b78e2aeed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -390,7 +390,7 @@ importers: version: 2.1.0 semver: specifier: ~7.8.1 - version: 7.8.2 + version: 7.8.5 sval: specifier: ^0.6.12 version: 0.6.12 @@ -623,7 +623,7 @@ importers: version: 7.0.1 semver: specifier: ^7.8.1 - version: 7.8.2 + version: 7.8.5 unified-lint-rule: specifier: ^3.0.1 version: 3.0.1 @@ -8480,11 +8480,6 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.8.2: - resolution: {integrity: sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==} - engines: {node: '>=10'} - hasBin: true - semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} @@ -10378,7 +10373,7 @@ snapshots: outdent: 0.5.0 prettier: 2.8.8 resolve-from: 5.0.0 - semver: 7.8.2 + semver: 7.8.5 '@changesets/assemble-release-plan@6.0.10': dependencies: @@ -10387,7 +10382,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 - semver: 7.8.2 + semver: 7.8.5 '@changesets/changelog-git@0.2.1': dependencies: @@ -10426,7 +10421,7 @@ snapshots: package-manager-detector: 0.2.11 picocolors: 1.1.1 resolve-from: 5.0.0 - semver: 7.8.2 + semver: 7.8.5 spawndamnit: 3.0.1 term-size: 2.2.1 transitivePeerDependencies: @@ -10452,7 +10447,7 @@ snapshots: '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 picocolors: 1.1.1 - semver: 7.8.2 + semver: 7.8.5 '@changesets/get-github-info@0.8.0': dependencies: @@ -11308,7 +11303,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.10.0 + '@emnapi/runtime': 1.11.2 optional: true '@img/sharp-wasm32@0.35.3': @@ -11526,7 +11521,7 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 + '@emnapi/runtime': 1.11.2 '@tybys/wasm-util': 0.10.2 optional: true @@ -11625,7 +11620,7 @@ snapshots: remark-rehype: 11.1.2 remark-stringify: 11.0.0 rolldown: 1.0.0-rc.13 - semver: 7.8.2 + semver: 7.8.5 shiki: 4.0.2 tinyglobby: 0.2.16 unified: 11.0.5 @@ -11724,7 +11719,7 @@ snapshots: dependencies: '@nodevu/parsefiles': 0.0.3 luxon: 3.6.1 - semver: 7.8.2 + semver: 7.8.5 '@nodevu/parsefiles@0.0.3': {} @@ -13117,7 +13112,7 @@ snapshots: react-docgen: 7.1.1 react-dom: 19.2.6(react@19.2.6) resolve: 1.22.12 - semver: 7.8.2 + semver: 7.8.5 storybook: 10.4.1(@testing-library/dom@10.4.0)(@types/react@19.2.16)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) tsconfig-paths: 4.2.0 webpack: 5.107.2(@swc/core@1.15.40)(postcss@8.5.15) @@ -13673,7 +13668,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.59.1 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.2 + semver: 7.8.5 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 @@ -14427,7 +14422,7 @@ snapshots: postcss-modules-scope: 3.2.1(postcss@8.5.15) postcss-modules-values: 4.0.0(postcss@8.5.15) postcss-value-parser: 4.2.0 - semver: 7.8.2 + semver: 7.8.5 optionalDependencies: webpack: 5.107.2(@swc/core@1.15.40)(postcss@8.5.15) @@ -14974,7 +14969,7 @@ snapshots: eslint-import-context: 0.1.9(unrs-resolver@1.11.1) is-glob: 4.0.3 minimatch: 10.2.5 - semver: 7.8.2 + semver: 7.8.5 stable-hash-x: 0.2.0 unrs-resolver: 1.11.1 optionalDependencies: @@ -15482,7 +15477,7 @@ snapshots: minimatch: 3.1.5 node-abort-controller: 3.1.1 schema-utils: 3.3.0 - semver: 7.8.2 + semver: 7.8.5 tapable: 2.3.3 typescript: 5.9.3 webpack: 5.107.2(@swc/core@1.15.40)(postcss@8.5.15) @@ -16029,7 +16024,7 @@ snapshots: is-bun-module@2.0.0: dependencies: - semver: 7.8.2 + semver: 7.8.5 is-callable@1.2.7: {} @@ -17449,7 +17444,7 @@ snapshots: cosmiconfig: 9.0.1(typescript@5.9.3) jiti: 2.7.0 postcss: 8.5.15 - semver: 7.8.2 + semver: 7.8.5 optionalDependencies: webpack: 5.107.2(@swc/core@1.15.40)(postcss@8.5.15) transitivePeerDependencies: @@ -18406,8 +18401,6 @@ snapshots: semver@6.3.1: {} - semver@7.8.2: {} - semver@7.8.5: {} send@1.2.1: @@ -18463,7 +18456,7 @@ snapshots: dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 - semver: 7.8.2 + semver: 7.8.5 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -18662,7 +18655,7 @@ snapshots: oxc-parser: 0.127.0 oxc-resolver: 11.20.0 recast: 0.23.11 - semver: 7.8.2 + semver: 7.8.5 use-sync-external-store: 1.6.0(react@19.2.6) ws: 8.21.0 optionalDependencies: From 51ec7483be8b98d1e88b9522a56fa7cc12cb87f0 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Tue, 28 Jul 2026 13:36:59 -0700 Subject: [PATCH 6/8] fixup! --- .github/workflows/playwright-cloudflare-open-next.yml | 4 ++-- .github/workflows/tmp-cloudflare-open-next-deploy.yml | 8 ++------ apps/site/turbo.json | 1 + docs/cloudflare-build-and-deployment.md | 4 ++-- platforms/cloudflare/package.json | 8 ++++---- platforms/cloudflare/turbo.json | 9 +++++++++ 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.github/workflows/playwright-cloudflare-open-next.yml b/.github/workflows/playwright-cloudflare-open-next.yml index d56eed85f9064..b049a3d08ddde 100644 --- a/.github/workflows/playwright-cloudflare-open-next.yml +++ b/.github/workflows/playwright-cloudflare-open-next.yml @@ -50,8 +50,8 @@ jobs: working-directory: apps/site run: node_modules/.bin/playwright install --with-deps - - name: Build OpenNext worker - run: pnpm --filter=@node-core/platform-cloudflare build:worker + - name: Build open-next site + run: node_modules/.bin/turbo build --filter=@node-core/platform-cloudflare --cache-dir=.turbo/cache - name: Run Playwright tests run: pnpm --filter=@node-core/platform-cloudflare test:e2e diff --git a/.github/workflows/tmp-cloudflare-open-next-deploy.yml b/.github/workflows/tmp-cloudflare-open-next-deploy.yml index 2a4c118494997..505be1ac820ee 100644 --- a/.github/workflows/tmp-cloudflare-open-next-deploy.yml +++ b/.github/workflows/tmp-cloudflare-open-next-deploy.yml @@ -52,15 +52,11 @@ jobs: - name: Install packages run: pnpm install --frozen-lockfile - - name: Build blog data - working-directory: apps/site - run: node --run build:blog-data - - name: Build open-next site - run: pnpm --filter=@node-core/platform-cloudflare build:worker + run: node_modules/.bin/turbo build --filter=@node-core/platform-cloudflare --cache-dir=.turbo/cache - name: Deploy open-next site - run: pnpm --filter=@node-core/platform-cloudflare deploy:worker + run: pnpm --filter=@node-core/platform-cloudflare deploy env: CF_WORKERS_SCRIPTS_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/apps/site/turbo.json b/apps/site/turbo.json index 0dc8b6586db99..21ef09bee6696 100644 --- a/apps/site/turbo.json +++ b/apps/site/turbo.json @@ -109,6 +109,7 @@ "cache": false }, "lint:types": { + "dependsOn": ["build:blog-data"], "cache": false }, "format": { diff --git a/docs/cloudflare-build-and-deployment.md b/docs/cloudflare-build-and-deployment.md index d4485971d8f35..bb1e00f3e99b5 100644 --- a/docs/cloudflare-build-and-deployment.md +++ b/docs/cloudflare-build-and-deployment.md @@ -77,6 +77,6 @@ For more details, refer to the [Sentry Cloudflare guide](https://docs.sentry.io/ Building, previewing, and deploying the website targeting the Cloudflare network is implemented via the following commands (all part of the [`@node-core/platform-cloudflare`](../platforms/cloudflare/package.json) package): -- `pnpm --filter=@node-core/platform-cloudflare build:worker` builds the website using the OpenNext Cloudflare adapter +- `pnpm --filter=@node-core/platform-cloudflare build` builds the website using the OpenNext Cloudflare adapter - `pnpm --filter=@node-core/platform-cloudflare preview` runs a previously built worker locally in a server simulating the Cloudflare hosting (using the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)) -- `pnpm --filter=@node-core/platform-cloudflare deploy:worker` builds the website using the OpenNext Cloudflare adapter and deploys it to the Cloudflare network (using the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)) +- `pnpm --filter=@node-core/platform-cloudflare deploy` builds the website using the OpenNext Cloudflare adapter and deploys it to the Cloudflare network (using the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)) diff --git a/platforms/cloudflare/package.json b/platforms/cloudflare/package.json index 5fe483d8bc136..1e5bedae58299 100644 --- a/platforms/cloudflare/package.json +++ b/platforms/cloudflare/package.json @@ -8,10 +8,10 @@ "directory": "platforms/cloudflare" }, "scripts": { - "prebuild:worker": "node ./scripts/twoslash-fsmap/index.mjs", - "build:worker": "cross-env NODE_OPTIONS=--conditions=cloudflare pnpm --filter=@node-core/website exec opennextjs-cloudflare build --openNextConfigPath ../../platforms/cloudflare/open-next.config.ts --config ../../platforms/cloudflare/wrangler.jsonc", - "predeploy:worker": "node ./scripts/twoslash-fsmap/index.mjs", - "deploy:worker": "cross-env NODE_OPTIONS=--conditions=cloudflare pnpm --filter=@node-core/website exec opennextjs-cloudflare deploy --openNextConfigPath ../../platforms/cloudflare/open-next.config.ts --config ../../platforms/cloudflare/wrangler.jsonc", + "prebuild": "node ./scripts/twoslash-fsmap/index.mjs", + "build": "cross-env NODE_OPTIONS=--conditions=cloudflare pnpm --filter=@node-core/website exec opennextjs-cloudflare build --openNextConfigPath ../../platforms/cloudflare/open-next.config.ts --config ../../platforms/cloudflare/wrangler.jsonc", + "predeploy": "node ./scripts/twoslash-fsmap/index.mjs", + "deploy": "cross-env NODE_OPTIONS=--conditions=cloudflare pnpm --filter=@node-core/website exec opennextjs-cloudflare deploy --openNextConfigPath ../../platforms/cloudflare/open-next.config.ts --config ../../platforms/cloudflare/wrangler.jsonc", "preview": "pnpm --filter=@node-core/website exec wrangler dev --config ../../platforms/cloudflare/wrangler.jsonc", "test:e2e": "cross-env NODE_OPTIONS=--conditions=cloudflare pnpm --filter=@node-core/website playwright", "lint:types": "tsc --noEmit" diff --git a/platforms/cloudflare/turbo.json b/platforms/cloudflare/turbo.json index bd6e869301068..462764e8d817c 100644 --- a/platforms/cloudflare/turbo.json +++ b/platforms/cloudflare/turbo.json @@ -2,6 +2,15 @@ "$schema": "https://turbo.build/schema.json", "extends": ["//"], "tasks": { + "build": { + "dependsOn": [ + "@node-core/website#build:blog-data", + "@node-core/rehype-shiki#build", + "@node-core/remark-lint#build", + "@node-core/ui-components#build", + "@node-core/website-i18n#build" + ] + }, "lint:types": { "cache": false } From da587ef60567ea59a783189a0d10c5768a599a55 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Tue, 28 Jul 2026 13:58:18 -0700 Subject: [PATCH 7/8] fixup! --- .github/workflows/build.yml | 4 ++-- apps/site/turbo.json | 24 +++++++++++++++++++++--- package.json | 4 ++-- platforms/cloudflare/next.config.mjs | 14 ++++++++++++++ 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bc7ed61eb9258..dc843b604f629 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,7 +59,7 @@ jobs: - name: Build Next.js # We want a ISR build on CI to ensure that regular Next.js builds work as expected. - run: node_modules/.bin/turbo build --cache-dir=.turbo/cache + run: node_modules/.bin/turbo build --filter=@node-core/website --cache-dir=.turbo/cache env: # We want to ensure we have enough RAM allocated to the Node.js process # this should be a last resort in case by any chances the build memory gets too high @@ -76,7 +76,7 @@ jobs: - name: Build Next.js (Static Export) # We want to generate a static build, as it is a requirement of our website. - run: node_modules/.bin/turbo deploy --cache-dir=.turbo/cache + run: node_modules/.bin/turbo deploy --filter=@node-core/website --cache-dir=.turbo/cache env: # We want to ensure we have enough RAM allocated to the Node.js process # this should be a last resort in case by any chances the build memory gets too high diff --git a/apps/site/turbo.json b/apps/site/turbo.json index 21ef09bee6696..bcb3469ba50dc 100644 --- a/apps/site/turbo.json +++ b/apps/site/turbo.json @@ -3,7 +3,13 @@ "extends": ["//"], "tasks": { "dev": { - "dependsOn": ["build:blog-data", "^build"], + "dependsOn": [ + "build:blog-data", + "@node-core/rehype-shiki#build", + "@node-core/remark-lint#build", + "@node-core/ui-components#build", + "@node-core/website-i18n#build" + ], "cache": false, "persistent": true, "env": [ @@ -22,7 +28,13 @@ ] }, "build": { - "dependsOn": ["build:blog-data", "^build"], + "dependsOn": [ + "build:blog-data", + "@node-core/rehype-shiki#build", + "@node-core/remark-lint#build", + "@node-core/ui-components#build", + "@node-core/website-i18n#build" + ], "inputs": [ "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx}", "{app,components,layouts,pages,styles}/**/*.css", @@ -116,7 +128,13 @@ "cache": false }, "test:unit": { - "dependsOn": ["build:blog-data", "^build"], + "dependsOn": [ + "build:blog-data", + "@node-core/rehype-shiki#build", + "@node-core/remark-lint#build", + "@node-core/ui-components#build", + "@node-core/website-i18n#build" + ], "inputs": [ "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx,mjs}", "{app,components,layouts,pages,styles}/**/*.css", diff --git a/package.json b/package.json index 31eeaee9d445c..3e1a7a0711b49 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,10 @@ "name": "Node.js Website Team and Contributors" }, "scripts": { - "build": "turbo build", + "build": "turbo build --filter=@node-core/website", "changeset": "changeset", "changeset:version": "changeset version", - "deploy": "turbo deploy", + "deploy": "turbo deploy --filter=@node-core/website", "dev": "turbo dev", "format": "turbo //#prettier:fix prettier:fix lint:fix", "lint": "turbo lint", diff --git a/platforms/cloudflare/next.config.mjs b/platforms/cloudflare/next.config.mjs index 621f905dd1ab4..ebf5c26eb4013 100644 --- a/platforms/cloudflare/next.config.mjs +++ b/platforms/cloudflare/next.config.mjs @@ -18,4 +18,18 @@ export default { // runs. loaderFile: '../../platforms/cloudflare/image-loader.ts', }, + // `NODE_OPTIONS=--conditions=cloudflare` only applies to Node's own resolver, + // which is what loads this file. The bundler resolves `#platform/*` with its + // own set of conditions, so it must be told about `cloudflare` separately — + // without this, the bundled code falls back to `platforms/default`. + webpack: config => { + config.resolve.conditionNames = [ + 'cloudflare', + // Anything already configured by Next.js, which in turn inherits + // webpack's defaults through the `'...'` entry. + ...config.resolve.conditionNames, + ]; + + return config; + }, }; From 02d56f28ed3a08b8a9cacd32589620d44db67c42 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Tue, 28 Jul 2026 18:07:18 -0700 Subject: [PATCH 8/8] fixup! --- platforms/vercel/next.config.mjs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/platforms/vercel/next.config.mjs b/platforms/vercel/next.config.mjs index 06fe148d109ed..f759ff64b58bd 100644 --- a/platforms/vercel/next.config.mjs +++ b/platforms/vercel/next.config.mjs @@ -10,4 +10,18 @@ export default { !process.env.NEXT_PUBLIC_BASE_URL && process.env.VERCEL_URL ? { NEXT_PUBLIC_BASE_URL: `https://${process.env.VERCEL_URL}` } : {}, + // `NODE_OPTIONS=--conditions=vercel` (see `vercel.json`) only applies to + // Node's own resolver, which is what loads this file. Turbopack resolves + // `#platform/*` with its own set of conditions and, unlike webpack, exposes + // no way to extend them (i think). + // + // TODO(@avivkeller): File a bug with turbopack + turbopack: { + resolveAlias: { + '#platform/analytics': '@node-core/platform-vercel/analytics.tsx', + '#platform/instrumentation': + '@node-core/platform-vercel/instrumentation.ts', + '#platform/shiki.mjs': '@node-core/platform-vercel/shiki.mjs', + }, + }, };