@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
@@ -4,53 +4,63 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.findPlugins = void 0;
7
+ const console_1 = require("console");
8
+ const stream_1 = require("stream");
7
9
  const core_1 = require("@swc/core");
10
+ // eslint-disable-next-line import/no-extraneous-dependencies
8
11
  const glob_1 = __importDefault(require("glob"));
12
+ const get_1 = __importDefault(require("lodash/get"));
13
+ const diff_1 = __importDefault(require("../config/utils/diff"));
9
14
  const resolveDependenciesSync_1 = require("../utils/resolveDependenciesSync");
15
+ function table(input) {
16
+ // @see https://stackoverflow.com/a/67859384
17
+ const ts = new stream_1.Transform({
18
+ transform(chunk, enc, cb) {
19
+ cb(null, chunk);
20
+ },
21
+ });
22
+ const logger = new console_1.Console({ stdout: ts });
23
+ logger.table(input);
24
+ const t = (ts.read() || '').toString();
25
+ let result = '';
26
+ for (const row of t.split(/[\r\n]+/)) {
27
+ let r = row.replace(/[^┬]*┬/, '┌');
28
+ r = r.replace(/^├─*┼/, '├');
29
+ r = r.replace(/│[^│]*/, '');
30
+ r = r.replace(/^└─*┴/, '└');
31
+ r = r.replace(/'/g, ' ');
32
+ result += `${r}\n`;
33
+ }
34
+ console.log(result);
35
+ }
10
36
  function parseStructure(file) {
11
37
  const ast = (0, core_1.parseFileSync)(file, { syntax: 'typescript', tsx: true });
12
- // const ast = swc.parseFileSync(file, { syntax: 'typescript' })
13
38
  const imports = {};
14
39
  const exports = {};
15
40
  ast.body.forEach((node) => {
16
- switch (node.type) {
17
- case 'ImportDeclaration':
18
- node.specifiers.forEach((s) => {
19
- if (s.type === 'ImportSpecifier') {
20
- imports[s.local.value] = node.source.value;
21
- }
22
- });
23
- break;
24
- case 'ExportDeclaration':
25
- switch (node.declaration.type) {
26
- case 'VariableDeclaration':
27
- node.declaration.declarations.forEach((declaration) => {
28
- if (declaration.id.type !== 'Identifier')
29
- return;
30
- switch (declaration.init?.type) {
31
- case 'StringLiteral':
32
- exports[declaration.id.value] = declaration.init.value;
33
- break;
34
- }
35
- });
36
- break;
37
- case 'FunctionDeclaration':
38
- if (node.declaration.type === 'FunctionDeclaration') {
39
- // console.log('func', node.declaration)
40
- }
41
- break;
42
- default:
43
- console.log('unknown', node.declaration);
41
+ if (node.type === 'ImportDeclaration') {
42
+ node.specifiers.forEach((s) => {
43
+ if (s.type === 'ImportSpecifier') {
44
+ imports[s.local.value] = node.source.value;
45
+ }
46
+ });
47
+ }
48
+ if (node.type === 'ExportDeclaration' && node.declaration.type === 'VariableDeclaration') {
49
+ node.declaration.declarations.forEach((declaration) => {
50
+ if (declaration.init?.type === 'StringLiteral' && declaration.id.type === 'Identifier') {
51
+ exports[declaration.id.value] = declaration.init.value;
44
52
  }
45
- break;
46
- // default:
47
- // console.log('hallo', node)
53
+ });
48
54
  }
49
55
  });
50
56
  return exports;
51
57
  }
52
- function findPlugins(cwd = process.cwd()) {
58
+ let prevlog;
59
+ function findPlugins(config, cwd = process.cwd()) {
53
60
  const dependencies = (0, resolveDependenciesSync_1.resolveDependenciesSync)(cwd);
61
+ const debug = Boolean(config.debug?.pluginStatus);
62
+ if (debug)
63
+ console.time('findPlugins');
54
64
  const plugins = [];
55
65
  dependencies.forEach((dependency, path) => {
56
66
  const files = glob_1.default.sync(`${dependency}/plugins/**/*.tsx`);
@@ -59,14 +69,33 @@ function findPlugins(cwd = process.cwd()) {
59
69
  const result = parseStructure(file);
60
70
  if (!result)
61
71
  return;
62
- // if (result.ifEnv && !process.env[result.ifEnv]) return
63
- plugins.push({ ...result, plugin: file.replace(dependency, path).replace('.tsx', '') });
72
+ plugins.push({
73
+ plugin: file.replace(dependency, path).replace('.tsx', ''),
74
+ ...result,
75
+ enabled: !result.ifConfig || Boolean((0, get_1.default)(config, result.ifConfig)),
76
+ });
64
77
  }
65
78
  catch (e) {
66
79
  console.error(`Error parsing ${file}`, e);
67
80
  }
68
81
  });
69
82
  });
83
+ if (debug) {
84
+ const res = (0, diff_1.default)(prevlog, plugins);
85
+ if (!prevlog) {
86
+ prevlog = res;
87
+ table(plugins.map(({ plugin, component, ifConfig, enabled, exported }) => ({
88
+ '💉': enabled ? '✅' : '❌',
89
+ Reason: `${ifConfig ? `${ifConfig}` : ''}`,
90
+ Plugin: plugin,
91
+ Target: `${exported}#${component}`,
92
+ })));
93
+ }
94
+ else if (res) {
95
+ table(res);
96
+ }
97
+ console.timeEnd('findPlugins');
98
+ }
70
99
  return plugins;
71
100
  }
72
101
  exports.findPlugins = findPlugins;
@@ -74,8 +74,8 @@ exports.generateInterceptor = generateInterceptor;
74
74
  function generateInterceptors(plugins, resolve) {
75
75
  // todo: Do not use reduce as we're passing the accumulator to the next iteration
76
76
  const byExportedComponent = plugins.reduce((acc, plug) => {
77
- const { exported, component } = plug;
78
- if (!exported || !component)
77
+ const { exported, component, enabled } = plug;
78
+ if (!exported || !component || !enabled)
79
79
  return acc;
80
80
  const resolved = resolve(exported);
81
81
  if (!acc[resolved.fromRoot])
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.writeInterceptors = void 0;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
+ // eslint-disable-next-line import/no-extraneous-dependencies
9
10
  const glob_1 = __importDefault(require("glob"));
10
11
  const resolveDependenciesSync_1 = require("../utils/resolveDependenciesSync");
11
12
  function writeInterceptors(interceptors, cwd = process.cwd()) {
@@ -20,7 +20,11 @@ const resolveDependency = (cwd = process.cwd()) => {
20
20
  if (dependency === depCandidate || dependency.startsWith(`${depCandidate}/`)) {
21
21
  const relative = dependency.replace(depCandidate, '');
22
22
  const rootCandidate = dependency.replace(depCandidate, root);
23
- const fromRoot = [`${rootCandidate}`, `${rootCandidate}/index`].find((location) => ['ts', 'tsx'].find((extension) => node_fs_1.default.existsSync(`${location}.${extension}`)));
23
+ const fromRoot = [
24
+ `${rootCandidate}`,
25
+ `${rootCandidate}/index`,
26
+ `${rootCandidate}/src/index`,
27
+ ].find((location) => ['ts', 'tsx'].find((extension) => node_fs_1.default.existsSync(`${location}.${extension}`)));
24
28
  if (!fromRoot)
25
29
  throw Error("Can't find plugin target");
26
30
  const denormalized = fromRoot.replace(root, depCandidate);
@@ -4,19 +4,94 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.withGraphCommerce = void 0;
7
- const next_transpile_modules_1 = __importDefault(require("next-transpile-modules"));
7
+ const circular_dependency_plugin_1 = __importDefault(require("circular-dependency-plugin"));
8
+ const plugin_1 = require("inspectpack/plugin");
8
9
  const webpack_1 = require("webpack");
10
+ const loadConfig_1 = require("./config/loadConfig");
11
+ const configToImportMeta_1 = require("./config/utils/configToImportMeta");
9
12
  const InterceptorPlugin_1 = require("./interceptors/InterceptorPlugin");
10
13
  const resolveDependenciesSync_1 = require("./utils/resolveDependenciesSync");
11
- function extendConfig(nextConfig, modules) {
14
+ let graphcommerceConfig;
15
+ function domains(config) {
16
+ return Object.values(config.i18n.reduce((acc, loc) => {
17
+ if (!loc.domain)
18
+ return acc;
19
+ acc[loc.domain] = {
20
+ defaultLocale: loc.defaultLocale ? loc.locale : acc[loc.domain]?.defaultLocale,
21
+ locales: [...(acc[loc.domain]?.locales ?? []), loc.locale],
22
+ domain: loc.domain,
23
+ http: true,
24
+ };
25
+ return acc;
26
+ }, {}));
27
+ }
28
+ function remotePatterns(url) {
29
+ const urlObj = new URL(url);
30
+ return {
31
+ hostname: urlObj.hostname,
32
+ protocol: urlObj.protocol,
33
+ port: urlObj.port,
34
+ };
35
+ }
36
+ /**
37
+ * GraphCommerce configuration: .
38
+ *
39
+ * ```ts
40
+ * const { withGraphCommerce } = require('@graphcommerce/next-config')
41
+ *
42
+ * module.exports = withGraphCommerce(nextConfig)
43
+ * ```
44
+ */
45
+ function withGraphCommerce(nextConfig, cwd) {
46
+ graphcommerceConfig ??= (0, loadConfig_1.loadConfig)(cwd);
47
+ const importMetaPaths = (0, configToImportMeta_1.configToImportMeta)(graphcommerceConfig);
48
+ const { i18n } = graphcommerceConfig;
12
49
  return {
13
50
  ...nextConfig,
51
+ i18n: {
52
+ defaultLocale: i18n.find((locale) => locale.defaultLocale)?.locale ?? i18n[0].locale,
53
+ locales: i18n.map((locale) => locale.locale),
54
+ domains: [...domains(graphcommerceConfig), ...(nextConfig.i18n?.domains ?? [])],
55
+ },
56
+ images: {
57
+ domains: [
58
+ new URL(graphcommerceConfig.magentoEndpoint).hostname,
59
+ 'media.graphassets.com',
60
+ ...(nextConfig.images?.domains ?? []),
61
+ ],
62
+ },
63
+ transpilePackages: [
64
+ ...[...(0, resolveDependenciesSync_1.resolveDependenciesSync)().keys()].slice(1),
65
+ ...(nextConfig.transpilePackages ?? []),
66
+ ],
14
67
  webpack: (config, options) => {
15
68
  // Allow importing yml/yaml files for graphql-mesh
16
69
  config.module?.rules?.push({ test: /\.ya?ml$/, use: 'js-yaml-loader' });
70
+ if (!config.plugins)
71
+ config.plugins = [];
72
+ // Make import.meta.graphCommerce available for usage.
73
+ config.plugins.push(new webpack_1.DefinePlugin(importMetaPaths));
17
74
  // To properly properly treeshake @apollo/client we need to define the __DEV__ property
18
75
  if (!options.isServer) {
19
- config.plugins = [new webpack_1.DefinePlugin({ __DEV__: options.dev }), ...(config.plugins ?? [])];
76
+ config.plugins.push(new webpack_1.DefinePlugin({ __DEV__: options.dev }));
77
+ if (graphcommerceConfig.debug?.webpackCircularDependencyPlugin) {
78
+ config.plugins.push(new circular_dependency_plugin_1.default({
79
+ exclude: /readable-stream|duplexer2|node_modules\/next/,
80
+ }));
81
+ }
82
+ if (graphcommerceConfig.debug?.webpackDuplicatesPlugin) {
83
+ config.plugins.push(new plugin_1.DuplicatesPlugin({
84
+ ignoredPackages: [
85
+ // very small
86
+ 'react-is',
87
+ // build issue
88
+ 'tslib',
89
+ // server
90
+ 'isarray',
91
+ 'readable-stream',
92
+ ],
93
+ }));
94
+ }
20
95
  }
21
96
  // @lingui .po file support
22
97
  config.module?.rules?.push({ test: /\.po/, use: '@lingui/loader' });
@@ -24,15 +99,12 @@ function extendConfig(nextConfig, modules) {
24
99
  layers: true,
25
100
  topLevelAwait: true,
26
101
  };
27
- config.snapshot = {
28
- ...(config.snapshot ?? {}),
29
- managedPaths: [new RegExp(`^(.+?[\\/]node_modules[\\/])(?!${modules.join('|')})`)],
30
- };
31
- // `config.watchOptions.ignored = ['**/.git/**', '**/node_modules/**', '**/.next/**']
32
- config.watchOptions = {
33
- ...(config.watchOptions ?? {}),
34
- ignored: ['**/.git/**', `**/node_modules/!(${modules.join('|')})**`, '**/.next/**'],
35
- };
102
+ // config.snapshot = {
103
+ // ...(config.snapshot ?? {}),
104
+ // managedPaths: [
105
+ // new RegExp(`^(.+?[\\/]node_modules[\\/])(?!${transpilePackages.join('|')})`),
106
+ // ],
107
+ // }
36
108
  if (!config.resolve)
37
109
  config.resolve = {};
38
110
  config.resolve.alias = {
@@ -43,15 +115,9 @@ function extendConfig(nextConfig, modules) {
43
115
  '@mui/styled-engine': '@mui/styled-engine/modern',
44
116
  '@mui/system': '@mui/system/modern',
45
117
  };
46
- config.plugins = [...(config.plugins ?? []), new InterceptorPlugin_1.InterceptorPlugin()];
118
+ config.plugins.push(new InterceptorPlugin_1.InterceptorPlugin(graphcommerceConfig));
47
119
  return typeof nextConfig.webpack === 'function' ? nextConfig.webpack(config, options) : config;
48
120
  },
49
121
  };
50
122
  }
51
- function withGraphCommerce(conf = {}) {
52
- const { packages = [] } = conf;
53
- const dependencies = [...(0, resolveDependenciesSync_1.resolveDependenciesSync)().keys()].slice(1);
54
- const modules = [...dependencies, ...packages];
55
- return (config) => extendConfig((0, next_transpile_modules_1.default)(modules)(config), modules);
56
- }
57
123
  exports.withGraphCommerce = withGraphCommerce;
package/package.json CHANGED
@@ -2,23 +2,30 @@
2
2
  "name": "@graphcommerce/next-config",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "5.2.0-canary.9",
5
+ "version": "6.0.0-canary.20",
6
6
  "type": "commonjs",
7
7
  "main": "dist/index.js",
8
- "typings": "dist/index.d.ts",
8
+ "types": "src/index.ts",
9
9
  "scripts": {
10
10
  "dev": "tsc -W --preserveWatchOutput",
11
- "generate": "node dist/generate.js",
12
11
  "build": "tsc",
13
- "prepack": "yarn build"
12
+ "prepack": "tsc"
14
13
  },
15
14
  "dependencies": {
16
- "@lingui/loader": "^3.15.0",
15
+ "@graphql-mesh/cli": "0.82.14",
16
+ "@lingui/loader": "3.17.1",
17
17
  "@swc/core": "1.3.23",
18
+ "circular-dependency-plugin": "^5.2.2",
19
+ "inspectpack": "^4.7.1",
18
20
  "js-yaml-loader": "^1.2.2",
19
- "next-transpile-modules": "^10.0.0"
21
+ "lodash": "^4.17.21",
22
+ "znv": "^0.3.2",
23
+ "zod": "^3.20.2"
20
24
  },
21
25
  "devDependencies": {
26
+ "@types/circular-dependency-plugin": "^5.0.5",
27
+ "@types/cli-table": "^0.3.1",
28
+ "@types/lodash": "^4.14.191",
22
29
  "typescript": "4.9.4"
23
30
  },
24
31
  "peerDependencies": {
@@ -0,0 +1,52 @@
1
+ import { writeFileSync } from 'fs'
2
+ // eslint-disable-next-line import/no-extraneous-dependencies
3
+ import { generate } from '@graphql-codegen/cli'
4
+ import { transformFileSync } from '@swc/core'
5
+ import { resolveDependenciesSync } from '../utils/resolveDependenciesSync'
6
+ import { resolveDependency } from '../utils/resolveDependency'
7
+ import { isMonorepo } from '../utils/isMonorepo'
8
+
9
+ const packages = [...resolveDependenciesSync().values()].filter((p) => p !== '.')
10
+
11
+ const resolve = resolveDependency()
12
+
13
+ const schemaLocations = packages.map((p) => `${p}/**/Config.graphqls`)
14
+
15
+ export async function generateConfig() {
16
+ const targetTs = `${resolve('@graphcommerce/next-config').root}/src/generated/config.ts`
17
+ const targetJs = `${resolve('@graphcommerce/next-config').root}/dist/generated/config.js`
18
+
19
+ await generate({
20
+ silent: true,
21
+ schema: ['graphql/**/Config.graphqls', ...schemaLocations],
22
+ generates: {
23
+ [targetTs]: {
24
+ plugins: ['typescript', 'typescript-validation-schema', 'add'],
25
+ config: {
26
+ // enumsAsTypes: true,
27
+ content: '/* eslint-disable */',
28
+ schema: 'zod',
29
+ notAllowEmptyString: true,
30
+ enumsAsTypes: true,
31
+ scalarSchemas: {
32
+ Domain: 'z.string()',
33
+ DateTime: 'z.date()',
34
+ RichTextAST: 'z.object.json()',
35
+ },
36
+ },
37
+ },
38
+ ...(isMonorepo() && {
39
+ '../../docs/framework/config.md': {
40
+ plugins: ['@graphcommerce/graphql-codegen-markdown-docs'],
41
+ },
42
+ }),
43
+ },
44
+ })
45
+
46
+ const result = transformFileSync(targetTs, {
47
+ module: { type: 'commonjs' },
48
+ env: { targets: { node: '16' } },
49
+ })
50
+
51
+ writeFileSync(targetJs, result.code)
52
+ }
@@ -0,0 +1,13 @@
1
+ import type { Path } from 'react-hook-form'
2
+ import { GraphCommerceConfig } from '../generated/config'
3
+
4
+ export * from './generateConfig'
5
+ export * from './loadConfig'
6
+
7
+ declare global {
8
+ interface ImportMeta {
9
+ graphCommerce: GraphCommerceConfig
10
+ }
11
+ }
12
+
13
+ export type IfConfig = Path<GraphCommerceConfig>
@@ -0,0 +1,40 @@
1
+ // eslint-disable-next-line import/no-extraneous-dependencies
2
+ import { cosmiconfigSync } from 'cosmiconfig'
3
+ import type { GraphCommerceConfig } from '../generated/config'
4
+ import { GraphCommerceConfigSchema } from '../generated/config'
5
+ import { formatAppliedEnv } from './utils/mergeEnvIntoConfig'
6
+ import { rewriteLegacyEnv } from './utils/rewriteLegacyEnv'
7
+
8
+ export * from './utils/configToImportMeta'
9
+
10
+ const moduleName = 'graphcommerce'
11
+ const loader = cosmiconfigSync(moduleName)
12
+
13
+ export function loadConfig(cwd: string): GraphCommerceConfig {
14
+ try {
15
+ const result = loader.search(cwd)
16
+
17
+ if (!result) throw Error("Couldn't find a graphcommerce.config.(m)js in the project.")
18
+
19
+ const schema = GraphCommerceConfigSchema()
20
+ const config = schema.parse(result.config)
21
+
22
+ if (!config) throw Error("Couldn't find a graphcommerce.config.(m)js in the project.")
23
+
24
+ const [mergedConfig, applyResult] = rewriteLegacyEnv(
25
+ GraphCommerceConfigSchema(),
26
+ config,
27
+ process.env,
28
+ )
29
+
30
+ if (applyResult.length > 0) console.log(formatAppliedEnv(applyResult))
31
+
32
+ return schema.parse(mergedConfig)
33
+ } catch (error) {
34
+ if (error instanceof Error) {
35
+ console.log(error.message)
36
+ process.exit(1)
37
+ }
38
+ throw error
39
+ }
40
+ }
@@ -0,0 +1,36 @@
1
+ function flattenKeys(value: unknown, initialPathPrefix: string): Record<string, unknown> {
2
+ // Is a scalar:
3
+ if (value === null || value === undefined || typeof value === 'number') {
4
+ return { [initialPathPrefix]: value }
5
+ }
6
+
7
+ if (typeof value === 'string') {
8
+ return { [initialPathPrefix]: JSON.stringify(value) }
9
+ }
10
+
11
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
12
+ return { [initialPathPrefix]: JSON.stringify(value) }
13
+ }
14
+
15
+ if (typeof value === 'object') {
16
+ return {
17
+ [initialPathPrefix]:
18
+ process.env.NODE_ENV === 'development'
19
+ ? `{ __debug: "'${initialPathPrefix}' can not be destructured, please access deeper properties directly" }`
20
+ : '{}',
21
+ ...Object.keys(value)
22
+ .map((key) => {
23
+ const deep = (value as Record<string, unknown>)[key]
24
+ return flattenKeys(deep, `${initialPathPrefix}.${key}`)
25
+ })
26
+ .reduce((acc, path) => ({ ...acc, ...path })),
27
+ }
28
+ }
29
+
30
+ throw Error(`Unexpected value: ${value}`)
31
+ }
32
+
33
+ /** The result of this function is passed to the webpack DefinePlugin as import.meta.graphCommerce.* */
34
+ export function configToImportMeta(config: unknown) {
35
+ return flattenKeys(config, 'import.meta.graphCommerce') as Record<string, string>
36
+ }
@@ -0,0 +1,39 @@
1
+ function isObject(val: unknown): val is Record<string, unknown> {
2
+ return typeof val === 'object' && val !== null
3
+ }
4
+ function isArray(val: unknown): val is unknown[] {
5
+ return Array.isArray(val)
6
+ }
7
+
8
+ /** Simple diff function, retuns the values of the second object. */
9
+ export default function diff(item1: unknown, item2: unknown): unknown {
10
+ const item1Type = typeof item2
11
+ const item2Type = typeof item2
12
+ const isSame = item1Type === item2Type
13
+
14
+ // If the types aren't the same we always have a diff
15
+ if (!isSame) return item2
16
+
17
+ if (isArray(item1) && isArray(item2)) {
18
+ const res = item1.map((val, idx) => diff(val, item2[idx])).filter((val) => !!val)
19
+ return res.length ? res : undefined
20
+ }
21
+
22
+ if (isObject(item1) && isObject(item2)) {
23
+ const entriesRight = Object.fromEntries(
24
+ Object.entries(item1)
25
+ .map(([key, val]) => [key, diff(val, item2[key])])
26
+ .filter((entry) => !!entry[1]),
27
+ )
28
+ const entriesLeft = Object.fromEntries(
29
+ Object.entries(item2)
30
+ .map(([key, val]) => [key, diff(item1[key], val)])
31
+ .filter((entry) => !!entry[1]),
32
+ )
33
+ const entries = { ...entriesRight, ...entriesLeft } as Record<string, unknown>
34
+
35
+ return Object.keys(entries).length ? entries : undefined
36
+ }
37
+
38
+ return item2 === item1 ? undefined : item2
39
+ }