@constructive-io/graphql-codegen 4.7.3 → 4.8.1
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/core/codegen/cli/command-map-generator.js +36 -11
- package/core/codegen/cli/custom-command-generator.js +59 -13
- package/core/codegen/cli/docs-generator.d.ts +1 -1
- package/core/codegen/cli/docs-generator.js +137 -54
- package/core/codegen/cli/executor-generator.js +20 -6
- package/core/codegen/cli/infra-generator.js +17 -14
- package/core/codegen/cli/table-command-generator.js +209 -53
- package/core/codegen/docs-utils.d.ts +12 -1
- package/core/codegen/docs-utils.js +49 -1
- package/core/codegen/hooks-docs-generator.d.ts +1 -1
- package/core/codegen/hooks-docs-generator.js +47 -7
- package/core/codegen/orm/docs-generator.d.ts +1 -1
- package/core/codegen/orm/docs-generator.js +57 -20
- package/core/generate.d.ts +5 -0
- package/core/generate.js +48 -12
- package/core/workspace.d.ts +13 -0
- package/core/workspace.js +92 -0
- package/esm/core/codegen/cli/command-map-generator.js +36 -11
- package/esm/core/codegen/cli/custom-command-generator.js +60 -14
- package/esm/core/codegen/cli/docs-generator.d.ts +1 -1
- package/esm/core/codegen/cli/docs-generator.js +138 -55
- package/esm/core/codegen/cli/executor-generator.js +20 -6
- package/esm/core/codegen/cli/infra-generator.js +17 -14
- package/esm/core/codegen/cli/table-command-generator.js +210 -54
- package/esm/core/codegen/docs-utils.d.ts +12 -1
- package/esm/core/codegen/docs-utils.js +48 -1
- package/esm/core/codegen/hooks-docs-generator.d.ts +1 -1
- package/esm/core/codegen/hooks-docs-generator.js +48 -8
- package/esm/core/codegen/orm/docs-generator.d.ts +1 -1
- package/esm/core/codegen/orm/docs-generator.js +58 -21
- package/esm/core/generate.d.ts +5 -0
- package/esm/core/generate.js +48 -12
- package/esm/core/workspace.d.ts +13 -0
- package/esm/core/workspace.js +89 -0
- package/esm/types/config.d.ts +12 -4
- package/package.json +22 -22
- package/types/config.d.ts +12 -4
|
@@ -4,6 +4,7 @@ exports.generateHooksReadme = generateHooksReadme;
|
|
|
4
4
|
exports.generateHooksAgentsDocs = generateHooksAgentsDocs;
|
|
5
5
|
exports.getHooksMcpTools = getHooksMcpTools;
|
|
6
6
|
exports.generateHooksSkills = generateHooksSkills;
|
|
7
|
+
const komoji_1 = require("komoji");
|
|
7
8
|
const docs_utils_1 = require("./docs-utils");
|
|
8
9
|
const utils_1 = require("./utils");
|
|
9
10
|
function getCustomHookName(op) {
|
|
@@ -377,8 +378,11 @@ function getHooksMcpTools(tables, customOperations) {
|
|
|
377
378
|
}
|
|
378
379
|
return tools;
|
|
379
380
|
}
|
|
380
|
-
function generateHooksSkills(tables, customOperations) {
|
|
381
|
+
function generateHooksSkills(tables, customOperations, targetName) {
|
|
381
382
|
const files = [];
|
|
383
|
+
const skillName = `hooks-${targetName}`;
|
|
384
|
+
const referenceNames = [];
|
|
385
|
+
// Generate reference files for each table
|
|
382
386
|
for (const table of tables) {
|
|
383
387
|
const { singularName, pluralName } = (0, utils_1.getTableNames)(table);
|
|
384
388
|
const pk = (0, utils_1.getPrimaryKeyInfo)(table)[0];
|
|
@@ -386,10 +390,12 @@ function generateHooksSkills(tables, customOperations) {
|
|
|
386
390
|
const selectFields = scalarFields
|
|
387
391
|
.map((f) => `${f.name}: true`)
|
|
388
392
|
.join(', ');
|
|
393
|
+
const refName = (0, komoji_1.toKebabCase)(singularName);
|
|
394
|
+
referenceNames.push(refName);
|
|
389
395
|
files.push({
|
|
390
|
-
fileName:
|
|
391
|
-
content: (0, docs_utils_1.
|
|
392
|
-
|
|
396
|
+
fileName: `${skillName}/references/${refName}.md`,
|
|
397
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
398
|
+
title: singularName,
|
|
393
399
|
description: table.description || `React Query hooks for ${table.name} data operations`,
|
|
394
400
|
language: 'typescript',
|
|
395
401
|
usage: [
|
|
@@ -429,15 +435,18 @@ function generateHooksSkills(tables, customOperations) {
|
|
|
429
435
|
}),
|
|
430
436
|
});
|
|
431
437
|
}
|
|
438
|
+
// Generate reference files for custom operations
|
|
432
439
|
for (const op of customOperations) {
|
|
433
440
|
const hookName = getCustomHookName(op);
|
|
434
441
|
const callArgs = op.args.length > 0
|
|
435
442
|
? `{ ${op.args.map((a) => `${a.name}: '<value>'`).join(', ')} }`
|
|
436
443
|
: '';
|
|
444
|
+
const refName = (0, komoji_1.toKebabCase)(op.name);
|
|
445
|
+
referenceNames.push(refName);
|
|
437
446
|
files.push({
|
|
438
|
-
fileName:
|
|
439
|
-
content: (0, docs_utils_1.
|
|
440
|
-
|
|
447
|
+
fileName: `${skillName}/references/${refName}.md`,
|
|
448
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
449
|
+
title: op.name,
|
|
441
450
|
description: op.description ||
|
|
442
451
|
`React Query ${op.kind} hook for ${op.name}`,
|
|
443
452
|
language: 'typescript',
|
|
@@ -464,5 +473,36 @@ function generateHooksSkills(tables, customOperations) {
|
|
|
464
473
|
}),
|
|
465
474
|
});
|
|
466
475
|
}
|
|
476
|
+
// Generate the overview SKILL.md
|
|
477
|
+
const hookExamples = tables.slice(0, 3).map((t) => (0, utils_1.getListQueryHookName)(t));
|
|
478
|
+
files.push({
|
|
479
|
+
fileName: `${skillName}/SKILL.md`,
|
|
480
|
+
content: (0, docs_utils_1.buildSkillFile)({
|
|
481
|
+
name: skillName,
|
|
482
|
+
description: `React Query hooks for the ${targetName} API — provides typed query and mutation hooks for ${tables.length} tables and ${customOperations.length} custom operations`,
|
|
483
|
+
language: 'typescript',
|
|
484
|
+
usage: [
|
|
485
|
+
`// Import hooks`,
|
|
486
|
+
`import { ${hookExamples[0] || 'useModelQuery'} } from './hooks';`,
|
|
487
|
+
'',
|
|
488
|
+
`// Query hooks: use<Model>Query, use<Model>sQuery`,
|
|
489
|
+
`// Mutation hooks: useCreate<Model>Mutation, useUpdate<Model>Mutation, useDelete<Model>Mutation`,
|
|
490
|
+
'',
|
|
491
|
+
`const { data, isLoading } = ${hookExamples[0] || 'useModelQuery'}({`,
|
|
492
|
+
` selection: { fields: { id: true } },`,
|
|
493
|
+
`});`,
|
|
494
|
+
],
|
|
495
|
+
examples: [
|
|
496
|
+
{
|
|
497
|
+
description: 'Query records',
|
|
498
|
+
code: [
|
|
499
|
+
`const { data, isLoading } = ${hookExamples[0] || 'useModelQuery'}({`,
|
|
500
|
+
' selection: { fields: { id: true } },',
|
|
501
|
+
'});',
|
|
502
|
+
],
|
|
503
|
+
},
|
|
504
|
+
],
|
|
505
|
+
}, referenceNames),
|
|
506
|
+
});
|
|
467
507
|
return files;
|
|
468
508
|
}
|
|
@@ -3,4 +3,4 @@ import type { GeneratedDocFile, McpTool } from '../docs-utils';
|
|
|
3
3
|
export declare function generateOrmReadme(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
|
|
4
4
|
export declare function generateOrmAgentsDocs(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
|
|
5
5
|
export declare function getOrmMcpTools(tables: CleanTable[], customOperations: CleanOperation[]): McpTool[];
|
|
6
|
-
export declare function generateOrmSkills(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile[];
|
|
6
|
+
export declare function generateOrmSkills(tables: CleanTable[], customOperations: CleanOperation[], targetName: string): GeneratedDocFile[];
|
|
@@ -4,6 +4,7 @@ exports.generateOrmReadme = generateOrmReadme;
|
|
|
4
4
|
exports.generateOrmAgentsDocs = generateOrmAgentsDocs;
|
|
5
5
|
exports.getOrmMcpTools = getOrmMcpTools;
|
|
6
6
|
exports.generateOrmSkills = generateOrmSkills;
|
|
7
|
+
const komoji_1 = require("komoji");
|
|
7
8
|
const docs_utils_1 = require("../docs-utils");
|
|
8
9
|
const utils_1 = require("../utils");
|
|
9
10
|
function generateOrmReadme(tables, customOperations) {
|
|
@@ -346,30 +347,36 @@ function getOrmMcpTools(tables, customOperations) {
|
|
|
346
347
|
}
|
|
347
348
|
return tools;
|
|
348
349
|
}
|
|
349
|
-
function generateOrmSkills(tables, customOperations) {
|
|
350
|
+
function generateOrmSkills(tables, customOperations, targetName) {
|
|
350
351
|
const files = [];
|
|
352
|
+
const skillName = `orm-${targetName}`;
|
|
353
|
+
const referenceNames = [];
|
|
354
|
+
// Generate reference files for each table
|
|
351
355
|
for (const table of tables) {
|
|
352
356
|
const { singularName } = (0, utils_1.getTableNames)(table);
|
|
353
357
|
const pk = (0, utils_1.getPrimaryKeyInfo)(table)[0];
|
|
354
358
|
const editableFields = (0, docs_utils_1.getEditableFields)(table);
|
|
359
|
+
const modelName = (0, utils_1.lcFirst)(singularName);
|
|
360
|
+
const refName = (0, komoji_1.toKebabCase)(singularName);
|
|
361
|
+
referenceNames.push(refName);
|
|
355
362
|
files.push({
|
|
356
|
-
fileName:
|
|
357
|
-
content: (0, docs_utils_1.
|
|
358
|
-
|
|
363
|
+
fileName: `${skillName}/references/${refName}.md`,
|
|
364
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
365
|
+
title: singularName,
|
|
359
366
|
description: table.description || `ORM operations for ${table.name} records`,
|
|
360
367
|
language: 'typescript',
|
|
361
368
|
usage: [
|
|
362
|
-
`db.${
|
|
363
|
-
`db.${
|
|
364
|
-
`db.${
|
|
365
|
-
`db.${
|
|
366
|
-
`db.${
|
|
369
|
+
`db.${modelName}.findMany({ select: { id: true } }).execute()`,
|
|
370
|
+
`db.${modelName}.findOne({ ${pk.name}: '<value>', select: { id: true } }).execute()`,
|
|
371
|
+
`db.${modelName}.create({ data: { ${editableFields.map((f) => `${f.name}: '<value>'`).join(', ')} }, select: { id: true } }).execute()`,
|
|
372
|
+
`db.${modelName}.update({ where: { ${pk.name}: '<value>' }, data: { ${editableFields[0]?.name || 'field'}: '<new>' }, select: { id: true } }).execute()`,
|
|
373
|
+
`db.${modelName}.delete({ where: { ${pk.name}: '<value>' } }).execute()`,
|
|
367
374
|
],
|
|
368
375
|
examples: [
|
|
369
376
|
{
|
|
370
377
|
description: `List all ${singularName} records`,
|
|
371
378
|
code: [
|
|
372
|
-
`const items = await db.${
|
|
379
|
+
`const items = await db.${modelName}.findMany({`,
|
|
373
380
|
` select: { ${pk.name}: true, ${editableFields[0]?.name || 'name'}: true }`,
|
|
374
381
|
'}).execute();',
|
|
375
382
|
],
|
|
@@ -377,7 +384,7 @@ function generateOrmSkills(tables, customOperations) {
|
|
|
377
384
|
{
|
|
378
385
|
description: `Create a ${singularName}`,
|
|
379
386
|
code: [
|
|
380
|
-
`const item = await db.${
|
|
387
|
+
`const item = await db.${modelName}.create({`,
|
|
381
388
|
` data: { ${editableFields.map((f) => `${f.name}: 'value'`).join(', ')} },`,
|
|
382
389
|
` select: { ${pk.name}: true }`,
|
|
383
390
|
'}).execute();',
|
|
@@ -387,30 +394,60 @@ function generateOrmSkills(tables, customOperations) {
|
|
|
387
394
|
}),
|
|
388
395
|
});
|
|
389
396
|
}
|
|
397
|
+
// Generate reference files for custom operations
|
|
390
398
|
for (const op of customOperations) {
|
|
391
399
|
const accessor = op.kind === 'query' ? 'query' : 'mutation';
|
|
392
400
|
const callArgs = op.args.length > 0
|
|
393
401
|
? `{ ${op.args.map((a) => `${a.name}: '<value>'`).join(', ')} }`
|
|
394
402
|
: '';
|
|
403
|
+
const refName = (0, komoji_1.toKebabCase)(op.name);
|
|
404
|
+
referenceNames.push(refName);
|
|
395
405
|
files.push({
|
|
396
|
-
fileName:
|
|
397
|
-
content: (0, docs_utils_1.
|
|
398
|
-
|
|
406
|
+
fileName: `${skillName}/references/${refName}.md`,
|
|
407
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
408
|
+
title: op.name,
|
|
399
409
|
description: op.description || `Execute the ${op.name} ${op.kind}`,
|
|
400
410
|
language: 'typescript',
|
|
401
|
-
usage: [
|
|
402
|
-
`db.${accessor}.${op.name}(${callArgs}).execute()`,
|
|
403
|
-
],
|
|
411
|
+
usage: [`db.${accessor}.${op.name}(${callArgs}).execute()`],
|
|
404
412
|
examples: [
|
|
405
413
|
{
|
|
406
414
|
description: `Run ${op.name}`,
|
|
407
|
-
code: [
|
|
408
|
-
`const result = await db.${accessor}.${op.name}(${callArgs}).execute();`,
|
|
409
|
-
],
|
|
415
|
+
code: [`const result = await db.${accessor}.${op.name}(${callArgs}).execute();`],
|
|
410
416
|
},
|
|
411
417
|
],
|
|
412
418
|
}),
|
|
413
419
|
});
|
|
414
420
|
}
|
|
421
|
+
// Generate the overview SKILL.md
|
|
422
|
+
const tableNames = tables.map((t) => (0, utils_1.lcFirst)((0, utils_1.getTableNames)(t).singularName));
|
|
423
|
+
files.push({
|
|
424
|
+
fileName: `${skillName}/SKILL.md`,
|
|
425
|
+
content: (0, docs_utils_1.buildSkillFile)({
|
|
426
|
+
name: skillName,
|
|
427
|
+
description: `ORM client for the ${targetName} API — provides typed CRUD operations for ${tables.length} tables and ${customOperations.length} custom operations`,
|
|
428
|
+
language: 'typescript',
|
|
429
|
+
usage: [
|
|
430
|
+
`// Import the ORM client`,
|
|
431
|
+
`import { db } from './orm';`,
|
|
432
|
+
'',
|
|
433
|
+
`// Available models: ${tableNames.slice(0, 8).join(', ')}${tableNames.length > 8 ? ', ...' : ''}`,
|
|
434
|
+
`db.<model>.findMany({ select: { id: true } }).execute()`,
|
|
435
|
+
`db.<model>.findOne({ id: '<value>', select: { id: true } }).execute()`,
|
|
436
|
+
`db.<model>.create({ data: { ... }, select: { id: true } }).execute()`,
|
|
437
|
+
`db.<model>.update({ where: { id: '<value>' }, data: { ... }, select: { id: true } }).execute()`,
|
|
438
|
+
`db.<model>.delete({ where: { id: '<value>' } }).execute()`,
|
|
439
|
+
],
|
|
440
|
+
examples: [
|
|
441
|
+
{
|
|
442
|
+
description: 'Query records',
|
|
443
|
+
code: [
|
|
444
|
+
`const items = await db.${tableNames[0] || 'model'}.findMany({`,
|
|
445
|
+
' select: { id: true }',
|
|
446
|
+
'}).execute();',
|
|
447
|
+
],
|
|
448
|
+
},
|
|
449
|
+
],
|
|
450
|
+
}, referenceNames),
|
|
451
|
+
});
|
|
415
452
|
return files;
|
|
416
453
|
}
|
package/core/generate.d.ts
CHANGED
|
@@ -33,6 +33,11 @@ export interface GenerateResult {
|
|
|
33
33
|
*/
|
|
34
34
|
export interface GenerateInternalOptions {
|
|
35
35
|
skipCli?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Internal-only name for the target when generating skills.
|
|
38
|
+
* Used by generateMulti() so skill names are stable and composable.
|
|
39
|
+
*/
|
|
40
|
+
targetName?: string;
|
|
36
41
|
}
|
|
37
42
|
export declare function generate(options?: GenerateOptions, internalOptions?: GenerateInternalOptions): Promise<GenerateResult>;
|
|
38
43
|
export declare function expandApiNamesToMultiTarget(config: GraphQLSDKConfigTarget): Record<string, GraphQLSDKConfigTarget> | null;
|
package/core/generate.js
CHANGED
|
@@ -66,6 +66,18 @@ const target_docs_generator_1 = require("./codegen/target-docs-generator");
|
|
|
66
66
|
const introspect_1 = require("./introspect");
|
|
67
67
|
const output_1 = require("./output");
|
|
68
68
|
const pipeline_1 = require("./pipeline");
|
|
69
|
+
const workspace_1 = require("./workspace");
|
|
70
|
+
function resolveSkillsOutputDir(config, outputRoot) {
|
|
71
|
+
const workspaceRoot = (0, workspace_1.findWorkspaceRoot)(node_path_1.default.resolve(outputRoot)) ??
|
|
72
|
+
(0, workspace_1.findWorkspaceRoot)(process.cwd()) ??
|
|
73
|
+
process.cwd();
|
|
74
|
+
if (config.skillsPath) {
|
|
75
|
+
return node_path_1.default.isAbsolute(config.skillsPath)
|
|
76
|
+
? config.skillsPath
|
|
77
|
+
: node_path_1.default.resolve(workspaceRoot, config.skillsPath);
|
|
78
|
+
}
|
|
79
|
+
return node_path_1.default.resolve(workspaceRoot, 'skills');
|
|
80
|
+
}
|
|
69
81
|
async function generate(options = {}, internalOptions) {
|
|
70
82
|
// Apply defaults to get resolved config
|
|
71
83
|
const config = (0, config_1.getConfigOptions)(options);
|
|
@@ -261,6 +273,8 @@ async function generate(options = {}, internalOptions) {
|
|
|
261
273
|
...(customOperations.mutations ?? []),
|
|
262
274
|
];
|
|
263
275
|
const allMcpTools = [];
|
|
276
|
+
const targetName = internalOptions?.targetName ?? 'default';
|
|
277
|
+
const skillsToWrite = [];
|
|
264
278
|
if (runOrm) {
|
|
265
279
|
if (docsConfig.readme) {
|
|
266
280
|
const readme = (0, docs_generator_2.generateOrmReadme)(tables, allCustomOps);
|
|
@@ -274,8 +288,8 @@ async function generate(options = {}, internalOptions) {
|
|
|
274
288
|
allMcpTools.push(...(0, docs_generator_2.getOrmMcpTools)(tables, allCustomOps));
|
|
275
289
|
}
|
|
276
290
|
if (docsConfig.skills) {
|
|
277
|
-
for (const skill of (0, docs_generator_2.generateOrmSkills)(tables, allCustomOps)) {
|
|
278
|
-
|
|
291
|
+
for (const skill of (0, docs_generator_2.generateOrmSkills)(tables, allCustomOps, targetName)) {
|
|
292
|
+
skillsToWrite.push({ path: skill.fileName, content: skill.content });
|
|
279
293
|
}
|
|
280
294
|
}
|
|
281
295
|
}
|
|
@@ -292,8 +306,8 @@ async function generate(options = {}, internalOptions) {
|
|
|
292
306
|
allMcpTools.push(...(0, hooks_docs_generator_1.getHooksMcpTools)(tables, allCustomOps));
|
|
293
307
|
}
|
|
294
308
|
if (docsConfig.skills) {
|
|
295
|
-
for (const skill of (0, hooks_docs_generator_1.generateHooksSkills)(tables, allCustomOps)) {
|
|
296
|
-
|
|
309
|
+
for (const skill of (0, hooks_docs_generator_1.generateHooksSkills)(tables, allCustomOps, targetName)) {
|
|
310
|
+
skillsToWrite.push({ path: skill.fileName, content: skill.content });
|
|
297
311
|
}
|
|
298
312
|
}
|
|
299
313
|
}
|
|
@@ -313,8 +327,8 @@ async function generate(options = {}, internalOptions) {
|
|
|
313
327
|
allMcpTools.push(...(0, docs_generator_1.getCliMcpTools)(tables, allCustomOps, toolName));
|
|
314
328
|
}
|
|
315
329
|
if (docsConfig.skills) {
|
|
316
|
-
for (const skill of (0, docs_generator_1.generateSkills)(tables, allCustomOps, toolName)) {
|
|
317
|
-
|
|
330
|
+
for (const skill of (0, docs_generator_1.generateSkills)(tables, allCustomOps, toolName, targetName)) {
|
|
331
|
+
skillsToWrite.push({ path: skill.fileName, content: skill.content });
|
|
318
332
|
}
|
|
319
333
|
}
|
|
320
334
|
}
|
|
@@ -352,6 +366,21 @@ async function generate(options = {}, internalOptions) {
|
|
|
352
366
|
};
|
|
353
367
|
}
|
|
354
368
|
allFilesWritten.push(...(writeResult.filesWritten ?? []));
|
|
369
|
+
if (skillsToWrite.length > 0) {
|
|
370
|
+
const skillsOutputDir = resolveSkillsOutputDir(config, outputRoot);
|
|
371
|
+
const skillsWriteResult = await (0, output_1.writeGeneratedFiles)(skillsToWrite, skillsOutputDir, [], {
|
|
372
|
+
pruneStaleFiles: false,
|
|
373
|
+
});
|
|
374
|
+
if (!skillsWriteResult.success) {
|
|
375
|
+
return {
|
|
376
|
+
success: false,
|
|
377
|
+
message: `Failed to write generated skill files: ${skillsWriteResult.errors?.join(', ')}`,
|
|
378
|
+
output: skillsOutputDir,
|
|
379
|
+
errors: skillsWriteResult.errors,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
allFilesWritten.push(...(skillsWriteResult.filesWritten ?? []));
|
|
383
|
+
}
|
|
355
384
|
}
|
|
356
385
|
const generators = [
|
|
357
386
|
runReactQuery && 'React Query',
|
|
@@ -530,7 +559,7 @@ async function generateMulti(options) {
|
|
|
530
559
|
dryRun,
|
|
531
560
|
schemaOnly,
|
|
532
561
|
schemaOnlyFilename: schemaOnly ? `${name}.graphql` : undefined,
|
|
533
|
-
}, useUnifiedCli ? { skipCli: true } :
|
|
562
|
+
}, useUnifiedCli ? { skipCli: true, targetName: name } : { targetName: name });
|
|
534
563
|
results.push({ name, result });
|
|
535
564
|
if (!result.success) {
|
|
536
565
|
hasError = true;
|
|
@@ -613,17 +642,24 @@ async function generateMulti(options) {
|
|
|
613
642
|
if (docsConfig.mcp) {
|
|
614
643
|
allMcpTools.push(...(0, docs_generator_1.getMultiTargetCliMcpTools)(docsInput));
|
|
615
644
|
}
|
|
616
|
-
if (docsConfig.skills) {
|
|
617
|
-
for (const skill of (0, docs_generator_1.generateMultiTargetSkills)(docsInput)) {
|
|
618
|
-
cliFilesToWrite.push({ path: node_path_1.default.posix.join('cli', skill.fileName), content: skill.content });
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
645
|
if (docsConfig.mcp && allMcpTools.length > 0) {
|
|
622
646
|
const mcpFile = (0, target_docs_generator_1.generateCombinedMcpConfig)(allMcpTools, toolName);
|
|
623
647
|
cliFilesToWrite.push({ path: node_path_1.default.posix.join('cli', mcpFile.fileName), content: mcpFile.content });
|
|
624
648
|
}
|
|
625
649
|
const { writeGeneratedFiles: writeFiles } = await Promise.resolve().then(() => __importStar(require('./output')));
|
|
626
650
|
await writeFiles(cliFilesToWrite, '.', [], { pruneStaleFiles: false });
|
|
651
|
+
if (docsConfig.skills) {
|
|
652
|
+
const cliSkillsToWrite = (0, docs_generator_1.generateMultiTargetSkills)(docsInput).map((skill) => ({
|
|
653
|
+
path: skill.fileName,
|
|
654
|
+
content: skill.content,
|
|
655
|
+
}));
|
|
656
|
+
const firstTargetResolved = (0, config_1.getConfigOptions)({
|
|
657
|
+
...(firstTargetConfig ?? {}),
|
|
658
|
+
...(cliOverrides ?? {}),
|
|
659
|
+
});
|
|
660
|
+
const skillsOutputDir = resolveSkillsOutputDir(firstTargetResolved, firstTargetResolved.output);
|
|
661
|
+
await writeFiles(cliSkillsToWrite, skillsOutputDir, [], { pruneStaleFiles: false });
|
|
662
|
+
}
|
|
627
663
|
}
|
|
628
664
|
// Generate root-root README if multi-target
|
|
629
665
|
if (names.length > 1 && targetInfos.length > 0 && !dryRun) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the workspace root directory.
|
|
3
|
+
*
|
|
4
|
+
* Search order:
|
|
5
|
+
* 1. pnpm-workspace.yaml (pnpm workspaces)
|
|
6
|
+
* 2. lerna.json (lerna workspaces)
|
|
7
|
+
* 3. package.json with "workspaces" field (npm/yarn workspaces)
|
|
8
|
+
* 4. Nearest package.json (fallback for non-workspace projects)
|
|
9
|
+
*
|
|
10
|
+
* @param cwd - Starting directory to search from (defaults to process.cwd())
|
|
11
|
+
* @returns The workspace root path, or null if nothing found
|
|
12
|
+
*/
|
|
13
|
+
export declare function findWorkspaceRoot(cwd?: string): string | null;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findWorkspaceRoot = findWorkspaceRoot;
|
|
4
|
+
/**
|
|
5
|
+
* Workspace detection utilities
|
|
6
|
+
*
|
|
7
|
+
* Finds the root of a workspace by walking up directories looking for
|
|
8
|
+
* workspace markers (pnpm-workspace.yaml, lerna.json, package.json with workspaces).
|
|
9
|
+
* Falls back to the nearest package.json directory.
|
|
10
|
+
*
|
|
11
|
+
* Inspired by @pgpmjs/env walkUp / resolvePnpmWorkspace patterns.
|
|
12
|
+
*/
|
|
13
|
+
const node_fs_1 = require("node:fs");
|
|
14
|
+
const node_path_1 = require("node:path");
|
|
15
|
+
/**
|
|
16
|
+
* Walk up directories from startDir looking for a file.
|
|
17
|
+
* Returns the directory containing the file, or null if not found.
|
|
18
|
+
*/
|
|
19
|
+
function walkUp(startDir, filename) {
|
|
20
|
+
let currentDir = (0, node_path_1.resolve)(startDir);
|
|
21
|
+
while (currentDir) {
|
|
22
|
+
const targetPath = (0, node_path_1.resolve)(currentDir, filename);
|
|
23
|
+
if ((0, node_fs_1.existsSync)(targetPath)) {
|
|
24
|
+
return currentDir;
|
|
25
|
+
}
|
|
26
|
+
const parentDir = (0, node_path_1.dirname)(currentDir);
|
|
27
|
+
if (parentDir === currentDir) {
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
currentDir = parentDir;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Find the first pnpm workspace root by looking for pnpm-workspace.yaml
|
|
36
|
+
*/
|
|
37
|
+
function findPnpmWorkspace(cwd) {
|
|
38
|
+
return walkUp(cwd, 'pnpm-workspace.yaml');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find the first lerna workspace root by looking for lerna.json
|
|
42
|
+
*/
|
|
43
|
+
function findLernaWorkspace(cwd) {
|
|
44
|
+
return walkUp(cwd, 'lerna.json');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Find the first npm/yarn workspace root by looking for package.json with workspaces field
|
|
48
|
+
*/
|
|
49
|
+
function findNpmWorkspace(cwd) {
|
|
50
|
+
let currentDir = (0, node_path_1.resolve)(cwd);
|
|
51
|
+
const root = (0, node_path_1.parse)(currentDir).root;
|
|
52
|
+
while (currentDir !== root) {
|
|
53
|
+
const packageJsonPath = (0, node_path_1.resolve)(currentDir, 'package.json');
|
|
54
|
+
if ((0, node_fs_1.existsSync)(packageJsonPath)) {
|
|
55
|
+
try {
|
|
56
|
+
const packageJson = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, 'utf8'));
|
|
57
|
+
if (packageJson.workspaces) {
|
|
58
|
+
return currentDir;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Ignore JSON parse errors
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
currentDir = (0, node_path_1.dirname)(currentDir);
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Find the nearest package.json directory (fallback)
|
|
71
|
+
*/
|
|
72
|
+
function findPackageRoot(cwd) {
|
|
73
|
+
return walkUp(cwd, 'package.json');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Find the workspace root directory.
|
|
77
|
+
*
|
|
78
|
+
* Search order:
|
|
79
|
+
* 1. pnpm-workspace.yaml (pnpm workspaces)
|
|
80
|
+
* 2. lerna.json (lerna workspaces)
|
|
81
|
+
* 3. package.json with "workspaces" field (npm/yarn workspaces)
|
|
82
|
+
* 4. Nearest package.json (fallback for non-workspace projects)
|
|
83
|
+
*
|
|
84
|
+
* @param cwd - Starting directory to search from (defaults to process.cwd())
|
|
85
|
+
* @returns The workspace root path, or null if nothing found
|
|
86
|
+
*/
|
|
87
|
+
function findWorkspaceRoot(cwd = process.cwd()) {
|
|
88
|
+
return (findPnpmWorkspace(cwd) ??
|
|
89
|
+
findLernaWorkspace(cwd) ??
|
|
90
|
+
findNpmWorkspace(cwd) ??
|
|
91
|
+
findPackageRoot(cwd));
|
|
92
|
+
}
|
|
@@ -11,6 +11,25 @@ function createNamedImportDeclaration(moduleSpecifier, namedImports, typeOnly =
|
|
|
11
11
|
decl.importKind = typeOnly ? 'type' : 'value';
|
|
12
12
|
return decl;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Build the command handler function type:
|
|
16
|
+
* (argv: Partial<Record<string, unknown>>, prompter: Inquirerer, options: CLIOptions) => Promise<void>
|
|
17
|
+
* This matches the actual exported handler signatures from table/custom command files.
|
|
18
|
+
*/
|
|
19
|
+
function buildCommandHandlerType() {
|
|
20
|
+
const argvParam = t.identifier('argv');
|
|
21
|
+
argvParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Partial'), t.tsTypeParameterInstantiation([
|
|
22
|
+
t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
23
|
+
t.tsStringKeyword(),
|
|
24
|
+
t.tsUnknownKeyword(),
|
|
25
|
+
])),
|
|
26
|
+
])));
|
|
27
|
+
const prompterParam = t.identifier('prompter');
|
|
28
|
+
prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
|
|
29
|
+
const optionsParam = t.identifier('options');
|
|
30
|
+
optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('CLIOptions')));
|
|
31
|
+
return t.tsFunctionType(null, [argvParam, prompterParam, optionsParam], t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Promise'), t.tsTypeParameterInstantiation([t.tsVoidKeyword()]))));
|
|
32
|
+
}
|
|
14
33
|
export function generateCommandMap(tables, customOperations, toolName) {
|
|
15
34
|
const statements = [];
|
|
16
35
|
statements.push(createNamedImportDeclaration('inquirerer', [
|
|
@@ -37,18 +56,17 @@ export function generateCommandMap(tables, customOperations, toolName) {
|
|
|
37
56
|
statements.push(createImportDeclaration(`./commands/${kebab}`, importName));
|
|
38
57
|
}
|
|
39
58
|
const mapProperties = commandEntries.map((entry) => t.objectProperty(t.stringLiteral(entry.kebab), t.identifier(entry.importName)));
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const createCommandMapAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
44
|
-
t.tsStringKeyword(),
|
|
45
|
-
t.tsFunctionType(null, [], t.tsTypeAnnotation(t.tsAnyKeyword())),
|
|
46
|
-
])));
|
|
59
|
+
// Build command handler type matching actual handler signature:
|
|
60
|
+
// (argv: Partial<Record<string, unknown>>, prompter: Inquirerer, options: CLIOptions) => Promise<void>
|
|
61
|
+
const commandHandlerType = buildCommandHandlerType();
|
|
47
62
|
const createCommandMapId = t.identifier('createCommandMap');
|
|
48
63
|
createCommandMapId.typeAnnotation = t.tsTypeAnnotation(t.tsParenthesizedType(t.tsFunctionType(null, [], t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
49
64
|
t.tsStringKeyword(),
|
|
50
|
-
|
|
65
|
+
commandHandlerType,
|
|
51
66
|
]))))));
|
|
67
|
+
const createCommandMapFunc = t.variableDeclaration('const', [
|
|
68
|
+
t.variableDeclarator(createCommandMapId, t.arrowFunctionExpression([], t.objectExpression(mapProperties))),
|
|
69
|
+
]);
|
|
52
70
|
statements.push(createCommandMapFunc);
|
|
53
71
|
const usageLines = [
|
|
54
72
|
'',
|
|
@@ -115,7 +133,7 @@ export function generateCommandMap(tables, customOperations, toolName) {
|
|
|
115
133
|
]),
|
|
116
134
|
]))),
|
|
117
135
|
]),
|
|
118
|
-
t.expressionStatement(t.assignmentExpression('=', t.identifier('command'), t.memberExpression(t.identifier('answer'), t.identifier('command')))),
|
|
136
|
+
t.expressionStatement(t.assignmentExpression('=', t.identifier('command'), t.tsAsExpression(t.memberExpression(t.identifier('answer'), t.identifier('command')), t.tsStringKeyword()))),
|
|
119
137
|
])),
|
|
120
138
|
t.variableDeclaration('const', [
|
|
121
139
|
t.variableDeclarator(t.identifier('commandFn'), t.memberExpression(t.identifier('commandMap'), t.identifier('command'), true)),
|
|
@@ -186,8 +204,15 @@ export function generateMultiTargetCommandMap(input) {
|
|
|
186
204
|
}
|
|
187
205
|
}
|
|
188
206
|
const mapProperties = commandEntries.map((entry) => t.objectProperty(t.stringLiteral(entry.kebab), t.identifier(entry.importName)));
|
|
207
|
+
// Build command handler type matching actual handler signature
|
|
208
|
+
const multiTargetCommandHandlerType = buildCommandHandlerType();
|
|
209
|
+
const multiTargetCreateCommandMapId = t.identifier('createCommandMap');
|
|
210
|
+
multiTargetCreateCommandMapId.typeAnnotation = t.tsTypeAnnotation(t.tsParenthesizedType(t.tsFunctionType(null, [], t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
211
|
+
t.tsStringKeyword(),
|
|
212
|
+
multiTargetCommandHandlerType,
|
|
213
|
+
]))))));
|
|
189
214
|
const createCommandMapFunc = t.variableDeclaration('const', [
|
|
190
|
-
t.variableDeclarator(
|
|
215
|
+
t.variableDeclarator(multiTargetCreateCommandMapId, t.arrowFunctionExpression([], t.objectExpression(mapProperties))),
|
|
191
216
|
]);
|
|
192
217
|
statements.push(createCommandMapFunc);
|
|
193
218
|
const usageLines = [
|
|
@@ -261,7 +286,7 @@ export function generateMultiTargetCommandMap(input) {
|
|
|
261
286
|
]),
|
|
262
287
|
]))),
|
|
263
288
|
]),
|
|
264
|
-
t.expressionStatement(t.assignmentExpression('=', t.identifier('command'), t.memberExpression(t.identifier('answer'), t.identifier('command')))),
|
|
289
|
+
t.expressionStatement(t.assignmentExpression('=', t.identifier('command'), t.tsAsExpression(t.memberExpression(t.identifier('answer'), t.identifier('command')), t.tsStringKeyword()))),
|
|
265
290
|
])),
|
|
266
291
|
t.variableDeclaration('const', [
|
|
267
292
|
t.variableDeclarator(t.identifier('commandFn'), t.memberExpression(t.identifier('commandMap'), t.identifier('command'), true)),
|