@graphcommerce/next-config 9.1.0-canary.55 → 10.0.0-canary.57

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 (36) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/Config.graphqls +3 -3
  3. package/__tests__/config/utils/__snapshots__/mergeEnvIntoConfig.ts.snap +1 -1
  4. package/__tests__/interceptors/generateInterceptors.ts +133 -150
  5. package/dist/config/loadConfig.js +7 -0
  6. package/dist/generated/config.js +9 -9
  7. package/dist/index.js +807 -2442
  8. package/dist/loadConfig-nJiCKeL1.js +311 -0
  9. package/dist/utils/findParentPath.js +36 -0
  10. package/package.json +41 -20
  11. package/src/commands/cleanupInterceptors.ts +26 -0
  12. package/src/commands/codegen.ts +13 -15
  13. package/src/commands/codegenInterceptors.ts +31 -0
  14. package/src/{config/commands → commands}/exportConfig.ts +3 -3
  15. package/src/{config/commands → commands}/generateConfig.ts +12 -9
  16. package/src/commands/generateConfigValues.ts +265 -0
  17. package/src/commands/index.ts +7 -0
  18. package/src/config/index.ts +0 -9
  19. package/src/config/loadConfig.ts +0 -1
  20. package/src/config/utils/mergeEnvIntoConfig.ts +27 -4
  21. package/src/generated/config.ts +13 -14
  22. package/src/index.ts +7 -39
  23. package/src/interceptors/generateInterceptor.ts +192 -157
  24. package/src/interceptors/generateInterceptors.ts +11 -4
  25. package/src/interceptors/updatePackageExports.ts +147 -0
  26. package/src/interceptors/writeInterceptors.ts +91 -36
  27. package/src/types.ts +26 -0
  28. package/src/utils/{isMonorepo.ts → findParentPath.ts} +2 -2
  29. package/src/utils/index.ts +7 -0
  30. package/src/utils/resolveDependenciesSync.ts +7 -9
  31. package/src/utils/resolveDependency.ts +1 -1
  32. package/src/withGraphCommerce.ts +30 -49
  33. package/tsconfig.json +3 -1
  34. package/__tests__/config/utils/configToImportMeta.ts +0 -121
  35. package/src/interceptors/InterceptorPlugin.ts +0 -141
  36. package/src/interceptors/commands/codegenInterceptors.ts +0 -27
@@ -0,0 +1,265 @@
1
+ /* eslint-disable quotes */
2
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs'
3
+ import path from 'path'
4
+ import prettierConf from '@graphcommerce/prettier-config-pwa'
5
+ import { transformFileSync } from '@swc/core'
6
+ import dotenv from 'dotenv'
7
+ import prettier from 'prettier'
8
+ import { loadConfig } from '../config/loadConfig'
9
+ import type { GraphCommerceConfig } from '../generated/config'
10
+ import { GraphCommerceConfigSchema } from '../generated/config'
11
+ import { resolveDependency } from '../utils/resolveDependency'
12
+
13
+ dotenv.config({ quiet: true })
14
+
15
+ const resolve = resolveDependency()
16
+
17
+ /** Keeps camelCase for file names and export names */
18
+ function toFileName(key: string): string {
19
+ return key
20
+ }
21
+
22
+ /** Generates a TypeScript value literal for the given value */
23
+ function generateValueLiteral(value: unknown): string {
24
+ if (value === null) return 'null'
25
+ if (value === undefined) return 'undefined'
26
+ if (typeof value === 'string') return JSON.stringify(value)
27
+ if (typeof value === 'boolean' || typeof value === 'number') return String(value)
28
+ if (Array.isArray(value) || (typeof value === 'object' && value !== null)) {
29
+ return JSON.stringify(value, null, 2)
30
+ }
31
+ return JSON.stringify(value)
32
+ }
33
+
34
+ /** Checks if a value should get its own file (non-null objects only, not arrays) */
35
+ function shouldCreateFile(value: unknown): boolean {
36
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
37
+ }
38
+
39
+ /** Gets the schema keys for a specific config section */
40
+ function getSectionSchemaKeys(configKey: keyof GraphCommerceConfig): string[] {
41
+ try {
42
+ const mainSchema = GraphCommerceConfigSchema()
43
+ const sectionSchema = mainSchema.shape[configKey]
44
+
45
+ if (!sectionSchema) return []
46
+
47
+ // Handle different schema wrapper types
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ let unwrappedSchema: any = sectionSchema
50
+
51
+ // Unwrap wrapper types in a loop to handle chained wrappers (e.g., ZodLazy containing ZodOptional)
52
+ // eslint-disable-next-line no-constant-condition
53
+ while (true) {
54
+ if (!unwrappedSchema || typeof unwrappedSchema !== 'object') break
55
+ if (!('_def' in unwrappedSchema)) break
56
+
57
+ // eslint-disable-next-line no-underscore-dangle
58
+ const def = unwrappedSchema._def
59
+ const typeName = def?.typeName as string | undefined
60
+
61
+ // Handle ZodLazy - need to evaluate the getter
62
+ if (typeName === 'ZodLazy' && def.getter) {
63
+ unwrappedSchema = def.getter()
64
+ continue
65
+ }
66
+
67
+ // Handle ZodOptional, ZodNullable, ZodDefault
68
+ if (def.innerType) {
69
+ unwrappedSchema = def.innerType
70
+ continue
71
+ }
72
+
73
+ // Handle ZodObject - this is what we want
74
+ if (typeName === 'ZodObject' && def.shape) {
75
+ const shape =
76
+ typeof def.shape === 'function'
77
+ ? (def.shape as () => Record<string, unknown>)()
78
+ : (def.shape as Record<string, unknown>)
79
+ return Object.keys(shape || {})
80
+ }
81
+
82
+ break
83
+ }
84
+
85
+
86
+ // Direct ZodObject
87
+ if (unwrappedSchema && 'shape' in unwrappedSchema) {
88
+ const shape =
89
+ typeof unwrappedSchema.shape === 'function'
90
+ ? (unwrappedSchema.shape as () => Record<string, unknown>)()
91
+ : (unwrappedSchema.shape as Record<string, unknown>)
92
+ return Object.keys(shape || {})
93
+ }
94
+ } catch {
95
+ // Silently fail and return empty array
96
+ }
97
+
98
+ return []
99
+ }
100
+
101
+ /** Creates a configuration section file for nested objects */
102
+ async function createConfigSectionFile(
103
+ sectionName: string,
104
+ sectionValue: Record<string, unknown>,
105
+ targetDir: string,
106
+ targetDistDir: string,
107
+ configKey: keyof GraphCommerceConfig,
108
+ ): Promise<void> {
109
+ const fileName = `${toFileName(sectionName)}.ts`
110
+ const filePath = path.join(targetDir, fileName)
111
+ const distFileName = `${toFileName(sectionName)}.js`
112
+ const distFilePath = path.join(targetDistDir, distFileName)
113
+
114
+ // Get all schema keys for this section to include all properties, even undefined ones
115
+ const schemaKeys = getSectionSchemaKeys(configKey)
116
+
117
+ // Create complete section object with schema defaults
118
+ const completeSectionValue: Record<string, unknown> = {}
119
+ for (const key of schemaKeys) {
120
+ completeSectionValue[key] = sectionValue[key] // Use actual value if present, undefined if not
121
+ }
122
+
123
+ // Create individual exports for each property with proper typing
124
+ const exports = Object.entries(completeSectionValue)
125
+ .map(([key, value]) => {
126
+ const valueStr = generateValueLiteral(value)
127
+ const propertyPath = `'${sectionName}.${key}'`
128
+
129
+ // Always include type annotation
130
+ const typeAnnotation = `: Get<GraphCommerceConfig, ${propertyPath}>`
131
+
132
+ return `export const ${key}${typeAnnotation} = ${valueStr}`
133
+ })
134
+ .join('\n\n')
135
+
136
+ // Always need the Get import since we always use type annotations
137
+ const needsGetImport = true
138
+
139
+ const imports = needsGetImport
140
+ ? `import type { GraphCommerceConfig } from '../config'\nimport type { Get } from 'type-fest'`
141
+ : `import type { GraphCommerceConfig } from '../config'`
142
+
143
+ const content = `// Auto-generated by 'yarn graphcommerce codegen-config-values'
144
+ ${imports}
145
+
146
+ ${exports}
147
+ `
148
+
149
+ const formattedContent = await prettier.format(content, {
150
+ ...prettierConf,
151
+ parser: 'typescript',
152
+ plugins: prettierConf.plugins?.filter(
153
+ (p) => typeof p === 'string' && !p.includes('prettier-plugin-sort-imports'),
154
+ ),
155
+ })
156
+
157
+ // Write TypeScript file
158
+ writeFileSync(filePath, formattedContent)
159
+
160
+ // Transform and write JavaScript file
161
+ const result = transformFileSync(filePath, {
162
+ module: { type: 'nodenext' },
163
+ env: { targets: { node: '18' } },
164
+ })
165
+
166
+ writeFileSync(distFilePath, result.code)
167
+ }
168
+
169
+ export async function generateConfigValues() {
170
+ const resolved = resolve('@graphcommerce/next-config')
171
+ if (!resolved) throw Error('Could not resolve @graphcommerce/next-config')
172
+
173
+ const config = loadConfig(process.cwd())
174
+ const targetDir = `${resolved.root}/src/generated/configValues`
175
+ const targetDistDir = `${resolved.root}/dist/generated/configValues`
176
+
177
+ // Clean up existing directories and recreate them
178
+ if (existsSync(targetDir)) {
179
+ rmSync(targetDir, { recursive: true, force: true })
180
+ }
181
+ if (existsSync(targetDistDir)) {
182
+ rmSync(targetDistDir, { recursive: true, force: true })
183
+ }
184
+ mkdirSync(targetDir, { recursive: true })
185
+ mkdirSync(targetDistDir, { recursive: true })
186
+
187
+ // Get all schema keys to ensure we include everything, even undefined values
188
+ const schema = GraphCommerceConfigSchema()
189
+ const schemaKeys = Object.keys(schema.shape) as (keyof GraphCommerceConfig)[]
190
+
191
+ // Create a complete config object with schema defaults
192
+ const completeConfig: Record<string, unknown> = {}
193
+ for (const key of schemaKeys) {
194
+ completeConfig[key] = config[key] // Use actual value if present, undefined if not
195
+ }
196
+
197
+ const configEntries = Object.entries(completeConfig) as [keyof GraphCommerceConfig, unknown][]
198
+ const nestedObjects: [string, Record<string, unknown>, keyof GraphCommerceConfig][] = []
199
+ const rootExports: string[] = []
200
+
201
+ // Separate nested objects from root-level values
202
+ for (const [key, value] of configEntries) {
203
+ if (shouldCreateFile(value)) {
204
+ // Create file for nested object
205
+ nestedObjects.push([key, value as Record<string, unknown>, key])
206
+ rootExports.push(`export * as ${key} from './${toFileName(key)}'`)
207
+ } else {
208
+ // Add to root exports (including undefined values)
209
+ const valueStr = generateValueLiteral(value)
210
+ // Always include type annotation
211
+ const typeAnnotation = `: Get<GraphCommerceConfig, '${key}'>`
212
+ rootExports.push(`export const ${key}${typeAnnotation} = ${valueStr}`)
213
+ }
214
+ }
215
+
216
+ // Create files for nested objects
217
+ await Promise.all(
218
+ nestedObjects.map(([sectionName, sectionValue, configKey]) =>
219
+ createConfigSectionFile(sectionName, sectionValue, targetDir, targetDistDir, configKey),
220
+ ),
221
+ )
222
+
223
+ // Always need the Get import since we always use type annotations
224
+ const rootNeedsGetImport = true
225
+
226
+ const rootImports = rootNeedsGetImport
227
+ ? `import type { GraphCommerceConfig } from '../config'\nimport type { Get } from 'type-fest'`
228
+ : `import type { GraphCommerceConfig } from '../config'`
229
+
230
+ // Create the main index file
231
+ const indexContent = `// Auto-generated by 'yarn graphcommerce codegen-config-values'
232
+ ${rootImports}
233
+
234
+ ${rootExports.join('\n')}
235
+ `
236
+
237
+ const formattedIndexContent = await prettier.format(indexContent, {
238
+ ...prettierConf,
239
+ parser: 'typescript',
240
+ plugins: prettierConf.plugins?.filter(
241
+ (p) => typeof p === 'string' && !p.includes('prettier-plugin-sort-imports'),
242
+ ),
243
+ })
244
+
245
+ const indexPath = path.join(targetDir, 'index.ts')
246
+ const distIndexPath = path.join(targetDistDir, 'index.js')
247
+
248
+ // Write TypeScript index file
249
+ writeFileSync(indexPath, formattedIndexContent)
250
+
251
+ // Transform and write JavaScript index file
252
+ const indexResult = transformFileSync(indexPath, {
253
+ module: { type: 'nodenext' },
254
+ env: { targets: { node: '20' } },
255
+ })
256
+
257
+ writeFileSync(distIndexPath, indexResult.code)
258
+
259
+ // eslint-disable-next-line no-console
260
+ console.log(`✅ Generated config values in ${targetDir} and ${targetDistDir}`)
261
+ // eslint-disable-next-line no-console
262
+ console.log(`📁 Created ${nestedObjects.length} nested object files + index.ts/.js`)
263
+ // eslint-disable-next-line no-console
264
+ console.log(`📝 Root exports: ${configEntries.length - nestedObjects.length}`)
265
+ }
@@ -0,0 +1,7 @@
1
+ export * from './cleanupInterceptors'
2
+ export * from './codegen'
3
+ export * from './codegenInterceptors'
4
+ export * from './copyFiles'
5
+ export * from './exportConfig'
6
+ export * from './generateConfig'
7
+ export * from './generateConfigValues'
@@ -1,17 +1,8 @@
1
1
  import type { Path, PathValue } from 'react-hook-form'
2
2
  import type { GraphCommerceConfig } from '../generated/config'
3
3
 
4
- export * from './commands/generateConfig'
5
- export * from './commands/exportConfig'
6
-
7
4
  export * from './loadConfig'
8
5
 
9
- declare global {
10
- interface ImportMeta {
11
- graphCommerce: GraphCommerceConfig
12
- }
13
- }
14
-
15
6
  export type IfConfig<P extends Path<GraphCommerceConfig> = Path<GraphCommerceConfig>> =
16
7
  | P
17
8
  | [P, PathValue<GraphCommerceConfig, P>]
@@ -5,7 +5,6 @@ import { GraphCommerceConfigSchema } from '../generated/config'
5
5
  import { demoConfig } from './demoConfig'
6
6
  import { formatAppliedEnv, mergeEnvIntoConfig } from './utils/mergeEnvIntoConfig'
7
7
 
8
- export * from './utils/configToImportMeta'
9
8
  export * from './utils/replaceConfigInString'
10
9
 
11
10
  const moduleName = 'graphcommerce'
@@ -9,6 +9,7 @@ import {
9
9
  ZodDefault,
10
10
  ZodEffects,
11
11
  ZodEnum,
12
+ ZodLazy,
12
13
  ZodNullable,
13
14
  ZodNumber,
14
15
  ZodObject,
@@ -47,6 +48,7 @@ export type ZodNode =
47
48
  | ZodEffects<ZodTypeAny>
48
49
  | ZodObject<ZodRawShape>
49
50
  | ZodArray<ZodTypeAny>
51
+ | ZodLazy<ZodTypeAny>
50
52
  | ZodString
51
53
  | ZodNumber
52
54
  | ZodBoolean
@@ -59,10 +61,31 @@ export function configToEnvSchema(schema: ZodNode) {
59
61
  function walk(incomming: ZodNode, path: string[] = []) {
60
62
  let node = incomming
61
63
 
62
- if (node instanceof ZodEffects) node = node.innerType()
63
- if (node instanceof ZodOptional) node = node.unwrap()
64
- if (node instanceof ZodNullable) node = node.unwrap()
65
- if (node instanceof ZodDefault) node = node.removeDefault()
64
+ // Unwrap wrapper types in a loop to handle chained wrappers (e.g., ZodLazy containing ZodOptional)
65
+ // eslint-disable-next-line no-constant-condition
66
+ while (true) {
67
+ if (node instanceof ZodEffects) {
68
+ node = node.innerType()
69
+ continue
70
+ }
71
+ if (node instanceof ZodOptional) {
72
+ node = node.unwrap()
73
+ continue
74
+ }
75
+ if (node instanceof ZodNullable) {
76
+ node = node.unwrap()
77
+ continue
78
+ }
79
+ if (node instanceof ZodDefault) {
80
+ node = node.removeDefault()
81
+ continue
82
+ }
83
+ if (node instanceof ZodLazy) {
84
+ node = node.schema
85
+ continue
86
+ }
87
+ break
88
+ }
66
89
 
67
90
  if (node instanceof ZodObject) {
68
91
  if (path.length > 0) {
@@ -59,9 +59,9 @@ export type DatalayerConfig = {
59
59
  * storefrontConfigDefault,
60
60
  * useStorefrontConfig,
61
61
  * } from '@graphcommerce/next-ui'
62
- *
62
+ * import { cartDisplayPricesInclTax } from '@graphcommerce/next-config/config'
63
63
  * // Accessing a global value
64
- * const globalConf = import.meta.graphCommerce.cartDisplayPricesInclTax
64
+ * const globalConf = cartDisplayPricesInclTax
65
65
  *
66
66
  * function MyComponent() {
67
67
  * // Configuration configured per storefront locale.
@@ -72,8 +72,7 @@ export type DatalayerConfig = {
72
72
  *
73
73
  * // Or as single line
74
74
  * const scopedConfigWithFallback2 =
75
- * useStorefrontConfig().cartDisplayPricesInclTax ??
76
- * import.meta.graphCommerce.cartDisplayPricesInclTax
75
+ * useStorefrontConfig().cartDisplayPricesInclTax ?? cartDisplayPricesInclTax
77
76
  *
78
77
  * return <div>{googleRecaptchaKey}</div>
79
78
  * }
@@ -263,7 +262,7 @@ export type GraphCommerceConfig = {
263
262
  */
264
263
  graphqlMeshEditMode?: InputMaybe<Scalars['Boolean']['input']>
265
264
  /**
266
- * The HyGraph endpoint.
265
+ * The Hygraph endpoint.
267
266
  *
268
267
  * > Read-only endpoint that allows low latency and high read-throughput content delivery.
269
268
  *
@@ -604,7 +603,7 @@ export function GraphCommerceConfigSchema(): z.ZodObject<Properties<GraphCommerc
604
603
  compare: z.boolean().nullish(),
605
604
  compareVariant: CompareVariantSchema.default('ICON').nullish(),
606
605
  configurableVariantForSimple: z.boolean().default(false).nullish(),
607
- configurableVariantValues: MagentoConfigurableVariantValuesSchema().nullish(),
606
+ configurableVariantValues: z.lazy(() => MagentoConfigurableVariantValuesSchema().nullish()),
608
607
  containerSizingContent: ContainerSizingSchema.default('FULL_WIDTH').nullish(),
609
608
  containerSizingShell: ContainerSizingSchema.default('FULL_WIDTH').nullish(),
610
609
  crossSellsHideCartItems: z.boolean().default(false).nullish(),
@@ -613,12 +612,12 @@ export function GraphCommerceConfigSchema(): z.ZodObject<Properties<GraphCommerc
613
612
  customerCompanyFieldsEnable: z.boolean().nullish(),
614
613
  customerDeleteEnabled: z.boolean().nullish(),
615
614
  customerXMagentoCacheIdDisable: z.boolean().nullish(),
616
- dataLayer: DatalayerConfigSchema().nullish(),
617
- debug: GraphCommerceDebugConfigSchema().nullish(),
615
+ dataLayer: z.lazy(() => DatalayerConfigSchema().nullish()),
616
+ debug: z.lazy(() => GraphCommerceDebugConfigSchema().nullish()),
618
617
  demoMode: z.boolean().default(true).nullish(),
619
618
  enableGuestCheckoutLogin: z.boolean().nullish(),
620
619
  googleAnalyticsId: z.string().nullish(),
621
- googlePlaystore: GraphCommerceGooglePlaystoreConfigSchema().nullish(),
620
+ googlePlaystore: z.lazy(() => GraphCommerceGooglePlaystoreConfigSchema().nullish()),
622
621
  googleRecaptchaKey: z.string().nullish(),
623
622
  googleTagmanagerId: z.string().nullish(),
624
623
  graphqlMeshEditMode: z.boolean().default(false).nullish(),
@@ -629,16 +628,16 @@ export function GraphCommerceConfigSchema(): z.ZodObject<Properties<GraphCommerc
629
628
  limitSsg: z.boolean().nullish(),
630
629
  magentoEndpoint: z.string().min(1),
631
630
  magentoVersion: z.number(),
632
- permissions: GraphCommercePermissionsSchema().nullish(),
631
+ permissions: z.lazy(() => GraphCommercePermissionsSchema().nullish()),
633
632
  previewSecret: z.string().nullish(),
634
633
  productFiltersLayout: ProductFiltersLayoutSchema.default('DEFAULT').nullish(),
635
634
  productFiltersPro: z.boolean().nullish(),
636
635
  productListPaginationVariant: PaginationVariantSchema.default('COMPACT').nullish(),
637
636
  productRoute: z.string().nullish(),
638
- recentlyViewedProducts: RecentlyViewedProductsConfigSchema().nullish(),
637
+ recentlyViewedProducts: z.lazy(() => RecentlyViewedProductsConfigSchema().nullish()),
639
638
  robotsAllow: z.boolean().nullish(),
640
- sidebarGallery: SidebarGalleryConfigSchema().nullish(),
641
- storefront: z.array(GraphCommerceStorefrontConfigSchema()),
639
+ sidebarGallery: z.lazy(() => SidebarGalleryConfigSchema().nullish()),
640
+ storefront: z.array(z.lazy(() => GraphCommerceStorefrontConfigSchema())),
642
641
  wishlistHideForGuests: z.boolean().nullish(),
643
642
  wishlistShowFeedbackMessage: z.boolean().nullish(),
644
643
  })
@@ -692,7 +691,7 @@ export function GraphCommerceStorefrontConfigSchema(): z.ZodObject<
692
691
  linguiLocale: z.string().nullish(),
693
692
  locale: z.string().min(1),
694
693
  magentoStoreCode: z.string().min(1),
695
- permissions: GraphCommercePermissionsSchema().nullish(),
694
+ permissions: z.lazy(() => GraphCommercePermissionsSchema().nullish()),
696
695
  robotsAllow: z.boolean().nullish(),
697
696
  })
698
697
  }
package/src/index.ts CHANGED
@@ -1,45 +1,13 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import type React from 'react'
3
- import type { Path, PathValue } from 'react-hook-form'
4
- import type { GraphCommerceConfig } from './generated/config'
5
-
6
- export * from './utils/isMonorepo'
7
- export * from './utils/resolveDependenciesSync'
8
- export * from './utils/packageRoots'
9
- export * from './utils/sig'
1
+ export * from './commands'
2
+ export * from './config'
10
3
  export {
11
- type GraphCommerceConfig,
12
4
  GraphCommerceConfigSchema,
13
- type GraphCommerceStorefrontConfig,
5
+ GraphCommerceDebugConfigSchema,
14
6
  GraphCommerceStorefrontConfigSchema,
7
+ type GraphCommerceConfig,
15
8
  type GraphCommerceDebugConfig,
16
- GraphCommerceDebugConfigSchema,
9
+ type GraphCommerceStorefrontConfig,
17
10
  } from './generated/config'
11
+ export * from './types'
12
+ export * from './utils'
18
13
  export * from './withGraphCommerce'
19
- export * from './config'
20
- export * from './interceptors/commands/codegenInterceptors'
21
- export * from './commands/copyFiles'
22
- export * from './commands/codegen'
23
-
24
- export type PluginProps<P extends Record<string, unknown> = Record<string, unknown>> = P & {
25
- Prev: React.FC<P>
26
- }
27
-
28
- export type FunctionPlugin<T extends (...args: any[]) => any> = (
29
- prev: T,
30
- ...args: Parameters<T>
31
- ) => ReturnType<T>
32
-
33
- /** @deprecated Use FunctionPlugin instead */
34
- export type MethodPlugin<T extends (...args: any[]) => any> = (
35
- prev: T,
36
- ...args: Parameters<T>
37
- ) => ReturnType<T>
38
-
39
- export type PluginConfig<P extends Path<GraphCommerceConfig> = Path<GraphCommerceConfig>> = {
40
- type: PluginType
41
- module: string
42
- ifConfig?: P | [P, PathValue<GraphCommerceConfig, P>]
43
- }
44
-
45
- export type PluginType = 'component' | 'function' | 'replace'