@salesforce/storefront-next-dev 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +181 -0
- package/README.md +302 -0
- package/dist/cartridge-services/index.d.ts +60 -0
- package/dist/cartridge-services/index.d.ts.map +1 -0
- package/dist/cartridge-services/index.js +954 -0
- package/dist/cartridge-services/index.js.map +1 -0
- package/dist/cli.js +3373 -0
- package/dist/configs/react-router.config.d.ts +13 -0
- package/dist/configs/react-router.config.d.ts.map +1 -0
- package/dist/configs/react-router.config.js +36 -0
- package/dist/configs/react-router.config.js.map +1 -0
- package/dist/extensibility/templates/install-instructions.mdc.hbs +192 -0
- package/dist/extensibility/templates/uninstall-instructions.mdc.hbs +137 -0
- package/dist/index.d.ts +327 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2606 -0
- package/dist/index.js.map +1 -0
- package/dist/mrt/sfnext-server-chunk-DUt5XHAg.mjs +1 -0
- package/dist/mrt/sfnext-server-jiti-DjnmHo-6.mjs +10 -0
- package/dist/mrt/sfnext-server-jiti-DjnmHo-6.mjs.map +1 -0
- package/dist/mrt/ssr.d.ts +19 -0
- package/dist/mrt/ssr.d.ts.map +1 -0
- package/dist/mrt/ssr.mjs +246 -0
- package/dist/mrt/ssr.mjs.map +1 -0
- package/dist/mrt/streamingHandler.d.ts +11 -0
- package/dist/mrt/streamingHandler.d.ts.map +1 -0
- package/dist/mrt/streamingHandler.mjs +255 -0
- package/dist/mrt/streamingHandler.mjs.map +1 -0
- package/dist/react-router/Scripts.d.ts +36 -0
- package/dist/react-router/Scripts.d.ts.map +1 -0
- package/dist/react-router/Scripts.js +68 -0
- package/dist/react-router/Scripts.js.map +1 -0
- package/package.json +157 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["resolvedConfig: ResolvedConfig","path","__dirname","path","resolvedConfig: ResolvedConfig","buildDirectory: string","componentRegistry: PluginComponentRegistry","contextProviders: PluginContextProviderConfig[]","path","componentRegistry: PluginComponentRegistry","contextProviders: PluginContextProviderConfig[]","sourceDir: string","path","err: unknown","viteConfig: ResolvedConfig","glob","path","DEFAULT_COMPONENT_GROUP","baseComponentId: string","verbose","components: ComponentInfo[]","resolve","error","existingContent: string","projectRoot: string","resolve","verbose","error","join","resolve","verbose","error","resolvedConfig: ResolvedConfig","missingInstrumentation: Array<{ adapter: string; event: string }>","plugins: Plugin[]","path","result: DependencyRecord","path","filesInArchive: string[]","bundle_metadata: BundleMetadata","pkg.version","URL","errorData: { message?: string } | { message: string }","json: { detail?: string } | undefined","resolve","alias: Record<string, string>","existsSync","readFileSync","existsSync","path","colors: Record<string, typeof chalk.green>","colors","ServerModeFeatureMap: Record<ServerMode, ServerModeFeatures>","existsSync","error","FILE_EXTENSIONS: string[]","configuredExtensions: Record<string, unknown>","extensions: ExtensionsSelection","fs","path","e: unknown","error","newLines: string[]","blockMarkers: { extension: string; line: number }[]","err: unknown","isCliAvailable: boolean | null","readFileSync","error","existsSync","result: Array<{ id: string; path: string; file: string; index?: boolean }>","fullPath: string","VALID_ATTRIBUTE_TYPES: readonly AttributeType[]","TYPE_MAPPING: Record<string, string>","result: Record<string, unknown>","error","result: unknown[]","attributes: Record<string, unknown>[]","attribute: Record<string, unknown>","regionDefinitions: Record<string, unknown>[]","regionDefinition: Record<string, unknown>","components: unknown[]","pageTypes: unknown[]","aspects: unknown[]","cartridgeData: Record<string, unknown>","files: string[]","allComponents: unknown[]","allPageTypes: unknown[]","allAspects: unknown[]"],"sources":["../src/plugins/fixReactRouterManifestUrls.ts","../src/plugins/readableChunkFileNames.ts","../src/mrt/utils.ts","../src/plugins/managedRuntimeBundle.ts","../src/plugins/patchReactRouter.ts","../src/extensibility/plugin-utils.ts","../src/plugins/transformPlugins.ts","../src/plugins/watchConfigFiles.ts","../src/plugins/staticRegistry.ts","../src/plugins/configLoader.ts","../src/plugins/eventInstrumentationValidator.ts","../src/plugin.ts","../package.json","../src/utils/logger.ts","../src/utils.ts","../src/bundle.ts","../src/cloud-api.ts","../src/config.ts","../src/commands/push.ts","../src/server/ts-import.ts","../src/server/config.ts","../src/utils/paths.ts","../src/server/middleware/proxy.ts","../src/server/middleware/static.ts","../src/server/middleware/compression.ts","../src/server/middleware/logging.ts","../src/server/middleware/host-header.ts","../src/server/utils.ts","../src/server/modes.ts","../src/server/index.ts","../src/extensibility/path-util.ts","../src/extensibility/trim-extensions.ts","../src/cartridge-services/react-router-config.ts","../src/cartridge-services/generate-cartridge.ts"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { Plugin, ResolvedConfig } from 'vite';\nimport path from 'node:path';\nimport fs from 'fs-extra';\n\nfunction patchAssetsPaths(dir: string) {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n patchAssetsPaths(fullPath);\n } else if (entry.isFile() && entry.name.endsWith('.js')) {\n const content = fs.readFileSync(fullPath, 'utf-8');\n if (content.includes('\"/assets/') || content.includes(\"'/assets/\")) {\n // Transform asset URLs that start with /assets/ to use dynamic bundle path\n fs.writeFileSync(\n fullPath,\n content.replace(/[\"']\\/assets\\//g, '(window._BUNDLE_PATH || \"/\") + \"assets/')\n );\n // eslint-disable-next-line no-console\n console.log(`patched /assets/ references in ${fullPath}`);\n }\n }\n }\n}\n\n/**\n * Plugin to transform React Router client manifest URLs to use dynamic bundle paths\n */\nexport function fixReactRouterManifestUrlsPlugin(): Plugin {\n let resolvedConfig: ResolvedConfig;\n\n return {\n name: 'odyssey:fix-react-router-manifest-urls',\n enforce: 'post', // Run after React Router plugin\n\n configResolved(config: ResolvedConfig) {\n resolvedConfig = config;\n },\n\n // Post-process client manifest files after they're written to disk\n closeBundle() {\n const clientBuildDir = resolvedConfig.environments.client.build.outDir;\n if (fs.existsSync(clientBuildDir)) {\n // Patch references to `/assets/` in files within React Router's client build directory\n patchAssetsPaths(clientBuildDir);\n }\n },\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { Plugin, Rollup } from 'vite';\nimport path from 'path';\n\n/**\n * Generates human-readable chunk file names for better debugging in production builds.\n *\n * Transforms Rollup's default hash-based chunk names into structured paths that reflect\n * the original source location, making it easier to identify and debug specific chunks.\n *\n * @param chunkInfo - Rollup's pre-rendered chunk information containing module IDs and metadata\n * @returns A formatted string pattern for the chunk filename with one of these formats:\n * - `assets/(folder1)-(folder2)-filename.[hash].js` for source files in /src/\n * - `assets/(package)-(pkg-name)-(subfolder)-filename.[hash].js` for node_modules\n * - `assets/(chunk)-[name].[hash].js` as fallback for chunks without identifiable paths\n *\n * @example\n * // Source file: /src/components/ui/Button.tsx\n * // Output: assets/(components)-(ui)-Button.[hash].js\n *\n * @example\n * // Node module: /node_modules/@radix-ui/react-dialog/dist/index.js\n * // Output: assets/(package)-(@radix-ui)-(react-dialog)-(dist)-index.[hash].js\n */\nexport const readableChunkFileNames = (chunkInfo: Rollup.PreRenderedChunk) => {\n const moduleIds = chunkInfo.moduleIds;\n const defaultName = 'assets/(chunk)-[name].[hash].js';\n if (!moduleIds || moduleIds.length === 0) {\n return defaultName;\n }\n\n // Rollup moduleIds are in reverse order, the last moduleId is the one that was first in the source code\n const lastModuleId = moduleIds[moduleIds.length - 1];\n\n const toPosixPath = (pathname: string) => {\n return pathname.replace(/\\\\/g, '/');\n };\n\n const getFileName = (pathname: string) => {\n const posixPath = toPosixPath(pathname);\n const parsed = path.posix.parse(posixPath);\n const withoutQuery = parsed.base.split('?')[0];\n return withoutQuery.replace(/\\.(tsx?|jsx?|mjs|js)$/, '');\n };\n\n const cleanPath = (pathname: string) => {\n return pathname?.split('?')[0];\n };\n\n const normalizedModuleId = toPosixPath(lastModuleId);\n\n // Check if the module is from the application source code (under /src/ directory)\n // This generates chunk names based on the folder structure within src/\n // Example: src/components/ui/Button.tsx → assets/(components)-(ui)-Button.[hash].js\n if (normalizedModuleId.includes('/src/')) {\n const cleanedPath = toPosixPath(cleanPath(lastModuleId));\n const match = cleanedPath.match(/\\/src\\/(.+)$/);\n if (match) {\n const pathAfterSrc = match[1];\n const parts = pathAfterSrc.split('/');\n\n const fileName = getFileName(parts[parts.length - 1]);\n\n const folders = parts.slice(0, -1);\n\n const segments = folders.map((f) => `(${f})`).join('-');\n return `assets/${segments}-${fileName}.[hash].js`;\n }\n }\n\n // Check if the module is from an external package (under /node_modules/ directory)\n // This generates chunk names that include the package name and internal path\n // Example: node_modules/@radix-ui/react-dialog/dist/index.js → assets/(package)-(@radix-ui)-(react-dialog)-(dist)-index.[hash].js\n if (normalizedModuleId.includes('/node_modules/')) {\n const cleanedPath = toPosixPath(cleanPath(lastModuleId));\n\n const parts = cleanedPath.split('/node_modules/');\n const afterNodeModules = parts[parts.length - 1];\n\n const pathParts = afterNodeModules.split('/');\n\n // Handle scoped packages (@org/package)\n let packageName;\n let remainingPath;\n\n if (pathParts[0].startsWith('@')) {\n // Scoped package: @org/package/rest/of/path\n packageName = `${pathParts[0]}-${pathParts[1]}`;\n remainingPath = pathParts.slice(2);\n } else {\n // Regular package: package/rest/of/path\n packageName = pathParts[0];\n remainingPath = pathParts.slice(1);\n }\n\n const fileName = getFileName(remainingPath[remainingPath.length - 1]);\n\n const folders = remainingPath.slice(0, -1);\n\n const segments = ['package', packageName, ...folders].map((s) => `(${s})`).join('-');\n\n return `assets/${segments}-${fileName}.[hash].js`;\n }\n\n return defaultName;\n};\n\n/**\n * Vite plugin that configures Rollup to use human-readable chunk file names in production builds.\n *\n * Applies the `readableChunkFileNames` naming strategy to both code-split chunks and entry files,\n * making it easier to identify the source of specific chunks when debugging production builds.\n *\n * @returns A Vite plugin that configures chunk naming for the client build environment\n *\n * @example\n * // In vite.config.ts\n * export default defineConfig({\n * plugins: [readableChunkFileNamesPlugin()]\n * })\n */\nexport const readableChunkFileNamesPlugin = (): Plugin => {\n return {\n name: 'odyssey:readable-chunk-file-names',\n apply: 'build',\n config() {\n return {\n environments: {\n client: {\n build: {\n rollupOptions: {\n output: {\n chunkFileNames: readableChunkFileNames,\n entryFileNames: readableChunkFileNames,\n },\n },\n },\n },\n },\n };\n },\n };\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { APIGatewayProxyEvent } from 'aws-lambda';\n\nconst MRT_BUNDLE_TYPE_SSR = 'ssr' as const;\nconst MRT_STREAMING_ENTRY_FILE = 'streamingHandler' as const;\nexport type MrtBundleType = typeof MRT_BUNDLE_TYPE_SSR | typeof MRT_STREAMING_ENTRY_FILE;\n/**\n * Gets the MRT entry file for the given mode\n * @param mode - The mode to get the MRT entry file for\n * @returns The MRT entry file for the given mode\n */\nexport const getMrtEntryFile = (mode: string): MrtBundleType => {\n // TODO: Move the MRT_BUNDLE_TYPE env var to a command line option with sfnext\n // Streaming is enabled by default in production unless explicitly set to 'ssr'\n const enableStreaming = process.env.MRT_BUNDLE_TYPE !== MRT_BUNDLE_TYPE_SSR && mode === 'production';\n return enableStreaming ? MRT_STREAMING_ENTRY_FILE : MRT_BUNDLE_TYPE_SSR;\n};\n\n/**\n * Merges headers from event.headers into event.multiValueHeaders.\n *\n * @codegenie/serverless-express prefers multiValueHeaders over headers when both exist.\n * However, some headers (like x-correlation-id added by CloudFront/MRT) may only exist\n * in event.headers and not in event.multiValueHeaders, causing them to be lost.\n *\n * This function ensures all headers from event.headers are present in multiValueHeaders.\n */\nexport function mergeHeadersIntoMultiValueHeaders(event: APIGatewayProxyEvent): APIGatewayProxyEvent {\n if (!event.headers || !event.multiValueHeaders) {\n return event;\n }\n\n const mergedMultiValueHeaders = { ...event.multiValueHeaders };\n\n for (const [key, value] of Object.entries(event.headers)) {\n // Only add if not already in multiValueHeaders (case-insensitive check)\n const existingKey = Object.keys(mergedMultiValueHeaders).find((k) => k.toLowerCase() === key.toLowerCase());\n\n if (!existingKey && value !== undefined) {\n mergedMultiValueHeaders[key] = [value];\n }\n }\n\n return {\n ...event,\n multiValueHeaders: mergedMultiValueHeaders,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { Plugin, ResolvedConfig } from 'vite';\nimport path from 'path';\nimport fs from 'fs-extra';\nimport { fileURLToPath } from 'url';\nimport { getMrtEntryFile } from '../mrt/utils';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n/**\n * This is a Vite plugin specifically for building the Managed Runtime production bundle.\n * This plugin relies on the @react-router/dev/vite plugin to work.\n * This plugin creates the Managed Runtime production bundle from the build output of the @react-router/dev/vite plugin.\n *\n * @returns {Plugin} A Vite plugin for building the Managed Runtime production react-router bundle\n */\nexport const managedRuntimeBundlePlugin = (): Plugin => {\n let resolvedConfig: ResolvedConfig;\n\n // Note: The react-router vite plugin does not use/respect vite's config.build.outDir\n // We must not use the resolvedConfig.build.outDir\n // Instead, react-router has a \"buildDirectory\" option from react-router.config.ts\n // Should we infer the build directory from the react-router config OR let the user configure it?\n let buildDirectory: string;\n\n /**\n * Creates the Managed Runtime production bundle assets\n * - ssr.mjs or streamingHandler.mjs\n * - loader.js\n * - package.json\n *\n * @returns {Promise<void>}\n */\n const createManagedRuntimeBundleAssets = async (): Promise<void> => {\n const loaderPath = path.resolve(buildDirectory, 'loader.js');\n // TODO: Move the MRT_BUNDLE_TYPE env var to a command line option with sfnext\n const mrtEntryFile = `${getMrtEntryFile(resolvedConfig?.mode)}.mjs`;\n const mrtEntryPath = path.resolve(buildDirectory, mrtEntryFile);\n\n await fs.ensureDir(buildDirectory);\n await fs.outputFile(loaderPath, '// This file is intentionally empty');\n\n const prebuiltMrtEntryPath = path.resolve(__dirname, `./mrt/${mrtEntryFile}`);\n await fs.copy(prebuiltMrtEntryPath, mrtEntryPath);\n\n // Copy shared chunks generated by the build process\n // These are identified by the 'sfnext-server-' prefix\n const mrtDir = path.resolve(__dirname, './mrt');\n if (await fs.pathExists(mrtDir)) {\n const files = await fs.readdir(mrtDir);\n for (const file of files) {\n if (file.startsWith('sfnext-server-') && file.endsWith('.mjs')) {\n await fs.copy(path.join(mrtDir, file), path.resolve(buildDirectory, file));\n }\n }\n }\n\n const packageJsonPath = path.resolve(resolvedConfig.root, 'package.json');\n const buildPackageJsonPath = path.resolve(buildDirectory, 'package.json');\n\n const packageJson = await fs.readJson(packageJsonPath);\n\n // Currently MRT only supports CJS modules, and we are building a CJS bundle.\n // But we need to make sure the package.json doesn't have the \"type\" key to use ESM\n // otherwise the MRT environment will break because the node ESM resolution will not work\n // for our CJS bundle.\n delete packageJson.type;\n await fs.writeJson(buildPackageJsonPath, packageJson, { spaces: 2 });\n };\n\n return {\n name: 'odyssey:managed-runtime-bundle',\n apply: 'build',\n config({ mode }) {\n return {\n environments: {\n ssr: {\n resolve: {\n noExternal: true,\n },\n },\n },\n experimental: {\n renderBuiltUrl(filename, { type }) {\n if (mode !== 'preview' && (type === 'asset' || type === 'public')) {\n const runtimeCode = `(typeof window !== 'undefined' ? window._BUNDLE_PATH : ('/mobify/bundle/'+(process.env.BUNDLE_ID??'local')+'/client/')) + ${JSON.stringify(filename)}`;\n\n return {\n runtime: runtimeCode,\n };\n }\n },\n },\n };\n },\n configResolved(config) {\n resolvedConfig = config;\n\n // @ts-expect-error: react-router plugin context is not typed\n buildDirectory = config.__reactRouterPluginContext.reactRouterConfig.buildDirectory;\n },\n buildApp: {\n order: 'post',\n handler: async () => {\n await createManagedRuntimeBundleAssets();\n },\n },\n };\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { Plugin } from 'vite';\n\nconst VIRTUAL_MODULE_ID = '\\0patched-react-router';\nconst MODULE_TO_PATCH = 'react-router';\n\n/**\n * This plugin intercepts imports of 'react-router' and provides patched versions\n * of specific components (like Scripts) with custom logic.\n *\n * @returns {Plugin} A Vite plugin for patching react-router components\n */\nexport const patchReactRouterPlugin = (): Plugin => {\n let isTestMode = false;\n\n return {\n name: 'odyssey:patch-react-router',\n // must be enforce: 'pre'\n // otherwise the react-router plugin will resolve the module first\n // and we will not be able to enhance the module with our custom logic\n enforce: 'pre',\n config(_config, { mode }) {\n // Detect test mode to disable patching\n // Virtual module IDs with \\0 prefix cause path resolution errors on Windows\n // when Vitest tries to resolve them with vi.importActual\n isTestMode = mode === 'test';\n },\n configEnvironment(name) {\n if (isTestMode) {\n return;\n }\n if (name === 'ssr') {\n // By default, on dev mode, Vite does not process external modules like react-router\n // but we need to patch it, so we mark react-router as noExternal\n // so that it is included in the Vite plugin pipeline, and we can patch it\n return {\n resolve: {\n noExternal: ['react-router'],\n },\n };\n }\n },\n resolveId(id, importer) {\n // Skip patching in test mode to avoid Windows path resolution errors\n if (isTestMode) {\n return null;\n }\n if (id === MODULE_TO_PATCH) {\n // In the virtual module, we need to import the same react-router module\n // and then re-export everything from it, and override a subset of the exports.\n // This following code is to make sure that the import from the virtual module\n // imports the same react-router module and not causing infinite loop.\n if (importer === VIRTUAL_MODULE_ID || importer?.includes('storefront-next-dev')) {\n return null;\n }\n return VIRTUAL_MODULE_ID;\n }\n return null;\n },\n\n load(id) {\n // Skip patching in test mode\n if (isTestMode) {\n return null;\n }\n if (id === VIRTUAL_MODULE_ID) {\n const scriptsImportPath = '@salesforce/storefront-next-dev/react-router/Scripts';\n\n const code = `\n export * from 'react-router';\n export { Scripts } from '${scriptsImportPath}';\n `;\n return code;\n }\n return null;\n },\n };\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { parse } from '@babel/parser';\nimport {\n isJSXIdentifier,\n isJSXAttribute,\n jsxText,\n isJSXElement,\n isJSXFragment,\n jsxElement,\n jsxOpeningElement,\n jsxIdentifier,\n jsxClosingElement,\n jsxFragment,\n jsxOpeningFragment,\n jsxClosingFragment,\n type JSXElement as BabelJSXElement,\n type File,\n type VariableDeclaration as BabelVariableDeclaration,\n type ReturnStatement as BabelReturnStatement,\n type ImportDeclaration as BabelImportDeclaration,\n} from '@babel/types';\nimport fs from 'fs-extra';\nimport { generate } from '@babel/generator';\nimport path from 'path';\n\nimport traverseModule, { type NodePath } from '@babel/traverse';\n\nexport interface PluginComponentConfig {\n pluginId: string;\n path: string;\n namespace: string;\n componentName: string;\n order: number;\n}\n\nexport interface PluginContextProviderConfig {\n path: string;\n namespace: string;\n componentName: string;\n order: number;\n}\n\nexport type PluginComponentRegistry = Record<string, PluginComponentConfig[]>;\n\nconst traverse = (traverseModule as unknown as { default: typeof traverseModule }).default || traverseModule;\n\nconst PLUGIN_COMPONENT_TAG = 'PluginComponent';\nconst PLUGIN_PROVIDERS_TAG = 'PluginProviders';\nconst PLUGIN_ID_ATTRIBUTE = 'pluginId';\n\n/**\n * Find and replace the PluginProviders tags with the corresponding context providers\n * @param element - the AST element to replace\n * @param contextProviders - the context providers to replace\n */\nfunction findAndReplaceProviders(\n element: NodePath<BabelJSXElement>,\n contextProviders: PluginContextProviderConfig[]\n): void {\n if (isJSXIdentifier(element.node.openingElement.name, { name: PLUGIN_PROVIDERS_TAG })) {\n // if there are context providers, replace the PluginProviders tag with the corresponding context providers in reverse order\n if (contextProviders.length > 0) {\n let nested = element.node.children;\n for (let i = contextProviders.length - 1; i >= 0; i--) {\n const contextProvider = contextProviders[i];\n const componentName = contextProvider.componentName;\n const providerElement = jsxElement(\n jsxOpeningElement(jsxIdentifier(componentName), [], false),\n jsxClosingElement(jsxIdentifier(componentName)),\n nested,\n false\n );\n nested = [providerElement];\n }\n element.replaceWithMultiple(nested);\n } else {\n // no replacement needed, just remove the PluginProviders tag,\n // but wrap the children in a JSXFragment\n element.replaceWith(jsxFragment(jsxOpeningFragment(), jsxClosingFragment(), element.node.children));\n }\n }\n}\n\n/**\n * Find and replace the plugin component with the replacement code\n * @param componentName - the name of the component to replace\n * @param element - the AST element as the replacement candidate\n * @param pluginRegistry - the plugin registry\n * @returns the pluginId that was replaced, or null if no replacement was found\n */\nfunction findAndReplaceComponent(\n componentName: string,\n element: NodePath<BabelJSXElement>,\n pluginRegistry: PluginComponentRegistry\n): string | null {\n let pluginIdReplaced = null;\n if (isJSXIdentifier(element.node.openingElement.name, { name: componentName })) {\n let replaced = false;\n // Find the \"pluginId\" property value from the JSX node, then replace the element with the replacement code\n // if no matching replacement is found, remove the element\n if (Array.isArray(element.node.openingElement.attributes)) {\n const attr = element.node.openingElement.attributes.find(\n (a) => isJSXAttribute(a) && isJSXIdentifier(a.name, { name: PLUGIN_ID_ATTRIBUTE })\n );\n const pluginId =\n attr && isJSXAttribute(attr) && attr.value && 'value' in attr.value ? attr.value.value : undefined;\n if (pluginId == null) {\n throw new Error(`PluginComponent must contain a pluginId attribute`);\n }\n if (pluginRegistry[pluginId] && pluginRegistry[pluginId].length > 0) {\n // Create JSX elements for each component\n const components = pluginRegistry[pluginId].map((pluginComponent: PluginComponentConfig) => {\n return jsxElement(\n jsxOpeningElement(jsxIdentifier(pluginComponent.componentName), [], true),\n null,\n [],\n true\n );\n });\n\n // If multiple components, wrap in a fragment; otherwise use single component\n if (components.length > 1) {\n element.replaceWith(jsxFragment(jsxOpeningFragment(), jsxClosingFragment(), components));\n } else {\n element.replaceWith(components[0]);\n }\n pluginIdReplaced = pluginId;\n replaced = true;\n }\n }\n if (!replaced) {\n if (element.node.children && element.node.children.length > 0) {\n // replace the element with its children\n element.replaceWithMultiple(element.node.children);\n } else {\n // No children, just remove the element\n element.remove();\n }\n }\n }\n return pluginIdReplaced;\n}\n\n/**\n * Run a replacement pass on the AST\n * @param ast - the AST to traverse\n * @param tagName - the name of the tag to replace\n * @param pluginRegistry - the plugin registry\n * @param contextProviders - the context providers to replace\n * @returns a set of pluginIds that were replaced\n */\nfunction runReplacementPass(\n ast: File,\n tagName: string,\n pluginRegistry: PluginComponentRegistry | null = null,\n contextProviders: PluginContextProviderConfig[] | null = null\n): Set<string> {\n const pluginIdsReplaced = new Set<string>();\n // Helper function to apply the replacement for a given path, targeting either the PluginComponent or PluginProviders tag\n const applyReplacement = (pathToReplace: NodePath<BabelJSXElement>) => {\n if (pluginRegistry) {\n const replacedId = findAndReplaceComponent(tagName, pathToReplace, pluginRegistry);\n if (replacedId) pluginIdsReplaced.add(replacedId);\n } else if (contextProviders) {\n findAndReplaceProviders(pathToReplace, contextProviders);\n }\n };\n traverse(ast, {\n // look for variable declarations that contains <PluginComponent .../> in JSX fragment\n VariableDeclaration(nodePath: NodePath<BabelVariableDeclaration>) {\n const declarationPaths = nodePath.get('declarations');\n const declarationsArray = Array.isArray(declarationPaths) ? declarationPaths : [declarationPaths];\n\n for (const declarationPath of declarationsArray) {\n const initPath = declarationPath.get('init');\n if (initPath && isJSXElement(initPath.node)) {\n const content = generate(initPath.node).code;\n if (new RegExp(`<(${tagName})(\\\\s|\\\\/|>)`).test(content)) {\n // Handle the init node itself\n applyReplacement(initPath as NodePath<BabelJSXElement>);\n\n // Traverse nested JSX elements with the same handler\n initPath.traverse({\n JSXElement(inner: NodePath<BabelJSXElement>) {\n applyReplacement(inner);\n },\n });\n }\n }\n }\n },\n // look for return statements that contains the tag\n ReturnStatement(nodePath: NodePath<BabelReturnStatement>) {\n const arg = nodePath.node.argument;\n if (!isJSXElement(arg) && !isJSXFragment(arg)) {\n return;\n }\n nodePath.traverse({\n JSXElement(inner: NodePath<BabelJSXElement>) {\n applyReplacement(inner);\n },\n });\n },\n });\n return pluginIdsReplaced;\n}\n\n/**\n * Build the import statements for the plugin components\n * @param pluginIds - the pluginIds that were replaced\n * @param pluginRegistry - the plugin registry\n * @returns the import statements\n */\nfunction buildReplacementImportStatements(pluginIds: Set<string>, pluginRegistry: PluginComponentRegistry): string {\n const importStatements = new Set<string>();\n for (const pluginId of pluginIds) {\n const pluginComponents = pluginRegistry[pluginId];\n for (const pluginComponent of pluginComponents) {\n importStatements.add(\n `import ${pluginComponent.componentName} from '@/${pluginComponent.path.replace('.tsx', '')}';`\n );\n }\n }\n return Array.from(importStatements).join('\\n');\n}\n\nexport function transformPlugins(\n code: string,\n pluginRegistry: PluginComponentRegistry,\n contextProviders: PluginContextProviderConfig[]\n): string | null {\n if (!code.includes(PLUGIN_COMPONENT_TAG) && !code.includes(PLUGIN_PROVIDERS_TAG)) {\n return null;\n }\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx', 'decorators-legacy'],\n });\n\n // replace PluginComponent tags with the corresponding components\n if (code.includes(PLUGIN_COMPONENT_TAG)) {\n const pluginIdsReplaced = runReplacementPass(ast, PLUGIN_COMPONENT_TAG, pluginRegistry, null);\n const replacementImportStatements = buildReplacementImportStatements(pluginIdsReplaced, pluginRegistry);\n // Single import rewrite pass\n traverse(ast, {\n ImportDeclaration(nodePath: NodePath<BabelImportDeclaration>) {\n const source = nodePath.node.source.value;\n if (source.includes('@/plugins/plugin-component')) {\n nodePath.replaceWith(jsxText(replacementImportStatements));\n }\n },\n });\n }\n\n // replace PluginProviders tags with the corresponding components\n if (code.includes(PLUGIN_PROVIDERS_TAG)) {\n // add import statements for the context providers\n const importStatements = new Set<string>();\n for (const contextProvider of contextProviders) {\n importStatements.add(\n `import ${contextProvider.componentName} from '@/${contextProvider.path.replace('.tsx', '')}';`\n );\n }\n const replacementImportStatements = Array.from(importStatements).join('\\n');\n // Single import rewrite pass\n traverse(ast, {\n ImportDeclaration(nodePath: NodePath<BabelImportDeclaration>) {\n const source = nodePath.node.source.value;\n if (source.includes('@/plugins/plugin-providers')) {\n nodePath.replaceWith(jsxText(replacementImportStatements));\n }\n },\n });\n runReplacementPass(ast, PLUGIN_PROVIDERS_TAG, null, contextProviders);\n }\n return generate(ast).code;\n}\n\n/**\n * Build the plugin registry from the extension directories\n * @param rootDir - the root directory of the project\n * @param sourceDir - the source directory of the project\n * @returns the plugin registry\n */\nexport function buildPluginRegistry(rootDir: string): {\n componentRegistry: PluginComponentRegistry;\n contextProviders: PluginContextProviderConfig[];\n} {\n const componentRegistry: PluginComponentRegistry = {};\n const contextProviders: PluginContextProviderConfig[] = [];\n const extensionDirPath = path.join(rootDir, 'extensions');\n const extensionDirs = fs.readdirSync(extensionDirPath, { withFileTypes: true });\n\n const getNamespaceAndComponentName = (dir: fs.Dirent, filePath: string) => {\n const namespace = dir.name\n .split('-')\n .map((word: string) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join('');\n const fileName = filePath.split('/').pop()?.replace('.tsx', '');\n const baseComponentName = fileName\n ?.split('-')\n .map((word: string) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join('');\n const componentName = `${namespace}_${baseComponentName}`;\n return { namespace, componentName };\n };\n for (const dir of extensionDirs) {\n if (dir.isDirectory()) {\n const configPath = path.join(extensionDirPath, dir.name, 'plugin-config.json');\n if (fs.existsSync(configPath)) {\n const pluginConfig = fs.readJsonSync(configPath);\n if (pluginConfig && pluginConfig.components) {\n for (const pluginComponent of pluginConfig.components) {\n const { pluginId, path: componentPath, order = 0 } = pluginComponent;\n if (pluginId && componentPath) {\n if (!componentRegistry[pluginId]) {\n componentRegistry[pluginId] = [];\n }\n const { namespace, componentName } = getNamespaceAndComponentName(dir, componentPath);\n componentRegistry[pluginId].push({\n pluginId,\n path: componentPath,\n order,\n namespace,\n componentName,\n });\n }\n }\n }\n if (pluginConfig && pluginConfig.contextProviders) {\n for (const contextProvider of pluginConfig.contextProviders) {\n const { path: providerPath, order = 0 } = contextProvider;\n if (providerPath) {\n const { namespace, componentName } = getNamespaceAndComponentName(dir, providerPath);\n contextProviders.push({ path: providerPath, namespace, componentName, order });\n }\n }\n }\n }\n }\n }\n // Sort each extension's components by order\n for (const pluginId in componentRegistry) {\n componentRegistry[pluginId].sort((a, b) => a.order - b.order);\n }\n contextProviders.sort((a, b) => a.order - b.order);\n return { componentRegistry, contextProviders };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n buildPluginRegistry,\n transformPlugins,\n type PluginContextProviderConfig,\n type PluginComponentRegistry,\n} from '../extensibility/plugin-utils';\nimport path from 'path';\nimport type { ResolvedConfig } from 'vite';\n\n// --- Vite Plugin --------------------------------------------------------------\n\nexport function transformPluginPlaceholderPlugin() {\n // Memoize the extension registry - build it once and reuse across all file transformations\n let componentRegistry: PluginComponentRegistry;\n let contextProviders: PluginContextProviderConfig[];\n let sourceDir: string;\n\n return {\n name: 'odyssey:transform-plugin-placeholder',\n enforce: 'pre' as const, // run before Vite's default TS/JS transforms\n configResolved(config: ResolvedConfig) {\n // extract source directory from vite config\n sourceDir =\n config.resolve.alias.find((alias) => alias.find === '@')?.replacement ||\n path.resolve(__dirname, './src');\n },\n buildStart() {\n // Build the registry once at the start of the build\n ({ componentRegistry, contextProviders } = buildPluginRegistry(sourceDir));\n },\n\n transform(code: string, id: string) {\n try {\n const transformedCode = transformPlugins(code, componentRegistry, contextProviders);\n if (transformedCode) {\n return {\n code: transformedCode,\n map: null,\n };\n }\n return null;\n } catch (err: unknown) {\n // eslint-disable-next-line no-console\n console.error(\n `PluginComponent replace ERROR in ${id}: ${err instanceof Error ? err.stack : String(err)}`\n );\n throw err;\n }\n },\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { ViteDevServer, ResolvedConfig } from 'vite';\nimport path from 'path';\n\nexport const watchConfigFilesPlugin = () => {\n let viteConfig: ResolvedConfig;\n return {\n name: 'odyssey:watch-config-files',\n configResolved(config: ResolvedConfig) {\n viteConfig = config;\n },\n configureServer(server: ViteDevServer) {\n const aliases = viteConfig.resolve.alias;\n const root = Object.values(aliases).find((alias) => alias.find === '@')?.replacement || 'src';\n // Use path.posix.join to ensure forward slashes for glob patterns (required even on Windows)\n const glob = path.posix.join(root, 'extensions', '**', 'plugin-config.json');\n server.watcher.add(glob);\n\n const onChange = (file: string) => {\n if (file.endsWith('plugin-config.json')) {\n // eslint-disable-next-line no-console\n console.log(`🔁 plugin-config.json changed: ${file}`);\n void server.restart();\n }\n };\n\n server.watcher.on('add', onChange);\n server.watcher.on('change', onChange);\n server.watcher.on('unlink', onChange);\n },\n };\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { Plugin } from 'vite';\nimport { readFileSync, writeFileSync, existsSync } from 'fs';\nimport { resolve, relative, dirname } from 'path';\nimport { glob } from 'glob';\nimport {\n Project,\n Node,\n type Decorator,\n type SourceFile,\n ts,\n type FunctionDeclaration,\n type VariableStatement,\n} from 'ts-morph';\n\n// Default component group when none is specified in the decorator\nconst DEFAULT_COMPONENT_GROUP = 'odyssey_base';\n\n/**\n * Information about a discovered component\n */\nexport interface ComponentInfo {\n /** Component ID from @Component decorator */\n id: string;\n /** Absolute path to the component file */\n filePath: string;\n /** Relative import path from registry.ts */\n relativePath: string;\n /** Whether the component has loader exports */\n hasLoader: boolean;\n /** Whether the component has clientLoader export */\n hasClientLoader: boolean;\n /** Whether the component has fallback export */\n hasFallback: boolean;\n}\n\n/**\n * Configuration options for the static registry plugin\n */\nexport interface StaticRegistryPluginConfig {\n /**\n * Path to the components directory to scan\n * @default 'src/components'\n */\n componentPath?: string;\n\n /**\n * Path to the registry file to update\n * Note: The registry file must contain STATIC_REGISTRY_START and STATIC_REGISTRY_END markers\n * and must export a 'registry' variable (or use registryIdentifier to specify a different name)\n * @default 'src/lib/registry.ts'\n */\n registryPath?: string;\n\n /**\n * Name of the registry variable to use in generated code\n * @default 'registry'\n */\n registryIdentifier?: string;\n\n /**\n * Whether to fail the build on registry generation errors\n * @default true\n */\n failOnError?: boolean;\n\n /**\n * Enable verbose logging\n * @default false\n */\n verbose?: boolean;\n}\n\n/**\n * Extracts component ID and group from @Component decorator using ts-morph AST parsing\n */\nexport function extractComponentInfo(decorator: Decorator): { id: string; group: string } | null {\n const callExpression = decorator.getCallExpression();\n if (!callExpression) {\n return null;\n }\n\n const args = callExpression.getArguments();\n if (args.length === 0) {\n return null;\n }\n\n // First argument should be the component ID string (string literal or template literal)\n const firstArg = args[0];\n\n let baseComponentId: string;\n if (Node.isStringLiteral(firstArg)) {\n baseComponentId = firstArg.getLiteralValue();\n } else if (Node.isNoSubstitutionTemplateLiteral(firstArg)) {\n baseComponentId = firstArg.getText().slice(1, -1); // Remove backticks\n } else if (Node.isTemplateExpression(firstArg)) {\n // Template literals with interpolation cannot be resolved at build time\n throw new Error(\n `@Component id must be a simple string literal or backtick string without interpolation. Found: ${firstArg.getText()}`\n );\n } else {\n return null;\n }\n let group = DEFAULT_COMPONENT_GROUP;\n\n // Check if there's a second argument with metadata object\n if (args.length > 1) {\n const secondArg = args[1];\n if (Node.isObjectLiteralExpression(secondArg)) {\n // Look for group property in the metadata object\n const groupProperty = secondArg.getProperty('group');\n if (groupProperty && Node.isPropertyAssignment(groupProperty)) {\n const initializer = groupProperty.getInitializer();\n if (initializer && Node.isStringLiteral(initializer)) {\n group = initializer.getLiteralValue();\n }\n }\n }\n }\n\n return {\n id: `${group}.${baseComponentId}`,\n group,\n };\n}\n\n/**\n * Checks if a source file has a specific named export using ts-morph AST parsing\n */\nexport function hasNamedExport(sourceFile: SourceFile, exportName: string): boolean {\n // Check for function declarations: export function exportName(...)\n const functionDeclarations = sourceFile\n .getFunctions()\n .filter((func: FunctionDeclaration) => func.hasExportKeyword() && func.getName() === exportName);\n\n if (functionDeclarations.length > 0) {\n return true;\n }\n\n // Check for variable declarations: export const exportName = ...\n const variableStatements = sourceFile\n .getVariableStatements()\n .filter((stmt: VariableStatement) => stmt.hasExportKeyword());\n\n for (const stmt of variableStatements) {\n const declarations = stmt.getDeclarations();\n for (const decl of declarations) {\n if (decl.getName() === exportName) {\n return true;\n }\n }\n }\n\n // Check for export assignments: export { exportName } or export { localName as exportName }\n const exportDeclarations = sourceFile.getExportDeclarations();\n for (const exportDecl of exportDeclarations) {\n const namedExports = exportDecl.getNamedExports();\n for (const namedExport of namedExports) {\n // Check both the local name and the alias (if any)\n const localName = namedExport.getName();\n const aliasName = namedExport.getAliasNode()?.getText();\n\n if (localName === exportName || aliasName === exportName) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Checks if a source file has a fallback export (including default exports with 'fallback' in name)\n */\nexport function hasFallbackExport(sourceFile: SourceFile): boolean {\n // Check for named export 'fallback'\n if (hasNamedExport(sourceFile, 'fallback')) {\n return true;\n }\n\n // Check for default function declarations with 'fallback' in name\n const functions = sourceFile\n .getFunctions()\n .filter((func: FunctionDeclaration) => func.hasExportKeyword() && func.hasDefaultKeyword());\n\n for (const func of functions) {\n const name = func.getName();\n if (name && name.toLowerCase().includes('fallback')) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Scans all files in the component directory for @Component decorators and extracts metadata using ts-morph\n */\nexport async function scanComponents(\n project: Project,\n projectRoot: string,\n componentPath: string,\n registryPath: string,\n verbose: boolean\n): Promise<ComponentInfo[]> {\n // Scan all TypeScript/TSX files in the component directory recursively\n const componentPattern = `${componentPath}/**/*.{ts,tsx}`;\n const componentFiles = await glob(componentPattern, {\n cwd: projectRoot,\n absolute: true,\n });\n\n if (verbose) {\n console.log(`🔍 Scanning ${componentFiles.length} files in ${componentPath}...`);\n }\n\n const components: ComponentInfo[] = [];\n const registryDir = dirname(resolve(projectRoot, registryPath));\n\n for (const filePath of componentFiles) {\n try {\n // Read file content and create source file in ts-morph project\n const content = readFileSync(filePath, 'utf-8');\n const sourceFile = project.createSourceFile(filePath, content, { overwrite: true });\n\n // Find all classes with @Component decorator\n const classes = sourceFile.getClasses();\n\n for (const classDeclaration of classes) {\n const decorators = classDeclaration.getDecorators();\n\n for (const decorator of decorators) {\n const decoratorName = decorator.getName();\n\n if (decoratorName === 'Component') {\n const componentInfo = extractComponentInfo(decorator);\n\n if (componentInfo) {\n // Calculate relative path from registry.ts to component\n let relativePath = relative(registryDir, filePath)\n .replace(/\\\\/g, '/') // Normalize Windows paths\n .replace(/\\.(ts|tsx)$/, ''); // Remove extension\n\n // Ensure relative path starts with './' or '../'\n if (!relativePath.startsWith('.')) {\n relativePath = `./${relativePath}`;\n }\n\n // Check for React Router style loader exports using AST parsing\n const hasLoaderExport = hasNamedExport(sourceFile, 'loader');\n const hasClientLoaderExport = hasNamedExport(sourceFile, 'clientLoader');\n const hasFallback = hasFallbackExport(sourceFile);\n\n components.push({\n id: componentInfo.id,\n filePath,\n relativePath,\n hasLoader: hasLoaderExport,\n hasClientLoader: hasClientLoaderExport,\n hasFallback,\n });\n\n if (verbose) {\n const exports = [];\n if (hasLoaderExport) {\n exports.push('loader');\n }\n if (hasClientLoaderExport) {\n exports.push('clientLoader');\n }\n if (hasFallback) {\n exports.push('fallback');\n }\n const exportsText = exports.length > 0 ? ` (with ${exports.join(', ')})` : '';\n console.log(\n ` ✅ Found component: ${componentInfo.id} → ${relativePath}${exportsText}`\n );\n }\n }\n }\n }\n }\n } catch (error) {\n if (verbose) {\n console.warn(`⚠️ Could not process ${filePath}:`, (error as Error).message);\n }\n // Continue processing other files even if one fails\n }\n }\n\n return components;\n}\n\n/**\n * Generates the initializeRegistry function code\n */\nexport function generateRegistryCode(components: ComponentInfo[], registryIdentifier: string = 'registry'): string {\n // Ensure deterministic output: sort by component id, then by relativePath as a stable tiebreaker\n const sorted = [...components].sort(\n (a, b) => a.id.localeCompare(b.id) || a.relativePath.localeCompare(b.relativePath)\n );\n\n if (sorted.length === 0) {\n return `\n/* eslint-disable */\n/**\n * Initialize the static component registry.\n * This function is auto-generated by the staticRegistry Vite plugin.\n * \n * DO NOT EDIT THIS FUNCTION MANUALLY - it will be overwritten on next build.\n */\nexport function initializeRegistry(targetRegistry = ${registryIdentifier}): void {\n // No components found with @Component decorators\n}\n`;\n }\n\n const registrations = sorted\n .map(({ id, relativePath, hasLoader, hasClientLoader, hasFallback }) => {\n if (hasLoader || hasClientLoader || hasFallback) {\n // Register with metadata - tell registry the function/component names\n const metadata = [];\n if (hasLoader) {\n metadata.push(`loader: 'loader'`);\n }\n if (hasClientLoader) {\n metadata.push(`clientLoader: 'clientLoader'`);\n }\n if (hasFallback) {\n metadata.push(`fallback: 'fallback'`);\n }\n\n return ` targetRegistry.registerImporter('${id}', () => import('${relativePath}'), { ${metadata.join(', ')} });`;\n } else {\n return ` targetRegistry.registerImporter('${id}', () => import('${relativePath}'));`;\n }\n })\n .join('\\n');\n\n return `\n/* eslint-disable */\n/**\n * Initialize the static component registry.\n * This function is auto-generated by the staticRegistry Vite plugin.\n * \n * DO NOT EDIT THIS FUNCTION MANUALLY - it will be overwritten on next build.\n * \n * Components registered: ${sorted.map((c) => c.id).join(', ')}\n */\nexport function initializeRegistry(targetRegistry = ${registryIdentifier}): void {\n${registrations}\n}\n`;\n}\n\n/**\n * Updates the registry.ts file with the generated code\n */\nexport function updateRegistryFile(registryFilePath: string, generatedCode: string, verbose: boolean): void {\n let existingContent: string;\n\n // Check if file exists, if not create a basic one\n if (!existsSync(registryFilePath)) {\n if (verbose) {\n console.log(`📝 Creating new registry file...`);\n }\n\n // Create a basic registry file\n const basicRegistryContent = `import { ComponentRegistry } from '@/lib/component-registry';\n\n// Create the component registry instance\nexport const registry = new ComponentRegistry();\n\n// STATIC_REGISTRY_START\n// Generated content will be inserted here by the static registry plugin\n// STATIC_REGISTRY_END\n`;\n writeFileSync(registryFilePath, basicRegistryContent, 'utf-8');\n existingContent = basicRegistryContent;\n } else {\n try {\n existingContent = readFileSync(registryFilePath, 'utf-8');\n } catch (error) {\n throw new Error(`Failed to read registry file: ${(error as Error).message}`);\n }\n }\n\n // Use explicit markers for generated content\n const startMarker = '// STATIC_REGISTRY_START';\n const endMarker = '// STATIC_REGISTRY_END';\n\n const startIndex = existingContent.indexOf(startMarker);\n const endIndex = existingContent.indexOf(endMarker);\n\n if (startIndex === -1 || endIndex === -1) {\n throw new Error(\n `Registry file ${registryFilePath} is missing static registry markers. ` +\n `Please add \"${startMarker}\" and \"${endMarker}\" markers to define the generated content area.`\n );\n }\n\n const before = existingContent.slice(0, startIndex + startMarker.length);\n const after = existingContent.slice(endIndex);\n\n const updatedContent = `${before}\\n${generatedCode}\\n${after}`;\n\n try {\n writeFileSync(registryFilePath, updatedContent, 'utf-8');\n if (verbose) {\n console.log(`💾 Updated registry file: ${registryFilePath}`);\n }\n } catch (error) {\n throw new Error(`Failed to write registry file: ${(error as Error).message}`);\n }\n}\n\n/**\n * Vite plugin that generates static component registry based on @Component decorators.\n *\n * This plugin scans component files for @Component decorators and automatically generates\n * a static registry function that pre-registers all components with their import paths.\n * This eliminates the need for manual component registration and provides build-time\n * optimization for component discovery.\n *\n * @param config - Configuration options for the plugin\n * @returns A Vite plugin that generates static component registrations\n *\n * @example\n * // In vite.config.ts\n * export default defineConfig({\n * plugins: [\n * staticRegistryPlugin({\n * componentPath: 'src/components',\n * registryPath: 'src/lib/registry.ts',\n * verbose: true\n * })\n * ]\n * })\n */\nexport const staticRegistryPlugin = (config: StaticRegistryPluginConfig = {}): Plugin => {\n const {\n componentPath = 'src/components',\n registryPath = 'src/lib/static-registry.ts',\n registryIdentifier = 'registry',\n failOnError = true,\n verbose = false,\n } = config;\n\n let projectRoot: string;\n\n const runRegistryGeneration = async () => {\n if (verbose) {\n console.log('🚀 Starting static registry generation...');\n }\n\n // Create a fresh Project for this run only\n const project = new Project({\n compilerOptions: {\n target: ts.ScriptTarget.Latest,\n module: ts.ModuleKind.ESNext,\n jsx: ts.JsxEmit.ReactJSX,\n allowJs: true,\n skipLibCheck: true,\n noEmit: true,\n },\n });\n\n // Build AST, extract plain data\n const components = await scanComponents(project, projectRoot, componentPath, registryPath, verbose);\n\n // From here on we do not need the AST any more.\n // `components` is just an array of plain objects.\n // `project` will fall out of scope after this function returns and can be GC'd.\n\n if (verbose) {\n console.log(`📦 Found ${components.length} components with @Component decorators`);\n }\n\n const generatedCode = generateRegistryCode(components, registryIdentifier);\n const registryFilePath = resolve(projectRoot, registryPath);\n updateRegistryFile(registryFilePath, generatedCode, verbose);\n\n if (verbose) {\n console.log('✅ Static registry generation complete!');\n }\n\n return registryFilePath;\n };\n\n return {\n name: 'storefrontnext:static-registry',\n\n configResolved(resolvedConfig) {\n projectRoot = resolvedConfig.root;\n },\n\n async buildStart() {\n try {\n await runRegistryGeneration();\n } catch (error) {\n console.error(`❌ Static registry generation failed: ${(error as Error).message}`);\n if (failOnError) {\n throw error;\n }\n console.warn('⚠️ Continuing build without static registry...');\n }\n },\n\n async handleHotUpdate({ file, server }) {\n const normalizedComponentPath = componentPath.replace(/\\\\/g, '/');\n const normalizedFile = file.replace(/\\\\/g, '/');\n\n if (\n normalizedFile.includes(`/${normalizedComponentPath}/`) &&\n (normalizedFile.endsWith('.ts') || normalizedFile.endsWith('.tsx'))\n ) {\n if (verbose) {\n console.log(`🔄 Component file changed: ${file}, regenerating registry...`);\n }\n\n try {\n const registryFilePath = await runRegistryGeneration();\n\n const registryModule = server.moduleGraph.getModuleById(registryFilePath);\n if (registryModule) {\n await server.reloadModule(registryModule);\n }\n\n if (verbose) {\n console.log('✅ Registry regenerated successfully!');\n }\n } catch (error) {\n console.error(`❌ Failed to regenerate registry: ${(error as Error).message}`);\n }\n\n return [];\n }\n },\n };\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { resolve } from 'path';\nimport { pathToFileURL } from 'url';\n\n/**\n * Adapter configuration with event toggles\n * Event toggles are a dynamic map - any event name can be toggled\n */\nexport interface AdapterConfig {\n enabled?: boolean;\n eventToggles?: Record<string, boolean>;\n}\n\n/**\n * Expected structure of engagement config from config.server.ts\n */\nexport interface EngagementConfig {\n adapters?: Record<string, AdapterConfig>;\n}\n\n/**\n * Load the engagement config from config.server.ts\n */\nexport async function loadEngagementConfig(\n projectRoot: string,\n configPath: string,\n verbose: boolean\n): Promise<EngagementConfig | null> {\n const absoluteConfigPath = resolve(projectRoot, configPath);\n\n try {\n // Use dynamic import with file URL for ESM compatibility\n const configUrl = pathToFileURL(absoluteConfigPath).href;\n const configModule = await import(configUrl);\n const config = configModule.default;\n\n if (verbose) {\n // eslint-disable-next-line no-console\n console.log(` 📄 Loaded config from ${configPath}`);\n }\n\n // Navigate to engagement config\n const engagement = config?.app?.engagement as EngagementConfig | undefined;\n\n if (!engagement) {\n if (verbose) {\n // eslint-disable-next-line no-console\n console.log(` ⚠️ No engagement config found in ${configPath}`);\n }\n return null;\n }\n\n return engagement;\n } catch (error) {\n // Config might not exist or have import errors - this is non-fatal\n if (verbose) {\n // eslint-disable-next-line no-console\n console.warn(` ⚠️ Could not load config from ${configPath}: ${(error as Error).message}`);\n }\n return null;\n }\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { Plugin, ResolvedConfig } from 'vite';\nimport { glob } from 'glob';\nimport { readFileSync } from 'fs';\nimport { resolve, join } from 'path';\nimport { loadEngagementConfig, type EngagementConfig } from './configLoader';\n\n/**\n * Configuration options for the event instrumentation validator plugin\n */\nexport interface EventInstrumentationValidatorConfig {\n /**\n * Path to config module relative to project root\n * @default 'config.server.ts'\n */\n configPath?: string;\n\n /**\n * Directories to scan for trackEvent calls relative to project root\n * @default ['src']\n */\n scanPaths?: string[];\n\n /**\n * Whether to fail the build on missing instrumentation\n * @default false (warning only)\n */\n failOnMissing?: boolean;\n\n /**\n * Enable verbose logging\n * @default false\n */\n verbose?: boolean;\n}\n\n/**\n * Extract all trackEvent calls from source files and return the event types found\n */\nasync function scanForInstrumentedEvents(\n projectRoot: string,\n scanPaths: string[],\n verbose: boolean\n): Promise<Set<string>> {\n const instrumentedEvents = new Set<string>();\n\n // Regex patterns to match trackEvent calls\n // Pattern 1: trackEvent(..., ..., ..., 'event_type', ...)\n // The event type is the 4th argument\n const trackEventPattern = /trackEvent\\s*\\([^,]+,[^,]+,[^,]+,\\s*['\"]([^'\"]+)['\"]/g;\n\n // Pattern 2: sendViewPageEvent (special case for view_page)\n const sendViewPagePattern = /sendViewPageEvent\\s*\\(/g;\n\n // Pattern 3: createEvent('event_type', ...) - backup pattern\n const createEventPattern = /createEvent\\s*\\(\\s*['\"]([^'\"]+)['\"]/g;\n\n for (const scanPath of scanPaths) {\n const absoluteScanPath = resolve(projectRoot, scanPath);\n const pattern = join(absoluteScanPath, '**/*.{ts,tsx}');\n\n const files = await glob(pattern, {\n ignore: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx', '**/node_modules/**'],\n });\n\n if (verbose) {\n console.log(` 📂 Scanning ${files.length} files in ${scanPath}...`);\n }\n\n for (const file of files) {\n try {\n const content = readFileSync(file, 'utf-8');\n\n // Find trackEvent calls\n let match;\n while ((match = trackEventPattern.exec(content)) !== null) {\n const eventType = match[1];\n instrumentedEvents.add(eventType);\n if (verbose) {\n console.log(` ✓ Found trackEvent('${eventType}') in ${file}`);\n }\n }\n\n // Check for sendViewPageEvent (implies view_page is instrumented)\n if (sendViewPagePattern.test(content)) {\n instrumentedEvents.add('view_page');\n if (verbose) {\n console.log(` ✓ Found sendViewPageEvent() in ${file}`);\n }\n }\n\n // Find createEvent calls as backup\n while ((match = createEventPattern.exec(content)) !== null) {\n const eventType = match[1];\n instrumentedEvents.add(eventType);\n if (verbose) {\n console.log(` ✓ Found createEvent('${eventType}') in ${file}`);\n }\n }\n\n // Reset regex lastIndex for next file\n trackEventPattern.lastIndex = 0;\n sendViewPagePattern.lastIndex = 0;\n createEventPattern.lastIndex = 0;\n } catch (error) {\n if (verbose) {\n console.warn(` ⚠️ Could not read ${file}: ${(error as Error).message}`);\n }\n }\n }\n }\n\n return instrumentedEvents;\n}\n\n/**\n * Extract enabled event toggles per adapter\n * Dynamically iterates over all keys in eventToggles - supports custom event types\n */\nfunction extractEnabledEvents(engagement: EngagementConfig): Map<string, Set<string>> {\n const adapterEvents = new Map<string, Set<string>>();\n\n if (!engagement.adapters) {\n return adapterEvents;\n }\n\n for (const [adapterName, adapterConfig] of Object.entries(engagement.adapters)) {\n // Skip disabled adapters\n if (!adapterConfig.enabled) {\n continue;\n }\n\n const enabledEvents = new Set<string>();\n\n if (adapterConfig.eventToggles) {\n // Dynamically iterate over all keys in eventToggles\n for (const [eventType, isEnabled] of Object.entries(adapterConfig.eventToggles)) {\n if (isEnabled === true) {\n enabledEvents.add(eventType);\n }\n }\n }\n\n if (enabledEvents.size > 0) {\n adapterEvents.set(adapterName, enabledEvents);\n }\n }\n\n return adapterEvents;\n}\n\n/**\n * Vite plugin that validates event instrumentation at build time.\n *\n * This plugin scans source files for trackEvent() calls and validates that\n * all enabled event toggles in config.server.ts have corresponding instrumentation.\n *\n * @param config - Configuration options for the plugin\n * @returns A Vite plugin that validates event instrumentation\n *\n * @example\n * // In vite.config.ts\n * export default defineConfig({\n * plugins: [\n * eventInstrumentationValidatorPlugin({\n * configPath: 'config.server.ts',\n * scanPaths: ['src'],\n * verbose: true\n * })\n * ]\n * })\n */\nexport const eventInstrumentationValidatorPlugin = (config: EventInstrumentationValidatorConfig = {}): Plugin => {\n const { configPath = 'config.server.ts', scanPaths = ['src'], failOnMissing = false, verbose = false } = config;\n\n let resolvedConfig: ResolvedConfig;\n\n return {\n name: 'storefrontnext:event-instrumentation-validator',\n apply: 'build',\n\n configResolved(viteConfig) {\n resolvedConfig = viteConfig;\n },\n\n async buildStart() {\n const projectRoot = resolvedConfig.root;\n\n if (verbose) {\n console.log('\\n🔍 [event-instrumentation] Validating event instrumentation...');\n }\n\n // Load engagement config\n const engagement = await loadEngagementConfig(projectRoot, configPath, verbose);\n\n if (!engagement) {\n if (verbose) {\n console.log(' ℹ️ Skipping validation - no engagement config found\\n');\n }\n return;\n }\n\n // Extract enabled events per adapter\n const adapterEvents = extractEnabledEvents(engagement);\n\n if (adapterEvents.size === 0) {\n if (verbose) {\n console.log(' ℹ️ No enabled adapters with event toggles found\\n');\n }\n return;\n }\n\n // Scan source files for instrumented events\n const instrumentedEvents = await scanForInstrumentedEvents(projectRoot, scanPaths, verbose);\n\n if (verbose) {\n console.log(`\\n 🔎 Found ${instrumentedEvents.size} instrumented event types:`);\n for (const event of instrumentedEvents) {\n console.log(` - ${event}`);\n }\n }\n\n // Validate each adapter's enabled events\n const missingInstrumentation: Array<{ adapter: string; event: string }> = [];\n\n for (const [adapterName, enabledEvents] of adapterEvents) {\n for (const eventType of enabledEvents) {\n if (!instrumentedEvents.has(eventType)) {\n missingInstrumentation.push({\n adapter: adapterName,\n event: eventType,\n });\n }\n }\n }\n\n // Report results\n if (missingInstrumentation.length === 0) {\n if (verbose) {\n console.log('\\n ✅ All enabled events are instrumented\\n');\n }\n return;\n }\n\n // Report missing instrumentation\n console.log('\\n');\n for (const { adapter, event } of missingInstrumentation) {\n console.warn(\n ` ⚠️ [event-instrumentation] ${adapter}.${event} is enabled but '${event}' is never instrumented`\n );\n }\n console.log('\\n');\n\n if (failOnMissing) {\n throw new Error(\n `[event-instrumentation] ${missingInstrumentation.length} event(s) are enabled but not instrumented. ` +\n `Either add instrumentation or disable the event toggles in config.server.ts.`\n );\n }\n },\n };\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { Plugin } from 'vite';\nimport { fixReactRouterManifestUrlsPlugin } from './plugins/fixReactRouterManifestUrls';\nimport { readableChunkFileNamesPlugin } from './plugins/readableChunkFileNames';\nimport { managedRuntimeBundlePlugin } from './plugins/managedRuntimeBundle';\nimport { patchReactRouterPlugin } from './plugins/patchReactRouter';\nimport { transformPluginPlaceholderPlugin } from './plugins/transformPlugins';\nimport { watchConfigFilesPlugin } from './plugins/watchConfigFiles';\nimport { staticRegistryPlugin, type StaticRegistryPluginConfig } from './plugins/staticRegistry';\nimport {\n eventInstrumentationValidatorPlugin,\n type EventInstrumentationValidatorConfig,\n} from './plugins/eventInstrumentationValidator';\n\n/**\n * Configuration options for the Storefront Next Vite plugin.\n */\nexport interface StorefrontNextPluginsConfig {\n /**\n * Enable human-readable chunk file names for easier debugging in production builds.\n * When enabled, chunk files will be named based on their source location\n * rather than just the file name and random hashes.\n *\n * This is useful to identify the chunk files and is usually used in development,\n * in conjunction with the bundle analyzer.\n *\n * Example:\n *\n * ```\n * (package)-(pkg-name)-index.[hash].js\n * (components)-(ui)-(inputs)-(TextField)-index.[hash].js\n * ```\n *\n * Instead of:\n *\n * ```\n * index.[hash].js\n * ```\n *\n * @default false\n */\n readableChunkNames?: boolean;\n\n /**\n * Configuration for the static registry plugin that automatically generates\n * component registrations based on @Component decorators.\n *\n * Set to `false` to disable the static registry plugin entirely.\n *\n * @default { componentPath: 'src/components', registryPath: 'src/lib/registry.ts' }\n */\n staticRegistry?: StaticRegistryPluginConfig;\n\n /**\n * Configuration for the event instrumentation validator plugin that validates\n * all enabled analytics event toggles have corresponding trackEvent() calls.\n *\n * Set to `false` to disable the validator entirely.\n *\n * @default { configPath: 'config.server.ts', scanPaths: ['src'], failOnMissing: false }\n */\n eventInstrumentationValidator?: EventInstrumentationValidatorConfig | false;\n}\n\n/**\n * Storefront Next Vite plugin that powers the React Router RSC app.\n * Supports building and optimizing for the managed runtime environment.\n *\n * @param config - Configuration options for the plugin\n * @returns {Plugin[]} An array of Vite plugins for Storefront Next functionality\n *\n * @example\n * // With default options\n * export default defineConfig({\n * plugins: [storefrontNextPlugins()]\n * })\n *\n * @example\n * // Disable readable chunk names\n * export default defineConfig({\n * plugins: [storefrontNextPlugins({ readableChunkNames: false })]\n * })\n */\nexport function storefrontNextPlugins(config: StorefrontNextPluginsConfig = {}): Plugin[] {\n const {\n readableChunkNames = false,\n staticRegistry = {\n componentPath: '',\n registryPath: '',\n verbose: false,\n },\n eventInstrumentationValidator = {\n configPath: 'config.server.ts',\n scanPaths: ['src'],\n failOnMissing: false,\n verbose: false,\n },\n } = config;\n\n const plugins: Plugin[] = [\n managedRuntimeBundlePlugin(),\n fixReactRouterManifestUrlsPlugin(),\n patchReactRouterPlugin(),\n transformPluginPlaceholderPlugin(),\n watchConfigFilesPlugin(),\n ];\n\n // Add static registry plugin if enabled\n if (staticRegistry?.componentPath && staticRegistry?.registryPath) {\n plugins.push(staticRegistryPlugin(staticRegistry));\n }\n\n // Add event instrumentation validator plugin if not explicitly disabled\n if (eventInstrumentationValidator !== false) {\n plugins.push(eventInstrumentationValidatorPlugin(eventInstrumentationValidator));\n }\n\n if (readableChunkNames) {\n plugins.push(readableChunkFileNamesPlugin());\n }\n\n return plugins;\n}\n","{\n \"name\": \"@salesforce/storefront-next-dev\",\n \"version\": \"0.1.0\",\n \"description\": \"Dev and build tools for SFCC Storefront Next\",\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"import\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n }\n },\n \"./react-router-preset\": {\n \"import\": {\n \"types\": \"./dist/configs/react-router.config.d.ts\",\n \"default\": \"./dist/configs/react-router.config.js\"\n }\n },\n \"./react-router/Scripts\": {\n \"import\": {\n \"types\": \"./dist/react-router/Scripts.d.ts\",\n \"default\": \"./dist/react-router/Scripts.js\"\n }\n },\n \"./mrt/streamingHandler\": {\n \"import\": {\n \"types\": \"./dist/mrt/streamingHandler.d.ts\",\n \"default\": \"./dist/mrt/streamingHandler.mjs\"\n }\n },\n \"./mrt/ssr\": {\n \"import\": {\n \"types\": \"./dist/mrt/ssr.d.ts\",\n \"default\": \"./dist/mrt/ssr.mjs\"\n }\n },\n \"./cartridge-services\": {\n \"import\": {\n \"types\": \"./dist/cartridge-services/index.d.ts\",\n \"default\": \"./dist/cartridge-services/index.js\"\n }\n }\n },\n \"bin\": {\n \"sfnext\": \"./dist/cli.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"prepare\": \"pnpm build\",\n \"build\": \"tsdown\",\n \"dev\": \"tsdown --watch\",\n \"test\": \"pnpm typecheck && vitest --run --coverage\",\n \"typecheck\": \"tsc --noEmit\",\n \"lint\": \"eslint src --ext .ts,.tsx\",\n \"lint:fix\": \"eslint src --ext .ts,.tsx --fix\",\n \"clean\": \"shx rm -rf dist\",\n \"push\": \"node dist/cli.js push\",\n \"create-bundle\": \"node dist/cli.js create-bundle\",\n \"generate-cartridge\": \"node dist/cli.js generate-cartridge\",\n \"deploy-cartridge\": \"node dist/cli.js deploy-cartridge\"\n },\n \"keywords\": [\n \"vite\",\n \"plugin\",\n \"salesforce\",\n \"storefront-next\",\n \"commerce-cloud\",\n \"pwa-kit\",\n \"deployment\",\n \"bundle\"\n ],\n \"author\": \"cc-pwa-kit@salesforce.com\",\n \"license\": \"Apache-2.0\",\n \"peerDependencies\": {\n \"@react-router/dev\": \">=7.0.0\",\n \"react\": \">=19.0.0\",\n \"react-dom\": \">=19.0.0\",\n \"react-router\": \">=7.0.0\",\n \"vite\": \">=7.0.0\"\n },\n \"devDependencies\": {\n \"@react-router/dev\": \"7.12.0\",\n \"@serverless/event-mocks\": \"1.1.1\",\n \"@types/archiver\": \"^6.0.2\",\n \"@types/aws-lambda\": \"8.10.159\",\n \"@types/babel__generator\": \"7.27.0\",\n \"@types/babel__traverse\": \"7.28.0\",\n \"@types/compressible\": \"2.0.3\",\n \"@types/compression\": \"^1.7.5\",\n \"@types/express\": \"4.17.23\",\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/glob\": \"^8.1.0\",\n \"@types/handlebars\": \"^4.1.0\",\n \"@types/jest\": \"30.0.0\",\n \"@types/minimatch\": \"^5.1.2\",\n \"@types/morgan\": \"^1.9.9\",\n \"@types/negotiator\": \"0.6.4\",\n \"@types/node\": \"^24.0.0\",\n \"@types/prompts\": \"2.4.9\",\n \"@types/react\": \"19.1.13\",\n \"@types/react-dom\": \"19.1.9\",\n \"@types/semver\": \"7.7.1\",\n \"@vitest/coverage-v8\": \"4.0.18\",\n \"fs-extra\": \"11.3.2\",\n \"jest\": \"^29.7.0\",\n \"memfs\": \"^4.42.0\",\n \"react\": \"19.2.3\",\n \"react-dom\": \"19.2.3\",\n \"react-router\": \"7.12.0\",\n \"shx\": \"^0.4.0\",\n \"ts-jest\": \"29.4.1\",\n \"tsdown\": \"^0.15.4\",\n \"typescript\": \"^5.6.0\",\n \"vite\": \"7.1.7\",\n \"vitest\": \"4.0.18\"\n },\n \"dependencies\": {\n \"@babel/parser\": \"^7.28.4\",\n \"@babel/traverse\": \"^7.28.4\",\n \"@babel/types\": \"7.28.4\",\n \"@babel/generator\": \"7.28.5\",\n \"@codegenie/serverless-express\": \"4.17.0\",\n \"@h4ad/serverless-adapter\": \"4.4.0\",\n \"@react-router/express\": \"7.12.0\",\n \"archiver\": \"^7.0.1\",\n \"chalk\": \"^5.3.0\",\n \"commander\": \"^12.1.0\",\n \"compressible\": \"2.0.18\",\n \"compression\": \"^1.7.4\",\n \"dotenv\": \"^16.4.7\",\n \"express\": \"4.21.2\",\n \"fs-extra\": \"^11.2.0\",\n \"glob\": \"^11.0.0\",\n \"handlebars\": \"4.7.8\",\n \"http-proxy-middleware\": \"^3.0.0\",\n \"jiti\": \"^2.6.1\",\n \"minimatch\": \"^10.0.1\",\n \"morgan\": \"^1.10.0\",\n \"negotiator\": \"1.0.0\",\n \"npm-run-path\": \"6.0.0\",\n \"prompts\": \"2.4.2\",\n \"ts-morph\": \"^24.0.0\",\n \"tsx\": \"^4.20.5\",\n \"undici\": \"^6.21.3\",\n \"zod\": \"4.1.13\"\n },\n \"engines\": {\n \"node\": \">=24.0.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* eslint-disable no-console */\nimport os from 'os';\nimport chalk from 'chalk';\nimport { createRequire } from 'module';\nimport type { ServerMode } from '../server/modes';\nimport pkg from '../../package.json' with { type: 'json' };\n\n/**\n * Get the local network IPv4 address\n */\nexport function getNetworkAddress(): string | undefined {\n const interfaces = os.networkInterfaces();\n for (const name of Object.keys(interfaces)) {\n const iface = interfaces[name];\n if (!iface) continue;\n for (const alias of iface) {\n if (alias.family === 'IPv4' && !alias.internal) {\n return alias.address;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Get the version of a package from the project's package.json\n */\nexport function getPackageVersion(packageName: string, projectDir: string): string {\n try {\n const require = createRequire(import.meta.url);\n const pkgPath = require.resolve(`${packageName}/package.json`, { paths: [projectDir] });\n const pkgJson = require(pkgPath) as { version: string };\n return pkgJson.version;\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Logger utilities\n */\nconst colors = {\n warn: 'yellow',\n error: 'red',\n success: 'cyan',\n info: 'green',\n debug: 'gray',\n} as const;\n\nconst fancyLog = (level: keyof typeof colors, msg: string) => {\n const color = colors[level];\n const colorFn = chalk[color];\n console.log(`${colorFn(level)}: ${msg}`);\n};\n\nexport const info = (msg: string) => fancyLog('info', msg);\nexport const success = (msg: string) => fancyLog('success', msg);\nexport const warn = (msg: string) => fancyLog('warn', msg);\nexport const error = (msg: string) => fancyLog('error', msg);\n\nexport const debug = (msg: string, data?: unknown) => {\n // Only log debug messages if DEBUG environment variable is set or not in production\n if (process.env.DEBUG || process.env.NODE_ENV !== 'production') {\n fancyLog('debug', msg);\n if (data) {\n console.log(data);\n }\n }\n};\n\n/**\n * Print the server information banner with URLs and versions\n */\nexport function printServerInfo(mode: ServerMode, port: number, startTime: number, projectDir: string): void {\n const elapsed = Date.now() - startTime;\n const sfnextVersion = pkg.version;\n const reactVersion = getPackageVersion('react', projectDir);\n const reactRouterVersion = getPackageVersion('react-router', projectDir);\n\n const modeLabel = mode === 'development' ? 'Development Mode' : 'Preview Mode';\n\n console.log();\n console.log(` ${chalk.cyan.bold('⚡ SFCC Storefront Next')} ${chalk.dim(`v${sfnextVersion}`)}`);\n console.log(` ${chalk.green.bold(modeLabel)}`);\n console.log();\n console.log(\n ` ${chalk.dim('react')} ${chalk.green(`v${reactVersion}`)} ${chalk.dim('│')} ` +\n `${chalk.dim('react-router')} ${chalk.green(`v${reactRouterVersion}`)} ${chalk.dim('│')} ` +\n `${chalk.green(`ready in ${elapsed}ms`)}`\n );\n console.log();\n}\n\n/**\n * Print server configuration details (proxy, static, etc.)\n */\nexport function printServerConfig(config: {\n mode: ServerMode;\n port: number;\n enableProxy?: boolean;\n enableStaticServing?: boolean;\n enableCompression?: boolean;\n proxyPath?: string;\n proxyTarget?: string;\n shortCode?: string;\n organizationId?: string;\n clientId?: string;\n siteId?: string;\n}): void {\n const {\n port,\n enableProxy,\n enableStaticServing,\n enableCompression,\n proxyPath,\n proxyTarget,\n shortCode,\n organizationId,\n clientId,\n siteId,\n } = config;\n\n console.log(` ${chalk.bold('Environment Configuration:')}`);\n\n if (enableProxy && proxyPath && proxyTarget && shortCode) {\n console.log(\n ` ${chalk.green('✓')} ${chalk.bold('Proxy:')} ${chalk.cyan(`localhost:${port}${proxyPath}`)} ${chalk.dim('→')} ${chalk.cyan(proxyTarget)}`\n );\n console.log(` ${chalk.dim('Short Code: ')} ${chalk.dim(shortCode)}`);\n if (organizationId) {\n console.log(` ${chalk.dim('Organization ID:')} ${chalk.dim(organizationId)}`);\n }\n if (clientId) {\n console.log(` ${chalk.dim('Client ID: ')} ${chalk.dim(clientId)}`);\n }\n if (siteId) {\n console.log(` ${chalk.dim('Site ID: ')} ${chalk.dim(siteId)}`);\n }\n } else {\n console.log(` ${chalk.gray('○')} ${chalk.bold('Proxy: ')} ${chalk.dim('disabled')}`);\n }\n\n if (enableStaticServing) {\n console.log(` ${chalk.green('✓')} ${chalk.bold('Static: ')} ${chalk.dim('enabled')}`);\n }\n\n if (enableCompression) {\n console.log(` ${chalk.green('✓')} ${chalk.bold('Compression: ')} ${chalk.dim('enabled')}`);\n }\n\n // URLs\n const localUrl = `http://localhost:${port}`;\n const networkAddress = getNetworkAddress();\n const networkUrl = networkAddress ? `http://${networkAddress}:${port}` : null;\n\n console.log();\n console.log(` ${chalk.green('➜')} ${chalk.bold('Local: ')} ${chalk.cyan(localUrl)}`);\n if (networkUrl) {\n console.log(` ${chalk.green('➜')} ${chalk.bold('Network:')} ${chalk.cyan(networkUrl)}`);\n }\n\n console.log();\n console.log(` ${chalk.dim('Press')} ${chalk.bold('Ctrl+C')} ${chalk.dim('to stop the server')}`);\n console.log();\n}\n\n/**\n * Print shutdown message\n */\nexport function printShutdownMessage(): void {\n console.log(`\\n ${chalk.yellow('⚡')} ${chalk.dim('Server shutting down...')}\\n`);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport os from 'os';\nimport path from 'path';\nimport fs from 'fs-extra';\nimport { execSync } from 'child_process';\nimport dotenv from 'dotenv';\nimport { warn, debug } from './utils/logger';\nimport type { Credentials, ProjectPackage, DependencyTree, DependencyRecord } from './types';\n\n// Configuration\nexport const DEFAULT_CLOUD_ORIGIN = 'https://cloud.mobify.com';\n\nexport const getDefaultBuildDir = (targetDir: string) => path.join(targetDir, 'build');\n\n// Set default NODE_ENV if not specified\nconst NODE_ENV = process.env.NODE_ENV || 'development';\n\n/**\n * Get current environment with default fallback\n */\nexport const getEnvironment = () => NODE_ENV;\n\n/**\n * Check if running in production mode\n */\nexport const isProduction = () => NODE_ENV === 'production';\n\n/**\n * Check if running in development mode\n */\nexport const isDevelopment = () => NODE_ENV === 'development';\n\n/**\n * Get credentials file path based on cloud origin\n */\nexport const getCredentialsFile = (cloudOrigin: string, credentialsFile?: string): string => {\n if (credentialsFile) {\n return credentialsFile;\n }\n\n const url = new URL(cloudOrigin);\n const host = url.host;\n const suffix = host === 'cloud.mobify.com' ? '' : `--${host}`;\n return path.join(os.homedir(), `.mobify${suffix}`);\n};\n\n/**\n * Read credentials from file\n */\nexport const readCredentials = async (filepath: string): Promise<Credentials> => {\n try {\n const data = await fs.readJSON(filepath);\n return {\n username: data.username,\n api_key: data.api_key,\n };\n } catch {\n throw new Error(\n `Credentials file \"${filepath}\" not found.\\n` +\n 'Visit https://runtime.commercecloud.com/account/settings for ' +\n 'steps on authorizing your computer to push bundles.'\n );\n }\n};\n\n/**\n * Get project package.json\n */\nexport const getProjectPkg = (projectDir: string): ProjectPackage => {\n const packagePath = path.join(projectDir, 'package.json');\n try {\n return fs.readJSONSync(packagePath);\n } catch {\n throw new Error(`Could not read project package at \"${packagePath}\"`);\n }\n};\n\n/**\n * Load .env file from project directory\n */\nexport const loadEnvFile = (projectDir: string): void => {\n const envPath = path.join(projectDir, '.env');\n\n if (fs.existsSync(envPath)) {\n dotenv.config({ path: envPath });\n } else {\n warn('No .env file found');\n }\n};\n\n/**\n * Get MRT configuration with priority logic: .env -> package.json -> defaults\n */\nexport const getMrtConfig = (\n projectDir: string\n): { defaultMrtProject: string; defaultMrtTarget: string | undefined } => {\n // Load .env file first\n loadEnvFile(projectDir);\n\n const pkg = getProjectPkg(projectDir);\n\n // Priority: .env -> package.json name\n const defaultMrtProject = process.env.MRT_PROJECT ?? pkg.name;\n\n // Fail fast if project cannot be determined\n if (!defaultMrtProject || defaultMrtProject.trim() === '') {\n throw new Error(\n \"Project name couldn't be determined. Do one of these options:\\n\" +\n ' 1. Set MRT_PROJECT in your .env file, or\\n' +\n ' 2. Ensure package.json has a valid \"name\" field.'\n );\n }\n\n // Priority: .env -> undefined (target is optional)\n const defaultMrtTarget = process.env.MRT_TARGET ?? undefined;\n\n debug('MRT configuration resolved', {\n projectDir,\n envMrtProject: process.env.MRT_PROJECT,\n envMrtTarget: process.env.MRT_TARGET,\n packageName: pkg.name,\n resolvedProject: defaultMrtProject,\n resolvedTarget: defaultMrtTarget,\n });\n\n return { defaultMrtProject, defaultMrtTarget };\n};\n\n/**\n * Get project dependency tree (simplified version)\n */\nexport const getProjectDependencyTree = (projectDir: string): DependencyTree | null => {\n try {\n const tmpFile = path.join(os.tmpdir(), `npm-ls-${Date.now()}.json`);\n execSync(`npm ls --all --json > ${tmpFile}`, {\n stdio: 'ignore',\n cwd: projectDir,\n });\n const data = fs.readJSONSync(tmpFile);\n fs.unlinkSync(tmpFile);\n return data;\n } catch {\n // Don't prevent bundles from being pushed if this step fails\n return null;\n }\n};\n\n/**\n * Get PWA Kit dependencies from dependency tree\n */\nexport const getPwaKitDependencies = (dependencyTree: DependencyTree | null): DependencyRecord => {\n if (!dependencyTree) return {};\n\n const pwaKitDependencies = ['@salesforce/storefront-next-dev'];\n\n const result: DependencyRecord = {};\n\n const searchDeps = (tree: DependencyTree) => {\n if (tree.dependencies) {\n for (const [name, dep] of Object.entries(tree.dependencies)) {\n if (pwaKitDependencies.includes(name)) {\n result[name] = dep.version || 'unknown';\n }\n if (dep.dependencies) {\n searchDeps({ dependencies: dep.dependencies });\n }\n }\n }\n };\n\n searchDeps(dependencyTree);\n return result;\n};\n\n/**\n * Get default commit message from git\n */\nexport const getDefaultMessage = (projectDir: string): string => {\n try {\n const branch = execSync('git rev-parse --abbrev-ref HEAD', {\n encoding: 'utf8',\n cwd: projectDir,\n }).trim();\n const commit = execSync('git rev-parse --short HEAD', {\n encoding: 'utf8',\n cwd: projectDir,\n }).trim();\n return `${branch}: ${commit}`;\n } catch {\n debug('Using default bundle message as no message was provided and not in a Git repo.');\n return 'PWA Kit Bundle';\n }\n};\n\n/**\n * Given a project directory and a record of config overrides, generate a new .env file with the overrides based on the .env.default file.\n * @param projectDir\n * @param configOverrides\n */\nexport const generateEnvFile = (projectDir: string, configOverrides: Record<string, string>) => {\n const envDefaultPath = path.join(projectDir, '.env.default');\n const envPath = path.join(projectDir, '.env');\n if (!fs.existsSync(envDefaultPath)) {\n // eslint-disable-next-line no-console\n console.warn(`${envDefaultPath} not found`);\n return;\n }\n const envDefaultContent = fs.readFileSync(envDefaultPath, 'utf8');\n const envDefaultLines = envDefaultContent.split('\\n');\n\n // Create new .env content by taking .env.default as the base and\n // overriding values when a matching key is supplied in configOverrides.\n const envOutputLines = envDefaultLines.map((line) => {\n // Preserve comments and blank lines as-is\n if (!line || line.trim().startsWith('#')) return line;\n\n const eqIndex = line.indexOf('=');\n if (eqIndex === -1) return line;\n\n const key = line.slice(0, eqIndex);\n const originalValue = line.slice(eqIndex + 1);\n const override = Object.prototype.hasOwnProperty.call(configOverrides, key) ? configOverrides[key] : undefined;\n return `${key}=${override ?? originalValue}`;\n });\n\n // Write the generated content to .env (do not modify .env.default)\n fs.writeFileSync(envPath, envOutputLines.join('\\n'));\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport path from 'path';\nimport fs from 'fs-extra';\nimport os from 'os';\nimport archiver from 'archiver';\nimport { Minimatch } from 'minimatch';\nimport { getProjectPkg, getProjectDependencyTree, getPwaKitDependencies } from './utils';\nimport type { Bundle, BundleMetadata, SSRParameters, FilePatterns } from './types';\n\ninterface CreateBundleOptions {\n message: string;\n ssr_parameters: SSRParameters;\n ssr_only: FilePatterns;\n ssr_shared: FilePatterns;\n buildDirectory: string;\n projectDirectory: string;\n projectSlug: string;\n}\n\n/**\n * Create a bundle from the build directory\n */\nexport const createBundle = async (options: CreateBundleOptions): Promise<Bundle> => {\n const { message, ssr_parameters, ssr_only, ssr_shared, buildDirectory, projectDirectory, projectSlug } = options;\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'storefront-next-dev-push-'));\n const destination = path.join(tmpDir, 'build.tar');\n const filesInArchive: string[] = [];\n\n // Validate that SSR file patterns are defined\n if (!ssr_only || ssr_only.length === 0 || !ssr_shared || ssr_shared.length === 0) {\n throw new Error('no ssrOnly or ssrShared files are defined');\n }\n\n return new Promise((resolve, reject) => {\n const output = fs.createWriteStream(destination);\n const archive = archiver('tar');\n\n archive.pipe(output);\n\n const newRoot = path.join(projectSlug, 'bld', '');\n\n // Exclude Storybook and test files from the archive\n const storybookExclusions = [\n '**/*.stories.tsx',\n '**/*.stories.ts',\n '**/*-snapshot.tsx',\n '.storybook/**/*',\n 'storybook-static/**/*',\n '**/__mocks__/**/*',\n '**/__snapshots__/**/*',\n ];\n const storybookExclusionMatchers = storybookExclusions.map(\n (pattern) => new Minimatch(pattern, { nocomment: true })\n );\n\n archive.directory(buildDirectory, '', (entry) => {\n // Skip Storybook files\n if (entry.name && storybookExclusionMatchers.some((matcher) => matcher.match(entry.name))) {\n return false;\n }\n if (entry.stats?.isFile() && entry.name) {\n filesInArchive.push(entry.name);\n }\n entry.prefix = newRoot;\n return entry;\n });\n\n archive.on('error', reject);\n\n output.on('finish', () => {\n try {\n const pkg = getProjectPkg(projectDirectory);\n const { dependencies = {}, devDependencies = {} } = pkg;\n\n const dependencyTree = getProjectDependencyTree(projectDirectory);\n const pwaKitDeps = dependencyTree ? getPwaKitDependencies(dependencyTree) : {};\n\n const bundle_metadata: BundleMetadata = {\n dependencies: {\n ...dependencies,\n ...devDependencies,\n ...pwaKitDeps,\n },\n };\n\n const data = fs.readFileSync(destination);\n const encoding = 'base64' as const;\n\n // Clean up temp directory\n fs.rmSync(tmpDir, { recursive: true });\n\n // Create glob matching function\n const createGlobMatcher = (patterns: string[]) => {\n const allPatterns = patterns\n .map((pattern) => new Minimatch(pattern, { nocomment: true }))\n .filter((pattern) => !pattern.empty);\n\n const positivePatterns = allPatterns.filter((pattern) => !pattern.negate);\n const negativePatterns = allPatterns.filter((pattern) => pattern.negate);\n\n return (filePath: string): boolean => {\n if (filePath) {\n const positive = positivePatterns.some((pattern) => pattern.match(filePath));\n const negative = negativePatterns.some((pattern) => !pattern.match(filePath));\n return positive && !negative;\n }\n return false;\n };\n };\n\n resolve({\n message,\n encoding,\n data: data.toString(encoding),\n ssr_parameters,\n ssr_only: filesInArchive.filter(createGlobMatcher(ssr_only)),\n ssr_shared: filesInArchive.filter(createGlobMatcher(ssr_shared)),\n bundle_metadata,\n });\n } catch (err) {\n reject(err);\n }\n });\n\n archive.finalize().catch(reject);\n });\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n// Using built-in fetch (Node.js 18+)\nimport { URL } from 'url';\nimport type { Credentials, Bundle, CloudAPIResponse } from './types';\nimport pkg from '../package.json' with { type: 'json' };\n\nexport class CloudAPIClient {\n private credentials: Credentials;\n private origin: string;\n\n constructor({ credentials, origin }: { credentials: Credentials; origin: string }) {\n this.credentials = credentials;\n this.origin = origin;\n }\n\n private getAuthHeader() {\n const { username, api_key } = this.credentials;\n const encoded = Buffer.from(`${username}:${api_key}`, 'binary').toString('base64');\n return { Authorization: `Basic ${encoded}` };\n }\n\n private getHeaders() {\n return {\n 'User-Agent': `storefront-next-dev@${pkg.version}`,\n ...this.getAuthHeader(),\n };\n }\n\n /**\n * Push bundle to Managed Runtime\n */\n async push(bundle: Bundle, projectSlug: string, target?: string): Promise<CloudAPIResponse> {\n const base = `api/projects/${projectSlug}/builds/`;\n const pathname = target ? `${base}${target}/` : base;\n const url = new URL(this.origin);\n url.pathname = pathname;\n\n const body = Buffer.from(JSON.stringify(bundle));\n const headers = {\n ...this.getHeaders(),\n 'Content-Length': body.length.toString(),\n };\n\n const res = await fetch(url.toString(), {\n body,\n method: 'POST',\n headers,\n });\n\n if (res.status >= 400) {\n const bodyText = await res.text();\n let errorData: { message?: string } | { message: string };\n try {\n errorData = JSON.parse(bodyText);\n } catch {\n errorData = { message: bodyText };\n }\n\n throw new Error(\n `HTTP ${res.status}: ${errorData.message || bodyText}\\n` +\n `For more information visit https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/pushing-and-deploying-bundles.html`\n );\n }\n\n return (await res.json()) as CloudAPIResponse;\n }\n\n /**\n * Wait for deployment to complete\n */\n async waitForDeploy(project: string, environment: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const delay = 30000; // 30 seconds\n\n const check = async () => {\n const url = new URL(`/api/projects/${project}/target/${environment}`, this.origin);\n const res = await fetch(url, { headers: this.getHeaders() });\n\n if (!res.ok) {\n const text = await res.text();\n let json: { detail?: string } | undefined;\n try {\n if (text) json = JSON.parse(text);\n } catch {\n // Ignore parse errors\n }\n const message = json?.detail ?? text;\n const detail = message ? `: ${message}` : '';\n throw new Error(`${res.status} ${res.statusText}${detail}`);\n }\n\n const data = (await res.json()) as CloudAPIResponse;\n if (typeof data.state !== 'string') {\n return reject(new Error('An unknown state occurred when polling the deployment.'));\n }\n\n switch (data.state) {\n case 'CREATE_IN_PROGRESS':\n case 'PUBLISH_IN_PROGRESS':\n setTimeout(() => {\n void check().catch(reject);\n }, delay);\n return;\n case 'CREATE_FAILED':\n case 'PUBLISH_FAILED':\n return reject(new Error('Deployment failed.'));\n case 'ACTIVE':\n return resolve();\n default:\n return reject(new Error(`Unknown deployment state \"${data.state}\".`));\n }\n };\n\n setTimeout(() => {\n void check().catch(reject);\n }, delay);\n });\n }\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { getMrtEntryFile } from './mrt/utils';\nimport type { MrtSsrConfig } from './types';\n\nexport const CARTRIDGES_BASE_DIR = 'cartridges';\nexport const SFNEXT_BASE_CARTRIDGE_NAME = 'app_storefrontnext_base';\nexport const SFNEXT_BASE_CARTRIDGE_OUTPUT_DIR = `${SFNEXT_BASE_CARTRIDGE_NAME}/cartridge/experience`;\nexport const SFNEXT_BASE_CARTRIDGE_VERSION = '0.0.1';\n\n/**\n * When enabled, automatically generates and deploys cartridge metadata before an MRT push.\n * This is useful for keeping Page Designer metadata in sync with component changes.\n *\n * When enabled:\n * 1. Generates cartridge metadata from decorated components\n * 2. Deploys the cartridge to Commerce Cloud (requires dw.json configuration)\n * 3. Proceeds with the MRT push\n *\n * To enable: Set this to `true` in your local config.ts\n * Default: false (manual cartridge generation/deployment via `sfnext generate-cartridge` and `sfnext deploy-cartridge`)\n */\nexport const GENERATE_AND_DEPLOY_CARTRIDGE_ON_MRT_PUSH = false;\n\n/**\n * Build MRT SSR configuration for bundle deployment\n *\n * Defines which files should be:\n * - Server-only (ssrOnly): Deployed only to Lambda functions\n * - Shared (ssrShared): Deployed to both Lambda and CDN\n *\n * @param buildDirectory - Path to the build output directory\n * @param projectDirectory - Path to the project root (reserved for future use)\n * @returns MRT SSR configuration with glob patterns\n */\nexport const buildMrtConfig = (_buildDirectory: string, _projectDirectory?: string): MrtSsrConfig => {\n // SSR-only files: Server bundles and entry points\n // These are deployed only to Lambda functions, not to CDN\n const ssrEntryPoint = getMrtEntryFile('production');\n\n const ssrOnly = [\n 'server/**/*', // All server-side code\n 'loader.js', // SSR entry point\n `${ssrEntryPoint}.{js,mjs,cjs}`, // SSR entry point (supports CJS and ESM formats)\n `${ssrEntryPoint}.{js,mjs,cjs}.map`, // SSR source maps\n '!static/**/*', // Exclude static assets from server\n // Include any shared server chunks prefixed with sfnext-server- (generated by the build process)\n 'sfnext-server-*.mjs',\n // Exclude Storybook and test files\n '!**/*.stories.tsx',\n '!**/*.stories.ts',\n '!**/*-snapshot.tsx',\n '!.storybook/**/*',\n '!storybook-static/**/*',\n '!**/__mocks__/**/*',\n '!**/__snapshots__/**/*',\n ];\n\n // Shared files: Client bundles and static assets\n // These are deployed to both Lambda (for SSR) and CDN (for client)\n const ssrShared = [\n 'client/**/*', // All client-side bundles (with content hashes)\n 'static/**/*', // Static assets (images, fonts, etc.)\n '**/*.css', // Stylesheets\n '**/*.png',\n '**/*.jpg',\n '**/*.jpeg',\n '**/*.gif',\n '**/*.svg',\n '**/*.ico',\n '**/*.woff',\n '**/*.woff2',\n '**/*.ttf',\n '**/*.eot',\n // Exclude Storybook and test files\n '!**/*.stories.tsx',\n '!**/*.stories.ts',\n '!**/*-snapshot.tsx',\n '!.storybook/**/*',\n '!storybook-static/**/*',\n '!**/__mocks__/**/*',\n '!**/__snapshots__/**/*',\n ];\n\n // SSR function parameters\n const ssrParameters = {\n ssrFunctionNodeVersion: '24.x',\n };\n\n return {\n ssrOnly,\n ssrShared,\n ssrParameters,\n };\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport fs from 'fs-extra';\nimport { createBundle } from '../bundle';\nimport { CloudAPIClient } from '../cloud-api';\nimport { buildMrtConfig } from '../config';\nimport {\n DEFAULT_CLOUD_ORIGIN,\n getDefaultBuildDir,\n getCredentialsFile,\n readCredentials,\n getMrtConfig,\n getDefaultMessage,\n} from '../utils';\nimport { info, success, warn, error, debug } from '../utils/logger';\nimport type { PushOptions } from '../types';\n\n/**\n * Main function to push bundle to Managed Runtime\n */\nexport async function push(options: PushOptions): Promise<void> {\n // Get MRT configuration early for validation\n const mrtConfig = getMrtConfig(options.projectDirectory);\n const resolvedTarget = options.target ?? mrtConfig.defaultMrtTarget;\n\n // Input validation (fail fast - don't catch these)\n if (options.wait && !resolvedTarget) {\n throw new Error(\n 'You must provide a target to deploy to when using --wait (via --target flag or .env MRT_TARGET)'\n );\n }\n\n if ((options.user && !options.key) || (!options.user && options.key)) {\n throw new Error('You must provide both --user and --key together, or neither');\n }\n\n // Validate project directory exists\n if (!fs.existsSync(options.projectDirectory)) {\n throw new Error(`Project directory \"${options.projectDirectory}\" does not exist!`);\n }\n\n // Get project slug: CLI option -> .env -> package.json name\n const projectSlug = options.projectSlug ?? mrtConfig.defaultMrtProject;\n if (!projectSlug || projectSlug.trim() === '') {\n throw new Error('Project slug could not be determined from CLI, .env, or package.json');\n }\n\n // Use the resolved target from validation\n const target = resolvedTarget;\n\n // Set default build directory and validate it exists\n const buildDirectory = options.buildDirectory ?? getDefaultBuildDir(options.projectDirectory);\n if (!fs.existsSync(buildDirectory)) {\n throw new Error(`Build directory \"${buildDirectory}\" does not exist!`);\n }\n\n try {\n // Set deployment target environment variable\n if (target) {\n process.env.DEPLOY_TARGET = target;\n }\n\n // Get credentials\n let credentials;\n if (options.user && options.key) {\n credentials = {\n username: options.user,\n api_key: options.key,\n };\n } else {\n const credentialsPath = getCredentialsFile(\n options.cloudOrigin ?? DEFAULT_CLOUD_ORIGIN,\n options.credentialsFile\n );\n credentials = await readCredentials(credentialsPath);\n }\n\n // Build SSR configuration for MRT bundle\n const config = buildMrtConfig(buildDirectory, options.projectDirectory);\n\n // Set default message\n const message = options.message ?? getDefaultMessage(options.projectDirectory);\n\n info(`Creating bundle for project: ${projectSlug}`);\n if (options.projectSlug) {\n debug('Using project slug from CLI argument');\n } else if (process.env.MRT_PROJECT) {\n debug('Using project slug from .env MRT_PROJECT');\n } else {\n debug('Using project slug from package.json name');\n }\n if (target) {\n info(`Target environment: ${target}`);\n if (options.target) {\n debug('Using target from CLI argument');\n } else {\n debug('Using target from .env');\n }\n }\n\n // Debug: Log configuration arrays for troubleshooting\n debug('SSR shared files', config.ssrShared);\n debug('SSR only files', config.ssrOnly);\n\n // Create bundle\n const bundle = await createBundle({\n message,\n ssr_parameters: config.ssrParameters,\n ssr_only: config.ssrOnly,\n ssr_shared: config.ssrShared,\n buildDirectory,\n projectDirectory: options.projectDirectory,\n projectSlug,\n });\n\n // Create API client and push\n const client = new CloudAPIClient({\n credentials,\n origin: options.cloudOrigin ?? DEFAULT_CLOUD_ORIGIN,\n });\n\n info(`Beginning upload to ${options.cloudOrigin ?? DEFAULT_CLOUD_ORIGIN}`);\n const data = await client.push(bundle, projectSlug, target);\n\n // Debug: Log API response for troubleshooting\n debug('API response', data);\n\n // Handle warnings\n const warnings = data.warnings || [];\n warnings.forEach(warn);\n\n if (options.wait && target) {\n success('Bundle uploaded - waiting for deployment to complete');\n await client.waitForDeploy(projectSlug, target);\n success('Deployment complete!');\n } else {\n success('Bundle uploaded successfully!');\n }\n\n if (data.url) {\n info(`Bundle URL: ${data.url}`);\n }\n } catch (err) {\n error((err as Error).message || err?.toString() || 'Unknown error');\n throw err;\n }\n}\n\n// Export types\nexport type { PushOptions } from '../types';\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { resolve } from 'node:path';\nimport { existsSync, readFileSync } from 'node:fs';\n\n/**\n * Parse TypeScript paths from tsconfig.json and convert to jiti alias format.\n *\n * @param tsconfigPath - Path to tsconfig.json\n * @param projectDirectory - Project root directory for resolving relative paths\n * @returns Record of alias mappings for jiti\n *\n * @example\n * // tsconfig.json: { \"compilerOptions\": { \"paths\": { \"@/*\": [\"./src/*\"] } } }\n * // Returns: { \"@/\": \"/absolute/path/to/src/\" }\n */\nexport function parseTsconfigPaths(tsconfigPath: string, projectDirectory: string): Record<string, string> {\n const alias: Record<string, string> = {};\n\n if (!existsSync(tsconfigPath)) {\n return alias;\n }\n\n try {\n const tsconfigContent = readFileSync(tsconfigPath, 'utf-8');\n const tsconfig = JSON.parse(tsconfigContent) as {\n compilerOptions?: {\n paths?: Record<string, string[]>;\n baseUrl?: string;\n };\n };\n\n const paths = tsconfig.compilerOptions?.paths;\n const baseUrl = tsconfig.compilerOptions?.baseUrl || '.';\n\n if (paths) {\n for (const [key, values] of Object.entries(paths)) {\n if (values && values.length > 0) {\n // Convert TypeScript path pattern to jiti alias\n // e.g., \"@/*\": [\"./src/*\"] -> \"@/\": \"<projectDir>/src/\"\n const aliasKey = key.replace(/\\/\\*$/, '/');\n const aliasValue = values[0].replace(/\\/\\*$/, '/').replace(/^\\.\\//, '');\n alias[aliasKey] = resolve(projectDirectory, baseUrl, aliasValue);\n }\n }\n }\n } catch {\n // Ignore tsconfig parse errors - caller can work without aliases\n }\n\n return alias;\n}\n\nexport interface TsImportOptions {\n /** Project directory for resolving paths */\n projectDirectory: string;\n /** Optional path to tsconfig.json (defaults to projectDirectory/tsconfig.json) */\n tsconfigPath?: string;\n}\n\n/**\n * Import a TypeScript file using jiti with proper path alias resolution.\n * This is a cross-platform alternative to tsx that works on Windows.\n *\n * @param filePath - Absolute path to the TypeScript file to import\n * @param options - Import options including project directory\n * @returns The imported module\n */\nexport async function importTypescript<T = unknown>(filePath: string, options: TsImportOptions): Promise<T> {\n const { projectDirectory, tsconfigPath = resolve(projectDirectory, 'tsconfig.json') } = options;\n\n const { createJiti } = await import('jiti');\n const alias = parseTsconfigPaths(tsconfigPath, projectDirectory);\n\n const jiti = createJiti(import.meta.url, {\n fsCache: false,\n interopDefault: true,\n alias,\n });\n\n return jiti.import(filePath);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { resolve } from 'node:path';\nimport { existsSync } from 'node:fs';\nimport { importTypescript } from './ts-import';\n\n/**\n * Server configuration extracted from environment variables\n */\nexport interface ServerConfig {\n commerce: {\n api: {\n shortCode: string;\n organizationId: string;\n clientId: string;\n siteId: string;\n proxy: string;\n };\n };\n}\n\n/**\n * This is a temporary function before we move the config implementation from\n * template-retail-rsc-app to the SDK.\n *\n * @ TODO: Remove this function after we move the config implementation from\n * template-retail-rsc-app to the SDK.\n *\n */\nexport function loadConfigFromEnv(): ServerConfig {\n const shortCode = process.env.PUBLIC__app__commerce__api__shortCode;\n const organizationId = process.env.PUBLIC__app__commerce__api__organizationId;\n const clientId = process.env.PUBLIC__app__commerce__api__clientId;\n const siteId = process.env.PUBLIC__app__commerce__api__siteId;\n const proxy = process.env.PUBLIC__app__commerce__api__proxy || '/mobify/proxy/api';\n\n if (!shortCode) {\n throw new Error(\n 'Missing PUBLIC__app__commerce__api__shortCode environment variable.\\n' +\n 'Please set it in your .env file or environment.'\n );\n }\n\n if (!organizationId) {\n throw new Error(\n 'Missing PUBLIC__app__commerce__api__organizationId environment variable.\\n' +\n 'Please set it in your .env file or environment.'\n );\n }\n\n if (!clientId) {\n throw new Error(\n 'Missing PUBLIC__app__commerce__api__clientId environment variable.\\n' +\n 'Please set it in your .env file or environment.'\n );\n }\n\n if (!siteId) {\n throw new Error(\n 'Missing PUBLIC__app__commerce__api__siteId environment variable.\\n' +\n 'Please set it in your .env file or environment.'\n );\n }\n\n return {\n commerce: {\n api: {\n shortCode,\n organizationId,\n clientId,\n siteId,\n proxy,\n },\n },\n };\n}\n\n/**\n * Load storefront-next project configuration from config.server.ts.\n * Requires projectDirectory to be provided.\n *\n * @param projectDirectory - Project directory to load config.server.ts from\n * @throws Error if config.server.ts is not found or invalid\n */\nexport async function loadProjectConfig(projectDirectory: string): Promise<ServerConfig> {\n const configPath = resolve(projectDirectory, 'config.server.ts');\n const tsconfigPath = resolve(projectDirectory, 'tsconfig.json');\n\n if (!existsSync(configPath)) {\n throw new Error(\n `config.server.ts not found at ${configPath}.\\n` +\n 'Please ensure config.server.ts exists in your project root.'\n );\n }\n\n interface LoadedConfig {\n default?: {\n app?: {\n commerce?: {\n api?: {\n shortCode?: string;\n organizationId?: string;\n clientId?: string;\n siteId?: string;\n proxy?: string;\n };\n };\n };\n };\n }\n\n const loaded = await importTypescript<LoadedConfig>(configPath, {\n projectDirectory,\n tsconfigPath,\n });\n\n // Extract commerce API config from the loaded config\n const config = loaded.default;\n if (!config?.app?.commerce?.api) {\n throw new Error(\n `Invalid config.server.ts: missing app.commerce.api configuration.\\n` +\n 'Please ensure your config.server.ts has the commerce API configuration.'\n );\n }\n\n const api = config.app.commerce.api;\n\n // Validate required fields\n if (!api.shortCode) {\n throw new Error('Missing shortCode in config.server.ts commerce.api configuration');\n }\n if (!api.organizationId) {\n throw new Error('Missing organizationId in config.server.ts commerce.api configuration');\n }\n if (!api.clientId) {\n throw new Error('Missing clientId in config.server.ts commerce.api configuration');\n }\n if (!api.siteId) {\n throw new Error('Missing siteId in config.server.ts commerce.api configuration');\n }\n\n return {\n commerce: {\n api: {\n shortCode: api.shortCode,\n organizationId: api.organizationId,\n clientId: api.clientId,\n siteId: api.siteId,\n proxy: api.proxy || '/mobify/proxy/api',\n },\n },\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Get the Commerce Cloud API URL from a short code\n */\nexport function getCommerceCloudApiUrl(shortCode: string): string {\n return `https://${shortCode}.api.commercecloud.salesforce.com`;\n}\n\n/**\n * Get the bundle path for static assets\n */\nexport function getBundlePath(bundleId: string): string {\n return `/mobify/bundle/${bundleId}/client/`;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createProxyMiddleware, type RequestHandler } from 'http-proxy-middleware';\nimport type { ServerConfig } from '../config';\nimport { getCommerceCloudApiUrl } from '../../utils/paths';\n\n/**\n * Create proxy middleware for Commerce Cloud API\n * Proxies requests from /mobify/proxy/api to the Commerce Cloud API\n */\nexport function createCommerceProxyMiddleware(config: ServerConfig): RequestHandler {\n const target = getCommerceCloudApiUrl(config.commerce.api.shortCode);\n\n return createProxyMiddleware({\n target,\n changeOrigin: true,\n });\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport express, { type RequestHandler } from 'express';\nimport path from 'path';\nimport { getBundlePath } from '../../utils/paths';\nimport { info } from '../../utils/logger';\n\n/**\n * Create static file serving middleware for client assets\n * Serves files from build/client at /mobify/bundle/{BUNDLE_ID}/client/\n */\nexport function createStaticMiddleware(bundleId: string, projectDirectory: string): RequestHandler {\n const bundlePath = getBundlePath(bundleId);\n const clientBuildDir = path.join(projectDirectory, 'build', 'client');\n\n info(`Serving static assets from ${clientBuildDir} at ${bundlePath}`);\n\n return express.static(clientBuildDir, {\n setHeaders: (res) => {\n res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');\n res.setHeader('x-local-static-cache-control', '1');\n },\n });\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport compression from 'compression';\nimport type { RequestHandler } from 'express';\nimport zlib from 'node:zlib';\nimport { warn } from '../../utils/logger';\n\n/**\n * Parse and validate COMPRESSION_LEVEL environment variable\n * @returns Valid compression level (0-9) or default compression level\n */\nfunction getCompressionLevel(): number {\n const raw = process.env.COMPRESSION_LEVEL;\n const DEFAULT = zlib.constants.Z_DEFAULT_COMPRESSION;\n\n if (raw == null || raw.trim() === '') {\n return DEFAULT;\n }\n\n const level = Number(raw);\n\n const isValid = Number.isInteger(level) && level >= 0 && level <= 9;\n\n if (!isValid) {\n warn(`[compression] Invalid COMPRESSION_LEVEL=\"${raw}\". ` + `Using default (${DEFAULT}).`);\n return DEFAULT;\n }\n\n return level;\n}\n\n/**\n * Create compression middleware for gzip/brotli compression\n * Used in preview mode to optimize response sizes\n */\nexport function createCompressionMiddleware(): RequestHandler {\n const compressionLevel = getCompressionLevel();\n\n return compression({\n filter: (req, res) => {\n if (req.headers['x-no-compression']) {\n return false;\n }\n return compression.filter(req, res);\n },\n // Compression level (0-9, higher = better compression but slower)\n // default is zlib.constants.Z_DEFAULT_COMPRESSION = -1\n level: compressionLevel,\n });\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { RequestHandler } from 'express';\nimport morgan from 'morgan';\nimport chalk from 'chalk';\nimport { minimatch } from 'minimatch';\n\n/**\n * Patterns for URLs to skip logging (static assets and Vite internals)\n */\nconst SKIP_PATTERNS = [\n '/@vite/**',\n '/@id/**',\n '/@fs/**',\n '/@react-router/**',\n '/src/**',\n '/node_modules/**',\n '**/*.js',\n '**/*.css',\n '**/*.ts',\n '**/*.tsx',\n '**/*.js.map',\n '**/*.css.map',\n];\n\n/**\n * Create request logging middleware\n * Used in dev and preview modes for request visibility\n */\nexport function createLoggingMiddleware(): RequestHandler {\n // Custom format with colors\n morgan.token('status-colored', (req, res) => {\n const status = res.statusCode;\n let color = chalk.green;\n\n if (status >= 500) {\n color = chalk.red;\n } else if (status >= 400) {\n color = chalk.yellow;\n } else if (status >= 300) {\n color = chalk.cyan;\n }\n\n return color(String(status));\n });\n\n morgan.token('method-colored', (req) => {\n const method = req.method;\n const colors: Record<string, typeof chalk.green> = {\n GET: chalk.green,\n POST: chalk.blue,\n PUT: chalk.yellow,\n DELETE: chalk.red,\n PATCH: chalk.magenta,\n };\n const color = (method && colors[method]) || chalk.white;\n return color(method);\n });\n\n // Format: [METHOD] /path - STATUS (response-time ms)\n return morgan(\n (tokens, req, res) => {\n return [\n chalk.gray('['),\n tokens['method-colored'](req, res),\n chalk.gray(']'),\n tokens.url(req, res),\n '-',\n tokens['status-colored'](req, res),\n chalk.gray(`(${tokens['response-time'](req, res)}ms)`),\n ].join(' ');\n },\n {\n // Skip logging for static assets to reduce noise\n skip: (req) => {\n return SKIP_PATTERNS.some((pattern) => minimatch(req.url, pattern, { dot: true }));\n },\n }\n );\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { RequestHandler } from 'express';\n\n/**\n * Normalizes the X-Forwarded-Host header to support React Router's CSRF validation features.\n *\n * NOTE: This middleware performs header manipulation as a temporary, internal\n * solution for MRT/Lambda environments. It may be updated or removed if React Router\n * introduces a first-class configuration for validating against forwarded headers.\n *\n * React Router v7.12+ uses the X-Forwarded-Host header (preferring it over Host)\n * to validate request origins for security. In Managed Runtime (MRT) with a vanity\n * domain, the eCDN automatically sets the X-Forwarded-Host to the vanity domain.\n * React Router handles cases where this header contains multiple comma-separated\n * values by prioritizing the first entry.\n *\n * This middleware ensures that X-Forwarded-Host is always present by falling back\n * to a configured public domain if the header is missing (e.g., local development).\n * By only modifying X-Forwarded-Host, we provide a consistent environment for\n * React Router's security checks without modifying the internal 'Host' header,\n * which is required for environment-specific routing logic (e.g., Hybrid Proxy).\n *\n * Priority order:\n * 1. X-Forwarded-Host: Automatically set by eCDN for vanity domains.\n * 2. EXTERNAL_DOMAIN_NAME: Fallback environment variable for the public domain\n * used when no forwarded headers are present (e.g., local development).\n */\nexport function createHostHeaderMiddleware(): RequestHandler {\n return (req, _res, next) => {\n // If X-Forwarded-Host is missing, populate it from the trusted fallback.\n // React Router v7 uses this header (preferring it over Host) to validate\n // against the 'Origin' for CSRF protection.\n if (!req.get('x-forwarded-host') && process.env.EXTERNAL_DOMAIN_NAME) {\n req.headers['x-forwarded-host'] = process.env.EXTERNAL_DOMAIN_NAME;\n }\n\n next();\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { ServerBuild } from 'react-router';\nimport { getBundlePath } from '../utils/paths';\n\n/**\n * Patch React Router build to rewrite asset URLs with the correct bundle path\n * This is needed because the build output uses /assets/ but we preview at /mobify/bundle/{BUNDLE_ID}/client/assets/\n */\nexport function patchReactRouterBuild(build: ServerBuild, bundleId: string): ServerBuild {\n const bundlePath = getBundlePath(bundleId);\n\n // Clone the assets object and replace /assets/ paths with bundle path\n const assetsJson = JSON.stringify(build.assets);\n const patchedAssetsJson = assetsJson.replace(/\"\\/assets\\//g, `\"${bundlePath}assets/`);\n const newAssets = JSON.parse(patchedAssetsJson);\n\n // Return a new build object with patched publicPath and assets\n return Object.assign({}, build, {\n publicPath: bundlePath,\n assets: newAssets,\n });\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport type ServerMode = 'development' | 'preview' | 'production';\n\n/**\n * Feature flags for each server mode\n */\nexport interface ServerModeFeatures {\n /** Enable Commerce API proxy middleware to forward /mobify/proxy/api requests to SCAPI */\n enableProxy: boolean;\n\n /** Enable static file serving from build/client directory */\n enableStaticServing: boolean;\n\n /** Enable gzip/brotli compression middleware for responses */\n enableCompression: boolean;\n\n /** Enable HTTP request/response logging */\n enableLogging: boolean;\n\n /** Enable patching of asset URLs with bundle path (for CDN deployment) */\n enableAssetUrlPatching: boolean;\n}\n\n/**\n * Default feature configuration for each server mode\n */\nexport const ServerModeFeatureMap: Record<ServerMode, ServerModeFeatures> = {\n development: {\n enableProxy: true,\n enableStaticServing: false,\n enableCompression: false,\n enableLogging: true,\n enableAssetUrlPatching: false,\n },\n preview: {\n enableProxy: true,\n enableStaticServing: true,\n enableCompression: true,\n enableLogging: true,\n enableAssetUrlPatching: true,\n },\n production: {\n enableProxy: false,\n enableStaticServing: false,\n enableCompression: true,\n enableLogging: true,\n enableAssetUrlPatching: true,\n },\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport express, { type Express } from 'express';\nimport { createRequestHandler } from '@react-router/express';\nimport { type ServerBuild } from 'react-router';\nimport type { ViteDevServer } from 'vite';\nimport { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { loadConfigFromEnv, type ServerConfig } from './config';\nimport { importTypescript } from './ts-import';\nimport { createCommerceProxyMiddleware } from './middleware/proxy';\nimport { createStaticMiddleware } from './middleware/static';\nimport { createCompressionMiddleware } from './middleware/compression';\nimport { createLoggingMiddleware } from './middleware/logging';\nimport { createHostHeaderMiddleware } from './middleware/host-header';\nimport { patchReactRouterBuild } from './utils';\nimport { ServerModeFeatureMap, type ServerMode, type ServerModeFeatures } from './modes';\nimport { getBundlePath } from '../utils/paths';\n\nexport interface ServerOptions extends Partial<ServerModeFeatures> {\n /** Server mode: development (with Vite), preview (preview), or production (minimal) */\n mode: ServerMode;\n\n /** Project root directory (optional, defaults to process.cwd()) */\n projectDirectory?: string;\n\n /** Server configuration (optional, will load from env vars if not provided) */\n config?: ServerConfig;\n\n /** Server port (optional, for logging) */\n port?: number;\n\n /** Vite dev server instance (required for development mode) */\n vite?: ViteDevServer;\n\n /** React Router server build (required for preview/production modes) */\n build?: ServerBuild;\n\n /** Enable streaming of responses */\n streaming?: boolean;\n}\n\n/**\n * Create a unified Express server for development, preview, or production mode\n */\nexport async function createServer(options: ServerOptions): Promise<Express> {\n const {\n mode,\n projectDirectory = process.cwd(),\n config: providedConfig,\n vite,\n build,\n streaming = false,\n enableProxy = ServerModeFeatureMap[mode].enableProxy,\n enableStaticServing = ServerModeFeatureMap[mode].enableStaticServing,\n enableCompression = ServerModeFeatureMap[mode].enableCompression,\n enableLogging = ServerModeFeatureMap[mode].enableLogging,\n enableAssetUrlPatching = ServerModeFeatureMap[mode].enableAssetUrlPatching,\n } = options;\n\n if (mode === 'development' && !vite) {\n throw new Error('Vite dev server instance is required for development mode');\n }\n\n if ((mode === 'preview' || mode === 'production') && !build) {\n throw new Error('React Router server build is required for preview/production mode');\n }\n\n // Use provided config or load from environment variables\n // TODO: move the config implementation from template-retail-rsc-app to the SDK.\n const config = providedConfig ?? loadConfigFromEnv();\n\n // Load bundle ID from environment\n const bundleId = process.env.BUNDLE_ID ?? 'local';\n\n // Create Express app\n const app = express();\n app.disable('x-powered-by');\n\n // Apply middleware based on mode\n if (enableLogging) {\n app.use(createLoggingMiddleware());\n }\n\n // If streaming is enabled then compression needs to be handled by the streaming handler\n // in the streamingHandler file\n if (enableCompression && !streaming) {\n app.use(createCompressionMiddleware());\n }\n\n if (enableStaticServing && build) {\n const bundlePath = getBundlePath(bundleId);\n app.use(bundlePath, createStaticMiddleware(bundleId, projectDirectory));\n }\n\n // Load and apply custom middlewares from the app's middleware-registry.ts\n // This allows extensions to inject middleware (e.g. Hybrid Proxy)\n const middlewareRegistryPath = resolve(projectDirectory, 'src/server/middleware-registry.ts');\n if (existsSync(middlewareRegistryPath)) {\n interface MiddlewareRegistry {\n customMiddlewares?: express.RequestHandler[];\n }\n\n const registry = await importTypescript<MiddlewareRegistry>(middlewareRegistryPath, {\n projectDirectory,\n });\n\n if (registry.customMiddlewares && Array.isArray(registry.customMiddlewares)) {\n registry.customMiddlewares.forEach((middleware: express.RequestHandler) => {\n app.use(middleware);\n });\n }\n }\n\n if (mode === 'development' && vite) {\n // In development, Vite middleware handles HMR, transforms, and proxy\n app.use(vite.middlewares);\n }\n\n if (enableProxy) {\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n app.use(config.commerce.api.proxy, createCommerceProxyMiddleware(config));\n }\n\n // Normalize the Host header for React Router's CSRF validation features\n app.use(createHostHeaderMiddleware());\n\n // SSR request handler\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n app.all('*', await createSSRHandler(mode, bundleId, vite, build, enableAssetUrlPatching));\n\n return app;\n}\n\n/**\n * Create the SSR request handler based on mode\n */\nasync function createSSRHandler(\n mode: ServerMode,\n bundleId: string,\n vite: ViteDevServer | undefined,\n build: ServerBuild | undefined,\n enableAssetUrlPatching: boolean\n) {\n if (mode === 'development' && vite) {\n // The vite package is not designed to be bundlable via build tools\n // You will run into a lot of build errors if you try to bundle it.\n // So, we dynamically import it here to avoid bundling it in production.\n const { isRunnableDevEnvironment } = await import('vite');\n\n return async (req: express.Request, res: express.Response, next: express.NextFunction) => {\n try {\n const ssrEnvironment = vite.environments.ssr;\n\n // Check if the environment is runnable (has a module runner)\n if (!isRunnableDevEnvironment(ssrEnvironment)) {\n const error = new Error(\n 'SSR environment is not runnable. Please ensure:\\n' +\n ' 1. \"@salesforce/storefront-next-dev\" plugin is added to vite.config.ts\\n' +\n ' 2. React Router config uses the Storefront Next preset'\n );\n next(error);\n return;\n }\n\n // Load server build using Vite Environment API\n // This gets the latest build with HMR updates\n const devBuild = await ssrEnvironment.runner.import('virtual:react-router/server-build');\n\n // Use the same request handler pattern as production\n const handler = createRequestHandler({\n build: devBuild,\n mode: process.env.NODE_ENV,\n });\n\n await handler(req, res, next);\n } catch (error) {\n // Let Vite handle SSR errors with nice error overlay\n vite.ssrFixStacktrace(error as Error);\n next(error);\n }\n };\n } else if (build) {\n // Serve/Production mode: static build\n let patchedBuild = build;\n\n if (enableAssetUrlPatching) {\n patchedBuild = patchReactRouterBuild(build, bundleId);\n }\n\n return createRequestHandler({\n build: patchedBuild,\n mode: process.env.NODE_ENV,\n });\n } else {\n throw new Error('Invalid server configuration: no vite or build provided');\n }\n}\n\n// Re-export config and types\nexport { loadProjectConfig, loadConfigFromEnv, type ServerConfig } from './config';\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs';\nimport path from 'path';\n\n// Cache for tsconfig paths to avoid repeated disk reads and JSON parsing\nlet cachedTsconfigPaths: Record<string, string[] | string> | null = null;\nlet cachedTsconfigRoot: string | null = null;\n\nexport const FILE_EXTENSIONS: string[] = ['.tsx', '.ts', '.d.ts'];\n\n/**\n * Strip the comments from the JSON string\n * @param jsonString\n * @returns {string}\n */\nfunction stripJsonComments(jsonString: string): string {\n return jsonString\n .replace(/\\/\\/.*$/gm, '') // remove // comments\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, ''); // remove /* */ comments\n}\n\n/**\n * Load the tsconfig.json paths from the project root\n * @param projectRoot\n * @returns {Record<string, string[] | string>}\n */\nfunction loadTsconfigPaths(projectRoot: string): Record<string, string[] | string> | null {\n // If cache is valid for the same root, return it\n if (cachedTsconfigPaths && cachedTsconfigRoot === projectRoot) {\n return cachedTsconfigPaths;\n }\n\n const tsconfigPath = path.join(projectRoot, 'tsconfig.json');\n if (!fs.existsSync(tsconfigPath)) {\n cachedTsconfigPaths = {}; // empty object\n cachedTsconfigRoot = projectRoot;\n return cachedTsconfigPaths;\n }\n\n try {\n const tsconfigContent = stripJsonComments(fs.readFileSync(tsconfigPath, 'utf-8'));\n const tsconfig = JSON.parse(tsconfigContent);\n const paths = tsconfig?.compilerOptions?.paths;\n if (paths && typeof paths === 'object') {\n cachedTsconfigPaths = paths;\n } else {\n cachedTsconfigPaths = {}; // empty object\n }\n cachedTsconfigRoot = projectRoot;\n return cachedTsconfigPaths;\n } catch (error) {\n throw new Error(`Error parsing tsconfig.json for project ${projectRoot}: ${String(error)}`);\n }\n}\n\n/**\n * Resolve the path from the alias to the real path by consulting tsconfig.json paths configuration\n * @param {string} importPath\n * @param {string} projectRoot\n * @returns {string}\n */\nexport function resolvePathFromAlias(importPath: string, projectRoot: string): string {\n // First check if this is a relative import - if so, return as is\n if (importPath.startsWith('.')) {\n return importPath;\n }\n\n // Load and cache tsconfig paths\n const paths = loadTsconfigPaths(projectRoot);\n if (!paths || typeof paths !== 'object' || Object.keys(paths).length === 0) {\n return importPath;\n }\n\n // Find matching alias\n for (const [alias, mappings] of Object.entries(paths)) {\n // Convert TypeScript path pattern to regex (escape '+' to literal, keep '*' as wildcard)\n const aliasEscapedPlus = alias.replace(/\\+/g, '\\\\+');\n const aliasPattern = aliasEscapedPlus.replace(/\\*/g, '(.*)');\n const aliasRegex = new RegExp(`^${aliasPattern}$`);\n const match = importPath.match(aliasRegex);\n\n if (match) {\n const mappingArray = Array.isArray(mappings) ? mappings : [mappings];\n // Try each mapping until we find an existing file\n for (const mapping of mappingArray) {\n // Replace wildcards in the mapping with captured groups\n let resolvedPath = mapping;\n for (let i = 1; i < match.length; i++) {\n resolvedPath = resolvedPath.replace('*', match[i]);\n }\n\n // Remove leading \"./\" from the mapping if present\n if (resolvedPath.startsWith('./')) {\n resolvedPath = resolvedPath.substring(2);\n }\n\n const fullPath = path.resolve(projectRoot, resolvedPath);\n\n // Check if the file exists (with common extensions)\n for (const ext of FILE_EXTENSIONS) {\n const pathWithExt = fullPath + ext;\n if (fs.existsSync(pathWithExt)) {\n return pathWithExt;\n }\n }\n\n // Also check if it's a directory with index file\n if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {\n for (const indexFile of ['index.ts', 'index.tsx', 'index.js', 'index.jsx']) {\n const indexPath = path.join(fullPath, indexFile);\n if (fs.existsSync(indexPath)) {\n return indexPath;\n }\n }\n // If directory exists but no index file, return the directory path\n return fullPath;\n }\n }\n }\n }\n\n // If no existing file was found for this alias simply return the original import path\n return importPath;\n}\n\nexport function isSupportedFileExtension(fileName: string): boolean {\n return FILE_EXTENSIONS.some((ext) => fileName.endsWith(ext));\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Utility to trim the directory to remove unused components and unused extensions.\n * This is used to reduce the size of the project by removing the code that is not part of the selected extensions.\n */\n/* eslint-disable no-console */\nimport fs from 'fs';\nimport path from 'path';\nimport type ExtensionConfig from './extension-config';\nimport { isSupportedFileExtension } from './path-util';\n\ntype ExtensionsSelection = Record<string, boolean>;\n\nconst SINGLE_LINE_MARKER = '@sfdc-extension-line';\nconst BLOCK_MARKER_START = '@sfdc-extension-block-start';\nconst BLOCK_MARKER_END = '@sfdc-extension-block-end';\nconst FILE_MARKER = '@sfdc-extension-file';\nlet verbose = false;\n\nexport default function trimExtensions(\n directory: string,\n selectedExtensions?: Partial<ExtensionsSelection>,\n extensionConfig?: typeof ExtensionConfig,\n verboseOverride: boolean = false\n): void {\n const startTime = Date.now();\n verbose = verboseOverride ?? false;\n\n // read available extensions from config file\n const configuredExtensions: Record<string, unknown> = extensionConfig?.extensions || {};\n const extensions: ExtensionsSelection = {};\n Object.keys(configuredExtensions).forEach((pluginKey) => {\n extensions[pluginKey] = Boolean(selectedExtensions?.[pluginKey]) || false;\n });\n\n if (Object.keys(extensions).length === 0) {\n if (verbose) {\n console.log('No plugins found, skipping trim');\n }\n return;\n }\n\n const processDirectory = (dir: string): void => {\n const files = fs.readdirSync(dir);\n files.forEach((file) => {\n const filePath = path.join(dir, file);\n const stats = fs.statSync(filePath);\n\n if (!filePath.includes('node_modules')) {\n if (stats.isDirectory()) {\n processDirectory(filePath);\n } else if (isSupportedFileExtension(file)) {\n processFile(filePath, extensions);\n }\n }\n });\n };\n\n processDirectory(directory);\n if (extensionConfig?.extensions) {\n deleteExtensionFolders(directory, extensions, extensionConfig);\n updateExtensionConfig(directory, extensions);\n }\n const endTime = Date.now();\n if (verbose) {\n console.log(`Trim extensions took ${endTime - startTime}ms`);\n }\n}\n\n/**\n * Update the extension config file to only include the selected extensions.\n * @param projectDirectory - The project directory\n * @param extensionSelections - The selected extensions\n */\nfunction updateExtensionConfig(projectDirectory: string, extensionSelections: ExtensionsSelection) {\n const extensionConfigPath = path.join(projectDirectory, 'src', 'extensions', 'config.json');\n const extensionConfig = JSON.parse(fs.readFileSync(extensionConfigPath, 'utf8'));\n Object.keys(extensionConfig.extensions).forEach((extensionKey: string) => {\n if (!extensionSelections[extensionKey]) {\n delete extensionConfig.extensions[extensionKey];\n }\n });\n fs.writeFileSync(extensionConfigPath, JSON.stringify({ extensions: extensionConfig.extensions }, null, 4), 'utf8');\n}\n\n/**\n * Process a file to trim extension-specific code based on markers.\n * @param filePath - The file path to process\n * @param extensions - The extension selections\n */\nfunction processFile(filePath: string, extensions: ExtensionsSelection): void {\n const source = fs.readFileSync(filePath, 'utf-8');\n\n // If the file is guarded by a file-level marker and the extension is disabled, remove the file entirely\n if (source.includes(FILE_MARKER)) {\n // find() always returns a line since the if condition ensures marker exists, and FILE_MARKER has no newlines\n const markerLine = source.split('\\n').find((line) => line.includes(FILE_MARKER)) as string;\n const extMatch = Object.keys(extensions).find((ext) => markerLine.includes(ext));\n if (!extMatch) {\n if (verbose) {\n console.warn(\n `File ${filePath} is marked with ${markerLine} but it does not match any known extensions`\n );\n }\n } else if (extensions[extMatch] === false) {\n try {\n fs.unlinkSync(filePath);\n if (verbose) {\n console.log(`Deleted file ${filePath}`);\n }\n } catch (e: unknown) {\n const error = e as Error;\n console.error(`Error deleting file ${filePath}: ${error.message}`);\n throw e;\n }\n return;\n }\n }\n\n // extensions will always have keys since trimExtensions validates this before calling processFile\n const extKeys = Object.keys(extensions);\n const extensionRegex = new RegExp(extKeys.join('|'), 'g');\n if (extensionRegex.test(source)) {\n const lines = source.split('\\n');\n const newLines: string[] = [];\n const blockMarkers: { extension: string; line: number }[] = [];\n let skippingBlock = false;\n let i = 0;\n\n while (i < lines.length) {\n const line = lines[i];\n if (line.includes(SINGLE_LINE_MARKER)) {\n const matchingExtension = Object.keys(extensions).find((extension) => line.includes(extension));\n if (matchingExtension && extensions[matchingExtension] === false) {\n // Skip the marker line and the next line (the actual code line)\n i += 2;\n continue;\n }\n } else if (line.includes(BLOCK_MARKER_START)) {\n const matchingExtension = Object.keys(extensions).find((extension) => line.includes(extension));\n if (matchingExtension) {\n blockMarkers.push({ extension: matchingExtension, line: i });\n skippingBlock = extensions[matchingExtension] === false;\n } else {\n if (verbose) {\n console.warn(`Warning: Unknown marker found in ${filePath} at line ${i}: \\n${line}`);\n }\n }\n } else if (line.includes(BLOCK_MARKER_END)) {\n const matchingExtension = Object.keys(extensions).find((extension) => line.includes(extension));\n if (matchingExtension) {\n const extension = Object.keys(extensions).find((p) => line.includes(p));\n if (blockMarkers.length === 0) {\n throw new Error(\n `Block marker mismatch in ${filePath}, encountered end marker ${extension} without a matching start marker at line ${i}:\\n${lines[i]}`\n );\n }\n const startMarker = blockMarkers.pop() as { extension: string; line: number };\n if (!extension || startMarker.extension !== extension) {\n throw new Error(\n `Block marker mismatch in ${filePath}, expected end marker for ${startMarker.extension} but got ${extension} at line ${i}:\\n${lines[i]}`\n );\n }\n if (extensions[extension] === false) {\n // Skip the entire block (marker lines are already skipped by skippingBlock)\n skippingBlock = false;\n i++;\n continue;\n }\n }\n }\n if (!skippingBlock) {\n newLines.push(line);\n }\n i++;\n }\n\n if (blockMarkers.length > 0) {\n throw new Error(\n `Unclosed end marker found in ${filePath}: ${blockMarkers[blockMarkers.length - 1].extension}`\n );\n }\n\n // Only write if content changed\n const newSource = newLines.join('\\n');\n if (newSource !== source) {\n try {\n fs.writeFileSync(filePath, newSource);\n if (verbose) {\n console.log(`Updated file ${filePath}`);\n }\n } catch (e: unknown) {\n const error = e as Error;\n console.error(`Error updating file ${filePath}: ${error.message}`);\n throw e;\n }\n }\n }\n}\n\n/**\n * Delete extension folders for disabled extensions.\n * @param projectRoot - The project root directory\n * @param extensions - The extension selections\n * @param extensionConfig - The extension configuration\n */\nfunction deleteExtensionFolders(\n projectRoot: string,\n extensions: ExtensionsSelection,\n extensionConfig: typeof ExtensionConfig & { extensions: Record<string, unknown> }\n): void {\n const extensionsDir = path.join(projectRoot, 'src', 'extensions');\n if (!fs.existsSync(extensionsDir)) {\n return;\n }\n\n const configuredExtensions = extensionConfig.extensions;\n const disabledExtensions = Object.keys(extensions).filter((ext) => extensions[ext] === false);\n\n disabledExtensions.forEach((extKey) => {\n const extensionMeta = configuredExtensions[extKey] as { folder?: string } | undefined;\n if (extensionMeta?.folder) {\n const extensionFolderPath = path.join(extensionsDir, extensionMeta.folder);\n if (fs.existsSync(extensionFolderPath)) {\n try {\n fs.rmSync(extensionFolderPath, { recursive: true, force: true });\n if (verbose) {\n console.log(`Deleted extension folder: ${extensionFolderPath}`);\n }\n } catch (err: unknown) {\n const error = err as Error & { code?: string };\n if (error.code === 'EPERM') {\n console.error(\n `Permission denied - cannot delete ${extensionFolderPath}. You may need to run with sudo or check permissions.`\n );\n } else {\n console.error(`Error deleting ${extensionFolderPath}: ${error.message}`);\n }\n }\n }\n }\n });\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { join } from 'node:path';\nimport { existsSync, readFileSync, unlinkSync } from 'node:fs';\nimport { execSync } from 'node:child_process';\nimport { tmpdir } from 'node:os';\nimport { randomUUID } from 'node:crypto';\nimport { npmRunPathEnv } from 'npm-run-path';\nimport type { RouteConfigEntry } from '@react-router/dev/routes';\n\nlet isCliAvailable: boolean | null = null;\n\nfunction checkReactRouterCli(projectDirectory: string): boolean {\n if (isCliAvailable !== null) {\n return isCliAvailable;\n }\n\n try {\n execSync('react-router --version', {\n cwd: projectDirectory,\n env: npmRunPathEnv(),\n stdio: 'pipe',\n });\n isCliAvailable = true;\n } catch {\n isCliAvailable = false;\n }\n return isCliAvailable;\n}\n\n/**\n * Get the fully resolved routes from React Router by invoking its CLI.\n * This ensures we get the exact same route resolution as React Router uses internally,\n * including all presets, file-system routes, and custom route configurations.\n * @param projectDirectory - The project root directory\n * @returns Array of resolved route config entries\n * @example\n * const routes = getReactRouterRoutes('/path/to/project');\n * // Returns the same structure as `react-router routes --json`\n */\nfunction getReactRouterRoutes(projectDirectory: string): RouteConfigEntry[] {\n if (!checkReactRouterCli(projectDirectory)) {\n throw new Error(\n 'React Router CLI is not available. Please make sure @react-router/dev is installed and accessible.'\n );\n }\n\n // Use a temp file to avoid Node.js buffer limits (8KB default)\n const tempFile = join(tmpdir(), `react-router-routes-${randomUUID()}.json`);\n\n try {\n // Redirect output to temp file to avoid buffer truncation\n execSync(`react-router routes --json > \"${tempFile}\"`, {\n cwd: projectDirectory,\n env: npmRunPathEnv(),\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n const output = readFileSync(tempFile, 'utf-8');\n return JSON.parse(output) as RouteConfigEntry[];\n } catch (error) {\n throw new Error(`Failed to get routes from React Router CLI: ${(error as Error).message}`);\n } finally {\n // Clean up temp file\n try {\n if (existsSync(tempFile)) {\n unlinkSync(tempFile);\n }\n } catch {\n // Ignore cleanup errors\n }\n }\n}\n\n/**\n * Convert a file path to its corresponding route path using React Router's CLI.\n * This ensures we get the exact same route resolution as React Router uses internally.\n * @param filePath - Absolute path to the route file\n * @param projectRoot - The project root directory\n * @returns The route path (e.g., '/cart', '/product/:productId')\n * @example\n * const route = filePathToRoute('/path/to/project/src/routes/_app.cart.tsx', '/path/to/project');\n * // Returns: '/cart'\n */\nexport function filePathToRoute(filePath: string, projectRoot: string): string {\n // Normalize paths to POSIX-style\n const filePathPosix = filePath.replace(/\\\\/g, '/');\n\n // Get all routes from React Router CLI\n const routes = getReactRouterRoutes(projectRoot);\n const flatRoutes = flattenRoutes(routes);\n\n // Find the route that matches this file\n for (const route of flatRoutes) {\n // Normalize the route file path for comparison\n const routeFilePosix = route.file.replace(/\\\\/g, '/');\n\n // Check if the file path ends with the route file (handles relative vs. absolute paths)\n if (filePathPosix.endsWith(routeFilePosix) || filePathPosix.endsWith(`/${routeFilePosix}`)) {\n return route.path;\n }\n\n // Also check without leading ./\n const routeFileNormalized = routeFilePosix.replace(/^\\.\\//, '');\n if (filePathPosix.endsWith(routeFileNormalized) || filePathPosix.endsWith(`/${routeFileNormalized}`)) {\n return route.path;\n }\n }\n\n // Fallback: if no match found, return a warning path\n console.warn(`Warning: Could not find route for file: ${filePath}`);\n return '/unknown';\n}\n\n/**\n * Flatten a nested route tree into a flat array with computed paths.\n * Each route will have its full path computed from parent paths.\n * @param routes - The nested route config entries\n * @param parentPath - The parent path prefix (used internally for recursion)\n * @returns Flat array of routes with their full paths\n */\nfunction flattenRoutes(\n routes: RouteConfigEntry[],\n parentPath = ''\n): Array<{ id: string; path: string; file: string; index?: boolean }> {\n const result: Array<{ id: string; path: string; file: string; index?: boolean }> = [];\n\n for (const route of routes) {\n // Compute the full path\n let fullPath: string;\n if (route.index) {\n fullPath = parentPath || '/';\n } else if (route.path) {\n // Handle paths that already start with / (absolute paths from extensions)\n const pathSegment = route.path.startsWith('/') ? route.path : `/${route.path}`;\n fullPath = parentPath ? `${parentPath}${pathSegment}`.replace(/\\/+/g, '/') : pathSegment;\n } else {\n // Layout route without path - use parent path\n fullPath = parentPath || '/';\n }\n\n // Add this route if it has an id\n if (route.id) {\n result.push({\n id: route.id,\n path: fullPath,\n file: route.file,\n index: route.index,\n });\n }\n\n // Recursively process children\n if (route.children && route.children.length > 0) {\n const childPath = route.path ? fullPath : parentPath;\n result.push(...flattenRoutes(route.children, childPath));\n }\n }\n\n return result;\n}\n","#!/usr/bin/env node\n/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* eslint-disable no-console */\nimport { readdir, readFile, writeFile, mkdir, access, rm } from 'node:fs/promises';\nimport { join, extname, resolve, basename } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { Project, Node, type SourceFile, type PropertyDeclaration, type Decorator } from 'ts-morph';\nimport { filePathToRoute } from './react-router-config.js';\n\n// Re-export `filePathToRoute`\nexport { filePathToRoute };\n\nconst SKIP_DIRECTORIES = ['build', 'dist', 'node_modules', '.git', '.next', 'coverage'];\n\nconst DEFAULT_COMPONENT_GROUP = 'odyssey_base';\nconst ARCH_TYPE_HEADLESS = 'headless';\n\ntype AttributeType =\n | 'string'\n | 'text'\n | 'markup'\n | 'integer'\n | 'boolean'\n | 'product'\n | 'category'\n | 'file'\n | 'page'\n | 'image'\n | 'url'\n | 'enum'\n | 'custom'\n | 'cms_record';\n\nconst VALID_ATTRIBUTE_TYPES: readonly AttributeType[] = [\n 'string',\n 'text',\n 'markup',\n 'integer',\n 'boolean',\n 'product',\n 'category',\n 'file',\n 'page',\n 'image',\n 'url',\n 'enum',\n 'custom',\n 'cms_record',\n] as const;\n\n// Type mapping for TypeScript types to B2C Commerce attribute types\n// Based on official schema: https://salesforcecommercecloud.github.io/b2c-dev-doc/docs/current/content/attributedefinition.json\nconst TYPE_MAPPING: Record<string, string> = {\n String: 'string',\n string: 'string',\n Number: 'integer',\n number: 'integer',\n Boolean: 'boolean',\n boolean: 'boolean',\n Date: 'string', // B2C Commerce doesn't have a native date type, use string\n URL: 'url',\n CMSRecord: 'cms_record',\n};\n\n// Resolve attribute type in order: decorator type -> ts-morph type inference -> fallback to string\nfunction resolveAttributeType(decoratorType?: string, tsMorphType?: string, fieldName?: string): string {\n // 1) If the type is set on the decorator, use that (with validation)\n if (decoratorType) {\n if (!VALID_ATTRIBUTE_TYPES.includes(decoratorType as AttributeType)) {\n console.error(\n `Error: Invalid attribute type '${decoratorType}' for field '${fieldName || 'unknown'}'. Valid types are: ${VALID_ATTRIBUTE_TYPES.join(', ')}`\n );\n process.exit(1);\n }\n return decoratorType;\n }\n\n // 2) Use the type from ts-morph type inference\n if (tsMorphType && TYPE_MAPPING[tsMorphType]) {\n return TYPE_MAPPING[tsMorphType];\n }\n\n // 3) Fall back to string\n return 'string';\n}\n\n// Convert field name to human-readable name\nfunction toHumanReadableName(fieldName: string): string {\n return fieldName\n .replace(/([A-Z])/g, ' $1') // Add space before capital letters\n .replace(/^./, (str) => str.toUpperCase()) // Capitalize first letter\n .trim();\n}\n\n// Convert name to camelCase filename (handles spaces and hyphens, preserves existing camelCase)\nfunction toCamelCaseFileName(name: string): string {\n // If the name is already camelCase (no spaces or hyphens), return as-is\n if (!/[\\s-]/.test(name)) {\n return name;\n }\n\n return name\n .split(/[\\s-]+/) // Split by whitespace and hyphens\n .map((word, index) => {\n if (index === 0) {\n return word.toLowerCase(); // First word is all lowercase\n }\n return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); // Subsequent words are capitalized\n })\n .join(''); // Join without spaces or hyphens\n}\n\nfunction getTypeFromTsMorph(property: PropertyDeclaration, _sourceFile: SourceFile): string {\n try {\n const typeNode = property.getTypeNode();\n if (typeNode) {\n const typeText = typeNode.getText();\n // Extract the base type name from complex types\n const baseType = typeText.split('|')[0].split('&')[0].trim();\n return baseType;\n }\n } catch {\n // If type extraction fails, return string\n }\n\n return 'string';\n}\n\n// Helper function to parse any TypeScript expression into a JavaScript value\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction parseExpression(expression: any): unknown {\n if (Node.isStringLiteral(expression)) {\n return expression.getLiteralValue();\n } else if (Node.isNumericLiteral(expression)) {\n return expression.getLiteralValue();\n } else if (Node.isTrueLiteral(expression)) {\n return true;\n } else if (Node.isFalseLiteral(expression)) {\n return false;\n } else if (Node.isObjectLiteralExpression(expression)) {\n return parseNestedObject(expression);\n } else if (Node.isArrayLiteralExpression(expression)) {\n return parseArrayLiteral(expression);\n } else {\n return expression.getText();\n }\n}\n\n// Helper function to parse deeply nested object literals\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction parseNestedObject(objectLiteral: any): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n try {\n const properties = objectLiteral.getProperties();\n\n for (const property of properties) {\n if (Node.isPropertyAssignment(property)) {\n const name = property.getName();\n const initializer = property.getInitializer();\n\n if (initializer) {\n result[name] = parseExpression(initializer);\n }\n }\n }\n } catch (error) {\n console.warn(`Warning: Could not parse nested object: ${(error as Error).message}`);\n return result; // Return the result even if there was an error\n }\n\n return result;\n}\n\n// Helper function to parse array literals\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction parseArrayLiteral(arrayLiteral: any): unknown[] {\n const result: unknown[] = [];\n\n try {\n const elements = arrayLiteral.getElements();\n\n for (const element of elements) {\n result.push(parseExpression(element));\n }\n } catch (error) {\n console.warn(`Warning: Could not parse array literal: ${(error as Error).message}`);\n }\n\n return result;\n}\n\n// Parse decorator arguments using ts-morph\nfunction parseDecoratorArgs(decorator: Decorator): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n try {\n const args = decorator.getArguments();\n\n if (args.length === 0) {\n return result;\n }\n\n // Handle the first argument\n const firstArg = args[0];\n\n if (Node.isObjectLiteralExpression(firstArg)) {\n // First argument is an object literal - parse all its properties\n const properties = firstArg.getProperties();\n\n for (const property of properties) {\n if (Node.isPropertyAssignment(property)) {\n const name = property.getName();\n const initializer = property.getInitializer();\n\n if (initializer) {\n result[name] = parseExpression(initializer);\n }\n }\n }\n } else if (Node.isStringLiteral(firstArg)) {\n // First argument is a string literal - use it as the id\n result.id = parseExpression(firstArg);\n\n // Check if there's a second argument (options object)\n if (args.length > 1) {\n const secondArg = args[1];\n if (Node.isObjectLiteralExpression(secondArg)) {\n const properties = secondArg.getProperties();\n\n for (const property of properties) {\n if (Node.isPropertyAssignment(property)) {\n const name = property.getName();\n const initializer = property.getInitializer();\n\n if (initializer) {\n result[name] = parseExpression(initializer);\n }\n }\n }\n }\n }\n }\n\n return result;\n } catch (error) {\n console.warn(`Warning: Could not parse decorator arguments: ${(error as Error).message}`);\n return result;\n }\n}\n\nfunction extractAttributesFromSource(sourceFile: SourceFile, className: string): Record<string, unknown>[] {\n const attributes: Record<string, unknown>[] = [];\n\n try {\n // Find the class declaration\n const classDeclaration = sourceFile.getClass(className);\n if (!classDeclaration) {\n return attributes;\n }\n\n // Get all properties in the class\n const properties = classDeclaration.getProperties();\n\n for (const property of properties) {\n // Check if the property has an @AttributeDefinition decorator\n const attributeDecorator = property.getDecorator('AttributeDefinition');\n if (!attributeDecorator) {\n continue;\n }\n\n const fieldName = property.getName();\n const config = parseDecoratorArgs(attributeDecorator);\n\n const isRequired = !property.hasQuestionToken();\n\n const inferredType = (config.type as string) || getTypeFromTsMorph(property, sourceFile);\n\n const attribute: Record<string, unknown> = {\n id: config.id || fieldName,\n name: config.name || toHumanReadableName(fieldName),\n type: resolveAttributeType(config.type as string, inferredType, fieldName),\n required: config.required !== undefined ? config.required : isRequired,\n description: config.description || `Field: ${fieldName}`,\n };\n\n if (config.values) {\n attribute.values = config.values;\n }\n\n if (config.defaultValue !== undefined) {\n attribute.default_value = config.defaultValue;\n }\n\n attributes.push(attribute);\n }\n } catch (error) {\n console.warn(`Warning: Could not extract attributes from class ${className}: ${(error as Error).message}`);\n }\n\n return attributes;\n}\n\nfunction extractRegionDefinitionsFromSource(sourceFile: SourceFile, className: string): Record<string, unknown>[] {\n const regionDefinitions: Record<string, unknown>[] = [];\n\n try {\n // Find the class declaration\n const classDeclaration = sourceFile.getClass(className);\n if (!classDeclaration) {\n return regionDefinitions;\n }\n\n // Check for class-level @RegionDefinition decorator\n const classRegionDecorator = classDeclaration.getDecorator('RegionDefinition');\n if (classRegionDecorator) {\n const args = classRegionDecorator.getArguments();\n if (args.length > 0) {\n const firstArg = args[0];\n\n // Handle array literal argument (most common case)\n if (Node.isArrayLiteralExpression(firstArg)) {\n const elements = firstArg.getElements();\n for (const element of elements) {\n if (Node.isObjectLiteralExpression(element)) {\n const regionConfig = parseDecoratorArgs({\n getArguments: () => [element],\n } as unknown as Decorator);\n\n const regionDefinition: Record<string, unknown> = {\n id: regionConfig.id || 'region',\n name: regionConfig.name || 'Region',\n };\n\n // Add optional properties if they exist in the decorator\n if (regionConfig.componentTypes) {\n regionDefinition.component_types = regionConfig.componentTypes;\n }\n\n if (Array.isArray(regionConfig.componentTypeInclusions)) {\n regionDefinition.component_type_inclusions = regionConfig.componentTypeInclusions.map(\n (incl) => ({\n type_id: incl,\n })\n );\n }\n\n if (Array.isArray(regionConfig.componentTypeExclusions)) {\n regionDefinition.component_type_exclusions = regionConfig.componentTypeExclusions.map(\n (excl) => ({\n type_id: excl,\n })\n );\n }\n\n if (regionConfig.maxComponents !== undefined) {\n regionDefinition.max_components = regionConfig.maxComponents;\n }\n\n if (regionConfig.minComponents !== undefined) {\n regionDefinition.min_components = regionConfig.minComponents;\n }\n\n if (regionConfig.allowMultiple !== undefined) {\n regionDefinition.allow_multiple = regionConfig.allowMultiple;\n }\n\n if (regionConfig.defaultComponentConstructors) {\n regionDefinition.default_component_constructors =\n regionConfig.defaultComponentConstructors;\n }\n\n regionDefinitions.push(regionDefinition);\n }\n }\n }\n }\n }\n } catch (error) {\n console.warn(\n `Warning: Could not extract region definitions from class ${className}: ${(error as Error).message}`\n );\n }\n\n return regionDefinitions;\n}\n\nasync function processComponentFile(filePath: string, _projectRoot: string): Promise<unknown[]> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const components: unknown[] = [];\n\n // Check if file contains @Component decorator\n if (!content.includes('@Component')) {\n return components;\n }\n\n // Convert file path to module path (currently unused but may be needed in future)\n // const relativePath = relative(join(projectRoot, 'src'), filePath);\n // const modulePath = relativePath.replace(/\\.tsx?$/, '').replace(/\\\\/g, '/');\n\n try {\n // Create a ts-morph project and add the source file\n const project = new Project({\n useInMemoryFileSystem: true,\n skipAddingFilesFromTsConfig: true,\n });\n\n const sourceFile = project.createSourceFile(filePath, content);\n\n const classes = sourceFile.getClasses();\n\n for (const classDeclaration of classes) {\n const componentDecorator = classDeclaration.getDecorator('Component');\n if (!componentDecorator) {\n continue;\n }\n\n const className = classDeclaration.getName();\n if (!className) {\n continue;\n }\n\n const componentConfig = parseDecoratorArgs(componentDecorator);\n\n const attributes = extractAttributesFromSource(sourceFile, className);\n const regionDefinitions = extractRegionDefinitionsFromSource(sourceFile, className);\n\n const componentMetadata = {\n typeId: componentConfig.id || className.toLowerCase(),\n name: componentConfig.name || toHumanReadableName(className),\n group: componentConfig.group || DEFAULT_COMPONENT_GROUP,\n description: componentConfig.description || `Custom component: ${className}`,\n regionDefinitions,\n attributes,\n };\n\n components.push(componentMetadata);\n }\n } catch (error) {\n console.warn(`Warning: Could not process file ${filePath}:`, (error as Error).message);\n }\n\n return components;\n } catch (error) {\n console.warn(`Warning: Could not read file ${filePath}:`, (error as Error).message);\n return [];\n }\n}\n\nasync function processPageTypeFile(filePath: string, projectRoot: string): Promise<unknown[]> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const pageTypes: unknown[] = [];\n\n // Check if file contains @PageType decorator\n if (!content.includes('@PageType')) {\n return pageTypes;\n }\n\n try {\n // Create a ts-morph project and add the source file\n const project = new Project({\n useInMemoryFileSystem: true,\n skipAddingFilesFromTsConfig: true,\n });\n\n const sourceFile = project.createSourceFile(filePath, content);\n\n const classes = sourceFile.getClasses();\n\n for (const classDeclaration of classes) {\n const pageTypeDecorator = classDeclaration.getDecorator('PageType');\n if (!pageTypeDecorator) {\n continue;\n }\n\n const className = classDeclaration.getName();\n if (!className) {\n continue;\n }\n\n const pageTypeConfig = parseDecoratorArgs(pageTypeDecorator);\n\n const attributes = extractAttributesFromSource(sourceFile, className);\n const regionDefinitions = extractRegionDefinitionsFromSource(sourceFile, className);\n const route = filePathToRoute(filePath, projectRoot);\n\n const pageTypeMetadata = {\n typeId: pageTypeConfig.id || className.toLowerCase(),\n name: pageTypeConfig.name || toHumanReadableName(className),\n description: pageTypeConfig.description || `Custom page type: ${className}`,\n regionDefinitions,\n supportedAspectTypes: pageTypeConfig.supportedAspectTypes || [],\n attributes,\n route,\n };\n\n pageTypes.push(pageTypeMetadata);\n }\n } catch (error) {\n console.warn(`Warning: Could not process file ${filePath}:`, (error as Error).message);\n }\n\n return pageTypes;\n } catch (error) {\n console.warn(`Warning: Could not read file ${filePath}:`, (error as Error).message);\n return [];\n }\n}\n\nasync function processAspectFile(filePath: string, _projectRoot: string): Promise<unknown[]> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const aspects: unknown[] = [];\n\n // Check if file is a JSON aspect file\n if (!filePath.endsWith('.json') || !content.trim().startsWith('{')) {\n return aspects;\n }\n\n // Check if file is in the aspects directory\n if (!filePath.includes('/aspects/') && !filePath.includes('\\\\aspects\\\\')) {\n return aspects;\n }\n\n try {\n // Parse the JSON content\n const aspectData = JSON.parse(content);\n\n // Extract filename without extension as the aspect ID\n const fileName = basename(filePath, '.json');\n\n // Validate that it looks like an aspect file\n if (!aspectData.name || !aspectData.attribute_definitions) {\n return aspects;\n }\n\n const aspectMetadata = {\n id: fileName,\n name: aspectData.name,\n description: aspectData.description || `Aspect type: ${aspectData.name}`,\n attributeDefinitions: aspectData.attribute_definitions || [],\n supportedObjectTypes: aspectData.supported_object_types || [],\n };\n\n aspects.push(aspectMetadata);\n } catch (parseError) {\n console.warn(`Warning: Could not parse JSON in file ${filePath}:`, (parseError as Error).message);\n }\n\n return aspects;\n } catch (error) {\n console.warn(`Warning: Could not read file ${filePath}:`, (error as Error).message);\n return [];\n }\n}\n\nasync function generateComponentCartridge(\n component: Record<string, unknown>,\n outputDir: string,\n dryRun = false\n): Promise<void> {\n const fileName = toCamelCaseFileName(component.typeId as string);\n const groupDir = join(outputDir, component.group as string);\n const outputPath = join(groupDir, `${fileName}.json`);\n\n if (!dryRun) {\n // Ensure the group directory exists\n try {\n await mkdir(groupDir, { recursive: true });\n } catch {\n // Directory might already exist, which is fine\n }\n\n const attributeDefinitionGroups = [\n {\n id: component.typeId,\n name: component.name,\n description: component.description,\n attribute_definitions: component.attributes,\n },\n ];\n\n const cartridgeData = {\n name: component.name,\n description: component.description,\n group: component.group,\n arch_type: ARCH_TYPE_HEADLESS,\n region_definitions: component.regionDefinitions || [],\n attribute_definition_groups: attributeDefinitionGroups,\n };\n\n await writeFile(outputPath, JSON.stringify(cartridgeData, null, 2));\n }\n\n const prefix = dryRun ? ' - [DRY RUN]' : ' -';\n console.log(\n `${prefix} ${String(component.typeId)}: ${String(component.name)} (${String((component.attributes as unknown[]).length)} attributes) → ${fileName}.json`\n );\n}\n\nasync function generatePageTypeCartridge(\n pageType: Record<string, unknown>,\n outputDir: string,\n dryRun = false\n): Promise<void> {\n const fileName = toCamelCaseFileName(pageType.name as string);\n const outputPath = join(outputDir, `${fileName}.json`);\n\n if (!dryRun) {\n const cartridgeData: Record<string, unknown> = {\n name: pageType.name,\n description: pageType.description,\n arch_type: ARCH_TYPE_HEADLESS,\n region_definitions: pageType.regionDefinitions || [],\n };\n\n // Add attribute_definition_groups if there are attributes\n if (pageType.attributes && (pageType.attributes as unknown[]).length > 0) {\n const attributeDefinitionGroups = [\n {\n id: pageType.typeId || fileName,\n name: pageType.name,\n description: pageType.description,\n attribute_definitions: pageType.attributes,\n },\n ];\n cartridgeData.attribute_definition_groups = attributeDefinitionGroups;\n }\n\n // Add supported_aspect_types if specified\n if (pageType.supportedAspectTypes) {\n cartridgeData.supported_aspect_types = pageType.supportedAspectTypes;\n }\n\n if (pageType.route) {\n cartridgeData.route = pageType.route;\n }\n\n await writeFile(outputPath, JSON.stringify(cartridgeData, null, 2));\n }\n\n const prefix = dryRun ? ' - [DRY RUN]' : ' -';\n console.log(\n `${prefix} ${String(pageType.name)}: ${String(pageType.description)} (${String((pageType.attributes as unknown[]).length)} attributes) → ${fileName}.json`\n );\n}\n\nasync function generateAspectCartridge(\n aspect: Record<string, unknown>,\n outputDir: string,\n dryRun = false\n): Promise<void> {\n const fileName = toCamelCaseFileName(aspect.id as string);\n const outputPath = join(outputDir, `${fileName}.json`);\n\n if (!dryRun) {\n const cartridgeData: Record<string, unknown> = {\n name: aspect.name,\n description: aspect.description,\n arch_type: ARCH_TYPE_HEADLESS,\n attribute_definitions: aspect.attributeDefinitions || [],\n };\n\n // Add supported_object_types if specified\n if (aspect.supportedObjectTypes) {\n cartridgeData.supported_object_types = aspect.supportedObjectTypes;\n }\n\n await writeFile(outputPath, JSON.stringify(cartridgeData, null, 2));\n }\n\n const prefix = dryRun ? ' - [DRY RUN]' : ' -';\n console.log(\n `${prefix} ${String(aspect.name)}: ${String(aspect.description)} (${String((aspect.attributeDefinitions as unknown[]).length)} attributes) → ${fileName}.json`\n );\n}\n\n/**\n * Options for generateMetadata function\n */\nexport interface GenerateMetadataOptions {\n /**\n * Optional array of specific file paths to process.\n * If provided, only these files will be processed and existing cartridge files will NOT be deleted.\n * If omitted, the entire src/ directory will be scanned and all existing cartridge files will be deleted first.\n */\n filePaths?: string[];\n\n /**\n * Whether to run ESLint with --fix on generated JSON files to format them according to project settings.\n * Defaults to true.\n */\n lintFix?: boolean;\n\n /**\n * If true, scans files and reports what would be generated without actually writing any files or deleting directories.\n * Defaults to false.\n */\n dryRun?: boolean;\n}\n\n/**\n * Result returned by generateMetadata function\n */\nexport interface GenerateMetadataResult {\n componentsGenerated: number;\n pageTypesGenerated: number;\n aspectsGenerated: number;\n totalFiles: number;\n}\n\n/**\n * Runs ESLint with --fix on the specified directory to format JSON files.\n * This ensures generated JSON files match the project's Prettier/ESLint configuration.\n */\nfunction lintGeneratedFiles(metadataDir: string, projectRoot: string): void {\n try {\n console.log('🔧 Running ESLint --fix on generated JSON files...');\n\n // Run ESLint from the project root directory so it picks up the correct config\n // Use --no-error-on-unmatched-pattern to handle cases where no JSON files exist yet\n const command = `npx eslint \"${metadataDir}/**/*.json\" --fix --no-error-on-unmatched-pattern`;\n\n execSync(command, {\n cwd: projectRoot,\n stdio: 'pipe', // Suppress output unless there's an error\n encoding: 'utf-8',\n });\n\n console.log('✅ JSON files formatted successfully');\n } catch (error) {\n // ESLint returns non-zero exit code even when --fix resolves all issues\n // We only warn if there are actual unfixable issues\n const execError = error as { status?: number; stderr?: string; stdout?: string };\n\n // Exit code 1 usually means there were linting issues (some may have been fixed)\n // Exit code 2 means configuration error or other fatal error\n if (execError.status === 2) {\n const errMsg = execError.stderr || execError.stdout || 'Unknown error';\n console.warn(`⚠️ Warning: Could not run ESLint --fix: ${errMsg}`);\n } else if (execError.stderr && execError.stderr.includes('error')) {\n console.warn(`⚠️ Warning: Some linting issues could not be auto-fixed. Run ESLint manually to review.`);\n } else {\n // Exit code 1 with no errors in stderr usually means all issues were fixed\n console.log('✅ JSON files formatted successfully');\n }\n }\n}\n\n// Main function\nexport async function generateMetadata(\n projectDirectory: string,\n metadataDirectory: string,\n options?: GenerateMetadataOptions\n): Promise<GenerateMetadataResult> {\n try {\n const filePaths = options?.filePaths;\n const isIncrementalMode = filePaths && filePaths.length > 0;\n const dryRun = options?.dryRun || false;\n\n if (dryRun) {\n console.log('🔍 [DRY RUN] Scanning for decorated components and page types...');\n } else if (isIncrementalMode) {\n console.log(`🔍 Generating metadata for ${filePaths.length} specified file(s)...`);\n } else {\n console.log('🔍 Generating metadata for decorated components and page types...');\n }\n\n const projectRoot = resolve(projectDirectory);\n const srcDir = join(projectRoot, 'src');\n const metadataDir = resolve(metadataDirectory);\n const componentsOutputDir = join(metadataDir, 'components');\n const pagesOutputDir = join(metadataDir, 'pages');\n const aspectsOutputDir = join(metadataDir, 'aspects');\n\n // Skip directory operations in dry run mode\n if (!dryRun) {\n // Only delete existing directories in full scan mode (not incremental)\n if (!isIncrementalMode) {\n console.log('🗑️ Cleaning existing output directories...');\n for (const outputDir of [componentsOutputDir, pagesOutputDir, aspectsOutputDir]) {\n try {\n await rm(outputDir, { recursive: true, force: true });\n console.log(` - Deleted: ${outputDir}`);\n } catch {\n // Directory might not exist, which is fine\n console.log(` - Directory not found (skipping): ${outputDir}`);\n }\n }\n } else {\n console.log('📝 Incremental mode: existing cartridge files will be preserved/overwritten');\n }\n\n // Create output directories if they don't exist\n console.log('📁 Creating output directories...');\n for (const outputDir of [componentsOutputDir, pagesOutputDir, aspectsOutputDir]) {\n try {\n await mkdir(outputDir, { recursive: true });\n } catch (error) {\n try {\n await access(outputDir);\n // Directory exists, that's fine\n } catch {\n console.error(\n `❌ Error: Failed to create output directory ${outputDir}: ${(error as Error).message}`\n );\n process.exit(1);\n }\n }\n }\n } else if (isIncrementalMode) {\n console.log(`📝 [DRY RUN] Would process ${filePaths.length} specific file(s)`);\n } else {\n console.log('📝 [DRY RUN] Would clean and regenerate all metadata files');\n }\n\n let files: string[] = [];\n\n if (isIncrementalMode && filePaths) {\n // Use the specified file paths (resolve them relative to project root)\n files = filePaths.map((fp) => resolve(projectRoot, fp));\n console.log(`📂 Processing ${files.length} specified file(s)...`);\n } else {\n // Full scan mode: scan entire src directory\n const scanDirectory = async (dir: string): Promise<void> => {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (!SKIP_DIRECTORIES.includes(entry.name)) {\n await scanDirectory(fullPath);\n }\n } else if (\n entry.isFile() &&\n (extname(entry.name) === '.ts' ||\n extname(entry.name) === '.tsx' ||\n extname(entry.name) === '.json')\n ) {\n files.push(fullPath);\n }\n }\n };\n\n await scanDirectory(srcDir);\n }\n\n // Process each file for both components and page types\n const allComponents: unknown[] = [];\n const allPageTypes: unknown[] = [];\n const allAspects: unknown[] = [];\n\n for (const file of files) {\n const components = await processComponentFile(file, projectRoot);\n allComponents.push(...components);\n\n const pageTypes = await processPageTypeFile(file, projectRoot);\n allPageTypes.push(...pageTypes);\n\n const aspects = await processAspectFile(file, projectRoot);\n allAspects.push(...aspects);\n }\n\n if (allComponents.length === 0 && allPageTypes.length === 0 && allAspects.length === 0) {\n console.log('⚠️ No decorated components, page types, or aspect files found.');\n return {\n componentsGenerated: 0,\n pageTypesGenerated: 0,\n aspectsGenerated: 0,\n totalFiles: 0,\n };\n }\n\n // Generate component cartridge files\n if (allComponents.length > 0) {\n console.log(`✅ Found ${allComponents.length} decorated component(s):`);\n for (const component of allComponents) {\n await generateComponentCartridge(component as Record<string, unknown>, componentsOutputDir, dryRun);\n }\n if (dryRun) {\n console.log(\n `📄 [DRY RUN] Would generate ${allComponents.length} component metadata file(s) in: ${componentsOutputDir}`\n );\n } else {\n console.log(\n `📄 Generated ${allComponents.length} component metadata file(s) in: ${componentsOutputDir}`\n );\n }\n }\n\n // Generate page type cartridge files\n if (allPageTypes.length > 0) {\n console.log(`✅ Found ${allPageTypes.length} decorated page type(s):`);\n for (const pageType of allPageTypes) {\n await generatePageTypeCartridge(pageType as Record<string, unknown>, pagesOutputDir, dryRun);\n }\n if (dryRun) {\n console.log(\n `📄 [DRY RUN] Would generate ${allPageTypes.length} page type metadata file(s) in: ${pagesOutputDir}`\n );\n } else {\n console.log(`📄 Generated ${allPageTypes.length} page type metadata file(s) in: ${pagesOutputDir}`);\n }\n }\n\n if (allAspects.length > 0) {\n console.log(`✅ Found ${allAspects.length} decorated aspect(s):`);\n for (const aspect of allAspects) {\n await generateAspectCartridge(aspect as Record<string, unknown>, aspectsOutputDir, dryRun);\n }\n if (dryRun) {\n console.log(\n `📄 [DRY RUN] Would generate ${allAspects.length} aspect metadata file(s) in: ${aspectsOutputDir}`\n );\n } else {\n console.log(`📄 Generated ${allAspects.length} aspect metadata file(s) in: ${aspectsOutputDir}`);\n }\n }\n\n // Run ESLint --fix to format generated JSON files according to project settings\n const shouldLintFix = options?.lintFix !== false; // Default to true\n if (\n !dryRun &&\n shouldLintFix &&\n (allComponents.length > 0 || allPageTypes.length > 0 || allAspects.length > 0)\n ) {\n lintGeneratedFiles(metadataDir, projectRoot);\n }\n\n // Return statistics\n return {\n componentsGenerated: allComponents.length,\n pageTypesGenerated: allPageTypes.length,\n aspectsGenerated: allAspects.length,\n totalFiles: allComponents.length + allPageTypes.length + allAspects.length,\n };\n } catch (error) {\n console.error('❌ Error:', (error as Error).message);\n process.exit(1);\n }\n}\n"],"mappings":"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"}
|