@pradip1995/create-storefront-app 0.2.3 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/bin/create-storefront-app.js +147 -59
  2. package/lib/build-backend-package.js +54 -0
  3. package/lib/build-homepage-defaults.cjs +50 -0
  4. package/lib/deps.js +3 -1
  5. package/lib/docker-template-vars.js +32 -0
  6. package/lib/medusa-plugin-versions.json +55 -0
  7. package/lib/scaffold-backend.js +107 -0
  8. package/lib/scaffold-docker.js +82 -0
  9. package/lib/versions.js +4 -3
  10. package/package.json +3 -2
  11. package/templates/backend/.cursor/commands/seed-backend.md +21 -0
  12. package/templates/backend/.cursor/rules/medusa-backend.mdc +54 -0
  13. package/templates/backend/config/README.md +23 -0
  14. package/templates/backend/config/homepage-config.json +568 -0
  15. package/templates/backend/env.template +53 -0
  16. package/templates/backend/gitignore +8 -0
  17. package/templates/backend/medusa-config.full.ts +251 -0
  18. package/templates/backend/medusa-config.minimal.ts +141 -0
  19. package/templates/backend/scripts/build-dynamic-config-defaults.mjs +25 -0
  20. package/templates/backend/scripts/build-homepage-defaults.cjs +50 -0
  21. package/templates/backend/src/config/product-metadata-descriptors.ts +27 -0
  22. package/templates/backend/src/scripts/seed.ts +210 -0
  23. package/templates/backend/tsconfig.json +24 -0
  24. package/templates/docker/Makefile +88 -0
  25. package/templates/docker/backend/.dockerignore +8 -0
  26. package/templates/docker/backend/Dockerfile +30 -0
  27. package/templates/docker/data/README.md +12 -0
  28. package/templates/docker/data/dump.sql +3 -0
  29. package/templates/docker/docker/.env.example +36 -0
  30. package/templates/docker/docker/frontend.build.defaults +7 -0
  31. package/templates/docker/docker/gitignore +1 -0
  32. package/templates/docker/docker/postgres/01-create-postgres-role.sql +9 -0
  33. package/templates/docker/docker/postgres/docker-entrypoint-wrapper.sh +49 -0
  34. package/templates/docker/docker-compose.infra.yml +64 -0
  35. package/templates/docker/docker-compose.yml +138 -0
  36. package/templates/docker/storefront/.dockerignore +9 -0
  37. package/templates/docker/storefront/Dockerfile +53 -0
  38. package/templates/root/.cursor/commands/docker-dev.md +27 -0
  39. package/templates/root/.cursor/rules/monorepo-layout.mdc +55 -0
  40. package/templates/root/scripts/sync-publishable-key.mjs +108 -0
  41. package/templates/shared/.cursor/commands/change-theme-colors.md +1 -1
  42. package/templates/shared/.cursor/commands/fix-segment-issue.md +2 -2
  43. package/templates/shared/.cursor/commands/rebuild-storefront.md +1 -1
  44. package/templates/shared/.cursor/rules/customize-sections.mdc +7 -1
  45. package/templates/shared/.cursor/rules/customize-theme.mdc +1 -1
  46. package/templates/shared/.cursor/rules/storefront-architecture.mdc +3 -2
  47. package/templates/shared/.cursor/skills/fix-segment-issues/SKILL.md +1 -1
  48. package/templates/shared/README.md +100 -0
  49. package/templates/shared/root-gitignore +6 -0
  50. package/templates/shared/storefront-only-README.md +25 -0
@@ -4,6 +4,8 @@ import { join, dirname, resolve } from "path"
4
4
  import { fileURLToPath } from "url"
5
5
  import { execSync } from "child_process"
6
6
  import { buildDependencies } from "../lib/deps.js"
7
+ import { scaffoldBackend } from "../lib/scaffold-backend.js"
8
+ import { scaffoldDocker } from "../lib/scaffold-docker.js"
7
9
 
8
10
  const __dirname = dirname(fileURLToPath(import.meta.url))
9
11
  const PKG_ROOT = join(__dirname, "..")
@@ -19,6 +21,7 @@ function parseArgs(argv) {
19
21
  port: 8000,
20
22
  install: true,
21
23
  noPrompt: false,
24
+ withBackend: true,
22
25
  }
23
26
  const positional = []
24
27
 
@@ -32,6 +35,7 @@ function parseArgs(argv) {
32
35
  else if (arg === "--default-region" && argv[i + 1]) args.defaultRegion = argv[++i]
33
36
  else if (arg === "--port" && argv[i + 1]) args.port = Number(argv[++i])
34
37
  else if (arg === "--no-install") args.install = false
38
+ else if (arg === "--no-backend") args.withBackend = false
35
39
  else if (arg === "--no-prompt" || arg === "-y") args.noPrompt = true
36
40
  else if (arg === "--help" || arg === "-h") args.help = true
37
41
  else if (!arg.startsWith("-")) positional.push(arg)
@@ -43,26 +47,34 @@ function parseArgs(argv) {
43
47
 
44
48
  function printHelp() {
45
49
  console.log(`
46
- create-storefront-app — Scaffold a segment-based Medusa storefront
50
+ create-storefront-app — Scaffold Medusa backend + segment-based storefront
47
51
 
48
52
  Usage:
49
53
  npx @pradip1995/create-storefront-app <project-name> [options]
50
54
 
55
+ Creates:
56
+ <project-name>/
57
+ backend/ Medusa v2 (medusa dev, medusa build, seed)
58
+ storefront/ Segment storefront (storefront-build + Next.js)
59
+ docker-compose.yml, Makefile, docker/ (when backend is included)
60
+
61
+ Requires: Node 20+, npm. Docker optional (see README after scaffold).
62
+
51
63
  Options:
52
- --preset <full|minimal> Page/segment preset (default: full)
64
+ --preset <full|minimal> Page/segment + plugin preset (default: full)
65
+ --no-backend Storefront only (no backend/ folder)
53
66
  --dir <path> Parent directory (default: cwd)
54
67
  --backend-url <url> Medusa backend URL (default: http://localhost:9000)
55
- --publishable-key <key> Medusa publishable API key (default: pk_test)
68
+ --publishable-key <key> Storefront publishable key placeholder (default: pk_test)
56
69
  --base-url <url> Storefront URL (default: http://localhost:8000)
57
70
  --default-region <code> Default country code (default: in)
58
- --port <number> Dev server port (default: 8000)
71
+ --port <number> Storefront dev port (default: 8000)
59
72
  --no-install Skip npm install after scaffold
60
- --no-prompt, -y Non-interactive (use flag defaults)
73
+ --no-prompt, -y Non-interactive
61
74
 
62
75
  Examples:
63
76
  npx @pradip1995/create-storefront-app my-shop
64
- npx @pradip1995/create-storefront-app my-shop --preset minimal \\
65
- --publishable-key pk_xxx --backend-url http://localhost:9000
77
+ npx @pradip1995/create-storefront-app my-shop --preset minimal --no-install
66
78
  `)
67
79
  }
68
80
 
@@ -74,74 +86,54 @@ function applyTemplate(content, vars) {
74
86
  return content.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? "")
75
87
  }
76
88
 
77
- function scaffoldProject(args) {
78
- const projectRoot = join(args.dir, args.name)
79
- if (existsSync(projectRoot)) {
80
- console.error(`Error: ${projectRoot} already exists`)
81
- process.exit(1)
82
- }
83
-
89
+ function scaffoldStorefront(storefrontRoot, args, { flat = false } = {}) {
84
90
  const presetDir = join(PKG_ROOT, "templates", args.preset)
85
- if (!existsSync(join(presetDir, "pages.config.json"))) {
86
- console.error(`Unknown preset "${args.preset}". Use full or minimal.`)
87
- process.exit(1)
88
- }
89
91
 
90
- mkdirSync(projectRoot, { recursive: true })
91
- mkdirSync(join(projectRoot, "public"), { recursive: true })
92
+ mkdirSync(storefrontRoot, { recursive: true })
93
+ mkdirSync(join(storefrontRoot, "public"), { recursive: true })
92
94
 
93
- const pagesConfig = JSON.parse(
94
- readFileSync(join(presetDir, "pages.config.json"), "utf8")
95
- )
96
- writeJson(join(projectRoot, "pages.config.json"), pagesConfig)
95
+ const pagesConfig = JSON.parse(readFileSync(join(presetDir, "pages.config.json"), "utf8"))
96
+ writeJson(join(storefrontRoot, "pages.config.json"), pagesConfig)
97
97
 
98
98
  const storefrontConfig = JSON.parse(
99
99
  readFileSync(join(PKG_ROOT, "templates/shared/storefront.config.json"), "utf8")
100
100
  )
101
101
  storefrontConfig.defaultCountryCode = args.defaultRegion
102
- writeJson(join(projectRoot, "storefront.config.json"), storefrontConfig)
102
+ writeJson(join(storefrontRoot, "storefront.config.json"), storefrontConfig)
103
103
 
104
- cpSync(join(PKG_ROOT, "templates/shared/public"), join(projectRoot, "public"), {
104
+ cpSync(join(PKG_ROOT, "templates/shared/public"), join(storefrontRoot, "public"), {
105
105
  recursive: true,
106
106
  })
107
107
 
108
108
  const cursorTemplate = join(PKG_ROOT, "templates/shared/.cursor")
109
109
  if (existsSync(cursorTemplate)) {
110
- cpSync(cursorTemplate, join(projectRoot, ".cursor"), { recursive: true })
110
+ cpSync(cursorTemplate, join(storefrontRoot, ".cursor"), { recursive: true })
111
111
  }
112
112
 
113
113
  const themeOverridesTemplate = join(PKG_ROOT, "templates/shared/theme-overrides.css")
114
114
  if (existsSync(themeOverridesTemplate)) {
115
- writeFileSync(
116
- join(projectRoot, "theme-overrides.css"),
117
- readFileSync(themeOverridesTemplate, "utf8")
118
- )
115
+ writeFileSync(join(storefrontRoot, "theme-overrides.css"), readFileSync(themeOverridesTemplate, "utf8"))
119
116
  }
120
117
 
121
118
  writeFileSync(
122
- join(projectRoot, ".gitignore"),
119
+ join(storefrontRoot, ".gitignore"),
123
120
  readFileSync(join(PKG_ROOT, "templates/shared/gitignore"), "utf8")
124
121
  )
125
122
  writeFileSync(
126
- join(projectRoot, ".npmrc"),
123
+ join(storefrontRoot, ".npmrc"),
127
124
  readFileSync(join(PKG_ROOT, "templates/shared/npmrc"), "utf8")
128
125
  )
129
126
 
130
- const envTemplate = readFileSync(
131
- join(PKG_ROOT, "templates/shared/env.example"),
132
- "utf8"
133
- )
134
- writeFileSync(
135
- join(projectRoot, ".env.local"),
136
- applyTemplate(envTemplate, {
137
- BACKEND_URL: args.backendUrl,
138
- BASE_URL: args.baseUrl,
139
- DEFAULT_REGION: args.defaultRegion,
140
- PUBLISHABLE_KEY: args.publishableKey,
141
- })
142
- )
127
+ const envTemplate = readFileSync(join(PKG_ROOT, "templates/shared/env.example"), "utf8")
128
+ const envVars = {
129
+ BACKEND_URL: args.backendUrl,
130
+ BASE_URL: args.baseUrl,
131
+ DEFAULT_REGION: args.defaultRegion,
132
+ PUBLISHABLE_KEY: args.publishableKey,
133
+ }
134
+ writeFileSync(join(storefrontRoot, ".env.local"), applyTemplate(envTemplate, envVars))
143
135
  writeFileSync(
144
- join(projectRoot, ".env.example"),
136
+ join(storefrontRoot, ".env.example"),
145
137
  applyTemplate(envTemplate, {
146
138
  BACKEND_URL: "http://localhost:9000",
147
139
  BASE_URL: "http://localhost:8000",
@@ -151,7 +143,7 @@ function scaffoldProject(args) {
151
143
  )
152
144
 
153
145
  const pkg = {
154
- name: args.name,
146
+ name: flat ? args.name : `${args.name}-storefront`,
155
147
  private: true,
156
148
  scripts: {
157
149
  "storefront:build": "storefront-build",
@@ -161,7 +153,81 @@ function scaffoldProject(args) {
161
153
  },
162
154
  dependencies: buildDependencies(pagesConfig),
163
155
  }
164
- writeJson(join(projectRoot, "package.json"), pkg)
156
+ writeJson(join(storefrontRoot, "package.json"), pkg)
157
+ }
158
+
159
+ function scaffoldProject(args) {
160
+ const projectRoot = join(args.dir, args.name)
161
+ if (existsSync(projectRoot)) {
162
+ console.error(`Error: ${projectRoot} already exists`)
163
+ process.exit(1)
164
+ }
165
+
166
+ const presetDir = join(PKG_ROOT, "templates", args.preset)
167
+ if (!existsSync(join(presetDir, "pages.config.json"))) {
168
+ console.error(`Unknown preset "${args.preset}". Use full or minimal.`)
169
+ process.exit(1)
170
+ }
171
+
172
+ mkdirSync(projectRoot, { recursive: true })
173
+
174
+ if (args.withBackend) {
175
+ writeFileSync(
176
+ join(projectRoot, ".gitignore"),
177
+ readFileSync(join(PKG_ROOT, "templates/shared/root-gitignore"), "utf8")
178
+ )
179
+ writeFileSync(
180
+ join(projectRoot, "README.md"),
181
+ applyTemplate(readFileSync(join(PKG_ROOT, "templates/shared/README.md"), "utf8"), {
182
+ PROJECT_NAME: args.name,
183
+ BASE_URL: args.baseUrl,
184
+ BACKEND_URL: args.backendUrl,
185
+ DEFAULT_REGION: args.defaultRegion,
186
+ })
187
+ )
188
+
189
+ const rootPkg = {
190
+ name: args.name,
191
+ private: true,
192
+ scripts: {
193
+ "dev:storefront": "npm run dev --prefix storefront",
194
+ "build:storefront": "npm run build --prefix storefront",
195
+ "storefront:build": "npm run storefront:build --prefix storefront",
196
+ "dev:backend": "npm run dev --prefix backend",
197
+ "build:backend": "npm run build --prefix backend",
198
+ seed: "npm run seed --prefix backend && node scripts/sync-publishable-key.mjs --from-db",
199
+ },
200
+ }
201
+ writeJson(join(projectRoot, "package.json"), rootPkg)
202
+
203
+ const rootCursor = join(PKG_ROOT, "templates/root/.cursor")
204
+ if (existsSync(rootCursor)) {
205
+ cpSync(rootCursor, join(projectRoot, ".cursor"), { recursive: true })
206
+ }
207
+
208
+ scaffoldStorefront(join(projectRoot, "storefront"), args)
209
+ scaffoldBackend(join(projectRoot, "backend"), args)
210
+ scaffoldDocker(projectRoot, args)
211
+
212
+ mkdirSync(join(projectRoot, "scripts"), { recursive: true })
213
+ cpSync(
214
+ join(PKG_ROOT, "templates/root/scripts/sync-publishable-key.mjs"),
215
+ join(projectRoot, "scripts/sync-publishable-key.mjs")
216
+ )
217
+ } else {
218
+ scaffoldStorefront(projectRoot, args, { flat: true })
219
+ writeFileSync(
220
+ join(projectRoot, "README.md"),
221
+ applyTemplate(
222
+ readFileSync(join(PKG_ROOT, "templates/shared/storefront-only-README.md"), "utf8"),
223
+ {
224
+ PROJECT_NAME: args.name,
225
+ BASE_URL: args.baseUrl,
226
+ DEFAULT_REGION: args.defaultRegion,
227
+ }
228
+ )
229
+ )
230
+ }
165
231
 
166
232
  return projectRoot
167
233
  }
@@ -174,30 +240,52 @@ function main() {
174
240
  }
175
241
 
176
242
  console.log(
177
- `Creating storefront "${args.name}" (preset: ${args.preset})...`
243
+ `Creating "${args.name}" (preset: ${args.preset}${args.withBackend ? ", with Medusa backend" : ""})...`
178
244
  )
179
245
 
180
246
  const projectRoot = scaffoldProject(args)
181
247
 
182
248
  if (args.install) {
183
249
  console.log("\nInstalling dependencies...")
184
- try {
185
- execSync("npm install", { cwd: projectRoot, stdio: "inherit" })
186
- } catch {
187
- console.warn("\nWarning: npm install failed. Run `npm install` manually in the project directory.")
250
+ const dirs = args.withBackend
251
+ ? [join(projectRoot, "backend"), join(projectRoot, "storefront")]
252
+ : [projectRoot]
253
+ for (const dir of dirs) {
254
+ try {
255
+ execSync("npm install", { cwd: dir, stdio: "inherit" })
256
+ } catch {
257
+ console.warn(`Warning: npm install failed in ${dir}`)
258
+ }
188
259
  }
189
260
  }
190
261
 
191
- console.log(`
192
- Done! Created ${projectRoot}
262
+ const nextSteps = args.withBackend
263
+ ? `
264
+ Next steps:
265
+ cd ${args.name}
266
+
267
+ Docker (full stack):
268
+ make up && make migrate && make seed && make admin-create
193
269
 
270
+ Local Node (Docker infra):
271
+ make up-infra
272
+ npm run dev:backend # terminal 1
273
+ npm run seed && npm run dev:storefront # terminal 2
274
+
275
+ Fully local:
276
+ cd backend && npm run dev # set DATABASE_URL in backend/.env
277
+ cd backend && npm run seed
278
+ cd ../storefront && npm run dev
279
+
280
+ See README.md, Makefile, and .cursor/ guides.
281
+ `
282
+ : `
194
283
  Next steps:
195
284
  cd ${args.name}
196
285
  npm run dev
286
+ `
197
287
 
198
- Edit pages.config.json, theme-overrides.css, or .cursor/ guides.
199
- See .cursor/commands/ for theme, sections, and troubleshooting workflows.
200
- `)
288
+ console.log(`Done! Created ${projectRoot}${nextSteps}`)
201
289
  }
202
290
 
203
291
  main()
@@ -0,0 +1,54 @@
1
+ import { readFileSync } from "fs"
2
+ import { dirname, join } from "path"
3
+ import { fileURLToPath } from "url"
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url))
6
+ const VERSIONS = JSON.parse(
7
+ readFileSync(join(__dirname, "medusa-plugin-versions.json"), "utf8")
8
+ )
9
+
10
+ /** Build backend/package.json dependencies from medusa-plugin-versions.json */
11
+ export function buildBackendDependencies(preset = "full") {
12
+ const pluginKeys =
13
+ preset === "minimal" ? VERSIONS.minimalPlugins : VERSIONS.fullPlugins
14
+ const plugins = {}
15
+ for (const key of pluginKeys) {
16
+ if (VERSIONS.plugins[key]) plugins[key] = VERSIONS.plugins[key]
17
+ }
18
+ return {
19
+ medusa: { ...VERSIONS.medusa },
20
+ plugins,
21
+ }
22
+ }
23
+
24
+ export function buildBackendPackageJson(preset = "full", projectName = "backend") {
25
+ const { medusa, plugins } = buildBackendDependencies(preset)
26
+ return {
27
+ name: `${projectName}-backend`,
28
+ version: "0.0.1",
29
+ private: true,
30
+ description: "Medusa backend for segment-based storefront",
31
+ license: "MIT",
32
+ scripts: {
33
+ dev: "medusa develop",
34
+ build:
35
+ "TS_NODE_COMPILER=typescript TS_NODE_TRANSPILE_ONLY=true MEDUSA_ADMIN_DISABLE_SW=true medusa build",
36
+ start: "medusa start",
37
+ seed: "medusa exec ./src/scripts/seed.ts",
38
+ "dynamic-config:defaults": "node scripts/build-dynamic-config-defaults.mjs",
39
+ },
40
+ dependencies: {
41
+ ...medusa,
42
+ ...plugins,
43
+ },
44
+ devDependencies: {
45
+ "@types/node": "^20.12.11",
46
+ typescript: "^5.6.2",
47
+ },
48
+ engines: {
49
+ node: ">=20",
50
+ },
51
+ }
52
+ }
53
+
54
+ export { VERSIONS }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Build initial homepage-config values from schema structure defaultValue fields.
3
+ * Used by backend scaffold and `npm run dynamic-config:defaults`.
4
+ */
5
+
6
+ function buildDefaultsFromStructure(fields) {
7
+ const result = {}
8
+ for (const field of fields) {
9
+ const value = buildDefaultForField(field)
10
+ if (value !== undefined) {
11
+ result[field.id] = value
12
+ }
13
+ }
14
+ return result
15
+ }
16
+
17
+ function buildDefaultForField(field) {
18
+ if (field.defaultValue !== undefined) {
19
+ return field.defaultValue
20
+ }
21
+
22
+ if (field.type === "object" && field.children?.length) {
23
+ const nested = buildDefaultsFromStructure(field.children)
24
+ return Object.keys(nested).length > 0 ? nested : undefined
25
+ }
26
+
27
+ if (field.type === "array" && field.children?.length) {
28
+ const [itemSchema] = field.children
29
+ if (itemSchema?.type === "object" && itemSchema.children?.length) {
30
+ const itemDefaults = buildDefaultsFromStructure(itemSchema.children)
31
+ if (Object.keys(itemDefaults).length > 0) {
32
+ return [{ [itemSchema.id]: itemDefaults }]
33
+ }
34
+ }
35
+ return undefined
36
+ }
37
+
38
+ return undefined
39
+ }
40
+
41
+ function buildHomepageConfigDefaults(configSchema) {
42
+ if (!configSchema?.structure?.length) return {}
43
+ return buildDefaultsFromStructure(configSchema.structure)
44
+ }
45
+
46
+ module.exports = {
47
+ buildDefaultsFromStructure,
48
+ buildDefaultForField,
49
+ buildHomepageConfigDefaults,
50
+ }
package/lib/deps.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  COMMERCE_CORE_VERSION,
3
3
  FRAMEWORK_VERSION,
4
+ FRAMEWORK_COMPILER_VERSION,
4
5
  COMPONENTS_VERSION,
5
6
  FRAMEWORK_PACKAGES,
6
7
  RUNTIME_DEPS,
@@ -68,7 +69,8 @@ export function buildDependencies(pages) {
68
69
  }
69
70
 
70
71
  for (const name of FRAMEWORK_PACKAGES) {
71
- deps[name] = FRAMEWORK_VERSION
72
+ deps[name] =
73
+ name === "@pradip1995/framework-compiler" ? FRAMEWORK_COMPILER_VERSION : FRAMEWORK_VERSION
72
74
  }
73
75
 
74
76
  for (const name of [
@@ -0,0 +1,32 @@
1
+ /** Derive Docker / Postgres identifiers from project name */
2
+ export function sanitizeProjectSlug(name) {
3
+ const slug = String(name)
4
+ .toLowerCase()
5
+ .replace(/[^a-z0-9]+/g, "")
6
+ .slice(0, 24)
7
+ return slug || "shop"
8
+ }
9
+
10
+ /** Shared template vars for backend env, docker-compose, Makefile */
11
+ export function buildDockerTemplateVars(args) {
12
+ const slug = sanitizeProjectSlug(args.name)
13
+ const postgresUser = slug
14
+ const postgresPassword = `${slug}_secret`
15
+ const postgresDb = `${slug}_medusa`
16
+
17
+ return {
18
+ PROJECT_NAME: args.name,
19
+ CONTAINER_PREFIX: slug,
20
+ POSTGRES_USER: postgresUser,
21
+ POSTGRES_PASSWORD: postgresPassword,
22
+ POSTGRES_DB: postgresDb,
23
+ BACKEND_URL: args.backendUrl,
24
+ BASE_URL: args.baseUrl,
25
+ DEFAULT_REGION: args.defaultRegion,
26
+ DATABASE_URL: `postgres://${postgresUser}:${postgresPassword}@localhost:5432/${postgresDb}?sslmode=disable`,
27
+ DATABASE_URL_DOCKER: `postgres://${postgresUser}:${postgresPassword}@postgres:5432/${postgresDb}?sslmode=disable`,
28
+ PUBLISHABLE_KEY: args.publishableKey || "pk_test",
29
+ ADMIN_EMAIL: `admin@${slug}.local`,
30
+ ADMIN_PASSWORD: `${slug.charAt(0).toUpperCase()}${slug.slice(1)}@123`,
31
+ }
32
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "medusa": {
3
+ "@medusajs/admin-sdk": "2.12.3",
4
+ "@medusajs/auth-google": "2.12.3",
5
+ "@medusajs/cli": "2.12.3",
6
+ "@medusajs/framework": "2.12.3",
7
+ "@medusajs/medusa": "2.12.3",
8
+ "@medusajs/test-utils": "2.12.3"
9
+ },
10
+ "plugins": {
11
+ "medusa-product-helper": "0.0.73",
12
+ "medusa-dynamic-metadata": "0.0.7",
13
+ "medusa-plugin-dynamic-config": "^0.0.25",
14
+ "medusa-review-rating": "^0.0.38",
15
+ "medusa-contact-us": "^0.0.26",
16
+ "customer-registration": "^0.0.124",
17
+ "stock-monitoring": "^0.0.5",
18
+ "medusa-invoice-sbl": "^0.0.11",
19
+ "order-management": "^0.0.78",
20
+ "medusa-shiprocket-fulfillment-sbl": "0.0.28",
21
+ "medusa-notification-token-management": "^0.0.1",
22
+ "medusa-customer-file-upload": "0.0.1",
23
+ "medusa-analytics": "0.0.24",
24
+ "medusa-export": "^0.0.5",
25
+ "medusa-payment-provider": "0.0.1",
26
+ "@tsc_tech/medusa-plugin-smtp": "^0.0.3",
27
+ "medusa-twilio-sms": "^2.0.2",
28
+ "medusa-notification-provider": "^0.0.2"
29
+ },
30
+ "minimalPlugins": [
31
+ "medusa-product-helper",
32
+ "medusa-dynamic-metadata",
33
+ "medusa-plugin-dynamic-config",
34
+ "medusa-review-rating",
35
+ "medusa-contact-us",
36
+ "customer-registration"
37
+ ],
38
+ "fullPlugins": [
39
+ "medusa-product-helper",
40
+ "stock-monitoring",
41
+ "medusa-invoice-sbl",
42
+ "medusa-dynamic-metadata",
43
+ "medusa-review-rating",
44
+ "medusa-plugin-dynamic-config",
45
+ "medusa-contact-us",
46
+ "order-management",
47
+ "customer-registration",
48
+ "medusa-shiprocket-fulfillment-sbl",
49
+ "medusa-notification-token-management",
50
+ "medusa-customer-file-upload",
51
+ "medusa-analytics",
52
+ "medusa-export",
53
+ "medusa-payment-provider"
54
+ ]
55
+ }
@@ -0,0 +1,107 @@
1
+ import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"
2
+ import { join, dirname } from "path"
3
+ import { fileURLToPath } from "url"
4
+ import { buildBackendPackageJson, VERSIONS } from "./build-backend-package.js"
5
+ import { buildDockerTemplateVars } from "./docker-template-vars.js"
6
+ import { createRequire } from "module"
7
+
8
+ const require = createRequire(import.meta.url)
9
+ const { buildHomepageConfigDefaults } = require("./build-homepage-defaults.cjs")
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url))
12
+ const PKG_ROOT = join(__dirname, "..")
13
+ const BACKEND_TEMPLATE = join(PKG_ROOT, "templates", "backend")
14
+
15
+ const REGION_CURRENCY = {
16
+ in: { currency: "inr", name: "India" },
17
+ us: { currency: "usd", name: "United States" },
18
+ gb: { currency: "gbp", name: "United Kingdom" },
19
+ eu: { currency: "eur", name: "Europe" },
20
+ }
21
+
22
+ function applyTemplate(content, vars) {
23
+ return content.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? "")
24
+ }
25
+
26
+ function regionMeta(defaultRegion) {
27
+ return (
28
+ REGION_CURRENCY[defaultRegion] || {
29
+ currency: defaultRegion.length === 2 ? defaultRegion : "usd",
30
+ name: defaultRegion.toUpperCase(),
31
+ }
32
+ )
33
+ }
34
+
35
+ export function scaffoldBackend(backendRoot, args) {
36
+ const { preset, name, backendUrl, baseUrl, defaultRegion } = args
37
+ const meta = regionMeta(defaultRegion)
38
+ const dockerVars = buildDockerTemplateVars(args)
39
+ const templateVars = {
40
+ PROJECT_NAME: name,
41
+ BACKEND_URL: backendUrl,
42
+ BASE_URL: baseUrl,
43
+ DEFAULT_REGION: defaultRegion,
44
+ DEFAULT_CURRENCY: meta.currency,
45
+ DEFAULT_REGION_NAME: meta.name,
46
+ POSTGRES_USER: dockerVars.POSTGRES_USER,
47
+ POSTGRES_PASSWORD: dockerVars.POSTGRES_PASSWORD,
48
+ POSTGRES_DB: dockerVars.POSTGRES_DB,
49
+ DATABASE_URL: dockerVars.DATABASE_URL,
50
+ }
51
+
52
+ mkdirSync(backendRoot, { recursive: true })
53
+
54
+ const medusaConfigSrc =
55
+ preset === "minimal"
56
+ ? join(BACKEND_TEMPLATE, "medusa-config.minimal.ts")
57
+ : join(BACKEND_TEMPLATE, "medusa-config.full.ts")
58
+ writeFileSync(join(backendRoot, "medusa-config.ts"), readFileSync(medusaConfigSrc, "utf8"))
59
+
60
+ cpSync(join(BACKEND_TEMPLATE, "tsconfig.json"), join(backendRoot, "tsconfig.json"))
61
+ cpSync(join(BACKEND_TEMPLATE, "gitignore"), join(backendRoot, ".gitignore"))
62
+ cpSync(join(PKG_ROOT, "templates/shared/npmrc"), join(backendRoot, ".npmrc"))
63
+ cpSync(join(BACKEND_TEMPLATE, "config"), join(backendRoot, "config"), { recursive: true })
64
+ cpSync(join(BACKEND_TEMPLATE, "src"), join(backendRoot, "src"), { recursive: true })
65
+ cpSync(join(BACKEND_TEMPLATE, "scripts"), join(backendRoot, "scripts"), { recursive: true })
66
+
67
+ const homepageSchema = JSON.parse(
68
+ readFileSync(join(backendRoot, "config/homepage-config.json"), "utf8")
69
+ )
70
+ writeFileSync(
71
+ join(backendRoot, "config/homepage-config.defaults.json"),
72
+ `${JSON.stringify(buildHomepageConfigDefaults(homepageSchema), null, 2)}\n`
73
+ )
74
+
75
+ writeFileSync(
76
+ join(backendRoot, "config", "medusa-plugin-versions.json"),
77
+ JSON.stringify(VERSIONS, null, 2) + "\n"
78
+ )
79
+
80
+ const seedContent = applyTemplate(
81
+ readFileSync(join(BACKEND_TEMPLATE, "src/scripts/seed.ts"), "utf8"),
82
+ templateVars
83
+ )
84
+ mkdirSync(join(backendRoot, "src/scripts"), { recursive: true })
85
+ writeFileSync(join(backendRoot, "src/scripts/seed.ts"), seedContent)
86
+
87
+ writeFileSync(
88
+ join(backendRoot, ".env.template"),
89
+ applyTemplate(readFileSync(join(BACKEND_TEMPLATE, "env.template"), "utf8"), templateVars)
90
+ )
91
+ writeFileSync(
92
+ join(backendRoot, ".env"),
93
+ applyTemplate(readFileSync(join(BACKEND_TEMPLATE, "env.template"), "utf8"), templateVars)
94
+ )
95
+
96
+ writeFileSync(
97
+ join(backendRoot, "package.json"),
98
+ JSON.stringify(buildBackendPackageJson(preset, name), null, 2) + "\n"
99
+ )
100
+
101
+ const backendCursor = join(BACKEND_TEMPLATE, ".cursor")
102
+ if (existsSync(backendCursor)) {
103
+ cpSync(backendCursor, join(backendRoot, ".cursor"), { recursive: true })
104
+ }
105
+
106
+ return backendRoot
107
+ }