@salesforce/b2c-cli 2.3.0 → 2.4.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/bin/dev.js +8 -8
- package/bin/run.js +5 -7
- package/content/guidance/b2c/b2c-business-manager-extensions/SKILL.md +361 -0
- package/content/guidance/b2c/b2c-business-manager-extensions/references/EXTENSIONS-XML.md +458 -0
- package/content/guidance/b2c/b2c-controllers/SKILL.md +301 -0
- package/content/guidance/b2c/b2c-controllers/references/CLASSIC-PATTERNS.md +335 -0
- package/content/guidance/b2c/b2c-controllers/references/SFRA-PATTERNS.md +400 -0
- package/content/guidance/b2c/b2c-custom-api-development/SKILL.md +281 -0
- package/content/guidance/b2c/b2c-custom-api-development/references/CONTRACT.md +142 -0
- package/content/guidance/b2c/b2c-custom-api-development/references/IMPLEMENTATION.md +153 -0
- package/content/guidance/b2c/b2c-custom-api-development/references/TESTING.md +118 -0
- package/content/guidance/b2c/b2c-custom-caches/SKILL.md +279 -0
- package/content/guidance/b2c/b2c-custom-job-steps/SKILL.md +520 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/CHUNK-ORIENTED.md +377 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/JOBS-XML.md +212 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/STEPTYPES-JSON.md +373 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/TASK-ORIENTED.md +344 -0
- package/content/guidance/b2c/b2c-custom-objects/SKILL.md +327 -0
- package/content/guidance/b2c/b2c-custom-objects/references/OCAPI-SEARCH.md +298 -0
- package/content/guidance/b2c/b2c-forms/SKILL.md +242 -0
- package/content/guidance/b2c/b2c-forms/references/FORM-XML.md +409 -0
- package/content/guidance/b2c/b2c-hooks/SKILL.md +500 -0
- package/content/guidance/b2c/b2c-hooks/references/OCAPI-SCAPI-HOOKS.md +403 -0
- package/content/guidance/b2c/b2c-hooks/references/ORDER-HOOK-LIFECYCLE.md +169 -0
- package/content/guidance/b2c/b2c-hooks/references/SYSTEM-HOOKS.md +433 -0
- package/content/guidance/b2c/b2c-isml/SKILL.md +320 -0
- package/content/guidance/b2c/b2c-isml/references/EXPRESSIONS.md +366 -0
- package/content/guidance/b2c/b2c-isml/references/TAGS.md +443 -0
- package/content/guidance/b2c/b2c-localization/SKILL.md +344 -0
- package/content/guidance/b2c/b2c-localization/references/PATTERNS.md +407 -0
- package/content/guidance/b2c/b2c-logging/SKILL.md +352 -0
- package/content/guidance/b2c/b2c-logging/references/LOG-FILES.md +282 -0
- package/content/guidance/b2c/b2c-metadata/SKILL.md +406 -0
- package/content/guidance/b2c/b2c-metadata/references/SYSTEM-OBJECTS.md +320 -0
- package/content/guidance/b2c/b2c-metadata/references/XML-EXAMPLES.md +366 -0
- package/content/guidance/b2c/b2c-onboarding/SKILL.md +154 -0
- package/content/guidance/b2c/b2c-ordering/SKILL.md +391 -0
- package/content/guidance/b2c/b2c-page-designer/SKILL.md +410 -0
- package/content/guidance/b2c/b2c-page-designer/references/ATTRIBUTE-TYPES.md +436 -0
- package/content/guidance/b2c/b2c-page-designer/references/META-DEFINITIONS.md +340 -0
- package/content/guidance/b2c/b2c-querying-data/SKILL.md +289 -0
- package/content/guidance/b2c/b2c-querying-data/references/PERFORMANCE-APIS.md +74 -0
- package/content/guidance/b2c/b2c-scapi-admin/SKILL.md +53 -0
- package/content/guidance/b2c/b2c-scapi-admin/references/CLIENT-EXAMPLES.md +440 -0
- package/content/guidance/b2c/b2c-scapi-admin/references/INTEGRATION-PATTERNS.md +518 -0
- package/content/guidance/b2c/b2c-scapi-admin/references/OAUTH-SCOPES.md +337 -0
- package/content/guidance/b2c/b2c-scapi-shopper/SKILL.md +56 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/CHECKOUT-FLOW.md +466 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/CLIENT-EXAMPLES.md +351 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/COMMON-PATTERNS.md +390 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/SCOPES.md +290 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/SKILL.md +420 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/references/PASSKEYS.md +126 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/references/SESSION-BRIDGE.md +267 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/references/TOKEN-LIFECYCLE.md +367 -0
- package/content/guidance/b2c/b2c-webservices/SKILL.md +318 -0
- package/content/guidance/b2c/b2c-webservices/references/FTP-SERVICES.md +524 -0
- package/content/guidance/b2c/b2c-webservices/references/HTTP-SERVICES.md +578 -0
- package/content/guidance/b2c/b2c-webservices/references/SERVICES-XML.md +351 -0
- package/content/guidance/b2c/b2c-webservices/references/SOAP-SERVICES.md +587 -0
- package/content/guidance/b2c-cli/b2c-am/SKILL.md +277 -0
- package/content/guidance/b2c-cli/b2c-bm-users-roles/SKILL.md +210 -0
- package/content/guidance/b2c-cli/b2c-cap/SKILL.md +131 -0
- package/content/guidance/b2c-cli/b2c-cip/SKILL.md +116 -0
- package/content/guidance/b2c-cli/b2c-cip/references/KNOWN_TABLES.md +105 -0
- package/content/guidance/b2c-cli/b2c-cip/references/SALES_ANALYSIS.md +50 -0
- package/content/guidance/b2c-cli/b2c-cip/references/STARTER_QUERIES.md +147 -0
- package/content/guidance/b2c-cli/b2c-code/SKILL.md +146 -0
- package/content/guidance/b2c-cli/b2c-config/SKILL.md +462 -0
- package/content/guidance/b2c-cli/b2c-content/SKILL.md +176 -0
- package/content/guidance/b2c-cli/b2c-debug/SKILL.md +138 -0
- package/content/guidance/b2c-cli/b2c-docs/SKILL.md +301 -0
- package/content/guidance/b2c-cli/b2c-ecdn/SKILL.md +135 -0
- package/content/guidance/b2c-cli/b2c-ecdn/references/ADVANCED.md +97 -0
- package/content/guidance/b2c-cli/b2c-ecdn/references/SECURITY.md +75 -0
- package/content/guidance/b2c-cli/b2c-import-set-migrations/SKILL.md +265 -0
- package/content/guidance/b2c-cli/b2c-job/SKILL.md +64 -0
- package/content/guidance/b2c-cli/b2c-job/references/EXPORT.md +121 -0
- package/content/guidance/b2c-cli/b2c-job/references/IMPORT.md +55 -0
- package/content/guidance/b2c-cli/b2c-job/references/RUN-AND-MONITOR.md +119 -0
- package/content/guidance/b2c-cli/b2c-logs/SKILL.md +249 -0
- package/content/guidance/b2c-cli/b2c-metrics/SKILL.md +315 -0
- package/content/guidance/b2c-cli/b2c-mrt/SKILL.md +207 -0
- package/content/guidance/b2c-cli/b2c-mrt/references/BUNDLE-COMMANDS.md +203 -0
- package/content/guidance/b2c-cli/b2c-mrt/references/ENVIRONMENT-COMMANDS.md +218 -0
- package/content/guidance/b2c-cli/b2c-mrt/references/PROJECT-COMMANDS.md +154 -0
- package/content/guidance/b2c-cli/b2c-sandbox/SKILL.md +112 -0
- package/content/guidance/b2c-cli/b2c-scapi-custom/SKILL.md +126 -0
- package/content/guidance/b2c-cli/b2c-scapi-schemas/SKILL.md +66 -0
- package/content/guidance/b2c-cli/b2c-scapi-schemas/references/CLI-EXAMPLES.md +108 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/SKILL.md +57 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/references/IMPORT-OPTIONS.md +118 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/references/METADATA-XML.md +381 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/references/WORKFLOWS.md +182 -0
- package/content/guidance/b2c-cli/b2c-sites/SKILL.md +112 -0
- package/content/guidance/b2c-cli/b2c-slas/SKILL.md +182 -0
- package/content/guidance/b2c-cli/b2c-webdav/SKILL.md +186 -0
- package/content/guidance/b2c-ops/b2c-checkout-triage/SKILL.md +68 -0
- package/content/guidance/b2c-ops/b2c-job-health/SKILL.md +75 -0
- package/content/guidance/b2c-ops/b2c-job-health/references/job-logs.md +21 -0
- package/content/guidance/b2c-ops/b2c-order-failure-triage/SKILL.md +66 -0
- package/content/guidance/b2c-ops/b2c-order-failure-triage/references/order-evidence.md +87 -0
- package/content/guidance/b2c-ops/b2c-production-triage/SKILL.md +87 -0
- package/content/guidance/b2c-ops/b2c-production-triage/references/escalation.md +57 -0
- package/content/guidance/index.json +1949 -0
- package/content/guidance/storefront-next/sfnext-accessibility/SKILL.md +103 -0
- package/content/guidance/storefront-next/sfnext-accessibility/references/checklist.md +64 -0
- package/content/guidance/storefront-next/sfnext-analytics-consent/SKILL.md +91 -0
- package/content/guidance/storefront-next/sfnext-analytics-consent/references/CUSTOM-ADAPTER.md +49 -0
- package/content/guidance/storefront-next/sfnext-authentication/SKILL.md +127 -0
- package/content/guidance/storefront-next/sfnext-authentication/references/COOKIES.md +39 -0
- package/content/guidance/storefront-next/sfnext-authentication/references/LOGIN-FLOWS.md +59 -0
- package/content/guidance/storefront-next/sfnext-commerce-features/SKILL.md +67 -0
- package/content/guidance/storefront-next/sfnext-commerce-features/references/FEATURE-PREREQUISITES.md +37 -0
- package/content/guidance/storefront-next/sfnext-components/SKILL.md +153 -0
- package/content/guidance/storefront-next/sfnext-components/references/COMPONENT-AUTHORING.md +118 -0
- package/content/guidance/storefront-next/sfnext-components/references/SHAPE-TOKENS.md +51 -0
- package/content/guidance/storefront-next/sfnext-components/references/STORYBOOK.md +54 -0
- package/content/guidance/storefront-next/sfnext-components/references/TOKEN-SYSTEM.md +53 -0
- package/content/guidance/storefront-next/sfnext-components/references/TROUBLESHOOTING.md +19 -0
- package/content/guidance/storefront-next/sfnext-configuration/SKILL.md +163 -0
- package/content/guidance/storefront-next/sfnext-configuration/references/ENV-VARIABLES.md +55 -0
- package/content/guidance/storefront-next/sfnext-configuration/references/MULTI-SITE-URLS.md +137 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/SKILL.md +140 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/references/ACTIONS.md +53 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/references/API-CLIENTS.md +34 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/references/LOADERS.md +73 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/references/SCAPI-FETCHER.md +39 -0
- package/content/guidance/storefront-next/sfnext-deployment/SKILL.md +127 -0
- package/content/guidance/storefront-next/sfnext-deployment/references/MRT-DEPLOYMENT.md +59 -0
- package/content/guidance/storefront-next/sfnext-extensions/SKILL.md +118 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/ACTION-HOOKS.md +57 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/BASE-AUDIT.md +68 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/CLI-AND-INSTALL.md +58 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/EXTENSION-EXAMPLES.md +79 -0
- package/content/guidance/storefront-next/sfnext-hybrid-storefronts/SKILL.md +93 -0
- package/content/guidance/storefront-next/sfnext-hybrid-storefronts/references/HYBRID-PROXY-CONFIG.md +86 -0
- package/content/guidance/storefront-next/sfnext-i18n/SKILL.md +152 -0
- package/content/guidance/storefront-next/sfnext-i18n/references/locale-config.md +35 -0
- package/content/guidance/storefront-next/sfnext-overview/SKILL.md +101 -0
- package/content/guidance/storefront-next/sfnext-page-designer/SKILL.md +202 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/BUSINESS-MANAGER.md +46 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/COMPONENT-REGISTRY.md +111 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/DECORATOR-PATTERNS.md +168 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/REVIEW-CHECKLIST.md +83 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/TROUBLESHOOTING.md +35 -0
- package/content/guidance/storefront-next/sfnext-performance/SKILL.md +102 -0
- package/content/guidance/storefront-next/sfnext-performance/references/PERFORMANCE-REVIEW-CHECKLIST.md +85 -0
- package/content/guidance/storefront-next/sfnext-performance/references/SUSPENSE-AND-STREAMING.md +58 -0
- package/content/guidance/storefront-next/sfnext-project-setup/SKILL.md +147 -0
- package/content/guidance/storefront-next/sfnext-project-setup/references/PROJECT-STRUCTURE.md +61 -0
- package/content/guidance/storefront-next/sfnext-project-setup/references/SCRIPTS.md +47 -0
- package/content/guidance/storefront-next/sfnext-project-setup/references/SFNEXT-CLI.md +63 -0
- package/content/guidance/storefront-next/sfnext-quality-gates/SKILL.md +70 -0
- package/content/guidance/storefront-next/sfnext-quality-gates/references/lint-and-budgets.md +39 -0
- package/content/guidance/storefront-next/sfnext-revalidation/SKILL.md +121 -0
- package/content/guidance/storefront-next/sfnext-revalidation/references/POLICIES-AND-TAGS.md +55 -0
- package/content/guidance/storefront-next/sfnext-routing/SKILL.md +123 -0
- package/content/guidance/storefront-next/sfnext-routing/references/ROUTE-CONVENTIONS.md +65 -0
- package/content/guidance/storefront-next/sfnext-routing/references/URLS-AND-SEO-ROUTES.md +22 -0
- package/content/guidance/storefront-next/sfnext-scapi/SKILL.md +126 -0
- package/content/guidance/storefront-next/sfnext-scapi/references/WORKED-EXAMPLE.md +39 -0
- package/content/guidance/storefront-next/sfnext-security/SKILL.md +129 -0
- package/content/guidance/storefront-next/sfnext-security/references/COOKIE-DOMAIN.md +39 -0
- package/content/guidance/storefront-next/sfnext-security/references/TURNSTILE.md +43 -0
- package/content/guidance/storefront-next/sfnext-seo/SKILL.md +95 -0
- package/content/guidance/storefront-next/sfnext-seo/references/MULTI-DOMAIN-BASE-PATH.md +17 -0
- package/content/guidance/storefront-next/sfnext-seo/references/SEO-ROUTES.md +31 -0
- package/content/guidance/storefront-next/sfnext-state-management/SKILL.md +114 -0
- package/content/guidance/storefront-next/sfnext-state-management/references/PATTERNS.md +45 -0
- package/content/guidance/storefront-next/sfnext-testing/SKILL.md +151 -0
- package/content/guidance/storefront-next/sfnext-testing/references/E2E.md +38 -0
- package/content/guidance/storefront-next/sfnext-testing/references/STORYBOOK-PATTERNS.md +97 -0
- package/content/guidance/storefront-next/sfnext-testing/references/UNIT-AND-ROUTE-TESTS.md +60 -0
- package/content/guidance/storefront-next/sfnext-theming/SKILL.md +104 -0
- package/content/guidance/storefront-next/sfnext-theming/references/REBRAND-CHECKLIST.md +29 -0
- package/dist/commands/cap/install.d.ts +1 -0
- package/dist/commands/cap/list.d.ts +1 -0
- package/dist/commands/cap/pull.d.ts +1 -0
- package/dist/commands/cap/tasks.d.ts +1 -0
- package/dist/commands/cap/uninstall.d.ts +1 -0
- package/dist/commands/cip/describe.d.ts +1 -0
- package/dist/commands/cip/query.d.ts +1 -0
- package/dist/commands/cip/report/bot-traffic-share.d.ts +1 -0
- package/dist/commands/cip/report/checkout-funnel-dropoff.d.ts +1 -0
- package/dist/commands/cip/report/controller-error-rate-trend.d.ts +1 -0
- package/dist/commands/cip/report/controller-health-scorecard.d.ts +1 -0
- package/dist/commands/cip/report/customer-registration-trends.d.ts +1 -0
- package/dist/commands/cip/report/discount-depth-breakdown.d.ts +1 -0
- package/dist/commands/cip/report/inventory-stockout-by-location.d.ts +1 -0
- package/dist/commands/cip/report/new-vs-returning-buyer-revenue.d.ts +1 -0
- package/dist/commands/cip/report/ocapi-client-usage.d.ts +1 -0
- package/dist/commands/cip/report/ocapi-requests.d.ts +1 -0
- package/dist/commands/cip/report/payment-method-performance.d.ts +1 -0
- package/dist/commands/cip/report/product-co-purchase-analysis.d.ts +1 -0
- package/dist/commands/cip/report/promotion-discount-analysis.d.ts +1 -0
- package/dist/commands/cip/report/promotion-roi-leaderboard.d.ts +1 -0
- package/dist/commands/cip/report/recommender-effectiveness.d.ts +1 -0
- package/dist/commands/cip/report/remote-include-performance.d.ts +1 -0
- package/dist/commands/cip/report/revenue-by-channel.d.ts +1 -0
- package/dist/commands/cip/report/sales-analytics.d.ts +1 -0
- package/dist/commands/cip/report/sales-summary.d.ts +1 -0
- package/dist/commands/cip/report/scapi-cache-hit-ratio.d.ts +1 -0
- package/dist/commands/cip/report/scapi-error-rate-by-status.d.ts +1 -0
- package/dist/commands/cip/report/scapi-latency-distribution.d.ts +1 -0
- package/dist/commands/cip/report/scapi-traffic-latency.d.ts +1 -0
- package/dist/commands/cip/report/search-query-performance.d.ts +1 -0
- package/dist/commands/cip/report/top-referrers.d.ts +1 -0
- package/dist/commands/cip/report/top-selling-products.d.ts +1 -0
- package/dist/commands/cip/report/zero-result-searches.d.ts +1 -0
- package/dist/commands/cip/tables.d.ts +1 -0
- package/dist/commands/code/activate.d.ts +1 -0
- package/dist/commands/code/delete.d.ts +1 -0
- package/dist/commands/code/deploy.d.ts +1 -0
- package/dist/commands/code/download.d.ts +1 -0
- package/dist/commands/code/watch.d.ts +1 -0
- package/dist/commands/commands/search.d.ts +35 -0
- package/dist/commands/commands/search.js +85 -0
- package/dist/commands/commands/search.js.map +1 -0
- package/dist/commands/content/export.d.ts +1 -0
- package/dist/commands/content/list.d.ts +1 -0
- package/dist/commands/content/validate.d.ts +1 -0
- package/dist/commands/debug/cli.d.ts +1 -0
- package/dist/commands/debug/index.d.ts +1 -0
- package/dist/commands/docs/cache.d.ts +1 -0
- package/dist/commands/docs/download.d.ts +1 -0
- package/dist/commands/docs/read.d.ts +1 -0
- package/dist/commands/docs/schema.d.ts +1 -0
- package/dist/commands/docs/search.d.ts +1 -0
- package/dist/commands/docs/skill.d.ts +41 -0
- package/dist/commands/docs/skill.js +183 -0
- package/dist/commands/docs/skill.js.map +1 -0
- package/dist/commands/ecdn/cache/purge.d.ts +1 -0
- package/dist/commands/ecdn/certificates/add.d.ts +1 -0
- package/dist/commands/ecdn/certificates/delete.d.ts +1 -0
- package/dist/commands/ecdn/certificates/list.d.ts +1 -0
- package/dist/commands/ecdn/certificates/update.d.ts +1 -0
- package/dist/commands/ecdn/certificates/validate.d.ts +1 -0
- package/dist/commands/ecdn/cipher-suites/get.d.ts +1 -0
- package/dist/commands/ecdn/cipher-suites/update.d.ts +1 -0
- package/dist/commands/ecdn/firewall/create.d.ts +1 -0
- package/dist/commands/ecdn/firewall/delete.d.ts +1 -0
- package/dist/commands/ecdn/firewall/get.d.ts +1 -0
- package/dist/commands/ecdn/firewall/list.d.ts +1 -0
- package/dist/commands/ecdn/firewall/reorder.d.ts +1 -0
- package/dist/commands/ecdn/firewall/update.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/create.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/delete.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/get.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/list.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/update.d.ts +1 -0
- package/dist/commands/ecdn/logpush/ownership.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/create.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/delete.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/get.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/rules/delete.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/rules/update.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/update.d.ts +1 -0
- package/dist/commands/ecdn/mtls/create.d.ts +1 -0
- package/dist/commands/ecdn/mtls/delete.d.ts +1 -0
- package/dist/commands/ecdn/mtls/get.d.ts +1 -0
- package/dist/commands/ecdn/mtls/issue.d.ts +1 -0
- package/dist/commands/ecdn/mtls/list.d.ts +1 -0
- package/dist/commands/ecdn/mtls/setup.d.ts +1 -0
- package/dist/commands/ecdn/origin-headers/delete.d.ts +1 -0
- package/dist/commands/ecdn/origin-headers/get.d.ts +1 -0
- package/dist/commands/ecdn/origin-headers/set.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/notifications/create.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/notifications/delete.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/notifications/list.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/create.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/delete.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/get.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/list.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/update.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/scripts/get.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/scripts/list.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/create.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/delete.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/get.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/list.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/update.d.ts +1 -0
- package/dist/commands/ecdn/security/get.d.ts +1 -0
- package/dist/commands/ecdn/security/update.d.ts +1 -0
- package/dist/commands/ecdn/speed/get.d.ts +1 -0
- package/dist/commands/ecdn/speed/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/groups/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/groups/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/managed-rules/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/managed-rules/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/migrate.d.ts +1 -0
- package/dist/commands/ecdn/waf/owasp/get.d.ts +1 -0
- package/dist/commands/ecdn/waf/owasp/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/rules/get.d.ts +1 -0
- package/dist/commands/ecdn/waf/rules/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/rules/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/rulesets/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/rulesets/update.d.ts +1 -0
- package/dist/commands/ecdn/zones/create.d.ts +1 -0
- package/dist/commands/ecdn/zones/list.d.ts +1 -0
- package/dist/commands/job/execution/delete.d.ts +1 -0
- package/dist/commands/job/export.d.ts +1 -0
- package/dist/commands/job/import-set.d.ts +1 -0
- package/dist/commands/job/import.d.ts +1 -0
- package/dist/commands/job/log.d.ts +1 -0
- package/dist/commands/job/run.d.ts +1 -0
- package/dist/commands/job/search.d.ts +1 -0
- package/dist/commands/job/wait.d.ts +1 -0
- package/dist/commands/logs/get.d.ts +1 -0
- package/dist/commands/logs/list.d.ts +1 -0
- package/dist/commands/logs/tail.d.ts +1 -0
- package/dist/commands/metrics/controller.d.ts +1 -0
- package/dist/commands/metrics/ecdn.d.ts +1 -0
- package/dist/commands/metrics/mrt.d.ts +1 -0
- package/dist/commands/metrics/ocapi.d.ts +1 -0
- package/dist/commands/metrics/overall.d.ts +1 -0
- package/dist/commands/metrics/sales.d.ts +1 -0
- package/dist/commands/metrics/scapi-hooks.d.ts +1 -0
- package/dist/commands/metrics/scapi.d.ts +1 -0
- package/dist/commands/metrics/third-party.d.ts +1 -0
- package/dist/commands/mrt/bundle/delete.d.ts +1 -0
- package/dist/commands/mrt/bundle/deploy.d.ts +1 -0
- package/dist/commands/mrt/bundle/download.d.ts +1 -0
- package/dist/commands/mrt/bundle/history.d.ts +1 -0
- package/dist/commands/mrt/bundle/list.d.ts +1 -0
- package/dist/commands/mrt/bundle/save.d.ts +1 -0
- package/dist/commands/mrt/bundle/upload-v2.d.ts +1 -0
- package/dist/commands/mrt/env/access-control/list.d.ts +1 -0
- package/dist/commands/mrt/env/b2c.d.ts +1 -0
- package/dist/commands/mrt/env/clone.d.ts +1 -0
- package/dist/commands/mrt/env/create.d.ts +1 -0
- package/dist/commands/mrt/env/delete.d.ts +1 -0
- package/dist/commands/mrt/env/get.d.ts +1 -0
- package/dist/commands/mrt/env/invalidate.d.ts +1 -0
- package/dist/commands/mrt/env/list.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/clone.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/create.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/delete.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/list.d.ts +1 -0
- package/dist/commands/mrt/env/update.d.ts +1 -0
- package/dist/commands/mrt/env/var/delete.d.ts +1 -0
- package/dist/commands/mrt/env/var/list.d.ts +1 -0
- package/dist/commands/mrt/env/var/push.d.ts +1 -0
- package/dist/commands/mrt/env/var/set.d.ts +1 -0
- package/dist/commands/mrt/org/b2c.d.ts +1 -0
- package/dist/commands/mrt/org/cert/create.d.ts +1 -0
- package/dist/commands/mrt/org/cert/delete.d.ts +1 -0
- package/dist/commands/mrt/org/cert/get.d.ts +1 -0
- package/dist/commands/mrt/org/cert/list.d.ts +1 -0
- package/dist/commands/mrt/org/cert/restart-validation.d.ts +1 -0
- package/dist/commands/mrt/org/list.d.ts +1 -0
- package/dist/commands/mrt/org/member/add.d.ts +1 -0
- package/dist/commands/mrt/org/member/get.d.ts +1 -0
- package/dist/commands/mrt/org/member/list.d.ts +1 -0
- package/dist/commands/mrt/org/member/remove.d.ts +1 -0
- package/dist/commands/mrt/org/member/update.d.ts +1 -0
- package/dist/commands/mrt/project/create.d.ts +1 -0
- package/dist/commands/mrt/project/delete.d.ts +1 -0
- package/dist/commands/mrt/project/get.d.ts +1 -0
- package/dist/commands/mrt/project/list.d.ts +1 -0
- package/dist/commands/mrt/project/member/add.d.ts +1 -0
- package/dist/commands/mrt/project/member/get.d.ts +1 -0
- package/dist/commands/mrt/project/member/list.d.ts +1 -0
- package/dist/commands/mrt/project/member/remove.d.ts +1 -0
- package/dist/commands/mrt/project/member/update.d.ts +1 -0
- package/dist/commands/mrt/project/notification/delete.d.ts +1 -0
- package/dist/commands/mrt/project/notification/get.d.ts +1 -0
- package/dist/commands/mrt/project/update.d.ts +1 -0
- package/dist/commands/mrt/save-credentials.d.ts +1 -0
- package/dist/commands/mrt/tail-logs.d.ts +1 -0
- package/dist/commands/mrt/user/api-key.d.ts +1 -0
- package/dist/commands/mrt/user/email-prefs.d.ts +1 -0
- package/dist/commands/mrt/user/profile.d.ts +1 -0
- package/dist/commands/scaffold/init.js +2 -1
- package/dist/commands/scaffold/init.js.map +1 -1
- package/dist/commands/scapi/custom/status.d.ts +1 -0
- package/dist/commands/scapi/schemas/get.d.ts +1 -0
- package/dist/commands/scapi/schemas/list.d.ts +1 -0
- package/dist/commands/setup/ide/tsserver-plugin.d.ts +1 -0
- package/dist/commands/setup/ide/vscode-types.d.ts +1 -0
- package/dist/commands/setup/index.js +4 -3
- package/dist/commands/setup/index.js.map +1 -1
- package/dist/commands/setup/inspect.d.ts +1 -0
- package/dist/commands/setup/inspect.js +17 -2
- package/dist/commands/setup/inspect.js.map +1 -1
- package/dist/commands/setup/instance/create.d.ts +1 -0
- package/dist/commands/setup/instance/list.d.ts +1 -0
- package/dist/commands/setup/instance/remove.d.ts +1 -0
- package/dist/commands/setup/instance/set-active.d.ts +1 -0
- package/dist/commands/setup/openshell.d.ts +1 -0
- package/dist/commands/setup/skills.d.ts +1 -0
- package/dist/commands/slas/client/create.d.ts +1 -0
- package/dist/commands/slas/client/delete.d.ts +1 -0
- package/dist/commands/slas/client/get.d.ts +1 -0
- package/dist/commands/slas/client/list.d.ts +1 -0
- package/dist/commands/slas/client/open.d.ts +1 -0
- package/dist/commands/slas/client/update.d.ts +1 -0
- package/dist/commands/slas/token.d.ts +1 -0
- package/dist/commands/slas/token.js +2 -1
- package/dist/commands/slas/token.js.map +1 -1
- package/dist/commands/webdav/get.d.ts +1 -0
- package/dist/commands/webdav/mkdir.d.ts +2 -0
- package/dist/commands/webdav/mkdir.js +5 -3
- package/dist/commands/webdav/mkdir.js.map +1 -1
- package/dist/commands/webdav/put.d.ts +2 -0
- package/dist/commands/webdav/put.js +5 -3
- package/dist/commands/webdav/put.js.map +1 -1
- package/dist/commands/webdav/rm.d.ts +1 -0
- package/dist/help.d.ts +25 -0
- package/dist/help.js +96 -0
- package/dist/help.js.map +1 -0
- package/dist/lib/scaffold/generate-helper.js +3 -2
- package/dist/lib/scaffold/generate-helper.js.map +1 -1
- package/dist/lib/skills.d.ts +19 -0
- package/dist/lib/skills.js +75 -0
- package/dist/lib/skills.js.map +1 -0
- package/dist/utils/cip/command.d.ts +1 -0
- package/dist/utils/ecdn/zone-command.d.ts +1 -0
- package/dist/utils/slas/client.d.ts +1 -0
- package/oclif.manifest.json +10994 -7653
- package/package.json +11 -5
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sfnext-page-designer
|
|
3
|
+
description: >-
|
|
4
|
+
Build merchant-editable Page Designer content in a Storefront Next project: components decorated with @Component/@AttributeDefinition/@RegionDefinition, page routes decorated with @PageType, <Region> rendering, fetchPageWithComponentData, component loaders and fallbacks, the generated static registry, and the pnpm cartridge:generate / cartridge:validate / cartridge:deploy workflow. Use when adding or editing a Page Designer component, exposing a region on a route (home, PDP, PLP, search), fetching a page by pageId or aspectType, marking a Region critical, debugging a component that does not appear in the Business Manager palette or renders empty, or reviewing Page Designer code. Do not use for classic ISML/SFRA Page Designer (use `b2c:b2c-page-designer`), for turning a Figma frame into blocks (use `figma-to-sfnext-pagedesigner:figma-to-sfnext-pagedesigner`), or for plain React components that merchants never edit (use `storefront-next:sfnext-components`).
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Page Designer in Storefront Next
|
|
8
|
+
|
|
9
|
+
In Storefront Next your React components are the Page Designer components. You annotate them with decorators, the build generates Business Manager metadata (JSON) from those decorators, and at runtime a route loader fetches the page structure from the Shopper Experience API and `<Region>` renders it.
|
|
10
|
+
|
|
11
|
+
The in-project guide is `docs/README-PAGE-DESIGNER.md` and the rules in `AGENTS.md` apply; this skill is the task-oriented path through them. Where the guide and the source disagree, trust the source.
|
|
12
|
+
|
|
13
|
+
## Pieces and where they live
|
|
14
|
+
|
|
15
|
+
| Piece | Location in your project |
|
|
16
|
+
|-------|--------------------------|
|
|
17
|
+
| Decorators (`Component`, `AttributeDefinition`, `RegionDefinition`, `PageType`) | `@/lib/decorators` (`src/lib/decorators/`) |
|
|
18
|
+
| Page fetch with per-component loader promises | `fetchPageWithComponentData` in `@/lib/page-designer/page-loader.server` |
|
|
19
|
+
| Single-component fetch (preview, embedded) | `fetchComponentWithComponentData` in `@/lib/page-designer/component-loader.server` |
|
|
20
|
+
| Region renderer | `Region` from `@/components/region` |
|
|
21
|
+
| Generated registry (do not hand-edit) | `src/lib/page-designer/static-registry.ts` |
|
|
22
|
+
| Runtime hooks (`usePageDesignerMode`) | `@salesforce/storefront-next-runtime/design/react/core` |
|
|
23
|
+
| Design/preview mode detection in loaders | `isDesignModeActive` / `isPreviewModeActive` from `@salesforce/storefront-next-runtime/design/mode` |
|
|
24
|
+
| Generated Business Manager metadata | `cartridges/app_storefrontnext_base/cartridge/experience/{components,pages,aspects}` |
|
|
25
|
+
|
|
26
|
+
The runtime package's `/design` entry exports only the registry. Decorators come from your project, not from `@salesforce/storefront-next-runtime`.
|
|
27
|
+
|
|
28
|
+
## Workflow
|
|
29
|
+
|
|
30
|
+
1. Create or edit the component (metadata class, props, default export, `fallback`, optional `loader`) under `src/components/<name>/index.tsx`.
|
|
31
|
+
2. Run `pnpm dev` (or `pnpm build`). The Vite plugin scans `src/components` for `@Component` and rewrites the registry between the `STATIC_REGISTRY_START/END` markers.
|
|
32
|
+
3. `pnpm cartridge:generate` writes the metadata JSON (`pnpm build` runs it for you).
|
|
33
|
+
4. `pnpm cartridge:validate` checks the generated JSON against the schemas. Generation also fails on an invalid attribute config (for example a bad `searching` combination).
|
|
34
|
+
5. `pnpm cartridge:deploy` uploads the cartridge to the B2C instance (`pnpm cartridge:deploy -- --delete` removes old cartridge files first). The optional MCP tool `cartridge_deploy` does the same. See `b2c-cli:b2c-code` for credentials and code-version handling.
|
|
35
|
+
6. In Business Manager, merchants build pages from the new palette entries. See [Business Manager](references/BUSINESS-MANAGER.md).
|
|
36
|
+
|
|
37
|
+
## A component
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
// src/components/promo-banner/index.tsx
|
|
41
|
+
import { AttributeDefinition, Component, RegionDefinition } from '@/lib/decorators';
|
|
42
|
+
import { DynamicImage } from '@/components/dynamic-image';
|
|
43
|
+
import { type Image } from '@/types';
|
|
44
|
+
import { cn } from '@/lib/utils';
|
|
45
|
+
|
|
46
|
+
@Component('promoBanner', {
|
|
47
|
+
name: 'Promo Banner',
|
|
48
|
+
description: 'Headline and optional image. Headline text and alignment are editable.',
|
|
49
|
+
group: 'Content',
|
|
50
|
+
})
|
|
51
|
+
@RegionDefinition([])
|
|
52
|
+
export class PromoBannerMetadata {
|
|
53
|
+
@AttributeDefinition({ id: 'headline', name: 'Headline', type: 'string', required: true, defaultValue: 'Spring sale' })
|
|
54
|
+
headline?: string;
|
|
55
|
+
|
|
56
|
+
@AttributeDefinition({ id: 'image', name: 'Image', type: 'image' })
|
|
57
|
+
image?: Image;
|
|
58
|
+
|
|
59
|
+
@AttributeDefinition({ id: 'align', name: 'Alignment', type: 'enum', values: ['left', 'center'], defaultValue: 'left' })
|
|
60
|
+
align?: 'left' | 'center';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface PromoBannerProps {
|
|
64
|
+
headline?: string;
|
|
65
|
+
image?: Image;
|
|
66
|
+
align?: 'left' | 'center';
|
|
67
|
+
className?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Page Designer also injects component, data, designMetadata and regionId.
|
|
71
|
+
// Never spread them onto a DOM element; destructure them out first if you forward ...rest.
|
|
72
|
+
export default function PromoBanner({ headline = 'Spring sale', image, align = 'left', className }: PromoBannerProps) {
|
|
73
|
+
return (
|
|
74
|
+
<section className={cn('relative', align === 'center' && 'text-center', className)}>
|
|
75
|
+
{image?.url && <DynamicImage src={image.url} alt="" />}
|
|
76
|
+
<h2>{headline}</h2>
|
|
77
|
+
</section>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// REQUIRED. Rendered in a Suspense boundary; receives the same attribute props. Keep it light.
|
|
82
|
+
export function fallback() {
|
|
83
|
+
return <div className="h-48 animate-pulse bg-muted" />;
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Rules that matter:
|
|
88
|
+
|
|
89
|
+
- Attribute values arrive as props named after the class field, so the field name and `id` must agree.
|
|
90
|
+
- The metadata class must be `export`ed; the generator only sees exported classes.
|
|
91
|
+
- The `typeId` must be a string literal. It is stored as `<group>.<typeId>` (default group `storefrontnext_base`), for example `Content.promoBanner`. Use the `group` option (`Content`, `Layout`, ...) so related components sit together in the palette.
|
|
92
|
+
- An `image` attribute delivers an object (`{ url, focalPoint?, metaData? }`), typed `Image` from `@/types`, not a string. Check `src/components/hero/index.tsx` for the pattern.
|
|
93
|
+
- `name` and `description` are what merchants read in Business Manager; omitted ones fall back to the raw id.
|
|
94
|
+
- Full option tables, attribute types, enums, nested regions and cross-group refs: [Decorator Patterns](references/DECORATOR-PATTERNS.md).
|
|
95
|
+
|
|
96
|
+
## A loader
|
|
97
|
+
|
|
98
|
+
Export a callable named `loader` when the component needs data (products, categories, your own API). It receives `{ componentData, context, request }`; `componentData` is the whole SCAPI component object, so merchant-set attributes live at `componentData.data`.
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
// src/components/promo-products/loaders.ts
|
|
102
|
+
import type { LoaderFunctionArgs } from 'react-router';
|
|
103
|
+
import type { ShopperExperience } from '@/scapi';
|
|
104
|
+
import { fetchProductsByIds } from '@/lib/api/products.server';
|
|
105
|
+
|
|
106
|
+
export const loader = async (args: { componentData: unknown; context: LoaderFunctionArgs['context'] }) => {
|
|
107
|
+
const comp = args.componentData as ShopperExperience.schemas['Component'];
|
|
108
|
+
const { productIds } = (comp.data ?? {}) as { productIds?: string };
|
|
109
|
+
if (!productIds) return null;
|
|
110
|
+
return fetchProductsByIds(args.context, productIds.split(','));
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
// src/components/promo-products/index.tsx
|
|
116
|
+
export { loader } from './loaders';
|
|
117
|
+
export function fallback() { /* skeleton with reserved height */ }
|
|
118
|
+
export default function PromoProducts({ data }: { data?: Product[] | null }) { /* ... */ }
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- The exported `loader` must be a function. An object such as `{ server: fn }` is silently ignored and `data` stays undefined; unwrap it (`export const loader = loaders.server`), as `product-tile/index.tsx` does.
|
|
122
|
+
- The loader runs on the server only (stripped from the client bundle), so it may import `*.server` modules; the component file must not. An optional `clientLoader` export is client-only.
|
|
123
|
+
- Return `null` when nothing is configured, fetch in parallel with `Promise.all`, and let errors propagate so only that component is hidden.
|
|
124
|
+
- The registry records `{ loader: 'loader' }` and `{ fallback: 'fallback' }` capability flags for you on regeneration.
|
|
125
|
+
|
|
126
|
+
More on the registry shape, group-qualified ids, preload manifest and entry wiring: [Registry and Loading](references/COMPONENT-REGISTRY.md).
|
|
127
|
+
|
|
128
|
+
## A page route
|
|
129
|
+
|
|
130
|
+
Routes bind a URL to a Page Designer page template and render its regions. Routes with `@PageType` today: home (`_app._index.tsx`), PLP (`_app.c.$.tsx`), PDP (`_app.p.$.tsx`), search (`_app.search.tsx`), about-us (`_app.about-us.tsx`), and the component preview route. Cart, checkout, account and auth do not use Page Designer.
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { Region } from '@/components/region';
|
|
134
|
+
import { PageType } from '@/lib/decorators/page-type';
|
|
135
|
+
import { RegionDefinition } from '@/lib/decorators/region-definition';
|
|
136
|
+
import { fetchPageWithComponentData } from '@/lib/page-designer/page-loader.server';
|
|
137
|
+
|
|
138
|
+
@PageType({
|
|
139
|
+
name: 'Landing Page',
|
|
140
|
+
description: 'Campaign landing page with a banner and a main content area',
|
|
141
|
+
supportedAspectTypes: [],
|
|
142
|
+
})
|
|
143
|
+
@RegionDefinition([
|
|
144
|
+
{ id: 'banner', name: 'Banner Region', maxComponents: 1 },
|
|
145
|
+
{ id: 'main', name: 'Main Region' },
|
|
146
|
+
])
|
|
147
|
+
export class LandingPageMetadata {}
|
|
148
|
+
|
|
149
|
+
export function loader(args: Route.LoaderArgs) {
|
|
150
|
+
return { page: fetchPageWithComponentData(args, { pageId: 'landing' }) };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export default function Landing({ loaderData }: Route.ComponentProps) {
|
|
154
|
+
return (
|
|
155
|
+
<>
|
|
156
|
+
<Region page={loaderData.page} regionId="banner" />
|
|
157
|
+
<Region page={loaderData.page} regionId="main" />
|
|
158
|
+
</>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
- Fetch by `{ pageId }` for a fixed page, or by aspect: `{ aspectType: 'pdp', productId, categoryId? }` and `{ aspectType: 'plp', categoryId }`. The `aspectType` passed to the fetch must agree with `@PageType.supportedAspectTypes` (`['pdp']`, `['plp']`, or `[]` for fixed-page routes such as home); a mismatch shows the wrong template in Business Manager with no error.
|
|
164
|
+
- `fetchPageWithComponentData` attaches the per-component loader promises to the page (`page.componentData`). `<Region>` reads them itself; it takes no `componentData` prop, and the loader returns just `{ page }`. It resolves to `null` when the page is missing (404) or SCAPI errors, so empty regions are the normal unconfigured state.
|
|
165
|
+
- `<Region page={...}>` accepts a promise and renders in Suspense. Add `fallbackElement` only for a visible skeleton.
|
|
166
|
+
- Add `critical` to a page-level region only for above-the-fold or LCP content, and only with an awaited page (`page: await fetchPageWithComponentData(...)`), as `_app._index.tsx` does. Never on below-the-fold or catch-all regions. Details in [Registry and Loading](references/COMPONENT-REGISTRY.md).
|
|
167
|
+
- Nested regions inside a component use component mode, synchronously: `<Region component={component} regionId="content" />`. No Suspense wrapper, no `fallbackElement`, no promises. See `src/components/grid/index.tsx`.
|
|
168
|
+
- Do not use `errorElement` to render hard-coded content for an unconfigured page: it defeats merchant control and forces loaders to fetch data only for the fallback. The home route still contains such an `errorElement`; do not copy it. Use `fallbackElement` for loading and render nothing for empty.
|
|
169
|
+
- Metadata classes on routes are empty, exported, and never carry `@AttributeDefinition`.
|
|
170
|
+
|
|
171
|
+
## Design and preview mode
|
|
172
|
+
|
|
173
|
+
Business Manager loads your storefront in an iframe. In loaders use `isDesignModeActive(request)` / `isPreviewModeActive(request)` (the page loader already does and switches to the `pageId`/`pdToken` passed by Business Manager). In components use `usePageDesignerMode()` from `@salesforce/storefront-next-runtime/design/react/core`. `PageDesignerInit` (`src/page-designer-init.tsx`, rendered by `root.tsx`) blocks link navigation while editing and loads design-mode styles; do not remove it.
|
|
174
|
+
|
|
175
|
+
## Verify before you finish
|
|
176
|
+
|
|
177
|
+
- [ ] Metadata class exported; `typeId` literal; `group` set; every attribute has `name`, `description`, correct `type`
|
|
178
|
+
- [ ] Default export is the component; `fallback` exported and lightweight; `loader` (if any) is a function
|
|
179
|
+
- [ ] Every `<Region regionId>` matches a `@RegionDefinition` id, and vice versa
|
|
180
|
+
- [ ] `pnpm cartridge:generate && pnpm cartridge:validate` pass; the component is in `static-registry.ts`
|
|
181
|
+
- [ ] Review against [Review Checklist](references/REVIEW-CHECKLIST.md); symptoms in [Troubleshooting](references/TROUBLESHOOTING.md)
|
|
182
|
+
|
|
183
|
+
## Reference Documentation
|
|
184
|
+
|
|
185
|
+
- [Decorator Patterns](references/DECORATOR-PATTERNS.md) - options, attribute types, regions, page types
|
|
186
|
+
- [Registry and Loading](references/COMPONENT-REGISTRY.md) - static registry, loaders, critical regions, entry wiring
|
|
187
|
+
- [Business Manager](references/BUSINESS-MANAGER.md) - `route` and `aspectTypeIds`, page setup, palette
|
|
188
|
+
- [Review Checklist](references/REVIEW-CHECKLIST.md) - what to check in a component or page route
|
|
189
|
+
- [Troubleshooting](references/TROUBLESHOOTING.md) - component missing, empty, or wrong data
|
|
190
|
+
|
|
191
|
+
## Related Skills
|
|
192
|
+
|
|
193
|
+
- `storefront-next:sfnext-components` - component conventions, shadcn primitives, Storybook
|
|
194
|
+
- `storefront-next:sfnext-data-fetching` - loaders, `createApiClients`, streaming with Suspense/Await
|
|
195
|
+
- `storefront-next:sfnext-scapi` - calling SCAPI from loaders and actions
|
|
196
|
+
- `storefront-next:sfnext-performance` - LCP, preload, critical data
|
|
197
|
+
- `storefront-next:sfnext-theming` - tokens and brand styling for new components
|
|
198
|
+
- `storefront-next:sfnext-testing` - component and story tests
|
|
199
|
+
- `storefront-next:sfnext-deployment` - shipping the storefront bundle
|
|
200
|
+
- `figma-to-sfnext-pagedesigner:figma-to-sfnext-pagedesigner` - Figma frame to Page Designer blocks
|
|
201
|
+
- `b2c:b2c-page-designer` - classic (ISML/SFRA) Page Designer
|
|
202
|
+
- `b2c-cli:b2c-code` - deploying cartridges and code versions
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Business Manager Integration
|
|
2
|
+
|
|
3
|
+
How code becomes something merchants can use. The long-form version is the "Business Manager Integration" section of `docs/README-PAGE-DESIGNER.md`.
|
|
4
|
+
|
|
5
|
+
## What gets generated
|
|
6
|
+
|
|
7
|
+
`pnpm cartridge:generate` (also part of `pnpm build`) scans your decorators and writes JSON under `cartridges/app_storefrontnext_base/cartridge/experience/`:
|
|
8
|
+
|
|
9
|
+
| Folder | Source | Content |
|
|
10
|
+
|--------|--------|---------|
|
|
11
|
+
| `components/<group>/<typeId>.json` | `@Component` + `@AttributeDefinition` + `@RegionDefinition` | Palette entries and their attribute editors |
|
|
12
|
+
| `pages/<name>.json` | `@PageType` + `@RegionDefinition` on a route | Page templates, with `route` and `aspectTypeIds` |
|
|
13
|
+
| `aspects/*.json` | aspect types (`pdp`, `plp`) | Aspect definitions for product and category pages |
|
|
14
|
+
|
|
15
|
+
Never hand-edit these files; regenerate. `pnpm cartridge:validate` checks them against the schemas. `pnpm cartridge:deploy` uploads the cartridge; `pnpm cartridge:deploy -- --delete` clears old cartridge files first.
|
|
16
|
+
|
|
17
|
+
The first time you set up an instance, `sfnext setup-base-cartridge --slas-client-id <id>` registers what the base cartridge needs on your SLAS client (see `docs/README-EMAIL-CARTRIDGE.md` in your project and `b2c-cli:b2c-slas`).
|
|
18
|
+
|
|
19
|
+
## `route` and `aspectTypeIds`
|
|
20
|
+
|
|
21
|
+
The generated page JSON carries two fields that tie a route to Business Manager:
|
|
22
|
+
|
|
23
|
+
- `route`: the URL pattern Business Manager loads in its preview iframe, with `:param` placeholders replaced by the product, category or search term the merchant selected (for example `/:siteId/:localeId/product/:productId`, `/:siteId/:localeId/category/:categoryId`, `/:siteId/:localeId`). It comes from the route file, so a renamed route file changes it on the next generate.
|
|
24
|
+
- `aspectTypeIds`: from `@PageType.supportedAspectTypes`. It decides which page templates are offered when a merchant creates a page for a product (`pdp`) or category (`plp`). An empty array means the template is for a fixed page.
|
|
25
|
+
|
|
26
|
+
The route's loader must fetch with the same aspect the decorator advertises (`fetchPageWithComponentData(args, { aspectType: 'pdp', productId })` with `supportedAspectTypes: ['pdp']`). This cross-file agreement is the most common page-level bug and produces no error, only the wrong template in Business Manager.
|
|
27
|
+
|
|
28
|
+
## Merchant setup checklist
|
|
29
|
+
|
|
30
|
+
After deploying the cartridge:
|
|
31
|
+
|
|
32
|
+
1. Make sure the cartridge is on the cartridge path of the site and the active code version holds the upload.
|
|
33
|
+
2. If the storefront was created outside Business Manager, connect it to Business Manager so Page Designer and Storefront Preview can see it (Administration > Sites > Storefronts > Connect Existing; needs an administrator role; confirm current steps in the Salesforce Storefront Next documentation).
|
|
34
|
+
3. Merchant Tools > Content > Page Designer: create a page, choose the template (page type) that matches the route, add components to its regions.
|
|
35
|
+
4. For aspect pages, assign the page to the product or category; for fixed pages, use the `pageId` your loader requests (for example `homepage`, `aboutus`).
|
|
36
|
+
5. Publish, then check the storefront. A `null` page (missing or unpublished) renders empty regions rather than an error.
|
|
37
|
+
|
|
38
|
+
## Design vs preview mode
|
|
39
|
+
|
|
40
|
+
Business Manager opens your storefront with `mode` and `pdToken` query parameters. `fetchPageFromLoader` switches to the page Business Manager specifies while in these modes, and `PageDesignerInit` blocks link navigation in edit mode. Keep both; neither needs changes when you add components.
|
|
41
|
+
|
|
42
|
+
## Where to look next
|
|
43
|
+
|
|
44
|
+
- Component missing or empty: [Troubleshooting](TROUBLESHOOTING.md)
|
|
45
|
+
- Deploy credentials and code versions: `b2c-cli:b2c-code`, `b2c-cli:b2c-webdav`
|
|
46
|
+
- Pushing the storefront bundle itself: `storefront-next:sfnext-deployment`
|
package/content/guidance/storefront-next/sfnext-page-designer/references/COMPONENT-REGISTRY.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Registry and Loading
|
|
2
|
+
|
|
3
|
+
## The static registry
|
|
4
|
+
|
|
5
|
+
`src/lib/page-designer/static-registry.ts` maps fully-qualified component ids to lazy importers. The `staticRegistry` Vite plugin generates it from every `@Component` under `src/components` when `pnpm dev` or `pnpm build` starts, and again on hot updates. Everything between `// STATIC_REGISTRY_START` and `// STATIC_REGISTRY_END` is overwritten; do not edit it by hand and do not add entries manually.
|
|
6
|
+
|
|
7
|
+
Shape of the generated code (your file lists your project's components):
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
const staticRegistryImporters = [
|
|
11
|
+
() => import('../../components/hero/index'),
|
|
12
|
+
() => import('../../components/product-carousel/index'),
|
|
13
|
+
// ...
|
|
14
|
+
] as const;
|
|
15
|
+
|
|
16
|
+
export function initializeRegistry(targetRegistry = registry): void {
|
|
17
|
+
targetRegistry.registerImporter('Content.hero', staticRegistryImporters[0]);
|
|
18
|
+
targetRegistry.registerImporter('Layout.productCarousel', staticRegistryImporters[1], {
|
|
19
|
+
loader: 'loader',
|
|
20
|
+
fallback: 'fallback',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- Ids are `<group>.<typeId>`, so the `typeId` in `@Component('hero', ...)` plus `group: 'Content'` becomes `Content.hero`. These ids must match what Business Manager sends.
|
|
26
|
+
- The third argument records which named exports the module has: `loader` (server data) and `fallback` (Suspense skeleton). The plugin derives them from your exports.
|
|
27
|
+
- To see which components your project registers, read the generated file rather than relying on a fixed list.
|
|
28
|
+
|
|
29
|
+
If a new component is missing from the registry, the usual cause is that it is outside `src/components`, the `@Component` first argument is not a string literal, or the dev server was not running when the file changed. Restart `pnpm dev` or run `pnpm build`.
|
|
30
|
+
|
|
31
|
+
## Entry wiring (do not break)
|
|
32
|
+
|
|
33
|
+
`initializeRegistry()` is called once at module top level in `src/entry.server.tsx` and synchronously in `src/entry.client.tsx`, before any component markers are scanned. Keep it there; moving it into a React render function breaks registration ordering.
|
|
34
|
+
|
|
35
|
+
`vite-plugins/storefront-next.ts` enables the plugin with:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
staticRegistry: {
|
|
39
|
+
componentPath: 'src/components',
|
|
40
|
+
registryPath: 'src/lib/page-designer/static-registry.ts',
|
|
41
|
+
preloadManifest: true,
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`preloadManifest: true` builds the resource-hint manifest that `critical` regions and per-component preload hints depend on. Keep it on.
|
|
46
|
+
|
|
47
|
+
## How a page renders
|
|
48
|
+
|
|
49
|
+
1. The route loader calls `fetchPageWithComponentData(args, params)`.
|
|
50
|
+
2. It fetches the page (SCAPI Shopper Experience, resolved from the Data Store when that middleware is active), walks every region and nested region, and for each component whose registry entry has a `loader` calls it with `{ componentData, context, request }`. The resulting promises are stored as `page.componentData[component.id]`.
|
|
51
|
+
3. `<Region page={page} regionId="..." />` resolves each component's module from the registry, wraps it in Suspense (using the module's `fallback`), awaits that component's promise, and renders the default export with the attribute props plus `data`, `component`, `designMetadata` and `regionId`.
|
|
52
|
+
|
|
53
|
+
Because data is attached to the page, there is no separate `componentData` return key and no `componentData` prop on `<Region>`.
|
|
54
|
+
|
|
55
|
+
## Fetching a page
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { fetchPageWithComponentData } from '@/lib/page-designer/page-loader.server';
|
|
59
|
+
|
|
60
|
+
// Fixed page by id
|
|
61
|
+
fetchPageWithComponentData(args, { pageId: 'homepage' });
|
|
62
|
+
|
|
63
|
+
// By aspect (page assigned to a product or category in Business Manager)
|
|
64
|
+
fetchPageWithComponentData(args, { aspectType: 'pdp', productId, categoryId });
|
|
65
|
+
fetchPageWithComponentData(args, { aspectType: 'plp', categoryId });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Return the promise unawaited for a non-critical page (React Router streams it), or `await` it when a `critical` region needs the page synchronously. `fetchPageFromLoader` (same module) is the lower-level call that returns the raw page without `componentData`; routes use `fetchPageWithComponentData`.
|
|
69
|
+
|
|
70
|
+
Single components (for example an embedded content block or the preview route) use `fetchComponentWithComponentData` from `@/lib/page-designer/component-loader.server`.
|
|
71
|
+
|
|
72
|
+
## Module contract
|
|
73
|
+
|
|
74
|
+
| Export | Required | Notes |
|
|
75
|
+
|--------|----------|-------|
|
|
76
|
+
| default | yes | The React component. `forwardRef` components are fine. |
|
|
77
|
+
| `fallback` | yes | Lightweight skeleton; gets the same attribute props; reserve dimensions to avoid layout shift. No hooks that suspend, no fetching. |
|
|
78
|
+
| `loader` | optional | Must be a function `({ componentData, context, request }) => Promise`. Server only (stripped from the client bundle). Attributes are at `componentData.data`. |
|
|
79
|
+
| `clientLoader` | optional | Client-only counterpart (stripped from the server bundle). |
|
|
80
|
+
|
|
81
|
+
A `loader` that is an object (`{ server, client }`) is not callable; the loader never runs and `data` is undefined. Export the function: `export const loader = loaders.server`.
|
|
82
|
+
|
|
83
|
+
Loader guidance: return `null` when nothing is configured, fetch independent resources with `Promise.all`, do not swallow errors (the component's error boundary hides only that component), and reuse the shared `@/lib/api/*.server` helpers rather than building SCAPI calls inline.
|
|
84
|
+
|
|
85
|
+
## Critical regions
|
|
86
|
+
|
|
87
|
+
By default regions stream: the shell renders, then each component swaps in. For above-the-fold content that must be in the first HTML (hero, LCP image), make the region critical.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
export async function loader(args: Route.LoaderArgs) {
|
|
91
|
+
const page = await fetchPageWithComponentData(args, { pageId: 'homepage' }); // must be resolved
|
|
92
|
+
const recommendations = fetchRecommendations(args.context); // stay deferred
|
|
93
|
+
return { page, recommendations };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default function Home({ loaderData }: Route.ComponentProps) {
|
|
97
|
+
return <Region page={loaderData.page} regionId="headerbanner" critical />;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- `critical` is page mode only; nested component regions inherit it.
|
|
102
|
+
- The page must already be resolved; omit `fallbackElement` on a critical region.
|
|
103
|
+
- Each component's own `loader` data still streams inside its local Suspense boundary.
|
|
104
|
+
- Use it sparingly; it delays the initial shell. Never on below-the-fold or catch-all regions.
|
|
105
|
+
- Stylesheets added through a route's `links` export should use `createStorefrontStylesheetLink` from `@salesforce/storefront-next-runtime/design/react/preload` so critical component styles keep a stable cascade order.
|
|
106
|
+
|
|
107
|
+
See "Critical Page Regions" in `docs/README-PAGE-DESIGNER.md` for the full behavior.
|
|
108
|
+
|
|
109
|
+
## Error handling
|
|
110
|
+
|
|
111
|
+
Do not pass `errorElement` to show hard-coded content when a page is unconfigured. That hides setup problems, forces extra fetches in the loader, and bypasses merchant control. Use `fallbackElement` for loading states, or render nothing when a region is empty. The home route's existing `errorElement` is a legacy pattern; do not copy it to new routes.
|
package/content/guidance/storefront-next/sfnext-page-designer/references/DECORATOR-PATTERNS.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Decorator Patterns
|
|
2
|
+
|
|
3
|
+
All four decorators are imported from your project, not from the runtime package:
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { AttributeDefinition, Component, PageType, RegionDefinition } from '@/lib/decorators';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Deep imports (`@/lib/decorators/component`, `.../attribute-definition`, `.../page-type`, `.../region-definition`) also work and are used by some shipped files.
|
|
10
|
+
|
|
11
|
+
Decorators only attach metadata. They are stripped of behavior at runtime and read at build time by `pnpm cartridge:generate` (and by the registry plugin for `@Component`). Mistakes therefore show up as a missing or broken editor in Business Manager, not as a runtime error.
|
|
12
|
+
|
|
13
|
+
## `@Component(typeId, options)`
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
@Component('productCarousel', {
|
|
17
|
+
name: 'Product Carousel',
|
|
18
|
+
description: 'Scrollable row of product cards. Pick a category or add product tiles.',
|
|
19
|
+
group: 'Layout',
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
| Option | Meaning |
|
|
24
|
+
|--------|---------|
|
|
25
|
+
| `typeId` (first arg) | String literal, camelCase by convention (`hero`, `heroCarousel`, `megaMenu`). The registry plugin rejects non-literals. |
|
|
26
|
+
| `name`, `description` | Shown to merchants in the palette. Be specific about which attributes drive which behavior. |
|
|
27
|
+
| `group` | Palette folder. Default `storefrontnext_base`. Shipped components use `Content` and `Layout`. |
|
|
28
|
+
| `embedded`, `component_id` | Marks a singleton content block that is referenced rather than dropped into regions. The component preview route and `fetchComponentWithComponentData` use this path. |
|
|
29
|
+
|
|
30
|
+
The stored, fully-qualified id is `<group>.<typeId>` (`Layout.productCarousel`). That is the id that appears in SCAPI responses, in the registry, and in region include/exclude lists.
|
|
31
|
+
|
|
32
|
+
## `@AttributeDefinition(config)`
|
|
33
|
+
|
|
34
|
+
Put it on a field of the exported metadata class. The field name is the prop name your component receives.
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
@AttributeDefinition({
|
|
38
|
+
id: 'limit',
|
|
39
|
+
name: 'Product Limit',
|
|
40
|
+
description: 'Maximum number of products to show.',
|
|
41
|
+
type: 'integer',
|
|
42
|
+
required: false,
|
|
43
|
+
defaultValue: 12,
|
|
44
|
+
})
|
|
45
|
+
limit?: number;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Option | Notes |
|
|
49
|
+
|--------|-------|
|
|
50
|
+
| `id` | Attribute id written to metadata. Keep it identical to the field name; a mismatch means merchant values never reach the prop. |
|
|
51
|
+
| `name`, `description` | Merchant-facing label and help text. Without them Business Manager shows the raw id. |
|
|
52
|
+
| `type` | One of the types below. Default is a string attribute. An unknown string (such as `'number'`) is not type-checked and produces a broken editor. |
|
|
53
|
+
| `required` | Match the component: `required: true` only if it cannot render without a value. |
|
|
54
|
+
| `defaultValue` | Prefilled in the editor. Keep it equal to the component's destructuring default so editor and runtime agree. For `enum` it must be one of `values`. |
|
|
55
|
+
| `values` | Required for `enum`: the option list. |
|
|
56
|
+
| `editorDefinition` | For `type: 'custom'`: `{ type, configuration? }` selecting a custom editor. |
|
|
57
|
+
| `searching` | `{ searchable, refinable, boostFactor?, sortable? }`. Makes the attribute searchable in Business Manager. Both booleans are required. |
|
|
58
|
+
| `dynamicLookup` | `{ aspectAttributeAlias }`. Sources the value from an aspect attribute at render time instead of a stored value. Allowed on all types. |
|
|
59
|
+
|
|
60
|
+
### Attribute types
|
|
61
|
+
|
|
62
|
+
`string`, `text`, `markup`, `integer`, `boolean`, `product`, `category`, `file`, `page`, `image`, `url`, `enum`, `custom`, `cms_record`.
|
|
63
|
+
|
|
64
|
+
| Type | Value your component receives |
|
|
65
|
+
|------|-------------------------------|
|
|
66
|
+
| `string`, `text` | string (`text` is multi-line) |
|
|
67
|
+
| `markup` | raw HTML string; render with `dangerouslySetInnerHTML` only after deciding it is trusted content |
|
|
68
|
+
| `integer`, `boolean` | number, boolean |
|
|
69
|
+
| `enum` | one of `values` |
|
|
70
|
+
| `image` | object `{ url, focalPoint?, metaData? }` (`Image` from `@/types`) |
|
|
71
|
+
| `url` | string |
|
|
72
|
+
| `product`, `category` | the id (string); fetch details in a `loader` |
|
|
73
|
+
| `file`, `page`, `custom`, `cms_record` | reference values; check the generated JSON and a live SCAPI payload before relying on the shape |
|
|
74
|
+
|
|
75
|
+
### `searching` combinations
|
|
76
|
+
|
|
77
|
+
Generation fails (and `cartridge:validate` reports it) when the combination is invalid:
|
|
78
|
+
|
|
79
|
+
- `string`, `text`, `product`, `category`: all fields allowed.
|
|
80
|
+
- `markup`: `sortable` must be omitted or `false`.
|
|
81
|
+
- `custom`, `cms_record`: `refinable` must be `false`; `boostFactor` and `sortable` are not allowed.
|
|
82
|
+
- `integer`, `boolean`, `file`, `page`, `image`, `url`, `enum`: searching is not allowed.
|
|
83
|
+
|
|
84
|
+
## `@RegionDefinition(regions)`
|
|
85
|
+
|
|
86
|
+
Declares the slots a component (or a page route) exposes to merchants.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
@RegionDefinition([
|
|
90
|
+
{
|
|
91
|
+
id: 'products',
|
|
92
|
+
name: 'Products',
|
|
93
|
+
description: 'Add Product Tile components to populate this carousel.',
|
|
94
|
+
maxComponents: 12,
|
|
95
|
+
componentTypeInclusions: ['Content.productTile'],
|
|
96
|
+
},
|
|
97
|
+
])
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
| Field | Notes |
|
|
101
|
+
|-------|-------|
|
|
102
|
+
| `id`, `name` | Required. The `id` must match the `regionId` passed to `<Region>`. |
|
|
103
|
+
| `description` | Shown to merchants. |
|
|
104
|
+
| `maxComponents` | Set only when the layout structurally limits children. |
|
|
105
|
+
| `componentTypeInclusions`, `componentTypeExclusions` | Allow-list / deny-list of component types. Unqualified ids are prefixed with the host component's group; refer to another group with the full id (`'Content.productTile'` from a `Layout.*` host). |
|
|
106
|
+
| `defaultComponentConstructors` | `[{ id, typeId, data }]` components created when a merchant adds the region to a new page. `typeId` follows the same qualification rule. |
|
|
107
|
+
|
|
108
|
+
Leaf components may use `@RegionDefinition([])` or omit the decorator. A declared region that the implementation never renders is invisible to shoppers even when merchants fill it.
|
|
109
|
+
|
|
110
|
+
## `@PageType(config)`
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
@PageType({
|
|
114
|
+
name: 'Product Detail Page',
|
|
115
|
+
description: 'Product detail page with promotional and engagement regions',
|
|
116
|
+
supportedAspectTypes: ['pdp'],
|
|
117
|
+
})
|
|
118
|
+
@RegionDefinition([{ id: 'pdpPromo', name: 'Promo Content Region', maxComponents: 1 }])
|
|
119
|
+
export class ProductPageMetadata {}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
| Field | Notes |
|
|
123
|
+
|-------|-------|
|
|
124
|
+
| `name`, `description` | Human-readable. `name` is the template label merchants pick. |
|
|
125
|
+
| `supportedAspectTypes` | `['pdp']`, `['plp']`, or `[]` for routes not bound to an aspect (home, about-us, component preview). Must agree with the `aspectType` the route loader fetches. |
|
|
126
|
+
| `preview` | Only `'default'` is valid. Used by the component preview route. |
|
|
127
|
+
|
|
128
|
+
The class must be exported and empty. Never put `@AttributeDefinition` on a page type.
|
|
129
|
+
|
|
130
|
+
`sfnext generate-cartridge` parses decorators statically, so decorator arguments must be literals, not imported constants.
|
|
131
|
+
|
|
132
|
+
## Nested regions in a container
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { Region } from '@/components/region';
|
|
136
|
+
|
|
137
|
+
@Component('twoColumn', { name: 'Two Column', description: 'Two side-by-side regions.', group: 'Layout' })
|
|
138
|
+
@RegionDefinition([
|
|
139
|
+
{ id: 'left', name: 'Left' },
|
|
140
|
+
{ id: 'right', name: 'Right' },
|
|
141
|
+
])
|
|
142
|
+
export class TwoColumnMetadata {}
|
|
143
|
+
|
|
144
|
+
export default function TwoColumn({ component }: { component: ComponentType }) {
|
|
145
|
+
return (
|
|
146
|
+
<div className="grid grid-cols-2 gap-4">
|
|
147
|
+
<Region component={component} regionId="left" />
|
|
148
|
+
<Region component={component} regionId="right" />
|
|
149
|
+
</div>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
`ComponentType` is exported from `@/components/region`. Use `className` on `<Region>` for layout; the design-mode wrapper uses `display: contents`, so children stay direct grid/flex items. `src/components/grid/index.tsx` is the reference implementation.
|
|
155
|
+
|
|
156
|
+
## Images
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { DynamicImage } from '@/components/dynamic-image';
|
|
160
|
+
|
|
161
|
+
<DynamicImage src={image.url} alt="" />
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Always provide `alt` (empty for decorative images). Set `priority="high"` only on the LCP image, never on every image.
|
|
165
|
+
|
|
166
|
+
## Text that is not merchant content
|
|
167
|
+
|
|
168
|
+
Button labels and fallback messages that developers (not merchants) own should use `useTranslation()`. Text in attributes is edited and localized by merchants in Business Manager, so leave it raw.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Review Checklist
|
|
2
|
+
|
|
3
|
+
Use when reviewing or self-checking a Page Designer component (`@Component`) or page route (`@PageType`). Read-only: report findings with `file:line`, say why each matters, and group them as Bugs (break at runtime or in Business Manager), Conventions (drift from the rest of your project) and Polish.
|
|
4
|
+
|
|
5
|
+
A file is in scope if it uses `@Component` or `@PageType`, or is listed in `src/lib/page-designer/static-registry.ts`. Components use sections 1-4; page routes use sections 1 (page type part), 3 (region rendering) and 5.
|
|
6
|
+
|
|
7
|
+
## 1. Metadata
|
|
8
|
+
|
|
9
|
+
**`@Component`**
|
|
10
|
+
- `typeId` is a literal that matches the registry suffix (`Content.hero` -> `'hero'`). A mismatch means the component never resolves.
|
|
11
|
+
- `name` is human-readable and differs from `typeId`; `description` says which attributes drive which behavior.
|
|
12
|
+
- `group` is set (`Content` or `Layout`) rather than defaulting to `storefrontnext_base`.
|
|
13
|
+
- The metadata class is `export`ed.
|
|
14
|
+
|
|
15
|
+
**`@AttributeDefinition`** (every merchant-configurable prop needs one)
|
|
16
|
+
- `type` is one of the valid types (`integer`, not `number`). An invalid string is not caught by TypeScript.
|
|
17
|
+
- `enum` has `values`, and its `defaultValue` is one of them.
|
|
18
|
+
- `image` props are typed as the `Image` object (`image.url`), not `string`.
|
|
19
|
+
- `id` equals the field name; a mismatch means merchant values never reach the prop.
|
|
20
|
+
- `required` matches the component: a prop with a destructuring default should be `required: false`; a prop that crashes on `undefined` should be `true`.
|
|
21
|
+
- `defaultValue` equals the component's destructuring default (otherwise the editor pre-fills one value and runtime falls back to another).
|
|
22
|
+
- `name` and `description` present.
|
|
23
|
+
|
|
24
|
+
**`@RegionDefinition`**
|
|
25
|
+
- Every declared region id is rendered by a matching `<Region regionId>`, and every rendered id is declared.
|
|
26
|
+
- `componentTypeInclusions`/`Exclusions` are fully qualified when they cross groups (`'Content.productTile'` from a `Layout.*` host); unqualified ids take the host's group.
|
|
27
|
+
- `maxComponents` only where the layout limits children.
|
|
28
|
+
- `@RegionDefinition([])` or no decorator on a leaf is fine.
|
|
29
|
+
|
|
30
|
+
**`@PageType`** (routes)
|
|
31
|
+
- Exported, empty class with `name`, `description`, `supportedAspectTypes`. `[]` is valid for fixed-page routes.
|
|
32
|
+
- `supportedAspectTypes` agrees with the loader's `aspectType`. Flag a loader aspect not in the list, a listed aspect the loader never fetches, and aspect ids that do not fit the aspect (for example `productId` with a category aspect). This is the most valuable cross-file check: Business Manager shows the wrong template with no error.
|
|
33
|
+
- No `@AttributeDefinition` on page types.
|
|
34
|
+
|
|
35
|
+
## 2. Module contract (components)
|
|
36
|
+
|
|
37
|
+
- Default export is the component.
|
|
38
|
+
- Named `fallback` export exists, is light (no `useState`/`useEffect`, no fetching, nothing that suspends), uses the same attribute props, and reserves dimensions.
|
|
39
|
+
- Skeletons live in `fallback`, not in the main component.
|
|
40
|
+
- A `loader` export is a function with the `{ componentData, context, request }` signature; the registry has `{ loader: 'loader' }` (regenerate if not); attributes are read from `componentData.data`; `null` is returned for "nothing configured"; independent fetches use `Promise.all`; errors are not swallowed.
|
|
41
|
+
- Server-only imports (`*.server.ts`) appear only in loader files, never in the component body.
|
|
42
|
+
|
|
43
|
+
## 3. Rendering
|
|
44
|
+
|
|
45
|
+
- Injected props (`component`, `data`, `designMetadata`, `regionId`, and rarely `componentData`) are destructured out before any `...rest` spread onto a DOM element. Unused ones are prefixed with `_`.
|
|
46
|
+
- Nested regions use component mode: `<Region component={component} regionId="x" />`, with no `page` prop, no `fallbackElement`, no Suspense wrapper, no promises.
|
|
47
|
+
- Route regions use page mode: `<Region page={loaderData.page} regionId="x" />`. `critical` only on above-the-fold regions with an awaited page and no local `fallbackElement`.
|
|
48
|
+
- Instance-specific `<style>` output is scoped (for example with `useId()`); unscoped CSS collides when two instances share a page.
|
|
49
|
+
- Images: `Image` object, `DynamicImage` for responsive widths, `alt` always present (empty for decorative), `priority="high"` only on LCP candidates.
|
|
50
|
+
- No `'use client'` directives; this is React Router, not React Server Components.
|
|
51
|
+
- Developer-owned strings go through `useTranslation()`; merchant attribute text stays raw.
|
|
52
|
+
- `memo` only on components with stable props.
|
|
53
|
+
|
|
54
|
+
## 4. Anti-patterns
|
|
55
|
+
|
|
56
|
+
1. `errorElement` on a `<Region>` used to render hard-coded content for an unconfigured page.
|
|
57
|
+
2. A loader fetching data that only an `errorElement` uses.
|
|
58
|
+
3. Skeleton markup inside the main component.
|
|
59
|
+
4. `fetchPriority="high"` on every image.
|
|
60
|
+
5. An `@Component` that is not in `static-registry.ts` (registry not regenerated, or file outside `src/components`).
|
|
61
|
+
|
|
62
|
+
## 5. Do not flag
|
|
63
|
+
|
|
64
|
+
- `@PageType` with `supportedAspectTypes: []` on fixed-page routes.
|
|
65
|
+
- `@RegionDefinition([])` on leaves, or an omitted decorator on a leaf.
|
|
66
|
+
- Missing comments that justify valid choices.
|
|
67
|
+
- Formatting and import-order nits the linter already enforces.
|
|
68
|
+
|
|
69
|
+
## Report format
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
## Bugs
|
|
73
|
+
1. `src/components/foo/index.tsx:42` - Page Designer props leak to the DOM.
|
|
74
|
+
`designMetadata` and `component` are not destructured before `...rest` reaches a <div>; React warns in dev and emits `designmetadata="[object Object]"` in production.
|
|
75
|
+
|
|
76
|
+
## Conventions
|
|
77
|
+
2. `src/components/foo/index.tsx:18` - `@Component` has no `group`; peers use 'Content' or 'Layout'.
|
|
78
|
+
|
|
79
|
+
## Polish
|
|
80
|
+
3. `src/components/foo/index.tsx:25` - Description is generic.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
End with a one-line count per severity. A clean review should say so explicitly.
|