@graphcommerce/next-config 5.2.0-canary.9 → 6.0.0-canary.20
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/README.md +4 -9
- package/dist/config/generateConfig.js +49 -0
- package/dist/config/index.js +18 -0
- package/dist/config/loadConfig.js +48 -0
- package/dist/config/utils/configToImportMeta.js +34 -0
- package/dist/config/utils/diff.js +33 -0
- package/dist/config/utils/mergeEnvIntoConfig.js +175 -0
- package/dist/config/utils/rewriteLegacyEnv.js +110 -0
- package/dist/generated/config.js +56 -0
- package/dist/index.js +2 -10
- package/dist/interceptors/InterceptorPlugin.js +4 -3
- package/dist/interceptors/findPlugins.js +64 -35
- package/dist/interceptors/generateInterceptors.js +2 -2
- package/dist/interceptors/writeInterceptors.js +1 -0
- package/dist/utils/resolveDependency.js +5 -1
- package/dist/withGraphCommerce.js +85 -19
- package/package.json +13 -6
- package/src/config/generateConfig.ts +52 -0
- package/src/config/index.ts +13 -0
- package/src/config/loadConfig.ts +40 -0
- package/src/config/utils/configToImportMeta.ts +36 -0
- package/src/config/utils/diff.ts +39 -0
- package/src/config/utils/mergeEnvIntoConfig.ts +228 -0
- package/src/config/utils/rewriteLegacyEnv.ts +121 -0
- package/src/generated/config.ts +255 -0
- package/src/index.ts +2 -10
- package/src/interceptors/InterceptorPlugin.ts +4 -3
- package/src/interceptors/findPlugins.ts +73 -36
- package/src/interceptors/generateInterceptors.ts +4 -3
- package/src/interceptors/writeInterceptors.ts +1 -0
- package/src/utils/resolveDependency.ts +5 -1
- package/src/withGraphCommerce.ts +102 -30
- package/dist/index.d.ts +0 -10
- package/dist/interceptors/InterceptorPlugin.d.ts +0 -8
- package/dist/interceptors/findPlugins.d.ts +0 -2
- package/dist/interceptors/generateInterceptors.d.ts +0 -19
- package/dist/interceptors/writeInterceptors.d.ts +0 -2
- package/dist/utils/PackagesSort.d.ts +0 -3
- package/dist/utils/TopologicalSort.d.ts +0 -16
- package/dist/utils/isMonorepo.d.ts +0 -1
- package/dist/utils/resolveDependenciesSync.d.ts +0 -22
- package/dist/utils/resolveDependency.d.ts +0 -9
- package/dist/withGraphCommerce.d.ts +0 -6
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/* eslint-disable import/no-extraneous-dependencies */
|
|
2
|
+
import { mergeDeep, cloneDeep } from '@apollo/client/utilities'
|
|
3
|
+
import chalk from 'chalk'
|
|
4
|
+
import { get, set } from 'lodash'
|
|
5
|
+
import snakeCase from 'lodash/snakeCase'
|
|
6
|
+
import {
|
|
7
|
+
z,
|
|
8
|
+
ZodObject,
|
|
9
|
+
ZodBoolean,
|
|
10
|
+
ZodArray,
|
|
11
|
+
ZodString,
|
|
12
|
+
ZodNumber,
|
|
13
|
+
ZodNullable,
|
|
14
|
+
ZodOptional,
|
|
15
|
+
ZodEffects,
|
|
16
|
+
ZodRawShape,
|
|
17
|
+
ZodEnum,
|
|
18
|
+
} from 'zod'
|
|
19
|
+
import diff from './diff'
|
|
20
|
+
|
|
21
|
+
const fmt = (s: string) => s.split(/(\d+)/).map(snakeCase).join('')
|
|
22
|
+
const pathStr = (path: string[]) => ['GC', ...path].map(fmt).join('_').toUpperCase()
|
|
23
|
+
const dotNotation = (pathParts: string[]) =>
|
|
24
|
+
pathParts
|
|
25
|
+
.map((v) => {
|
|
26
|
+
const idx = Number(v)
|
|
27
|
+
return !Number.isNaN(idx) ? `[${idx}]` : v
|
|
28
|
+
})
|
|
29
|
+
.join('.')
|
|
30
|
+
|
|
31
|
+
function isJSON(str: string | undefined): boolean {
|
|
32
|
+
if (!str) return true
|
|
33
|
+
try {
|
|
34
|
+
JSON.parse(str)
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
return true
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type ZodNode =
|
|
42
|
+
| ZodNullable<any>
|
|
43
|
+
| ZodOptional<any>
|
|
44
|
+
| ZodEffects<any>
|
|
45
|
+
| ZodObject<any>
|
|
46
|
+
| ZodArray<any>
|
|
47
|
+
| ZodString
|
|
48
|
+
| ZodNumber
|
|
49
|
+
| ZodBoolean
|
|
50
|
+
|
|
51
|
+
export function configToEnvSchema(schema: ZodNode) {
|
|
52
|
+
const envSchema: ZodRawShape = {}
|
|
53
|
+
const envToDot: Record<string, string> = {}
|
|
54
|
+
|
|
55
|
+
function walk(incomming: ZodNode, path: string[] = []) {
|
|
56
|
+
let node = incomming
|
|
57
|
+
|
|
58
|
+
if (node instanceof ZodEffects) node = node.innerType()
|
|
59
|
+
if (node instanceof ZodOptional) node = node.unwrap()
|
|
60
|
+
if (node instanceof ZodNullable) node = node.unwrap()
|
|
61
|
+
|
|
62
|
+
if (node instanceof ZodObject) {
|
|
63
|
+
if (path.length > 0) {
|
|
64
|
+
envSchema[pathStr(path)] = z
|
|
65
|
+
.string()
|
|
66
|
+
.optional()
|
|
67
|
+
.refine(isJSON, { message: `Invalid JSON` })
|
|
68
|
+
.transform((val) => (val ? JSON.parse(val) : val))
|
|
69
|
+
envToDot[pathStr(path)] = dotNotation(path)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const typeNode = node as unknown as ZodObject<ZodRawShape>
|
|
73
|
+
|
|
74
|
+
Object.keys(typeNode.shape).forEach((key) => {
|
|
75
|
+
walk(typeNode.shape[key], [...path, key])
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (node instanceof ZodArray) {
|
|
82
|
+
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
|
83
|
+
if (path.length > 0) {
|
|
84
|
+
envSchema[pathStr(path)] = z
|
|
85
|
+
.string()
|
|
86
|
+
.optional()
|
|
87
|
+
.refine(isJSON, { message: `Invalid JSON` })
|
|
88
|
+
.transform((val) => (val ? JSON.parse(val) : val))
|
|
89
|
+
envToDot[pathStr(path)] = dotNotation(path)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
arr.forEach((key) => {
|
|
93
|
+
walk((node as unknown as ZodArray<ZodNode>).element, [...path, String(key)])
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (node instanceof ZodString || node instanceof ZodNumber || node instanceof ZodEnum) {
|
|
100
|
+
envSchema[pathStr(path)] = node.optional()
|
|
101
|
+
envToDot[pathStr(path)] = dotNotation(path)
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (node instanceof ZodBoolean) {
|
|
106
|
+
envSchema[pathStr(path)] = z
|
|
107
|
+
.enum(['true', '1', 'false', '0'])
|
|
108
|
+
.optional()
|
|
109
|
+
.transform((v) => {
|
|
110
|
+
if (v === 'true' || v === '1') return true
|
|
111
|
+
if (v === 'false' || v === '0') return false
|
|
112
|
+
return v
|
|
113
|
+
})
|
|
114
|
+
envToDot[pathStr(path)] = dotNotation(path)
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
throw Error(
|
|
119
|
+
`[@graphcommerce/next-config] Unknown type in schema ${node.constructor.name}. This is probably a bug please create an issue.`,
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
walk(schema)
|
|
123
|
+
|
|
124
|
+
return [z.object(envSchema), envToDot] as const
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export type ApplyResultItem = {
|
|
128
|
+
envVar: string
|
|
129
|
+
envValue: unknown
|
|
130
|
+
dotVar?: string | undefined
|
|
131
|
+
from?: unknown
|
|
132
|
+
to?: unknown
|
|
133
|
+
error?: string[]
|
|
134
|
+
warning?: string[]
|
|
135
|
+
}
|
|
136
|
+
export type ApplyResult = ApplyResultItem[]
|
|
137
|
+
|
|
138
|
+
export function mergeEnvIntoConfig(
|
|
139
|
+
schema: ZodNode,
|
|
140
|
+
config: Record<string, unknown>,
|
|
141
|
+
env: Record<string, string | undefined>,
|
|
142
|
+
) {
|
|
143
|
+
const filterEnv = Object.fromEntries(Object.entries(env).filter(([key]) => key.startsWith('GC_')))
|
|
144
|
+
|
|
145
|
+
const newConfig = cloneDeep(config)
|
|
146
|
+
const [envSchema, envToDot] = configToEnvSchema(schema)
|
|
147
|
+
const result = envSchema.safeParse(filterEnv)
|
|
148
|
+
|
|
149
|
+
const applyResult: ApplyResult = []
|
|
150
|
+
|
|
151
|
+
if (!result.success) {
|
|
152
|
+
Object.entries(result.error.flatten().fieldErrors).forEach(([envVar, error]) => {
|
|
153
|
+
const dotVar = envToDot[envVar]
|
|
154
|
+
const envValue = filterEnv[envVar]
|
|
155
|
+
applyResult.push({ envVar, envValue, dotVar, error })
|
|
156
|
+
})
|
|
157
|
+
return [undefined, applyResult] as const
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
Object.entries(result.data).forEach(([envVar, value]) => {
|
|
161
|
+
const dotVar = envToDot[envVar]
|
|
162
|
+
const envValue = filterEnv[envVar]
|
|
163
|
+
|
|
164
|
+
if (!dotVar) {
|
|
165
|
+
applyResult.push({ envVar, envValue })
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const dotValue = get(newConfig, dotVar)
|
|
170
|
+
const merged = mergeDeep(dotValue, value)
|
|
171
|
+
|
|
172
|
+
const from = diff(merged, dotValue)
|
|
173
|
+
const to = diff(dotValue, merged)
|
|
174
|
+
|
|
175
|
+
applyResult.push({ envVar, envValue, dotVar, from, to })
|
|
176
|
+
set(newConfig, dotVar, merged)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
return [newConfig, applyResult] as const
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Prints the applied env variables to the console
|
|
184
|
+
*
|
|
185
|
+
* The format is:
|
|
186
|
+
*
|
|
187
|
+
* - If from and to is empty, the value is unchanged: `=` (white)
|
|
188
|
+
* - If the from is empty, a new value is applied: `+` (green)
|
|
189
|
+
* - If the to is empty, a value is removed: `-` (red)
|
|
190
|
+
* - If both from and to is not empty, a value is changed: `~` (yellow)
|
|
191
|
+
*/
|
|
192
|
+
export function formatAppliedEnv(applyResult: ApplyResult) {
|
|
193
|
+
let hasError = false
|
|
194
|
+
let hasWarning = false
|
|
195
|
+
const lines = applyResult.map(({ from, to, envValue, envVar, dotVar, error, warning }) => {
|
|
196
|
+
const fromFmt = chalk.red(JSON.stringify(from))
|
|
197
|
+
const toFmt = chalk.green(JSON.stringify(to))
|
|
198
|
+
const envVariableFmt = `${envVar}='${envValue}'`
|
|
199
|
+
const dotVariableFmt = chalk.bold.underline(`${dotVar}`)
|
|
200
|
+
|
|
201
|
+
const baseLog = `${envVariableFmt} => ${dotVariableFmt}`
|
|
202
|
+
|
|
203
|
+
if (error) {
|
|
204
|
+
hasError = true
|
|
205
|
+
return `${chalk.red(` ⨉ ${envVariableFmt}`)} => ${error.join(', ')}`
|
|
206
|
+
}
|
|
207
|
+
if (warning) {
|
|
208
|
+
hasWarning = true
|
|
209
|
+
return `${chalk.yellowBright(` ‼ ${envVariableFmt}`)} => ${warning.join(', ')}`
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (!dotVar) return chalk.red(`${envVariableFmt} => ignored (no matching config)`)
|
|
213
|
+
|
|
214
|
+
if (from === undefined && to === undefined)
|
|
215
|
+
return ` = ${baseLog}: (ignored, no change/wrong format)`
|
|
216
|
+
if (from === undefined && to !== undefined) return ` ${chalk.green('+')} ${baseLog}: ${toFmt}`
|
|
217
|
+
if (from !== undefined && to === undefined) return ` ${chalk.red('-')} ${baseLog}: ${fromFmt}`
|
|
218
|
+
return ` ${chalk.yellowBright('~')} ${baseLog}: ${fromFmt} => ${toFmt}`
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
let header = chalk.blueBright(`info`)
|
|
222
|
+
if (hasWarning) header = chalk.yellowBright(`warning`)
|
|
223
|
+
if (hasError) header = chalk.yellowBright(`error`)
|
|
224
|
+
|
|
225
|
+
header += ` - Loaded GraphCommerce env variables`
|
|
226
|
+
|
|
227
|
+
return [header, ...lines].join('\n')
|
|
228
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import cloneDeep from 'lodash/cloneDeep'
|
|
2
|
+
import { GraphCommerceConfig } from '../../generated/config'
|
|
3
|
+
import { ApplyResult, mergeEnvIntoConfig, ZodNode } from './mergeEnvIntoConfig'
|
|
4
|
+
|
|
5
|
+
export function rewriteLegacyEnv(
|
|
6
|
+
schema: ZodNode,
|
|
7
|
+
config: GraphCommerceConfig,
|
|
8
|
+
env: Record<string, string | undefined>,
|
|
9
|
+
) {
|
|
10
|
+
const clonedEnv: Record<string, string | undefined> = cloneDeep(env)
|
|
11
|
+
const applied: ApplyResult = []
|
|
12
|
+
|
|
13
|
+
function renamedTo(to: string) {
|
|
14
|
+
return (envVar: string, envValue: string) => {
|
|
15
|
+
applied.push({
|
|
16
|
+
warning: [`should be renamed to ${to}='${envValue}'`],
|
|
17
|
+
envVar,
|
|
18
|
+
envValue,
|
|
19
|
+
})
|
|
20
|
+
clonedEnv[to] = envValue
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function notUsed() {
|
|
25
|
+
return (envVar: string, envValue: string) => {
|
|
26
|
+
applied.push({
|
|
27
|
+
warning: [`should be removed`],
|
|
28
|
+
envVar,
|
|
29
|
+
envValue,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const parsers: Record<string, (key: string, value: string) => void> = {
|
|
35
|
+
MAGENTO_ENDPPOINT: renamedTo('GC_MAGENTO_ENDPOINT'),
|
|
36
|
+
GRAPHCMS_URL: renamedTo('GC_HYGRAPH_ENDPOINT'),
|
|
37
|
+
NEXT_PUBLIC_GRAPHQL_ENDPOINT: notUsed(),
|
|
38
|
+
IMAGE_DOMAINS: (envVar: string, envValue: string) => {
|
|
39
|
+
applied.push({
|
|
40
|
+
warning: [
|
|
41
|
+
`should be removed: will automatically add the Magento/Hygraph URL. For more advanced configurations, see: https://nextjs.org/docs/api-reference/next/image#configuration-options`,
|
|
42
|
+
],
|
|
43
|
+
envVar,
|
|
44
|
+
envValue,
|
|
45
|
+
})
|
|
46
|
+
},
|
|
47
|
+
NEXT_PUBLIC_LOCALE_STORES: (envVar: string, envValue: string) => {
|
|
48
|
+
const parsed = JSON.parse(envValue) as Record<string, string>
|
|
49
|
+
|
|
50
|
+
applied.push({
|
|
51
|
+
warning: ['env variable is is modified, rewritten to GC_I18N.'],
|
|
52
|
+
envVar,
|
|
53
|
+
envValue,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
clonedEnv.GC_I18N = JSON.stringify(
|
|
57
|
+
Object.entries(parsed).map(([locale, magentoStoreCode], index) => {
|
|
58
|
+
config.i18n[index] = { ...config.i18n[index], locale, magentoStoreCode }
|
|
59
|
+
return { locale, magentoStoreCode }
|
|
60
|
+
}),
|
|
61
|
+
)
|
|
62
|
+
},
|
|
63
|
+
NEXT_PUBLIC_SITE_URL: renamedTo('GC_CANONICAL_BASE_URL'),
|
|
64
|
+
NEXT_PUBLIC_GTM_ID: renamedTo('GC_GOOGLE_TAGMANAGER_ID'),
|
|
65
|
+
NEXT_PUBLIC_GOOGLE_ANALYTICS: (envVar: string, envValue: string) => {
|
|
66
|
+
if (envValue.startsWith('{')) {
|
|
67
|
+
const parsed = JSON.parse(envValue) as Record<string, string>
|
|
68
|
+
|
|
69
|
+
clonedEnv.GC_GOOGLE_ANALYTICS_ID = 'enabled'
|
|
70
|
+
config.i18n.forEach((i18n, index) => {
|
|
71
|
+
if (parsed[i18n.locale]) {
|
|
72
|
+
clonedEnv[`GC_I18N_${index}_GOOGLE_ANALYTICS_ID`] = parsed[i18n.locale]
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
applied.push({
|
|
77
|
+
warning: ['should be rewritten to GC_I18N_*_GOOGLE_ANALYTICS_ID'],
|
|
78
|
+
envVar,
|
|
79
|
+
envValue,
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
renamedTo('GC_GOOGLE_ANALYTICS_ID')
|
|
85
|
+
},
|
|
86
|
+
NEXT_PUBLIC_GOOGLE_RECAPTCHA_V3_SITE_KEY: renamedTo('GC_GOOGLE_RECAPTCHA_KEY'),
|
|
87
|
+
NEXT_PUBLIC_DISPLAY_INCL_TAX: (envVar: string, envValue: string) => {
|
|
88
|
+
const inclTax = envValue.split(',').map((i) => i.trim())
|
|
89
|
+
|
|
90
|
+
config.i18n.forEach((i18n, index) => {
|
|
91
|
+
if (!inclTax.includes(i18n.locale)) return null
|
|
92
|
+
clonedEnv[`GC_I18N_${index}_CART_DISPLAY_PRICES_INCL_TAX`] = '1'
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
applied.push({
|
|
96
|
+
warning: ['env variable is renamed, move to configuration: cartDisplayPricesInclTax'],
|
|
97
|
+
envVar,
|
|
98
|
+
envValue,
|
|
99
|
+
})
|
|
100
|
+
clonedEnv.GC_DISPLAY_PRICES_INCL_TAX = envValue
|
|
101
|
+
},
|
|
102
|
+
PREVIEW_SECRET: renamedTo('GC_PREVIEW_SECRET'),
|
|
103
|
+
DEMO_MAGENTO_GRAPHCOMMERCE: renamedTo('GC_DEMO_MODE'),
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (env.SKIP_MIGRATION === '1' || env.SKIP_MIGRATION === 'true') {
|
|
107
|
+
return mergeEnvIntoConfig(schema, config, clonedEnv)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
Object.entries(env).forEach(([key, value]) => {
|
|
111
|
+
if (value === undefined) return
|
|
112
|
+
try {
|
|
113
|
+
parsers[key]?.(key, value)
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.error(`Error parsing ${key}`, e)
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const [newConfig, envApplied] = mergeEnvIntoConfig(schema, config, clonedEnv)
|
|
120
|
+
return [newConfig, [...applied, ...envApplied] as ApplyResult] as const
|
|
121
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
export type Maybe<T> = T | null;
|
|
4
|
+
export type InputMaybe<T> = Maybe<T>;
|
|
5
|
+
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
|
6
|
+
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
|
|
7
|
+
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
|
|
8
|
+
/** All built-in and custom scalars, mapped to their actual values */
|
|
9
|
+
export type Scalars = {
|
|
10
|
+
ID: string;
|
|
11
|
+
String: string;
|
|
12
|
+
Boolean: boolean;
|
|
13
|
+
Int: number;
|
|
14
|
+
Float: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type DeployEnvironment =
|
|
18
|
+
/** The development environment */
|
|
19
|
+
| 'development'
|
|
20
|
+
/** The staging environment */
|
|
21
|
+
| 'preview'
|
|
22
|
+
/** The production environment */
|
|
23
|
+
| 'production';
|
|
24
|
+
|
|
25
|
+
/** Global GraphCommerce configuration can be configured in your `graphcommerce.config.js` file in the root of your project. */
|
|
26
|
+
export type GraphCommerceConfig = {
|
|
27
|
+
/**
|
|
28
|
+
* The canonical base URL is used for SEO purposes.
|
|
29
|
+
*
|
|
30
|
+
* Examples:
|
|
31
|
+
* - https://example.com
|
|
32
|
+
* - https://example.com/en
|
|
33
|
+
* - https://example.com/en-US
|
|
34
|
+
*/
|
|
35
|
+
canonicalBaseUrl: Scalars['String'];
|
|
36
|
+
/**
|
|
37
|
+
* Due to a limitation of the GraphQL API it is not possible to determine if a cart should be displayed including or excluding tax.
|
|
38
|
+
*
|
|
39
|
+
* When Magento's StoreConfig adds this value, this can be replaced.
|
|
40
|
+
*/
|
|
41
|
+
cartDisplayPricesInclTax?: InputMaybe<Scalars['Boolean']>;
|
|
42
|
+
/**
|
|
43
|
+
* Due to a limitation in the GraphQL API of Magento 2, we need to know if the
|
|
44
|
+
* customer requires email confirmation.
|
|
45
|
+
*
|
|
46
|
+
* This value should match Magento 2's configuration value for
|
|
47
|
+
* `customer/create_account/confirm` and should be removed once we can query
|
|
48
|
+
*/
|
|
49
|
+
customerRequireEmailConfirmation?: InputMaybe<Scalars['Boolean']>;
|
|
50
|
+
/** Debug configuration for GraphCommerce */
|
|
51
|
+
debug?: InputMaybe<GraphCommerceDebugConfig>;
|
|
52
|
+
/**
|
|
53
|
+
* Enables some demo specific code that is probably not useful for a project:
|
|
54
|
+
*
|
|
55
|
+
* - Adds the "BY GC" to the product list items.
|
|
56
|
+
* - Adds "dominant_color" attribute swatches to the product list items.
|
|
57
|
+
* - Creates a big list items in the product list.
|
|
58
|
+
*/
|
|
59
|
+
demoMode?: InputMaybe<Scalars['Boolean']>;
|
|
60
|
+
/**
|
|
61
|
+
* Environment GraphCommerce is deployed to.
|
|
62
|
+
*
|
|
63
|
+
* Not to be confused with NODE_ENV: which will always be 'production' when running `next build`.
|
|
64
|
+
*/
|
|
65
|
+
deployEnvironment?: InputMaybe<DeployEnvironment>;
|
|
66
|
+
/**
|
|
67
|
+
* See https://support.google.com/analytics/answer/9539598?hl=en
|
|
68
|
+
*
|
|
69
|
+
* Provide a value to enable Google Analytics for your store.
|
|
70
|
+
*
|
|
71
|
+
* To enable only for a specific locale, override the value in the i18n config.
|
|
72
|
+
*/
|
|
73
|
+
googleAnalyticsId?: InputMaybe<Scalars['String']>;
|
|
74
|
+
/**
|
|
75
|
+
* Google reCAPTCHA key, get from https://developers.google.com/recaptcha/docs/v3
|
|
76
|
+
*
|
|
77
|
+
* This value is required even if you are configuring different values for each locale.
|
|
78
|
+
*/
|
|
79
|
+
googleRecaptchaKey?: InputMaybe<Scalars['String']>;
|
|
80
|
+
/**
|
|
81
|
+
* The Google Tagmanager ID to be used on the site.
|
|
82
|
+
*
|
|
83
|
+
* This value is required even if you are configuring different values for each locale.
|
|
84
|
+
*/
|
|
85
|
+
googleTagmanagerId?: InputMaybe<Scalars['String']>;
|
|
86
|
+
/**
|
|
87
|
+
* The HyGraph endpoint.
|
|
88
|
+
*
|
|
89
|
+
* Project settings -> API Access -> High Performance Read-only Content API
|
|
90
|
+
*/
|
|
91
|
+
hygraphEndpoint: Scalars['String'];
|
|
92
|
+
/** All i18n configuration for the project */
|
|
93
|
+
i18n: Array<GraphCommerceI18nConfig>;
|
|
94
|
+
/**
|
|
95
|
+
* GraphQL Magento endpoint.
|
|
96
|
+
*
|
|
97
|
+
* Examples:
|
|
98
|
+
* - https://magento2.test/graphql
|
|
99
|
+
*/
|
|
100
|
+
magentoEndpoint: Scalars['String'];
|
|
101
|
+
/** To enable next.js' preview mode, configure the secret you'd like to use. */
|
|
102
|
+
previewSecret?: InputMaybe<Scalars['String']>;
|
|
103
|
+
/**
|
|
104
|
+
* Product filters with better UI for mobile and desktop.
|
|
105
|
+
*
|
|
106
|
+
* @experimental This is an experimental feature and may change in the future.
|
|
107
|
+
*/
|
|
108
|
+
productFiltersPro: Scalars['Boolean'];
|
|
109
|
+
/**
|
|
110
|
+
* Allow the site to be indexed by search engines.
|
|
111
|
+
* If false, the robots.txt file will be set to disallow all.
|
|
112
|
+
*/
|
|
113
|
+
robotsAllow: Scalars['Boolean'];
|
|
114
|
+
/**
|
|
115
|
+
* On older versions of GraphCommerce products would use a product type specific route.
|
|
116
|
+
*
|
|
117
|
+
* This should only be set to false if you use the /product/[url] or /product/configurable/[url] routes.
|
|
118
|
+
*/
|
|
119
|
+
singleProductRoute: Scalars['Boolean'];
|
|
120
|
+
/** Hide the wishlist functionality for guests. */
|
|
121
|
+
wishlistHideForGuests?: InputMaybe<Scalars['Boolean']>;
|
|
122
|
+
/** Ignores wether a product is already in the wishlist, makes the toggle an add only. */
|
|
123
|
+
wishlistIgnoreProductWishlistStatus?: InputMaybe<Scalars['Boolean']>;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/** Debug configuration for GraphCommerce */
|
|
127
|
+
export type GraphCommerceDebugConfig = {
|
|
128
|
+
/** Reports which plugins are enabled or disabled. */
|
|
129
|
+
pluginStatus?: InputMaybe<Scalars['Boolean']>;
|
|
130
|
+
/**
|
|
131
|
+
* Cyclic dependencies can cause memory issues and other strange bugs.
|
|
132
|
+
* This plugin will warn you when it detects a cyclic dependency.
|
|
133
|
+
*
|
|
134
|
+
* When running into memory issues, it can be useful to enable this plugin.
|
|
135
|
+
*/
|
|
136
|
+
webpackCircularDependencyPlugin?: InputMaybe<Scalars['Boolean']>;
|
|
137
|
+
/**
|
|
138
|
+
* When updating packages it can happen that the same package is included with different versions in the same project.
|
|
139
|
+
*
|
|
140
|
+
* Issues that this can cause are:
|
|
141
|
+
* - The same package is included multiple times in the bundle, increasing the bundle size.
|
|
142
|
+
* - The Typescript types of the package are not compatible with each other, causing Typescript errors.
|
|
143
|
+
*/
|
|
144
|
+
webpackDuplicatesPlugin?: InputMaybe<Scalars['Boolean']>;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/** All i18n configuration for the project */
|
|
148
|
+
export type GraphCommerceI18nConfig = {
|
|
149
|
+
/**
|
|
150
|
+
* The canonical base URL is used for SEO purposes.
|
|
151
|
+
*
|
|
152
|
+
* Examples:
|
|
153
|
+
* - https://example.com
|
|
154
|
+
* - https://example.com/en
|
|
155
|
+
* - https://example.com/en-US
|
|
156
|
+
*/
|
|
157
|
+
canonicalBaseUrl?: InputMaybe<Scalars['String']>;
|
|
158
|
+
/** Due to a limitation of the GraphQL API it is not possible to determine if a cart should be displayed including or excluding tax. */
|
|
159
|
+
cartDisplayPricesInclTax?: InputMaybe<Scalars['Boolean']>;
|
|
160
|
+
/**
|
|
161
|
+
* There can only be one entry with defaultLocale set to true.
|
|
162
|
+
* - If there are more, the first one is used.
|
|
163
|
+
* - If there is none, the first entry is used.
|
|
164
|
+
*/
|
|
165
|
+
defaultLocale?: InputMaybe<Scalars['Boolean']>;
|
|
166
|
+
/** Domain configuration, must be a domain https://tools.ietf.org/html/rfc3986 */
|
|
167
|
+
domain?: InputMaybe<Scalars['String']>;
|
|
168
|
+
/**
|
|
169
|
+
* Configure different Google Analytics IDs for different locales.
|
|
170
|
+
*
|
|
171
|
+
* To disable for a specific locale, set the value to null.
|
|
172
|
+
*/
|
|
173
|
+
googleAnalyticsId?: InputMaybe<Scalars['String']>;
|
|
174
|
+
/** Locale specific google reCAPTCHA key. */
|
|
175
|
+
googleRecaptchaKey?: InputMaybe<Scalars['String']>;
|
|
176
|
+
/** The Google Tagmanager ID to be used per locale. */
|
|
177
|
+
googleTagmanagerId?: InputMaybe<Scalars['String']>;
|
|
178
|
+
/** Add a gcms-locales header to make sure queries return in a certain language, can be an array to define fallbacks. */
|
|
179
|
+
hygraphLocales?: InputMaybe<Array<Scalars['String']>>;
|
|
180
|
+
/** Specify a custom locale for to load translations. */
|
|
181
|
+
linguiLocale?: InputMaybe<Scalars['String']>;
|
|
182
|
+
/** Must be a locale string https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers */
|
|
183
|
+
locale: Scalars['String'];
|
|
184
|
+
/**
|
|
185
|
+
* Magento store code.
|
|
186
|
+
*
|
|
187
|
+
* Stores => All Stores => [Store View] => Store View Code
|
|
188
|
+
*
|
|
189
|
+
* Examples:
|
|
190
|
+
* - default
|
|
191
|
+
* - en-us
|
|
192
|
+
* - b2b-us
|
|
193
|
+
*/
|
|
194
|
+
magentoStoreCode: Scalars['String'];
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
type Properties<T> = Required<{
|
|
199
|
+
[K in keyof T]: z.ZodType<T[K], any, T[K]>;
|
|
200
|
+
}>;
|
|
201
|
+
|
|
202
|
+
type definedNonNullAny = {};
|
|
203
|
+
|
|
204
|
+
export const isDefinedNonNullAny = (v: any): v is definedNonNullAny => v !== undefined && v !== null;
|
|
205
|
+
|
|
206
|
+
export const definedNonNullAnySchema = z.any().refine((v) => isDefinedNonNullAny(v));
|
|
207
|
+
|
|
208
|
+
export const DeployEnvironmentSchema = z.enum(['development', 'preview', 'production']);
|
|
209
|
+
|
|
210
|
+
export function GraphCommerceConfigSchema(): z.ZodObject<Properties<GraphCommerceConfig>> {
|
|
211
|
+
return z.object<Properties<GraphCommerceConfig>>({
|
|
212
|
+
canonicalBaseUrl: z.string().min(1),
|
|
213
|
+
cartDisplayPricesInclTax: z.boolean().nullish(),
|
|
214
|
+
customerRequireEmailConfirmation: z.boolean().nullish(),
|
|
215
|
+
debug: GraphCommerceDebugConfigSchema().nullish(),
|
|
216
|
+
demoMode: z.boolean().nullish(),
|
|
217
|
+
deployEnvironment: DeployEnvironmentSchema.nullish(),
|
|
218
|
+
googleAnalyticsId: z.string().nullish(),
|
|
219
|
+
googleRecaptchaKey: z.string().nullish(),
|
|
220
|
+
googleTagmanagerId: z.string().nullish(),
|
|
221
|
+
hygraphEndpoint: z.string().min(1),
|
|
222
|
+
i18n: z.array(GraphCommerceI18nConfigSchema()),
|
|
223
|
+
magentoEndpoint: z.string().min(1),
|
|
224
|
+
previewSecret: z.string().nullish(),
|
|
225
|
+
productFiltersPro: z.boolean(),
|
|
226
|
+
robotsAllow: z.boolean(),
|
|
227
|
+
singleProductRoute: z.boolean(),
|
|
228
|
+
wishlistHideForGuests: z.boolean().nullish(),
|
|
229
|
+
wishlistIgnoreProductWishlistStatus: z.boolean().nullish()
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function GraphCommerceDebugConfigSchema(): z.ZodObject<Properties<GraphCommerceDebugConfig>> {
|
|
234
|
+
return z.object<Properties<GraphCommerceDebugConfig>>({
|
|
235
|
+
pluginStatus: z.boolean().nullish(),
|
|
236
|
+
webpackCircularDependencyPlugin: z.boolean().nullish(),
|
|
237
|
+
webpackDuplicatesPlugin: z.boolean().nullish()
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function GraphCommerceI18nConfigSchema(): z.ZodObject<Properties<GraphCommerceI18nConfig>> {
|
|
242
|
+
return z.object<Properties<GraphCommerceI18nConfig>>({
|
|
243
|
+
canonicalBaseUrl: z.string().nullish(),
|
|
244
|
+
cartDisplayPricesInclTax: z.boolean().nullish(),
|
|
245
|
+
defaultLocale: z.boolean().nullish(),
|
|
246
|
+
domain: z.string().nullish(),
|
|
247
|
+
googleAnalyticsId: z.string().nullish(),
|
|
248
|
+
googleRecaptchaKey: z.string().nullish(),
|
|
249
|
+
googleTagmanagerId: z.string().nullish(),
|
|
250
|
+
hygraphLocales: z.array(z.string().min(1)).nullish(),
|
|
251
|
+
linguiLocale: z.string().nullish(),
|
|
252
|
+
locale: z.string().min(1),
|
|
253
|
+
magentoStoreCode: z.string().min(1)
|
|
254
|
+
})
|
|
255
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
import { NextConfig } from 'next/dist/server/config-shared'
|
|
2
1
|
import type React from 'react'
|
|
3
|
-
import { withGraphCommerce } from './withGraphCommerce'
|
|
4
2
|
|
|
5
3
|
export * from './utils/isMonorepo'
|
|
6
4
|
export * from './utils/resolveDependenciesSync'
|
|
7
5
|
export * from './withGraphCommerce'
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
return withGraphCommerce({ packages })
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function withYarn1Scopes(packages?: string[]): (config: NextConfig) => NextConfig {
|
|
14
|
-
return withGraphCommerce({ packages })
|
|
15
|
-
}
|
|
6
|
+
export * from './generated/config'
|
|
7
|
+
export * from './config'
|
|
16
8
|
|
|
17
9
|
export type PluginProps<P extends Record<string, unknown> = Record<string, unknown>> = P & {
|
|
18
10
|
Prev: React.FC<P>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import { Compiler } from 'webpack'
|
|
3
|
+
import { GraphCommerceConfig } from '../generated/config'
|
|
3
4
|
import { ResolveDependency, resolveDependency } from '../utils/resolveDependency'
|
|
4
5
|
import { findPlugins } from './findPlugins'
|
|
5
6
|
import { generateInterceptors, GenerateInterceptorsReturn } from './generateInterceptors'
|
|
@@ -12,10 +13,10 @@ export class InterceptorPlugin {
|
|
|
12
13
|
|
|
13
14
|
private resolveDependency: ResolveDependency
|
|
14
15
|
|
|
15
|
-
constructor() {
|
|
16
|
+
constructor(private config: GraphCommerceConfig) {
|
|
16
17
|
this.resolveDependency = resolveDependency()
|
|
17
18
|
|
|
18
|
-
this.interceptors = generateInterceptors(findPlugins(), this.resolveDependency)
|
|
19
|
+
this.interceptors = generateInterceptors(findPlugins(this.config), this.resolveDependency)
|
|
19
20
|
this.interceptorByDepependency = Object.fromEntries(
|
|
20
21
|
Object.values(this.interceptors).map((i) => [i.dependency, i]),
|
|
21
22
|
)
|
|
@@ -28,7 +29,7 @@ export class InterceptorPlugin {
|
|
|
28
29
|
|
|
29
30
|
// After the compilation has succeeded we watch all possible plugin locations.
|
|
30
31
|
compiler.hooks.afterCompile.tap('InterceptorPlugin', (compilation) => {
|
|
31
|
-
const plugins = findPlugins()
|
|
32
|
+
const plugins = findPlugins(this.config)
|
|
32
33
|
|
|
33
34
|
plugins.forEach((p) => {
|
|
34
35
|
const absoluteFilePath = `${path.join(
|