cfsa-antigravity 1.0.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/cli.mjs +324 -0
- package/package.json +34 -0
- package/template/.agent/instructions/commands.md +48 -0
- package/template/.agent/instructions/patterns.md +61 -0
- package/template/.agent/instructions/structure.md +29 -0
- package/template/.agent/instructions/tech-stack.md +43 -0
- package/template/.agent/instructions/workflow.md +41 -0
- package/template/.agent/kit-sync.md +15 -0
- package/template/.agent/rules/boundary-not-placeholder.md +146 -0
- package/template/.agent/rules/completion-checklist.md +48 -0
- package/template/.agent/rules/decision-classification.md +103 -0
- package/template/.agent/rules/extensibility.md +47 -0
- package/template/.agent/rules/question-vs-command.md +81 -0
- package/template/.agent/rules/security-first.md +43 -0
- package/template/.agent/rules/specificity-standards.md +54 -0
- package/template/.agent/rules/tdd-contract-first.md +57 -0
- package/template/.agent/rules/vertical-slices.md +42 -0
- package/template/.agent/skill-library/MANIFEST.md +480 -0
- package/template/.agent/skill-library/README.md +38 -0
- package/template/.agent/skill-library/meta/brand-guidelines/SKILL.md +73 -0
- package/template/.agent/skill-library/meta/claude-code/README.md +9 -0
- package/template/.agent/skill-library/meta/claude-code/agent-development/SKILL.md +415 -0
- package/template/.agent/skill-library/meta/claude-code/hook-development/SKILL.md +712 -0
- package/template/.agent/skill-library/meta/claude-code/plugin-structure/SKILL.md +476 -0
- package/template/.agent/skill-library/meta/git-advanced/SKILL.md +972 -0
- package/template/.agent/skill-library/meta/mcp-builder/SKILL.md +236 -0
- package/template/.agent/skill-library/meta/product-marketing-context/SKILL.md +241 -0
- package/template/.agent/skill-library/meta/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skill-library/meta/tmux-processes/SKILL.md +210 -0
- package/template/.agent/skill-library/meta/using-tmux-for-interactive-commands/SKILL.md +178 -0
- package/template/.agent/skill-library/stack/3d/threejs-pro/SKILL.md +300 -0
- package/template/.agent/skill-library/stack/ai/ai-sdk/SKILL.md +77 -0
- package/template/.agent/skill-library/stack/ai/langchain/SKILL.md +530 -0
- package/template/.agent/skill-library/stack/ai/ollama/SKILL.md +321 -0
- package/template/.agent/skill-library/stack/ai/openai-sdk/SKILL.md +549 -0
- package/template/.agent/skill-library/stack/analytics/google-analytics/SKILL.md +153 -0
- package/template/.agent/skill-library/stack/api/graphql/SKILL.md +1061 -0
- package/template/.agent/skill-library/stack/api/trpc/SKILL.md +576 -0
- package/template/.agent/skill-library/stack/auth/authjs/SKILL.md +569 -0
- package/template/.agent/skill-library/stack/auth/clerk/SKILL.md +590 -0
- package/template/.agent/skill-library/stack/auth/firebase-auth/SKILL.md +734 -0
- package/template/.agent/skill-library/stack/cms/payload-cms/SKILL.md +573 -0
- package/template/.agent/skill-library/stack/cms/shopify/SKILL.md +1193 -0
- package/template/.agent/skill-library/stack/cms/wordpress/SKILL.md +1104 -0
- package/template/.agent/skill-library/stack/css/sass-scss/SKILL.md +1121 -0
- package/template/.agent/skill-library/stack/css/tailwind-css-patterns/SKILL.md +863 -0
- package/template/.agent/skill-library/stack/css/tailwind-design-system/SKILL.md +490 -0
- package/template/.agent/skill-library/stack/css/vanilla-css/SKILL.md +1078 -0
- package/template/.agent/skill-library/stack/databases/clickhouse/SKILL.md +311 -0
- package/template/.agent/skill-library/stack/databases/influxdb/SKILL.md +280 -0
- package/template/.agent/skill-library/stack/databases/lancedb/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/databases/mongodb/SKILL.md +1169 -0
- package/template/.agent/skill-library/stack/databases/neo4j/SKILL.md +839 -0
- package/template/.agent/skill-library/stack/databases/pgvector/SKILL.md +241 -0
- package/template/.agent/skill-library/stack/databases/pinecone/SKILL.md +212 -0
- package/template/.agent/skill-library/stack/databases/postgresql/SKILL.md +658 -0
- package/template/.agent/skill-library/stack/databases/qdrant/SKILL.md +312 -0
- package/template/.agent/skill-library/stack/databases/redis/SKILL.md +1079 -0
- package/template/.agent/skill-library/stack/databases/spacetimedb/SKILL.md +532 -0
- package/template/.agent/skill-library/stack/databases/sqlite/SKILL.md +1132 -0
- package/template/.agent/skill-library/stack/databases/supabase/SKILL.md +640 -0
- package/template/.agent/skill-library/stack/databases/surrealdb-expert/SKILL.md +945 -0
- package/template/.agent/skill-library/stack/databases/timescaledb/SKILL.md +745 -0
- package/template/.agent/skill-library/stack/databases/weaviate/SKILL.md +218 -0
- package/template/.agent/skill-library/stack/devops/github-actions/SKILL.md +554 -0
- package/template/.agent/skill-library/stack/devops/kubernetes/SKILL.md +950 -0
- package/template/.agent/skill-library/stack/devops/nginx/SKILL.md +841 -0
- package/template/.agent/skill-library/stack/devops/terraform/SKILL.md +860 -0
- package/template/.agent/skill-library/stack/email/resend/SKILL.md +391 -0
- package/template/.agent/skill-library/stack/engines/godot/SKILL.md +488 -0
- package/template/.agent/skill-library/stack/extensions/chrome-extension/SKILL.md +375 -0
- package/template/.agent/skill-library/stack/extensions/vscode-extension/SKILL.md +453 -0
- package/template/.agent/skill-library/stack/frameworks/astro-framework/SKILL.md +162 -0
- package/template/.agent/skill-library/stack/frameworks/electron/SKILL.md +1286 -0
- package/template/.agent/skill-library/stack/frameworks/fastapi/SKILL.md +650 -0
- package/template/.agent/skill-library/stack/frameworks/hono/SKILL.md +90 -0
- package/template/.agent/skill-library/stack/frameworks/nestjs/SKILL.md +878 -0
- package/template/.agent/skill-library/stack/frameworks/nextjs/SKILL.md +635 -0
- package/template/.agent/skill-library/stack/frameworks/nuxt/SKILL.md +564 -0
- package/template/.agent/skill-library/stack/frameworks/sveltekit/SKILL.md +614 -0
- package/template/.agent/skill-library/stack/frameworks/tauri/SKILL.md +920 -0
- package/template/.agent/skill-library/stack/gamedev/godot/SKILL.md +1032 -0
- package/template/.agent/skill-library/stack/gamedev/unity/SKILL.md +1175 -0
- package/template/.agent/skill-library/stack/hosting/aws/SKILL.md +467 -0
- package/template/.agent/skill-library/stack/hosting/cloudflare/SKILL.md +201 -0
- package/template/.agent/skill-library/stack/hosting/docker-expert/SKILL.md +409 -0
- package/template/.agent/skill-library/stack/hosting/vercel/SKILL.md +484 -0
- package/template/.agent/skill-library/stack/languages/bash-scripting/SKILL.md +773 -0
- package/template/.agent/skill-library/stack/languages/c-cpp/SKILL.md +712 -0
- package/template/.agent/skill-library/stack/languages/gdscript/SKILL.md +789 -0
- package/template/.agent/skill-library/stack/languages/go/SKILL.md +664 -0
- package/template/.agent/skill-library/stack/languages/java/SKILL.md +778 -0
- package/template/.agent/skill-library/stack/languages/kotlin/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/languages/python/SKILL.md +678 -0
- package/template/.agent/skill-library/stack/languages/rust/SKILL.md +673 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/SKILL.md +141 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/advanced-generics.md +90 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/branded-types.md +57 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/builder-pattern.md +71 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/common-pitfalls.md +135 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/conditional-types.md +27 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/decorators.md +98 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/discriminated-unions.md +62 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/mapped-types.md +53 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/performance-best-practices.md +104 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/template-literal-types.md +49 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/testing-types.md +112 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-guards.md +70 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-inference.md +101 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/utility-types.md +98 -0
- package/template/.agent/skill-library/stack/languages/vanilla-javascript/SKILL.md +803 -0
- package/template/.agent/skill-library/stack/messaging/kafka/SKILL.md +235 -0
- package/template/.agent/skill-library/stack/mobile/expo-react-native/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/mobile/flutter/SKILL.md +316 -0
- package/template/.agent/skill-library/stack/mobile/react-native/SKILL.md +337 -0
- package/template/.agent/skill-library/stack/monitoring/posthog/SKILL.md +396 -0
- package/template/.agent/skill-library/stack/monitoring/sentry/SKILL.md +509 -0
- package/template/.agent/skill-library/stack/observability/datadog/SKILL.md +179 -0
- package/template/.agent/skill-library/stack/observability/distributed-tracing/SKILL.md +140 -0
- package/template/.agent/skill-library/stack/observability/logging-best-practices/SKILL.md +168 -0
- package/template/.agent/skill-library/stack/observability/opentelemetry/SKILL.md +164 -0
- package/template/.agent/skill-library/stack/observability/prometheus-grafana/SKILL.md +246 -0
- package/template/.agent/skill-library/stack/observability/python-observability/SKILL.md +158 -0
- package/template/.agent/skill-library/stack/orm/drizzle-orm/SKILL.md +613 -0
- package/template/.agent/skill-library/stack/orm/prisma/SKILL.md +744 -0
- package/template/.agent/skill-library/stack/payments/lemonsqueezy/SKILL.md +393 -0
- package/template/.agent/skill-library/stack/payments/stripe-integration/SKILL.md +457 -0
- package/template/.agent/skill-library/stack/queue/bullmq/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/queue/inngest/SKILL.md +438 -0
- package/template/.agent/skill-library/stack/realtime/socketio/SKILL.md +595 -0
- package/template/.agent/skill-library/stack/search/elasticsearch/SKILL.md +248 -0
- package/template/.agent/skill-library/stack/search/meilisearch/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/security/crypto-patterns/SKILL.md +437 -0
- package/template/.agent/skill-library/stack/security/csp-cors-headers/SKILL.md +588 -0
- package/template/.agent/skill-library/stack/security/dependency-auditing/SKILL.md +560 -0
- package/template/.agent/skill-library/stack/security/input-sanitization/SKILL.md +430 -0
- package/template/.agent/skill-library/stack/security/owasp-web-security/SKILL.md +421 -0
- package/template/.agent/skill-library/stack/state/tanstack-query/SKILL.md +637 -0
- package/template/.agent/skill-library/stack/state/zustand/SKILL.md +483 -0
- package/template/.agent/skill-library/stack/storage/aws-s3/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/testing/playwright/SKILL.md +641 -0
- package/template/.agent/skill-library/stack/testing/storybook/SKILL.md +923 -0
- package/template/.agent/skill-library/stack/testing/testing-library/SKILL.md +872 -0
- package/template/.agent/skill-library/stack/testing/vitest/SKILL.md +714 -0
- package/template/.agent/skill-library/stack/ui/react-best-practices/SKILL.md +877 -0
- package/template/.agent/skill-library/stack/ui/react-composition-patterns/SKILL.md +1107 -0
- package/template/.agent/skill-library/stack/ui/react-flow/SKILL.md +425 -0
- package/template/.agent/skill-library/stack/ui/shadcn-ui/SKILL.md +703 -0
- package/template/.agent/skill-library/surface/api/api-caching/SKILL.md +458 -0
- package/template/.agent/skill-library/surface/api/api-documentation-openapi/SKILL.md +697 -0
- package/template/.agent/skill-library/surface/api/api-error-handling/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/api-security-checklist/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/api-versioning/SKILL.md +420 -0
- package/template/.agent/skill-library/surface/api/email-best-practices/SKILL.md +59 -0
- package/template/.agent/skill-library/surface/api/rate-limiting-abuse-protection/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/webhook-design/SKILL.md +752 -0
- package/template/.agent/skill-library/surface/cli/cli-configuration-management/SKILL.md +445 -0
- package/template/.agent/skill-library/surface/cli/cli-error-diagnostics/SKILL.md +515 -0
- package/template/.agent/skill-library/surface/cli/cli-shell-integration/SKILL.md +479 -0
- package/template/.agent/skill-library/surface/cli/cli-ux-design/SKILL.md +477 -0
- package/template/.agent/skill-library/surface/desktop/desktop-app-distribution/SKILL.md +416 -0
- package/template/.agent/skill-library/surface/desktop/desktop-security-sandboxing/SKILL.md +407 -0
- package/template/.agent/skill-library/surface/desktop/desktop-ux-conventions/SKILL.md +361 -0
- package/template/.agent/skill-library/surface/desktop/native-os-integration/SKILL.md +563 -0
- package/template/.agent/skill-library/surface/extension/browser-extension-patterns/SKILL.md +482 -0
- package/template/.agent/skill-library/surface/extension/plugin-architecture-design/SKILL.md +632 -0
- package/template/.agent/skill-library/surface/extension/vscode-extension-development/SKILL.md +728 -0
- package/template/.agent/skill-library/surface/mobile/app-store-submission/SKILL.md +304 -0
- package/template/.agent/skill-library/surface/mobile/mobile-offline-sync/SKILL.md +443 -0
- package/template/.agent/skill-library/surface/mobile/mobile-responsive-patterns/SKILL.md +432 -0
- package/template/.agent/skill-library/surface/mobile/push-notifications/SKILL.md +495 -0
- package/template/.agent/skill-library/surface/web/accessibility-compliance/SKILL.md +827 -0
- package/template/.agent/skill-library/surface/web/ai-seo/SKILL.md +398 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/content-patterns.md +285 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/platform-ranking-factors.md +152 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/SKILL.md +309 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/event-library.md +260 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/ga4-implementation.md +300 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/gtm-implementation.md +390 -0
- package/template/.agent/skill-library/surface/web/authentication-ui-flows/SKILL.md +530 -0
- package/template/.agent/skill-library/surface/web/dark-mode-theming/SKILL.md +516 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/SKILL.md +105 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/charts.csv +26 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/colors.csv +97 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/landing.csv +31 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/styles.csv +59 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/typography.csv +58 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/ux-guidelines.csv +100 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/core.py +258 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/design_system.py +1067 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/search.py +106 -0
- package/template/.agent/skill-library/surface/web/form-handling-validation/SKILL.md +675 -0
- package/template/.agent/skill-library/surface/web/frontend-design/SKILL.md +1393 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/cppn-hero.tsx +299 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/wave-hero.tsx +875 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/SKILL.md +111 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/scripts/ux_audit.py +739 -0
- package/template/.agent/skill-library/surface/web/i18n-localization/SKILL.md +154 -0
- package/template/.agent/skill-library/surface/web/offline-first-pwa/SKILL.md +657 -0
- package/template/.agent/skill-library/surface/web/page-cro/SKILL.md +182 -0
- package/template/.agent/skill-library/surface/web/page-cro/references/experiments.md +248 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/SKILL.md +238 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/references/playbooks.md +308 -0
- package/template/.agent/skill-library/surface/web/schema-markup/SKILL.md +179 -0
- package/template/.agent/skill-library/surface/web/schema-markup/references/schema-examples.md +398 -0
- package/template/.agent/skill-library/surface/web/seo-audit/SKILL.md +394 -0
- package/template/.agent/skill-library/surface/web/seo-audit/references/ai-writing-detection.md +200 -0
- package/template/.agent/skill-library/surface/web/web-performance-optimization/SKILL.md +646 -0
- package/template/.agent/skill-library/surface/web/web-scraping/SKILL.md +58 -0
- package/template/.agent/skills/accessibility/SKILL.md +522 -0
- package/template/.agent/skills/accessibility/references/WCAG.md +162 -0
- package/template/.agent/skills/adversarial-review/SKILL.md +90 -0
- package/template/.agent/skills/antigravity-workflows/SKILL.md +81 -0
- package/template/.agent/skills/antigravity-workflows/resources/implementation-playbook.md +36 -0
- package/template/.agent/skills/api-design-principles/SKILL.md +37 -0
- package/template/.agent/skills/api-design-principles/assets/api-design-checklist.md +155 -0
- package/template/.agent/skills/api-design-principles/assets/rest-api-template.py +182 -0
- package/template/.agent/skills/api-design-principles/references/graphql-schema-design.md +583 -0
- package/template/.agent/skills/api-design-principles/references/rest-best-practices.md +408 -0
- package/template/.agent/skills/api-design-principles/resources/implementation-playbook.md +513 -0
- package/template/.agent/skills/api-versioning/SKILL.md +420 -0
- package/template/.agent/skills/architecture-mapping/SKILL.md +219 -0
- package/template/.agent/skills/bootstrap-agents/SKILL.md +259 -0
- package/template/.agent/skills/brainstorming/SKILL.md +236 -0
- package/template/.agent/skills/brand-guidelines/SKILL.md +44 -0
- package/template/.agent/skills/clean-code/SKILL.md +94 -0
- package/template/.agent/skills/code-review-pro/SKILL.md +152 -0
- package/template/.agent/skills/concise-planning/SKILL.md +68 -0
- package/template/.agent/skills/cross-layer-consistency/SKILL.md +117 -0
- package/template/.agent/skills/database-schema-design/SKILL.md +429 -0
- package/template/.agent/skills/deployment-procedures/SKILL.md +241 -0
- package/template/.agent/skills/design-anti-cliche/SKILL.md +159 -0
- package/template/.agent/skills/design-direction/SKILL.md +45 -0
- package/template/.agent/skills/error-handling-patterns/SKILL.md +721 -0
- package/template/.agent/skills/find-skills/SKILL.md +145 -0
- package/template/.agent/skills/git-advanced/SKILL.md +972 -0
- package/template/.agent/skills/git-workflow/SKILL.md +420 -0
- package/template/.agent/skills/idea-extraction/SKILL.md +271 -0
- package/template/.agent/skills/logging-best-practices/SKILL.md +851 -0
- package/template/.agent/skills/migration-management/SKILL.md +384 -0
- package/template/.agent/skills/minimalist-surgical-development/SKILL.md +69 -0
- package/template/.agent/skills/parallel-agents/SKILL.md +165 -0
- package/template/.agent/skills/parallel-debugging/SKILL.md +135 -0
- package/template/.agent/skills/parallel-feature-development/SKILL.md +166 -0
- package/template/.agent/skills/performance-budgeting/SKILL.md +144 -0
- package/template/.agent/skills/pipeline-rubrics/SKILL.md +51 -0
- package/template/.agent/skills/pipeline-rubrics/references/architecture-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/be-rubric.md +21 -0
- package/template/.agent/skills/pipeline-rubrics/references/fe-rubric.md +20 -0
- package/template/.agent/skills/pipeline-rubrics/references/ia-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/scoring.md +28 -0
- package/template/.agent/skills/pipeline-rubrics/references/vision-rubric.md +11 -0
- package/template/.agent/skills/prd-templates/SKILL.md +88 -0
- package/template/.agent/skills/prd-templates/references/architecture-design-template.md +88 -0
- package/template/.agent/skills/prd-templates/references/be-spec-template.md +101 -0
- package/template/.agent/skills/prd-templates/references/data-placement-template.md +74 -0
- package/template/.agent/skills/prd-templates/references/decomposition-templates.md +211 -0
- package/template/.agent/skills/prd-templates/references/design-system-decisions.md +198 -0
- package/template/.agent/skills/prd-templates/references/engineering-standards-template.md +124 -0
- package/template/.agent/skills/prd-templates/references/fe-classification-procedures.md +47 -0
- package/template/.agent/skills/prd-templates/references/fe-spec-template.md +84 -0
- package/template/.agent/skills/prd-templates/references/infrastructure-report-template.md +71 -0
- package/template/.agent/skills/prd-templates/references/operational-templates.md +116 -0
- package/template/.agent/skills/prd-templates/references/placeholder-guard-template.md +21 -0
- package/template/.agent/skills/prd-templates/references/surface-model.md +61 -0
- package/template/.agent/skills/prd-templates/references/vision-template.md +66 -0
- package/template/.agent/skills/prompt-engineer/README.md +659 -0
- package/template/.agent/skills/prompt-engineer/SKILL.md +249 -0
- package/template/.agent/skills/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skills/resolve-ambiguity/SKILL.md +278 -0
- package/template/.agent/skills/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skills/security-scanning-security-hardening/SKILL.md +231 -0
- package/template/.agent/skills/session-continuity/SKILL.md +730 -0
- package/template/.agent/skills/session-continuity/protocols/01-session-resumption.md +38 -0
- package/template/.agent/skills/session-continuity/protocols/02-progress-generation.md +85 -0
- package/template/.agent/skills/session-continuity/protocols/03-progress-update.md +70 -0
- package/template/.agent/skills/session-continuity/protocols/04-pattern-extraction.md +60 -0
- package/template/.agent/skills/session-continuity/protocols/05-session-close.md +37 -0
- package/template/.agent/skills/session-continuity/protocols/06-decision-analysis.md +84 -0
- package/template/.agent/skills/session-continuity/protocols/07-spec-pipeline-generation.md +48 -0
- package/template/.agent/skills/session-continuity/protocols/08-spec-pipeline-update.md +43 -0
- package/template/.agent/skills/session-continuity/protocols/09-parallel-claim.md +122 -0
- package/template/.agent/skills/session-continuity/protocols/10-placeholder-verification-gate.md +104 -0
- package/template/.agent/skills/session-continuity/protocols/ambiguity-gates.md +48 -0
- package/template/.agent/skills/skill-creator/LICENSE.txt +202 -0
- package/template/.agent/skills/skill-creator/README.md +270 -0
- package/template/.agent/skills/skill-creator/SKILL.md +590 -0
- package/template/.agent/skills/skill-creator/references/output-patterns.md +82 -0
- package/template/.agent/skills/skill-creator/references/workflows.md +28 -0
- package/template/.agent/skills/skill-creator/scripts/init_skill.py +303 -0
- package/template/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
- package/template/.agent/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/template/.agent/skills/spec-writing/SKILL.md +110 -0
- package/template/.agent/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/template/.agent/skills/systematic-debugging/SKILL.md +297 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/template/.agent/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/template/.agent/skills/systematic-debugging/find-polluter.sh +63 -0
- package/template/.agent/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/template/.agent/skills/systematic-debugging/test-academic.md +14 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/template/.agent/skills/tdd-workflow/SKILL.md +409 -0
- package/template/.agent/skills/tech-stack-catalog/SKILL.md +49 -0
- package/template/.agent/skills/tech-stack-catalog/references/constraint-questions.md +21 -0
- package/template/.agent/skills/tech-stack-catalog/references/dev-tooling-decisions.md +37 -0
- package/template/.agent/skills/tech-stack-catalog/references/surface-decision-tables.md +69 -0
- package/template/.agent/skills/technical-writer/SKILL.md +242 -0
- package/template/.agent/skills/testing-strategist/SKILL.md +932 -0
- package/template/.agent/skills/verification-before-completion/SKILL.md +145 -0
- package/template/.agent/skills/workflow-automation/SKILL.md +73 -0
- package/template/.agent/workflows/audit-ambiguity-execute.md +165 -0
- package/template/.agent/workflows/audit-ambiguity-rubrics.md +83 -0
- package/template/.agent/workflows/audit-ambiguity.md +64 -0
- package/template/.agent/workflows/bootstrap-agents-fill.md +201 -0
- package/template/.agent/workflows/bootstrap-agents-provision.md +197 -0
- package/template/.agent/workflows/bootstrap-agents.md +66 -0
- package/template/.agent/workflows/create-prd-architecture.md +119 -0
- package/template/.agent/workflows/create-prd-compile.md +138 -0
- package/template/.agent/workflows/create-prd-design-system.md +135 -0
- package/template/.agent/workflows/create-prd-security.md +113 -0
- package/template/.agent/workflows/create-prd-stack.md +91 -0
- package/template/.agent/workflows/create-prd.md +168 -0
- package/template/.agent/workflows/decompose-architecture-structure.md +82 -0
- package/template/.agent/workflows/decompose-architecture-validate.md +119 -0
- package/template/.agent/workflows/decompose-architecture.md +111 -0
- package/template/.agent/workflows/evolve-contract.md +98 -0
- package/template/.agent/workflows/evolve-feature-cascade.md +140 -0
- package/template/.agent/workflows/evolve-feature-classify.md +116 -0
- package/template/.agent/workflows/evolve-feature.md +56 -0
- package/template/.agent/workflows/ideate-discover.md +144 -0
- package/template/.agent/workflows/ideate-extract.md +129 -0
- package/template/.agent/workflows/ideate-validate.md +117 -0
- package/template/.agent/workflows/ideate.md +113 -0
- package/template/.agent/workflows/implement-slice-setup.md +113 -0
- package/template/.agent/workflows/implement-slice-tdd.md +198 -0
- package/template/.agent/workflows/implement-slice.md +50 -0
- package/template/.agent/workflows/plan-phase.md +202 -0
- package/template/.agent/workflows/propagate-decision-apply.md +135 -0
- package/template/.agent/workflows/propagate-decision-scan.md +147 -0
- package/template/.agent/workflows/propagate-decision.md +56 -0
- package/template/.agent/workflows/remediate-pipeline-assess.md +138 -0
- package/template/.agent/workflows/remediate-pipeline-execute.md +135 -0
- package/template/.agent/workflows/remediate-pipeline.md +55 -0
- package/template/.agent/workflows/resolve-ambiguity.md +82 -0
- package/template/.agent/workflows/sync-kit.md +209 -0
- package/template/.agent/workflows/update-architecture-map.md +74 -0
- package/template/.agent/workflows/validate-phase.md +219 -0
- package/template/.agent/workflows/verify-infrastructure.md +207 -0
- package/template/.agent/workflows/write-architecture-spec-deepen.md +139 -0
- package/template/.agent/workflows/write-architecture-spec-design.md +202 -0
- package/template/.agent/workflows/write-architecture-spec.md +63 -0
- package/template/.agent/workflows/write-be-spec-classify.md +165 -0
- package/template/.agent/workflows/write-be-spec-write.md +98 -0
- package/template/.agent/workflows/write-be-spec.md +76 -0
- package/template/.agent/workflows/write-fe-spec-classify.md +170 -0
- package/template/.agent/workflows/write-fe-spec-write.md +94 -0
- package/template/.agent/workflows/write-fe-spec.md +71 -0
- package/template/AGENTS.md +176 -0
- package/template/GEMINI.md +177 -0
- package/template/docs/README.md +187 -0
- package/template/docs/audits/.gitkeep +0 -0
- package/template/docs/audits/README.md +10 -0
- package/template/docs/plans/.gitkeep +0 -0
- package/template/docs/plans/README.md +21 -0
- package/template/docs/plans/be/.gitkeep +0 -0
- package/template/docs/plans/be/README.md +11 -0
- package/template/docs/plans/fe/.gitkeep +0 -0
- package/template/docs/plans/fe/README.md +11 -0
- package/template/docs/plans/ia/.gitkeep +0 -0
- package/template/docs/plans/ia/README.md +17 -0
- package/template/docs/plans/ia/deep-dives/.gitkeep +0 -0
- package/template/docs/plans/ia/deep-dives/README.md +5 -0
- package/template/docs/plans/phases/.gitkeep +0 -0
- package/template/docs/plans/phases/README.md +11 -0
|
@@ -0,0 +1,1193 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: shopify
|
|
3
|
+
description: "Expert Shopify development guide covering Liquid templating, Online Store 2.0 theme architecture, Storefront API, Admin API (GraphQL), Shopify CLI, app development (Remix), Polaris UI, webhooks, checkout extensions, Shopify Functions, metafields, cart customization, theme app extensions, Hydrogen, Oxygen hosting, and performance optimization."
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Shopify Expert
|
|
8
|
+
|
|
9
|
+
## 1. Liquid Templating
|
|
10
|
+
|
|
11
|
+
### Core Syntax
|
|
12
|
+
|
|
13
|
+
Liquid is Shopify's template language. It has three main constructs: objects, tags, and filters.
|
|
14
|
+
|
|
15
|
+
```liquid
|
|
16
|
+
{%- comment -%} Objects: output dynamic content {%- endcomment -%}
|
|
17
|
+
{{ product.title }}
|
|
18
|
+
{{ product.price | money }}
|
|
19
|
+
{{ shop.name }}
|
|
20
|
+
|
|
21
|
+
{%- comment -%} Tags: logic and control flow {%- endcomment -%}
|
|
22
|
+
{% if product.available %}
|
|
23
|
+
<button type="button">Add to Cart</button>
|
|
24
|
+
{% else %}
|
|
25
|
+
<button type="button" disabled>Sold Out</button>
|
|
26
|
+
{% endif %}
|
|
27
|
+
|
|
28
|
+
{%- comment -%} Filters: transform output {%- endcomment -%}
|
|
29
|
+
{{ product.title | upcase }}
|
|
30
|
+
{{ product.price | money_with_currency }}
|
|
31
|
+
{{ 'hero.jpg' | asset_url | image_tag: loading: 'lazy', width: 800 }}
|
|
32
|
+
{{ product.description | strip_html | truncate: 150 }}
|
|
33
|
+
{{ 'now' | date: '%Y-%m-%d' }}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Whitespace Control
|
|
37
|
+
|
|
38
|
+
Use `{%-` and `-%}` (with hyphens) to strip surrounding whitespace. This prevents blank lines in rendered HTML.
|
|
39
|
+
|
|
40
|
+
```liquid
|
|
41
|
+
{%- assign greeting = 'Hello' -%}
|
|
42
|
+
{%- if customer -%}
|
|
43
|
+
<p>{{ greeting }}, {{ customer.first_name }}</p>
|
|
44
|
+
{%- endif -%}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Liquid Objects Reference
|
|
48
|
+
|
|
49
|
+
```liquid
|
|
50
|
+
{%- comment -%} Product {%- endcomment -%}
|
|
51
|
+
{{ product.id }}
|
|
52
|
+
{{ product.title }}
|
|
53
|
+
{{ product.handle }} {%- comment -%} URL slug {%- endcomment -%}
|
|
54
|
+
{{ product.description }}
|
|
55
|
+
{{ product.price | money }} {%- comment -%} Lowest variant price {%- endcomment -%}
|
|
56
|
+
{{ product.compare_at_price | money }} {%- comment -%} Original price (for sales) {%- endcomment -%}
|
|
57
|
+
{{ product.vendor }}
|
|
58
|
+
{{ product.type }}
|
|
59
|
+
{{ product.tags }}
|
|
60
|
+
{{ product.images }}
|
|
61
|
+
{{ product.variants }}
|
|
62
|
+
{{ product.options }}
|
|
63
|
+
{{ product.metafields.custom.warranty }} {%- comment -%} Custom metafield {%- endcomment -%}
|
|
64
|
+
|
|
65
|
+
{%- comment -%} Variant {%- endcomment -%}
|
|
66
|
+
{{ variant.id }}
|
|
67
|
+
{{ variant.title }}
|
|
68
|
+
{{ variant.price | money }}
|
|
69
|
+
{{ variant.sku }}
|
|
70
|
+
{{ variant.available }}
|
|
71
|
+
{{ variant.inventory_quantity }}
|
|
72
|
+
|
|
73
|
+
{%- comment -%} Cart {%- endcomment -%}
|
|
74
|
+
{{ cart.item_count }}
|
|
75
|
+
{{ cart.total_price | money }}
|
|
76
|
+
{{ cart.items }}
|
|
77
|
+
{{ cart.note }}
|
|
78
|
+
{{ cart.attributes }}
|
|
79
|
+
|
|
80
|
+
{%- comment -%} Customer {%- endcomment -%}
|
|
81
|
+
{{ customer.id }}
|
|
82
|
+
{{ customer.first_name }}
|
|
83
|
+
{{ customer.email }}
|
|
84
|
+
{{ customer.orders_count }}
|
|
85
|
+
{{ customer.total_spent | money }}
|
|
86
|
+
{{ customer.tags }}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Iteration Patterns
|
|
90
|
+
|
|
91
|
+
```liquid
|
|
92
|
+
{%- for product in collection.products limit: 12 -%}
|
|
93
|
+
<div class="product-card">
|
|
94
|
+
<h3>{{ product.title }}</h3>
|
|
95
|
+
<p>{{ product.price | money }}</p>
|
|
96
|
+
{%- if forloop.first -%}
|
|
97
|
+
<span class="badge">First Item</span>
|
|
98
|
+
{%- endif -%}
|
|
99
|
+
{%- if forloop.last -%}
|
|
100
|
+
<span class="badge">Last Item</span>
|
|
101
|
+
{%- endif -%}
|
|
102
|
+
<span>{{ forloop.index }} of {{ forloop.length }}</span>
|
|
103
|
+
</div>
|
|
104
|
+
{%- else -%}
|
|
105
|
+
<p>No products found in this collection.</p>
|
|
106
|
+
{%- endfor -%}
|
|
107
|
+
|
|
108
|
+
{%- comment -%} Paginate collections {%- endcomment -%}
|
|
109
|
+
{%- paginate collection.products by 24 -%}
|
|
110
|
+
{%- for product in collection.products -%}
|
|
111
|
+
{% render 'product-card', product: product %}
|
|
112
|
+
{%- endfor -%}
|
|
113
|
+
{{ paginate | default_pagination }}
|
|
114
|
+
{%- endpaginate -%}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 2. Theme Architecture (Online Store 2.0)
|
|
120
|
+
|
|
121
|
+
### Directory Structure
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
my-theme/
|
|
125
|
+
assets/ # CSS, JS, images, fonts
|
|
126
|
+
config/
|
|
127
|
+
settings_schema.json # Theme settings definitions
|
|
128
|
+
settings_data.json # Theme settings values (auto-generated)
|
|
129
|
+
layout/
|
|
130
|
+
theme.liquid # Main layout wrapper
|
|
131
|
+
password.liquid # Password page layout
|
|
132
|
+
locales/
|
|
133
|
+
en.default.json # Translation strings
|
|
134
|
+
sections/ # Reusable, configurable sections
|
|
135
|
+
header.liquid
|
|
136
|
+
footer.liquid
|
|
137
|
+
featured-collection.liquid
|
|
138
|
+
hero-banner.liquid
|
|
139
|
+
rich-text.liquid
|
|
140
|
+
snippets/ # Reusable partials (no settings schema)
|
|
141
|
+
product-card.liquid
|
|
142
|
+
price.liquid
|
|
143
|
+
icon.liquid
|
|
144
|
+
templates/ # Page templates (JSON for OS 2.0)
|
|
145
|
+
index.json
|
|
146
|
+
product.json
|
|
147
|
+
collection.json
|
|
148
|
+
page.json
|
|
149
|
+
blog.json
|
|
150
|
+
article.json
|
|
151
|
+
cart.json
|
|
152
|
+
404.json
|
|
153
|
+
customers/
|
|
154
|
+
login.json
|
|
155
|
+
account.json
|
|
156
|
+
order.json
|
|
157
|
+
blocks/ # App blocks and theme blocks
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### JSON Templates (Online Store 2.0)
|
|
161
|
+
|
|
162
|
+
JSON templates define which sections appear and in what order. Merchants customize them in the theme editor.
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"name": "Product",
|
|
167
|
+
"sections": {
|
|
168
|
+
"main": {
|
|
169
|
+
"type": "main-product",
|
|
170
|
+
"settings": {
|
|
171
|
+
"show_vendor": true,
|
|
172
|
+
"show_sku": false
|
|
173
|
+
},
|
|
174
|
+
"blocks": {
|
|
175
|
+
"title": { "type": "title" },
|
|
176
|
+
"price": { "type": "price" },
|
|
177
|
+
"variant_picker": { "type": "variant_picker", "settings": { "picker_type": "button" } },
|
|
178
|
+
"quantity": { "type": "quantity_selector" },
|
|
179
|
+
"buy_buttons": { "type": "buy_buttons", "settings": { "show_dynamic_checkout": true } },
|
|
180
|
+
"description": { "type": "description" }
|
|
181
|
+
},
|
|
182
|
+
"block_order": ["title", "price", "variant_picker", "quantity", "buy_buttons", "description"]
|
|
183
|
+
},
|
|
184
|
+
"recommendations": {
|
|
185
|
+
"type": "product-recommendations",
|
|
186
|
+
"settings": {
|
|
187
|
+
"heading": "You may also like",
|
|
188
|
+
"products_to_show": 4
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"order": ["main", "recommendations"]
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Section Schema
|
|
197
|
+
|
|
198
|
+
```liquid
|
|
199
|
+
{% comment %} sections/featured-collection.liquid {% endcomment %}
|
|
200
|
+
|
|
201
|
+
<section class="featured-collection" data-section-id="{{ section.id }}">
|
|
202
|
+
<div class="page-width">
|
|
203
|
+
{%- if section.settings.heading != blank -%}
|
|
204
|
+
<h2>{{ section.settings.heading }}</h2>
|
|
205
|
+
{%- endif -%}
|
|
206
|
+
|
|
207
|
+
<div class="grid grid--{{ section.settings.columns }}">
|
|
208
|
+
{%- for product in section.settings.collection.products limit: section.settings.products_to_show -%}
|
|
209
|
+
{% render 'product-card', product: product, show_vendor: section.settings.show_vendor %}
|
|
210
|
+
{%- endfor -%}
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</section>
|
|
214
|
+
|
|
215
|
+
{% schema %}
|
|
216
|
+
{
|
|
217
|
+
"name": "Featured Collection",
|
|
218
|
+
"tag": "section",
|
|
219
|
+
"class": "section-featured-collection",
|
|
220
|
+
"settings": [
|
|
221
|
+
{
|
|
222
|
+
"type": "text",
|
|
223
|
+
"id": "heading",
|
|
224
|
+
"label": "Heading",
|
|
225
|
+
"default": "Featured Collection"
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"type": "collection",
|
|
229
|
+
"id": "collection",
|
|
230
|
+
"label": "Collection"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"type": "range",
|
|
234
|
+
"id": "products_to_show",
|
|
235
|
+
"min": 2,
|
|
236
|
+
"max": 12,
|
|
237
|
+
"step": 1,
|
|
238
|
+
"default": 4,
|
|
239
|
+
"label": "Products to show"
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"type": "select",
|
|
243
|
+
"id": "columns",
|
|
244
|
+
"label": "Columns",
|
|
245
|
+
"options": [
|
|
246
|
+
{ "value": "2", "label": "2" },
|
|
247
|
+
{ "value": "3", "label": "3" },
|
|
248
|
+
{ "value": "4", "label": "4" }
|
|
249
|
+
],
|
|
250
|
+
"default": "4"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"type": "checkbox",
|
|
254
|
+
"id": "show_vendor",
|
|
255
|
+
"label": "Show vendor",
|
|
256
|
+
"default": false
|
|
257
|
+
}
|
|
258
|
+
],
|
|
259
|
+
"presets": [
|
|
260
|
+
{
|
|
261
|
+
"name": "Featured Collection"
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
{% endschema %}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## 3. Storefront API
|
|
271
|
+
|
|
272
|
+
### GraphQL Queries
|
|
273
|
+
|
|
274
|
+
The Storefront API is a public-facing read API for headless storefronts. It uses a public access token.
|
|
275
|
+
|
|
276
|
+
```graphql
|
|
277
|
+
# Fetch products
|
|
278
|
+
query GetProducts($first: Int!, $after: String) {
|
|
279
|
+
products(first: $first, after: $after) {
|
|
280
|
+
edges {
|
|
281
|
+
cursor
|
|
282
|
+
node {
|
|
283
|
+
id
|
|
284
|
+
title
|
|
285
|
+
handle
|
|
286
|
+
description
|
|
287
|
+
priceRange {
|
|
288
|
+
minVariantPrice {
|
|
289
|
+
amount
|
|
290
|
+
currencyCode
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
images(first: 1) {
|
|
294
|
+
edges {
|
|
295
|
+
node {
|
|
296
|
+
url(transform: { maxWidth: 800 })
|
|
297
|
+
altText
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
variants(first: 10) {
|
|
302
|
+
edges {
|
|
303
|
+
node {
|
|
304
|
+
id
|
|
305
|
+
title
|
|
306
|
+
price { amount currencyCode }
|
|
307
|
+
availableForSale
|
|
308
|
+
selectedOptions { name value }
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
pageInfo {
|
|
315
|
+
hasNextPage
|
|
316
|
+
endCursor
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
# Create a cart
|
|
322
|
+
mutation CartCreate($lines: [CartLineInput!]!) {
|
|
323
|
+
cartCreate(input: { lines: $lines }) {
|
|
324
|
+
cart {
|
|
325
|
+
id
|
|
326
|
+
checkoutUrl
|
|
327
|
+
totalQuantity
|
|
328
|
+
cost {
|
|
329
|
+
totalAmount { amount currencyCode }
|
|
330
|
+
subtotalAmount { amount currencyCode }
|
|
331
|
+
}
|
|
332
|
+
lines(first: 50) {
|
|
333
|
+
edges {
|
|
334
|
+
node {
|
|
335
|
+
id
|
|
336
|
+
quantity
|
|
337
|
+
merchandise {
|
|
338
|
+
... on ProductVariant {
|
|
339
|
+
id
|
|
340
|
+
title
|
|
341
|
+
price { amount currencyCode }
|
|
342
|
+
product { title handle }
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
userErrors { field message }
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### TypeScript Client
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
// lib/shopify.ts
|
|
358
|
+
const STOREFRONT_API_URL = `https://${process.env.SHOPIFY_STORE_DOMAIN}/api/2024-10/graphql.json`;
|
|
359
|
+
const STOREFRONT_TOKEN = process.env.SHOPIFY_STOREFRONT_TOKEN;
|
|
360
|
+
|
|
361
|
+
export async function storefrontQuery<T>(query: string, variables?: Record<string, unknown>): Promise<T> {
|
|
362
|
+
const response = await fetch(STOREFRONT_API_URL, {
|
|
363
|
+
method: 'POST',
|
|
364
|
+
headers: {
|
|
365
|
+
'Content-Type': 'application/json',
|
|
366
|
+
'X-Shopify-Storefront-Access-Token': STOREFRONT_TOKEN,
|
|
367
|
+
},
|
|
368
|
+
body: JSON.stringify({ query, variables }),
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
if (!response.ok) {
|
|
372
|
+
throw new Error('Storefront API error: ' + response.statusText);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const json = await response.json();
|
|
376
|
+
if (json.errors) {
|
|
377
|
+
throw new Error('GraphQL errors: ' + JSON.stringify(json.errors));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return json.data as T;
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## 4. Admin API (GraphQL)
|
|
387
|
+
|
|
388
|
+
### Authenticated Queries
|
|
389
|
+
|
|
390
|
+
The Admin API requires an access token from a Shopify app or custom app.
|
|
391
|
+
|
|
392
|
+
```graphql
|
|
393
|
+
# Fetch orders
|
|
394
|
+
query GetOrders($first: Int!) {
|
|
395
|
+
orders(first: $first, sortKey: CREATED_AT, reverse: true) {
|
|
396
|
+
edges {
|
|
397
|
+
node {
|
|
398
|
+
id
|
|
399
|
+
name
|
|
400
|
+
createdAt
|
|
401
|
+
displayFinancialStatus
|
|
402
|
+
displayFulfillmentStatus
|
|
403
|
+
totalPriceSet {
|
|
404
|
+
shopMoney { amount currencyCode }
|
|
405
|
+
}
|
|
406
|
+
customer {
|
|
407
|
+
firstName
|
|
408
|
+
lastName
|
|
409
|
+
email
|
|
410
|
+
}
|
|
411
|
+
lineItems(first: 10) {
|
|
412
|
+
edges {
|
|
413
|
+
node {
|
|
414
|
+
title
|
|
415
|
+
quantity
|
|
416
|
+
variant { sku }
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
# Update product metafield
|
|
426
|
+
mutation UpdateProductMetafield($input: ProductInput!) {
|
|
427
|
+
productUpdate(input: $input) {
|
|
428
|
+
product {
|
|
429
|
+
id
|
|
430
|
+
metafields(first: 5) {
|
|
431
|
+
edges {
|
|
432
|
+
node { namespace key value }
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
userErrors { field message }
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Admin API Client
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
// lib/shopify-admin.ts
|
|
445
|
+
const ADMIN_API_URL = `https://${process.env.SHOPIFY_STORE_DOMAIN}/admin/api/2024-10/graphql.json`;
|
|
446
|
+
const ADMIN_TOKEN = process.env.SHOPIFY_ADMIN_TOKEN;
|
|
447
|
+
|
|
448
|
+
export async function adminQuery<T>(query: string, variables?: Record<string, unknown>): Promise<T> {
|
|
449
|
+
const response = await fetch(ADMIN_API_URL, {
|
|
450
|
+
method: 'POST',
|
|
451
|
+
headers: {
|
|
452
|
+
'Content-Type': 'application/json',
|
|
453
|
+
'X-Shopify-Access-Token': ADMIN_TOKEN,
|
|
454
|
+
},
|
|
455
|
+
body: JSON.stringify({ query, variables }),
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
const json = await response.json();
|
|
459
|
+
if (json.errors) {
|
|
460
|
+
throw new Error('Admin API errors: ' + JSON.stringify(json.errors));
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return json.data as T;
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## 5. Shopify CLI and App Development
|
|
470
|
+
|
|
471
|
+
### CLI Commands
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
# Create a new app
|
|
475
|
+
shopify app init
|
|
476
|
+
|
|
477
|
+
# Start development server with hot reload
|
|
478
|
+
shopify app dev
|
|
479
|
+
|
|
480
|
+
# Generate extensions
|
|
481
|
+
shopify app generate extension
|
|
482
|
+
|
|
483
|
+
# Deploy app
|
|
484
|
+
shopify app deploy
|
|
485
|
+
|
|
486
|
+
# Theme commands
|
|
487
|
+
shopify theme dev # Start local theme development server
|
|
488
|
+
shopify theme push # Push theme to store
|
|
489
|
+
shopify theme pull # Pull theme from store
|
|
490
|
+
shopify theme list # List themes in store
|
|
491
|
+
shopify theme package # Package theme as zip
|
|
492
|
+
shopify theme check # Lint theme with Theme Check
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Remix App Template
|
|
496
|
+
|
|
497
|
+
```typescript
|
|
498
|
+
// app/routes/app._index.tsx (Remix app inside Shopify Admin)
|
|
499
|
+
import { json, type LoaderFunctionArgs } from '@remix-run/node';
|
|
500
|
+
import { useLoaderData } from '@remix-run/react';
|
|
501
|
+
import { Page, Layout, Card, DataTable, Text } from '@shopify/polaris';
|
|
502
|
+
import { authenticate } from '../shopify.server';
|
|
503
|
+
|
|
504
|
+
export async function loader({ request }: LoaderFunctionArgs) {
|
|
505
|
+
const { admin } = await authenticate.admin(request);
|
|
506
|
+
|
|
507
|
+
const response = await admin.graphql(`
|
|
508
|
+
query {
|
|
509
|
+
products(first: 10) {
|
|
510
|
+
edges {
|
|
511
|
+
node {
|
|
512
|
+
id
|
|
513
|
+
title
|
|
514
|
+
status
|
|
515
|
+
totalInventory
|
|
516
|
+
priceRangeV2 {
|
|
517
|
+
minVariantPrice { amount currencyCode }
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
`);
|
|
524
|
+
|
|
525
|
+
const data = await response.json();
|
|
526
|
+
return json({ products: data.data.products.edges.map((e: any) => e.node) });
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
export default function Index() {
|
|
530
|
+
const { products } = useLoaderData<typeof loader>();
|
|
531
|
+
|
|
532
|
+
const rows = products.map((product: any) => [
|
|
533
|
+
product.title,
|
|
534
|
+
product.status,
|
|
535
|
+
product.totalInventory,
|
|
536
|
+
`$${product.priceRangeV2.minVariantPrice.amount}`,
|
|
537
|
+
]);
|
|
538
|
+
|
|
539
|
+
return (
|
|
540
|
+
<Page title="Product Dashboard">
|
|
541
|
+
<Layout>
|
|
542
|
+
<Layout.Section>
|
|
543
|
+
<Card>
|
|
544
|
+
<DataTable
|
|
545
|
+
columnContentTypes={['text', 'text', 'numeric', 'numeric']}
|
|
546
|
+
headings={['Product', 'Status', 'Inventory', 'Price']}
|
|
547
|
+
rows={rows}
|
|
548
|
+
/>
|
|
549
|
+
</Card>
|
|
550
|
+
</Layout.Section>
|
|
551
|
+
</Layout>
|
|
552
|
+
</Page>
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
## 6. Webhooks
|
|
560
|
+
|
|
561
|
+
### Webhook Registration
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
// Register webhooks in your app configuration
|
|
565
|
+
// shopify.app.toml
|
|
566
|
+
// [webhooks]
|
|
567
|
+
// api_version = "2024-10"
|
|
568
|
+
//
|
|
569
|
+
// [[webhooks.subscriptions]]
|
|
570
|
+
// topics = ["orders/create", "orders/updated"]
|
|
571
|
+
// uri = "/webhooks"
|
|
572
|
+
|
|
573
|
+
// app/webhooks.ts -- Webhook handler
|
|
574
|
+
import crypto from 'crypto';
|
|
575
|
+
|
|
576
|
+
export function verifyWebhookHMAC(body: string, hmacHeader: string, secret: string): boolean {
|
|
577
|
+
const hash = crypto
|
|
578
|
+
.createHmac('sha256', secret)
|
|
579
|
+
.update(body, 'utf8')
|
|
580
|
+
.digest('base64');
|
|
581
|
+
return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(hmacHeader));
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Handle order creation
|
|
585
|
+
export async function handleOrderCreate(payload: Record<string, unknown>): Promise<void> {
|
|
586
|
+
const orderId = payload.id;
|
|
587
|
+
const email = payload.email;
|
|
588
|
+
const totalPrice = payload.total_price;
|
|
589
|
+
const lineItems = payload.line_items;
|
|
590
|
+
|
|
591
|
+
// Process the order (sync to your system, send notification, etc.)
|
|
592
|
+
console.log(`New order ${orderId}: $${totalPrice}`);
|
|
593
|
+
}
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### Webhook Topics
|
|
597
|
+
|
|
598
|
+
```
|
|
599
|
+
orders/create -- New order placed
|
|
600
|
+
orders/updated -- Order modified
|
|
601
|
+
orders/paid -- Order payment confirmed
|
|
602
|
+
orders/fulfilled -- Order fully shipped
|
|
603
|
+
products/create -- New product created
|
|
604
|
+
products/update -- Product modified
|
|
605
|
+
products/delete -- Product removed
|
|
606
|
+
customers/create -- New customer account
|
|
607
|
+
customers/update -- Customer info changed
|
|
608
|
+
carts/create -- New cart created
|
|
609
|
+
carts/update -- Cart modified
|
|
610
|
+
checkouts/create -- Checkout initiated
|
|
611
|
+
checkouts/update -- Checkout modified
|
|
612
|
+
app/uninstalled -- App removed from store
|
|
613
|
+
shop/update -- Store settings changed
|
|
614
|
+
inventory_levels/update -- Stock level changed
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
## 7. Checkout Extensions and Shopify Functions
|
|
620
|
+
|
|
621
|
+
### Checkout UI Extension
|
|
622
|
+
|
|
623
|
+
```typescript
|
|
624
|
+
// extensions/checkout-ui/src/Checkout.tsx
|
|
625
|
+
import {
|
|
626
|
+
reactExtension,
|
|
627
|
+
Banner,
|
|
628
|
+
BlockStack,
|
|
629
|
+
Text,
|
|
630
|
+
useCartLines,
|
|
631
|
+
useTotalAmount,
|
|
632
|
+
} from '@shopify/ui-extensions-react/checkout';
|
|
633
|
+
|
|
634
|
+
export default reactExtension('purchase.checkout.block.render', () => <FreeShippingBanner />);
|
|
635
|
+
|
|
636
|
+
function FreeShippingBanner() {
|
|
637
|
+
const cartLines = useCartLines();
|
|
638
|
+
const totalAmount = useTotalAmount();
|
|
639
|
+
const freeShippingThreshold = 75.0;
|
|
640
|
+
const remaining = freeShippingThreshold - parseFloat(totalAmount.amount);
|
|
641
|
+
|
|
642
|
+
if (remaining <= 0) {
|
|
643
|
+
return (
|
|
644
|
+
<Banner status="success">
|
|
645
|
+
<Text>You qualify for free shipping!</Text>
|
|
646
|
+
</Banner>
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return (
|
|
651
|
+
<Banner status="info">
|
|
652
|
+
<BlockStack>
|
|
653
|
+
<Text>Add ${remaining.toFixed(2)} more for free shipping!</Text>
|
|
654
|
+
</BlockStack>
|
|
655
|
+
</Banner>
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
### Shopify Functions (Discount Example)
|
|
661
|
+
|
|
662
|
+
```rust
|
|
663
|
+
// extensions/discount-function/src/main.rs
|
|
664
|
+
use shopify_function::prelude::*;
|
|
665
|
+
use shopify_function::Result;
|
|
666
|
+
|
|
667
|
+
#[shopify_function_target(query_path = "src/run.graphql", schema_path = "schema.graphql")]
|
|
668
|
+
fn run(input: input::ResponseData) -> Result<output::FunctionRunResult> {
|
|
669
|
+
let mut targets = Vec::new();
|
|
670
|
+
|
|
671
|
+
for line in &input.cart.lines {
|
|
672
|
+
if let Some(product_variant) = &line.merchandise.as_variant() {
|
|
673
|
+
if line.quantity >= 3 {
|
|
674
|
+
targets.push(output::Target {
|
|
675
|
+
product_variant: Some(output::ProductVariantTarget {
|
|
676
|
+
id: product_variant.id.clone(),
|
|
677
|
+
quantity: None,
|
|
678
|
+
}),
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
if targets.is_empty() {
|
|
685
|
+
return Ok(output::FunctionRunResult {
|
|
686
|
+
discounts: vec![],
|
|
687
|
+
discount_application_strategy: output::DiscountApplicationStrategy::FIRST,
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
Ok(output::FunctionRunResult {
|
|
692
|
+
discounts: vec![output::Discount {
|
|
693
|
+
message: Some("Buy 3+ get 10% off".to_string()),
|
|
694
|
+
targets,
|
|
695
|
+
value: output::Value::Percentage(output::Percentage { value: "10.0".to_string() }),
|
|
696
|
+
}],
|
|
697
|
+
discount_application_strategy: output::DiscountApplicationStrategy::FIRST,
|
|
698
|
+
})
|
|
699
|
+
}
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
---
|
|
703
|
+
|
|
704
|
+
## 8. Metafields
|
|
705
|
+
|
|
706
|
+
### Defining Metafields
|
|
707
|
+
|
|
708
|
+
```graphql
|
|
709
|
+
# Create a metafield definition (Admin API)
|
|
710
|
+
mutation CreateMetafieldDefinition {
|
|
711
|
+
metafieldDefinitionCreate(definition: {
|
|
712
|
+
name: "Warranty Info"
|
|
713
|
+
namespace: "custom"
|
|
714
|
+
key: "warranty"
|
|
715
|
+
type: "single_line_text_field"
|
|
716
|
+
description: "Product warranty information"
|
|
717
|
+
ownerType: PRODUCT
|
|
718
|
+
validations: [
|
|
719
|
+
{ name: "min", value: "1" }
|
|
720
|
+
{ name: "max", value: "200" }
|
|
721
|
+
]
|
|
722
|
+
pin: true
|
|
723
|
+
}) {
|
|
724
|
+
createdDefinition { id }
|
|
725
|
+
userErrors { field message }
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
### Accessing Metafields in Liquid
|
|
731
|
+
|
|
732
|
+
```liquid
|
|
733
|
+
{%- comment -%} Product metafields {%- endcomment -%}
|
|
734
|
+
{{ product.metafields.custom.warranty.value }}
|
|
735
|
+
{{ product.metafields.custom.care_instructions.value }}
|
|
736
|
+
|
|
737
|
+
{%- comment -%} Rich text metafield {%- endcomment -%}
|
|
738
|
+
{{ product.metafields.custom.long_description | metafield_tag }}
|
|
739
|
+
|
|
740
|
+
{%- comment -%} Image metafield {%- endcomment -%}
|
|
741
|
+
{%- assign hero = product.metafields.custom.hero_image.value -%}
|
|
742
|
+
{%- if hero -%}
|
|
743
|
+
{{ hero | image_url: width: 1200 | image_tag: loading: 'lazy' }}
|
|
744
|
+
{%- endif -%}
|
|
745
|
+
|
|
746
|
+
{%- comment -%} JSON metafield {%- endcomment -%}
|
|
747
|
+
{%- assign specs = product.metafields.custom.specifications.value -%}
|
|
748
|
+
{%- if specs -%}
|
|
749
|
+
<dl>
|
|
750
|
+
{%- for spec in specs -%}
|
|
751
|
+
<dt>{{ spec.label }}</dt>
|
|
752
|
+
<dd>{{ spec.value }}</dd>
|
|
753
|
+
{%- endfor -%}
|
|
754
|
+
</dl>
|
|
755
|
+
{%- endif -%}
|
|
756
|
+
|
|
757
|
+
{%- comment -%} List of references (e.g. related products) {%- endcomment -%}
|
|
758
|
+
{%- for related in product.metafields.custom.related_products.value -%}
|
|
759
|
+
{% render 'product-card', product: related %}
|
|
760
|
+
{%- endfor -%}
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### Metafield Types
|
|
764
|
+
|
|
765
|
+
```
|
|
766
|
+
single_line_text_field -- Short text
|
|
767
|
+
multi_line_text_field -- Long text (preserves line breaks)
|
|
768
|
+
rich_text_field -- HTML-like rich text
|
|
769
|
+
number_integer -- Whole numbers
|
|
770
|
+
number_decimal -- Decimal numbers
|
|
771
|
+
boolean -- True/false
|
|
772
|
+
date -- Date (YYYY-MM-DD)
|
|
773
|
+
date_time -- Date and time (ISO 8601)
|
|
774
|
+
color -- Hex color (#RRGGBB)
|
|
775
|
+
url -- Valid URL
|
|
776
|
+
json -- Arbitrary JSON
|
|
777
|
+
file_reference -- Uploaded file
|
|
778
|
+
product_reference -- Link to a product
|
|
779
|
+
collection_reference -- Link to a collection
|
|
780
|
+
variant_reference -- Link to a variant
|
|
781
|
+
page_reference -- Link to a page
|
|
782
|
+
list.single_line_text_field -- List of text values
|
|
783
|
+
list.product_reference -- List of product links
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
---
|
|
787
|
+
|
|
788
|
+
## 9. Cart and Checkout Customization
|
|
789
|
+
|
|
790
|
+
### AJAX Cart API
|
|
791
|
+
|
|
792
|
+
```javascript
|
|
793
|
+
// Add to cart
|
|
794
|
+
async function addToCart(variantId, quantity = 1) {
|
|
795
|
+
const response = await fetch('/cart/add.js', {
|
|
796
|
+
method: 'POST',
|
|
797
|
+
headers: { 'Content-Type': 'application/json' },
|
|
798
|
+
body: JSON.stringify({
|
|
799
|
+
items: [{ id: variantId, quantity }],
|
|
800
|
+
}),
|
|
801
|
+
});
|
|
802
|
+
return response.json();
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Update cart item
|
|
806
|
+
async function updateCartItem(key, quantity) {
|
|
807
|
+
const response = await fetch('/cart/change.js', {
|
|
808
|
+
method: 'POST',
|
|
809
|
+
headers: { 'Content-Type': 'application/json' },
|
|
810
|
+
body: JSON.stringify({ id: key, quantity }),
|
|
811
|
+
});
|
|
812
|
+
return response.json();
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// Get cart state
|
|
816
|
+
async function getCart() {
|
|
817
|
+
const response = await fetch('/cart.js');
|
|
818
|
+
return response.json();
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Clear cart
|
|
822
|
+
async function clearCart() {
|
|
823
|
+
const response = await fetch('/cart/clear.js', {
|
|
824
|
+
method: 'POST',
|
|
825
|
+
headers: { 'Content-Type': 'application/json' },
|
|
826
|
+
});
|
|
827
|
+
return response.json();
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Add cart attributes (order notes, gift wrapping, etc.)
|
|
831
|
+
async function updateCartAttributes(attributes) {
|
|
832
|
+
const response = await fetch('/cart/update.js', {
|
|
833
|
+
method: 'POST',
|
|
834
|
+
headers: { 'Content-Type': 'application/json' },
|
|
835
|
+
body: JSON.stringify({ attributes }),
|
|
836
|
+
});
|
|
837
|
+
return response.json();
|
|
838
|
+
}
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
### Cart Drawer Section
|
|
842
|
+
|
|
843
|
+
```liquid
|
|
844
|
+
{% comment %} sections/cart-drawer.liquid {% endcomment %}
|
|
845
|
+
<div id="cart-drawer" class="cart-drawer" data-section-id="{{ section.id }}">
|
|
846
|
+
<div class="cart-drawer__header">
|
|
847
|
+
<h2>Your Cart ({{ cart.item_count }})</h2>
|
|
848
|
+
<button type="button" class="cart-drawer__close" aria-label="Close cart">X</button>
|
|
849
|
+
</div>
|
|
850
|
+
|
|
851
|
+
{%- if cart.item_count > 0 -%}
|
|
852
|
+
<div class="cart-drawer__items">
|
|
853
|
+
{%- for item in cart.items -%}
|
|
854
|
+
<div class="cart-item" data-key="{{ item.key }}">
|
|
855
|
+
<img
|
|
856
|
+
src="{{ item.image | image_url: width: 120 }}"
|
|
857
|
+
alt="{{ item.title | escape }}"
|
|
858
|
+
width="120"
|
|
859
|
+
height="120"
|
|
860
|
+
loading="lazy"
|
|
861
|
+
>
|
|
862
|
+
<div class="cart-item__details">
|
|
863
|
+
<a href="{{ item.url }}">{{ item.product.title }}</a>
|
|
864
|
+
{%- unless item.product.has_only_default_variant -%}
|
|
865
|
+
<p class="cart-item__variant">{{ item.variant.title }}</p>
|
|
866
|
+
{%- endunless -%}
|
|
867
|
+
<p class="cart-item__price">{{ item.final_line_price | money }}</p>
|
|
868
|
+
<div class="cart-item__quantity">
|
|
869
|
+
<button type="button" data-action="decrease">-</button>
|
|
870
|
+
<input type="number" value="{{ item.quantity }}" min="0" max="99" aria-label="Quantity">
|
|
871
|
+
<button type="button" data-action="increase">+</button>
|
|
872
|
+
</div>
|
|
873
|
+
</div>
|
|
874
|
+
</div>
|
|
875
|
+
{%- endfor -%}
|
|
876
|
+
</div>
|
|
877
|
+
|
|
878
|
+
<div class="cart-drawer__footer">
|
|
879
|
+
<div class="cart-drawer__subtotal">
|
|
880
|
+
<span>Subtotal</span>
|
|
881
|
+
<span>{{ cart.total_price | money }}</span>
|
|
882
|
+
</div>
|
|
883
|
+
<a href="/checkout" class="button button--primary">Checkout</a>
|
|
884
|
+
</div>
|
|
885
|
+
{%- else -%}
|
|
886
|
+
<p class="cart-drawer__empty">Your cart is empty.</p>
|
|
887
|
+
{%- endif -%}
|
|
888
|
+
</div>
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
---
|
|
892
|
+
|
|
893
|
+
## 10. Hydrogen (React Framework)
|
|
894
|
+
|
|
895
|
+
### Project Structure
|
|
896
|
+
|
|
897
|
+
```
|
|
898
|
+
hydrogen-store/
|
|
899
|
+
app/
|
|
900
|
+
components/ # Reusable React components
|
|
901
|
+
ProductCard.tsx
|
|
902
|
+
CartDrawer.tsx
|
|
903
|
+
Layout.tsx
|
|
904
|
+
routes/ # File-based routing (Remix)
|
|
905
|
+
($locale)._index.tsx
|
|
906
|
+
($locale).products.$handle.tsx
|
|
907
|
+
($locale).collections.$handle.tsx
|
|
908
|
+
($locale).cart.tsx
|
|
909
|
+
($locale).account.tsx
|
|
910
|
+
lib/
|
|
911
|
+
shopify.ts # Storefront API client
|
|
912
|
+
fragments.ts # GraphQL fragments
|
|
913
|
+
styles/
|
|
914
|
+
app.css
|
|
915
|
+
root.tsx
|
|
916
|
+
entry.server.tsx
|
|
917
|
+
public/
|
|
918
|
+
hydrogen.config.ts
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
### Hydrogen Component
|
|
922
|
+
|
|
923
|
+
```typescript
|
|
924
|
+
// app/routes/($locale).products.$handle.tsx
|
|
925
|
+
import { json, type LoaderFunctionArgs } from '@shopify/remix-oxygen';
|
|
926
|
+
import { useLoaderData } from '@remix-run/react';
|
|
927
|
+
import { Image, Money, ShopPayButton } from '@shopify/hydrogen';
|
|
928
|
+
|
|
929
|
+
export async function loader({ params, context }: LoaderFunctionArgs) {
|
|
930
|
+
const { handle } = params;
|
|
931
|
+
const { storefront } = context;
|
|
932
|
+
|
|
933
|
+
const { product } = await storefront.query(PRODUCT_QUERY, {
|
|
934
|
+
variables: { handle },
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
if (!product) {
|
|
938
|
+
throw new Response('Product not found', { status: 404 });
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
return json({ product });
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
export default function ProductPage() {
|
|
945
|
+
const { product } = useLoaderData<typeof loader>();
|
|
946
|
+
const selectedVariant = product.variants.nodes[0];
|
|
947
|
+
|
|
948
|
+
return (
|
|
949
|
+
<div className="product-page">
|
|
950
|
+
<div className="product-gallery">
|
|
951
|
+
{product.images.nodes.map((image: any) => (
|
|
952
|
+
<Image
|
|
953
|
+
key={image.id}
|
|
954
|
+
data={image}
|
|
955
|
+
sizes="(min-width: 768px) 50vw, 100vw"
|
|
956
|
+
loading="eager"
|
|
957
|
+
/>
|
|
958
|
+
))}
|
|
959
|
+
</div>
|
|
960
|
+
<div className="product-info">
|
|
961
|
+
<h1>{product.title}</h1>
|
|
962
|
+
<Money data={selectedVariant.price} />
|
|
963
|
+
{product.descriptionHtml && (
|
|
964
|
+
<div dangerouslySetInnerHTML={{ __html: product.descriptionHtml }} />
|
|
965
|
+
)}
|
|
966
|
+
<ShopPayButton
|
|
967
|
+
variantIds={[selectedVariant.id]}
|
|
968
|
+
storeDomain={`${import.meta.env.PUBLIC_STORE_DOMAIN}`}
|
|
969
|
+
/>
|
|
970
|
+
</div>
|
|
971
|
+
</div>
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
const PRODUCT_QUERY = `#graphql
|
|
976
|
+
query Product($handle: String!) {
|
|
977
|
+
product(handle: $handle) {
|
|
978
|
+
id
|
|
979
|
+
title
|
|
980
|
+
handle
|
|
981
|
+
descriptionHtml
|
|
982
|
+
images(first: 10) {
|
|
983
|
+
nodes {
|
|
984
|
+
id
|
|
985
|
+
url
|
|
986
|
+
altText
|
|
987
|
+
width
|
|
988
|
+
height
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
variants(first: 50) {
|
|
992
|
+
nodes {
|
|
993
|
+
id
|
|
994
|
+
title
|
|
995
|
+
availableForSale
|
|
996
|
+
price { amount currencyCode }
|
|
997
|
+
compareAtPrice { amount currencyCode }
|
|
998
|
+
selectedOptions { name value }
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
`;
|
|
1004
|
+
```
|
|
1005
|
+
|
|
1006
|
+
---
|
|
1007
|
+
|
|
1008
|
+
## 11. Theme App Extensions
|
|
1009
|
+
|
|
1010
|
+
```liquid
|
|
1011
|
+
{% comment %} App blocks that merchants can add to any section {% endcomment %}
|
|
1012
|
+
{% comment %} extensions/theme-app-extension/blocks/product-reviews.liquid {% endcomment %}
|
|
1013
|
+
|
|
1014
|
+
<div class="app-reviews" data-product-id="{{ product.id }}">
|
|
1015
|
+
{%- if block.settings.show_summary -%}
|
|
1016
|
+
<div class="reviews-summary">
|
|
1017
|
+
<span class="reviews-rating">{{ product.metafields.myapp.average_rating.value }}</span>
|
|
1018
|
+
<span class="reviews-count">({{ product.metafields.myapp.review_count.value }} reviews)</span>
|
|
1019
|
+
</div>
|
|
1020
|
+
{%- endif -%}
|
|
1021
|
+
|
|
1022
|
+
<div id="reviews-list" data-product-handle="{{ product.handle }}">
|
|
1023
|
+
Loading reviews...
|
|
1024
|
+
</div>
|
|
1025
|
+
</div>
|
|
1026
|
+
|
|
1027
|
+
<script src="{{ 'reviews-widget.js' | asset_url }}" defer></script>
|
|
1028
|
+
|
|
1029
|
+
{% schema %}
|
|
1030
|
+
{
|
|
1031
|
+
"name": "Product Reviews",
|
|
1032
|
+
"target": "section",
|
|
1033
|
+
"settings": [
|
|
1034
|
+
{
|
|
1035
|
+
"type": "checkbox",
|
|
1036
|
+
"id": "show_summary",
|
|
1037
|
+
"label": "Show rating summary",
|
|
1038
|
+
"default": true
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
"type": "range",
|
|
1042
|
+
"id": "reviews_per_page",
|
|
1043
|
+
"min": 3,
|
|
1044
|
+
"max": 20,
|
|
1045
|
+
"step": 1,
|
|
1046
|
+
"default": 5,
|
|
1047
|
+
"label": "Reviews per page"
|
|
1048
|
+
}
|
|
1049
|
+
]
|
|
1050
|
+
}
|
|
1051
|
+
{% endschema %}
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
---
|
|
1055
|
+
|
|
1056
|
+
## 12. Performance Optimization
|
|
1057
|
+
|
|
1058
|
+
### Image Optimization
|
|
1059
|
+
|
|
1060
|
+
```liquid
|
|
1061
|
+
{%- comment -%} Responsive images with srcset {%- endcomment -%}
|
|
1062
|
+
{{ product.featured_image | image_url: width: 800 | image_tag:
|
|
1063
|
+
loading: 'lazy',
|
|
1064
|
+
widths: '200,400,600,800',
|
|
1065
|
+
sizes: '(min-width: 768px) 50vw, 100vw'
|
|
1066
|
+
}}
|
|
1067
|
+
|
|
1068
|
+
{%- comment -%} Above-the-fold images: eager loading {%- endcomment -%}
|
|
1069
|
+
{{ product.featured_image | image_url: width: 1200 | image_tag:
|
|
1070
|
+
loading: 'eager',
|
|
1071
|
+
fetchpriority: 'high',
|
|
1072
|
+
widths: '400,600,800,1200'
|
|
1073
|
+
}}
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
### Script Loading
|
|
1077
|
+
|
|
1078
|
+
```liquid
|
|
1079
|
+
{%- comment -%} Defer non-critical JavaScript {%- endcomment -%}
|
|
1080
|
+
<script src="{{ 'cart-drawer.js' | asset_url }}" defer></script>
|
|
1081
|
+
|
|
1082
|
+
{%- comment -%} Lazy-load third-party scripts {%- endcomment -%}
|
|
1083
|
+
<script>
|
|
1084
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
1085
|
+
var observer = new IntersectionObserver(function(entries) {
|
|
1086
|
+
entries.forEach(function(entry) {
|
|
1087
|
+
if (entry.isIntersecting) {
|
|
1088
|
+
var script = document.createElement('script');
|
|
1089
|
+
script.src = entry.target.dataset.src;
|
|
1090
|
+
document.body.appendChild(script);
|
|
1091
|
+
observer.unobserve(entry.target);
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
});
|
|
1095
|
+
document.querySelectorAll('[data-lazy-script]').forEach(function(el) {
|
|
1096
|
+
observer.observe(el);
|
|
1097
|
+
});
|
|
1098
|
+
});
|
|
1099
|
+
</script>
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
### Liquid Performance
|
|
1103
|
+
|
|
1104
|
+
```liquid
|
|
1105
|
+
{%- comment -%} GOOD: Assign once, use many {%- endcomment -%}
|
|
1106
|
+
{%- assign featured_image = product.featured_image -%}
|
|
1107
|
+
{%- if featured_image -%}
|
|
1108
|
+
{{ featured_image | image_url: width: 400 | image_tag }}
|
|
1109
|
+
{%- endif -%}
|
|
1110
|
+
|
|
1111
|
+
{%- comment -%} GOOD: Use render instead of include (scoped, faster) {%- endcomment -%}
|
|
1112
|
+
{% render 'product-card', product: product %}
|
|
1113
|
+
|
|
1114
|
+
{%- comment -%} BAD: include shares all variables (slower, harder to debug) {%- endcomment -%}
|
|
1115
|
+
{% include 'product-card' %}
|
|
1116
|
+
|
|
1117
|
+
{%- comment -%} GOOD: Limit collection loops {%- endcomment -%}
|
|
1118
|
+
{%- for product in collection.products limit: 12 -%}
|
|
1119
|
+
...
|
|
1120
|
+
{%- endfor -%}
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
---
|
|
1124
|
+
|
|
1125
|
+
## 13. Common Anti-Patterns
|
|
1126
|
+
|
|
1127
|
+
### Querying in Loops
|
|
1128
|
+
|
|
1129
|
+
```liquid
|
|
1130
|
+
{%- comment -%} BAD: Fetching data inside loops {%- endcomment -%}
|
|
1131
|
+
{%- for product in collection.products -%}
|
|
1132
|
+
{%- assign related = product.metafields.custom.related.value -%}
|
|
1133
|
+
{%- for r in related -%}
|
|
1134
|
+
{%- comment -%} Each iteration triggers a metafield lookup {%- endcomment -%}
|
|
1135
|
+
{{ r.title }}
|
|
1136
|
+
{%- endfor -%}
|
|
1137
|
+
{%- endfor -%}
|
|
1138
|
+
```
|
|
1139
|
+
|
|
1140
|
+
### Hardcoding Store Data
|
|
1141
|
+
|
|
1142
|
+
```liquid
|
|
1143
|
+
{%- comment -%} BAD: Hardcoded currency {%- endcomment -%}
|
|
1144
|
+
<span>${{ product.price | divided_by: 100.0 }}</span>
|
|
1145
|
+
|
|
1146
|
+
{%- comment -%} GOOD: Use money filters (handles multi-currency) {%- endcomment -%}
|
|
1147
|
+
<span>{{ product.price | money }}</span>
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
### Ignoring Section Rendering API
|
|
1151
|
+
|
|
1152
|
+
```javascript
|
|
1153
|
+
// BAD: Full page reload for section updates
|
|
1154
|
+
window.location.reload();
|
|
1155
|
+
|
|
1156
|
+
// GOOD: Use Section Rendering API for partial updates
|
|
1157
|
+
async function refreshSection(sectionId) {
|
|
1158
|
+
const url = `${window.location.pathname}?sections=${sectionId}`;
|
|
1159
|
+
const response = await fetch(url);
|
|
1160
|
+
const data = await response.json();
|
|
1161
|
+
const container = document.querySelector(`[data-section-id="${sectionId}"]`);
|
|
1162
|
+
if (container) {
|
|
1163
|
+
container.outerHTML = data[sectionId];
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
---
|
|
1169
|
+
|
|
1170
|
+
## 14. Critical Reminders
|
|
1171
|
+
|
|
1172
|
+
### ALWAYS
|
|
1173
|
+
|
|
1174
|
+
- Use JSON templates for Online Store 2.0 compatibility
|
|
1175
|
+
- Use `render` instead of `include` for snippets (scoped and faster)
|
|
1176
|
+
- Add `loading="lazy"` to images below the fold
|
|
1177
|
+
- Use money filters (`| money`) instead of manual price formatting
|
|
1178
|
+
- Validate webhook HMAC signatures before processing payloads
|
|
1179
|
+
- Use Storefront API access tokens (not Admin tokens) in client-side code
|
|
1180
|
+
- Test themes with Theme Check (`shopify theme check`)
|
|
1181
|
+
- Use metafield definitions for type validation
|
|
1182
|
+
- Handle pagination for large collections (never assume small datasets)
|
|
1183
|
+
|
|
1184
|
+
### NEVER
|
|
1185
|
+
|
|
1186
|
+
- Expose Admin API access tokens in client-side JavaScript
|
|
1187
|
+
- Use `include` when `render` is available (deprecated, slower, leaks scope)
|
|
1188
|
+
- Hardcode prices, currencies, or store-specific data in templates
|
|
1189
|
+
- Skip webhook verification (attackers can send fake webhook payloads)
|
|
1190
|
+
- Use `posts_per_page: -1` equivalents -- always paginate API responses
|
|
1191
|
+
- Modify the checkout without using Checkout Extensions (violates Shopify rules)
|
|
1192
|
+
- Rely on `cart.js` responses being synchronous -- always use async/await
|
|
1193
|
+
- Store customer PII in metafields without understanding Shopify privacy requirements
|