@salesforce/storefront-next-dev 1.4.0 → 1.4.1-alpha.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.
Files changed (137) hide show
  1. package/README.md +13 -2
  2. package/dist/assets.js +25 -0
  3. package/dist/audit/catalog/categories/best-practices/CATEGORY.md +21 -0
  4. package/dist/audit/catalog/categories/best-practices/rules/commerce-api-client.md +20 -0
  5. package/dist/audit/catalog/categories/best-practices/rules/error-boundaries.md +19 -0
  6. package/dist/audit/catalog/categories/best-practices/rules/production-integrations.md +20 -0
  7. package/dist/audit/catalog/categories/best-practices/rules/route-revalidation.md +19 -0
  8. package/dist/audit/catalog/categories/best-practices/rules/server-data-loading.md +19 -0
  9. package/dist/audit/catalog/categories/performance/CATEGORY.md +18 -0
  10. package/dist/audit/catalog/categories/performance/rules/app-config-size.js +15 -0
  11. package/dist/audit/catalog/categories/performance/rules/app-config-size.md +22 -0
  12. package/dist/audit/catalog/categories/performance/rules/cache-control.js +59 -0
  13. package/dist/audit/catalog/categories/performance/rules/cache-control.md +20 -0
  14. package/dist/audit/catalog/categories/performance/rules/cls-budget.js +13 -0
  15. package/dist/audit/catalog/categories/performance/rules/cls-budget.md +20 -0
  16. package/dist/audit/catalog/categories/performance/rules/content-encoding.js +35 -0
  17. package/dist/audit/catalog/categories/performance/rules/content-encoding.md +20 -0
  18. package/dist/audit/catalog/categories/performance/rules/dom-size.js +18 -0
  19. package/dist/audit/catalog/categories/performance/rules/dom-size.md +22 -0
  20. package/dist/audit/catalog/categories/performance/rules/html-document-size.js +15 -0
  21. package/dist/audit/catalog/categories/performance/rules/html-document-size.md +20 -0
  22. package/dist/audit/catalog/categories/performance/rules/image-dimensions.js +141 -0
  23. package/dist/audit/catalog/categories/performance/rules/image-dimensions.md +22 -0
  24. package/dist/audit/catalog/categories/performance/rules/image-transformation.js +117 -0
  25. package/dist/audit/catalog/categories/performance/rules/image-transformation.md +22 -0
  26. package/dist/audit/catalog/categories/performance/rules/javascript-size.js +126 -0
  27. package/dist/audit/catalog/categories/performance/rules/javascript-size.md +20 -0
  28. package/dist/audit/catalog/categories/performance/rules/large-images.js +187 -0
  29. package/dist/audit/catalog/categories/performance/rules/large-images.md +22 -0
  30. package/dist/audit/catalog/categories/performance/rules/lcp-budget.js +13 -0
  31. package/dist/audit/catalog/categories/performance/rules/lcp-budget.md +20 -0
  32. package/dist/audit/catalog/categories/performance/rules/lcp-image-priority.js +227 -0
  33. package/dist/audit/catalog/categories/performance/rules/lcp-image-priority.md +27 -0
  34. package/dist/audit/catalog/categories/performance/rules/page-data-size.js +15 -0
  35. package/dist/audit/catalog/categories/performance/rules/page-data-size.md +22 -0
  36. package/dist/audit/catalog/categories/performance/rules/reduce-javascript-on-load.js +97 -0
  37. package/dist/audit/catalog/categories/performance/rules/reduce-javascript-on-load.md +73 -0
  38. package/dist/audit/catalog/categories/performance/rules/render-blocking-font-size.js +201 -0
  39. package/dist/audit/catalog/categories/performance/rules/render-blocking-font-size.md +20 -0
  40. package/dist/audit/catalog/categories/performance/rules/server-request-waterfall.md +85 -0
  41. package/dist/audit/catalog/categories/performance/rules/tbt-budget.js +13 -0
  42. package/dist/audit/catalog/categories/performance/rules/tbt-budget.md +20 -0
  43. package/dist/audit/catalog/categories/performance/rules/third-party-fonts.js +265 -0
  44. package/dist/audit/catalog/categories/performance/rules/third-party-fonts.md +20 -0
  45. package/dist/audit/catalog/categories/performance/rules/ttfb-budget.js +17 -0
  46. package/dist/audit/catalog/categories/performance/rules/ttfb-budget.md +20 -0
  47. package/dist/audit/catalog/categories/security/CATEGORY.md +17 -0
  48. package/dist/audit/catalog/categories/security/rules/browser-hardening-headers.js +37 -0
  49. package/dist/audit/catalog/categories/security/rules/browser-hardening-headers.md +20 -0
  50. package/dist/audit/catalog/categories/security/rules/content-security-policy.js +40 -0
  51. package/dist/audit/catalog/categories/security/rules/content-security-policy.md +20 -0
  52. package/dist/audit/catalog/categories/security/rules/csp-runtime-compatibility.md +24 -0
  53. package/dist/audit/catalog/categories/security/rules/dependency-vulnerabilities.md +19 -0
  54. package/dist/audit/catalog/categories/security/rules/dynamic-code-execution.md +19 -0
  55. package/dist/audit/catalog/categories/security/rules/hardcoded-credentials.md +19 -0
  56. package/dist/audit/catalog/categories/security/rules/html-config-secrets.js +46 -0
  57. package/dist/audit/catalog/categories/security/rules/html-config-secrets.md +22 -0
  58. package/dist/audit/catalog/categories/security/rules/protected-route-authorization.md +19 -0
  59. package/dist/audit/catalog/categories/security/rules/public-client-secrets.md +20 -0
  60. package/dist/audit/catalog/categories/security/rules/raw-html-sinks.md +20 -0
  61. package/dist/audit/catalog/categories/security/rules/strict-transport-security.js +183 -0
  62. package/dist/audit/catalog/categories/security/rules/strict-transport-security.md +24 -0
  63. package/dist/audit/catalog/categories/seo/CATEGORY.md +17 -0
  64. package/dist/audit/catalog/categories/seo/rules/canonical-url.md +20 -0
  65. package/dist/audit/catalog/categories/seo/rules/locale-alternates.md +19 -0
  66. package/dist/audit/catalog/categories/seo/rules/meta-description.md +19 -0
  67. package/dist/audit/catalog/categories/seo/rules/page-title.md +19 -0
  68. package/dist/audit/catalog/categories/seo/rules/robots-indexing.md +20 -0
  69. package/dist/audit/catalog/categories/seo/rules/structured-data.md +19 -0
  70. package/dist/audit/instructions/evaluate-rule.md +42 -0
  71. package/dist/audit/schemas/audit-result.schema.json +1237 -0
  72. package/dist/audit/ui/assets/banner-brand-default-CDRB3_S0.png +0 -0
  73. package/dist/audit/ui/assets/banner-group-public-default-BGRJzVY8.png +0 -0
  74. package/dist/audit/ui/assets/banner-user-default-BW81Ba3z.png +0 -0
  75. package/dist/audit/ui/assets/bg-info@2x-CzpP0vaE.png +0 -0
  76. package/dist/audit/ui/assets/einstein-figure-B1xFOo8O.svg +1 -0
  77. package/dist/audit/ui/assets/group_avatar_160-CrxVHlYA.png +0 -0
  78. package/dist/audit/ui/assets/group_avatar_200-CSLS8I0r.png +0 -0
  79. package/dist/audit/ui/assets/index-BKCWlO9b.css +1 -0
  80. package/dist/audit/ui/assets/index-CUPt8e4A.js +44 -0
  81. package/dist/audit/ui/assets/profile_avatar_160-CtQTUBo0.png +0 -0
  82. package/dist/audit/ui/assets/profile_avatar_200-DAztxshF.png +0 -0
  83. package/dist/audit/ui/assets/symbols-BRmpfDbO.svg +1 -0
  84. package/dist/audit/ui/assets/symbols-D0Ne6690.svg +1 -0
  85. package/dist/audit/ui/einstein.png +0 -0
  86. package/dist/audit/ui/index.html +13 -0
  87. package/dist/audit.js +1896 -0
  88. package/dist/cartridge-services/index.d.ts +5 -0
  89. package/dist/cartridge-services/index.d.ts.map +1 -1
  90. package/dist/cartridge-services/index.js +94 -7
  91. package/dist/cartridge-services/index.js.map +1 -1
  92. package/dist/catalog.js +89 -0
  93. package/dist/commands/audit/catalog.js +27 -0
  94. package/dist/commands/audit/finalize.js +42 -0
  95. package/dist/commands/audit/prepare.js +84 -0
  96. package/dist/commands/audit/progress.js +51 -0
  97. package/dist/commands/audit/serve.js +63 -0
  98. package/dist/commands/config/aggregate-extensions.js +1 -0
  99. package/dist/commands/create-bundle.js +2 -1
  100. package/dist/commands/create-instructions.js +1 -0
  101. package/dist/commands/create-storefront.js +1 -0
  102. package/dist/commands/deploy-cartridge.js +11 -3
  103. package/dist/commands/dev.js +2 -0
  104. package/dist/commands/extensions/create.js +1 -0
  105. package/dist/commands/extensions/install.js +1 -0
  106. package/dist/commands/extensions/list.js +1 -0
  107. package/dist/commands/extensions/remove.js +1 -0
  108. package/dist/commands/generate-cartridge.js +49 -5
  109. package/dist/commands/locales/aggregate-extensions.js +1 -0
  110. package/dist/commands/prepare-local.js +1 -0
  111. package/dist/commands/preview.js +2 -0
  112. package/dist/commands/push.js +3 -1
  113. package/dist/commands/validate-cartridge.js +13 -4
  114. package/dist/config.js +13 -2
  115. package/dist/document-size.js +231 -0
  116. package/dist/events.js +688 -0
  117. package/dist/flags2.js +34 -0
  118. package/dist/generate-cartridge.js +41 -6
  119. package/dist/har.js +205 -0
  120. package/dist/images.js +146 -0
  121. package/dist/index.js +3 -3
  122. package/dist/index.js.map +1 -1
  123. package/dist/live.js +277 -0
  124. package/dist/logger.js +1 -4
  125. package/dist/measured-run.js +91 -0
  126. package/dist/metric-budget.js +125 -0
  127. package/dist/mrt/ssr.mjs +19 -19
  128. package/dist/mrt/ssr.mjs.map +1 -1
  129. package/dist/mrt/streamingHandler.mjs +26 -26
  130. package/dist/mrt/streamingHandler.mjs.map +1 -1
  131. package/dist/package.js +5 -0
  132. package/dist/sanitize.js +185 -0
  133. package/dist/schema.js +121 -0
  134. package/dist/server.js +8 -60
  135. package/dist/server2.js +185 -0
  136. package/dist/ts-import.js +57 -0
  137. package/package.json +22 -5
@@ -20,6 +20,11 @@ interface GenerateMetadataOptions {
20
20
  * Defaults to false.
21
21
  */
22
22
  dryRun?: boolean;
23
+ /**
24
+ * Site ID used for site-specific configuration, such as SEO route prefixes.
25
+ * Defaults to `app.defaultSiteId` in the project config.
26
+ */
27
+ siteId?: string;
23
28
  }
24
29
  /**
25
30
  * Result returned by generateMetadata function
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/cartridge-services/generate-cartridge.ts"],"sourcesContent":[],"mappings":";;AAk8BA;;;AAIG,UA/Cc,uBAAA,CA+Cd;EAAO;;;;;;;;;;;;;;;;;;;;UAvBO,sBAAA;;;;;;iBAmBK,gBAAA,gEAGR,0BACX,QAAQ"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/cartridge-services/generate-cartridge.ts"],"sourcesContent":[],"mappings":";;AA8+BA;;;AAIG,UArFc,uBAAA,CAqFd;EAAO;;;;;;;;;;;;;;;;;;;;;;;;;UAvDO,sBAAA;;;;;;iBAmDK,gBAAA,gEAGR,0BACX,QAAQ"}
@@ -1,7 +1,7 @@
1
1
  import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
2
+ import { existsSync, readFileSync, unlinkSync } from "node:fs";
2
3
  import { basename, extname, join, resolve } from "node:path";
3
4
  import { Node, Project } from "ts-morph";
4
- import { existsSync, readFileSync, unlinkSync } from "node:fs";
5
5
  import { execSync } from "node:child_process";
6
6
  import { tmpdir } from "node:os";
7
7
  import { randomUUID } from "node:crypto";
@@ -101,6 +101,14 @@ const logger = {
101
101
 
102
102
  //#endregion
103
103
  //#region src/cartridge-services/react-router-config.ts
104
+ const DEFAULT_PAGE_DESIGNER_PREFIXES = {
105
+ product: "p",
106
+ category: "c"
107
+ };
108
+ const PAGE_DESIGNER_PARAMETER_NAMES = {
109
+ product: "productId",
110
+ category: "categoryId"
111
+ };
104
112
  let isCliAvailable = null;
105
113
  function filePathEndsWithRoute(filePath, routeFile) {
106
114
  const routeFileNormalized = routeFile.replace(/^\.\//, "");
@@ -169,18 +177,26 @@ function getReactRouterRoutes(projectDirectory) {
169
177
  }
170
178
  /**
171
179
  * Convert a file path to its corresponding route path using React Router's CLI.
172
- * This ensures we get the exact same route resolution as React Router uses internally.
180
+ * PDP/PLP paths use configured prefixes and named ID parameters for Page Designer.
173
181
  * @param filePath - Absolute path to the route file
174
182
  * @param projectRoot - The project root directory
175
- * @returns The route path (e.g., '/cart', '/p/*')
183
+ * @param seoPrefixes - Optional selected site's PDP/PLP prefixes. Defaults to p/c.
184
+ * @returns The computed Page Designer route path (e.g., '/cart', '/p/:productId')
176
185
  * @example
177
186
  * const route = filePathToRoute('/path/to/project/src/routes/_app.cart.tsx', '/path/to/project');
178
187
  * // Returns: '/cart'
179
188
  */
180
- function filePathToRoute(filePath, projectRoot) {
189
+ function filePathToRoute(filePath, projectRoot, seoPrefixes) {
181
190
  const filePathPosix = filePath.replace(/\\/g, "/");
182
191
  const canonicalRoutes = flattenRoutes(getReactRouterRoutes(projectRoot)).filter((route) => !route.id.endsWith("--root-duplicate"));
183
192
  for (const route of canonicalRoutes) if (routeFileMatches(filePathPosix, route.file.replace(/\\/g, "/"))) {
193
+ const routeFileName = route.id.split("/").at(-1);
194
+ const seoResource = routeFileName === "_app.p.$" ? "product" : routeFileName === "_app.c.$" ? "category" : void 0;
195
+ if (seoResource) {
196
+ const configuredPrefix = seoPrefixes?.[seoResource] ?? DEFAULT_PAGE_DESIGNER_PREFIXES[seoResource];
197
+ const pathSegments = (route.path ?? "").split("/").filter(Boolean);
198
+ return `${`/${(pathSegments.at(-1) === "*" ? pathSegments.slice(0, -2) : pathSegments).join("/")}`.replace(/\/$/, "")}/${configuredPrefix}/:${PAGE_DESIGNER_PARAMETER_NAMES[seoResource]}`.replace(/\/+/g, "/");
199
+ }
184
200
  const seoAliases = canonicalRoutes.filter((candidate) => candidate.id.startsWith(`${route.id}--seo-alias--`)).sort((left, right) => left.path.localeCompare(right.path));
185
201
  if (seoAliases.length > 0) {
186
202
  if (seoAliases.length > 1) logger.warn(`Multiple SEO route aliases match ${filePath}; using ${seoAliases[0].path} for Page Designer metadata.`);
@@ -274,6 +290,59 @@ function resolveBiomeBin(filePath) {
274
290
  return null;
275
291
  }
276
292
 
293
+ //#endregion
294
+ //#region src/server/ts-import.ts
295
+ /**
296
+ * Parse TypeScript paths from tsconfig.json and convert to jiti alias format.
297
+ *
298
+ * @param tsconfigPath - Path to tsconfig.json
299
+ * @param projectDirectory - Project root directory for resolving relative paths
300
+ * @returns Record of alias mappings for jiti
301
+ *
302
+ * @example
303
+ * // tsconfig.json: { "compilerOptions": { "paths": { "@/*": ["./src/*"] } } }
304
+ * // Returns: { "@/": "/absolute/path/to/src/" }
305
+ */
306
+ function parseTsconfigPaths(tsconfigPath, projectDirectory) {
307
+ const alias = {};
308
+ if (!existsSync(tsconfigPath)) return alias;
309
+ try {
310
+ const tsconfigContent = readFileSync(tsconfigPath, "utf-8");
311
+ const tsconfig = JSON.parse(tsconfigContent);
312
+ const paths = tsconfig.compilerOptions?.paths;
313
+ const baseUrl = tsconfig.compilerOptions?.baseUrl || ".";
314
+ if (paths) {
315
+ for (const [key, values] of Object.entries(paths)) if (values && values.length > 0) {
316
+ const aliasKey = key.replace(/\/\*$/, "/");
317
+ alias[aliasKey] = resolve(projectDirectory, baseUrl, values[0].replace(/\/\*$/, "/").replace(/^\.\//, ""));
318
+ }
319
+ }
320
+ } catch {}
321
+ const sortedAlias = {};
322
+ Object.keys(alias).sort((a, b) => b.length - a.length).forEach((key) => {
323
+ sortedAlias[key] = alias[key];
324
+ });
325
+ return sortedAlias;
326
+ }
327
+ /**
328
+ * Import a TypeScript file using jiti with proper path alias resolution.
329
+ * This is a cross-platform alternative to tsx that works on Windows.
330
+ *
331
+ * @param filePath - Absolute path to the TypeScript file to import
332
+ * @param options - Import options including project directory
333
+ * @returns The imported module
334
+ */
335
+ async function importTypescript(filePath, options) {
336
+ const { projectDirectory, tsconfigPath = resolve(projectDirectory, "tsconfig.json") } = options;
337
+ const { createJiti } = await import("jiti");
338
+ const alias = parseTsconfigPaths(tsconfigPath, projectDirectory);
339
+ return createJiti(import.meta.url, {
340
+ fsCache: false,
341
+ interopDefault: true,
342
+ alias
343
+ }).import(filePath);
344
+ }
345
+
277
346
  //#endregion
278
347
  //#region src/cartridge-services/generate-cartridge.ts
279
348
  const SKIP_DIRECTORIES = [
@@ -629,7 +698,7 @@ async function processComponentFile(filePath, _projectRoot) {
629
698
  return [];
630
699
  }
631
700
  }
632
- async function processPageTypeFile(filePath, projectRoot) {
701
+ async function processPageTypeFile(filePath, projectRoot, seoPrefixes) {
633
702
  try {
634
703
  const content = await readFile(filePath, "utf-8");
635
704
  const pageTypes = [];
@@ -648,7 +717,7 @@ async function processPageTypeFile(filePath, projectRoot) {
648
717
  const pageTypeConfig = parseDecoratorArgs(pageTypeDecorator);
649
718
  const attributes = extractAttributesFromSource(sourceFile, className);
650
719
  const regionDefinitions = extractRegionDefinitionsFromSource(sourceFile, className);
651
- const route = filePathToRoute(filePath, projectRoot);
720
+ const route = filePathToRoute(filePath, projectRoot, seoPrefixes);
652
721
  const pageTypeMetadata = {
653
722
  typeId: pageTypeConfig.id || className.toLowerCase(),
654
723
  name: pageTypeConfig.name || toHumanReadableName(className),
@@ -776,6 +845,23 @@ function lintGeneratedFiles(metadataDir) {
776
845
  logger.debug("🔧 Formatting generated JSON files with Biome...");
777
846
  formatDirectoryWithProjectBiome(metadataDir);
778
847
  }
848
+ function isCanonicalProductOrCategoryRouteFile(filePath, projectRoot) {
849
+ const relativePath = filePath.slice(projectRoot.length).replace(/\\/g, "/").replace(/^\//, "");
850
+ return /^src\/(?:.*\/)?routes\/_app\.(?:p|c)\.\$\.tsx$/.test(relativePath);
851
+ }
852
+ async function loadPageDesignerSeoPrefixes(projectRoot, requestedSiteId) {
853
+ const configPath = join(projectRoot, "config.server.ts");
854
+ if (existsSync(configPath) !== true) return void 0;
855
+ const app = (await importTypescript(configPath, { projectDirectory: projectRoot })).default?.app;
856
+ const seoRoutes = (app?.url)?.seoRoutes;
857
+ if (seoRoutes === void 0) return void 0;
858
+ const siteId = requestedSiteId ?? (typeof app?.defaultSiteId === "string" ? app.defaultSiteId : void 0);
859
+ const selected = siteId && Object.prototype.hasOwnProperty.call(seoRoutes, siteId) ? seoRoutes[siteId] : seoRoutes["*"];
860
+ return {
861
+ product: selected?.product?.prefix,
862
+ category: selected?.category?.prefix
863
+ };
864
+ }
779
865
  async function generateMetadata(projectDirectory, metadataDirectory, options) {
780
866
  try {
781
867
  const filePaths = options?.filePaths;
@@ -850,13 +936,14 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
850
936
  else logger.warn(` - Unable to access ${configMetadataDir}:`, error.message);
851
937
  }
852
938
  }
939
+ const seoPrefixes = files.some((file) => isCanonicalProductOrCategoryRouteFile(file, projectRoot)) ? await loadPageDesignerSeoPrefixes(projectRoot, options?.siteId) : void 0;
853
940
  const allComponents = [];
854
941
  const allPageTypes = [];
855
942
  const allAspects = [];
856
943
  for (const file of files) {
857
944
  const components = await processComponentFile(file, projectRoot);
858
945
  allComponents.push(...components);
859
- const pageTypes = await processPageTypeFile(file, projectRoot);
946
+ const pageTypes = await processPageTypeFile(file, projectRoot, seoPrefixes);
860
947
  allPageTypes.push(...pageTypes);
861
948
  const aspects = await processAspectFile(file, projectRoot);
862
949
  allAspects.push(...aspects);