@pradip1995/framework-compiler 0.2.6 → 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.
- package/backend-templates/build-dynamic-config-defaults.mjs +25 -0
- package/backend-templates/build-homepage-defaults.cjs +50 -0
- package/backend-templates/config/homepage-config.json +568 -0
- package/backend-templates/generate-backend-package.cjs +59 -0
- package/backend-templates/generate-medusa-config.cjs +265 -0
- package/backend-templates/homepage-config.json +568 -0
- package/backend-templates/medusa-plugin-versions.json +55 -0
- package/backend-templates/plugin-definitions.cjs +124 -0
- package/backend-templates/product-metadata-descriptors.ts +27 -0
- package/backend-templates/scripts/build-dynamic-config-defaults.mjs +25 -0
- package/backend-templates/scripts/build-homepage-defaults.cjs +50 -0
- package/backend-templates/seed.ts +210 -0
- package/backend-templates/src/config/product-metadata-descriptors.ts +27 -0
- package/backend-templates/src/scripts/seed.ts +210 -0
- package/backend-templates/tsconfig.json +24 -0
- package/bin/backend-build.js +190 -0
- package/dynamic-config-schema/sync-backend-schema.cjs +6 -2
- package/package.json +4 -2
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Medusa plugin entries keyed by npm package name.
|
|
3
|
+
* Used by generate-medusa-config.cjs to build medusa-config.ts from plugins.config.json.
|
|
4
|
+
*/
|
|
5
|
+
module.exports = {
|
|
6
|
+
"medusa-product-helper": {
|
|
7
|
+
resolve: "medusa-product-helper",
|
|
8
|
+
options: {},
|
|
9
|
+
},
|
|
10
|
+
"stock-monitoring": {
|
|
11
|
+
resolve: "stock-monitoring",
|
|
12
|
+
options: { low_stock_threshold: 10, slow_moving_days_threshold: 90 },
|
|
13
|
+
},
|
|
14
|
+
"medusa-invoice-sbl": {
|
|
15
|
+
resolve: "medusa-invoice-sbl",
|
|
16
|
+
options: {
|
|
17
|
+
attachInvoiceToEmail: true,
|
|
18
|
+
guestJwtSecret: "process.env.JWT_SECRET",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
"medusa-dynamic-metadata": {
|
|
22
|
+
resolve: "medusa-dynamic-metadata",
|
|
23
|
+
options: {
|
|
24
|
+
entities: {
|
|
25
|
+
products: {
|
|
26
|
+
descriptors: "PRODUCT_METADATA_DESCRIPTORS",
|
|
27
|
+
expose_client_helpers: true,
|
|
28
|
+
filterable: true,
|
|
29
|
+
},
|
|
30
|
+
categories: {
|
|
31
|
+
descriptors: [
|
|
32
|
+
{ key: "category_image", label: "Category Image", type: "file" },
|
|
33
|
+
{ key: "category_background_color", label: "Category Background Color", type: "text" },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
collections: {
|
|
37
|
+
descriptors: [
|
|
38
|
+
{ key: "collection_icon", label: "Collection Icon", type: "file" },
|
|
39
|
+
{
|
|
40
|
+
key: "home_featured",
|
|
41
|
+
label: "Show on Home",
|
|
42
|
+
type: "select",
|
|
43
|
+
options: [
|
|
44
|
+
{ value: "true", label: "Yes" },
|
|
45
|
+
{ value: "false", label: "No" },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
product_variants: {
|
|
51
|
+
descriptors: [{ key: "variant_image", label: "Variant Image", type: "file" }],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
"medusa-review-rating": {
|
|
57
|
+
resolve: "medusa-review-rating",
|
|
58
|
+
options: { autoApprove: true, requireVerifiedPurchase: false, multiple_rating: true },
|
|
59
|
+
},
|
|
60
|
+
"medusa-plugin-dynamic-config": {
|
|
61
|
+
resolve: "medusa-plugin-dynamic-config",
|
|
62
|
+
options: {
|
|
63
|
+
configs: { "homepage-config": "homepageConfig" },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
"medusa-contact-us": {
|
|
67
|
+
resolve: "medusa-contact-us",
|
|
68
|
+
options: {
|
|
69
|
+
default_status: "pending",
|
|
70
|
+
payload_fields: [
|
|
71
|
+
{ key: "full_name", type: "text", required: true, label: "Full Name" },
|
|
72
|
+
{ key: "email", type: "text", required: true, label: "Email" },
|
|
73
|
+
{ key: "message", type: "textarea", required: true, label: "Message" },
|
|
74
|
+
],
|
|
75
|
+
allowed_statuses: ["pending", "in_progress", "resolved", "closed"],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
"order-management": {
|
|
79
|
+
resolve: "order-management",
|
|
80
|
+
options: {
|
|
81
|
+
jwtSecret: "process.env.JWT_SECRET",
|
|
82
|
+
storefrontUrl: "storefrontUrl",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
"customer-registration": {
|
|
86
|
+
resolve: "customer-registration",
|
|
87
|
+
options: {
|
|
88
|
+
storefrontUrl: "storefrontUrl",
|
|
89
|
+
registration: { identifier: "both", require_verification: true },
|
|
90
|
+
login: { identifier: "both" },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
"medusa-shiprocket-fulfillment-sbl": {
|
|
94
|
+
resolve: "medusa-shiprocket-fulfillment-sbl",
|
|
95
|
+
options: {
|
|
96
|
+
shiprocket: {
|
|
97
|
+
email: "process.env.SHIPROCKET_EMAIL",
|
|
98
|
+
password: "process.env.SHIPROCKET_PASSWORD",
|
|
99
|
+
pickupLocation: 'process.env.SHIPROCKET_PICKUP_LOCATION || "Primary"',
|
|
100
|
+
},
|
|
101
|
+
webhookSecret: "process.env.SHIPROCKET_WEBHOOK_SECRET",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
"medusa-notification-token-management": {
|
|
105
|
+
resolve: "medusa-notification-token-management",
|
|
106
|
+
options: {},
|
|
107
|
+
},
|
|
108
|
+
"medusa-customer-file-upload": {
|
|
109
|
+
resolve: "medusa-customer-file-upload",
|
|
110
|
+
options: {},
|
|
111
|
+
},
|
|
112
|
+
"medusa-analytics": {
|
|
113
|
+
resolveSpecial: "medusa-analytics",
|
|
114
|
+
options: {},
|
|
115
|
+
},
|
|
116
|
+
"medusa-export": {
|
|
117
|
+
resolve: "medusa-export",
|
|
118
|
+
options: {},
|
|
119
|
+
},
|
|
120
|
+
"medusa-payment-provider": {
|
|
121
|
+
resolve: "medusa-payment-provider",
|
|
122
|
+
options: {},
|
|
123
|
+
},
|
|
124
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Minimal product metadata for storefront filters and PDP specs */
|
|
2
|
+
export const PRODUCT_METADATA_DESCRIPTORS = [
|
|
3
|
+
{
|
|
4
|
+
key: "style",
|
|
5
|
+
label: "Style",
|
|
6
|
+
type: "select",
|
|
7
|
+
filterable: true,
|
|
8
|
+
options: [
|
|
9
|
+
{ value: "casual", label: "Casual" },
|
|
10
|
+
{ value: "formal", label: "Formal" },
|
|
11
|
+
{ value: "festive", label: "Festive" },
|
|
12
|
+
],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
key: "fabric",
|
|
16
|
+
label: "Fabric",
|
|
17
|
+
type: "select",
|
|
18
|
+
filterable: true,
|
|
19
|
+
options: [
|
|
20
|
+
{ value: "cotton", label: "Cotton" },
|
|
21
|
+
{ value: "silk", label: "Silk" },
|
|
22
|
+
{ value: "linen", label: "Linen" },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
{ key: "size_fit", label: "Size & Fit", type: "textarea" },
|
|
26
|
+
{ key: "material_care", label: "Material & Care", type: "textarea" },
|
|
27
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Regenerate config/homepage-config.defaults.json from config/homepage-config.json.
|
|
4
|
+
* Run after editing the schema structure in the backend.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, writeFileSync } from "fs"
|
|
7
|
+
import { join, dirname } from "path"
|
|
8
|
+
import { fileURLToPath } from "url"
|
|
9
|
+
import { createRequire } from "module"
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
12
|
+
const backendRoot = join(__dirname, "..")
|
|
13
|
+
const require = createRequire(import.meta.url)
|
|
14
|
+
const { buildHomepageConfigDefaults } = require("./build-homepage-defaults.cjs")
|
|
15
|
+
|
|
16
|
+
const schemaPath = join(backendRoot, "config/homepage-config.json")
|
|
17
|
+
const schema = JSON.parse(readFileSync(schemaPath, "utf8"))
|
|
18
|
+
const defaults = buildHomepageConfigDefaults(schema)
|
|
19
|
+
|
|
20
|
+
writeFileSync(
|
|
21
|
+
join(backendRoot, "config/homepage-config.defaults.json"),
|
|
22
|
+
`${JSON.stringify(defaults, null, 2)}\n`
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
console.log("Wrote config/homepage-config.defaults.json")
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build initial homepage-config values from schema structure defaultValue fields.
|
|
3
|
+
* Copied into each scaffolded backend at scripts/build-homepage-defaults.cjs
|
|
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
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { ExecArgs } from "@medusajs/framework/types"
|
|
2
|
+
import {
|
|
3
|
+
ContainerRegistrationKeys,
|
|
4
|
+
Modules,
|
|
5
|
+
ProductStatus,
|
|
6
|
+
} from "@medusajs/framework/utils"
|
|
7
|
+
import {
|
|
8
|
+
createApiKeysWorkflow,
|
|
9
|
+
createInventoryLevelsWorkflow,
|
|
10
|
+
createProductCategoriesWorkflow,
|
|
11
|
+
createProductsWorkflow,
|
|
12
|
+
createRegionsWorkflow,
|
|
13
|
+
createSalesChannelsWorkflow,
|
|
14
|
+
createShippingOptionsWorkflow,
|
|
15
|
+
createShippingProfilesWorkflow,
|
|
16
|
+
createStockLocationsWorkflow,
|
|
17
|
+
createTaxRegionsWorkflow,
|
|
18
|
+
linkSalesChannelsToApiKeyWorkflow,
|
|
19
|
+
linkSalesChannelsToStockLocationWorkflow,
|
|
20
|
+
updateStoresWorkflow,
|
|
21
|
+
} from "@medusajs/medusa/core-flows"
|
|
22
|
+
|
|
23
|
+
const SEED_COUNTRIES = ["{{DEFAULT_REGION}}"]
|
|
24
|
+
const SEED_CURRENCY = "{{DEFAULT_CURRENCY}}"
|
|
25
|
+
const SEED_REGION_NAME = "{{DEFAULT_REGION_NAME}}"
|
|
26
|
+
|
|
27
|
+
export default async function seedStore({ container }: ExecArgs) {
|
|
28
|
+
const logger = container.resolve(ContainerRegistrationKeys.LOGGER)
|
|
29
|
+
const link = container.resolve(ContainerRegistrationKeys.LINK)
|
|
30
|
+
const fulfillmentModuleService = container.resolve(Modules.FULFILLMENT)
|
|
31
|
+
const salesChannelModuleService = container.resolve(Modules.SALES_CHANNEL)
|
|
32
|
+
const storeModuleService = container.resolve(Modules.STORE)
|
|
33
|
+
|
|
34
|
+
logger.info("Seeding store...")
|
|
35
|
+
const [store] = await storeModuleService.listStores()
|
|
36
|
+
|
|
37
|
+
let [salesChannel] = await salesChannelModuleService.listSalesChannels({
|
|
38
|
+
name: "Default Sales Channel",
|
|
39
|
+
})
|
|
40
|
+
if (!salesChannel) {
|
|
41
|
+
const { result } = await createSalesChannelsWorkflow(container).run({
|
|
42
|
+
input: { salesChannelsData: [{ name: "Default Sales Channel" }] },
|
|
43
|
+
})
|
|
44
|
+
salesChannel = result[0]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await updateStoresWorkflow(container).run({
|
|
48
|
+
input: {
|
|
49
|
+
selector: { id: store.id },
|
|
50
|
+
update: {
|
|
51
|
+
default_sales_channel_id: salesChannel.id,
|
|
52
|
+
supported_currencies: [{ currency_code: SEED_CURRENCY, is_default: true }],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
logger.info("Seeding region...")
|
|
58
|
+
const { result: regions } = await createRegionsWorkflow(container).run({
|
|
59
|
+
input: {
|
|
60
|
+
regions: [
|
|
61
|
+
{
|
|
62
|
+
name: SEED_REGION_NAME,
|
|
63
|
+
currency_code: SEED_CURRENCY,
|
|
64
|
+
countries: SEED_COUNTRIES,
|
|
65
|
+
payment_providers: ["pp_system_default"],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
const region = regions[0]
|
|
71
|
+
|
|
72
|
+
await createTaxRegionsWorkflow(container).run({
|
|
73
|
+
input: SEED_COUNTRIES.map((country_code) => ({
|
|
74
|
+
country_code,
|
|
75
|
+
provider_id: "tp_system",
|
|
76
|
+
})),
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const { result: stockLocations } = await createStockLocationsWorkflow(container).run({
|
|
80
|
+
input: {
|
|
81
|
+
locations: [
|
|
82
|
+
{
|
|
83
|
+
name: "Primary Warehouse",
|
|
84
|
+
address: { city: "Default", country_code: SEED_COUNTRIES[0], address_1: "" },
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
const stockLocation = stockLocations[0]
|
|
90
|
+
|
|
91
|
+
await updateStoresWorkflow(container).run({
|
|
92
|
+
input: {
|
|
93
|
+
selector: { id: store.id },
|
|
94
|
+
update: { default_location_id: stockLocation.id },
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
await link.create({
|
|
99
|
+
[Modules.STOCK_LOCATION]: { stock_location_id: stockLocation.id },
|
|
100
|
+
[Modules.FULFILLMENT]: { fulfillment_provider_id: "manual_manual" },
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const shippingProfiles = await fulfillmentModuleService.listShippingProfiles({ type: "default" })
|
|
104
|
+
let shippingProfile = shippingProfiles[0]
|
|
105
|
+
if (!shippingProfile) {
|
|
106
|
+
const { result } = await createShippingProfilesWorkflow(container).run({
|
|
107
|
+
input: { data: [{ name: "Default Shipping Profile", type: "default" }] },
|
|
108
|
+
})
|
|
109
|
+
shippingProfile = result[0]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const fulfillmentSet = await fulfillmentModuleService.createFulfillmentSets({
|
|
113
|
+
name: "Primary delivery",
|
|
114
|
+
type: "shipping",
|
|
115
|
+
service_zones: [
|
|
116
|
+
{
|
|
117
|
+
name: SEED_REGION_NAME,
|
|
118
|
+
geo_zones: SEED_COUNTRIES.map((country_code) => ({ country_code, type: "country" as const })),
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
await link.create({
|
|
124
|
+
[Modules.STOCK_LOCATION]: { stock_location_id: stockLocation.id },
|
|
125
|
+
[Modules.FULFILLMENT]: { fulfillment_set_id: fulfillmentSet.id },
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
await createShippingOptionsWorkflow(container).run({
|
|
129
|
+
input: [
|
|
130
|
+
{
|
|
131
|
+
name: "Standard Shipping",
|
|
132
|
+
price_type: "flat",
|
|
133
|
+
provider_id: "manual_manual",
|
|
134
|
+
service_zone_id: fulfillmentSet.service_zones[0].id,
|
|
135
|
+
shipping_profile_id: shippingProfile.id,
|
|
136
|
+
type: { label: "Standard", description: "Ship in 2-5 days.", code: "standard" },
|
|
137
|
+
prices: [{ currency_code: SEED_CURRENCY, amount: 0 }, { region_id: region.id, amount: 0 }],
|
|
138
|
+
rules: [
|
|
139
|
+
{ attribute: "enabled_in_store", value: "true", operator: "eq" },
|
|
140
|
+
{ attribute: "is_return", value: "false", operator: "eq" },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
await linkSalesChannelsToStockLocationWorkflow(container).run({
|
|
147
|
+
input: { id: stockLocation.id, add: [salesChannel.id] },
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
logger.info("Creating publishable API key...")
|
|
151
|
+
const { result: apiKeys } = await createApiKeysWorkflow(container).run({
|
|
152
|
+
input: {
|
|
153
|
+
api_keys: [{ title: "Storefront", type: "publishable", created_by: "" }],
|
|
154
|
+
},
|
|
155
|
+
})
|
|
156
|
+
const publishableKey = apiKeys[0]
|
|
157
|
+
await linkSalesChannelsToApiKeyWorkflow(container).run({
|
|
158
|
+
input: { id: publishableKey.id, add: [salesChannel.id] },
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
logger.info("Seeding sample category and product...")
|
|
162
|
+
const { result: categories } = await createProductCategoriesWorkflow(container).run({
|
|
163
|
+
input: { product_categories: [{ name: "Featured", is_active: true }] },
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
const { result: products } = await createProductsWorkflow(container).run({
|
|
167
|
+
input: {
|
|
168
|
+
products: [
|
|
169
|
+
{
|
|
170
|
+
title: "Sample Product",
|
|
171
|
+
handle: "sample-product",
|
|
172
|
+
status: ProductStatus.PUBLISHED,
|
|
173
|
+
category_ids: [categories[0].id],
|
|
174
|
+
options: [{ title: "Size", values: ["S", "M", "L"] }],
|
|
175
|
+
variants: [
|
|
176
|
+
{
|
|
177
|
+
title: "S",
|
|
178
|
+
sku: "SAMPLE-S",
|
|
179
|
+
options: { Size: "S" },
|
|
180
|
+
prices: [{ currency_code: SEED_CURRENCY, amount: 999 }],
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
sales_channels: [{ id: salesChannel.id }],
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const inventoryModule = container.resolve(Modules.INVENTORY)
|
|
190
|
+
const inventoryItems = await inventoryModule.listInventoryItems({ sku: "SAMPLE-S" })
|
|
191
|
+
if (inventoryItems[0]) {
|
|
192
|
+
await createInventoryLevelsWorkflow(container).run({
|
|
193
|
+
input: {
|
|
194
|
+
inventory_levels: [
|
|
195
|
+
{
|
|
196
|
+
inventory_item_id: inventoryItems[0].id,
|
|
197
|
+
location_id: stockLocation.id,
|
|
198
|
+
stocked_quantity: 100,
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
logger.info("")
|
|
206
|
+
logger.info("=== Seed complete ===")
|
|
207
|
+
logger.info(`Region: ${SEED_REGION_NAME} (${SEED_COUNTRIES.join(", ")})`)
|
|
208
|
+
logger.info(`Publishable API key id: ${publishableKey.id}`)
|
|
209
|
+
logger.info(`Sample product: ${products[0]?.handle}`)
|
|
210
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Minimal product metadata for storefront filters and PDP specs */
|
|
2
|
+
export const PRODUCT_METADATA_DESCRIPTORS = [
|
|
3
|
+
{
|
|
4
|
+
key: "style",
|
|
5
|
+
label: "Style",
|
|
6
|
+
type: "select",
|
|
7
|
+
filterable: true,
|
|
8
|
+
options: [
|
|
9
|
+
{ value: "casual", label: "Casual" },
|
|
10
|
+
{ value: "formal", label: "Formal" },
|
|
11
|
+
{ value: "festive", label: "Festive" },
|
|
12
|
+
],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
key: "fabric",
|
|
16
|
+
label: "Fabric",
|
|
17
|
+
type: "select",
|
|
18
|
+
filterable: true,
|
|
19
|
+
options: [
|
|
20
|
+
{ value: "cotton", label: "Cotton" },
|
|
21
|
+
{ value: "silk", label: "Silk" },
|
|
22
|
+
{ value: "linen", label: "Linen" },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
{ key: "size_fit", label: "Size & Fit", type: "textarea" },
|
|
26
|
+
{ key: "material_care", label: "Material & Care", type: "textarea" },
|
|
27
|
+
]
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { ExecArgs } from "@medusajs/framework/types"
|
|
2
|
+
import {
|
|
3
|
+
ContainerRegistrationKeys,
|
|
4
|
+
Modules,
|
|
5
|
+
ProductStatus,
|
|
6
|
+
} from "@medusajs/framework/utils"
|
|
7
|
+
import {
|
|
8
|
+
createApiKeysWorkflow,
|
|
9
|
+
createInventoryLevelsWorkflow,
|
|
10
|
+
createProductCategoriesWorkflow,
|
|
11
|
+
createProductsWorkflow,
|
|
12
|
+
createRegionsWorkflow,
|
|
13
|
+
createSalesChannelsWorkflow,
|
|
14
|
+
createShippingOptionsWorkflow,
|
|
15
|
+
createShippingProfilesWorkflow,
|
|
16
|
+
createStockLocationsWorkflow,
|
|
17
|
+
createTaxRegionsWorkflow,
|
|
18
|
+
linkSalesChannelsToApiKeyWorkflow,
|
|
19
|
+
linkSalesChannelsToStockLocationWorkflow,
|
|
20
|
+
updateStoresWorkflow,
|
|
21
|
+
} from "@medusajs/medusa/core-flows"
|
|
22
|
+
|
|
23
|
+
const SEED_COUNTRIES = ["{{DEFAULT_REGION}}"]
|
|
24
|
+
const SEED_CURRENCY = "{{DEFAULT_CURRENCY}}"
|
|
25
|
+
const SEED_REGION_NAME = "{{DEFAULT_REGION_NAME}}"
|
|
26
|
+
|
|
27
|
+
export default async function seedStore({ container }: ExecArgs) {
|
|
28
|
+
const logger = container.resolve(ContainerRegistrationKeys.LOGGER)
|
|
29
|
+
const link = container.resolve(ContainerRegistrationKeys.LINK)
|
|
30
|
+
const fulfillmentModuleService = container.resolve(Modules.FULFILLMENT)
|
|
31
|
+
const salesChannelModuleService = container.resolve(Modules.SALES_CHANNEL)
|
|
32
|
+
const storeModuleService = container.resolve(Modules.STORE)
|
|
33
|
+
|
|
34
|
+
logger.info("Seeding store...")
|
|
35
|
+
const [store] = await storeModuleService.listStores()
|
|
36
|
+
|
|
37
|
+
let [salesChannel] = await salesChannelModuleService.listSalesChannels({
|
|
38
|
+
name: "Default Sales Channel",
|
|
39
|
+
})
|
|
40
|
+
if (!salesChannel) {
|
|
41
|
+
const { result } = await createSalesChannelsWorkflow(container).run({
|
|
42
|
+
input: { salesChannelsData: [{ name: "Default Sales Channel" }] },
|
|
43
|
+
})
|
|
44
|
+
salesChannel = result[0]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await updateStoresWorkflow(container).run({
|
|
48
|
+
input: {
|
|
49
|
+
selector: { id: store.id },
|
|
50
|
+
update: {
|
|
51
|
+
default_sales_channel_id: salesChannel.id,
|
|
52
|
+
supported_currencies: [{ currency_code: SEED_CURRENCY, is_default: true }],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
logger.info("Seeding region...")
|
|
58
|
+
const { result: regions } = await createRegionsWorkflow(container).run({
|
|
59
|
+
input: {
|
|
60
|
+
regions: [
|
|
61
|
+
{
|
|
62
|
+
name: SEED_REGION_NAME,
|
|
63
|
+
currency_code: SEED_CURRENCY,
|
|
64
|
+
countries: SEED_COUNTRIES,
|
|
65
|
+
payment_providers: ["pp_system_default"],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
const region = regions[0]
|
|
71
|
+
|
|
72
|
+
await createTaxRegionsWorkflow(container).run({
|
|
73
|
+
input: SEED_COUNTRIES.map((country_code) => ({
|
|
74
|
+
country_code,
|
|
75
|
+
provider_id: "tp_system",
|
|
76
|
+
})),
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const { result: stockLocations } = await createStockLocationsWorkflow(container).run({
|
|
80
|
+
input: {
|
|
81
|
+
locations: [
|
|
82
|
+
{
|
|
83
|
+
name: "Primary Warehouse",
|
|
84
|
+
address: { city: "Default", country_code: SEED_COUNTRIES[0], address_1: "" },
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
const stockLocation = stockLocations[0]
|
|
90
|
+
|
|
91
|
+
await updateStoresWorkflow(container).run({
|
|
92
|
+
input: {
|
|
93
|
+
selector: { id: store.id },
|
|
94
|
+
update: { default_location_id: stockLocation.id },
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
await link.create({
|
|
99
|
+
[Modules.STOCK_LOCATION]: { stock_location_id: stockLocation.id },
|
|
100
|
+
[Modules.FULFILLMENT]: { fulfillment_provider_id: "manual_manual" },
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const shippingProfiles = await fulfillmentModuleService.listShippingProfiles({ type: "default" })
|
|
104
|
+
let shippingProfile = shippingProfiles[0]
|
|
105
|
+
if (!shippingProfile) {
|
|
106
|
+
const { result } = await createShippingProfilesWorkflow(container).run({
|
|
107
|
+
input: { data: [{ name: "Default Shipping Profile", type: "default" }] },
|
|
108
|
+
})
|
|
109
|
+
shippingProfile = result[0]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const fulfillmentSet = await fulfillmentModuleService.createFulfillmentSets({
|
|
113
|
+
name: "Primary delivery",
|
|
114
|
+
type: "shipping",
|
|
115
|
+
service_zones: [
|
|
116
|
+
{
|
|
117
|
+
name: SEED_REGION_NAME,
|
|
118
|
+
geo_zones: SEED_COUNTRIES.map((country_code) => ({ country_code, type: "country" as const })),
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
await link.create({
|
|
124
|
+
[Modules.STOCK_LOCATION]: { stock_location_id: stockLocation.id },
|
|
125
|
+
[Modules.FULFILLMENT]: { fulfillment_set_id: fulfillmentSet.id },
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
await createShippingOptionsWorkflow(container).run({
|
|
129
|
+
input: [
|
|
130
|
+
{
|
|
131
|
+
name: "Standard Shipping",
|
|
132
|
+
price_type: "flat",
|
|
133
|
+
provider_id: "manual_manual",
|
|
134
|
+
service_zone_id: fulfillmentSet.service_zones[0].id,
|
|
135
|
+
shipping_profile_id: shippingProfile.id,
|
|
136
|
+
type: { label: "Standard", description: "Ship in 2-5 days.", code: "standard" },
|
|
137
|
+
prices: [{ currency_code: SEED_CURRENCY, amount: 0 }, { region_id: region.id, amount: 0 }],
|
|
138
|
+
rules: [
|
|
139
|
+
{ attribute: "enabled_in_store", value: "true", operator: "eq" },
|
|
140
|
+
{ attribute: "is_return", value: "false", operator: "eq" },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
await linkSalesChannelsToStockLocationWorkflow(container).run({
|
|
147
|
+
input: { id: stockLocation.id, add: [salesChannel.id] },
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
logger.info("Creating publishable API key...")
|
|
151
|
+
const { result: apiKeys } = await createApiKeysWorkflow(container).run({
|
|
152
|
+
input: {
|
|
153
|
+
api_keys: [{ title: "Storefront", type: "publishable", created_by: "" }],
|
|
154
|
+
},
|
|
155
|
+
})
|
|
156
|
+
const publishableKey = apiKeys[0]
|
|
157
|
+
await linkSalesChannelsToApiKeyWorkflow(container).run({
|
|
158
|
+
input: { id: publishableKey.id, add: [salesChannel.id] },
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
logger.info("Seeding sample category and product...")
|
|
162
|
+
const { result: categories } = await createProductCategoriesWorkflow(container).run({
|
|
163
|
+
input: { product_categories: [{ name: "Featured", is_active: true }] },
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
const { result: products } = await createProductsWorkflow(container).run({
|
|
167
|
+
input: {
|
|
168
|
+
products: [
|
|
169
|
+
{
|
|
170
|
+
title: "Sample Product",
|
|
171
|
+
handle: "sample-product",
|
|
172
|
+
status: ProductStatus.PUBLISHED,
|
|
173
|
+
category_ids: [categories[0].id],
|
|
174
|
+
options: [{ title: "Size", values: ["S", "M", "L"] }],
|
|
175
|
+
variants: [
|
|
176
|
+
{
|
|
177
|
+
title: "S",
|
|
178
|
+
sku: "SAMPLE-S",
|
|
179
|
+
options: { Size: "S" },
|
|
180
|
+
prices: [{ currency_code: SEED_CURRENCY, amount: 999 }],
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
sales_channels: [{ id: salesChannel.id }],
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const inventoryModule = container.resolve(Modules.INVENTORY)
|
|
190
|
+
const inventoryItems = await inventoryModule.listInventoryItems({ sku: "SAMPLE-S" })
|
|
191
|
+
if (inventoryItems[0]) {
|
|
192
|
+
await createInventoryLevelsWorkflow(container).run({
|
|
193
|
+
input: {
|
|
194
|
+
inventory_levels: [
|
|
195
|
+
{
|
|
196
|
+
inventory_item_id: inventoryItems[0].id,
|
|
197
|
+
location_id: stockLocation.id,
|
|
198
|
+
stocked_quantity: 100,
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
logger.info("")
|
|
206
|
+
logger.info("=== Seed complete ===")
|
|
207
|
+
logger.info(`Region: ${SEED_REGION_NAME} (${SEED_COUNTRIES.join(", ")})`)
|
|
208
|
+
logger.info(`Publishable API key id: ${publishableKey.id}`)
|
|
209
|
+
logger.info(`Sample product: ${products[0]?.handle}`)
|
|
210
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2021",
|
|
4
|
+
"esModuleInterop": true,
|
|
5
|
+
"module": "Node16",
|
|
6
|
+
"moduleResolution": "Node16",
|
|
7
|
+
"emitDecoratorMetadata": true,
|
|
8
|
+
"experimentalDecorators": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"declaration": false,
|
|
11
|
+
"outDir": "./.medusa/server",
|
|
12
|
+
"rootDir": "./",
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"strictNullChecks": true
|
|
17
|
+
},
|
|
18
|
+
"ts-node": {
|
|
19
|
+
"compiler": "typescript",
|
|
20
|
+
"transpileOnly": true
|
|
21
|
+
},
|
|
22
|
+
"include": ["**/*", ".medusa/types/*"],
|
|
23
|
+
"exclude": ["node_modules", ".medusa/server", ".medusa/admin", ".cache"]
|
|
24
|
+
}
|