@graphcommerce/next-config 8.1.0-canary.7 → 8.1.0-canary.9

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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/next-config",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "8.1.0-canary.7",
5
+ "version": "8.1.0-canary.9",
6
6
  "type": "commonjs",
7
7
  "main": "dist/index.js",
8
8
  "types": "src/index.ts",
@@ -1,5 +1,8 @@
1
1
  import { loadConfig } from '../loadConfig'
2
2
  import { exportConfigToEnv } from '../utils/exportConfigToEnv'
3
+ import dotenv from 'dotenv'
4
+
5
+ dotenv.config()
3
6
 
4
7
  // eslint-disable-next-line @typescript-eslint/require-await
5
8
  export async function exportConfig() {
@@ -2,10 +2,13 @@ import { writeFileSync } from 'fs'
2
2
  // eslint-disable-next-line import/no-extraneous-dependencies
3
3
  import { generate } from '@graphql-codegen/cli'
4
4
  import { transformFileSync } from '@swc/core'
5
+ import dotenv from 'dotenv'
5
6
  import { isMonorepo } from '../../utils/isMonorepo'
6
7
  import { resolveDependenciesSync } from '../../utils/resolveDependenciesSync'
7
8
  import { resolveDependency } from '../../utils/resolveDependency'
8
9
 
10
+ dotenv.config()
11
+
9
12
  const packages = [...resolveDependenciesSync().values()].filter((p) => p !== '.')
10
13
 
11
14
  const resolve = resolveDependency()
@@ -365,11 +365,7 @@ export type GraphCommerceStorefrontConfig = {
365
365
  googleTagmanagerId?: InputMaybe<Scalars['String']['input']>;
366
366
  /** Add a gcms-locales header to make sure queries return in a certain language, can be an array to define fallbacks. */
367
367
  hygraphLocales?: InputMaybe<Array<Scalars['String']['input']>>;
368
- /**
369
- * Specify a custom locale for to load translations. Must be lowercase valid locale.
370
- *
371
- * This value is also used for the Intl.
372
- */
368
+ /** Custom locale used to load the .po files. Must be a valid locale, also used for Intl functions. */
373
369
  linguiLocale?: InputMaybe<Scalars['String']['input']>;
374
370
  /**
375
371
  * Must be a [locale string](https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers) for automatic redirects to work.
@@ -388,6 +384,11 @@ export type GraphCommerceStorefrontConfig = {
388
384
  * - b2b-us
389
385
  */
390
386
  magentoStoreCode: Scalars['String']['input'];
387
+ /**
388
+ * Allow the site to be indexed by search engines.
389
+ * If false, the robots.txt file will be set to disallow all.
390
+ */
391
+ robotsAllow?: InputMaybe<Scalars['Boolean']['input']>;
391
392
  };
392
393
 
393
394
  /** Options to configure which values will be replaced when a variant is selected on the product page. */
@@ -512,7 +513,8 @@ export function GraphCommerceStorefrontConfigSchema(): z.ZodObject<Properties<Gr
512
513
  hygraphLocales: z.array(z.string().min(1)).nullish(),
513
514
  linguiLocale: z.string().nullish(),
514
515
  locale: z.string().min(1),
515
- magentoStoreCode: z.string().min(1)
516
+ magentoStoreCode: z.string().min(1),
517
+ robotsAllow: z.boolean().nullish()
516
518
  })
517
519
  }
518
520
 
package/src/index.ts CHANGED
@@ -9,6 +9,7 @@ export * from './withGraphCommerce'
9
9
  export * from './generated/config'
10
10
  export * from './config'
11
11
  export * from './runtimeCachingOptimizations'
12
+ export * from './interceptors/commands/codegenInterceptors'
12
13
 
13
14
  export type PluginProps<P extends Record<string, unknown> = Record<string, unknown>> = P & {
14
15
  Prev: React.FC<P>
@@ -0,0 +1,27 @@
1
+ import { loadConfig } from '../../config/loadConfig'
2
+ import { resolveDependency } from '../../utils/resolveDependency'
3
+ import { findPlugins } from '../findPlugins'
4
+ import { generateInterceptors } from '../generateInterceptors'
5
+ import { writeInterceptors } from '../writeInterceptors'
6
+ import dotenv from 'dotenv'
7
+
8
+ dotenv.config()
9
+
10
+ // eslint-disable-next-line @typescript-eslint/require-await
11
+ export async function codegenInterceptors() {
12
+ const conf = loadConfig(process.cwd())
13
+
14
+ const [plugins, errors] = findPlugins(conf)
15
+
16
+ const generatedInterceptors = await generateInterceptors(
17
+ plugins,
18
+ resolveDependency(),
19
+ conf.debug,
20
+ true,
21
+ )
22
+
23
+ // const generated = Date.now()
24
+ // console.log('Generated interceptors in', generated - found, 'ms')
25
+
26
+ await writeInterceptors(generatedInterceptors)
27
+ }
@@ -17,10 +17,14 @@ export function findPlugins(config: GraphCommerceConfig, cwd: string = process.c
17
17
 
18
18
  const errors: string[] = []
19
19
  const plugins: PluginConfig[] = []
20
- dependencies.forEach((dependency, path) => {
21
- const files = globSync(`${dependency}/plugins/**/*.{ts,tsx}`, { dotRelative: true })
20
+ dependencies.forEach((filePath, packageName) => {
21
+ const files = globSync(`${filePath}/plugins/**/*.{ts,tsx}`)
22
22
  files.forEach((file) => {
23
- const sourceModule = file.replace(dependency, path).replace('.tsx', '').replace('.ts', '')
23
+ let sourceModule = file.replace('.tsx', '').replace('.ts', '')
24
+ if (file.startsWith(filePath))
25
+ sourceModule = `${packageName}/${sourceModule.slice(filePath.length + 1)}`
26
+
27
+ if (packageName === '.' && !sourceModule.startsWith('.')) sourceModule = `./${sourceModule}`
24
28
 
25
29
  try {
26
30
  const ast = parseFileSync(file, { syntax: 'typescript', tsx: true })
@@ -66,24 +66,24 @@ export type Interceptor = ResolveDependencyReturn & {
66
66
 
67
67
  export type MaterializedPlugin = Interceptor & { template: string }
68
68
 
69
- export const SOURCE_START = '/** ❗️ Original (modified) source starts here **/'
70
- export const SOURCE_END = '/** ❗️ Original (modified) source ends here **/'
69
+ export const SOURCE_START = '/** Original source starts here (do not modify!): **/'
70
+ export const SOURCE_END = '/** Original source ends here (do not modify!) **/'
71
71
 
72
72
  const originalSuffix = 'Original'
73
- const sourceSuffix = 'Source'
73
+ const sourceSuffix = 'Plugin'
74
74
  const interceptorSuffix = 'Interceptor'
75
75
  const disabledSuffix = 'Disabled'
76
76
  const name = (plugin: PluginConfig) =>
77
- `${plugin.sourceExport}${plugin.sourceModule
77
+ `${plugin.sourceModule
78
78
  .split('/')
79
79
  [plugin.sourceModule.split('/').length - 1].replace(/[^a-zA-Z0-9]/g, '')}`
80
80
 
81
81
  const fileName = (plugin: PluginConfig) => `${plugin.sourceModule}#${plugin.sourceExport}`
82
82
 
83
83
  const originalName = (n: string) => `${n}${originalSuffix}`
84
- const sourceName = (n: string) => `${n}${sourceSuffix}`
84
+ const sourceName = (n: string) => `${n}`
85
85
  const interceptorName = (n: string) => `${n}${interceptorSuffix}`
86
- const interceptorPropsName = (n: string) => `${interceptorName(n)}Props`
86
+ const interceptorPropsName = (n: string) => `${n}Props`
87
87
 
88
88
  export function moveRelativeDown(plugins: PluginConfig[]) {
89
89
  return [...plugins].sort((a, b) => {
@@ -153,7 +153,12 @@ export async function generateInterceptor(
153
153
  const duplicateInterceptors = new Set()
154
154
 
155
155
  let carry = originalName(base)
156
- const carryProps: string[] = []
156
+ let carryProps: string[] = []
157
+ const pluginSee: string[] = []
158
+
159
+ pluginSee.push(
160
+ `@see {@link file://${interceptor.sourcePathRelative}} for original source file`,
161
+ )
157
162
 
158
163
  const pluginStr = plugins
159
164
  .reverse()
@@ -178,14 +183,19 @@ export async function generateInterceptor(
178
183
  carryProps.push(interceptorPropsName(name(p)))
179
184
 
180
185
  result = `type ${interceptorPropsName(name(p))} = React.ComponentProps<typeof ${sourceName(name(p))}>`
186
+
187
+ pluginSee.push(
188
+ `@see {${sourceName(name(p))}} for replacement of the original source (original source not used)`,
189
+ )
181
190
  }
182
191
 
183
192
  if (isReactPluginConfig(p)) {
184
- carryProps.push(interceptorPropsName(name(p)))
193
+ const withBraces = config.pluginStatus || process.env.NODE_ENV === 'development'
185
194
 
186
195
  result = `
187
- type ${interceptorPropsName(name(p))} = DistributedOmit<React.ComponentProps<typeof ${sourceName(name(p))}>, 'Prev'>
188
- const ${interceptorName(name(p))} = (props: ${carryProps.join(' & ')}) => {
196
+ type ${interceptorPropsName(name(p))} = ${carryProps.join(' & ')} & OmitPrev<React.ComponentProps<typeof ${sourceName(name(p))}>, 'Prev'>
197
+
198
+ const ${interceptorName(name(p))} = (props: ${interceptorPropsName(name(p))}) => ${withBraces ? `{` : '('}
189
199
  ${config.pluginStatus ? `logOnce(\`🔌 Rendering ${base} with plugin(s): ${wrapChain} wrapping <${base}/>\`)` : ''}
190
200
 
191
201
  ${
@@ -194,8 +204,11 @@ export async function generateInterceptor(
194
204
  logOnce('${fileName(p)} does not spread props to prev: <Prev {...props}/>. This will cause issues if multiple plugins are applied to this component.')`
195
205
  : ''
196
206
  }
197
- return <${sourceName(name(p))} {...props} Prev={${carry} as React.FC} />
198
- }`
207
+ ${withBraces ? `return` : ''} <${sourceName(name(p))} {...props} Prev={${carry}} />
208
+ ${withBraces ? `}` : ')'}`
209
+
210
+ carryProps = [interceptorPropsName(name(p))]
211
+ pluginSee.push(`@see {${sourceName(name(p))}} for source of applied plugin`)
199
212
  }
200
213
 
201
214
  if (isMethodPluginConfig(p)) {
@@ -203,6 +216,7 @@ export async function generateInterceptor(
203
216
  ${config.pluginStatus ? `logOnce(\`🔌 Calling ${base} with plugin(s): ${wrapChain} wrapping ${base}()\`)` : ''}
204
217
  return ${sourceName(name(p))}(${carry}, ...args)
205
218
  }`
219
+ pluginSee.push(`@see {${sourceName(name(p))}} for source of applied plugin`)
206
220
  }
207
221
 
208
222
  carry = p.type === 'replace' ? sourceName(name(p)) : interceptorName(name(p))
@@ -216,8 +230,18 @@ export async function generateInterceptor(
216
230
  throw new Error(`Cannot mix React and Method plugins for ${base} in ${dependency}.`)
217
231
  }
218
232
 
233
+ const seeString = `
234
+ /**
235
+ * Here you see the 'interceptor' that is applying all the configured plugins.
236
+ *
237
+ * This file is NOT meant to be modified directly and is auto-generated if the plugins or the original source changes.
238
+ *
239
+ ${pluginSee.map((s) => `* ${s}`).join('\n')}
240
+ */`
241
+
219
242
  if (process.env.NODE_ENV === 'development' && isComponent) {
220
243
  return `${pluginStr}
244
+ ${seeString}
221
245
  export const ${base}: typeof ${carry} = (props) => {
222
246
  return <${carry} {...props} data-plugin />
223
247
  }`
@@ -225,6 +249,7 @@ export async function generateInterceptor(
225
249
 
226
250
  return `
227
251
  ${pluginStr}
252
+ ${seeString}
228
253
  export const ${base} = ${carry}
229
254
  `
230
255
  })
@@ -247,12 +272,13 @@ export async function generateInterceptor(
247
272
  /* This file is automatically generated for ${dependency} */
248
273
  ${
249
274
  Object.values(targetExports).some((t) => t.some((p) => p.type === 'component'))
250
- ? `import type { DistributedOmit } from 'type-fest'`
275
+ ? `import type { DistributedOmit as OmitPrev } from 'type-fest'`
251
276
  : ''
252
277
  }
253
278
 
254
279
  ${pluginImports}
255
280
 
281
+ /** @see {@link file://${interceptor.sourcePathRelative}} for source of original */
256
282
  ${SOURCE_START}
257
283
  ${printSync(ast).code}
258
284
  ${SOURCE_END}
@@ -19,6 +19,7 @@ export async function generateInterceptors(
19
19
  plugins: PluginConfig[],
20
20
  resolve: ResolveDependency,
21
21
  config?: GraphCommerceDebugConfig | null | undefined,
22
+ force?: boolean,
22
23
  ): Promise<GenerateInterceptorsReturn> {
23
24
  const byTargetModuleAndExport = moveRelativeDown(plugins).reduce<Record<string, Interceptor>>(
24
25
  (acc, plug) => {
@@ -68,12 +69,14 @@ export async function generateInterceptors(
68
69
  Object.entries(byTargetModuleAndExport).map(async ([target, interceptor]) => {
69
70
  const file = `${interceptor.fromRoot}.interceptor.tsx`
70
71
 
71
- const originalSource = (await fs
72
- .access(file, fs.constants.F_OK)
73
- .then(() => true)
74
- .catch(() => false))
75
- ? (await fs.readFile(file)).toString()
76
- : undefined
72
+ const originalSource =
73
+ !force &&
74
+ (await fs
75
+ .access(file, fs.constants.F_OK)
76
+ .then(() => true)
77
+ .catch(() => false))
78
+ ? (await fs.readFile(file)).toString()
79
+ : undefined
77
80
 
78
81
  return [
79
82
  target,
@@ -12,6 +12,7 @@ function resolveRecursivePackageJson(
12
12
  dependencyPath: string,
13
13
  dependencyStructure: DependencyStructure,
14
14
  root: string,
15
+ additionalDependencies: string[] = [],
15
16
  ) {
16
17
  const isRoot = dependencyPath === root
17
18
  const fileName = require.resolve(path.join(dependencyPath, 'package.json'))
@@ -28,8 +29,9 @@ function resolveRecursivePackageJson(
28
29
  const dependencies = [
29
30
  ...new Set(
30
31
  [
31
- ...Object.keys(packageJson.dependencies ?? {}),
32
- ...Object.keys(packageJson.devDependencies ?? {}),
32
+ ...Object.keys(packageJson.dependencies ?? []),
33
+ ...Object.keys(packageJson.devDependencies ?? []),
34
+ ...additionalDependencies,
33
35
  // ...Object.keys(packageJson.peerDependencies ?? {}),
34
36
  ].filter((name) => name.includes('graphcommerce')),
35
37
  ),
@@ -77,7 +79,13 @@ export function sortDependencies(dependencyStructure: DependencyStructure): Pack
77
79
  export function resolveDependenciesSync(root = process.cwd()) {
78
80
  const cached = resolveCache.get(root)
79
81
  if (cached) return cached
80
- const dependencyStructure = resolveRecursivePackageJson(root, {}, root)
82
+
83
+ const dependencyStructure = resolveRecursivePackageJson(
84
+ root,
85
+ {},
86
+ root,
87
+ process.env.PRIVATE_ADDITIONAL_DEPENDENCIES?.split(',') ?? [],
88
+ )
81
89
 
82
90
  const sorted = sortDependencies(dependencyStructure)
83
91
  resolveCache.set(root, sorted)
@@ -11,6 +11,7 @@ export type ResolveDependencyReturn =
11
11
  fromModule: string
12
12
  source: string
13
13
  sourcePath: string
14
+ sourcePathRelative: string
14
15
  }
15
16
 
16
17
  export type ResolveDependency = (
@@ -31,6 +32,7 @@ export const resolveDependency = (cwd: string = process.cwd()) => {
31
32
  root: '.',
32
33
  source: '',
33
34
  sourcePath: '',
35
+ sourcePathRelative: '',
34
36
  dependency,
35
37
  fromRoot: dependency,
36
38
  fromModule: dependency,
@@ -73,6 +75,10 @@ export const resolveDependency = (cwd: string = process.cwd()) => {
73
75
  ? '.'
74
76
  : `./${relative.split('/')[relative.split('/').length - 1]}`
75
77
 
78
+ const sourcePathRelative = !sourcePath
79
+ ? '.'
80
+ : `./${sourcePath.split('/')[sourcePath.split('/').length - 1]}`
81
+
76
82
  if (dependency.startsWith('./')) fromModule = `.${relative}`
77
83
 
78
84
  dependencyPaths = {
@@ -83,6 +89,7 @@ export const resolveDependency = (cwd: string = process.cwd()) => {
83
89
  fromModule,
84
90
  source,
85
91
  sourcePath,
92
+ sourcePathRelative,
86
93
  }
87
94
  }
88
95
  })
@@ -121,8 +121,9 @@ export function withGraphCommerce(nextConfig: NextConfig, cwd: string): NextConf
121
121
  config.plugins.push(new DefinePlugin(importMetaPaths))
122
122
 
123
123
  // To properly properly treeshake @apollo/client we need to define the __DEV__ property
124
+ config.plugins.push(new DefinePlugin({ 'globalThis.__DEV__': options.dev }))
125
+
124
126
  if (!options.isServer) {
125
- config.plugins.push(new DefinePlugin({ __DEV__: options.dev }))
126
127
  if (graphcommerceConfig.debug?.webpackCircularDependencyPlugin) {
127
128
  config.plugins.push(
128
129
  new CircularDependencyPlugin({