@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.
Files changed (43) hide show
  1. package/README.md +4 -9
  2. package/dist/config/generateConfig.js +49 -0
  3. package/dist/config/index.js +18 -0
  4. package/dist/config/loadConfig.js +48 -0
  5. package/dist/config/utils/configToImportMeta.js +34 -0
  6. package/dist/config/utils/diff.js +33 -0
  7. package/dist/config/utils/mergeEnvIntoConfig.js +175 -0
  8. package/dist/config/utils/rewriteLegacyEnv.js +110 -0
  9. package/dist/generated/config.js +56 -0
  10. package/dist/index.js +2 -10
  11. package/dist/interceptors/InterceptorPlugin.js +4 -3
  12. package/dist/interceptors/findPlugins.js +64 -35
  13. package/dist/interceptors/generateInterceptors.js +2 -2
  14. package/dist/interceptors/writeInterceptors.js +1 -0
  15. package/dist/utils/resolveDependency.js +5 -1
  16. package/dist/withGraphCommerce.js +85 -19
  17. package/package.json +13 -6
  18. package/src/config/generateConfig.ts +52 -0
  19. package/src/config/index.ts +13 -0
  20. package/src/config/loadConfig.ts +40 -0
  21. package/src/config/utils/configToImportMeta.ts +36 -0
  22. package/src/config/utils/diff.ts +39 -0
  23. package/src/config/utils/mergeEnvIntoConfig.ts +228 -0
  24. package/src/config/utils/rewriteLegacyEnv.ts +121 -0
  25. package/src/generated/config.ts +255 -0
  26. package/src/index.ts +2 -10
  27. package/src/interceptors/InterceptorPlugin.ts +4 -3
  28. package/src/interceptors/findPlugins.ts +73 -36
  29. package/src/interceptors/generateInterceptors.ts +4 -3
  30. package/src/interceptors/writeInterceptors.ts +1 -0
  31. package/src/utils/resolveDependency.ts +5 -1
  32. package/src/withGraphCommerce.ts +102 -30
  33. package/dist/index.d.ts +0 -10
  34. package/dist/interceptors/InterceptorPlugin.d.ts +0 -8
  35. package/dist/interceptors/findPlugins.d.ts +0 -2
  36. package/dist/interceptors/generateInterceptors.d.ts +0 -19
  37. package/dist/interceptors/writeInterceptors.d.ts +0 -2
  38. package/dist/utils/PackagesSort.d.ts +0 -3
  39. package/dist/utils/TopologicalSort.d.ts +0 -16
  40. package/dist/utils/isMonorepo.d.ts +0 -1
  41. package/dist/utils/resolveDependenciesSync.d.ts +0 -22
  42. package/dist/utils/resolveDependency.d.ts +0 -9
  43. package/dist/withGraphCommerce.d.ts +0 -6
@@ -1,63 +1,78 @@
1
+ import { Console } from 'console'
2
+ import { Transform } from 'stream'
1
3
  import { parseFileSync } from '@swc/core'
4
+ // eslint-disable-next-line import/no-extraneous-dependencies
2
5
  import glob from 'glob'
6
+ import get from 'lodash/get'
7
+ import type { Path } from 'react-hook-form'
8
+ import diff from '../config/utils/diff'
9
+ import { GraphCommerceConfig } from '../generated/config'
3
10
  import { resolveDependenciesSync } from '../utils/resolveDependenciesSync'
4
11
  import type { PluginConfig } from './generateInterceptors'
5
12
 
13
+ function table(input: any) {
14
+ // @see https://stackoverflow.com/a/67859384
15
+ const ts = new Transform({
16
+ transform(chunk, enc, cb) {
17
+ cb(null, chunk)
18
+ },
19
+ })
20
+ const logger = new Console({ stdout: ts })
21
+ logger.table(input)
22
+ const t = (ts.read() || '').toString()
23
+ let result = ''
24
+ for (const row of t.split(/[\r\n]+/)) {
25
+ let r = row.replace(/[^┬]*┬/, '┌')
26
+ r = r.replace(/^├─*┼/, '├')
27
+ r = r.replace(/│[^│]*/, '')
28
+ r = r.replace(/^└─*┴/, '└')
29
+ r = r.replace(/'/g, ' ')
30
+ result += `${r}\n`
31
+ }
32
+ console.log(result)
33
+ }
34
+
6
35
  type ParseResult = {
7
36
  component?: string
8
37
  exported?: string
9
- ifEnv?: string
38
+ ifConfig?: Path<GraphCommerceConfig>
10
39
  }
11
40
 
12
41
  function parseStructure(file: string): ParseResult {
13
42
  const ast = parseFileSync(file, { syntax: 'typescript', tsx: true })
14
- // const ast = swc.parseFileSync(file, { syntax: 'typescript' })
15
43
 
16
44
  const imports: Record<string, string> = {}
17
45
  const exports: Record<string, string> = {}
18
46
 
19
47
  ast.body.forEach((node) => {
20
- switch (node.type) {
21
- case 'ImportDeclaration':
22
- node.specifiers.forEach((s) => {
23
- if (s.type === 'ImportSpecifier') {
24
- imports[s.local.value] = node.source.value
25
- }
26
- })
27
- break
28
- case 'ExportDeclaration':
29
- switch (node.declaration.type) {
30
- case 'VariableDeclaration':
31
- node.declaration.declarations.forEach((declaration) => {
32
- if (declaration.id.type !== 'Identifier') return
33
-
34
- switch (declaration.init?.type) {
35
- case 'StringLiteral':
36
- exports[declaration.id.value] = declaration.init.value
37
- break
38
- }
39
- })
40
- break
41
- case 'FunctionDeclaration':
42
- if (node.declaration.type === 'FunctionDeclaration') {
43
- // console.log('func', node.declaration)
44
- }
45
- break
46
- default:
47
- console.log('unknown', node.declaration)
48
+ if (node.type === 'ImportDeclaration') {
49
+ node.specifiers.forEach((s) => {
50
+ if (s.type === 'ImportSpecifier') {
51
+ imports[s.local.value] = node.source.value
52
+ }
53
+ })
54
+ }
55
+ if (node.type === 'ExportDeclaration' && node.declaration.type === 'VariableDeclaration') {
56
+ node.declaration.declarations.forEach((declaration) => {
57
+ if (declaration.init?.type === 'StringLiteral' && declaration.id.type === 'Identifier') {
58
+ exports[declaration.id.value] = declaration.init.value
48
59
  }
49
- break
50
- // default:
51
- // console.log('hallo', node)
60
+ })
52
61
  }
53
62
  })
54
63
 
55
64
  return exports as ParseResult
56
65
  }
57
66
 
58
- export function findPlugins(cwd: string = process.cwd()) {
67
+ let prevlog: any
68
+
69
+ export function findPlugins(config: GraphCommerceConfig, cwd: string = process.cwd()) {
59
70
  const dependencies = resolveDependenciesSync(cwd)
60
71
 
72
+ const debug = Boolean(config.debug?.pluginStatus)
73
+
74
+ if (debug) console.time('findPlugins')
75
+
61
76
  const plugins: PluginConfig[] = []
62
77
  dependencies.forEach((dependency, path) => {
63
78
  const files = glob.sync(`${dependency}/plugins/**/*.tsx`)
@@ -65,14 +80,36 @@ export function findPlugins(cwd: string = process.cwd()) {
65
80
  try {
66
81
  const result = parseStructure(file)
67
82
  if (!result) return
68
- // if (result.ifEnv && !process.env[result.ifEnv]) return
69
83
 
70
- plugins.push({ ...result, plugin: file.replace(dependency, path).replace('.tsx', '') })
84
+ plugins.push({
85
+ plugin: file.replace(dependency, path).replace('.tsx', ''),
86
+ ...result,
87
+ enabled: !result.ifConfig || Boolean(get(config, result.ifConfig)),
88
+ })
71
89
  } catch (e) {
72
90
  console.error(`Error parsing ${file}`, e)
73
91
  }
74
92
  })
75
93
  })
76
94
 
95
+ if (debug) {
96
+ const res = diff(prevlog, plugins)
97
+ if (!prevlog) {
98
+ prevlog = res
99
+
100
+ table(
101
+ plugins.map(({ plugin, component, ifConfig, enabled, exported }) => ({
102
+ '💉': enabled ? '✅' : '❌',
103
+ Reason: `${ifConfig ? `${ifConfig}` : ''}`,
104
+ Plugin: plugin,
105
+ Target: `${exported}#${component}`,
106
+ })),
107
+ )
108
+ } else if (res) {
109
+ table(res)
110
+ }
111
+ console.timeEnd('findPlugins')
112
+ }
113
+
77
114
  return plugins
78
115
  }
@@ -4,7 +4,8 @@ export type PluginConfig = {
4
4
  component?: string
5
5
  exported?: string
6
6
  plugin: string
7
- ifEnv?: string
7
+ ifConfig?: string
8
+ enabled: boolean
8
9
  }
9
10
 
10
11
  type Plugin = ResolveDependencyReturn & {
@@ -109,8 +110,8 @@ export function generateInterceptors(
109
110
  ): GenerateInterceptorsReturn {
110
111
  // todo: Do not use reduce as we're passing the accumulator to the next iteration
111
112
  const byExportedComponent = plugins.reduce((acc, plug) => {
112
- const { exported, component } = plug
113
- if (!exported || !component) return acc
113
+ const { exported, component, enabled } = plug
114
+ if (!exported || !component || !enabled) return acc
114
115
 
115
116
  const resolved = resolve(exported)
116
117
 
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs'
2
2
  import path from 'path'
3
+ // eslint-disable-next-line import/no-extraneous-dependencies
3
4
  import glob from 'glob'
4
5
  import { resolveDependenciesSync } from '../utils/resolveDependenciesSync'
5
6
  import { GenerateInterceptorsReturn } from './generateInterceptors'
@@ -27,7 +27,11 @@ export const resolveDependency = (cwd: string = process.cwd()) => {
27
27
  const relative = dependency.replace(depCandidate, '')
28
28
 
29
29
  const rootCandidate = dependency.replace(depCandidate, root)
30
- const fromRoot = [`${rootCandidate}`, `${rootCandidate}/index`].find((location) =>
30
+ const fromRoot = [
31
+ `${rootCandidate}`,
32
+ `${rootCandidate}/index`,
33
+ `${rootCandidate}/src/index`,
34
+ ].find((location) =>
31
35
  ['ts', 'tsx'].find((extension) => fs.existsSync(`${location}.${extension}`)),
32
36
  )
33
37
  if (!fromRoot) throw Error("Can't find plugin target")
@@ -1,19 +1,110 @@
1
+ import CircularDependencyPlugin from 'circular-dependency-plugin'
2
+ import { DuplicatesPlugin } from 'inspectpack/plugin'
1
3
  import type { NextConfig } from 'next'
2
- import withTranspileModules from 'next-transpile-modules'
3
- import { DefinePlugin, Configuration } from 'webpack'
4
+ import { DomainLocale } from 'next/dist/server/config'
5
+ import { RemotePattern } from 'next/dist/shared/lib/image-config'
6
+ import { DefinePlugin, Configuration, WebpackPluginInstance } from 'webpack'
7
+ import { loadConfig } from './config/loadConfig'
8
+ import { configToImportMeta } from './config/utils/configToImportMeta'
9
+ import { GraphCommerceConfig } from './generated/config'
4
10
  import { InterceptorPlugin } from './interceptors/InterceptorPlugin'
5
11
  import { resolveDependenciesSync } from './utils/resolveDependenciesSync'
6
12
 
7
- function extendConfig(nextConfig: NextConfig, modules: string[]): NextConfig {
13
+ let graphcommerceConfig: GraphCommerceConfig
14
+
15
+ function domains(config: GraphCommerceConfig): DomainLocale[] {
16
+ return Object.values(
17
+ config.i18n.reduce((acc, loc) => {
18
+ if (!loc.domain) return acc
19
+
20
+ acc[loc.domain] = {
21
+ defaultLocale: loc.defaultLocale ? loc.locale : acc[loc.domain]?.defaultLocale,
22
+ locales: [...(acc[loc.domain]?.locales ?? []), loc.locale],
23
+ domain: loc.domain,
24
+ http: true,
25
+ } as DomainLocale
26
+
27
+ return acc
28
+ }, {} as Record<string, DomainLocale>),
29
+ )
30
+ }
31
+
32
+ function remotePatterns(url: string): RemotePattern {
33
+ const urlObj = new URL(url)
34
+ return {
35
+ hostname: urlObj.hostname,
36
+ protocol: urlObj.protocol as RemotePattern['protocol'],
37
+ port: urlObj.port,
38
+ }
39
+ }
40
+
41
+ /**
42
+ * GraphCommerce configuration: .
43
+ *
44
+ * ```ts
45
+ * const { withGraphCommerce } = require('@graphcommerce/next-config')
46
+ *
47
+ * module.exports = withGraphCommerce(nextConfig)
48
+ * ```
49
+ */
50
+ export function withGraphCommerce(nextConfig: NextConfig, cwd: string): NextConfig {
51
+ graphcommerceConfig ??= loadConfig(cwd)
52
+ const importMetaPaths = configToImportMeta(graphcommerceConfig)
53
+
54
+ const { i18n } = graphcommerceConfig
55
+
8
56
  return {
9
57
  ...nextConfig,
58
+ i18n: {
59
+ defaultLocale: i18n.find((locale) => locale.defaultLocale)?.locale ?? i18n[0].locale,
60
+ locales: i18n.map((locale) => locale.locale),
61
+ domains: [...domains(graphcommerceConfig), ...(nextConfig.i18n?.domains ?? [])],
62
+ },
63
+ images: {
64
+ domains: [
65
+ new URL(graphcommerceConfig.magentoEndpoint).hostname,
66
+ 'media.graphassets.com',
67
+ ...(nextConfig.images?.domains ?? []),
68
+ ],
69
+ },
70
+ transpilePackages: [
71
+ ...[...resolveDependenciesSync().keys()].slice(1),
72
+ ...(nextConfig.transpilePackages ?? []),
73
+ ],
10
74
  webpack: (config: Configuration, options) => {
11
75
  // Allow importing yml/yaml files for graphql-mesh
12
76
  config.module?.rules?.push({ test: /\.ya?ml$/, use: 'js-yaml-loader' })
13
77
 
78
+ if (!config.plugins) config.plugins = []
79
+
80
+ // Make import.meta.graphCommerce available for usage.
81
+ config.plugins.push(new DefinePlugin(importMetaPaths))
82
+
14
83
  // To properly properly treeshake @apollo/client we need to define the __DEV__ property
15
84
  if (!options.isServer) {
16
- config.plugins = [new DefinePlugin({ __DEV__: options.dev }), ...(config.plugins ?? [])]
85
+ config.plugins.push(new DefinePlugin({ __DEV__: options.dev }))
86
+ if (graphcommerceConfig.debug?.webpackCircularDependencyPlugin) {
87
+ config.plugins.push(
88
+ new CircularDependencyPlugin({
89
+ exclude: /readable-stream|duplexer2|node_modules\/next/,
90
+ }) as WebpackPluginInstance,
91
+ )
92
+ }
93
+ if (graphcommerceConfig.debug?.webpackDuplicatesPlugin) {
94
+ config.plugins.push(
95
+ new DuplicatesPlugin({
96
+ ignoredPackages: [
97
+ // very small
98
+ 'react-is',
99
+ // build issue
100
+ 'tslib',
101
+ // server
102
+ 'isarray',
103
+ 'readable-stream',
104
+ ],
105
+ }),
106
+ )
107
+ }
17
108
  }
18
109
 
19
110
  // @lingui .po file support
@@ -24,16 +115,12 @@ function extendConfig(nextConfig: NextConfig, modules: string[]): NextConfig {
24
115
  topLevelAwait: true,
25
116
  }
26
117
 
27
- config.snapshot = {
28
- ...(config.snapshot ?? {}),
29
- managedPaths: [new RegExp(`^(.+?[\\/]node_modules[\\/])(?!${modules.join('|')})`)],
30
- }
31
-
32
- // `config.watchOptions.ignored = ['**/.git/**', '**/node_modules/**', '**/.next/**']
33
- config.watchOptions = {
34
- ...(config.watchOptions ?? {}),
35
- ignored: ['**/.git/**', `**/node_modules/!(${modules.join('|')})**`, '**/.next/**'],
36
- }
118
+ // config.snapshot = {
119
+ // ...(config.snapshot ?? {}),
120
+ // managedPaths: [
121
+ // new RegExp(`^(.+?[\\/]node_modules[\\/])(?!${transpilePackages.join('|')})`),
122
+ // ],
123
+ // }
37
124
 
38
125
  if (!config.resolve) config.resolve = {}
39
126
  config.resolve.alias = {
@@ -45,24 +132,9 @@ function extendConfig(nextConfig: NextConfig, modules: string[]): NextConfig {
45
132
  '@mui/system': '@mui/system/modern',
46
133
  }
47
134
 
48
- config.plugins = [...(config.plugins ?? []), new InterceptorPlugin()]
135
+ config.plugins.push(new InterceptorPlugin(graphcommerceConfig))
49
136
 
50
137
  return typeof nextConfig.webpack === 'function' ? nextConfig.webpack(config, options) : config
51
138
  },
52
139
  }
53
140
  }
54
-
55
- export type GraphCommerceConfig = {
56
- /** Additional packages that should be transpiled, usually this auto generated. */
57
- packages?: string[]
58
- }
59
-
60
- export function withGraphCommerce(
61
- conf: GraphCommerceConfig = {},
62
- ): (config: NextConfig) => NextConfig {
63
- const { packages = [] } = conf
64
- const dependencies = [...resolveDependenciesSync().keys()].slice(1)
65
-
66
- const modules = [...dependencies, ...packages]
67
- return (config) => extendConfig(withTranspileModules(modules)(config), modules)
68
- }
package/dist/index.d.ts DELETED
@@ -1,10 +0,0 @@
1
- import { NextConfig } from 'next/dist/server/config-shared';
2
- import type React from 'react';
3
- export * from './utils/isMonorepo';
4
- export * from './utils/resolveDependenciesSync';
5
- export * from './withGraphCommerce';
6
- export declare function withYarn1Workspaces(packages?: string[]): (config: NextConfig) => NextConfig;
7
- export declare function withYarn1Scopes(packages?: string[]): (config: NextConfig) => NextConfig;
8
- export type PluginProps<P extends Record<string, unknown> = Record<string, unknown>> = P & {
9
- Prev: React.FC<P>;
10
- };
@@ -1,8 +0,0 @@
1
- import { Compiler } from 'webpack';
2
- export declare class InterceptorPlugin {
3
- private interceptors;
4
- private interceptorByDepependency;
5
- private resolveDependency;
6
- constructor();
7
- apply(compiler: Compiler): void;
8
- }
@@ -1,2 +0,0 @@
1
- import type { PluginConfig } from './generateInterceptors';
2
- export declare function findPlugins(cwd?: string): PluginConfig[];
@@ -1,19 +0,0 @@
1
- import { ResolveDependency, ResolveDependencyReturn } from '../utils/resolveDependency';
2
- export type PluginConfig = {
3
- component?: string;
4
- exported?: string;
5
- plugin: string;
6
- ifEnv?: string;
7
- };
8
- type Plugin = ResolveDependencyReturn & {
9
- components: Record<string, PluginConfig[]>;
10
- target: string;
11
- template?: string;
12
- };
13
- export type MaterializedPlugin = Plugin & {
14
- template: string;
15
- };
16
- export declare function generateInterceptor(plugin: Plugin): MaterializedPlugin;
17
- export type GenerateInterceptorsReturn = Record<string, MaterializedPlugin>;
18
- export declare function generateInterceptors(plugins: PluginConfig[], resolve: ResolveDependency): GenerateInterceptorsReturn;
19
- export {};
@@ -1,2 +0,0 @@
1
- import { GenerateInterceptorsReturn } from './generateInterceptors';
2
- export declare function writeInterceptors(interceptors: GenerateInterceptorsReturn, cwd?: string): void;
@@ -1,3 +0,0 @@
1
- import { TopologicalSort } from './TopologicalSort';
2
- export declare class PackagesSort extends TopologicalSort<string, string> {
3
- }
@@ -1,16 +0,0 @@
1
- export interface INodeWithChildren<KeyType, ValueType> {
2
- children: InternalNodesMap<KeyType, ValueType>;
3
- node: ValueType;
4
- }
5
- export type InternalNodesMap<KeyType, ValueType> = Map<KeyType, INodeWithChildren<KeyType, ValueType>>;
6
- export declare class TopologicalSort<KeyType, ValueType> {
7
- #private;
8
- constructor(nodes: Map<KeyType, ValueType>);
9
- addNode(key: KeyType, node: ValueType): this;
10
- addNodes(nodes: Map<KeyType, ValueType>): void;
11
- addEdge(fromKey: KeyType, toKey: KeyType): void;
12
- sort(): Map<KeyType, INodeWithChildren<KeyType, ValueType>>;
13
- private exploreNode;
14
- private addInternalNode;
15
- private addMultipleInternalNodes;
16
- }
@@ -1 +0,0 @@
1
- export declare function isMonorepo(): boolean;
@@ -1,22 +0,0 @@
1
- type PackageNames = Map<string, string>;
2
- type DependencyStructure = Record<string, {
3
- dirName: string;
4
- dependencies: string[];
5
- }>;
6
- /**
7
- * We're sorting all dependencies topologically
8
- *
9
- * This can detect dependency cycles and throw an error
10
- */
11
- export declare function sortDependencies(dependencyStructure: DependencyStructure): PackageNames;
12
- /**
13
- * This will return a list of all dependencies that have `graphcommerce` in the name, matching:
14
- *
15
- * - `@graphcommerce/package-name`
16
- * - `@mycompany/graphcommerce-my-feature`
17
- *
18
- * It will traverse children until it finds a package that doesn't contain graphcommerce in the name
19
- * and stop there, not checking children.
20
- */
21
- export declare function resolveDependenciesSync(root?: string): PackageNames;
22
- export {};
@@ -1,9 +0,0 @@
1
- export type ResolveDependencyReturn = {
2
- dependency: string;
3
- denormalized: string;
4
- root: string;
5
- fromRoot: string;
6
- fromModule: string;
7
- };
8
- export type ResolveDependency = (req: string) => ResolveDependencyReturn;
9
- export declare const resolveDependency: (cwd?: string) => (dependency: string) => ResolveDependencyReturn;
@@ -1,6 +0,0 @@
1
- import type { NextConfig } from 'next';
2
- export type GraphCommerceConfig = {
3
- /** Additional packages that should be transpiled, usually this auto generated. */
4
- packages?: string[];
5
- };
6
- export declare function withGraphCommerce(conf?: GraphCommerceConfig): (config: NextConfig) => NextConfig;