diff --git a/packages/core/src/config/define-config.ts b/packages/core/src/config/define-config.ts index fc5e311d5b..8cab75ff4e 100644 --- a/packages/core/src/config/define-config.ts +++ b/packages/core/src/config/define-config.ts @@ -6,6 +6,7 @@ import { DeepPartial, SitecoreConfig, SitecoreConfigInput } from './models'; * Provides default initial values for SitecoreConfig * @returns default config */ + export const getFallbackConfig = (): SitecoreConfig => ({ api: { edge: { @@ -20,6 +21,7 @@ export const getFallbackConfig = (): SitecoreConfig => ({ }, }, editingSecret: process.env.SITECORE_EDITING_SECRET || 'editing-secret-missing', + isEditingHost: !!process.env.SITECORE_EDITING_SECRET, retries: { count: 3, retryStrategy: new DefaultRetryStrategy({ diff --git a/packages/core/src/config/models.ts b/packages/core/src/config/models.ts index 164e8b086d..5838bbfeb0 100644 --- a/packages/core/src/config/models.ts +++ b/packages/core/src/config/models.ts @@ -79,6 +79,12 @@ export type SitecoreConfigInput = { * Default comes from the SITECORE_EDITING_SECRET environment variable. */ editingSecret?: string; + /** + * Indicates whether the app is running as an Editing Host. + * Recommended to be derived from the presence of `editingSecret`. + * Defaults to `!!process.env.SITECORE_EDITING_SECRET`. + */ + isEditingHost?: boolean; /** * Retry configuration applied to Layout, Dictionary and ErrorPages services */ diff --git a/packages/create-content-sdk-app/src/templates/nextjs/next.config.js b/packages/create-content-sdk-app/src/templates/nextjs/next.config.js index 0951dad4bc..7653145b05 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs/next.config.js +++ b/packages/create-content-sdk-app/src/templates/nextjs/next.config.js @@ -1,10 +1,30 @@ const path = require('path'); +const webpack = require('webpack'); const SassAlias = require('sass-alias'); +const fs = require('fs'); /** * @type {import('next').NextConfig} */ +// Determine host role strictly from env for build-time safety +const isEditingHost = !!process.env.SITECORE_EDITING_SECRET; + +// For Rendering Host builds, ensure the editing API folder is removed before Next scans pages +if (!isEditingHost) { + try { + const editingApiDir = path.join(process.cwd(), 'src', 'pages', 'api', 'editing'); + if (fs.existsSync(editingApiDir)) { + fs.rmSync(editingApiDir, { recursive: true, force: true }); + console.log('next.config: removed editing API routes for Rendering Host'); + } + } catch (error) { + console.warn('Failed to remove editing API routes:', error); + } +} + const nextConfig = { + // Default Next.js page extensions + pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'], // Allow specifying a distinct distDir when concurrently running app in a container distDir: process.env.NEXTJS_DIST_DIR || '.next', @@ -42,7 +62,7 @@ const nextConfig = { }, async rewrites() { - return [ + const routes = [ // healthz check { source: '/healthz', @@ -58,12 +78,17 @@ const nextConfig = { source: '/sitemap:id([\\w-]{0,}).xml', destination: '/api/sitemap' }, - // feaas api route - { + ]; + + // Only add FEAAS editing route for Editing Host builds + if (isEditingHost) { + routes.push({ source: '/feaas-render', destination: '/api/editing/feaas/render', - }, - ]; + }); + } + + return routes; }, webpack: (config, options) => { @@ -71,8 +96,37 @@ const nextConfig = { // Add a loader to strip out getComponentServerProps from components in the client bundle config.module.rules.unshift({ test: /src\\components\\.*\.tsx$/, + // reference this for the editing host exclusion task use: ['@sitecore-content-sdk\\nextjs\\component-props-loader'], }); + + // Exclude editing-only imports (DesignLibrary, .sitecore/import-map) from client bundle when not Editing Host + if (!isEditingHost) { + // Reuse the enhanced component-props loader to strip editing-only code across the app + config.module.rules.unshift({ + test: /src\\.*\.(ts|tsx)$/, + use: ['@sitecore-content-sdk\\nextjs\\component-props-loader'], + }); + + // Client-only aliases and ignore rules for editing-only code + config.resolve = config.resolve || {}; + config.resolve.alias = { + ...config.resolve.alias, + '@sitecore-content-sdk/nextjs/editing': false, + '@sitecore-content-sdk/nextjs/editing/codegen/import-map': false, + // Do NOT alias core editing on server; we strip only client usage + '@sitecore-content-sdk/react/dist/esm/components/DesignLibrary': false, + '.sitecore/import-map': false, + }; + + config.plugins = config.plugins || []; + config.plugins.push( + new webpack.IgnorePlugin({ + resourceRegExp: + /^(?:\.sitecore\/import-map(?:\.ts|\.js)?|@sitecore-content-sdk\/(?:core|nextjs)\/editing(?:\/.*)?|@sitecore-content-sdk\/react\/dist\/esm\/components\/DesignLibrary(?:\.js)?)$/, + }) + ); + } } else { // Force use of CommonJS on the server for FEAAS SDK since Content SDK also uses CommonJS entrypoint to FEAAS SDK. // This prevents issues arising due to FEAAS SDK's dual CommonJS/ES module support on the server (via conditional exports). @@ -86,6 +140,9 @@ const nextConfig = { ...config.externals, ]; } + + // Note: server bundle remains untouched; client-only handling above + <%_ if (helper.isDev) { -%> // monorepo configuration start if (options.isServer) { diff --git a/packages/create-content-sdk-app/src/templates/nextjs/sitecore.cli.config.ts b/packages/create-content-sdk-app/src/templates/nextjs/sitecore.cli.config.ts index 4107dcab1a..6cd43c13c9 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs/sitecore.cli.config.ts +++ b/packages/create-content-sdk-app/src/templates/nextjs/sitecore.cli.config.ts @@ -17,10 +17,14 @@ export default defineCliConfig({ extractFiles({ scConfig, }), - writeImportMap({ - paths: ['src/components'], - scConfig, - }), + ...(scConfig.disableCodeGeneration + ? [] + : [ + writeImportMap({ + paths: ['src/components'], + scConfig, + }), + ]), ], }, componentMap: { diff --git a/packages/create-content-sdk-app/src/templates/nextjs/sitecore.config.ts b/packages/create-content-sdk-app/src/templates/nextjs/sitecore.config.ts index 24de77be3d..5f2ad6cb6a 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs/sitecore.config.ts +++ b/packages/create-content-sdk-app/src/templates/nextjs/sitecore.config.ts @@ -4,4 +4,12 @@ import { defineConfig } from '@sitecore-content-sdk/nextjs/config'; * See the documentation for `defineConfig`: * https://doc.sitecore.com/xmc/en/developers/content-sdk/the-sitecore-configuration-file.html */ -export default defineConfig({}); +// Single source of truth derived from env +const isEditingHost = !!process.env.SITECORE_EDITING_SECRET; + +export default defineConfig({ + // Persist the flag on config so other parts of the app can read it + isEditingHost, + // Use the flag instead of re-deriving from env + disableCodeGeneration: !isEditingHost, +}); diff --git a/packages/create-content-sdk-app/src/templates/nextjs/src/Layout.tsx b/packages/create-content-sdk-app/src/templates/nextjs/src/Layout.tsx index af71021024..f82b7c178e 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs/src/Layout.tsx +++ b/packages/create-content-sdk-app/src/templates/nextjs/src/Layout.tsx @@ -1,12 +1,13 @@ -/** +// @ts-nocheck +/** * This Layout is needed for Starter Kit. */ import React, { JSX } from 'react'; import Head from 'next/head'; -import { Placeholder, Field, DesignLibrary, Page } from '@sitecore-content-sdk/nextjs'; +import { Placeholder, Field, Page, DesignLibrary } from '@sitecore-content-sdk/nextjs'; import Scripts from 'src/Scripts'; import SitecoreStyles from 'src/components/content-sdk/SitecoreStyles'; - +import scConfig from 'sitecore.config'; interface LayoutProps { page: Page; } @@ -21,7 +22,20 @@ const Layout = ({ page }: LayoutProps): JSX.Element => { const { route } = layout.sitecore; const fields = route?.fields as RouteFields; const mainClassPageEditing = mode.isEditing ? 'editing-mode' : 'prod-mode'; - const importMapDynamic = () => import('.sitecore/import-map'); + + // Use the public flag exposed by next.config.js (derived from SITECORE_EDITING_SECRET) + const isEditingHost = !!scConfig.isEditingHost; + let importMap: any = undefined; + if (isEditingHost) { + try { + importMap = require('.sitecore/import-map').default; + } catch (error) { + console.warn( + 'DesignLibrary: failed to load .sitecore/import-map; variant generation may be limited.', + error + ); + } + } return ( <> @@ -35,7 +49,7 @@ const Layout = ({ page }: LayoutProps): JSX.Element => { {/* root placeholder for the app, which we add components to using route data */}