@pradip1995/create-storefront-app 0.2.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.
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"
3
+ import { join, dirname, resolve } from "path"
4
+ import { fileURLToPath } from "url"
5
+ import { execSync } from "child_process"
6
+ import { buildDependencies } from "../lib/deps.js"
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url))
9
+ const PKG_ROOT = join(__dirname, "..")
10
+
11
+ const THEME_MAP = { valero: "valero", impulse: "impulse", sahsha: "impulse" }
12
+
13
+ function parseArgs(argv) {
14
+ const args = {
15
+ preset: "full",
16
+ theme: "valero",
17
+ dir: process.cwd(),
18
+ backendUrl: "http://localhost:9000",
19
+ baseUrl: "http://localhost:8000",
20
+ defaultRegion: "in",
21
+ publishableKey: "pk_test",
22
+ port: 8000,
23
+ install: true,
24
+ noPrompt: false,
25
+ }
26
+ const positional = []
27
+
28
+ for (let i = 2; i < argv.length; i++) {
29
+ const arg = argv[i]
30
+ if (arg === "--preset" && argv[i + 1]) args.preset = argv[++i]
31
+ else if (arg === "--theme" && argv[i + 1]) args.theme = argv[++i]
32
+ else if (arg === "--dir" && argv[i + 1]) args.dir = resolve(argv[++i])
33
+ else if (arg === "--backend-url" && argv[i + 1]) args.backendUrl = argv[++i]
34
+ else if (arg === "--publishable-key" && argv[i + 1]) args.publishableKey = argv[++i]
35
+ else if (arg === "--base-url" && argv[i + 1]) args.baseUrl = argv[++i]
36
+ else if (arg === "--default-region" && argv[i + 1]) args.defaultRegion = argv[++i]
37
+ else if (arg === "--port" && argv[i + 1]) args.port = Number(argv[++i])
38
+ else if (arg === "--no-install") args.install = false
39
+ else if (arg === "--no-prompt" || arg === "-y") args.noPrompt = true
40
+ else if (arg === "--help" || arg === "-h") args.help = true
41
+ else if (!arg.startsWith("-")) positional.push(arg)
42
+ }
43
+
44
+ args.name = positional[0]
45
+ return args
46
+ }
47
+
48
+ function printHelp() {
49
+ console.log(`
50
+ create-storefront-app — Scaffold a segment-based Medusa storefront
51
+
52
+ Usage:
53
+ npx @pradip1995/create-storefront-app <project-name> [options]
54
+
55
+ Options:
56
+ --preset <full|minimal> Page/segment preset (default: full)
57
+ --theme <valero|impulse> CSS theme token set (default: valero)
58
+ --dir <path> Parent directory (default: cwd)
59
+ --backend-url <url> Medusa backend URL (default: http://localhost:9000)
60
+ --publishable-key <key> Medusa publishable API key (default: pk_test)
61
+ --base-url <url> Storefront URL (default: http://localhost:8000)
62
+ --default-region <code> Default country code (default: in)
63
+ --port <number> Dev server port (default: 8000)
64
+ --no-install Skip npm install after scaffold
65
+ --no-prompt, -y Non-interactive (use flag defaults)
66
+
67
+ Examples:
68
+ npx @pradip1995/create-storefront-app my-shop
69
+ npx @pradip1995/create-storefront-app my-shop --preset minimal --theme impulse \\
70
+ --publishable-key pk_xxx --backend-url http://localhost:9000
71
+ `)
72
+ }
73
+
74
+ function writeJson(path, data) {
75
+ writeFileSync(path, JSON.stringify(data, null, 2) + "\n")
76
+ }
77
+
78
+ function applyTemplate(content, vars) {
79
+ return content.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? "")
80
+ }
81
+
82
+ function scaffoldProject(args) {
83
+ const projectRoot = join(args.dir, args.name)
84
+ if (existsSync(projectRoot)) {
85
+ console.error(`Error: ${projectRoot} already exists`)
86
+ process.exit(1)
87
+ }
88
+
89
+ const presetDir = join(PKG_ROOT, "templates", args.preset)
90
+ if (!existsSync(join(presetDir, "pages.config.json"))) {
91
+ console.error(`Unknown preset "${args.preset}". Use full or minimal.`)
92
+ process.exit(1)
93
+ }
94
+
95
+ const cssTheme = THEME_MAP[args.theme] || args.theme
96
+ mkdirSync(projectRoot, { recursive: true })
97
+ mkdirSync(join(projectRoot, "public"), { recursive: true })
98
+
99
+ const pagesConfig = JSON.parse(
100
+ readFileSync(join(presetDir, "pages.config.json"), "utf8")
101
+ )
102
+ writeJson(join(projectRoot, "pages.config.json"), pagesConfig)
103
+
104
+ const storefrontConfig = JSON.parse(
105
+ readFileSync(join(PKG_ROOT, "templates/shared/storefront.config.json"), "utf8")
106
+ )
107
+ storefrontConfig.theme = cssTheme
108
+ storefrontConfig.defaultCountryCode = args.defaultRegion
109
+ writeJson(join(projectRoot, "storefront.config.json"), storefrontConfig)
110
+
111
+ cpSync(join(PKG_ROOT, "templates/shared/public"), join(projectRoot, "public"), {
112
+ recursive: true,
113
+ })
114
+
115
+ writeFileSync(
116
+ join(projectRoot, ".gitignore"),
117
+ readFileSync(join(PKG_ROOT, "templates/shared/gitignore"), "utf8")
118
+ )
119
+ writeFileSync(
120
+ join(projectRoot, ".npmrc"),
121
+ readFileSync(join(PKG_ROOT, "templates/shared/npmrc"), "utf8")
122
+ )
123
+
124
+ const envTemplate = readFileSync(
125
+ join(PKG_ROOT, "templates/shared/env.example"),
126
+ "utf8"
127
+ )
128
+ writeFileSync(
129
+ join(projectRoot, ".env.local"),
130
+ applyTemplate(envTemplate, {
131
+ BACKEND_URL: args.backendUrl,
132
+ BASE_URL: args.baseUrl,
133
+ DEFAULT_REGION: args.defaultRegion,
134
+ PUBLISHABLE_KEY: args.publishableKey,
135
+ THEME: args.theme,
136
+ })
137
+ )
138
+ writeFileSync(
139
+ join(projectRoot, ".env.example"),
140
+ applyTemplate(envTemplate, {
141
+ BACKEND_URL: "http://localhost:9000",
142
+ BASE_URL: "http://localhost:8000",
143
+ DEFAULT_REGION: "in",
144
+ PUBLISHABLE_KEY: "pk_test",
145
+ THEME: "valero",
146
+ })
147
+ )
148
+
149
+ const pkg = {
150
+ name: args.name,
151
+ private: true,
152
+ scripts: {
153
+ "storefront:build": "storefront-build",
154
+ dev: `npm run storefront:build && cd .generated-app && npm install && npm run dev -- -p ${args.port}`,
155
+ build: "npm run storefront:build && cd .generated-app && npm install && npm run build",
156
+ start: "npm run storefront:build && cd .generated-app && npm install && npm run start",
157
+ },
158
+ dependencies: buildDependencies(pagesConfig),
159
+ }
160
+ writeJson(join(projectRoot, "package.json"), pkg)
161
+
162
+ return projectRoot
163
+ }
164
+
165
+ function main() {
166
+ const args = parseArgs(process.argv)
167
+ if (args.help || !args.name) {
168
+ printHelp()
169
+ process.exit(args.help ? 0 : 1)
170
+ }
171
+
172
+ console.log(
173
+ `Creating storefront "${args.name}" (preset: ${args.preset}, theme: ${args.theme})...`
174
+ )
175
+
176
+ const projectRoot = scaffoldProject(args)
177
+
178
+ if (args.install) {
179
+ console.log("\nInstalling dependencies...")
180
+ try {
181
+ execSync("npm install", { cwd: projectRoot, stdio: "inherit" })
182
+ } catch {
183
+ console.warn("\nWarning: npm install failed. Run `npm install` manually in the project directory.")
184
+ }
185
+ }
186
+
187
+ console.log(`
188
+ Done! Created ${projectRoot}
189
+
190
+ Next steps:
191
+ cd ${args.name}
192
+ npm run dev
193
+
194
+ Edit pages.config.json and storefront.config.json, then rebuild with npm run storefront:build.
195
+ `)
196
+ }
197
+
198
+ main()
package/lib/deps.js ADDED
@@ -0,0 +1,89 @@
1
+ import {
2
+ COMMERCE_CORE_VERSION,
3
+ FRAMEWORK_VERSION,
4
+ COMPONENTS_VERSION,
5
+ FRAMEWORK_PACKAGES,
6
+ RUNTIME_DEPS,
7
+ } from "./versions.js"
8
+
9
+ function packageBaseName(pkg) {
10
+ if (!pkg.includes("/")) return pkg
11
+ const parts = pkg.split("/")
12
+ if (parts[0].startsWith("@") && parts.length >= 2) {
13
+ return `${parts[0]}/${parts[1]}`
14
+ }
15
+ return pkg
16
+ }
17
+
18
+ /** Mirror framework-compiler collectPackages for dependency resolution. */
19
+ export function collectPackages(pages) {
20
+ const segments = new Set()
21
+ const layouts = new Set()
22
+ const workflows = new Set()
23
+
24
+ for (const page of pages) {
25
+ workflows.add(page.workflow)
26
+ layouts.add(packageBaseName(page.layout))
27
+ for (const s of page.segments) segments.add(s)
28
+ }
29
+
30
+ if (segments.has("@pradip1995/segment-product-grid")) {
31
+ segments.add("@pradip1995/segment-product-card")
32
+ }
33
+ if (
34
+ segments.has("@pradip1995/segment-new-arrivals") ||
35
+ segments.has("@pradip1995/segment-loved-by-moms") ||
36
+ segments.has("@pradip1995/segment-bestsellers-carousel") ||
37
+ segments.has("@pradip1995/segment-collections-showcase")
38
+ ) {
39
+ segments.add("@pradip1995/segment-product-card")
40
+ }
41
+
42
+ return {
43
+ segments: [...segments],
44
+ layouts: [...layouts],
45
+ workflows: [...workflows],
46
+ }
47
+ }
48
+
49
+ const CHROME_SEGMENTS = [
50
+ "@pradip1995/segment-nav",
51
+ "@pradip1995/segment-footer",
52
+ "@pradip1995/segment-promo-bar",
53
+ ]
54
+
55
+ const BASE_COMPONENT_PACKAGES = [
56
+ "@pradip1995/segment-tokens",
57
+ "@pradip1995/segment-primitives",
58
+ "@pradip1995/segment-loader",
59
+ "@pradip1995/segment-assets",
60
+ ]
61
+
62
+ /** Build package.json dependencies from pages.config.json content. */
63
+ export function buildDependencies(pages) {
64
+ const { segments, layouts, workflows } = collectPackages(pages)
65
+ const deps = {
66
+ ...RUNTIME_DEPS,
67
+ "@pradip1995/commerce-core": COMMERCE_CORE_VERSION,
68
+ }
69
+
70
+ for (const name of FRAMEWORK_PACKAGES) {
71
+ deps[name] = FRAMEWORK_VERSION
72
+ }
73
+
74
+ for (const name of [
75
+ ...BASE_COMPONENT_PACKAGES,
76
+ ...CHROME_SEGMENTS,
77
+ ...layouts,
78
+ ...workflows,
79
+ ...segments,
80
+ ]) {
81
+ deps[name] = COMPONENTS_VERSION
82
+ }
83
+
84
+ if (segments.includes("@pradip1995/segment-google-login")) {
85
+ deps["@pradip1995/commerce-auth"] = "^4.0.0"
86
+ }
87
+
88
+ return deps
89
+ }
@@ -0,0 +1,31 @@
1
+ /** Published package semver ranges — bump when releasing framework/components. */
2
+ export const COMMERCE_CORE_VERSION = "^4.0.0"
3
+ export const FRAMEWORK_VERSION = "^0.2.0"
4
+ export const COMPONENTS_VERSION = "^0.2.0"
5
+
6
+ export const FRAMEWORK_PACKAGES = [
7
+ "@pradip1995/plugin-sdk",
8
+ "@pradip1995/framework-core",
9
+ "@pradip1995/framework-runtime",
10
+ "@pradip1995/framework-compiler",
11
+ "@pradip1995/medusa-connector",
12
+ "@pradip1995/workflow-home",
13
+ "@pradip1995/workflow-layout",
14
+ "@pradip1995/workflow-store",
15
+ "@pradip1995/workflow-product",
16
+ "@pradip1995/workflow-cart",
17
+ "@pradip1995/workflow-checkout",
18
+ "@pradip1995/workflow-account",
19
+ "@pradip1995/workflow-order",
20
+ "@pradip1995/workflow-wishlist",
21
+ "@pradip1995/workflow-help",
22
+ ]
23
+
24
+ export const RUNTIME_DEPS = {
25
+ next: "15.3.8",
26
+ react: "19.0.3",
27
+ "react-dom": "19.0.3",
28
+ tailwindcss: "^3.4.17",
29
+ postcss: "^8.4.49",
30
+ autoprefixer: "^10.4.20",
31
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@pradip1995/create-storefront-app",
3
+ "version": "0.2.0",
4
+ "description": "Scaffold a segment-based Medusa storefront using @pradip1995/framework packages",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "publishConfig": {
8
+ "access": "public",
9
+ "registry": "https://registry.npmjs.org/"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/SmartByteLabs/storefront-framework.git",
14
+ "directory": "packages/create-storefront-app"
15
+ },
16
+ "bin": {
17
+ "create-storefront-app": "./bin/create-storefront-app.js"
18
+ },
19
+ "files": [
20
+ "bin",
21
+ "lib",
22
+ "templates"
23
+ ],
24
+ "engines": {
25
+ "node": ">=20"
26
+ },
27
+ "scripts": {
28
+ "typecheck": "node --check bin/create-storefront-app.js && node --check lib/deps.js && node --check lib/versions.js"
29
+ }
30
+ }
@@ -0,0 +1,102 @@
1
+ [
2
+ {
3
+ "route": "/",
4
+ "workflow": "@pradip1995/workflow-home",
5
+ "layout": "@pradip1995/layout-default",
6
+ "segments": [
7
+ "@pradip1995/segment-hero",
8
+ "@pradip1995/segment-shop-by-category",
9
+ "@pradip1995/segment-new-arrivals",
10
+ "@pradip1995/segment-promotional-banners",
11
+ "@pradip1995/segment-collections-showcase",
12
+ "@pradip1995/segment-why-choose-us",
13
+ "@pradip1995/segment-bestsellers-carousel",
14
+ "@pradip1995/segment-reviews-marquee",
15
+ "@pradip1995/segment-features"
16
+ ],
17
+ "metadata": {
18
+ "title": "Home",
19
+ "description": "Welcome to our store"
20
+ }
21
+ },
22
+ {
23
+ "route": "/store",
24
+ "workflow": "@pradip1995/workflow-store",
25
+ "layout": "@pradip1995/layout-default/store",
26
+ "segments": [
27
+ "@pradip1995/segment-mobile-filters",
28
+ "@pradip1995/segment-refinement-list",
29
+ "@pradip1995/segment-product-grid"
30
+ ]
31
+ },
32
+ {
33
+ "route": "/products/[handle]",
34
+ "workflow": "@pradip1995/workflow-product",
35
+ "layout": "@pradip1995/layout-product",
36
+ "segments": [
37
+ "@pradip1995/segment-product-gallery",
38
+ "@pradip1995/segment-product-info",
39
+ "@pradip1995/segment-product-actions",
40
+ "@pradip1995/segment-related-products"
41
+ ]
42
+ },
43
+ {
44
+ "route": "/cart",
45
+ "workflow": "@pradip1995/workflow-cart",
46
+ "layout": "@pradip1995/layout-default/cart",
47
+ "segments": [
48
+ "@pradip1995/segment-cart-item",
49
+ "@pradip1995/segment-cart-summary"
50
+ ]
51
+ },
52
+ {
53
+ "route": "/checkout",
54
+ "workflow": "@pradip1995/workflow-checkout",
55
+ "layout": "@pradip1995/layout-checkout",
56
+ "segments": [
57
+ "@pradip1995/segment-checkout-form",
58
+ "@pradip1995/segment-checkout-summary"
59
+ ]
60
+ },
61
+ {
62
+ "route": "/account/*",
63
+ "workflow": "@pradip1995/workflow-account",
64
+ "layout": "@pradip1995/layout-account",
65
+ "segments": [
66
+ "@pradip1995/segment-login-template",
67
+ "@pradip1995/segment-google-login"
68
+ ]
69
+ },
70
+ {
71
+ "route": "/wishlist",
72
+ "workflow": "@pradip1995/workflow-wishlist",
73
+ "layout": "@pradip1995/layout-default/wishlist",
74
+ "segments": ["@pradip1995/segment-wishlist"],
75
+ "metadata": {
76
+ "title": "Wishlist",
77
+ "description": "Your saved items"
78
+ }
79
+ },
80
+ {
81
+ "route": "/help",
82
+ "workflow": "@pradip1995/workflow-help",
83
+ "layout": "@pradip1995/layout-default",
84
+ "segments": ["@pradip1995/segment-help"],
85
+ "metadata": {
86
+ "title": "Help & FAQs",
87
+ "description": "Get answers to your questions and contact our support team"
88
+ }
89
+ },
90
+ {
91
+ "route": "/orders/[id]",
92
+ "workflow": "@pradip1995/workflow-order",
93
+ "layout": "@pradip1995/layout-default",
94
+ "segments": ["@pradip1995/segment-order-details"]
95
+ },
96
+ {
97
+ "route": "/order/[id]/confirmed",
98
+ "workflow": "@pradip1995/workflow-order",
99
+ "layout": "@pradip1995/layout-default",
100
+ "segments": ["@pradip1995/segment-order-details"]
101
+ }
102
+ ]
@@ -0,0 +1,65 @@
1
+ [
2
+ {
3
+ "route": "/",
4
+ "workflow": "@pradip1995/workflow-home",
5
+ "layout": "@pradip1995/layout-default",
6
+ "segments": [
7
+ "@pradip1995/segment-hero",
8
+ "@pradip1995/segment-shop-by-category",
9
+ "@pradip1995/segment-new-arrivals",
10
+ "@pradip1995/segment-features"
11
+ ],
12
+ "metadata": {
13
+ "title": "Home",
14
+ "description": "Welcome to our store"
15
+ }
16
+ },
17
+ {
18
+ "route": "/store",
19
+ "workflow": "@pradip1995/workflow-store",
20
+ "layout": "@pradip1995/layout-default/store",
21
+ "segments": [
22
+ "@pradip1995/segment-mobile-filters",
23
+ "@pradip1995/segment-refinement-list",
24
+ "@pradip1995/segment-product-grid"
25
+ ]
26
+ },
27
+ {
28
+ "route": "/products/[handle]",
29
+ "workflow": "@pradip1995/workflow-product",
30
+ "layout": "@pradip1995/layout-product",
31
+ "segments": [
32
+ "@pradip1995/segment-product-gallery",
33
+ "@pradip1995/segment-product-info",
34
+ "@pradip1995/segment-product-actions",
35
+ "@pradip1995/segment-related-products"
36
+ ]
37
+ },
38
+ {
39
+ "route": "/cart",
40
+ "workflow": "@pradip1995/workflow-cart",
41
+ "layout": "@pradip1995/layout-default/cart",
42
+ "segments": [
43
+ "@pradip1995/segment-cart-item",
44
+ "@pradip1995/segment-cart-summary"
45
+ ]
46
+ },
47
+ {
48
+ "route": "/checkout",
49
+ "workflow": "@pradip1995/workflow-checkout",
50
+ "layout": "@pradip1995/layout-checkout",
51
+ "segments": [
52
+ "@pradip1995/segment-checkout-form",
53
+ "@pradip1995/segment-checkout-summary"
54
+ ]
55
+ },
56
+ {
57
+ "route": "/account/*",
58
+ "workflow": "@pradip1995/workflow-account",
59
+ "layout": "@pradip1995/layout-account",
60
+ "segments": [
61
+ "@pradip1995/segment-login-template",
62
+ "@pradip1995/segment-google-login"
63
+ ]
64
+ }
65
+ ]
@@ -0,0 +1,14 @@
1
+ # Medusa backend
2
+ MEDUSA_BACKEND_URL={{BACKEND_URL}}
3
+ NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY={{PUBLISHABLE_KEY}}
4
+ NEXT_PUBLIC_MEDUSA_BACKEND_URL={{BACKEND_URL}}
5
+ NEXT_PUBLIC_BASE_URL={{BASE_URL}}
6
+ NEXT_PUBLIC_DEFAULT_REGION={{DEFAULT_REGION}}
7
+ NEXT_PUBLIC_STOREFRONT_THEME={{THEME}}
8
+
9
+ # Payments (Razorpay test — replace for production)
10
+ # NEXT_PUBLIC_RAZORPAY_KEY=
11
+
12
+ # Google OAuth — client ID must match backend GOOGLE_CLIENT_ID
13
+ # NEXT_PUBLIC_GOOGLE_CLIENT_ID=
14
+ # NEXT_PUBLIC_GOOGLE_OAUTH_CALLBACK_URL={{BASE_URL}}/auth/customer/google/callback
@@ -0,0 +1,8 @@
1
+ node_modules
2
+ .generated-app
3
+ .next
4
+ .turbo
5
+ *.tsbuildinfo
6
+ .env
7
+ .env.local
8
+ .DS_Store
@@ -0,0 +1 @@
1
+ legacy-peer-deps=true
@@ -0,0 +1,6 @@
1
+ # Store assets
2
+
3
+ Place brand logos and static files here. Files in `public/` are copied into the generated Next.js app at build time.
4
+
5
+ - `Logo.png` — used in nav/footer when configured
6
+ - Override segment assets by adding files with the same path as in `@pradip1995/segment-assets`
@@ -0,0 +1,8 @@
1
+ {
2
+ "theme": "valero",
3
+ "defaultCountryCode": "in",
4
+ "loader": {
5
+ "variant": "spinner",
6
+ "enabled": true
7
+ }
8
+ }