@salesforce/storefront-next-dev 0.3.1-alpha.1 → 0.4.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.
- package/README.md +12 -12
- package/bin/run.js +19 -9
- package/dist/cartridge-services/index.js +9 -1
- package/dist/cartridge-services/index.js.map +1 -1
- package/dist/commands/config/inspect.js +191 -0
- package/dist/commands/create-bundle.js +1 -1
- package/dist/commands/create-storefront.js +3 -3
- package/dist/commands/dev.js +1 -3
- package/dist/commands/extensions/install.js +1 -1
- package/dist/commands/extensions/list.js +1 -1
- package/dist/commands/extensions/remove.js +1 -1
- package/dist/commands/generate-cartridge.js +1 -1
- package/dist/commands/locales/aggregate-extensions.js +181 -0
- package/dist/commands/prepare-local.js +1 -1
- package/dist/commands/preview.js +1 -3
- package/dist/commands/scapi/add.js +298 -0
- package/dist/commands/scapi/list.js +35 -0
- package/dist/commands/scapi/remove.js +56 -0
- package/dist/commands/validate-cartridge.js +1 -1
- package/dist/configs/react-router.config.js +2 -1
- package/dist/configs/react-router.config.js.map +1 -1
- package/dist/data-store/local-provider.d.ts +42 -0
- package/dist/data-store/local-provider.d.ts.map +1 -0
- package/dist/data-store/local-provider.js +66 -0
- package/dist/data-store/local-provider.js.map +1 -0
- package/dist/flags.js +5 -3
- package/dist/generate-cartridge.js +9 -1
- package/dist/generate-custom-clients.js +73 -0
- package/dist/hooks/init.js +29 -4
- package/dist/index.d.ts +46 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +260 -42
- package/dist/index.js.map +1 -1
- package/dist/logger.js +1 -1
- package/dist/mrt/ssr.mjs +51 -51
- package/dist/mrt/ssr.mjs.map +1 -1
- package/dist/mrt/streamingHandler.mjs +57 -57
- package/dist/mrt/streamingHandler.mjs.map +1 -1
- package/dist/schema-utils.js +64 -0
- package/dist/utils.js +3 -11
- package/package.json +19 -3
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import chalk from "chalk";
|
|
|
4
4
|
import path$1, { dirname, join as join$1, relative, resolve as resolve$1 } from "path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
6
6
|
import { parse } from "@babel/parser";
|
|
7
|
-
import { isArrayPattern, isClassDeclaration, isExportSpecifier, isFunctionDeclaration, isIdentifier, isJSXAttribute, isJSXElement, isJSXFragment, isJSXIdentifier, isMemberExpression, isObjectPattern, isObjectProperty, isRestElement, isVariableDeclaration, jsxClosingElement, jsxClosingFragment, jsxElement, jsxFragment, jsxIdentifier, jsxOpeningElement, jsxOpeningFragment, jsxText } from "@babel/types";
|
|
7
|
+
import { booleanLiteral, identifier, importDeclaration, importSpecifier, isArrayPattern, isClassDeclaration, isExportSpecifier, isFunctionDeclaration, isIdentifier, isJSXAttribute, isJSXElement, isJSXFragment, isJSXIdentifier, isMemberExpression, isObjectPattern, isObjectProperty, isRestElement, isStringLiteral, isVariableDeclaration, jsxAttribute, jsxClosingElement, jsxClosingFragment, jsxElement, jsxExpressionContainer, jsxFragment, jsxIdentifier, jsxOpeningElement, jsxOpeningFragment, jsxText, stringLiteral } from "@babel/types";
|
|
8
8
|
import { generate } from "@babel/generator";
|
|
9
9
|
import traverseModule from "@babel/traverse";
|
|
10
10
|
import fs$1, { existsSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -122,7 +122,7 @@ function patchAssetsPaths(dir) {
|
|
|
122
122
|
function fixReactRouterManifestUrlsPlugin() {
|
|
123
123
|
let resolvedConfig;
|
|
124
124
|
return {
|
|
125
|
-
name: "
|
|
125
|
+
name: "storefront-next:fix-react-router-manifest-urls",
|
|
126
126
|
enforce: "post",
|
|
127
127
|
configResolved(config) {
|
|
128
128
|
resolvedConfig = config;
|
|
@@ -284,7 +284,7 @@ const readableChunkFileNames = (chunkInfo) => {
|
|
|
284
284
|
*/
|
|
285
285
|
const readableChunkFileNamesPlugin = () => {
|
|
286
286
|
return {
|
|
287
|
-
name: "
|
|
287
|
+
name: "storefront-next:readable-chunk-file-names",
|
|
288
288
|
apply: "build",
|
|
289
289
|
config() {
|
|
290
290
|
return { environments: { client: { build: { rollupOptions: { output: {
|
|
@@ -355,7 +355,7 @@ const managedRuntimeBundlePlugin = () => {
|
|
|
355
355
|
await fs.writeJson(buildPackageJsonPath, packageJson, { spaces: 2 });
|
|
356
356
|
};
|
|
357
357
|
return {
|
|
358
|
-
name: "
|
|
358
|
+
name: "storefront-next:managed-runtime-bundle",
|
|
359
359
|
apply: "build",
|
|
360
360
|
config({ mode }) {
|
|
361
361
|
return {
|
|
@@ -396,7 +396,7 @@ const patchReactRouterPlugin = () => {
|
|
|
396
396
|
let isTestMode = false;
|
|
397
397
|
let isDevMode = false;
|
|
398
398
|
return {
|
|
399
|
-
name: "
|
|
399
|
+
name: "storefront-next:patch-react-router",
|
|
400
400
|
enforce: "pre",
|
|
401
401
|
config(_config, { mode }) {
|
|
402
402
|
isTestMode = mode === "test";
|
|
@@ -428,10 +428,11 @@ const patchReactRouterPlugin = () => {
|
|
|
428
428
|
|
|
429
429
|
//#endregion
|
|
430
430
|
//#region src/extensibility/target-utils.ts
|
|
431
|
-
const traverse$
|
|
431
|
+
const traverse$2 = traverseModule.default || traverseModule;
|
|
432
432
|
const TARGET_COMPONENT_TAG = "UITarget";
|
|
433
|
-
const TARGET_PROVIDERS_TAG = "
|
|
433
|
+
const TARGET_PROVIDERS_TAG = "UITargetProviders";
|
|
434
434
|
const TARGET_ID_ATTRIBUTE = "targetId";
|
|
435
|
+
const TARGET_COMPONENT_JSX_RE = /* @__PURE__ */ new RegExp(`<${TARGET_COMPONENT_TAG}[\\s/>]`);
|
|
435
436
|
/**
|
|
436
437
|
* Find and replace the TargetProviders tags with the corresponding context providers
|
|
437
438
|
* @param element - the AST element to replace
|
|
@@ -463,17 +464,26 @@ function findAndReplaceComponent(componentName, element, targetRegistry) {
|
|
|
463
464
|
const targetId = attr && isJSXAttribute(attr) && attr.value && "value" in attr.value ? attr.value.value : void 0;
|
|
464
465
|
if (targetId == null) throw new Error(`UITarget must contain a targetId attribute`);
|
|
465
466
|
if (targetRegistry[targetId] && targetRegistry[targetId].length > 0) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
467
|
+
if (element.node.children.length > 0) {
|
|
468
|
+
let nestedChildren = element.node.children;
|
|
469
|
+
for (let i = targetRegistry[targetId].length - 1; i >= 0; i--) {
|
|
470
|
+
const targetComponent = targetRegistry[targetId][i];
|
|
471
|
+
nestedChildren = [jsxElement(jsxOpeningElement(jsxIdentifier(targetComponent.componentName), [], false), jsxClosingElement(jsxIdentifier(targetComponent.componentName)), nestedChildren, false)];
|
|
472
|
+
}
|
|
473
|
+
element.replaceWith(nestedChildren[0]);
|
|
474
|
+
} else {
|
|
475
|
+
const components = targetRegistry[targetId].map((targetComponent) => {
|
|
476
|
+
return jsxElement(jsxOpeningElement(jsxIdentifier(targetComponent.componentName), [], true), null, [], true);
|
|
477
|
+
});
|
|
478
|
+
if (components.length > 1) element.replaceWith(jsxFragment(jsxOpeningFragment(), jsxClosingFragment(), components));
|
|
479
|
+
else element.replaceWith(components[0]);
|
|
480
|
+
}
|
|
471
481
|
targetIdReplaced = targetId;
|
|
472
482
|
replaced = true;
|
|
473
483
|
}
|
|
474
484
|
}
|
|
475
|
-
if (!replaced) if (element.node.children && element.node.children.length > 0) element.
|
|
476
|
-
else element.
|
|
485
|
+
if (!replaced) if (element.node.children && element.node.children.length > 0) element.replaceWith(jsxFragment(jsxOpeningFragment(), jsxClosingFragment(), element.node.children));
|
|
486
|
+
else element.replaceWith(jsxFragment(jsxOpeningFragment(), jsxClosingFragment(), []));
|
|
477
487
|
}
|
|
478
488
|
return targetIdReplaced;
|
|
479
489
|
}
|
|
@@ -493,7 +503,7 @@ function runReplacementPass(ast, tagName, targetRegistry = null, contextProvider
|
|
|
493
503
|
if (replacedId) targetIdsReplaced.add(replacedId);
|
|
494
504
|
} else if (contextProviders) findAndReplaceProviders(pathToReplace, contextProviders);
|
|
495
505
|
};
|
|
496
|
-
traverse$
|
|
506
|
+
traverse$2(ast, {
|
|
497
507
|
VariableDeclaration(nodePath) {
|
|
498
508
|
const declarationPaths = nodePath.get("declarations");
|
|
499
509
|
const declarationsArray = Array.isArray(declarationPaths) ? declarationPaths : [declarationPaths];
|
|
@@ -535,7 +545,7 @@ function buildReplacementImportStatements(targetIds, targetRegistry) {
|
|
|
535
545
|
return Array.from(importStatements).join("\n");
|
|
536
546
|
}
|
|
537
547
|
function transformTargets(code, targetRegistry, contextProviders) {
|
|
538
|
-
if (!
|
|
548
|
+
if (!TARGET_COMPONENT_JSX_RE.test(code) && !code.includes(TARGET_PROVIDERS_TAG)) return null;
|
|
539
549
|
const ast = parse(code, {
|
|
540
550
|
sourceType: "module",
|
|
541
551
|
plugins: [
|
|
@@ -544,18 +554,18 @@ function transformTargets(code, targetRegistry, contextProviders) {
|
|
|
544
554
|
"decorators-legacy"
|
|
545
555
|
]
|
|
546
556
|
});
|
|
547
|
-
if (
|
|
557
|
+
if (TARGET_COMPONENT_JSX_RE.test(code)) {
|
|
548
558
|
const replacementImportStatements = buildReplacementImportStatements(runReplacementPass(ast, TARGET_COMPONENT_TAG, targetRegistry, null), targetRegistry);
|
|
549
|
-
traverse$
|
|
550
|
-
if (nodePath.node.source.value
|
|
559
|
+
traverse$2(ast, { ImportDeclaration(nodePath) {
|
|
560
|
+
if (nodePath.node.source.value === "@/targets/ui-target") nodePath.replaceWith(jsxText(replacementImportStatements));
|
|
551
561
|
} });
|
|
552
562
|
}
|
|
553
563
|
if (code.includes(TARGET_PROVIDERS_TAG)) {
|
|
554
564
|
const importStatements = /* @__PURE__ */ new Set();
|
|
555
565
|
for (const contextProvider of contextProviders) importStatements.add(`import ${contextProvider.componentName} from '@/${contextProvider.path.replace(".tsx", "")}';`);
|
|
556
566
|
const replacementImportStatements = Array.from(importStatements).join("\n");
|
|
557
|
-
traverse$
|
|
558
|
-
if (nodePath.node.source.value
|
|
567
|
+
traverse$2(ast, { ImportDeclaration(nodePath) {
|
|
568
|
+
if (nodePath.node.source.value === "@/targets/ui-target-providers") nodePath.replaceWith(jsxText(replacementImportStatements));
|
|
559
569
|
} });
|
|
560
570
|
runReplacementPass(ast, TARGET_PROVIDERS_TAG, null, contextProviders);
|
|
561
571
|
}
|
|
@@ -567,7 +577,7 @@ function transformTargets(code, targetRegistry, contextProviders) {
|
|
|
567
577
|
* @param sourceDir - the source directory of the project
|
|
568
578
|
* @returns the target registry
|
|
569
579
|
*/
|
|
570
|
-
function buildTargetRegistry(rootDir) {
|
|
580
|
+
function buildTargetRegistry(rootDir, options = {}) {
|
|
571
581
|
const componentRegistry = {};
|
|
572
582
|
const contextProviders = [];
|
|
573
583
|
const extensionDirPath = path$1.join(rootDir, "extensions");
|
|
@@ -584,6 +594,7 @@ function buildTargetRegistry(rootDir) {
|
|
|
584
594
|
const configPath = path$1.join(extensionDirPath, dir.name, TARGET_CONFIG_FILENAME);
|
|
585
595
|
if (fs.existsSync(configPath)) {
|
|
586
596
|
const extensionConfig = fs.readJsonSync(configPath);
|
|
597
|
+
if (options.isProduction && extensionConfig.devOnly === true) continue;
|
|
587
598
|
if (extensionConfig && extensionConfig.components) for (const component of extensionConfig.components) {
|
|
588
599
|
const { targetId, path: componentPath, order = 0 } = component;
|
|
589
600
|
if (targetId && componentPath) {
|
|
@@ -626,16 +637,19 @@ function transformTargetPlaceholderPlugin() {
|
|
|
626
637
|
let componentRegistry;
|
|
627
638
|
let contextProviders;
|
|
628
639
|
let sourceDir;
|
|
640
|
+
let isProduction = false;
|
|
629
641
|
return {
|
|
630
|
-
name: "
|
|
642
|
+
name: "storefront-next:transform-target-placeholder",
|
|
631
643
|
enforce: "pre",
|
|
632
644
|
configResolved(config) {
|
|
633
645
|
sourceDir = config.resolve.alias.find((alias) => alias.find === "@")?.replacement || path$1.resolve(__dirname, "./src");
|
|
646
|
+
isProduction = config.mode === "production";
|
|
634
647
|
},
|
|
635
648
|
buildStart() {
|
|
636
|
-
({componentRegistry, contextProviders} = buildTargetRegistry(sourceDir));
|
|
649
|
+
({componentRegistry, contextProviders} = buildTargetRegistry(sourceDir, { isProduction }));
|
|
637
650
|
},
|
|
638
651
|
transform(code, id) {
|
|
652
|
+
if (process.env.VITE_UI_TARGET_DEV_MODE === "true") return null;
|
|
639
653
|
try {
|
|
640
654
|
const transformedCode = transformTargets(code, componentRegistry, contextProviders);
|
|
641
655
|
if (transformedCode) return {
|
|
@@ -656,7 +670,7 @@ function transformTargetPlaceholderPlugin() {
|
|
|
656
670
|
const watchConfigFilesPlugin = () => {
|
|
657
671
|
let viteConfig;
|
|
658
672
|
return {
|
|
659
|
-
name: "
|
|
673
|
+
name: "storefront-next:watch-config-files",
|
|
660
674
|
configResolved(config) {
|
|
661
675
|
viteConfig = config;
|
|
662
676
|
},
|
|
@@ -680,7 +694,7 @@ const watchConfigFilesPlugin = () => {
|
|
|
680
694
|
|
|
681
695
|
//#endregion
|
|
682
696
|
//#region src/plugins/staticRegistry.ts
|
|
683
|
-
const DEFAULT_COMPONENT_GROUP$1 = "
|
|
697
|
+
const DEFAULT_COMPONENT_GROUP$1 = "storefrontnext_base";
|
|
684
698
|
/**
|
|
685
699
|
* Extracts component ID and group from @Component decorator using ts-morph AST parsing
|
|
686
700
|
*/
|
|
@@ -861,9 +875,14 @@ export const registry = new ComponentRegistry();
|
|
|
861
875
|
const endIndex = existingContent.indexOf(endMarker);
|
|
862
876
|
if (startIndex === -1 || endIndex === -1) throw new Error(`Registry file ${registryFilePath} is missing static registry markers. Please add "${startMarker}" and "${endMarker}" markers to define the generated content area.`);
|
|
863
877
|
const updatedContent = `${existingContent.slice(0, startIndex + 24)}\n${generatedCode}\n${existingContent.slice(endIndex)}`;
|
|
878
|
+
if (updatedContent === existingContent) {
|
|
879
|
+
logger.debug(`⏭️ Registry unchanged, skipping write: ${registryFilePath}`);
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
864
882
|
try {
|
|
865
883
|
writeFileSync(registryFilePath, updatedContent, "utf-8");
|
|
866
884
|
logger.debug(`💾 Updated registry file: ${registryFilePath}`);
|
|
885
|
+
return true;
|
|
867
886
|
} catch (error) {
|
|
868
887
|
throw new Error(`Failed to write registry file: ${error.message}`);
|
|
869
888
|
}
|
|
@@ -907,9 +926,12 @@ const staticRegistryPlugin = (config = {}) => {
|
|
|
907
926
|
logger.debug(`📦 Found ${components.length} components with @Component decorators`);
|
|
908
927
|
const generatedCode = generateRegistryCode(components, registryIdentifier);
|
|
909
928
|
const registryFilePath = resolve$1(projectRoot, registryPath);
|
|
910
|
-
updateRegistryFile(registryFilePath, generatedCode);
|
|
929
|
+
const changed = updateRegistryFile(registryFilePath, generatedCode);
|
|
911
930
|
logger.debug("✅ Static registry generation complete!");
|
|
912
|
-
return
|
|
931
|
+
return {
|
|
932
|
+
registryFilePath,
|
|
933
|
+
changed
|
|
934
|
+
};
|
|
913
935
|
};
|
|
914
936
|
return {
|
|
915
937
|
name: "storefrontnext:static-registry",
|
|
@@ -931,10 +953,12 @@ const staticRegistryPlugin = (config = {}) => {
|
|
|
931
953
|
if (normalizedFile.includes(`/${normalizedComponentPath}/`) && (normalizedFile.endsWith(".ts") || normalizedFile.endsWith(".tsx"))) {
|
|
932
954
|
logger.debug(`🔄 Component file changed: ${file}, regenerating registry...`);
|
|
933
955
|
try {
|
|
934
|
-
const registryFilePath = await runRegistryGeneration();
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
956
|
+
const { registryFilePath, changed } = await runRegistryGeneration();
|
|
957
|
+
if (changed) {
|
|
958
|
+
const registryModule = server.moduleGraph.getModuleById(registryFilePath);
|
|
959
|
+
if (registryModule) await server.reloadModule(registryModule);
|
|
960
|
+
logger.debug("✅ Registry regenerated successfully!");
|
|
961
|
+
} else logger.debug("⏭️ Registry unchanged, skipping reload");
|
|
938
962
|
} catch (error) {
|
|
939
963
|
logger.error(`❌ Failed to regenerate registry: ${error.message}`);
|
|
940
964
|
}
|
|
@@ -1124,7 +1148,7 @@ const buildMiddlewareRegistryPlugin = () => {
|
|
|
1124
1148
|
/** App source directory (e.g. 'src' or './src') from React Router config. */
|
|
1125
1149
|
let appDirectory;
|
|
1126
1150
|
return {
|
|
1127
|
-
name: "
|
|
1151
|
+
name: "storefront-next:build-middleware-registry",
|
|
1128
1152
|
apply: "build",
|
|
1129
1153
|
configResolved(config) {
|
|
1130
1154
|
resolvedConfig = config;
|
|
@@ -1263,7 +1287,7 @@ function platformEntryPlugin() {
|
|
|
1263
1287
|
let userServerEntryPath;
|
|
1264
1288
|
let userClientEntryPath;
|
|
1265
1289
|
return {
|
|
1266
|
-
name: "
|
|
1290
|
+
name: "storefront-next:platform-entry",
|
|
1267
1291
|
enforce: "pre",
|
|
1268
1292
|
config(_config, { mode }) {
|
|
1269
1293
|
isTestMode = mode === "test";
|
|
@@ -1364,7 +1388,7 @@ const workspacePlugin = () => {
|
|
|
1364
1388
|
|
|
1365
1389
|
//#endregion
|
|
1366
1390
|
//#region src/plugins/componentLoaders.ts
|
|
1367
|
-
const traverse = traverseModule.default || traverseModule;
|
|
1391
|
+
const traverse$1 = traverseModule.default || traverseModule;
|
|
1368
1392
|
const generate$1 = generate.default || generate;
|
|
1369
1393
|
/**
|
|
1370
1394
|
* Names of exports to strip per environment.
|
|
@@ -1394,7 +1418,7 @@ function hasExportCandidate(code, names) {
|
|
|
1394
1418
|
*/
|
|
1395
1419
|
function hasComponentDecorator(ast) {
|
|
1396
1420
|
let found = false;
|
|
1397
|
-
traverse(ast, { ClassDeclaration(path$2) {
|
|
1421
|
+
traverse$1(ast, { ClassDeclaration(path$2) {
|
|
1398
1422
|
const decorators = path$2.node.decorators;
|
|
1399
1423
|
if (!decorators) return;
|
|
1400
1424
|
for (const decorator of decorators) if (decorator.expression.type === "CallExpression" && isIdentifier(decorator.expression.callee) && decorator.expression.callee.name === "Component") {
|
|
@@ -1441,7 +1465,7 @@ function stripExports(code, exportsToStrip, preParsedAst) {
|
|
|
1441
1465
|
let changed = false;
|
|
1442
1466
|
const previouslyReferencedIdentifiers = findReferencedIdentifiers(ast);
|
|
1443
1467
|
const removedExportLocalNames = /* @__PURE__ */ new Set();
|
|
1444
|
-
traverse(ast, { ExportNamedDeclaration(path$2) {
|
|
1468
|
+
traverse$1(ast, { ExportNamedDeclaration(path$2) {
|
|
1445
1469
|
const { declaration, specifiers } = path$2.node;
|
|
1446
1470
|
if (declaration && isVariableDeclaration(declaration)) {
|
|
1447
1471
|
const remaining = declaration.declarations.filter((decl) => {
|
|
@@ -1499,7 +1523,7 @@ function stripExports(code, exportsToStrip, preParsedAst) {
|
|
|
1499
1523
|
}
|
|
1500
1524
|
}
|
|
1501
1525
|
} });
|
|
1502
|
-
if (changed) traverse(ast, { ExpressionStatement(path$2) {
|
|
1526
|
+
if (changed) traverse$1(ast, { ExpressionStatement(path$2) {
|
|
1503
1527
|
if (!path$2.parentPath?.isProgram()) return;
|
|
1504
1528
|
if (path$2.node.expression.type === "AssignmentExpression") {
|
|
1505
1529
|
const left = path$2.node.expression.left;
|
|
@@ -1598,10 +1622,103 @@ function componentLoadersPlugin(config = {}) {
|
|
|
1598
1622
|
};
|
|
1599
1623
|
}
|
|
1600
1624
|
|
|
1625
|
+
//#endregion
|
|
1626
|
+
//#region src/plugins/ssrSourcemapFix.ts
|
|
1627
|
+
const INLINE_SOURCEMAP_PREFIX = "//# sourceMappingURL=data:application/json;base64,";
|
|
1628
|
+
/**
|
|
1629
|
+
* Vite plugin that fixes SSR sourcemap `sources` to use full module paths
|
|
1630
|
+
* instead of bare basenames.
|
|
1631
|
+
*
|
|
1632
|
+
* **Problem:** When the React Router `v8_viteEnvironmentApi` future flag is
|
|
1633
|
+
* enabled, SSR modules are evaluated by Vite's Environment API module runner.
|
|
1634
|
+
* Vite's `ssrTransform` generates inline sourcemaps with
|
|
1635
|
+
* `sources: [path.basename(url)]` (e.g., `"search.tsx"` or `"index.tsx"`).
|
|
1636
|
+
* V8's debugger cannot resolve bare basenames back to files on disk, so
|
|
1637
|
+
* Chrome DevTools (via `--inspect`) displays the wrong source file content
|
|
1638
|
+
* when pausing at breakpoints — even for files with unique names.
|
|
1639
|
+
*
|
|
1640
|
+
* **Why `fetchModule`:** A Vite transform plugin cannot fix this because
|
|
1641
|
+
* `ssrTransform` runs *after* the plugin transform pipeline and overwrites
|
|
1642
|
+
* `map.sources` with `[path.basename(url)]`. The only viable interception
|
|
1643
|
+
* point is `fetchModule` — the public API method on `DevEnvironment` that
|
|
1644
|
+
* returns the final transformed code (with inline sourcemaps already embedded)
|
|
1645
|
+
* to the module runner.
|
|
1646
|
+
*
|
|
1647
|
+
* **Removable:** If Vite updates `ssrTransform` to use full paths instead of
|
|
1648
|
+
* `path.basename()`, this plugin can be deleted with no other changes.
|
|
1649
|
+
*
|
|
1650
|
+
* Only active in development mode (`configureServer` does not run in build).
|
|
1651
|
+
*/
|
|
1652
|
+
function ssrSourcemapFixPlugin() {
|
|
1653
|
+
return {
|
|
1654
|
+
name: "storefront-next:ssr-sourcemap-fix",
|
|
1655
|
+
configureServer(server) {
|
|
1656
|
+
const ssrEnv = server.environments.ssr;
|
|
1657
|
+
if (!ssrEnv) return;
|
|
1658
|
+
const originalFetchModule = ssrEnv.fetchModule.bind(ssrEnv);
|
|
1659
|
+
ssrEnv.fetchModule = async (id, importer, options) => {
|
|
1660
|
+
const result = await originalFetchModule(id, importer, options);
|
|
1661
|
+
if (!result || "externalize" in result || "cache" in result || !("code" in result)) return result;
|
|
1662
|
+
const smIndex = result.code.lastIndexOf(INLINE_SOURCEMAP_PREFIX);
|
|
1663
|
+
if (smIndex === -1) return result;
|
|
1664
|
+
try {
|
|
1665
|
+
const base64Start = smIndex + 50;
|
|
1666
|
+
const base64End = result.code.indexOf("\n", base64Start);
|
|
1667
|
+
const base64Data = base64End === -1 ? result.code.slice(base64Start).trim() : result.code.slice(base64Start, base64End).trim();
|
|
1668
|
+
const mapJson = JSON.parse(Buffer.from(base64Data, "base64").toString("utf-8"));
|
|
1669
|
+
if (!mapJson.sources || !Array.isArray(mapJson.sources)) return result;
|
|
1670
|
+
const fileId = result.file || result.id;
|
|
1671
|
+
if (!fileId) return result;
|
|
1672
|
+
if (mapJson.sources.length !== 1) return result;
|
|
1673
|
+
const source = mapJson.sources[0];
|
|
1674
|
+
if (!source || source.includes("/")) return result;
|
|
1675
|
+
mapJson.sources = [fileId];
|
|
1676
|
+
const patchedBase64 = Buffer.from(JSON.stringify(mapJson)).toString("base64");
|
|
1677
|
+
result.code = result.code.slice(0, base64Start) + patchedBase64 + (base64End === -1 ? "" : result.code.slice(base64End));
|
|
1678
|
+
} catch {}
|
|
1679
|
+
return result;
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
//#endregion
|
|
1686
|
+
//#region src/plugins/i18n.ts
|
|
1687
|
+
/**
|
|
1688
|
+
* Vite plugin that splits locale translation files into per-language chunks.
|
|
1689
|
+
*
|
|
1690
|
+
* Wraps any existing `manualChunks` configuration so that locale modules are
|
|
1691
|
+
* assigned to `locales-{lang}` chunks (e.g., `locales-en-GB`) while all other
|
|
1692
|
+
* module IDs are delegated to the user's original `manualChunks` function or object.
|
|
1693
|
+
*
|
|
1694
|
+
* @param config - Optional configuration for custom locale patterns
|
|
1695
|
+
* @returns A Vite plugin that configures locale-based code splitting
|
|
1696
|
+
*/
|
|
1697
|
+
function i18nPlugin(config) {
|
|
1698
|
+
const pattern = config?.localePattern ?? /\/src\/locales\/([^/]+)\//;
|
|
1699
|
+
return {
|
|
1700
|
+
name: "storefront-next:i18n",
|
|
1701
|
+
apply: "build",
|
|
1702
|
+
config(viteConfig) {
|
|
1703
|
+
const output = viteConfig.build?.rollupOptions?.output;
|
|
1704
|
+
if (Array.isArray(output)) return;
|
|
1705
|
+
const existingManualChunks = output?.manualChunks;
|
|
1706
|
+
return { build: { rollupOptions: { output: { manualChunks(id, meta) {
|
|
1707
|
+
const localeMatch = id.match(pattern);
|
|
1708
|
+
if (localeMatch) return `locales-${localeMatch[1]}`;
|
|
1709
|
+
if (typeof existingManualChunks === "function") return existingManualChunks.call(this, id, meta);
|
|
1710
|
+
if (existingManualChunks && typeof existingManualChunks === "object") {
|
|
1711
|
+
for (const [name, ids] of Object.entries(existingManualChunks)) if (ids.includes(id)) return name;
|
|
1712
|
+
}
|
|
1713
|
+
} } } } };
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1601
1718
|
//#endregion
|
|
1602
1719
|
//#region src/storefront-next-targets.ts
|
|
1603
1720
|
/**
|
|
1604
|
-
* Storefront Next Vite plugin that powers the React Router
|
|
1721
|
+
* Storefront Next Vite plugin that powers the React Router app.
|
|
1605
1722
|
* Supports building and optimizing for the managed runtime environment.
|
|
1606
1723
|
*
|
|
1607
1724
|
* @param config - Configuration options for the plugin
|
|
@@ -1630,13 +1747,15 @@ function storefrontNextTargets(config = {}) {
|
|
|
1630
1747
|
} } = config;
|
|
1631
1748
|
const plugins = [
|
|
1632
1749
|
...process.env.SCAPI_PROXY_HOST ? [workspacePlugin()] : [],
|
|
1750
|
+
i18nPlugin(),
|
|
1633
1751
|
managedRuntimeBundlePlugin(),
|
|
1634
1752
|
fixReactRouterManifestUrlsPlugin(),
|
|
1635
1753
|
patchReactRouterPlugin(),
|
|
1636
1754
|
platformEntryPlugin(),
|
|
1637
1755
|
transformTargetPlaceholderPlugin(),
|
|
1638
1756
|
watchConfigFilesPlugin(),
|
|
1639
|
-
buildMiddlewareRegistryPlugin()
|
|
1757
|
+
buildMiddlewareRegistryPlugin(),
|
|
1758
|
+
ssrSourcemapFixPlugin()
|
|
1640
1759
|
];
|
|
1641
1760
|
if (staticRegistry?.componentPath && staticRegistry?.registryPath) {
|
|
1642
1761
|
plugins.push(staticRegistryPlugin(staticRegistry));
|
|
@@ -1647,6 +1766,97 @@ function storefrontNextTargets(config = {}) {
|
|
|
1647
1766
|
return plugins;
|
|
1648
1767
|
}
|
|
1649
1768
|
|
|
1769
|
+
//#endregion
|
|
1770
|
+
//#region src/plugins/uiTargetDevMode.ts
|
|
1771
|
+
const traverse = traverseModule.default || traverseModule;
|
|
1772
|
+
const UI_TARGET_JSX_RE = /<UITarget[\s/>]/;
|
|
1773
|
+
/**
|
|
1774
|
+
* Vite plugin that adds visual markers to UITarget components in development.
|
|
1775
|
+
*
|
|
1776
|
+
* PRODUCTION: This plugin is completely inactive - zero overhead.
|
|
1777
|
+
* DEVELOPMENT: Transforms UITarget JSX to add visual debugging markers.
|
|
1778
|
+
*
|
|
1779
|
+
* @example
|
|
1780
|
+
* // Source code:
|
|
1781
|
+
* <UITarget targetId="pdp.loyalty.badge">
|
|
1782
|
+
* <Widget />
|
|
1783
|
+
* </UITarget>
|
|
1784
|
+
*
|
|
1785
|
+
* // Transformed in DEV mode:
|
|
1786
|
+
* <UITargetDevMarker
|
|
1787
|
+
* targetId="pdp.loyalty.badge"
|
|
1788
|
+
* __file__="/src/components/product.tsx"
|
|
1789
|
+
* __hasChildren__={true}
|
|
1790
|
+
* >
|
|
1791
|
+
* <Widget />
|
|
1792
|
+
* </UITargetDevMarker>
|
|
1793
|
+
*/
|
|
1794
|
+
function uiTargetDevModePlugin(config = {}) {
|
|
1795
|
+
if (process.env.NODE_ENV === "production") return { name: "storefront-next:ui-target-dev-mode-noop" };
|
|
1796
|
+
if (!(config.enabled ?? process.env.VITE_UI_TARGET_DEV_MODE === "true")) return { name: "storefront-next:ui-target-dev-mode-disabled" };
|
|
1797
|
+
logger.info("🎯 UITarget Dev Mode enabled");
|
|
1798
|
+
if (config.filterCategory) logger.info(` Filtering to category: ${config.filterCategory} (build-time)`);
|
|
1799
|
+
return {
|
|
1800
|
+
name: "storefront-next:ui-target-dev-mode",
|
|
1801
|
+
enforce: "pre",
|
|
1802
|
+
transform(code, id) {
|
|
1803
|
+
if (!id.match(/\.(tsx|jsx)$/)) return null;
|
|
1804
|
+
if (id.includes("node_modules")) return null;
|
|
1805
|
+
if (id.includes("/targets/ui-target.tsx")) return null;
|
|
1806
|
+
if (!UI_TARGET_JSX_RE.test(code) || !code.includes("from '@/targets/ui-target'")) return null;
|
|
1807
|
+
try {
|
|
1808
|
+
const ast = parse(code, {
|
|
1809
|
+
sourceType: "module",
|
|
1810
|
+
plugins: [
|
|
1811
|
+
"typescript",
|
|
1812
|
+
"jsx",
|
|
1813
|
+
"decorators-legacy"
|
|
1814
|
+
]
|
|
1815
|
+
});
|
|
1816
|
+
let hasTransforms = false;
|
|
1817
|
+
const targetIds = [];
|
|
1818
|
+
traverse(ast, { JSXElement(path$2) {
|
|
1819
|
+
const openingElement = path$2.node.openingElement;
|
|
1820
|
+
if (!isJSXIdentifier(openingElement.name) || openingElement.name.name !== "UITarget") return;
|
|
1821
|
+
const targetIdAttr = openingElement.attributes.find((attr) => isJSXAttribute(attr) && isJSXIdentifier(attr.name) && attr.name.name === "targetId");
|
|
1822
|
+
if (!targetIdAttr) {
|
|
1823
|
+
logger.warn(`UITarget without targetId in ${id}`);
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
const targetId = isStringLiteral(targetIdAttr.value) ? targetIdAttr.value.value : null;
|
|
1827
|
+
if (!targetId) {
|
|
1828
|
+
logger.warn(`UITarget with non-string targetId in ${id}`);
|
|
1829
|
+
return;
|
|
1830
|
+
}
|
|
1831
|
+
if (config.filterCategory && !targetId.startsWith(`${config.filterCategory}.`)) return;
|
|
1832
|
+
const hasChildren = path$2.node.children.length > 0;
|
|
1833
|
+
openingElement.name = jsxIdentifier("UITargetDevMarker");
|
|
1834
|
+
if (path$2.node.closingElement) path$2.node.closingElement.name = jsxIdentifier("UITargetDevMarker");
|
|
1835
|
+
const hint = config.hintMap?.[targetId];
|
|
1836
|
+
openingElement.attributes.push(jsxAttribute(jsxIdentifier("__file__"), stringLiteral(id)), jsxAttribute(jsxIdentifier("__hasChildren__"), jsxExpressionContainer(booleanLiteral(hasChildren))), ...hint ? [jsxAttribute(jsxIdentifier("__hint__"), stringLiteral(hint))] : []);
|
|
1837
|
+
hasTransforms = true;
|
|
1838
|
+
targetIds.push(targetId);
|
|
1839
|
+
} });
|
|
1840
|
+
if (!hasTransforms) return null;
|
|
1841
|
+
const devMarkerImport = importDeclaration([importSpecifier(identifier("UITargetDevMarker"), identifier("UITargetDevMarker"))], stringLiteral("@/lib/ui-target-dev-mode/marker"));
|
|
1842
|
+
ast.program.body.unshift(devMarkerImport);
|
|
1843
|
+
const output = generate(ast, {
|
|
1844
|
+
retainLines: true,
|
|
1845
|
+
compact: false
|
|
1846
|
+
}, code);
|
|
1847
|
+
logger.debug(`Transformed ${targetIds.length} targets in ${id.split("/").pop()}`);
|
|
1848
|
+
return {
|
|
1849
|
+
code: output.code,
|
|
1850
|
+
map: output.map
|
|
1851
|
+
};
|
|
1852
|
+
} catch (error) {
|
|
1853
|
+
logger.error(`Failed to transform UITarget in ${id}:`, error);
|
|
1854
|
+
return null;
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1650
1860
|
//#endregion
|
|
1651
1861
|
//#region src/plugins/hybridProxy.ts
|
|
1652
1862
|
/**
|
|
@@ -2918,7 +3128,7 @@ const SKIP_DIRECTORIES = [
|
|
|
2918
3128
|
".next",
|
|
2919
3129
|
"coverage"
|
|
2920
3130
|
];
|
|
2921
|
-
const DEFAULT_COMPONENT_GROUP = "
|
|
3131
|
+
const DEFAULT_COMPONENT_GROUP = "storefrontnext_base";
|
|
2922
3132
|
const ARCH_TYPE_HEADLESS = "headless";
|
|
2923
3133
|
const VALID_ATTRIBUTE_TYPES = [
|
|
2924
3134
|
"string",
|
|
@@ -3415,6 +3625,14 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
|
|
|
3415
3625
|
}
|
|
3416
3626
|
};
|
|
3417
3627
|
await scanDirectory(srcDir);
|
|
3628
|
+
const configMetadataDir = join(projectRoot, "config-metadata");
|
|
3629
|
+
try {
|
|
3630
|
+
await access(configMetadataDir);
|
|
3631
|
+
await scanDirectory(configMetadataDir);
|
|
3632
|
+
} catch (error) {
|
|
3633
|
+
if (error.code === "ENOENT") logger.debug(` - Directory not found (skipping): ${configMetadataDir}`);
|
|
3634
|
+
else logger.warn(` - Unable to access ${configMetadataDir}:`, error.message);
|
|
3635
|
+
}
|
|
3418
3636
|
}
|
|
3419
3637
|
const allComponents = [];
|
|
3420
3638
|
const allPageTypes = [];
|
|
@@ -3471,5 +3689,5 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
|
|
|
3471
3689
|
}
|
|
3472
3690
|
|
|
3473
3691
|
//#endregion
|
|
3474
|
-
export { clearCache, createServer, storefrontNextTargets as default, extractPatterns, generateMetadata, hybridProxyPlugin, loadConfigFromEnv, loadProjectConfig, shouldRouteToNext, testPatterns, transformTargetPlaceholderPlugin, trimExtensions };
|
|
3692
|
+
export { clearCache, createServer, storefrontNextTargets as default, extractPatterns, generateMetadata, hybridProxyPlugin, loadConfigFromEnv, loadProjectConfig, shouldRouteToNext, testPatterns, transformTargetPlaceholderPlugin, trimExtensions, uiTargetDevModePlugin };
|
|
3475
3693
|
//# sourceMappingURL=index.js.map
|