@pradip1995/create-storefront-app 0.2.2 → 0.3.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.
Files changed (53) hide show
  1. package/bin/create-storefront-app.js +149 -53
  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 +16 -0
  12. package/templates/backend/.cursor/rules/medusa-backend.mdc +55 -0
  13. package/templates/backend/config/README.md +21 -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 +212 -0
  23. package/templates/backend/tsconfig.json +24 -0
  24. package/templates/docker/Makefile +86 -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 +32 -0
  39. package/templates/root/.cursor/rules/monorepo-layout.mdc +55 -0
  40. package/templates/shared/.cursor/commands/change-theme-colors.md +28 -0
  41. package/templates/shared/.cursor/commands/fix-segment-issue.md +56 -0
  42. package/templates/shared/.cursor/commands/rebuild-storefront.md +19 -0
  43. package/templates/shared/.cursor/commands/update-home-sections.md +50 -0
  44. package/templates/shared/.cursor/rules/customize-sections.mdc +49 -0
  45. package/templates/shared/.cursor/rules/customize-theme.mdc +57 -0
  46. package/templates/shared/.cursor/rules/storefront-architecture.mdc +41 -0
  47. package/templates/shared/.cursor/skills/customize-storefront-theme/SKILL.md +47 -0
  48. package/templates/shared/.cursor/skills/fix-segment-issues/SKILL.md +68 -0
  49. package/templates/shared/.cursor/skills/update-storefront-sections/SKILL.md +52 -0
  50. package/templates/shared/README.md +100 -0
  51. package/templates/shared/root-gitignore +6 -0
  52. package/templates/shared/storefront-only-README.md +25 -0
  53. package/templates/shared/theme-overrides.css +16 -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,61 +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
+ const cursorTemplate = join(PKG_ROOT, "templates/shared/.cursor")
109
+ if (existsSync(cursorTemplate)) {
110
+ cpSync(cursorTemplate, join(storefrontRoot, ".cursor"), { recursive: true })
111
+ }
112
+
113
+ const themeOverridesTemplate = join(PKG_ROOT, "templates/shared/theme-overrides.css")
114
+ if (existsSync(themeOverridesTemplate)) {
115
+ writeFileSync(join(storefrontRoot, "theme-overrides.css"), readFileSync(themeOverridesTemplate, "utf8"))
116
+ }
117
+
108
118
  writeFileSync(
109
- join(projectRoot, ".gitignore"),
119
+ join(storefrontRoot, ".gitignore"),
110
120
  readFileSync(join(PKG_ROOT, "templates/shared/gitignore"), "utf8")
111
121
  )
112
122
  writeFileSync(
113
- join(projectRoot, ".npmrc"),
123
+ join(storefrontRoot, ".npmrc"),
114
124
  readFileSync(join(PKG_ROOT, "templates/shared/npmrc"), "utf8")
115
125
  )
116
126
 
117
- const envTemplate = readFileSync(
118
- join(PKG_ROOT, "templates/shared/env.example"),
119
- "utf8"
120
- )
121
- writeFileSync(
122
- join(projectRoot, ".env.local"),
123
- applyTemplate(envTemplate, {
124
- BACKEND_URL: args.backendUrl,
125
- BASE_URL: args.baseUrl,
126
- DEFAULT_REGION: args.defaultRegion,
127
- PUBLISHABLE_KEY: args.publishableKey,
128
- })
129
- )
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))
130
135
  writeFileSync(
131
- join(projectRoot, ".env.example"),
136
+ join(storefrontRoot, ".env.example"),
132
137
  applyTemplate(envTemplate, {
133
138
  BACKEND_URL: "http://localhost:9000",
134
139
  BASE_URL: "http://localhost:8000",
@@ -138,7 +143,7 @@ function scaffoldProject(args) {
138
143
  )
139
144
 
140
145
  const pkg = {
141
- name: args.name,
146
+ name: flat ? args.name : `${args.name}-storefront`,
142
147
  private: true,
143
148
  scripts: {
144
149
  "storefront:build": "storefront-build",
@@ -148,7 +153,75 @@ function scaffoldProject(args) {
148
153
  },
149
154
  dependencies: buildDependencies(pagesConfig),
150
155
  }
151
- 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",
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
+ } else {
212
+ scaffoldStorefront(projectRoot, args, { flat: true })
213
+ writeFileSync(
214
+ join(projectRoot, "README.md"),
215
+ applyTemplate(
216
+ readFileSync(join(PKG_ROOT, "templates/shared/storefront-only-README.md"), "utf8"),
217
+ {
218
+ PROJECT_NAME: args.name,
219
+ BASE_URL: args.baseUrl,
220
+ DEFAULT_REGION: args.defaultRegion,
221
+ }
222
+ )
223
+ )
224
+ }
152
225
 
153
226
  return projectRoot
154
227
  }
@@ -161,29 +234,52 @@ function main() {
161
234
  }
162
235
 
163
236
  console.log(
164
- `Creating storefront "${args.name}" (preset: ${args.preset})...`
237
+ `Creating "${args.name}" (preset: ${args.preset}${args.withBackend ? ", with Medusa backend" : ""})...`
165
238
  )
166
239
 
167
240
  const projectRoot = scaffoldProject(args)
168
241
 
169
242
  if (args.install) {
170
243
  console.log("\nInstalling dependencies...")
171
- try {
172
- execSync("npm install", { cwd: projectRoot, stdio: "inherit" })
173
- } catch {
174
- console.warn("\nWarning: npm install failed. Run `npm install` manually in the project directory.")
244
+ const dirs = args.withBackend
245
+ ? [join(projectRoot, "backend"), join(projectRoot, "storefront")]
246
+ : [projectRoot]
247
+ for (const dir of dirs) {
248
+ try {
249
+ execSync("npm install", { cwd: dir, stdio: "inherit" })
250
+ } catch {
251
+ console.warn(`Warning: npm install failed in ${dir}`)
252
+ }
175
253
  }
176
254
  }
177
255
 
178
- console.log(`
179
- Done! Created ${projectRoot}
256
+ const nextSteps = args.withBackend
257
+ ? `
258
+ Next steps:
259
+ cd ${args.name}
260
+
261
+ Docker (full stack):
262
+ make up && make migrate && make seed && make admin-create
180
263
 
264
+ Local Node (Docker infra):
265
+ make up-infra
266
+ npm run dev:backend # terminal 1
267
+ npm run seed && npm run dev:storefront # terminal 2
268
+
269
+ Fully local:
270
+ cd backend && npm run dev # set DATABASE_URL in backend/.env
271
+ cd backend && npm run seed
272
+ cd ../storefront && npm run dev
273
+
274
+ See README.md, Makefile, and .cursor/ guides.
275
+ `
276
+ : `
181
277
  Next steps:
182
278
  cd ${args.name}
183
279
  npm run dev
280
+ `
184
281
 
185
- Edit pages.config.json and storefront.config.json, then rebuild with npm run storefront:build.
186
- `)
282
+ console.log(`Done! Created ${projectRoot}${nextSteps}`)
187
283
  }
188
284
 
189
285
  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
+ }