@graphcommerce/next-config 8.1.0-canary.2 → 8.1.0-canary.5
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/CHANGELOG.md +133 -1
- package/Config.graphqls +4 -2
- package/__tests__/config/utils/__snapshots__/mergeEnvIntoConfig.ts.snap +19 -2
- package/__tests__/config/utils/replaceConfigInString.ts +4 -0
- package/__tests__/interceptors/findPlugins.ts +473 -113
- package/__tests__/interceptors/generateInterceptors.ts +610 -322
- package/__tests__/interceptors/parseStructure.ts +158 -0
- package/__tests__/interceptors/writeInterceptors.ts +23 -14
- package/__tests__/utils/resolveDependenciesSync.ts +28 -25
- package/dist/config/commands/generateConfig.js +5 -2
- package/dist/config/demoConfig.js +19 -4
- package/dist/generated/config.js +8 -1
- package/dist/interceptors/InterceptorPlugin.js +70 -25
- package/dist/interceptors/RenameVisitor.js +19 -0
- package/dist/interceptors/Visitor.js +1418 -0
- package/dist/interceptors/extractExports.js +201 -0
- package/dist/interceptors/findOriginalSource.js +87 -0
- package/dist/interceptors/findPlugins.js +21 -53
- package/dist/interceptors/generateInterceptor.js +200 -0
- package/dist/interceptors/generateInterceptors.js +38 -179
- package/dist/interceptors/parseStructure.js +71 -0
- package/dist/interceptors/swc.js +16 -0
- package/dist/interceptors/writeInterceptors.js +19 -10
- package/dist/utils/resolveDependency.js +27 -5
- package/dist/withGraphCommerce.js +2 -1
- package/package.json +4 -1
- package/src/config/commands/generateConfig.ts +5 -2
- package/src/config/demoConfig.ts +19 -4
- package/src/config/index.ts +4 -2
- package/src/generated/config.ts +25 -3
- package/src/index.ts +16 -6
- package/src/interceptors/InterceptorPlugin.ts +90 -32
- package/src/interceptors/RenameVisitor.ts +17 -0
- package/src/interceptors/Visitor.ts +1847 -0
- package/src/interceptors/extractExports.ts +230 -0
- package/src/interceptors/findOriginalSource.ts +105 -0
- package/src/interceptors/findPlugins.ts +36 -87
- package/src/interceptors/generateInterceptor.ts +271 -0
- package/src/interceptors/generateInterceptors.ts +67 -237
- package/src/interceptors/parseStructure.ts +82 -0
- package/src/interceptors/swc.ts +13 -0
- package/src/interceptors/writeInterceptors.ts +26 -10
- package/src/utils/resolveDependency.ts +51 -12
- package/src/withGraphCommerce.ts +2 -1
|
@@ -3,194 +3,53 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.generateInterceptors =
|
|
6
|
+
exports.generateInterceptors = void 0;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
function isMethodPluginConfig(plugin) {
|
|
21
|
-
if (!isPluginBaseConfig(plugin))
|
|
22
|
-
return false;
|
|
23
|
-
return plugin.func !== undefined;
|
|
24
|
-
}
|
|
25
|
-
exports.isMethodPluginConfig = isMethodPluginConfig;
|
|
26
|
-
function isPluginConfig(plugin) {
|
|
27
|
-
return isReactPluginConfig(plugin) || isMethodPluginConfig(plugin);
|
|
28
|
-
}
|
|
29
|
-
exports.isPluginConfig = isPluginConfig;
|
|
30
|
-
function moveRelativeDown(plugins) {
|
|
31
|
-
return [...plugins].sort((a, b) => {
|
|
32
|
-
if (a.plugin.startsWith('.') && !b.plugin.startsWith('.'))
|
|
33
|
-
return 1;
|
|
34
|
-
if (!a.plugin.startsWith('.') && b.plugin.startsWith('.'))
|
|
35
|
-
return -1;
|
|
36
|
-
return 0;
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
function generateInterceptor(interceptor, config) {
|
|
40
|
-
const { fromModule, dependency, components, funcs } = interceptor;
|
|
41
|
-
const pluginConfigs = [...Object.entries(components), ...Object.entries(funcs)]
|
|
42
|
-
.map(([, plugins]) => plugins)
|
|
43
|
-
.flat();
|
|
44
|
-
const duplicateImports = new Set();
|
|
45
|
-
const pluginImports = moveRelativeDown([...pluginConfigs].sort((a, b) => a.plugin.localeCompare(b.plugin)))
|
|
46
|
-
.map((plugin) => {
|
|
47
|
-
const { plugin: p } = plugin;
|
|
48
|
-
if (isReactPluginConfig(plugin))
|
|
49
|
-
return `import { Plugin as ${p.split('/')[p.split('/').length - 1]} } from '${p}'`;
|
|
50
|
-
return `import { plugin as ${p.split('/')[p.split('/').length - 1]} } from '${p}'`;
|
|
51
|
-
})
|
|
52
|
-
.filter((str) => {
|
|
53
|
-
if (duplicateImports.has(str))
|
|
54
|
-
return false;
|
|
55
|
-
duplicateImports.add(str);
|
|
56
|
-
return true;
|
|
57
|
-
})
|
|
58
|
-
.join('\n');
|
|
59
|
-
const imports = [
|
|
60
|
-
...Object.entries(components).map(([component]) => `${component} as ${component}Base`),
|
|
61
|
-
...Object.entries(funcs).map(([func]) => `${func} as ${func}Base`),
|
|
62
|
-
];
|
|
63
|
-
const importInjectables = imports.length > 1
|
|
64
|
-
? `import {
|
|
65
|
-
${imports.join(',\n ')},
|
|
66
|
-
} from '${fromModule}'`
|
|
67
|
-
: `import { ${imports[0]} } from '${fromModule}'`;
|
|
68
|
-
const entries = [
|
|
69
|
-
...Object.entries(components),
|
|
70
|
-
...Object.entries(funcs),
|
|
71
|
-
];
|
|
72
|
-
const pluginExports = entries
|
|
73
|
-
.map(([base, plugins]) => {
|
|
74
|
-
const duplicateInterceptors = new Set();
|
|
75
|
-
const name = (p) => p.plugin.split('/')[p.plugin.split('/').length - 1];
|
|
76
|
-
const filterNoDuplicate = (p) => {
|
|
77
|
-
if (duplicateInterceptors.has(name(p)))
|
|
78
|
-
return false;
|
|
79
|
-
duplicateInterceptors.add(name(p));
|
|
80
|
-
return true;
|
|
81
|
-
};
|
|
82
|
-
let carry = `${base}Base`;
|
|
83
|
-
const pluginStr = plugins
|
|
84
|
-
.reverse()
|
|
85
|
-
.filter(filterNoDuplicate)
|
|
86
|
-
.map((p) => {
|
|
87
|
-
let result;
|
|
88
|
-
if (isReactPluginConfig(p)) {
|
|
89
|
-
const wrapChain = plugins
|
|
90
|
-
.reverse()
|
|
91
|
-
.map((pl) => `<${name(pl)}/>`)
|
|
92
|
-
.join(' wrapping ');
|
|
93
|
-
const debugLog = carry === `${base}Base` && config.pluginStatus
|
|
94
|
-
? `\n logInterceptor(\`🔌 Rendering ${base} with plugin(s): ${wrapChain} wrapping <${base}/>\`)`
|
|
95
|
-
: '';
|
|
96
|
-
result = `function ${name(p)}Interceptor(props: ${base}Props) {${debugLog}
|
|
97
|
-
return <${name(p)} {...props} Prev={${carry}} />
|
|
98
|
-
}`;
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
const wrapChain = plugins
|
|
102
|
-
.reverse()
|
|
103
|
-
.map((pl) => `${name(pl)}()`)
|
|
104
|
-
.join(' wrapping ');
|
|
105
|
-
const debugLog = carry === `${base}Base` && config.pluginStatus
|
|
106
|
-
? `\n logInterceptor(\`🔌 Calling ${base} with plugin(s): ${wrapChain} wrapping ${base}()\`)`
|
|
107
|
-
: '';
|
|
108
|
-
result = `const ${name(p)}Interceptor: typeof ${base}Base = (...args) => {${debugLog}
|
|
109
|
-
return ${name(p)}(${carry}, ...args)
|
|
110
|
-
}`;
|
|
111
|
-
}
|
|
112
|
-
carry = `${name(p)}Interceptor`;
|
|
113
|
-
return result;
|
|
114
|
-
})
|
|
115
|
-
.join('\n');
|
|
116
|
-
const isComponent = plugins.every((p) => isReactPluginConfig(p));
|
|
117
|
-
if (isComponent && plugins.some((p) => isMethodPluginConfig(p))) {
|
|
118
|
-
throw new Error(`Cannot mix React and Method plugins for ${base} in ${dependency}.`);
|
|
119
|
-
}
|
|
120
|
-
return `
|
|
121
|
-
/**
|
|
122
|
-
* Interceptor for \`${isComponent ? `<${base}/>` : `${base}()`}\` with these plugins:
|
|
123
|
-
*
|
|
124
|
-
${plugins.map((p) => ` * - \`${p.plugin}\``).join('\n')}
|
|
125
|
-
*/
|
|
126
|
-
${isComponent ? `type ${base}Props = ComponentProps<typeof ${base}Base>\n\n` : ``}${pluginStr}
|
|
127
|
-
export const ${base} = ${carry}`;
|
|
128
|
-
})
|
|
129
|
-
.join('\n');
|
|
130
|
-
const logOnce = config.pluginStatus
|
|
131
|
-
? `
|
|
132
|
-
const logged: Set<string> = new Set();
|
|
133
|
-
const logInterceptor = (log: string, ...additional: unknown[]) => {
|
|
134
|
-
if (logged.has(log)) return
|
|
135
|
-
logged.add(log)
|
|
136
|
-
console.log(log, ...additional)
|
|
137
|
-
}
|
|
138
|
-
`
|
|
139
|
-
: '';
|
|
140
|
-
const componentExports = `export * from '${fromModule}'`;
|
|
141
|
-
const template = `/* This file is automatically generated for ${dependency} */
|
|
142
|
-
|
|
143
|
-
${componentExports}
|
|
144
|
-
${pluginImports}
|
|
145
|
-
import { ComponentProps } from 'react'
|
|
146
|
-
${importInjectables}
|
|
147
|
-
${logOnce}${pluginExports}
|
|
148
|
-
`;
|
|
149
|
-
return { ...interceptor, template };
|
|
150
|
-
}
|
|
151
|
-
exports.generateInterceptor = generateInterceptor;
|
|
152
|
-
function generateInterceptors(plugins, resolve, config) {
|
|
153
|
-
// todo: Do not use reduce as we're passing the accumulator to the next iteration
|
|
154
|
-
const byExportedComponent = moveRelativeDown(plugins).reduce((acc, plug) => {
|
|
155
|
-
const { exported, plugin } = plug;
|
|
156
|
-
if (!isPluginConfig(plug) || !plug.enabled)
|
|
8
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
9
|
+
const findOriginalSource_1 = require("./findOriginalSource");
|
|
10
|
+
const generateInterceptor_1 = require("./generateInterceptor");
|
|
11
|
+
async function generateInterceptors(plugins, resolve, config) {
|
|
12
|
+
const byTargetModuleAndExport = (0, generateInterceptor_1.moveRelativeDown)(plugins).reduce((acc, plug) => {
|
|
13
|
+
let { sourceModule: pluginPath } = plug;
|
|
14
|
+
if (!(0, generateInterceptor_1.isPluginConfig)(plug) || !plug.enabled)
|
|
15
|
+
return acc;
|
|
16
|
+
const result = resolve(plug.targetModule, { includeSources: true });
|
|
17
|
+
const { error, resolved } = (0, findOriginalSource_1.findOriginalSource)(plug, result, resolve);
|
|
18
|
+
if (error) {
|
|
19
|
+
console.log(error.message);
|
|
157
20
|
return acc;
|
|
158
|
-
const resolved = resolve(exported);
|
|
159
|
-
let pluginPathFromResolved = plugin;
|
|
160
|
-
if (plugin.startsWith('.')) {
|
|
161
|
-
const resolvedPlugin = resolve(plugin);
|
|
162
|
-
pluginPathFromResolved = node_path_1.default.relative(resolved.fromRoot.split('/').slice(0, -1).join('/'), resolvedPlugin.fromRoot);
|
|
163
21
|
}
|
|
164
|
-
|
|
22
|
+
const { fromRoot } = resolved;
|
|
23
|
+
if (pluginPath.startsWith('.')) {
|
|
24
|
+
const resolvedPlugin = resolve(pluginPath);
|
|
25
|
+
if (resolvedPlugin) {
|
|
26
|
+
pluginPath = node_path_1.default.relative(resolved.fromRoot.split('/').slice(0, -1).join('/'), resolvedPlugin.fromRoot);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (!acc[resolved.fromRoot]) {
|
|
165
30
|
acc[resolved.fromRoot] = {
|
|
166
31
|
...resolved,
|
|
167
32
|
target: `${resolved.fromRoot}.interceptor`,
|
|
168
|
-
|
|
169
|
-
funcs: {},
|
|
33
|
+
targetExports: {},
|
|
170
34
|
};
|
|
171
|
-
if (isReactPluginConfig(plug)) {
|
|
172
|
-
const { component } = plug;
|
|
173
|
-
if (!acc[resolved.fromRoot].components[component])
|
|
174
|
-
acc[resolved.fromRoot].components[component] = [];
|
|
175
|
-
acc[resolved.fromRoot].components[component].push({
|
|
176
|
-
...plug,
|
|
177
|
-
plugin: pluginPathFromResolved,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
if (isMethodPluginConfig(plug)) {
|
|
181
|
-
const { func } = plug;
|
|
182
|
-
if (!acc[resolved.fromRoot].funcs[func])
|
|
183
|
-
acc[resolved.fromRoot].funcs[func] = [];
|
|
184
|
-
acc[resolved.fromRoot].funcs[func].push({
|
|
185
|
-
...plug,
|
|
186
|
-
plugin: pluginPathFromResolved,
|
|
187
|
-
});
|
|
188
35
|
}
|
|
36
|
+
if (!acc[fromRoot].targetExports[plug.targetExport])
|
|
37
|
+
acc[fromRoot].targetExports[plug.targetExport] = [];
|
|
38
|
+
acc[fromRoot].targetExports[plug.targetExport].push({ ...plug, sourceModule: pluginPath });
|
|
189
39
|
return acc;
|
|
190
40
|
}, {});
|
|
191
|
-
return Object.fromEntries(Object.entries(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
41
|
+
return Object.fromEntries(await Promise.all(Object.entries(byTargetModuleAndExport).map(async ([target, interceptor]) => {
|
|
42
|
+
const file = `${interceptor.fromRoot}.interceptor.tsx`;
|
|
43
|
+
const originalSource = (await promises_1.default
|
|
44
|
+
.access(file, promises_1.default.constants.F_OK)
|
|
45
|
+
.then(() => true)
|
|
46
|
+
.catch(() => false))
|
|
47
|
+
? (await promises_1.default.readFile(file)).toString()
|
|
48
|
+
: undefined;
|
|
49
|
+
return [
|
|
50
|
+
target,
|
|
51
|
+
await (0, generateInterceptor_1.generateInterceptor)(interceptor, config ?? {}, originalSource),
|
|
52
|
+
];
|
|
53
|
+
})));
|
|
195
54
|
}
|
|
196
55
|
exports.generateInterceptors = generateInterceptors;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseStructure = void 0;
|
|
7
|
+
const get_1 = __importDefault(require("lodash/get"));
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
const extractExports_1 = require("./extractExports");
|
|
10
|
+
const pluginConfigParsed = zod_1.z.object({
|
|
11
|
+
type: zod_1.z.enum(['component', 'function', 'replace']),
|
|
12
|
+
module: zod_1.z.string(),
|
|
13
|
+
export: zod_1.z.string(),
|
|
14
|
+
ifConfig: zod_1.z.union([zod_1.z.string(), zod_1.z.tuple([zod_1.z.string(), zod_1.z.string()])]).optional(),
|
|
15
|
+
});
|
|
16
|
+
function nonNullable(value) {
|
|
17
|
+
return value !== null && value !== undefined;
|
|
18
|
+
}
|
|
19
|
+
const isObject = (input) => typeof input === 'object' && input !== null && !Array.isArray(input);
|
|
20
|
+
function parseStructure(ast, gcConfig, sourceModule) {
|
|
21
|
+
const [exports, errors] = (0, extractExports_1.extractExports)(ast);
|
|
22
|
+
if (errors.length)
|
|
23
|
+
console.error(`Plugin error for`, errors.join('\n'));
|
|
24
|
+
const { config: moduleConfig, component, func, exported, ifConfig, plugin, Plugin, ...rest } = exports;
|
|
25
|
+
const exportVals = Object.keys(rest);
|
|
26
|
+
if (component && !moduleConfig)
|
|
27
|
+
exportVals.push('Plugin');
|
|
28
|
+
if (func && !moduleConfig)
|
|
29
|
+
exportVals.push('plugin');
|
|
30
|
+
return exportVals
|
|
31
|
+
.map((exportVal) => {
|
|
32
|
+
let config = isObject(moduleConfig) ? moduleConfig : {};
|
|
33
|
+
if (!moduleConfig && component) {
|
|
34
|
+
config = { type: 'component', module: exported, ifConfig, export: 'Plugin' };
|
|
35
|
+
}
|
|
36
|
+
else if (!moduleConfig && func) {
|
|
37
|
+
config = { type: 'function', module: exported, ifConfig, export: 'plugin' };
|
|
38
|
+
}
|
|
39
|
+
else if (isObject(moduleConfig)) {
|
|
40
|
+
config = { ...moduleConfig, export: exportVal };
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.error(`Plugin configuration invalid! See ${sourceModule}`);
|
|
44
|
+
}
|
|
45
|
+
const parsed = pluginConfigParsed.safeParse(config);
|
|
46
|
+
if (!parsed.success) {
|
|
47
|
+
if (errors.length)
|
|
48
|
+
console.error(parsed.error.errors.map((e) => `${e.path} ${e.message}`).join('\n'));
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
let enabled = true;
|
|
52
|
+
if (parsed.data.ifConfig) {
|
|
53
|
+
enabled = Array.isArray(parsed.data.ifConfig)
|
|
54
|
+
? (0, get_1.default)(gcConfig, parsed.data.ifConfig[0]) === parsed.data.ifConfig[1]
|
|
55
|
+
: Boolean((0, get_1.default)(gcConfig, parsed.data.ifConfig));
|
|
56
|
+
}
|
|
57
|
+
const val = {
|
|
58
|
+
targetExport: exports.component || exports.func || parsed.data.export,
|
|
59
|
+
sourceModule,
|
|
60
|
+
sourceExport: parsed.data.export,
|
|
61
|
+
targetModule: parsed.data.module,
|
|
62
|
+
type: parsed.data.type,
|
|
63
|
+
enabled,
|
|
64
|
+
};
|
|
65
|
+
if (parsed.data.ifConfig)
|
|
66
|
+
val.ifConfig = parsed.data.ifConfig;
|
|
67
|
+
return val;
|
|
68
|
+
})
|
|
69
|
+
.filter(nonNullable);
|
|
70
|
+
}
|
|
71
|
+
exports.parseStructure = parseStructure;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printSync = exports.parseSync = void 0;
|
|
4
|
+
const core_1 = require("@swc/core");
|
|
5
|
+
function parseSync(src) {
|
|
6
|
+
return (0, core_1.parseSync)(src, {
|
|
7
|
+
syntax: 'typescript',
|
|
8
|
+
tsx: true,
|
|
9
|
+
comments: true,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
exports.parseSync = parseSync;
|
|
13
|
+
function printSync(m) {
|
|
14
|
+
return (0, core_1.printSync)(m);
|
|
15
|
+
}
|
|
16
|
+
exports.printSync = printSync;
|
|
@@ -4,20 +4,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.writeInterceptors = void 0;
|
|
7
|
-
const
|
|
7
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
10
|
-
const glob_1 =
|
|
10
|
+
const glob_1 = require("glob");
|
|
11
11
|
const resolveDependenciesSync_1 = require("../utils/resolveDependenciesSync");
|
|
12
|
-
function
|
|
12
|
+
function checkFileExists(file) {
|
|
13
|
+
return promises_1.default
|
|
14
|
+
.access(file, promises_1.default.constants.F_OK)
|
|
15
|
+
.then(() => true)
|
|
16
|
+
.catch(() => false);
|
|
17
|
+
}
|
|
18
|
+
async function writeInterceptors(interceptors, cwd = process.cwd()) {
|
|
13
19
|
const dependencies = (0, resolveDependenciesSync_1.resolveDependenciesSync)(cwd);
|
|
14
20
|
const existing = [];
|
|
15
21
|
dependencies.forEach((dependency) => {
|
|
16
|
-
const files = glob_1.
|
|
22
|
+
const files = (0, glob_1.sync)([`${dependency}/**/*.interceptor.tsx`, `${dependency}/**/*.interceptor.ts`], { cwd });
|
|
17
23
|
existing.push(...files);
|
|
18
24
|
});
|
|
19
|
-
Object.entries(interceptors).
|
|
20
|
-
const
|
|
25
|
+
const written = Object.entries(interceptors).map(async ([, plugin]) => {
|
|
26
|
+
const extension = plugin.sourcePath.endsWith('.tsx') ? '.tsx' : '.ts';
|
|
27
|
+
const relativeFile = `${plugin.fromRoot}.interceptor${extension}`;
|
|
21
28
|
if (existing.includes(relativeFile)) {
|
|
22
29
|
delete existing[existing.indexOf(relativeFile)];
|
|
23
30
|
}
|
|
@@ -25,12 +32,14 @@ function writeInterceptors(interceptors, cwd = process.cwd()) {
|
|
|
25
32
|
delete existing[existing.indexOf(`./${relativeFile}`)];
|
|
26
33
|
}
|
|
27
34
|
const fileToWrite = path_1.default.join(cwd, relativeFile);
|
|
28
|
-
const isSame =
|
|
29
|
-
|
|
35
|
+
const isSame = (await checkFileExists(fileToWrite)) &&
|
|
36
|
+
(await promises_1.default.readFile(fileToWrite, 'utf8')).toString() === plugin.template;
|
|
30
37
|
if (!isSame)
|
|
31
|
-
|
|
38
|
+
await promises_1.default.writeFile(fileToWrite, plugin.template);
|
|
32
39
|
});
|
|
33
40
|
// Cleanup unused interceptors
|
|
34
|
-
existing.
|
|
41
|
+
const cleaned = existing.map(async (file) => (await checkFileExists(file)) && (await promises_1.default.unlink(file)));
|
|
42
|
+
await Promise.all(written);
|
|
43
|
+
await Promise.all(cleaned);
|
|
35
44
|
}
|
|
36
45
|
exports.writeInterceptors = writeInterceptors;
|
|
@@ -8,9 +8,12 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
8
8
|
const resolveDependenciesSync_1 = require("./resolveDependenciesSync");
|
|
9
9
|
const resolveDependency = (cwd = process.cwd()) => {
|
|
10
10
|
const dependencies = (0, resolveDependenciesSync_1.resolveDependenciesSync)(cwd);
|
|
11
|
-
|
|
11
|
+
function resolve(dependency, options = {}) {
|
|
12
|
+
const { includeSources = false } = options;
|
|
12
13
|
let dependencyPaths = {
|
|
13
14
|
root: '.',
|
|
15
|
+
source: '',
|
|
16
|
+
sourcePath: '',
|
|
14
17
|
dependency,
|
|
15
18
|
fromRoot: dependency,
|
|
16
19
|
fromModule: dependency,
|
|
@@ -20,13 +23,23 @@ const resolveDependency = (cwd = process.cwd()) => {
|
|
|
20
23
|
if (dependency === depCandidate || dependency.startsWith(`${depCandidate}/`)) {
|
|
21
24
|
const relative = dependency.replace(depCandidate, '');
|
|
22
25
|
const rootCandidate = dependency.replace(depCandidate, root);
|
|
26
|
+
let source = '';
|
|
27
|
+
let sourcePath = '';
|
|
23
28
|
const fromRoot = [
|
|
24
29
|
`${rootCandidate}`,
|
|
25
30
|
`${rootCandidate}/index`,
|
|
26
31
|
`${rootCandidate}/src/index`,
|
|
27
|
-
].find((location) => ['ts', 'tsx'].find((extension) =>
|
|
32
|
+
].find((location) => ['ts', 'tsx'].find((extension) => {
|
|
33
|
+
const candidatePath = `${location}.${extension}`;
|
|
34
|
+
const exists = node_fs_1.default.existsSync(candidatePath);
|
|
35
|
+
if (includeSources && exists) {
|
|
36
|
+
source = node_fs_1.default.readFileSync(candidatePath, 'utf-8');
|
|
37
|
+
sourcePath = candidatePath;
|
|
38
|
+
}
|
|
39
|
+
return exists;
|
|
40
|
+
}));
|
|
28
41
|
if (!fromRoot) {
|
|
29
|
-
|
|
42
|
+
return;
|
|
30
43
|
}
|
|
31
44
|
const denormalized = fromRoot.replace(root, depCandidate);
|
|
32
45
|
let fromModule = !relative
|
|
@@ -34,10 +47,19 @@ const resolveDependency = (cwd = process.cwd()) => {
|
|
|
34
47
|
: `./${relative.split('/')[relative.split('/').length - 1]}`;
|
|
35
48
|
if (dependency.startsWith('./'))
|
|
36
49
|
fromModule = `.${relative}`;
|
|
37
|
-
dependencyPaths = {
|
|
50
|
+
dependencyPaths = {
|
|
51
|
+
root,
|
|
52
|
+
dependency,
|
|
53
|
+
denormalized,
|
|
54
|
+
fromRoot,
|
|
55
|
+
fromModule,
|
|
56
|
+
source,
|
|
57
|
+
sourcePath,
|
|
58
|
+
};
|
|
38
59
|
}
|
|
39
60
|
});
|
|
40
61
|
return dependencyPaths;
|
|
41
|
-
}
|
|
62
|
+
}
|
|
63
|
+
return resolve;
|
|
42
64
|
};
|
|
43
65
|
exports.resolveDependency = resolveDependency;
|
|
@@ -47,6 +47,7 @@ function withGraphCommerce(nextConfig, cwd) {
|
|
|
47
47
|
experimental: {
|
|
48
48
|
...nextConfig.experimental,
|
|
49
49
|
scrollRestoration: true,
|
|
50
|
+
bundlePagesExternals: true,
|
|
50
51
|
swcPlugins: [...(nextConfig.experimental?.swcPlugins ?? []), ['@lingui/swc-plugin', {}]],
|
|
51
52
|
},
|
|
52
53
|
i18n: {
|
|
@@ -145,7 +146,7 @@ function withGraphCommerce(nextConfig, cwd) {
|
|
|
145
146
|
'@mui/system': '@mui/system/modern',
|
|
146
147
|
};
|
|
147
148
|
}
|
|
148
|
-
config.plugins.push(new InterceptorPlugin_1.InterceptorPlugin(graphcommerceConfig));
|
|
149
|
+
config.plugins.push(new InterceptorPlugin_1.InterceptorPlugin(graphcommerceConfig, !options.isServer));
|
|
149
150
|
return typeof nextConfig.webpack === 'function' ? nextConfig.webpack(config, options) : config;
|
|
150
151
|
},
|
|
151
152
|
};
|
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.
|
|
5
|
+
"version": "8.1.0-canary.5",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"types": "src/index.ts",
|
|
@@ -16,10 +16,13 @@
|
|
|
16
16
|
"@lingui/loader": "4.7.0",
|
|
17
17
|
"@lingui/swc-plugin": "4.0.4",
|
|
18
18
|
"@swc/core": "1.3.104",
|
|
19
|
+
"@swc/wasm-web": "^1.4.8",
|
|
19
20
|
"circular-dependency-plugin": "^5.2.2",
|
|
21
|
+
"glob": "^10.3.10",
|
|
20
22
|
"inspectpack": "^4.7.1",
|
|
21
23
|
"js-yaml-loader": "^1.2.2",
|
|
22
24
|
"lodash": "^4.17.21",
|
|
25
|
+
"woodpile": "^0.0.5",
|
|
23
26
|
"znv": "^0.4.0",
|
|
24
27
|
"zod": "^3.22.4"
|
|
25
28
|
},
|
|
@@ -13,8 +13,11 @@ const resolve = resolveDependency()
|
|
|
13
13
|
const schemaLocations = packages.map((p) => `${p}/**/Config.graphqls`)
|
|
14
14
|
|
|
15
15
|
export async function generateConfig() {
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
const resolved = resolve('@graphcommerce/next-config')
|
|
17
|
+
if (!resolved) throw Error('Could not resolve @graphcommerce/next-config')
|
|
18
|
+
|
|
19
|
+
const targetTs = `${resolved.root}/src/generated/config.ts`
|
|
20
|
+
const targetJs = `${resolved.root}/dist/generated/config.js`
|
|
18
21
|
|
|
19
22
|
await generate({
|
|
20
23
|
silent: true,
|
package/src/config/demoConfig.ts
CHANGED
|
@@ -15,10 +15,25 @@ export const demoConfig: PartialDeep<GraphCommerceConfig, { recurseIntoArrays: t
|
|
|
15
15
|
hygraphLocales: ['nl', 'en_us'],
|
|
16
16
|
cartDisplayPricesInclTax: true,
|
|
17
17
|
},
|
|
18
|
-
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
{
|
|
19
|
+
locale: 'fr-be',
|
|
20
|
+
magentoStoreCode: 'fr_BE',
|
|
21
|
+
cartDisplayPricesInclTax: true,
|
|
22
|
+
linguiLocale: 'fr',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
locale: 'nl-be',
|
|
26
|
+
magentoStoreCode: 'nl_BE',
|
|
27
|
+
cartDisplayPricesInclTax: true,
|
|
28
|
+
linguiLocale: 'nl',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
locale: 'en-gb',
|
|
32
|
+
magentoStoreCode: 'en_GB',
|
|
33
|
+
cartDisplayPricesInclTax: true,
|
|
34
|
+
linguiLocale: 'en',
|
|
35
|
+
},
|
|
36
|
+
{ locale: 'en-ca', magentoStoreCode: 'en_CA', linguiLocale: 'en' },
|
|
22
37
|
],
|
|
23
38
|
productFiltersPro: true,
|
|
24
39
|
productFiltersLayout: 'DEFAULT',
|
package/src/config/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Path } from 'react-hook-form'
|
|
1
|
+
import type { Path, PathValue } from 'react-hook-form'
|
|
2
2
|
import { GraphCommerceConfig } from '../generated/config'
|
|
3
3
|
|
|
4
4
|
export * from './commands/generateConfig'
|
|
@@ -12,4 +12,6 @@ declare global {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export type IfConfig = Path<GraphCommerceConfig
|
|
15
|
+
export type IfConfig<P extends Path<GraphCommerceConfig> = Path<GraphCommerceConfig>> =
|
|
16
|
+
| P
|
|
17
|
+
| [P, PathValue<GraphCommerceConfig, P>]
|
package/src/generated/config.ts
CHANGED
|
@@ -20,6 +20,12 @@ export type CompareVariant =
|
|
|
20
20
|
| 'CHECKBOX'
|
|
21
21
|
| 'ICON';
|
|
22
22
|
|
|
23
|
+
/** GoogleDatalayerConfig to allow enabling certain aspects of the datalayer */
|
|
24
|
+
export type DatalayerConfig = {
|
|
25
|
+
/** Enable core web vitals tracking for GraphCommerce */
|
|
26
|
+
coreWebVitals?: InputMaybe<Scalars['Boolean']['input']>;
|
|
27
|
+
};
|
|
28
|
+
|
|
23
29
|
/**
|
|
24
30
|
* # GraphCommerce configuration system
|
|
25
31
|
*
|
|
@@ -80,7 +86,7 @@ export type CompareVariant =
|
|
|
80
86
|
*
|
|
81
87
|
* You can export configuration by running `yarn graphcommerce export-config`
|
|
82
88
|
*
|
|
83
|
-
* ## Extending the configuration in your
|
|
89
|
+
* ## Extending the configuration in your project
|
|
84
90
|
*
|
|
85
91
|
* Create a graphql/Config.graphqls file in your project and extend the GraphCommerceConfig, GraphCommerceStorefrontConfig inputs to add configuration.
|
|
86
92
|
*
|
|
@@ -159,6 +165,7 @@ export type GraphCommerceConfig = {
|
|
|
159
165
|
* `customer/create_account/confirm` and should be removed once we can query
|
|
160
166
|
*/
|
|
161
167
|
customerRequireEmailConfirmation?: InputMaybe<Scalars['Boolean']['input']>;
|
|
168
|
+
dataLayer?: InputMaybe<DatalayerConfig>;
|
|
162
169
|
/** Debug configuration for GraphCommerce */
|
|
163
170
|
debug?: InputMaybe<GraphCommerceDebugConfig>;
|
|
164
171
|
/**
|
|
@@ -358,9 +365,17 @@ export type GraphCommerceStorefrontConfig = {
|
|
|
358
365
|
googleTagmanagerId?: InputMaybe<Scalars['String']['input']>;
|
|
359
366
|
/** Add a gcms-locales header to make sure queries return in a certain language, can be an array to define fallbacks. */
|
|
360
367
|
hygraphLocales?: InputMaybe<Array<Scalars['String']['input']>>;
|
|
361
|
-
/**
|
|
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
|
+
*/
|
|
362
373
|
linguiLocale?: InputMaybe<Scalars['String']['input']>;
|
|
363
|
-
/**
|
|
374
|
+
/**
|
|
375
|
+
* Must be a [locale string](https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers) for automatic redirects to work.
|
|
376
|
+
*
|
|
377
|
+
* This value can be used as a sub-path identifier only, make sure linguiLocale is configured for each URL.
|
|
378
|
+
*/
|
|
364
379
|
locale: Scalars['String']['input'];
|
|
365
380
|
/**
|
|
366
381
|
* Magento store code.
|
|
@@ -432,6 +447,12 @@ export const ProductFiltersLayoutSchema = z.enum(['DEFAULT', 'SIDEBAR']);
|
|
|
432
447
|
|
|
433
448
|
export const SidebarGalleryPaginationVariantSchema = z.enum(['DOTS', 'THUMBNAILS_BOTTOM']);
|
|
434
449
|
|
|
450
|
+
export function DatalayerConfigSchema(): z.ZodObject<Properties<DatalayerConfig>> {
|
|
451
|
+
return z.object({
|
|
452
|
+
coreWebVitals: z.boolean().nullish()
|
|
453
|
+
})
|
|
454
|
+
}
|
|
455
|
+
|
|
435
456
|
export function GraphCommerceConfigSchema(): z.ZodObject<Properties<GraphCommerceConfig>> {
|
|
436
457
|
return z.object({
|
|
437
458
|
canonicalBaseUrl: z.string().min(1),
|
|
@@ -443,6 +464,7 @@ export function GraphCommerceConfigSchema(): z.ZodObject<Properties<GraphCommerc
|
|
|
443
464
|
crossSellsHideCartItems: z.boolean().nullish(),
|
|
444
465
|
crossSellsRedirectItems: z.boolean().nullish(),
|
|
445
466
|
customerRequireEmailConfirmation: z.boolean().nullish(),
|
|
467
|
+
dataLayer: DatalayerConfigSchema().nullish(),
|
|
446
468
|
debug: GraphCommerceDebugConfigSchema().nullish(),
|
|
447
469
|
demoMode: z.boolean().nullish(),
|
|
448
470
|
enableGuestCheckoutLogin: z.boolean().nullish(),
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import type React from 'react'
|
|
3
|
-
|
|
3
|
+
import type { Path, PathValue } from 'react-hook-form'
|
|
4
|
+
import { GraphCommerceConfig } from './generated/config'
|
|
4
5
|
export * from './utils/isMonorepo'
|
|
5
6
|
export * from './utils/resolveDependenciesSync'
|
|
6
7
|
export * from './utils/packageRoots'
|
|
@@ -13,14 +14,23 @@ export type PluginProps<P extends Record<string, unknown> = Record<string, unkno
|
|
|
13
14
|
Prev: React.FC<P>
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export type
|
|
17
|
-
T
|
|
18
|
-
|
|
19
|
-
> = (
|
|
20
|
-
props: Parameters<T>[0] & AdditionalOptionalProps & { Prev: React.FC<Parameters<T>[0]> },
|
|
17
|
+
export type FunctionPlugin<T extends (...args: any[]) => any> = (
|
|
18
|
+
prev: T,
|
|
19
|
+
...args: Parameters<T>
|
|
21
20
|
) => ReturnType<T>
|
|
22
21
|
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated use FunctionPlugin instead
|
|
24
|
+
*/
|
|
23
25
|
export type MethodPlugin<T extends (...args: any[]) => any> = (
|
|
24
26
|
prev: T,
|
|
25
27
|
...args: Parameters<T>
|
|
26
28
|
) => ReturnType<T>
|
|
29
|
+
|
|
30
|
+
export type PluginConfig<P extends Path<GraphCommerceConfig> = Path<GraphCommerceConfig>> = {
|
|
31
|
+
type: PluginType
|
|
32
|
+
module: string
|
|
33
|
+
ifConfig?: P | [P, PathValue<GraphCommerceConfig, P>]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type PluginType = 'component' | 'function' | 'replace'
|