@geexcode/geex-angular 0.0.39 → 0.0.41
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/README.md +108 -0
- package/dist/README.md +108 -0
- package/dist/esbuild/gql-redirect.plugin.js +399 -261
- package/dist/fesm2022/geexcode-geex-angular.mjs +3111 -0
- package/dist/fesm2022/geexcode-geex-angular.mjs.map +1 -0
- package/dist/index.d.ts +994 -127
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -467
- package/dist/index.mjs +1 -420
- package/dist/rison/rison.js +553 -0
- package/dist/src/esbuild/gql-redirect.plugin.js +399 -0
- package/dist/src/rison/rison.js +553 -0
- package/package.json +87 -17
- package/dist/chunk-63O7VUAZ.mjs +0 -65
- package/dist/chunk-EBO3CZXG.mjs +0 -15
- package/dist/esbuild/gql-redirect.plugin.d.mts +0 -37
- package/dist/esbuild/gql-redirect.plugin.d.ts +0 -37
- package/dist/esbuild/gql-redirect.plugin.mjs +0 -269
- package/dist/index.d.mts +0 -148
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const json5 = require('json5');
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const { glob } = require('glob');
|
|
9
|
+
const yaml = require('yaml');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @type {string | undefined}
|
|
13
|
+
*/
|
|
14
|
+
let graphqlrcContent = undefined;
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {import('esbuild').Plugin} Plugin
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {Object} GqlRedirectPluginOptions
|
|
21
|
+
* @property {boolean} [verbose=true] Whether to enable verbose logging
|
|
22
|
+
* @property {string} [tsconfigPath='./tsconfig.json'] Path to tsconfig.json file for path alias resolution
|
|
23
|
+
* @property {boolean} [autoGqlGen=true] Whether to automatically run pnpm gqlgen when .gql.ts files are missing
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {{ alias: string, paths: string[] }} PathMapping
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Loads and parses TypeScript configuration for path alias resolution
|
|
32
|
+
* @param {string} tsconfigPath
|
|
33
|
+
* @returns {PathMapping[]}
|
|
34
|
+
*/
|
|
35
|
+
function loadTsconfigPaths(tsconfigPath) {
|
|
36
|
+
try {
|
|
37
|
+
const tsconfig = json5.parse(fs.readFileSync(tsconfigPath, 'utf8'));
|
|
38
|
+
const baseUrl = (tsconfig.compilerOptions && tsconfig.compilerOptions.baseUrl) || './';
|
|
39
|
+
const paths = (tsconfig.compilerOptions && tsconfig.compilerOptions.paths) || {};
|
|
40
|
+
|
|
41
|
+
return Object.entries(paths).map(([alias, pathList]) => ({
|
|
42
|
+
alias: alias.replace(/\/\*$/, ''),
|
|
43
|
+
paths: /** @type {string[]} */ (pathList).map(p =>
|
|
44
|
+
path.resolve(path.dirname(tsconfigPath), baseUrl, p.replace(/\/\*$/, ''))
|
|
45
|
+
),
|
|
46
|
+
}));
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.warn(`[gql-redirect] Failed to load tsconfig from ${tsconfigPath}:`, error);
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Find the directory that contains angular.json by walking up from startDir.
|
|
55
|
+
* Falls back to startDir if not found.
|
|
56
|
+
* @param {string} startDir
|
|
57
|
+
* @returns {string}
|
|
58
|
+
*/
|
|
59
|
+
function findAngularJsonDir(startDir) {
|
|
60
|
+
try {
|
|
61
|
+
let dir = startDir;
|
|
62
|
+
// Prevent infinite loop at filesystem root
|
|
63
|
+
while (true) {
|
|
64
|
+
if (fs.existsSync(path.join(dir, 'angular.json'))) return dir;
|
|
65
|
+
const parent = path.dirname(dir);
|
|
66
|
+
if (parent === dir) return startDir;
|
|
67
|
+
dir = parent;
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
return startDir;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Parse the .graphqlrc.yml file and extract documents patterns
|
|
76
|
+
* @param {string} projectRoot
|
|
77
|
+
* @returns {string[]}
|
|
78
|
+
*/
|
|
79
|
+
function parseGraphqlrcDocuments(projectRoot) {
|
|
80
|
+
try {
|
|
81
|
+
const graphqlrcPath = path.join(projectRoot, '.graphqlrc.yml');
|
|
82
|
+
if (!fs.existsSync(graphqlrcPath)) {
|
|
83
|
+
console.warn('[gql-redirect] .graphqlrc.yml not found, falling back to default patterns');
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
graphqlrcContent ??= fs.readFileSync(graphqlrcPath, 'utf8');
|
|
88
|
+
|
|
89
|
+
// Remove the hash comment line if present to avoid yaml parsing issues
|
|
90
|
+
const cleanContent = graphqlrcContent.replace(/^# gql-hash:.*\n/, '');
|
|
91
|
+
|
|
92
|
+
// Parse YAML content
|
|
93
|
+
const config = yaml.parse(cleanContent);
|
|
94
|
+
|
|
95
|
+
if (!config || !config.documents) {
|
|
96
|
+
console.warn('[gql-redirect] No documents section found in .graphqlrc.yml');
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Extract documents patterns
|
|
101
|
+
const documents = config.documents;
|
|
102
|
+
|
|
103
|
+
// Handle different document formats
|
|
104
|
+
if (Array.isArray(documents)) {
|
|
105
|
+
return documents.filter(doc => typeof doc === 'string');
|
|
106
|
+
} else if (typeof documents === 'string') {
|
|
107
|
+
return [documents];
|
|
108
|
+
} else {
|
|
109
|
+
console.warn('[gql-redirect] Unexpected documents format in .graphqlrc.yml');
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.warn(`[gql-redirect] Failed to parse .graphqlrc.yml:`, error.message);
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Find all .gql files based on .graphqlrc.yml documents configuration
|
|
121
|
+
* @param {string} projectRoot
|
|
122
|
+
* @returns {string[]}
|
|
123
|
+
*/
|
|
124
|
+
function findGqlFilesByDocuments(projectRoot) {
|
|
125
|
+
try {
|
|
126
|
+
const documentPatterns = parseGraphqlrcDocuments(projectRoot);
|
|
127
|
+
const allFiles = new Set();
|
|
128
|
+
|
|
129
|
+
// Process each document pattern
|
|
130
|
+
for (const pattern of documentPatterns) {
|
|
131
|
+
// Skip negation patterns for now, we'll handle them after
|
|
132
|
+
if (pattern.startsWith('!')) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const matches = glob.sync(pattern, {
|
|
138
|
+
cwd: projectRoot,
|
|
139
|
+
absolute: true,
|
|
140
|
+
nodir: true
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
matches.forEach(file => allFiles.add(file));
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.warn(`[gql-redirect] Failed to process pattern "${pattern}":`, error.message);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Apply negation patterns
|
|
150
|
+
const negationPatterns = documentPatterns.filter(pattern => pattern.startsWith('!'));
|
|
151
|
+
let filteredFiles = Array.from(allFiles);
|
|
152
|
+
|
|
153
|
+
for (const negPattern of negationPatterns) {
|
|
154
|
+
const pattern = negPattern.substring(1); // Remove the '!' prefix
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const excludeMatches = glob.sync(pattern, {
|
|
158
|
+
cwd: projectRoot,
|
|
159
|
+
absolute: true,
|
|
160
|
+
nodir: true
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const excludeSet = new Set(excludeMatches);
|
|
164
|
+
filteredFiles = filteredFiles.filter(file => !excludeSet.has(file));
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.warn(`[gql-redirect] Failed to process negation pattern "${negPattern}":`, error.message);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return filteredFiles.sort(); // Sort for consistent ordering
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.warn(`[gql-redirect] Failed to find GQL files:`, error.message);
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Calculate MD5 hash of all .gql files (based on .graphqlrc.yml documents config) and .graphqlrc.yml content (excluding last line comment)
|
|
179
|
+
* @param {string} projectRoot
|
|
180
|
+
* @returns {string}
|
|
181
|
+
*/
|
|
182
|
+
function calculateGqlHash(projectRoot) {
|
|
183
|
+
try {
|
|
184
|
+
const hash = crypto.createHash('md5');
|
|
185
|
+
|
|
186
|
+
// Find .gql files based on .graphqlrc.yml documents configuration
|
|
187
|
+
const gqlFiles = findGqlFilesByDocuments(projectRoot);
|
|
188
|
+
|
|
189
|
+
// Add content of all .gql files
|
|
190
|
+
for (const filePath of gqlFiles) {
|
|
191
|
+
try {
|
|
192
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
193
|
+
hash.update(`${path.relative(projectRoot, filePath)}:${content}`);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.warn(`[gql-redirect] Failed to read ${filePath}:`, error.message);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Add .graphqlrc.yml content (excluding last line comment)
|
|
200
|
+
const graphqlrcPath = path.join(projectRoot, '.graphqlrc.yml');
|
|
201
|
+
if (fs.existsSync(graphqlrcPath)) {
|
|
202
|
+
try {
|
|
203
|
+
graphqlrcContent ??= fs.readFileSync(graphqlrcPath, 'utf8');
|
|
204
|
+
const cleanContent = graphqlrcContent.replace(/^# gql-hash: .*/m, '');
|
|
205
|
+
hash.update(`.graphqlrc.yml:${cleanContent}`);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.warn(`[gql-redirect] Failed to read .graphqlrc.yml:`, error.message);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return hash.digest('hex');
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.warn(`[gql-redirect] Failed to calculate GQL hash:`, error.message);
|
|
214
|
+
return '';
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Get the previous GQL hash from the last line comment in .graphqlrc.yml
|
|
220
|
+
* @param {string} projectRoot
|
|
221
|
+
* @returns {string}
|
|
222
|
+
*/
|
|
223
|
+
function getPreviousGqlHash(projectRoot) {
|
|
224
|
+
try {
|
|
225
|
+
const graphqlrcPath = path.join(projectRoot, '.graphqlrc.yml');
|
|
226
|
+
if (!fs.existsSync(graphqlrcPath)) {
|
|
227
|
+
return '';
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
graphqlrcContent ??= fs.readFileSync(graphqlrcPath, 'utf8');
|
|
231
|
+
const match = graphqlrcContent.match(/^# gql-hash: (.*)$/m);
|
|
232
|
+
return match ? match[1].trim() : '';
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.warn(`[gql-redirect] Failed to read previous GQL hash:`, error.message);
|
|
235
|
+
return '';
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Update the GQL hash comment at the end of .graphqlrc.yml
|
|
241
|
+
* @param {string} projectRoot
|
|
242
|
+
* @param {string} newHash
|
|
243
|
+
*/
|
|
244
|
+
function updateGraphqlrcHash(projectRoot, newHash) {
|
|
245
|
+
try {
|
|
246
|
+
const graphqlrcPath = path.join(projectRoot, '.graphqlrc.yml');
|
|
247
|
+
if (!fs.existsSync(graphqlrcPath)) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Always read fresh content from file instead of using cached version
|
|
252
|
+
const currentContent = fs.readFileSync(graphqlrcPath, 'utf8');
|
|
253
|
+
const newContent = currentContent.replace(/^# gql-hash: .*/m, `# gql-hash: ${newHash}`);
|
|
254
|
+
fs.writeFileSync(graphqlrcPath, newContent, 'utf8');
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.warn(`[gql-redirect] Failed to update .graphqlrc.yml hash:`, error.message);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Executes pnpm gqlgen command in the project root directory with hash-based caching
|
|
262
|
+
* @param {string} projectRoot
|
|
263
|
+
* @param {boolean} verbose
|
|
264
|
+
* @returns {boolean} Returns true if command executed successfully or was skipped due to cache
|
|
265
|
+
*/
|
|
266
|
+
function runGqlGen(projectRoot, verbose) {
|
|
267
|
+
try {
|
|
268
|
+
// Calculate current hash of all .gql files and .graphqlrc.yml content
|
|
269
|
+
const currentGqlHash = calculateGqlHash(projectRoot);
|
|
270
|
+
|
|
271
|
+
// Get previous hash from .graphqlrc.yml comment
|
|
272
|
+
const previousGqlHash = getPreviousGqlHash(projectRoot);
|
|
273
|
+
|
|
274
|
+
// Compare hashes - only run gqlgen if they're different
|
|
275
|
+
if (currentGqlHash && currentGqlHash === previousGqlHash) {
|
|
276
|
+
console.log('[gql-redirect] GQL files unchanged, skipping pnpm gqlgen');
|
|
277
|
+
return true; // Consider this a success since no work was needed
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
console.log(`[gql-redirect] GQL files changed (${previousGqlHash}) => (${currentGqlHash}), running pnpm gqlgen in ${projectRoot}...`);
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
execSync('pnpm gqlgen', {
|
|
284
|
+
cwd: projectRoot,
|
|
285
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
286
|
+
timeout: 30000, // 30 second timeout
|
|
287
|
+
});
|
|
288
|
+
} catch (error) {
|
|
289
|
+
if (error.message.includes('NODE_TLS_REJECT_UNAUTHORIZED')) {
|
|
290
|
+
// ignore NODE_TLS_REJECT_UNAUTHORIZED error
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Update the hash in .graphqlrc.yml after successful execution
|
|
294
|
+
if (currentGqlHash) {
|
|
295
|
+
updateGraphqlrcHash(projectRoot, currentGqlHash);
|
|
296
|
+
}
|
|
297
|
+
if (verbose) {
|
|
298
|
+
console.log('[gql-redirect] Successfully executed pnpm gqlgen and updated hash');
|
|
299
|
+
}
|
|
300
|
+
return true;
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error(`[gql-redirect] Failed to execute pnpm gqlgen:`, error.message);
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Resolves a module path that might use TypeScript path aliases
|
|
309
|
+
* @param {string} modulePath
|
|
310
|
+
* @param {PathMapping[]} pathMappings
|
|
311
|
+
* @param {string} resolveDir
|
|
312
|
+
* @returns {string[]}
|
|
313
|
+
*/
|
|
314
|
+
function resolvePathAlias(modulePath, pathMappings, resolveDir) {
|
|
315
|
+
const possiblePaths = [];
|
|
316
|
+
|
|
317
|
+
for (const mapping of pathMappings) {
|
|
318
|
+
if (modulePath.startsWith(mapping.alias)) {
|
|
319
|
+
const remainingPath = modulePath.slice(mapping.alias.length);
|
|
320
|
+
const cleanRemainingPath = remainingPath.startsWith('/') ? remainingPath.slice(1) : remainingPath;
|
|
321
|
+
|
|
322
|
+
for (const basePath of mapping.paths) {
|
|
323
|
+
const resolvedPath = cleanRemainingPath ? path.join(basePath, cleanRemainingPath) : basePath;
|
|
324
|
+
possiblePaths.push(resolvedPath);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (possiblePaths.length === 0) {
|
|
330
|
+
possiblePaths.push(path.resolve(resolveDir, modulePath));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return possiblePaths;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* ESBuild plugin to handle .gql imports and redirect them to .gql.ts files
|
|
338
|
+
*
|
|
339
|
+
* This plugin resolves .gql imports by checking if a corresponding .gql.ts file exists.
|
|
340
|
+
* If it does, it redirects the import to the .gql.ts file. Otherwise, it lets esbuild
|
|
341
|
+
* handle the .gql file normally.
|
|
342
|
+
*
|
|
343
|
+
* Now supports TypeScript path aliases for proper module resolution.
|
|
344
|
+
*
|
|
345
|
+
* @param {GqlRedirectPluginOptions} [options]
|
|
346
|
+
* @returns {Plugin}
|
|
347
|
+
*/
|
|
348
|
+
function gqlRedirectPlugin(options = {}) {
|
|
349
|
+
const { verbose = false, tsconfigPath = './tsconfig.json', autoGqlGen = true } = options;
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
name: 'gql-redirect',
|
|
353
|
+
setup(build) {
|
|
354
|
+
const angularJsonDir = findAngularJsonDir(process.cwd());
|
|
355
|
+
const pathMappings = loadTsconfigPaths(tsconfigPath);
|
|
356
|
+
|
|
357
|
+
if (verbose && pathMappings.length > 0) {
|
|
358
|
+
console.log(`[gql-redirect] Loaded ${pathMappings.length} path mappings from ${tsconfigPath}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (autoGqlGen) {
|
|
362
|
+
runGqlGen(angularJsonDir, verbose);
|
|
363
|
+
}
|
|
364
|
+
build.onResolve({ filter: /\.gql$/ }, args => {
|
|
365
|
+
try {
|
|
366
|
+
const possiblePaths = resolvePathAlias(args.path, pathMappings, args.resolveDir);
|
|
367
|
+
|
|
368
|
+
// First pass: check if any .gql.ts files exist
|
|
369
|
+
for (const resolvedPath of possiblePaths) {
|
|
370
|
+
const gqlTsPath = resolvedPath + '.ts';
|
|
371
|
+
|
|
372
|
+
if (fs.existsSync(gqlTsPath)) {
|
|
373
|
+
if (verbose) {
|
|
374
|
+
const displayPath = path.relative(angularJsonDir, gqlTsPath) || '.';
|
|
375
|
+
console.log(`[gql-redirect] Redirecting "${args.path}" to "${displayPath}"`);
|
|
376
|
+
}
|
|
377
|
+
return { path: gqlTsPath };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (verbose) {
|
|
381
|
+
console.warn(`[gql-redirect] No .gql.ts found for ${args.path} in any of the resolved paths: ${possiblePaths.join(', ')}`);
|
|
382
|
+
} else {
|
|
383
|
+
console.warn(`[gql-redirect] No .gql.ts found for ${args.path}, please run pnpm gqlgen to ensure it.`);
|
|
384
|
+
}
|
|
385
|
+
return null;
|
|
386
|
+
} catch (error) {
|
|
387
|
+
if (verbose) {
|
|
388
|
+
console.error(`[gql-redirect] Error processing ${args.path}:`, error);
|
|
389
|
+
}
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
module.exports = gqlRedirectPlugin;
|
|
398
|
+
module.exports.default = gqlRedirectPlugin;
|
|
399
|
+
module.exports.gqlRedirectPlugin = gqlRedirectPlugin;
|