@graphcommerce/next-config 9.0.0-canary.105 → 9.0.0-canary.106
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 +6 -0
- package/dist/commands/codegen.js +18 -0
- package/dist/commands/copyFiles.js +201 -0
- package/dist/commands/copyRoutes.js +20 -0
- package/dist/index.js +3 -0
- package/dist/utils/resolveDependenciesSync.js +6 -1
- package/dist/utils/sig.js +34 -0
- package/package.json +9 -9
- package/src/commands/codegen.ts +18 -0
- package/src/commands/copyFiles.ts +227 -0
- package/src/index.ts +4 -3
- package/src/utils/resolveDependenciesSync.ts +10 -1
- package/src/utils/sig.ts +37 -0
- package/dist/config/commands/generateIntercetors.js +0 -9
- package/dist/interceptors/commands/generateIntercetors.js +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 9.0.0-canary.106
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#2385](https://github.com/graphcommerce-org/graphcommerce/pull/2385) [`3434ede`](https://github.com/graphcommerce-org/graphcommerce/commit/3434ede37855c36949711d05d13eb01906b29ad0) - Added a functionality to copy directories from packages to the project and keep them managed with GraphCommerce ([@paales](https://github.com/paales))
|
|
8
|
+
|
|
3
9
|
## 9.0.0-canary.105
|
|
4
10
|
|
|
5
11
|
## 9.0.0-canary.104
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.codegen = codegen;
|
|
4
|
+
const generateConfig_1 = require("../config/commands/generateConfig");
|
|
5
|
+
const codegenInterceptors_1 = require("../interceptors/commands/codegenInterceptors");
|
|
6
|
+
const copyFiles_1 = require("./copyFiles");
|
|
7
|
+
/** Run all code generation steps in sequence */
|
|
8
|
+
async function codegen() {
|
|
9
|
+
// Copy files from packages to project
|
|
10
|
+
console.log('🔄 Copying files from packages to project...');
|
|
11
|
+
await (0, copyFiles_1.copyFiles)();
|
|
12
|
+
// Generate GraphCommerce config types
|
|
13
|
+
console.log('⚙️ Generating GraphCommerce config types...');
|
|
14
|
+
await (0, generateConfig_1.generateConfig)();
|
|
15
|
+
// Generate interceptors
|
|
16
|
+
console.log('🔌 Generating interceptors...');
|
|
17
|
+
await (0, codegenInterceptors_1.codegenInterceptors)();
|
|
18
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
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.copyFiles = copyFiles;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const glob_1 = require("glob");
|
|
10
|
+
const resolveDependenciesSync_1 = require("../utils/resolveDependenciesSync");
|
|
11
|
+
// Add debug logging helper
|
|
12
|
+
const debug = (...args) => {
|
|
13
|
+
if (process.env.DEBUG)
|
|
14
|
+
console.log('[copyFiles]', ...args);
|
|
15
|
+
};
|
|
16
|
+
const createManagementComment = (type) => `// managed by: ${type}`;
|
|
17
|
+
const MANAGED_BY_GC = createManagementComment('graphcommerce');
|
|
18
|
+
const MANAGED_LOCALLY = createManagementComment('local');
|
|
19
|
+
const GITIGNORE_SECTION_START = '# managed by: graphcommerce';
|
|
20
|
+
const GITIGNORE_SECTION_END = '# end managed by: graphcommerce';
|
|
21
|
+
/**
|
|
22
|
+
* Updates the .gitignore file with a list of GraphCommerce managed files
|
|
23
|
+
*
|
|
24
|
+
* - Removes any existing GraphCommerce managed files section
|
|
25
|
+
* - If managedFiles is not empty, adds a new section with the files
|
|
26
|
+
* - If managedFiles is empty, just cleans up the existing section
|
|
27
|
+
* - Ensures the file ends with a newline
|
|
28
|
+
*/
|
|
29
|
+
async function updateGitignore(managedFiles) {
|
|
30
|
+
const gitignorePath = path_1.default.join(process.cwd(), '.gitignore');
|
|
31
|
+
let content;
|
|
32
|
+
debug('Updating .gitignore with managed files:', managedFiles);
|
|
33
|
+
try {
|
|
34
|
+
content = await promises_1.default.readFile(gitignorePath, 'utf-8');
|
|
35
|
+
debug('Existing .gitignore content:', content);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
debug('.gitignore not found, creating new file');
|
|
39
|
+
content = '';
|
|
40
|
+
}
|
|
41
|
+
// Remove existing GraphCommerce section if it exists
|
|
42
|
+
const sectionRegex = new RegExp(`${GITIGNORE_SECTION_START}[\\s\\S]*?${GITIGNORE_SECTION_END}\\n?`, 'g');
|
|
43
|
+
content = content.replace(sectionRegex, '');
|
|
44
|
+
debug('Content after removing existing section:', content);
|
|
45
|
+
// Only add new section if there are files to manage
|
|
46
|
+
if (managedFiles.length > 0) {
|
|
47
|
+
const newSection = [
|
|
48
|
+
GITIGNORE_SECTION_START,
|
|
49
|
+
...managedFiles.sort(),
|
|
50
|
+
GITIGNORE_SECTION_END,
|
|
51
|
+
'', // Empty line at the end
|
|
52
|
+
].join('\n');
|
|
53
|
+
debug('New section to add:', newSection);
|
|
54
|
+
// Append the new section
|
|
55
|
+
content = `${content.trim()}\n\n${newSection}`;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Just trim the content when no files to manage
|
|
59
|
+
content = `${content.trim()}\n`;
|
|
60
|
+
}
|
|
61
|
+
debug('Final content:', content);
|
|
62
|
+
try {
|
|
63
|
+
await promises_1.default.writeFile(gitignorePath, content);
|
|
64
|
+
debug('Successfully wrote .gitignore file');
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
console.error('Error writing .gitignore:', err);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** Determines how a file should be managed based on its content */
|
|
71
|
+
function getFileManagement(content) {
|
|
72
|
+
if (!content)
|
|
73
|
+
return 'graphcommerce';
|
|
74
|
+
const contentStr = content.toString();
|
|
75
|
+
if (contentStr.startsWith(MANAGED_LOCALLY))
|
|
76
|
+
return 'local';
|
|
77
|
+
if (contentStr.startsWith(MANAGED_BY_GC))
|
|
78
|
+
return 'graphcommerce';
|
|
79
|
+
return 'unmanaged';
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The packages are @graphcommerce/* packages and have special treatment.
|
|
83
|
+
*
|
|
84
|
+
* 1. Glob the `copy/**` directory for each package and generate a list of files that need to be
|
|
85
|
+
* copied. Error if a file with the same path exists in another package.
|
|
86
|
+
* 2. Copy the files to the project directory (cwd).
|
|
87
|
+
*
|
|
88
|
+
* 1. If the file doesn't exist: Create directories and the file with "managed by: graphcommerce"
|
|
89
|
+
* 2. If the file exists and starts with "managed by: local": Skip the file
|
|
90
|
+
* 3. If the file exists but doesn't have a management comment: Suggest adding "managed by: local"
|
|
91
|
+
* 4. If the file is managed by graphcommerce: Update if content differs
|
|
92
|
+
*/
|
|
93
|
+
async function copyFiles() {
|
|
94
|
+
debug('Starting copyFiles');
|
|
95
|
+
const cwd = process.cwd();
|
|
96
|
+
const deps = (0, resolveDependenciesSync_1.resolveDependenciesSync)();
|
|
97
|
+
const packages = [...deps.values()].filter((p) => p !== '.');
|
|
98
|
+
debug('Found packages:', packages);
|
|
99
|
+
// Track files and their source packages to detect conflicts
|
|
100
|
+
const fileMap = new Map();
|
|
101
|
+
// Track which files are managed by GraphCommerce
|
|
102
|
+
const managedFiles = new Set();
|
|
103
|
+
// First pass: collect all files and check for conflicts
|
|
104
|
+
await Promise.all(packages.map(async (pkg) => {
|
|
105
|
+
const copyDir = path_1.default.join(pkg, 'copy');
|
|
106
|
+
try {
|
|
107
|
+
const files = await (0, glob_1.glob)('**/*', { cwd: copyDir, nodir: true, dot: true });
|
|
108
|
+
debug(`Found files in ${pkg}:`, files);
|
|
109
|
+
for (const file of files) {
|
|
110
|
+
const sourcePath = path_1.default.join(copyDir, file);
|
|
111
|
+
const existing = fileMap.get(file);
|
|
112
|
+
if (existing) {
|
|
113
|
+
console.error(`Error: File conflict detected for '${file}'
|
|
114
|
+
Found in packages:
|
|
115
|
+
- ${existing.packagePath} -> ${existing.sourcePath}
|
|
116
|
+
- ${pkg} -> ${sourcePath}`);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
fileMap.set(file, { sourcePath, packagePath: pkg });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
// Skip if copy directory doesn't exist
|
|
124
|
+
if (err.code !== 'ENOENT') {
|
|
125
|
+
console.error(`Error scanning directory ${copyDir}: ${err.message}
|
|
126
|
+
Path: ${copyDir}`);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}));
|
|
131
|
+
// Second pass: copy files
|
|
132
|
+
await Promise.all(Array.from(fileMap.entries()).map(async ([file, { sourcePath }]) => {
|
|
133
|
+
const targetPath = path_1.default.join(cwd, file);
|
|
134
|
+
debug(`Processing file: ${file}`);
|
|
135
|
+
try {
|
|
136
|
+
await promises_1.default.mkdir(path_1.default.dirname(targetPath), { recursive: true });
|
|
137
|
+
const sourceContent = await promises_1.default.readFile(sourcePath);
|
|
138
|
+
const contentWithComment = Buffer.concat([
|
|
139
|
+
Buffer.from(`${MANAGED_BY_GC}\n`),
|
|
140
|
+
Buffer.from('// to modify this file, change it to managed by: local\n\n'),
|
|
141
|
+
sourceContent,
|
|
142
|
+
]);
|
|
143
|
+
let targetContent;
|
|
144
|
+
try {
|
|
145
|
+
targetContent = await promises_1.default.readFile(targetPath);
|
|
146
|
+
const management = getFileManagement(targetContent);
|
|
147
|
+
if (management === 'local') {
|
|
148
|
+
debug(`File ${file} is managed locally, skipping`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (management === 'unmanaged') {
|
|
152
|
+
console.log(`Note: File ${file} has been modified. Add '${MANAGED_LOCALLY.trim()}' at the top to manage it locally.`);
|
|
153
|
+
debug(`File ${file} doesn't have management comment, skipping`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
debug(`File ${file} is managed by graphcommerce, will update if needed`);
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
if (err.code !== 'ENOENT') {
|
|
160
|
+
console.error(`Error reading file ${file}: ${err.message}
|
|
161
|
+
Source: ${sourcePath}`);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
console.log(`Creating new file: ${file}
|
|
165
|
+
Source: ${sourcePath}`);
|
|
166
|
+
debug('File does not exist yet');
|
|
167
|
+
}
|
|
168
|
+
// Skip if content is identical (including magic comment)
|
|
169
|
+
if (targetContent && Buffer.compare(contentWithComment, targetContent) === 0) {
|
|
170
|
+
debug(`File ${file} content is identical to source, skipping`);
|
|
171
|
+
managedFiles.add(file);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// Copy the file with magic comment
|
|
175
|
+
await promises_1.default.writeFile(targetPath, contentWithComment);
|
|
176
|
+
if (targetContent) {
|
|
177
|
+
console.log(`Updated managed file: ${file}`);
|
|
178
|
+
debug(`Overwrote existing file: ${file}`);
|
|
179
|
+
}
|
|
180
|
+
// If the file is managed by GraphCommerce (new or updated), add it to managedFiles
|
|
181
|
+
if (!targetContent || targetContent.toString().startsWith(MANAGED_BY_GC)) {
|
|
182
|
+
managedFiles.add(file);
|
|
183
|
+
debug('Added managed file:', file);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
console.error(`Error copying file ${file}: ${err.message}
|
|
188
|
+
Source: ${sourcePath}`);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
}));
|
|
192
|
+
// Update .gitignore with the list of managed files
|
|
193
|
+
if (managedFiles.size > 0) {
|
|
194
|
+
debug('Found managed files:', Array.from(managedFiles));
|
|
195
|
+
await updateGitignore(Array.from(managedFiles));
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
debug('No managed files found, cleaning up .gitignore section');
|
|
199
|
+
await updateGitignore([]); // Pass empty array to clean up the section
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
7
|
+
// ... earlier code remains the same ...
|
|
8
|
+
try {
|
|
9
|
+
targetContent = await promises_1.default.readFile(targetPath);
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
if (err.code !== 'ENOENT')
|
|
13
|
+
throw err;
|
|
14
|
+
// File doesn't exist, log that we're creating it
|
|
15
|
+
console.log(`Creating new file: ${file}`);
|
|
16
|
+
}
|
|
17
|
+
// Skip if content is identical
|
|
18
|
+
if (targetContent && Buffer.compare(sourceContent, targetContent) === 0)
|
|
19
|
+
return;
|
|
20
|
+
// ... rest of the code remains the same ...
|
package/dist/index.js
CHANGED
|
@@ -17,8 +17,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./utils/isMonorepo"), exports);
|
|
18
18
|
__exportStar(require("./utils/resolveDependenciesSync"), exports);
|
|
19
19
|
__exportStar(require("./utils/packageRoots"), exports);
|
|
20
|
+
__exportStar(require("./utils/sig"), exports);
|
|
20
21
|
__exportStar(require("./withGraphCommerce"), exports);
|
|
21
22
|
__exportStar(require("./generated/config"), exports);
|
|
22
23
|
__exportStar(require("./config"), exports);
|
|
23
24
|
__exportStar(require("./runtimeCachingOptimizations"), exports);
|
|
24
25
|
__exportStar(require("./interceptors/commands/codegenInterceptors"), exports);
|
|
26
|
+
__exportStar(require("./commands/copyFiles"), exports);
|
|
27
|
+
__exportStar(require("./commands/codegen"), exports);
|
|
@@ -8,12 +8,14 @@ exports.resolveDependenciesSync = resolveDependenciesSync;
|
|
|
8
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const PackagesSort_1 = require("./PackagesSort");
|
|
11
|
+
const sig_1 = require("./sig");
|
|
11
12
|
const resolveCache = new Map();
|
|
12
13
|
function resolveRecursivePackageJson(dependencyPath, dependencyStructure, root, additionalDependencies = []) {
|
|
13
14
|
const isRoot = dependencyPath === root;
|
|
14
15
|
const fileName = require.resolve(node_path_1.default.join(dependencyPath, 'package.json'));
|
|
15
16
|
const packageJsonFile = node_fs_1.default.readFileSync(fileName, 'utf-8').toString();
|
|
16
17
|
const packageJson = JSON.parse(packageJsonFile);
|
|
18
|
+
const e = [atob('QGdyYXBoY29tbWVyY2UvYWRvYmUtY29tbWVyY2U=')].filter((n) => !globalThis.gcl ? true : !globalThis.gcl.includes(n));
|
|
17
19
|
if (!packageJson.name)
|
|
18
20
|
throw Error(`Package ${packageJsonFile} does not have a name field`);
|
|
19
21
|
// Previously processed
|
|
@@ -29,7 +31,9 @@ function resolveRecursivePackageJson(dependencyPath, dependencyStructure, root,
|
|
|
29
31
|
...Object.keys(packageJson.devDependencies ?? []),
|
|
30
32
|
...additionalDependencies,
|
|
31
33
|
...Object.keys(packageJson.peerDependencies ?? {}),
|
|
32
|
-
].filter((name) => name.includes('graphcommerce')
|
|
34
|
+
].filter((name) => name.includes('graphcommerce')
|
|
35
|
+
? !(e.length >= 0 && e.some((v) => name.startsWith(v)))
|
|
36
|
+
: false)),
|
|
33
37
|
];
|
|
34
38
|
const name = isRoot ? '.' : packageJson.name;
|
|
35
39
|
dependencyStructure[name] = {
|
|
@@ -66,6 +70,7 @@ function resolveDependenciesSync(root = process.cwd()) {
|
|
|
66
70
|
const cached = resolveCache.get(root);
|
|
67
71
|
if (cached)
|
|
68
72
|
return cached;
|
|
73
|
+
(0, sig_1.sig)();
|
|
69
74
|
const dependencyStructure = resolveRecursivePackageJson(root, {}, root, process.env.PRIVATE_ADDITIONAL_DEPENDENCIES?.split(',') ?? []);
|
|
70
75
|
const sorted = sortDependencies(dependencyStructure);
|
|
71
76
|
resolveCache.set(root, sorted);
|
|
@@ -0,0 +1,34 @@
|
|
|
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.g = g;
|
|
7
|
+
exports.sig = sig;
|
|
8
|
+
// import necessary modules
|
|
9
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
+
// Function to generate a license key based on input data
|
|
11
|
+
function g(data) {
|
|
12
|
+
const iv = crypto_1.default.randomBytes(16); // Initialization vector
|
|
13
|
+
const cipher = crypto_1.default.createCipheriv('aes-256-cbc', 'BbcFEkUydGw3nE9ZPm7gbxTIIBQ9IiKN', iv);
|
|
14
|
+
let encrypted = cipher.update(JSON.stringify(data), 'utf-8', 'hex');
|
|
15
|
+
encrypted += cipher.final('hex');
|
|
16
|
+
// Return the IV and the encrypted data as a single string, encoded in base64
|
|
17
|
+
return Buffer.from(`${iv.toString('hex')}:${encrypted}`).toString();
|
|
18
|
+
}
|
|
19
|
+
// Function to validate and decode the license key
|
|
20
|
+
function sig() {
|
|
21
|
+
const l = process.env[atob('R0NfTElDRU5TRQ==')];
|
|
22
|
+
if (!l)
|
|
23
|
+
return;
|
|
24
|
+
if (!globalThis.gcl)
|
|
25
|
+
try {
|
|
26
|
+
const decipher = crypto_1.default.createDecipheriv('aes-256-cbc', 'BbcFEkUydGw3nE9ZPm7gbxTIIBQ9IiKN', Buffer.from(l.split(':')[0], 'hex'));
|
|
27
|
+
let decrypted = decipher.update(l.split(':')[1], 'hex', 'utf-8');
|
|
28
|
+
decrypted += decipher.final('utf-8');
|
|
29
|
+
globalThis.gcl = JSON.parse(decrypted); // Parse and return the decoded data
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
// Silent
|
|
33
|
+
}
|
|
34
|
+
}
|
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": "9.0.0-canary.
|
|
5
|
+
"version": "9.0.0-canary.106",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"types": "src/index.ts",
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@graphql-mesh/cli": "latest",
|
|
16
|
-
"@lingui/loader": "4.
|
|
17
|
-
"@lingui/macro": "4.
|
|
18
|
-
"@lingui/react": "4.
|
|
19
|
-
"@lingui/swc-plugin": "4.0
|
|
20
|
-
"@swc/core": "1.
|
|
21
|
-
"@swc/wasm-web": "^1.
|
|
16
|
+
"@lingui/loader": "4.14.0",
|
|
17
|
+
"@lingui/macro": "4.14.0",
|
|
18
|
+
"@lingui/react": "4.14.0",
|
|
19
|
+
"@lingui/swc-plugin": "4.1.0",
|
|
20
|
+
"@swc/core": "1.9.3",
|
|
21
|
+
"@swc/wasm-web": "^1.9.3",
|
|
22
22
|
"@types/circular-dependency-plugin": "^5.0.8",
|
|
23
|
-
"@types/lodash": "^4.17.
|
|
23
|
+
"@types/lodash": "^4.17.13",
|
|
24
24
|
"babel-plugin-macros": "^3.1.0",
|
|
25
25
|
"circular-dependency-plugin": "^5.2.2",
|
|
26
26
|
"glob": "^10.4.5",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"js-yaml-loader": "^1.2.2",
|
|
30
30
|
"lodash": "^4.17.21",
|
|
31
31
|
"react": "^18.3.1",
|
|
32
|
-
"typescript": "5.
|
|
32
|
+
"typescript": "5.7.2",
|
|
33
33
|
"webpack": "^5.96.1",
|
|
34
34
|
"znv": "^0.4.0",
|
|
35
35
|
"zod": "^3.23.8"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { generateConfig } from '../config/commands/generateConfig'
|
|
2
|
+
import { codegenInterceptors } from '../interceptors/commands/codegenInterceptors'
|
|
3
|
+
import { copyFiles } from './copyFiles'
|
|
4
|
+
|
|
5
|
+
/** Run all code generation steps in sequence */
|
|
6
|
+
export async function codegen() {
|
|
7
|
+
// Copy files from packages to project
|
|
8
|
+
console.log('🔄 Copying files from packages to project...')
|
|
9
|
+
await copyFiles()
|
|
10
|
+
|
|
11
|
+
// Generate GraphCommerce config types
|
|
12
|
+
console.log('⚙️ Generating GraphCommerce config types...')
|
|
13
|
+
await generateConfig()
|
|
14
|
+
|
|
15
|
+
// Generate interceptors
|
|
16
|
+
console.log('🔌 Generating interceptors...')
|
|
17
|
+
await codegenInterceptors()
|
|
18
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import fs from 'fs/promises'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { glob } from 'glob'
|
|
4
|
+
import { resolveDependenciesSync } from '../utils/resolveDependenciesSync'
|
|
5
|
+
|
|
6
|
+
// Add debug logging helper
|
|
7
|
+
const debug = (...args: unknown[]) => {
|
|
8
|
+
if (process.env.DEBUG) console.log('[copyFiles]', ...args)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Add constants for the magic comments
|
|
12
|
+
type FileManagement = 'graphcommerce' | 'local'
|
|
13
|
+
const createManagementComment = (type: FileManagement) => `// managed by: ${type}`
|
|
14
|
+
|
|
15
|
+
const MANAGED_BY_GC = createManagementComment('graphcommerce')
|
|
16
|
+
const MANAGED_LOCALLY = createManagementComment('local')
|
|
17
|
+
|
|
18
|
+
const GITIGNORE_SECTION_START = '# managed by: graphcommerce'
|
|
19
|
+
const GITIGNORE_SECTION_END = '# end managed by: graphcommerce'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Updates the .gitignore file with a list of GraphCommerce managed files
|
|
23
|
+
*
|
|
24
|
+
* - Removes any existing GraphCommerce managed files section
|
|
25
|
+
* - If managedFiles is not empty, adds a new section with the files
|
|
26
|
+
* - If managedFiles is empty, just cleans up the existing section
|
|
27
|
+
* - Ensures the file ends with a newline
|
|
28
|
+
*/
|
|
29
|
+
async function updateGitignore(managedFiles: string[]) {
|
|
30
|
+
const gitignorePath = path.join(process.cwd(), '.gitignore')
|
|
31
|
+
let content: string
|
|
32
|
+
|
|
33
|
+
debug('Updating .gitignore with managed files:', managedFiles)
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
content = await fs.readFile(gitignorePath, 'utf-8')
|
|
37
|
+
debug('Existing .gitignore content:', content)
|
|
38
|
+
} catch (err) {
|
|
39
|
+
debug('.gitignore not found, creating new file')
|
|
40
|
+
content = ''
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Remove existing GraphCommerce section if it exists
|
|
44
|
+
const sectionRegex = new RegExp(
|
|
45
|
+
`${GITIGNORE_SECTION_START}[\\s\\S]*?${GITIGNORE_SECTION_END}\\n?`,
|
|
46
|
+
'g',
|
|
47
|
+
)
|
|
48
|
+
content = content.replace(sectionRegex, '')
|
|
49
|
+
debug('Content after removing existing section:', content)
|
|
50
|
+
|
|
51
|
+
// Only add new section if there are files to manage
|
|
52
|
+
if (managedFiles.length > 0) {
|
|
53
|
+
const newSection = [
|
|
54
|
+
GITIGNORE_SECTION_START,
|
|
55
|
+
...managedFiles.sort(),
|
|
56
|
+
GITIGNORE_SECTION_END,
|
|
57
|
+
'', // Empty line at the end
|
|
58
|
+
].join('\n')
|
|
59
|
+
debug('New section to add:', newSection)
|
|
60
|
+
|
|
61
|
+
// Append the new section
|
|
62
|
+
content = `${content.trim()}\n\n${newSection}`
|
|
63
|
+
} else {
|
|
64
|
+
// Just trim the content when no files to manage
|
|
65
|
+
content = `${content.trim()}\n`
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
debug('Final content:', content)
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
await fs.writeFile(gitignorePath, content)
|
|
72
|
+
debug('Successfully wrote .gitignore file')
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.error('Error writing .gitignore:', err)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Determines how a file should be managed based on its content */
|
|
79
|
+
function getFileManagement(content: Buffer | undefined): 'local' | 'graphcommerce' | 'unmanaged' {
|
|
80
|
+
if (!content) return 'graphcommerce'
|
|
81
|
+
const contentStr = content.toString()
|
|
82
|
+
if (contentStr.startsWith(MANAGED_LOCALLY)) return 'local'
|
|
83
|
+
if (contentStr.startsWith(MANAGED_BY_GC)) return 'graphcommerce'
|
|
84
|
+
return 'unmanaged'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The packages are @graphcommerce/* packages and have special treatment.
|
|
89
|
+
*
|
|
90
|
+
* 1. Glob the `copy/**` directory for each package and generate a list of files that need to be
|
|
91
|
+
* copied. Error if a file with the same path exists in another package.
|
|
92
|
+
* 2. Copy the files to the project directory (cwd).
|
|
93
|
+
*
|
|
94
|
+
* 1. If the file doesn't exist: Create directories and the file with "managed by: graphcommerce"
|
|
95
|
+
* 2. If the file exists and starts with "managed by: local": Skip the file
|
|
96
|
+
* 3. If the file exists but doesn't have a management comment: Suggest adding "managed by: local"
|
|
97
|
+
* 4. If the file is managed by graphcommerce: Update if content differs
|
|
98
|
+
*/
|
|
99
|
+
export async function copyFiles() {
|
|
100
|
+
debug('Starting copyFiles')
|
|
101
|
+
|
|
102
|
+
const cwd = process.cwd()
|
|
103
|
+
const deps = resolveDependenciesSync()
|
|
104
|
+
const packages = [...deps.values()].filter((p) => p !== '.')
|
|
105
|
+
debug('Found packages:', packages)
|
|
106
|
+
|
|
107
|
+
// Track files and their source packages to detect conflicts
|
|
108
|
+
const fileMap = new Map<string, { sourcePath: string; packagePath: string }>()
|
|
109
|
+
// Track which files are managed by GraphCommerce
|
|
110
|
+
const managedFiles = new Set<string>()
|
|
111
|
+
|
|
112
|
+
// First pass: collect all files and check for conflicts
|
|
113
|
+
await Promise.all(
|
|
114
|
+
packages.map(async (pkg) => {
|
|
115
|
+
const copyDir = path.join(pkg, 'copy')
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const files = await glob('**/*', { cwd: copyDir, nodir: true, dot: true })
|
|
119
|
+
debug(`Found files in ${pkg}:`, files)
|
|
120
|
+
|
|
121
|
+
for (const file of files) {
|
|
122
|
+
const sourcePath = path.join(copyDir, file)
|
|
123
|
+
const existing = fileMap.get(file)
|
|
124
|
+
|
|
125
|
+
if (existing) {
|
|
126
|
+
console.error(`Error: File conflict detected for '${file}'
|
|
127
|
+
Found in packages:
|
|
128
|
+
- ${existing.packagePath} -> ${existing.sourcePath}
|
|
129
|
+
- ${pkg} -> ${sourcePath}`)
|
|
130
|
+
process.exit(1)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fileMap.set(file, { sourcePath, packagePath: pkg })
|
|
134
|
+
}
|
|
135
|
+
} catch (err) {
|
|
136
|
+
// Skip if copy directory doesn't exist
|
|
137
|
+
if ((err as { code?: string }).code !== 'ENOENT') {
|
|
138
|
+
console.error(`Error scanning directory ${copyDir}: ${(err as Error).message}
|
|
139
|
+
Path: ${copyDir}`)
|
|
140
|
+
process.exit(1)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
// Second pass: copy files
|
|
147
|
+
await Promise.all(
|
|
148
|
+
Array.from(fileMap.entries()).map(async ([file, { sourcePath }]) => {
|
|
149
|
+
const targetPath = path.join(cwd, file)
|
|
150
|
+
debug(`Processing file: ${file}`)
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true })
|
|
154
|
+
|
|
155
|
+
const sourceContent = await fs.readFile(sourcePath)
|
|
156
|
+
const contentWithComment = Buffer.concat([
|
|
157
|
+
Buffer.from(`${MANAGED_BY_GC}\n`),
|
|
158
|
+
Buffer.from('// to modify this file, change it to managed by: local\n\n'),
|
|
159
|
+
sourceContent,
|
|
160
|
+
])
|
|
161
|
+
|
|
162
|
+
let targetContent: Buffer | undefined
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
targetContent = await fs.readFile(targetPath)
|
|
166
|
+
|
|
167
|
+
const management = getFileManagement(targetContent)
|
|
168
|
+
if (management === 'local') {
|
|
169
|
+
debug(`File ${file} is managed locally, skipping`)
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
if (management === 'unmanaged') {
|
|
173
|
+
console.log(
|
|
174
|
+
`Note: File ${file} has been modified. Add '${MANAGED_LOCALLY.trim()}' at the top to manage it locally.`,
|
|
175
|
+
)
|
|
176
|
+
debug(`File ${file} doesn't have management comment, skipping`)
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
debug(`File ${file} is managed by graphcommerce, will update if needed`)
|
|
181
|
+
} catch (err) {
|
|
182
|
+
if ((err as { code?: string }).code !== 'ENOENT') {
|
|
183
|
+
console.error(`Error reading file ${file}: ${(err as Error).message}
|
|
184
|
+
Source: ${sourcePath}`)
|
|
185
|
+
process.exit(1)
|
|
186
|
+
}
|
|
187
|
+
console.log(`Creating new file: ${file}
|
|
188
|
+
Source: ${sourcePath}`)
|
|
189
|
+
debug('File does not exist yet')
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Skip if content is identical (including magic comment)
|
|
193
|
+
if (targetContent && Buffer.compare(contentWithComment, targetContent) === 0) {
|
|
194
|
+
debug(`File ${file} content is identical to source, skipping`)
|
|
195
|
+
managedFiles.add(file)
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Copy the file with magic comment
|
|
200
|
+
await fs.writeFile(targetPath, contentWithComment)
|
|
201
|
+
if (targetContent) {
|
|
202
|
+
console.log(`Updated managed file: ${file}`)
|
|
203
|
+
debug(`Overwrote existing file: ${file}`)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// If the file is managed by GraphCommerce (new or updated), add it to managedFiles
|
|
207
|
+
if (!targetContent || targetContent.toString().startsWith(MANAGED_BY_GC)) {
|
|
208
|
+
managedFiles.add(file)
|
|
209
|
+
debug('Added managed file:', file)
|
|
210
|
+
}
|
|
211
|
+
} catch (err) {
|
|
212
|
+
console.error(`Error copying file ${file}: ${(err as Error).message}
|
|
213
|
+
Source: ${sourcePath}`)
|
|
214
|
+
process.exit(1)
|
|
215
|
+
}
|
|
216
|
+
}),
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
// Update .gitignore with the list of managed files
|
|
220
|
+
if (managedFiles.size > 0) {
|
|
221
|
+
debug('Found managed files:', Array.from(managedFiles))
|
|
222
|
+
await updateGitignore(Array.from(managedFiles))
|
|
223
|
+
} else {
|
|
224
|
+
debug('No managed files found, cleaning up .gitignore section')
|
|
225
|
+
await updateGitignore([]) // Pass empty array to clean up the section
|
|
226
|
+
}
|
|
227
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -6,11 +6,14 @@ import type { GraphCommerceConfig } from './generated/config'
|
|
|
6
6
|
export * from './utils/isMonorepo'
|
|
7
7
|
export * from './utils/resolveDependenciesSync'
|
|
8
8
|
export * from './utils/packageRoots'
|
|
9
|
+
export * from './utils/sig'
|
|
9
10
|
export * from './withGraphCommerce'
|
|
10
11
|
export * from './generated/config'
|
|
11
12
|
export * from './config'
|
|
12
13
|
export * from './runtimeCachingOptimizations'
|
|
13
14
|
export * from './interceptors/commands/codegenInterceptors'
|
|
15
|
+
export * from './commands/copyFiles'
|
|
16
|
+
export * from './commands/codegen'
|
|
14
17
|
|
|
15
18
|
export type PluginProps<P extends Record<string, unknown> = Record<string, unknown>> = P & {
|
|
16
19
|
Prev: React.FC<P>
|
|
@@ -21,9 +24,7 @@ export type FunctionPlugin<T extends (...args: any[]) => any> = (
|
|
|
21
24
|
...args: Parameters<T>
|
|
22
25
|
) => ReturnType<T>
|
|
23
26
|
|
|
24
|
-
/**
|
|
25
|
-
* @deprecated use FunctionPlugin instead
|
|
26
|
-
*/
|
|
27
|
+
/** @deprecated Use FunctionPlugin instead */
|
|
27
28
|
export type MethodPlugin<T extends (...args: any[]) => any> = (
|
|
28
29
|
prev: T,
|
|
29
30
|
...args: Parameters<T>
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import type { PackageJson } from 'type-fest'
|
|
4
4
|
import { PackagesSort } from './PackagesSort'
|
|
5
|
+
import { g, sig } from './sig'
|
|
5
6
|
|
|
6
7
|
type PackageNames = Map<string, string>
|
|
7
8
|
type DependencyStructure = Record<string, { dirName: string; dependencies: string[] }>
|
|
@@ -18,6 +19,9 @@ function resolveRecursivePackageJson(
|
|
|
18
19
|
const fileName = require.resolve(path.join(dependencyPath, 'package.json'))
|
|
19
20
|
const packageJsonFile = fs.readFileSync(fileName, 'utf-8').toString()
|
|
20
21
|
const packageJson = JSON.parse(packageJsonFile) as PackageJson
|
|
22
|
+
const e = [atob('QGdyYXBoY29tbWVyY2UvYWRvYmUtY29tbWVyY2U=')].filter((n) =>
|
|
23
|
+
!globalThis.gcl ? true : !globalThis.gcl.includes(n),
|
|
24
|
+
)
|
|
21
25
|
|
|
22
26
|
if (!packageJson.name) throw Error(`Package ${packageJsonFile} does not have a name field`)
|
|
23
27
|
|
|
@@ -36,7 +40,11 @@ function resolveRecursivePackageJson(
|
|
|
36
40
|
...Object.keys(packageJson.devDependencies ?? []),
|
|
37
41
|
...additionalDependencies,
|
|
38
42
|
...Object.keys(packageJson.peerDependencies ?? {}),
|
|
39
|
-
].filter((name) =>
|
|
43
|
+
].filter((name) =>
|
|
44
|
+
name.includes('graphcommerce')
|
|
45
|
+
? !(e.length >= 0 && e.some((v) => name.startsWith(v)))
|
|
46
|
+
: false,
|
|
47
|
+
),
|
|
40
48
|
),
|
|
41
49
|
]
|
|
42
50
|
|
|
@@ -82,6 +90,7 @@ export function sortDependencies(dependencyStructure: DependencyStructure): Pack
|
|
|
82
90
|
export function resolveDependenciesSync(root = process.cwd()) {
|
|
83
91
|
const cached = resolveCache.get(root)
|
|
84
92
|
if (cached) return cached
|
|
93
|
+
sig()
|
|
85
94
|
|
|
86
95
|
const dependencyStructure = resolveRecursivePackageJson(
|
|
87
96
|
root,
|
package/src/utils/sig.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// import necessary modules
|
|
2
|
+
import crypto from 'crypto'
|
|
3
|
+
|
|
4
|
+
declare global {
|
|
5
|
+
// eslint-disable-next-line vars-on-top, no-var
|
|
6
|
+
var gcl: string[] | undefined
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Function to generate a license key based on input data
|
|
10
|
+
export function g(data: string[]) {
|
|
11
|
+
const iv = crypto.randomBytes(16) // Initialization vector
|
|
12
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', 'BbcFEkUydGw3nE9ZPm7gbxTIIBQ9IiKN', iv)
|
|
13
|
+
let encrypted = cipher.update(JSON.stringify(data), 'utf-8', 'hex')
|
|
14
|
+
encrypted += cipher.final('hex')
|
|
15
|
+
// Return the IV and the encrypted data as a single string, encoded in base64
|
|
16
|
+
return Buffer.from(`${iv.toString('hex')}:${encrypted}`).toString()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Function to validate and decode the license key
|
|
20
|
+
export function sig() {
|
|
21
|
+
const l = process.env[atob('R0NfTElDRU5TRQ==')]
|
|
22
|
+
if (!l) return
|
|
23
|
+
|
|
24
|
+
if (!globalThis.gcl)
|
|
25
|
+
try {
|
|
26
|
+
const decipher = crypto.createDecipheriv(
|
|
27
|
+
'aes-256-cbc',
|
|
28
|
+
'BbcFEkUydGw3nE9ZPm7gbxTIIBQ9IiKN',
|
|
29
|
+
Buffer.from(l.split(':')[0], 'hex'),
|
|
30
|
+
)
|
|
31
|
+
let decrypted = decipher.update(l.split(':')[1], 'hex', 'utf-8')
|
|
32
|
+
decrypted += decipher.final('utf-8')
|
|
33
|
+
globalThis.gcl = JSON.parse(decrypted) // Parse and return the decoded data
|
|
34
|
+
} catch (error) {
|
|
35
|
+
// Silent
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.exportConfig = void 0;
|
|
4
|
-
const loadConfig_1 = require("../loadConfig");
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
6
|
-
async function exportConfig() {
|
|
7
|
-
const conf = (0, loadConfig_1.loadConfig)(process.cwd());
|
|
8
|
-
}
|
|
9
|
-
exports.exportConfig = exportConfig;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.exportConfig = void 0;
|
|
4
|
-
const loadConfig_1 = require("../../config/loadConfig");
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
6
|
-
async function exportConfig() {
|
|
7
|
-
const conf = (0, loadConfig_1.loadConfig)(process.cwd());
|
|
8
|
-
}
|
|
9
|
-
exports.exportConfig = exportConfig;
|