@salesforce/storefront-next-dev 0.2.0-alpha.2 → 0.3.0-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 (49) hide show
  1. package/dist/cartridge-services/index.d.ts.map +1 -1
  2. package/dist/cartridge-services/index.js +171 -50
  3. package/dist/cartridge-services/index.js.map +1 -1
  4. package/dist/commands/create-bundle.js +12 -11
  5. package/dist/commands/create-instructions.js +7 -5
  6. package/dist/commands/create-storefront.js +18 -22
  7. package/dist/commands/deploy-cartridge.js +67 -26
  8. package/dist/commands/dev.js +6 -4
  9. package/dist/commands/extensions/create.js +2 -0
  10. package/dist/commands/extensions/install.js +3 -7
  11. package/dist/commands/extensions/list.js +2 -0
  12. package/dist/commands/extensions/remove.js +3 -7
  13. package/dist/commands/generate-cartridge.js +23 -2
  14. package/dist/commands/preview.js +15 -10
  15. package/dist/commands/push.js +25 -19
  16. package/dist/commands/validate-cartridge.js +51 -0
  17. package/dist/config.js +74 -47
  18. package/dist/configs/react-router.config.d.ts.map +1 -1
  19. package/dist/configs/react-router.config.js +36 -0
  20. package/dist/configs/react-router.config.js.map +1 -1
  21. package/dist/dependency-utils.js +14 -16
  22. package/dist/entry/server.d.ts.map +1 -1
  23. package/dist/entry/server.js +221 -11
  24. package/dist/entry/server.js.map +1 -1
  25. package/dist/generate-cartridge.js +106 -50
  26. package/dist/index.d.ts +127 -13
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1147 -167
  29. package/dist/index.js.map +1 -1
  30. package/dist/local-dev-setup.js +13 -13
  31. package/dist/logger/index.d.ts +20 -0
  32. package/dist/logger/index.d.ts.map +1 -0
  33. package/dist/logger/index.js +69 -0
  34. package/dist/logger/index.js.map +1 -0
  35. package/dist/logger.js +79 -33
  36. package/dist/logger2.js +1 -0
  37. package/dist/manage-extensions.js +7 -13
  38. package/dist/mrt/ssr.mjs +60 -72
  39. package/dist/mrt/ssr.mjs.map +1 -1
  40. package/dist/mrt/streamingHandler.mjs +66 -78
  41. package/dist/mrt/streamingHandler.mjs.map +1 -1
  42. package/dist/react-router/Scripts.d.ts +1 -1
  43. package/dist/react-router/Scripts.d.ts.map +1 -1
  44. package/dist/react-router/Scripts.js +38 -2
  45. package/dist/react-router/Scripts.js.map +1 -1
  46. package/dist/server.js +296 -16
  47. package/dist/utils.js +4 -4
  48. package/dist/validate-cartridge.js +45 -0
  49. package/package.json +22 -5
@@ -1,3 +1,4 @@
1
+ import { t as logger } from "./logger.js";
1
2
  import { existsSync, readFileSync, unlinkSync } from "node:fs";
2
3
  import { basename, extname, join, resolve } from "node:path";
3
4
  import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
@@ -76,7 +77,7 @@ function filePathToRoute(filePath, projectRoot) {
76
77
  const routeFileNormalized = routeFilePosix.replace(/^\.\//, "");
77
78
  if (filePathPosix.endsWith(routeFileNormalized) || filePathPosix.endsWith(`/${routeFileNormalized}`)) return route.path;
78
79
  }
79
- console.warn(`Warning: Could not find route for file: ${filePath}`);
80
+ logger.warn(`Could not find route for file: ${filePath}`);
80
81
  return "/unknown";
81
82
  }
82
83
  /**
@@ -151,7 +152,7 @@ const TYPE_MAPPING = {
151
152
  function resolveAttributeType(decoratorType, tsMorphType, fieldName) {
152
153
  if (decoratorType) {
153
154
  if (!VALID_ATTRIBUTE_TYPES.includes(decoratorType)) {
154
- console.error(`Error: Invalid attribute type '${decoratorType}' for field '${fieldName || "unknown"}'. Valid types are: ${VALID_ATTRIBUTE_TYPES.join(", ")}`);
155
+ logger.error(`Invalid attribute type '${decoratorType}' for field '${fieldName || "unknown"}'. Valid types are: ${VALID_ATTRIBUTE_TYPES.join(", ")}`);
155
156
  process.exit(1);
156
157
  }
157
158
  return decoratorType;
@@ -176,6 +177,30 @@ function getTypeFromTsMorph(property, _sourceFile) {
176
177
  } catch {}
177
178
  return "string";
178
179
  }
180
+ /**
181
+ * Resolve a variable's initializer expression from the same source file,
182
+ * unwrapping `as const` type assertions.
183
+ */
184
+ function resolveVariableInitializer(sourceFile, name) {
185
+ const varDecl = sourceFile.getVariableDeclaration(name);
186
+ if (!varDecl) return void 0;
187
+ let initializer = varDecl.getInitializer();
188
+ if (initializer && Node.isAsExpression(initializer)) initializer = initializer.getExpression();
189
+ return initializer;
190
+ }
191
+ /**
192
+ * Check whether an AST node is a type that `parseExpression` can resolve to a
193
+ * concrete JS value (as opposed to falling through to `getText()`).
194
+ */
195
+ function isResolvableLiteral(node) {
196
+ return Node.isStringLiteral(node) || Node.isNumericLiteral(node) || Node.isTrueLiteral(node) || Node.isFalseLiteral(node) || Node.isObjectLiteralExpression(node) || Node.isArrayLiteralExpression(node);
197
+ }
198
+ var UnresolvedConstantReferenceError = class extends Error {
199
+ constructor(reference) {
200
+ super(`Cannot resolve constant reference '${reference}'. Ensure the variable is declared in the same file as a literal value.`);
201
+ this.name = "UnresolvedConstantReferenceError";
202
+ }
203
+ };
179
204
  function parseExpression(expression) {
180
205
  if (Node.isStringLiteral(expression)) return expression.getLiteralValue();
181
206
  else if (Node.isNumericLiteral(expression)) return expression.getLiteralValue();
@@ -183,7 +208,26 @@ function parseExpression(expression) {
183
208
  else if (Node.isFalseLiteral(expression)) return false;
184
209
  else if (Node.isObjectLiteralExpression(expression)) return parseNestedObject(expression);
185
210
  else if (Node.isArrayLiteralExpression(expression)) return parseArrayLiteral(expression);
186
- else return expression.getText();
211
+ else if (Node.isPropertyAccessExpression(expression)) {
212
+ const obj = expression.getExpression();
213
+ const propName = expression.getName();
214
+ if (Node.isIdentifier(obj)) {
215
+ const resolved = resolveVariableInitializer(expression.getSourceFile(), obj.getText());
216
+ if (resolved && Node.isObjectLiteralExpression(resolved)) {
217
+ const prop = resolved.getProperty(propName);
218
+ if (prop && Node.isPropertyAssignment(prop)) {
219
+ const propInit = prop.getInitializer();
220
+ if (propInit) return parseExpression(propInit);
221
+ }
222
+ }
223
+ throw new UnresolvedConstantReferenceError(expression.getText());
224
+ }
225
+ return expression.getText();
226
+ } else if (Node.isIdentifier(expression)) {
227
+ const resolved = resolveVariableInitializer(expression.getSourceFile(), expression.getText());
228
+ if (resolved && isResolvableLiteral(resolved)) return parseExpression(resolved);
229
+ return expression.getText();
230
+ } else return expression.getText();
187
231
  }
188
232
  function parseNestedObject(objectLiteral) {
189
233
  const result = {};
@@ -195,7 +239,7 @@ function parseNestedObject(objectLiteral) {
195
239
  if (initializer) result[name] = parseExpression(initializer);
196
240
  }
197
241
  } catch (error) {
198
- console.warn(`Warning: Could not parse nested object: ${error.message}`);
242
+ logger.warn(`Could not parse nested object: ${error.message}`);
199
243
  return result;
200
244
  }
201
245
  return result;
@@ -206,7 +250,7 @@ function parseArrayLiteral(arrayLiteral) {
206
250
  const elements = arrayLiteral.getElements();
207
251
  for (const element of elements) result.push(parseExpression(element));
208
252
  } catch (error) {
209
- console.warn(`Warning: Could not parse array literal: ${error.message}`);
253
+ logger.warn(`Could not parse array literal: ${error.message}`);
210
254
  }
211
255
  return result;
212
256
  }
@@ -239,7 +283,8 @@ function parseDecoratorArgs(decorator) {
239
283
  }
240
284
  return result;
241
285
  } catch (error) {
242
- console.warn(`Warning: Could not parse decorator arguments: ${error.message}`);
286
+ if (error instanceof UnresolvedConstantReferenceError) throw error;
287
+ logger.warn(`Could not parse decorator arguments: ${error.message}`);
243
288
  return result;
244
289
  }
245
290
  }
@@ -268,11 +313,15 @@ function extractAttributesFromSource(sourceFile, className) {
268
313
  attributes.push(attribute);
269
314
  }
270
315
  } catch (error) {
271
- console.warn(`Warning: Could not extract attributes from class ${className}: ${error.message}`);
316
+ if (error instanceof UnresolvedConstantReferenceError) throw error;
317
+ logger.warn(`Could not extract attributes from class ${className}: ${error.message}`);
272
318
  }
273
319
  return attributes;
274
320
  }
275
- function extractRegionDefinitionsFromSource(sourceFile, className) {
321
+ function normalizeComponentTypeId(typeId, defaultGroup) {
322
+ return typeId.includes(".") ? typeId : `${defaultGroup}.${typeId}`;
323
+ }
324
+ function extractRegionDefinitionsFromSource(sourceFile, className, defaultComponentGroup = DEFAULT_COMPONENT_GROUP) {
276
325
  const regionDefinitions = [];
277
326
  try {
278
327
  const classDeclaration = sourceFile.getClass(className);
@@ -291,8 +340,8 @@ function extractRegionDefinitionsFromSource(sourceFile, className) {
291
340
  name: regionConfig.name || "Region"
292
341
  };
293
342
  if (regionConfig.componentTypes) regionDefinition.component_types = regionConfig.componentTypes;
294
- if (Array.isArray(regionConfig.componentTypeInclusions)) regionDefinition.component_type_inclusions = regionConfig.componentTypeInclusions.map((incl) => ({ type_id: incl }));
295
- if (Array.isArray(regionConfig.componentTypeExclusions)) regionDefinition.component_type_exclusions = regionConfig.componentTypeExclusions.map((excl) => ({ type_id: excl }));
343
+ if (Array.isArray(regionConfig.componentTypeInclusions)) regionDefinition.component_type_inclusions = regionConfig.componentTypeInclusions.map((incl) => ({ type_id: normalizeComponentTypeId(String(incl), defaultComponentGroup) }));
344
+ if (Array.isArray(regionConfig.componentTypeExclusions)) regionDefinition.component_type_exclusions = regionConfig.componentTypeExclusions.map((excl) => ({ type_id: normalizeComponentTypeId(String(excl), defaultComponentGroup) }));
296
345
  if (regionConfig.maxComponents !== void 0) regionDefinition.max_components = regionConfig.maxComponents;
297
346
  if (regionConfig.minComponents !== void 0) regionDefinition.min_components = regionConfig.minComponents;
298
347
  if (regionConfig.allowMultiple !== void 0) regionDefinition.allow_multiple = regionConfig.allowMultiple;
@@ -303,7 +352,7 @@ function extractRegionDefinitionsFromSource(sourceFile, className) {
303
352
  }
304
353
  }
305
354
  } catch (error) {
306
- console.warn(`Warning: Could not extract region definitions from class ${className}: ${error.message}`);
355
+ logger.warn(`Warning: Could not extract region definitions from class ${className}: ${error.message}`);
307
356
  }
308
357
  return regionDefinitions;
309
358
  }
@@ -324,12 +373,13 @@ async function processComponentFile(filePath, _projectRoot) {
324
373
  const className = classDeclaration.getName();
325
374
  if (!className) continue;
326
375
  const componentConfig = parseDecoratorArgs(componentDecorator);
376
+ const componentGroup = String(componentConfig.group || DEFAULT_COMPONENT_GROUP);
327
377
  const attributes = extractAttributesFromSource(sourceFile, className);
328
- const regionDefinitions = extractRegionDefinitionsFromSource(sourceFile, className);
378
+ const regionDefinitions = extractRegionDefinitionsFromSource(sourceFile, className, componentGroup);
329
379
  const componentMetadata = {
330
380
  typeId: componentConfig.id || className.toLowerCase(),
331
381
  name: componentConfig.name || toHumanReadableName(className),
332
- group: componentConfig.group || DEFAULT_COMPONENT_GROUP,
382
+ group: componentGroup,
333
383
  description: componentConfig.description || `Custom component: ${className}`,
334
384
  regionDefinitions,
335
385
  attributes
@@ -337,11 +387,13 @@ async function processComponentFile(filePath, _projectRoot) {
337
387
  components.push(componentMetadata);
338
388
  }
339
389
  } catch (error) {
340
- console.warn(`Warning: Could not process file ${filePath}:`, error.message);
390
+ if (error instanceof UnresolvedConstantReferenceError) throw error;
391
+ logger.warn(`Could not process file ${filePath}:`, error.message);
341
392
  }
342
393
  return components;
343
394
  } catch (error) {
344
- console.warn(`Warning: Could not read file ${filePath}:`, error.message);
395
+ if (error instanceof UnresolvedConstantReferenceError) throw error;
396
+ logger.warn(`Could not read file ${filePath}:`, error.message);
345
397
  return [];
346
398
  }
347
399
  }
@@ -377,11 +429,11 @@ async function processPageTypeFile(filePath, projectRoot) {
377
429
  pageTypes.push(pageTypeMetadata);
378
430
  }
379
431
  } catch (error) {
380
- console.warn(`Warning: Could not process file ${filePath}:`, error.message);
432
+ logger.warn(`Could not process file ${filePath}:`, error.message);
381
433
  }
382
434
  return pageTypes;
383
435
  } catch (error) {
384
- console.warn(`Warning: Could not read file ${filePath}:`, error.message);
436
+ logger.warn(`Could not read file ${filePath}:`, error.message);
385
437
  return [];
386
438
  }
387
439
  }
@@ -404,11 +456,11 @@ async function processAspectFile(filePath, _projectRoot) {
404
456
  };
405
457
  aspects.push(aspectMetadata);
406
458
  } catch (parseError) {
407
- console.warn(`Warning: Could not parse JSON in file ${filePath}:`, parseError.message);
459
+ logger.warn(`Could not parse JSON in file ${filePath}:`, parseError.message);
408
460
  }
409
461
  return aspects;
410
462
  } catch (error) {
411
- console.warn(`Warning: Could not read file ${filePath}:`, error.message);
463
+ logger.warn(`Could not read file ${filePath}:`, error.message);
412
464
  return [];
413
465
  }
414
466
  }
@@ -437,7 +489,7 @@ async function generateComponentCartridge(component, outputDir, dryRun = false)
437
489
  await writeFile(outputPath, JSON.stringify(cartridgeData, null, 2));
438
490
  }
439
491
  const prefix = dryRun ? " - [DRY RUN]" : " -";
440
- console.log(`${prefix} ${String(component.typeId)}: ${String(component.name)} (${String(component.attributes.length)} attributes) → ${fileName}.json`);
492
+ logger.debug(`${prefix} ${String(component.typeId)}: ${String(component.name)} (${String(component.attributes.length)} attributes) → ${fileName}.json`);
441
493
  }
442
494
  async function generatePageTypeCartridge(pageType, outputDir, dryRun = false) {
443
495
  const fileName = toCamelCaseFileName(pageType.name);
@@ -460,7 +512,7 @@ async function generatePageTypeCartridge(pageType, outputDir, dryRun = false) {
460
512
  await writeFile(outputPath, JSON.stringify(cartridgeData, null, 2));
461
513
  }
462
514
  const prefix = dryRun ? " - [DRY RUN]" : " -";
463
- console.log(`${prefix} ${String(pageType.name)}: ${String(pageType.description)} (${String(pageType.attributes.length)} attributes) → ${fileName}.json`);
515
+ logger.debug(`${prefix} ${String(pageType.name)}: ${String(pageType.description)} (${String(pageType.attributes.length)} attributes) → ${fileName}.json`);
464
516
  }
465
517
  async function generateAspectCartridge(aspect, outputDir, dryRun = false) {
466
518
  const fileName = toCamelCaseFileName(aspect.id);
@@ -476,7 +528,7 @@ async function generateAspectCartridge(aspect, outputDir, dryRun = false) {
476
528
  await writeFile(outputPath, JSON.stringify(cartridgeData, null, 2));
477
529
  }
478
530
  const prefix = dryRun ? " - [DRY RUN]" : " -";
479
- console.log(`${prefix} ${String(aspect.name)}: ${String(aspect.description)} (${String(aspect.attributeDefinitions.length)} attributes) → ${fileName}.json`);
531
+ logger.debug(`${prefix} ${String(aspect.name)}: ${String(aspect.description)} (${String(aspect.attributeDefinitions.length)} attributes) → ${fileName}.json`);
480
532
  }
481
533
  /**
482
534
  * Runs ESLint with --fix on the specified directory to format JSON files.
@@ -484,20 +536,20 @@ async function generateAspectCartridge(aspect, outputDir, dryRun = false) {
484
536
  */
485
537
  function lintGeneratedFiles(metadataDir, projectRoot) {
486
538
  try {
487
- console.log("🔧 Running ESLint --fix on generated JSON files...");
539
+ logger.debug("🔧 Running ESLint --fix on generated JSON files...");
488
540
  execSync(`npx eslint "${metadataDir}/**/*.json" --fix --no-error-on-unmatched-pattern`, {
489
541
  cwd: projectRoot,
490
542
  stdio: "pipe",
491
543
  encoding: "utf-8"
492
544
  });
493
- console.log("✅ JSON files formatted successfully");
545
+ logger.debug("✅ JSON files formatted successfully");
494
546
  } catch (error) {
495
547
  const execError = error;
496
548
  if (execError.status === 2) {
497
549
  const errMsg = execError.stderr || execError.stdout || "Unknown error";
498
- console.warn(`⚠️ Warning: Could not run ESLint --fix: ${errMsg}`);
499
- } else if (execError.stderr && execError.stderr.includes("error")) console.warn(`⚠️ Warning: Some linting issues could not be auto-fixed. Run ESLint manually to review.`);
500
- else console.log("✅ JSON files formatted successfully");
550
+ logger.warn(`⚠️ Could not run ESLint --fix: ${errMsg}`);
551
+ } else if (execError.stderr && execError.stderr.includes("error")) logger.warn(`⚠️ Some linting issues could not be auto-fixed. Run ESLint manually to review.`);
552
+ else logger.debug("✅ JSON files formatted successfully");
501
553
  }
502
554
  }
503
555
  async function generateMetadata(projectDirectory, metadataDirectory, options) {
@@ -505,9 +557,9 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
505
557
  const filePaths = options?.filePaths;
506
558
  const isIncrementalMode = filePaths && filePaths.length > 0;
507
559
  const dryRun = options?.dryRun || false;
508
- if (dryRun) console.log("🔍 [DRY RUN] Scanning for decorated components and page types...");
509
- else if (isIncrementalMode) console.log(`🔍 Generating metadata for ${filePaths.length} specified file(s)...`);
510
- else console.log("🔍 Generating metadata for decorated components and page types...");
560
+ if (dryRun) logger.debug("🔍 [DRY RUN] Scanning for decorated components and page types...");
561
+ else if (isIncrementalMode) logger.debug(`🔍 Generating metadata for ${filePaths.length} specified file(s)...`);
562
+ else logger.debug("🔍 Generating metadata for decorated components and page types...");
511
563
  const projectRoot = resolve(projectDirectory);
512
564
  const srcDir = join(projectRoot, "src");
513
565
  const metadataDir = resolve(metadataDirectory);
@@ -516,7 +568,7 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
516
568
  const aspectsOutputDir = join(metadataDir, "aspects");
517
569
  if (!dryRun) {
518
570
  if (!isIncrementalMode) {
519
- console.log("🗑️ Cleaning existing output directories...");
571
+ logger.debug("🗑️ Cleaning existing output directories...");
520
572
  for (const outputDir of [
521
573
  componentsOutputDir,
522
574
  pagesOutputDir,
@@ -526,12 +578,12 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
526
578
  recursive: true,
527
579
  force: true
528
580
  });
529
- console.log(` - Deleted: ${outputDir}`);
581
+ logger.debug(` - Deleted: ${outputDir}`);
530
582
  } catch {
531
- console.log(` - Directory not found (skipping): ${outputDir}`);
583
+ logger.debug(` - Directory not found (skipping): ${outputDir}`);
532
584
  }
533
- } else console.log("📝 Incremental mode: existing cartridge files will be preserved/overwritten");
534
- console.log("📁 Creating output directories...");
585
+ } else logger.debug("📝 Incremental mode: existing cartridge files will be preserved/overwritten");
586
+ logger.debug("Creating output directories...");
535
587
  for (const outputDir of [
536
588
  componentsOutputDir,
537
589
  pagesOutputDir,
@@ -542,16 +594,18 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
542
594
  try {
543
595
  await access(outputDir);
544
596
  } catch {
545
- console.error(`❌ Error: Failed to create output directory ${outputDir}: ${error.message}`);
597
+ const err = error;
598
+ logger.error(`❌ Failed to create output directory ${outputDir}: ${err.message}`);
546
599
  process.exit(1);
600
+ throw err;
547
601
  }
548
602
  }
549
- } else if (isIncrementalMode) console.log(`📝 [DRY RUN] Would process ${filePaths.length} specific file(s)`);
550
- else console.log("📝 [DRY RUN] Would clean and regenerate all metadata files");
603
+ } else if (isIncrementalMode) logger.debug(`📝 [DRY RUN] Would process ${filePaths.length} specific file(s)`);
604
+ else logger.debug("📝 [DRY RUN] Would clean and regenerate all metadata files");
551
605
  let files = [];
552
606
  if (isIncrementalMode && filePaths) {
553
607
  files = filePaths.map((fp) => resolve(projectRoot, fp));
554
- console.log(`📂 Processing ${files.length} specified file(s)...`);
608
+ logger.debug(`📂 Processing ${files.length} specified file(s)...`);
555
609
  } else {
556
610
  const scanDirectory = async (dir) => {
557
611
  const entries = await readdir(dir, { withFileTypes: true });
@@ -576,7 +630,7 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
576
630
  allAspects.push(...aspects);
577
631
  }
578
632
  if (allComponents.length === 0 && allPageTypes.length === 0 && allAspects.length === 0) {
579
- console.log("⚠️ No decorated components, page types, or aspect files found.");
633
+ logger.info("⚠️ No decorated components, page types, or aspect files found.");
580
634
  return {
581
635
  componentsGenerated: 0,
582
636
  pageTypesGenerated: 0,
@@ -585,22 +639,22 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
585
639
  };
586
640
  }
587
641
  if (allComponents.length > 0) {
588
- console.log(`✅ Found ${allComponents.length} decorated component(s):`);
642
+ logger.debug(`✅ Found ${allComponents.length} decorated component(s)`);
589
643
  for (const component of allComponents) await generateComponentCartridge(component, componentsOutputDir, dryRun);
590
- if (dryRun) console.log(`📄 [DRY RUN] Would generate ${allComponents.length} component metadata file(s) in: ${componentsOutputDir}`);
591
- else console.log(`📄 Generated ${allComponents.length} component metadata file(s) in: ${componentsOutputDir}`);
644
+ if (dryRun) logger.info(`[DRY RUN] Would generate ${allComponents.length} component metadata file(s)`);
645
+ else logger.info(`Generated ${allComponents.length} component metadata file(s)`);
592
646
  }
593
647
  if (allPageTypes.length > 0) {
594
- console.log(`✅ Found ${allPageTypes.length} decorated page type(s):`);
648
+ logger.debug(`✅ Found ${allPageTypes.length} decorated page type(s)`);
595
649
  for (const pageType of allPageTypes) await generatePageTypeCartridge(pageType, pagesOutputDir, dryRun);
596
- if (dryRun) console.log(`📄 [DRY RUN] Would generate ${allPageTypes.length} page type metadata file(s) in: ${pagesOutputDir}`);
597
- else console.log(`📄 Generated ${allPageTypes.length} page type metadata file(s) in: ${pagesOutputDir}`);
650
+ if (dryRun) logger.info(`[DRY RUN] Would generate ${allPageTypes.length} page type metadata file(s)`);
651
+ else logger.info(`Generated ${allPageTypes.length} page type metadata file(s)`);
598
652
  }
599
653
  if (allAspects.length > 0) {
600
- console.log(`✅ Found ${allAspects.length} decorated aspect(s):`);
654
+ logger.debug(`✅ Found ${allAspects.length} decorated aspect(s)`);
601
655
  for (const aspect of allAspects) await generateAspectCartridge(aspect, aspectsOutputDir, dryRun);
602
- if (dryRun) console.log(`📄 [DRY RUN] Would generate ${allAspects.length} aspect metadata file(s) in: ${aspectsOutputDir}`);
603
- else console.log(`📄 Generated ${allAspects.length} aspect metadata file(s) in: ${aspectsOutputDir}`);
656
+ if (dryRun) logger.info(`[DRY RUN] Would generate ${allAspects.length} aspect metadata file(s)`);
657
+ else logger.info(`Generated ${allAspects.length} aspect metadata file(s)`);
604
658
  }
605
659
  const shouldLintFix = options?.lintFix !== false;
606
660
  if (!dryRun && shouldLintFix && (allComponents.length > 0 || allPageTypes.length > 0 || allAspects.length > 0)) lintGeneratedFiles(metadataDir, projectRoot);
@@ -611,8 +665,10 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
611
665
  totalFiles: allComponents.length + allPageTypes.length + allAspects.length
612
666
  };
613
667
  } catch (error) {
614
- console.error("❌ Error:", error.message);
668
+ const err = error;
669
+ logger.error("❌ Error:", err.message);
615
670
  process.exit(1);
671
+ throw err;
616
672
  }
617
673
  }
618
674
 
package/dist/index.d.ts CHANGED
@@ -31,11 +31,6 @@ interface StaticRegistryPluginConfig {
31
31
  * @default true
32
32
  */
33
33
  failOnError?: boolean;
34
- /**
35
- * Enable verbose logging
36
- * @default false
37
- */
38
- verbose?: boolean;
39
34
  }
40
35
  //#endregion
41
36
  //#region src/plugins/eventInstrumentationValidator.d.ts
@@ -59,11 +54,6 @@ interface EventInstrumentationValidatorConfig {
59
54
  * @default false (warning only)
60
55
  */
61
56
  failOnMissing?: boolean;
62
- /**
63
- * Enable verbose logging
64
- * @default false
65
- */
66
- verbose?: boolean;
67
57
  }
68
58
  //#endregion
69
59
  //#region src/storefront-next-targets.d.ts
@@ -148,6 +138,131 @@ declare function transformTargetPlaceholderPlugin(): {
148
138
  } | null;
149
139
  };
150
140
  //#endregion
141
+ //#region src/plugins/hybridProxy.d.ts
142
+
143
+ interface HybridProxyPluginOptions {
144
+ /** Whether hybrid proxying is enabled */
145
+ enabled: boolean;
146
+ /** SFCC origin URL to proxy non-matching routes to */
147
+ targetOrigin: string;
148
+ /** Cloudflare routing expression (routes matching go to Next) */
149
+ routingRules: string;
150
+ /**
151
+ * Callback that decides if a pathname should be handled by Storefront Next.
152
+ * Called for every request that isn't a Vite internal or SFCC path.
153
+ * Receives the pathname and the raw `routingRules` string; returns true to
154
+ * let React Router handle it, false to proxy to SFCC.
155
+ *
156
+ * @example
157
+ * import { shouldRouteToNext } from './src/lib/ecdn-matcher';
158
+ * hybridProxyPlugin({ routeMatcher: shouldRouteToNext, ... })
159
+ */
160
+ routeMatcher: (pathname: string, routingRules: string) => boolean;
161
+ /** SFCC Site ID (e.g., 'RefArchGlobal') */
162
+ siteId: string;
163
+ /** Locale for SFRA paths (e.g., 'en-GB'). Defaults to 'default' if not provided. */
164
+ locale?: string;
165
+ }
166
+ /**
167
+ * Vite plugin for hybrid proxying between Storefront Next and legacy SFRA.
168
+ *
169
+ * Uses http-proxy to silently forward non-matching requests to SFCC without visible
170
+ * redirects. Rewrites Set-Cookie headers, Location headers, and HTML/JSON response
171
+ * bodies to keep all navigation within the localhost proxy.
172
+ *
173
+ * Routing decisions are delegated to the `routeMatcher` callback injected via options,
174
+ * keeping the SDK free of template-specific routing logic.
175
+ *
176
+ * @param options - Plugin configuration
177
+ * @returns Vite plugin
178
+ */
179
+ declare function hybridProxyPlugin(options: HybridProxyPluginOptions): Plugin;
180
+ //#endregion
181
+ //#region src/plugins/ecdnMatcher.d.ts
182
+ /**
183
+ * Copyright 2026 Salesforce, Inc.
184
+ *
185
+ * Licensed under the Apache License, Version 2.0 (the "License");
186
+ * you may not use this file except in compliance with the License.
187
+ * You may obtain a copy of the License at
188
+ *
189
+ * http://www.apache.org/licenses/LICENSE-2.0
190
+ *
191
+ * Unless required by applicable law or agreed to in writing, software
192
+ * distributed under the License is distributed on an "AS IS" BASIS,
193
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
194
+ * See the License for the specific language governing permissions and
195
+ * limitations under the License.
196
+ */
197
+ /**
198
+ * Extracts regex patterns from a Cloudflare routing expression.
199
+ *
200
+ * Parses Cloudflare "matches" expressions like:
201
+ * (http.request.uri.path matches "^/$" or http.request.uri.path matches "^/category.*")
202
+ *
203
+ * And extracts the regex patterns: ["^/$", "^/category.*"]
204
+ *
205
+ * @param expression - Cloudflare expression string
206
+ * @returns Array of regex pattern strings
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * extractPatterns('(http.request.uri.path matches "^/$")');
211
+ * // Returns: ["^/$"]
212
+ *
213
+ * extractPatterns('(http.request.uri.path matches "^/$" or http.request.uri.path matches "^/search.*")');
214
+ * // Returns: ["^/$", "^/search.*"]
215
+ * ```
216
+ */
217
+ declare function extractPatterns(expression: string): string[];
218
+ /**
219
+ * Tests if a pathname matches any of the provided regex patterns (logical OR).
220
+ * Uses caching to optimize repeated pattern compilations.
221
+ *
222
+ * @param pathname - URL pathname to test (e.g., "/search", "/category/shoes")
223
+ * @param patterns - Array of regex pattern strings
224
+ * @returns true if pathname matches any pattern, false otherwise
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * testPatterns('/category/shoes', ['^/category.*', '^/search.*']);
229
+ * // Returns: true (matches first pattern)
230
+ *
231
+ * testPatterns('/checkout', ['^/category.*', '^/search.*']);
232
+ * // Returns: false (matches no patterns)
233
+ * ```
234
+ */
235
+ declare function testPatterns(pathname: string, patterns: string[]): boolean;
236
+ /**
237
+ * Main function: Determines if a pathname should route to Storefront Next
238
+ * or be proxied/redirected to SFRA/legacy backend.
239
+ *
240
+ * @param pathname - URL pathname (e.g., "/search", "/checkout")
241
+ * @param routingRules - Cloudflare routing expression string (optional)
242
+ * @returns true if should route to Storefront Next, false if should proxy to SFRA
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * const rules = '(http.request.uri.path matches "^/$" or http.request.uri.path matches "^/category.*")';
247
+ *
248
+ * shouldRouteToNext('/', rules); // true - route to Next
249
+ * shouldRouteToNext('/category/mens', rules); // true - route to Next
250
+ * shouldRouteToNext('/checkout', rules); // false - proxy to SFRA
251
+ * shouldRouteToNext('/any-path', undefined); // true - no rules = default to Next
252
+ * ```
253
+ */
254
+ declare function shouldRouteToNext(pathname: string, routingRules?: string): boolean;
255
+ /**
256
+ * Clears the regex cache. Useful for testing or when routing rules change.
257
+ *
258
+ * @example
259
+ * ```typescript
260
+ * clearCache();
261
+ * // All cached regex patterns are removed
262
+ * ```
263
+ */
264
+ declare function clearCache(): void;
265
+ //#endregion
151
266
  //#region src/server/config.d.ts
152
267
  /**
153
268
  * Server configuration extracted from environment variables
@@ -158,7 +273,6 @@ interface ServerConfig {
158
273
  shortCode: string;
159
274
  organizationId: string;
160
275
  clientId: string;
161
- siteId: string;
162
276
  proxy: string;
163
277
  proxyHost?: string;
164
278
  };
@@ -268,7 +382,7 @@ declare const ExtensionConfig: {
268
382
  //#endregion
269
383
  //#region src/extensibility/trim-extensions.d.ts
270
384
  type ExtensionsSelection = Record<string, boolean>;
271
- declare function trimExtensions(directory: string, selectedExtensions?: Partial<ExtensionsSelection>, extensionConfig?: typeof ExtensionConfig, verboseOverride?: boolean): void;
385
+ declare function trimExtensions(directory: string, selectedExtensions?: Partial<ExtensionsSelection>, extensionConfig?: typeof ExtensionConfig): void;
272
386
  //#endregion
273
387
  //#region src/cartridge-services/generate-cartridge.d.ts
274
388
  /**
@@ -303,5 +417,5 @@ interface GenerateMetadataResult {
303
417
  }
304
418
  declare function generateMetadata(projectDirectory: string, metadataDirectory: string, options?: GenerateMetadataOptions): Promise<GenerateMetadataResult>;
305
419
  //#endregion
306
- export { type GenerateMetadataOptions, type GenerateMetadataResult, type StorefrontNextTargetsConfig, createServer, storefrontNextTargets as default, generateMetadata, loadConfigFromEnv, loadProjectConfig, transformTargetPlaceholderPlugin, trimExtensions };
420
+ export { type GenerateMetadataOptions, type GenerateMetadataResult, type HybridProxyPluginOptions, type StorefrontNextTargetsConfig, clearCache, createServer, storefrontNextTargets as default, extractPatterns, generateMetadata, hybridProxyPlugin, loadConfigFromEnv, loadProjectConfig, shouldRouteToNext, testPatterns, transformTargetPlaceholderPlugin, trimExtensions };
307
421
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":["ExtensionMeta","ExtensionConfig","Record","default"],"sources":["../src/plugins/staticRegistry.ts","../src/plugins/eventInstrumentationValidator.ts","../src/storefront-next-targets.ts","../src/plugins/transformTargets.ts","../src/server/config.ts","../src/server/modes.ts","../src/server/index.ts","../src/extensibility/extension-config.d.ts","../src/extensibility/trim-extensions.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 */\nexport type ExtensionMeta = {\n name: string;\n description: string;\n installationInstructions: string;\n uninstallationInstructions: string;\n folder: string;\n dependencies: string[];\n defaultOn?: boolean;\n};\n\ndeclare const ExtensionConfig: {\n extensions: Record<string, ExtensionMeta>;\n};\n\nexport default ExtensionConfig;\n"],"mappings":";;;;;;;AMkDA;;;AAQa,UNLI,0BAAA,CMKJ;EAMF;;;;EAYW,aAAA,CAAA,EAAY,MAAA;EAAU;;;;;;;EC7DhCA;AAQV;;;;ECGG;AAA4B;;;EAWJ,WAAA,CAAA,EAAA,OAAA;EAAe;;;;ECmpB3B,OAAA,CAAA,EAAA,OAAA;AAwBjB;;;;;;;ANtrBgB,UFFC,mCAAA,CEWc;;;;ACb/B;EAqBgB,UAAA,CAAA,EAAA,MAAA;EAyDM;;;;ECrFV,SAAA,CAAA,EAAA,MAAU,EAAA;EAKL;;;;EC8BA,aAAA,CAAA,EAAc,OAAA;EAAgB;;;;EAiBnC,OAAA,CAAA,EAAA,OAAA;;;;;;AHzCZ;;UDQiB,2BAAA;;AEZjB;AAqBA;AAyDA;;;;ACrFA;AAKA;;;;AC8BA;;;;;;;;AA0BA;;;EAA4D,kBAAA,CAAA,EAAA,OAAA;EAAO;;;;AC7DnE;AAQE;;;mBL6CmB;EM1ChB;AAA4B;;;;;;;kCNoDG;AO0mBpC;AAwBA;AA8CA;;;;;;;;;;;;;;;;;;iBP1pBgB,qBAAA,UAA8B,8BAAmC;;;iBC1EjE,gCAAA,CAAA;;;yBASe;;EHkBd,SAAA,CAAA,IAAA,EAAA,MAAA,EAAA,EAAA,EAAA,MAA0B,CAAA,EAAA;;;;AC7B3C,CAAA;;;;;;UGFiB,YAAA;;;MJ+BA,SAAA,EAAA,MAAA;;;;MC7BA,KAAA,EAAA,MAAA;;;;ACUjB;AAkEA;;;;AC1EA;;;;ACJiB,iBAqBD,iBAAA,CAAA,CArBa,EAqBQ,YArBR;AAqB7B;AAyDA;;;;ACrFA;AAKA;iBDgFsB,iBAAA,4BAA6C,QAAQ;;;;;;;;;AJ/C3E;;;;AC7BA;;;;ACUA;AAkEgB,KGrFJ,UAAA,GHqFI,aAAqB,GAAS,SAAA,GAAA,YAAA;;;;AC1E9B,UENC,kBAAA,CFMD;;;;ECJC,mBAAY,EAAA,OAAA;EAqBb;EAyDM,iBAAA,EAAA,OAAiB;;;;ECrF3B,sBAAU,EAAA,OAAA;AAKtB;;;UC8BiB,aAAA,SAAsB,QAAQ;;QAErC;EF9BO;EAqBD,gBAAA,CAAA,EAAA,MAAiB;EAyDX;WE1CT;;;ED3CD;EAKK,IAAA,CAAA,EC4CN,aD5CM;;UC+CL;;EAjBK,SAAA,CAAA,EAAA,OAAc;;;;;AAiBnB,iBASU,YAAA,CATV,OAAA,EASgC,aAThC,CAAA,EASgD,OAThD,CASwD,OATxD,CAAA;;;;;;;;;ANdZ;;;;AC7BA;;;;ACUA;AAkEgB,KKrFJA,aAAAA,GLqFyB;;;;EC1ErB,0BAAA,EAAA,MAAA;;;;ACJhB,CAAA;AA8EA,cG3EcC,eH2EyB,EAAA;cG1EvBC,eAAeF;;;;KCA1B,mBAAA,GAAsB;iBAQH,cAAA,yCAEC,QAAQ,+CACJ;;;;;;ARgBZ,USmoBA,uBAAA,CTnoB0B;;;;AC7B3C;;;;ACUA;AAkEA;;;;AC1EA;;;;ACJA;AAqBA;AAyDA;;UK4mBiB,sBAAA;;EJjsBL,kBAAU,EAAA,MAAA;EAKL,gBAAA,EAAA,MAAkB;;;iBI0uBb,gBAAA,gEAGR,0BACX,QAAQ"}
1
+ {"version":3,"file":"index.d.ts","names":["ExtensionMeta","ExtensionConfig","Record","default"],"sources":["../src/plugins/staticRegistry.ts","../src/plugins/eventInstrumentationValidator.ts","../src/storefront-next-targets.ts","../src/plugins/transformTargets.ts","../src/plugins/hybridProxy.ts","../src/plugins/ecdnMatcher.ts","../src/server/config.ts","../src/server/modes.ts","../src/server/index.ts","../src/extensibility/extension-config.d.ts","../src/extensibility/trim-extensions.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 */\nexport type ExtensionMeta = {\n name: string;\n description: string;\n installationInstructions: string;\n uninstallationInstructions: string;\n folder: string;\n dependencies: string[];\n defaultOn?: boolean;\n};\n\ndeclare const ExtensionConfig: {\n extensions: Record<string, ExtensionMeta>;\n};\n\nexport default ExtensionConfig;\n"],"mappings":"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"}