@constructive-io/graphql-codegen 4.0.2 → 4.1.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.
Files changed (93) hide show
  1. package/cli/handler.d.ts +13 -0
  2. package/cli/handler.js +74 -0
  3. package/cli/index.js +11 -57
  4. package/core/codegen/barrel.d.ts +1 -0
  5. package/core/codegen/barrel.js +5 -2
  6. package/core/codegen/cli/arg-mapper.d.ts +4 -0
  7. package/core/codegen/cli/arg-mapper.js +117 -0
  8. package/core/codegen/cli/command-map-generator.d.ts +16 -0
  9. package/core/codegen/cli/command-map-generator.js +338 -0
  10. package/core/codegen/cli/custom-command-generator.d.ts +8 -0
  11. package/core/codegen/cli/custom-command-generator.js +155 -0
  12. package/core/codegen/cli/docs-generator.d.ts +26 -0
  13. package/core/codegen/cli/docs-generator.js +1399 -0
  14. package/core/codegen/cli/executor-generator.d.ts +11 -0
  15. package/core/codegen/cli/executor-generator.js +217 -0
  16. package/core/codegen/cli/index.d.ts +53 -0
  17. package/core/codegen/cli/index.js +153 -0
  18. package/core/codegen/cli/infra-generator.d.ts +9 -0
  19. package/core/codegen/cli/infra-generator.js +1195 -0
  20. package/core/codegen/cli/table-command-generator.d.ts +7 -0
  21. package/core/codegen/cli/table-command-generator.js +323 -0
  22. package/core/codegen/docs-utils.d.ts +30 -0
  23. package/core/codegen/docs-utils.js +122 -0
  24. package/core/codegen/hooks-docs-generator.d.ts +6 -0
  25. package/core/codegen/hooks-docs-generator.js +468 -0
  26. package/core/codegen/orm/docs-generator.d.ts +6 -0
  27. package/core/codegen/orm/docs-generator.js +416 -0
  28. package/core/codegen/target-docs-generator.d.ts +20 -0
  29. package/core/codegen/target-docs-generator.js +110 -0
  30. package/core/database/index.d.ts +0 -12
  31. package/core/database/index.js +2 -19
  32. package/core/generate.d.ts +34 -2
  33. package/core/generate.js +453 -12
  34. package/core/index.d.ts +0 -2
  35. package/core/index.js +0 -2
  36. package/core/introspect/source/database.js +2 -2
  37. package/core/introspect/source/pgpm-module.js +2 -2
  38. package/core/output/index.d.ts +1 -1
  39. package/core/output/index.js +1 -2
  40. package/core/output/writer.d.ts +0 -10
  41. package/core/output/writer.js +0 -31
  42. package/esm/cli/handler.d.ts +13 -0
  43. package/esm/cli/handler.js +71 -0
  44. package/esm/cli/index.js +11 -57
  45. package/esm/core/codegen/barrel.d.ts +1 -0
  46. package/esm/core/codegen/barrel.js +5 -2
  47. package/esm/core/codegen/cli/arg-mapper.d.ts +4 -0
  48. package/esm/core/codegen/cli/arg-mapper.js +80 -0
  49. package/esm/core/codegen/cli/command-map-generator.d.ts +16 -0
  50. package/esm/core/codegen/cli/command-map-generator.js +301 -0
  51. package/esm/core/codegen/cli/custom-command-generator.d.ts +8 -0
  52. package/esm/core/codegen/cli/custom-command-generator.js +119 -0
  53. package/esm/core/codegen/cli/docs-generator.d.ts +26 -0
  54. package/esm/core/codegen/cli/docs-generator.js +1387 -0
  55. package/esm/core/codegen/cli/executor-generator.d.ts +11 -0
  56. package/esm/core/codegen/cli/executor-generator.js +180 -0
  57. package/esm/core/codegen/cli/index.d.ts +53 -0
  58. package/esm/core/codegen/cli/index.js +128 -0
  59. package/esm/core/codegen/cli/infra-generator.d.ts +9 -0
  60. package/esm/core/codegen/cli/infra-generator.js +1156 -0
  61. package/esm/core/codegen/cli/table-command-generator.d.ts +7 -0
  62. package/esm/core/codegen/cli/table-command-generator.js +287 -0
  63. package/esm/core/codegen/docs-utils.d.ts +30 -0
  64. package/esm/core/codegen/docs-utils.js +112 -0
  65. package/esm/core/codegen/hooks-docs-generator.d.ts +6 -0
  66. package/esm/core/codegen/hooks-docs-generator.js +462 -0
  67. package/esm/core/codegen/orm/docs-generator.d.ts +6 -0
  68. package/esm/core/codegen/orm/docs-generator.js +410 -0
  69. package/esm/core/codegen/target-docs-generator.d.ts +20 -0
  70. package/esm/core/codegen/target-docs-generator.js +105 -0
  71. package/esm/core/database/index.d.ts +0 -12
  72. package/esm/core/database/index.js +1 -17
  73. package/esm/core/generate.d.ts +34 -2
  74. package/esm/core/generate.js +417 -12
  75. package/esm/core/index.d.ts +0 -2
  76. package/esm/core/index.js +0 -2
  77. package/esm/core/introspect/source/database.js +2 -2
  78. package/esm/core/introspect/source/pgpm-module.js +2 -2
  79. package/esm/core/output/index.d.ts +1 -1
  80. package/esm/core/output/index.js +1 -1
  81. package/esm/core/output/writer.d.ts +0 -10
  82. package/esm/core/output/writer.js +0 -30
  83. package/esm/generators/index.d.ts +0 -3
  84. package/esm/generators/index.js +0 -3
  85. package/esm/index.d.ts +4 -3
  86. package/esm/index.js +4 -2
  87. package/esm/types/config.d.ts +78 -0
  88. package/generators/index.d.ts +0 -3
  89. package/generators/index.js +0 -3
  90. package/index.d.ts +4 -3
  91. package/index.js +7 -2
  92. package/package.json +8 -7
  93. package/types/config.d.ts +78 -0
@@ -0,0 +1,71 @@
1
+ import { findConfigFile, loadConfigFile } from '../core/config';
2
+ import { expandApiNamesToMultiTarget, expandSchemaDirToMultiTarget, generate, generateMulti } from '../core/generate';
3
+ import { buildDbConfig, buildGenerateOptions, camelizeArgv, codegenQuestions, hasResolvedCodegenSource, normalizeCodegenListOptions, printResult, seedArgvFromConfig, } from './shared';
4
+ export async function runCodegenHandler(argv, prompter) {
5
+ const args = camelizeArgv(argv);
6
+ const schemaOnly = Boolean(args.schemaOnly);
7
+ const hasSourceFlags = Boolean(args.endpoint || args.schemaFile || args.schemaDir || args.schemas || args.apiNames);
8
+ const configPath = args.config ||
9
+ (!hasSourceFlags ? findConfigFile() : undefined);
10
+ const targetName = args.target;
11
+ let fileConfig = {};
12
+ if (configPath) {
13
+ const loaded = await loadConfigFile(configPath);
14
+ if (!loaded.success) {
15
+ console.error('x', loaded.error);
16
+ process.exit(1);
17
+ }
18
+ const config = loaded.config;
19
+ const isMulti = !('endpoint' in config ||
20
+ 'schemaFile' in config ||
21
+ 'schemaDir' in config ||
22
+ 'db' in config);
23
+ if (isMulti) {
24
+ const targets = config;
25
+ if (targetName && !targets[targetName]) {
26
+ console.error('x', `Target "${targetName}" not found. Available: ${Object.keys(targets).join(', ')}`);
27
+ process.exit(1);
28
+ }
29
+ const cliOptions = buildDbConfig(normalizeCodegenListOptions(args));
30
+ const selectedTargets = targetName
31
+ ? { [targetName]: targets[targetName] }
32
+ : targets;
33
+ const { results, hasError } = await generateMulti({
34
+ configs: selectedTargets,
35
+ cliOverrides: cliOptions,
36
+ schemaOnly,
37
+ });
38
+ for (const { name, result } of results) {
39
+ console.log(`\n[${name}]`);
40
+ printResult(result);
41
+ }
42
+ if (hasError)
43
+ process.exit(1);
44
+ return;
45
+ }
46
+ fileConfig = config;
47
+ }
48
+ const seeded = seedArgvFromConfig(args, fileConfig);
49
+ const answers = hasResolvedCodegenSource(seeded)
50
+ ? seeded
51
+ : await prompter.prompt(seeded, codegenQuestions);
52
+ const options = buildGenerateOptions(answers, fileConfig);
53
+ const expandedApi = expandApiNamesToMultiTarget(options);
54
+ const expandedDir = expandSchemaDirToMultiTarget(options);
55
+ const expanded = expandedApi || expandedDir;
56
+ if (expanded) {
57
+ const { results, hasError } = await generateMulti({
58
+ configs: expanded,
59
+ schemaOnly,
60
+ });
61
+ for (const { name, result } of results) {
62
+ console.log(`\n[${name}]`);
63
+ printResult(result);
64
+ }
65
+ if (hasError)
66
+ process.exit(1);
67
+ return;
68
+ }
69
+ const result = await generate({ ...options, schemaOnly });
70
+ printResult(result);
71
+ }
package/esm/cli/index.js CHANGED
@@ -6,10 +6,7 @@
6
6
  * All business logic is in the core modules.
7
7
  */
8
8
  import { CLI, getPackageJson } from 'inquirerer';
9
- import { findConfigFile, loadConfigFile } from '../core/config';
10
- import { generate } from '../core/generate';
11
- import { mergeConfig } from '../types/config';
12
- import { buildDbConfig, buildGenerateOptions, camelizeArgv, codegenQuestions, hasResolvedCodegenSource, normalizeCodegenListOptions, printResult, seedArgvFromConfig, } from './shared';
9
+ import { runCodegenHandler } from './handler';
13
10
  const usage = `
14
11
  graphql-codegen - GraphQL SDK generator for Constructive databases
15
12
 
@@ -20,10 +17,12 @@ Source Options (choose one):
20
17
  -c, --config <path> Path to config file
21
18
  -e, --endpoint <url> GraphQL endpoint URL
22
19
  -s, --schema-file <path> Path to GraphQL schema file
20
+ --schema-dir <dir> Directory of .graphql files (auto-expands to multi-target)
23
21
 
24
22
  Database Options:
25
23
  --schemas <list> Comma-separated PostgreSQL schemas
26
24
  --api-names <list> Comma-separated API names (mutually exclusive with --schemas)
25
+ Multiple apiNames auto-expand to multi-target (one schema per API).
27
26
 
28
27
  Generator Options:
29
28
  --react-query Generate React Query hooks
@@ -34,6 +33,11 @@ Generator Options:
34
33
  --dry-run Preview without writing files
35
34
  -v, --verbose Show detailed output
36
35
 
36
+ Schema Export:
37
+ --schema-only Export GraphQL SDL instead of running full codegen.
38
+ Works with any source (endpoint, file, database, PGPM).
39
+ With multiple apiNames, writes one .graphql per API.
40
+
37
41
  -h, --help Show this help message
38
42
  --version Show version number
39
43
  `;
@@ -47,56 +51,7 @@ export const commands = async (argv, prompter, _options) => {
47
51
  console.log(usage);
48
52
  process.exit(0);
49
53
  }
50
- const hasSourceFlags = Boolean(argv.endpoint ||
51
- argv.e ||
52
- argv['schema-file'] ||
53
- argv.s ||
54
- argv.schemas ||
55
- argv['api-names']);
56
- const explicitConfigPath = (argv.config || argv.c);
57
- const configPath = explicitConfigPath || (!hasSourceFlags ? findConfigFile() : undefined);
58
- const targetName = (argv.target || argv.t);
59
- let fileConfig = {};
60
- if (configPath) {
61
- const loaded = await loadConfigFile(configPath);
62
- if (!loaded.success) {
63
- console.error('x', loaded.error);
64
- process.exit(1);
65
- }
66
- const config = loaded.config;
67
- const isMulti = !('endpoint' in config ||
68
- 'schemaFile' in config ||
69
- 'db' in config);
70
- if (isMulti) {
71
- const targets = config;
72
- const names = targetName ? [targetName] : Object.keys(targets);
73
- if (targetName && !targets[targetName]) {
74
- console.error('x', `Target "${targetName}" not found. Available: ${Object.keys(targets).join(', ')}`);
75
- process.exit(1);
76
- }
77
- const cliOptions = buildDbConfig(normalizeCodegenListOptions(camelizeArgv(argv)));
78
- let hasError = false;
79
- for (const name of names) {
80
- console.log(`\n[${name}]`);
81
- const result = await generate(mergeConfig(targets[name], cliOptions));
82
- printResult(result);
83
- if (!result.success)
84
- hasError = true;
85
- }
86
- prompter.close();
87
- if (hasError)
88
- process.exit(1);
89
- return argv;
90
- }
91
- fileConfig = config;
92
- }
93
- const seeded = seedArgvFromConfig(argv, fileConfig);
94
- const answers = hasResolvedCodegenSource(seeded)
95
- ? seeded
96
- : await prompter.prompt(seeded, codegenQuestions);
97
- const options = buildGenerateOptions(answers, fileConfig);
98
- const result = await generate(options);
99
- printResult(result);
54
+ await runCodegenHandler(argv, prompter);
100
55
  prompter.close();
101
56
  return argv;
102
57
  };
@@ -112,16 +67,15 @@ export const options = {
112
67
  a: 'authorization',
113
68
  v: 'verbose',
114
69
  },
70
+ boolean: ['schema-only'],
115
71
  string: [
116
72
  'config',
117
73
  'endpoint',
118
74
  'schema-file',
75
+ 'schema-dir',
119
76
  'output',
120
77
  'target',
121
78
  'authorization',
122
- 'pgpm-module-path',
123
- 'pgpm-workspace-path',
124
- 'pgpm-module-name',
125
79
  'schemas',
126
80
  'api-names',
127
81
  ],
@@ -27,6 +27,7 @@ export interface RootBarrelOptions {
27
27
  hasTypes?: boolean;
28
28
  hasHooks?: boolean;
29
29
  hasOrm?: boolean;
30
+ hasCli?: boolean;
30
31
  }
31
32
  /**
32
33
  * Generate the root index.ts barrel file for the output directory.
@@ -135,7 +135,7 @@ export function generateMainBarrel(tables, options = {}) {
135
135
  * Re-exports from subdirectories based on which generators are enabled.
136
136
  */
137
137
  export function generateRootBarrel(options = {}) {
138
- const { hasTypes = false, hasHooks = false, hasOrm = false } = options;
138
+ const { hasTypes = false, hasHooks = false, hasOrm = false, hasCli = false } = options;
139
139
  const statements = [];
140
140
  if (hasTypes) {
141
141
  statements.push(exportAllFrom('./types'));
@@ -146,7 +146,10 @@ export function generateRootBarrel(options = {}) {
146
146
  if (hasOrm) {
147
147
  statements.push(exportAllFrom('./orm'));
148
148
  }
149
- // Add file header as leading comment on first statement
149
+ if (hasCli) {
150
+ statements.push(exportAllFrom('./cli'));
151
+ }
152
+ // Add file headeras leading comment on first statement
150
153
  if (statements.length > 0) {
151
154
  addJSDocComment(statements[0], [
152
155
  'Generated SDK - auto-generated, do not edit',
@@ -0,0 +1,4 @@
1
+ import * as t from '@babel/types';
2
+ import type { CleanArgument } from '../../../types/schema';
3
+ export declare function buildQuestionObject(arg: CleanArgument): t.ObjectExpression;
4
+ export declare function buildQuestionsArray(args: CleanArgument[]): t.ArrayExpression;
@@ -0,0 +1,80 @@
1
+ import * as t from '@babel/types';
2
+ function unwrapNonNull(typeRef) {
3
+ if (typeRef.kind === 'NON_NULL' && typeRef.ofType) {
4
+ return { inner: typeRef.ofType, required: true };
5
+ }
6
+ return { inner: typeRef, required: false };
7
+ }
8
+ function resolveBaseType(typeRef) {
9
+ if ((typeRef.kind === 'NON_NULL' || typeRef.kind === 'LIST') && typeRef.ofType) {
10
+ return resolveBaseType(typeRef.ofType);
11
+ }
12
+ return typeRef;
13
+ }
14
+ export function buildQuestionObject(arg) {
15
+ const { inner, required } = unwrapNonNull(arg.type);
16
+ const base = resolveBaseType(arg.type);
17
+ const props = [];
18
+ if (base.kind === 'ENUM' && base.enumValues && base.enumValues.length > 0) {
19
+ props.push(t.objectProperty(t.identifier('type'), t.stringLiteral('autocomplete')));
20
+ props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(arg.name)));
21
+ props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || arg.name)));
22
+ props.push(t.objectProperty(t.identifier('options'), t.arrayExpression(base.enumValues.map((v) => t.stringLiteral(v)))));
23
+ }
24
+ else if (base.kind === 'SCALAR' && base.name === 'Boolean') {
25
+ props.push(t.objectProperty(t.identifier('type'), t.stringLiteral('confirm')));
26
+ props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(arg.name)));
27
+ props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || arg.name)));
28
+ props.push(t.objectProperty(t.identifier('default'), t.booleanLiteral(false)));
29
+ }
30
+ else if (base.kind === 'SCALAR' &&
31
+ (base.name === 'Int' || base.name === 'Float')) {
32
+ props.push(t.objectProperty(t.identifier('type'), t.stringLiteral('text')));
33
+ props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(arg.name)));
34
+ props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || `${arg.name} (number)`)));
35
+ }
36
+ else if (inner.kind === 'INPUT_OBJECT' && inner.inputFields) {
37
+ return buildInputObjectQuestion(arg.name, inner, required);
38
+ }
39
+ else {
40
+ props.push(t.objectProperty(t.identifier('type'), t.stringLiteral('text')));
41
+ props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(arg.name)));
42
+ props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || arg.name)));
43
+ }
44
+ if (required) {
45
+ props.push(t.objectProperty(t.identifier('required'), t.booleanLiteral(true)));
46
+ }
47
+ return t.objectExpression(props);
48
+ }
49
+ function buildInputObjectQuestion(_name, typeRef, _required) {
50
+ if (typeRef.inputFields && typeRef.inputFields.length > 0) {
51
+ const firstField = typeRef.inputFields[0];
52
+ return buildQuestionObject(firstField);
53
+ }
54
+ return t.objectExpression([
55
+ t.objectProperty(t.identifier('type'), t.stringLiteral('text')),
56
+ t.objectProperty(t.identifier('name'), t.stringLiteral(_name)),
57
+ t.objectProperty(t.identifier('message'), t.stringLiteral(_name)),
58
+ ]);
59
+ }
60
+ export function buildQuestionsArray(args) {
61
+ const questions = [];
62
+ for (const arg of args) {
63
+ const base = resolveBaseType(arg.type);
64
+ const { inner } = unwrapNonNull(arg.type);
65
+ if (inner.kind === 'INPUT_OBJECT' && inner.inputFields) {
66
+ for (const field of inner.inputFields) {
67
+ questions.push(buildQuestionObject(field));
68
+ }
69
+ }
70
+ else if (base.kind === 'INPUT_OBJECT' && base.inputFields) {
71
+ for (const field of base.inputFields) {
72
+ questions.push(buildQuestionObject(field));
73
+ }
74
+ }
75
+ else {
76
+ questions.push(buildQuestionObject(arg));
77
+ }
78
+ }
79
+ return t.arrayExpression(questions);
80
+ }
@@ -0,0 +1,16 @@
1
+ import type { CleanTable, CleanOperation } from '../../../types/schema';
2
+ import type { GeneratedFile } from './executor-generator';
3
+ export declare function generateCommandMap(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): GeneratedFile;
4
+ export interface MultiTargetCommandMapInput {
5
+ toolName: string;
6
+ builtinNames: {
7
+ auth: string;
8
+ context: string;
9
+ };
10
+ targets: Array<{
11
+ name: string;
12
+ tables: CleanTable[];
13
+ customOperations: CleanOperation[];
14
+ }>;
15
+ }
16
+ export declare function generateMultiTargetCommandMap(input: MultiTargetCommandMapInput): GeneratedFile;
@@ -0,0 +1,301 @@
1
+ import * as t from '@babel/types';
2
+ import { toKebabCase } from 'komoji';
3
+ import { generateCode } from '../babel-ast';
4
+ import { getGeneratedFileHeader, getTableNames } from '../utils';
5
+ function createImportDeclaration(moduleSpecifier, defaultImportName) {
6
+ return t.importDeclaration([t.importDefaultSpecifier(t.identifier(defaultImportName))], t.stringLiteral(moduleSpecifier));
7
+ }
8
+ function createNamedImportDeclaration(moduleSpecifier, namedImports, typeOnly = false) {
9
+ const specifiers = namedImports.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
10
+ const decl = t.importDeclaration(specifiers, t.stringLiteral(moduleSpecifier));
11
+ decl.importKind = typeOnly ? 'type' : 'value';
12
+ return decl;
13
+ }
14
+ export function generateCommandMap(tables, customOperations, toolName) {
15
+ const statements = [];
16
+ statements.push(createNamedImportDeclaration('inquirerer', [
17
+ 'CLIOptions',
18
+ 'Inquirerer',
19
+ 'extractFirst',
20
+ ]));
21
+ const commandEntries = [];
22
+ commandEntries.push({ kebab: 'context', importName: 'contextCmd' });
23
+ statements.push(createImportDeclaration('./commands/context', 'contextCmd'));
24
+ commandEntries.push({ kebab: 'auth', importName: 'authCmd' });
25
+ statements.push(createImportDeclaration('./commands/auth', 'authCmd'));
26
+ for (const table of tables) {
27
+ const { singularName } = getTableNames(table);
28
+ const kebab = toKebabCase(singularName);
29
+ const importName = `${singularName}Cmd`;
30
+ commandEntries.push({ kebab, importName });
31
+ statements.push(createImportDeclaration(`./commands/${kebab}`, importName));
32
+ }
33
+ for (const op of customOperations) {
34
+ const kebab = toKebabCase(op.name);
35
+ const importName = `${op.name}Cmd`;
36
+ commandEntries.push({ kebab, importName });
37
+ statements.push(createImportDeclaration(`./commands/${kebab}`, importName));
38
+ }
39
+ const mapProperties = commandEntries.map((entry) => t.objectProperty(t.stringLiteral(entry.kebab), t.identifier(entry.importName)));
40
+ const createCommandMapFunc = t.variableDeclaration('const', [
41
+ t.variableDeclarator(t.identifier('createCommandMap'), t.arrowFunctionExpression([], t.objectExpression(mapProperties))),
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
+ ])));
47
+ const createCommandMapId = t.identifier('createCommandMap');
48
+ createCommandMapId.typeAnnotation = t.tsTypeAnnotation(t.tsParenthesizedType(t.tsFunctionType(null, [], t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
49
+ t.tsStringKeyword(),
50
+ t.tsFunctionType(null, [], t.tsTypeAnnotation(t.tsUnknownKeyword())),
51
+ ]))))));
52
+ statements.push(createCommandMapFunc);
53
+ const usageLines = [
54
+ '',
55
+ `${toolName} <command>`,
56
+ '',
57
+ 'Commands:',
58
+ ' context Manage API contexts',
59
+ ' auth Manage authentication',
60
+ ];
61
+ for (const table of tables) {
62
+ const { singularName } = getTableNames(table);
63
+ const kebab = toKebabCase(singularName);
64
+ usageLines.push(` ${kebab.padEnd(20)} ${singularName} CRUD operations`);
65
+ }
66
+ for (const op of customOperations) {
67
+ const kebab = toKebabCase(op.name);
68
+ usageLines.push(` ${kebab.padEnd(20)} ${op.description || op.name}`);
69
+ }
70
+ usageLines.push('');
71
+ usageLines.push(' --help, -h Show this help message');
72
+ usageLines.push(' --version, -v Show version');
73
+ usageLines.push('');
74
+ statements.push(t.variableDeclaration('const', [
75
+ t.variableDeclarator(t.identifier('usage'), t.stringLiteral(usageLines.join('\n'))),
76
+ ]));
77
+ const argvParam = t.identifier('argv');
78
+ argvParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Partial'), t.tsTypeParameterInstantiation([
79
+ t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
80
+ t.tsStringKeyword(),
81
+ t.tsUnknownKeyword(),
82
+ ])),
83
+ ])));
84
+ const prompterParam = t.identifier('prompter');
85
+ prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
86
+ const optionsParam = t.identifier('options');
87
+ optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('CLIOptions')));
88
+ const commandsBody = [
89
+ t.ifStatement(t.logicalExpression('||', t.memberExpression(t.identifier('argv'), t.identifier('help')), t.memberExpression(t.identifier('argv'), t.identifier('h'))), t.blockStatement([
90
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [t.identifier('usage')])),
91
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(0)])),
92
+ ])),
93
+ t.variableDeclaration('let', [
94
+ t.variableDeclarator(t.objectPattern([
95
+ t.objectProperty(t.identifier('first'), t.identifier('command')),
96
+ t.objectProperty(t.identifier('newArgv'), t.identifier('newArgv'), false, true),
97
+ ]), t.callExpression(t.identifier('extractFirst'), [
98
+ t.identifier('argv'),
99
+ ])),
100
+ ]),
101
+ t.variableDeclaration('const', [
102
+ t.variableDeclarator(t.identifier('commandMap'), t.callExpression(t.identifier('createCommandMap'), [])),
103
+ ]),
104
+ t.ifStatement(t.unaryExpression('!', t.identifier('command')), t.blockStatement([
105
+ t.variableDeclaration('const', [
106
+ t.variableDeclarator(t.identifier('answer'), t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('prompter'), t.identifier('prompt')), [
107
+ t.identifier('argv'),
108
+ t.arrayExpression([
109
+ t.objectExpression([
110
+ t.objectProperty(t.identifier('type'), t.stringLiteral('autocomplete')),
111
+ t.objectProperty(t.identifier('name'), t.stringLiteral('command')),
112
+ t.objectProperty(t.identifier('message'), t.stringLiteral('What do you want to do?')),
113
+ t.objectProperty(t.identifier('options'), t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('keys')), [t.identifier('commandMap')])),
114
+ ]),
115
+ ]),
116
+ ]))),
117
+ ]),
118
+ t.expressionStatement(t.assignmentExpression('=', t.identifier('command'), t.memberExpression(t.identifier('answer'), t.identifier('command')))),
119
+ ])),
120
+ t.variableDeclaration('const', [
121
+ t.variableDeclarator(t.identifier('commandFn'), t.memberExpression(t.identifier('commandMap'), t.identifier('command'), true)),
122
+ ]),
123
+ t.ifStatement(t.unaryExpression('!', t.identifier('commandFn')), t.blockStatement([
124
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [t.identifier('usage')])),
125
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('error')), [
126
+ t.templateLiteral([
127
+ t.templateElement({
128
+ raw: 'Unknown command: ',
129
+ cooked: 'Unknown command: ',
130
+ }),
131
+ t.templateElement({ raw: '', cooked: '' }, true),
132
+ ], [t.identifier('command')]),
133
+ ])),
134
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(1)])),
135
+ ])),
136
+ t.expressionStatement(t.awaitExpression(t.callExpression(t.identifier('commandFn'), [
137
+ t.identifier('newArgv'),
138
+ t.identifier('prompter'),
139
+ t.identifier('options'),
140
+ ]))),
141
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('prompter'), t.identifier('close')), [])),
142
+ t.returnStatement(t.identifier('argv')),
143
+ ];
144
+ const commandsFunc = t.arrowFunctionExpression([argvParam, prompterParam, optionsParam], t.blockStatement(commandsBody), true);
145
+ const commandsDecl = t.variableDeclaration('const', [
146
+ t.variableDeclarator(t.identifier('commands'), commandsFunc),
147
+ ]);
148
+ statements.push(t.exportNamedDeclaration(commandsDecl));
149
+ const header = getGeneratedFileHeader('CLI command map and entry point');
150
+ const code = generateCode(statements);
151
+ return {
152
+ fileName: 'commands.ts',
153
+ content: header + '\n' + code,
154
+ };
155
+ }
156
+ export function generateMultiTargetCommandMap(input) {
157
+ const { toolName, builtinNames, targets } = input;
158
+ const statements = [];
159
+ statements.push(createNamedImportDeclaration('inquirerer', [
160
+ 'CLIOptions',
161
+ 'Inquirerer',
162
+ 'extractFirst',
163
+ ]));
164
+ const commandEntries = [];
165
+ const contextImportName = `${builtinNames.context}Cmd`;
166
+ commandEntries.push({ kebab: builtinNames.context, importName: contextImportName });
167
+ statements.push(createImportDeclaration(`./commands/${builtinNames.context}`, contextImportName));
168
+ const authImportName = `${builtinNames.auth}Cmd`;
169
+ commandEntries.push({ kebab: builtinNames.auth, importName: authImportName });
170
+ statements.push(createImportDeclaration(`./commands/${builtinNames.auth}`, authImportName));
171
+ for (const target of targets) {
172
+ for (const table of target.tables) {
173
+ const { singularName } = getTableNames(table);
174
+ const kebab = toKebabCase(singularName);
175
+ const prefixedKebab = `${target.name}:${kebab}`;
176
+ const importName = `${target.name}${singularName[0].toUpperCase()}${singularName.slice(1)}Cmd`;
177
+ commandEntries.push({ kebab: prefixedKebab, importName });
178
+ statements.push(createImportDeclaration(`./commands/${target.name}/${kebab}`, importName));
179
+ }
180
+ for (const op of target.customOperations) {
181
+ const kebab = toKebabCase(op.name);
182
+ const prefixedKebab = `${target.name}:${kebab}`;
183
+ const importName = `${target.name}${op.name[0].toUpperCase()}${op.name.slice(1)}Cmd`;
184
+ commandEntries.push({ kebab: prefixedKebab, importName });
185
+ statements.push(createImportDeclaration(`./commands/${target.name}/${kebab}`, importName));
186
+ }
187
+ }
188
+ const mapProperties = commandEntries.map((entry) => t.objectProperty(t.stringLiteral(entry.kebab), t.identifier(entry.importName)));
189
+ const createCommandMapFunc = t.variableDeclaration('const', [
190
+ t.variableDeclarator(t.identifier('createCommandMap'), t.arrowFunctionExpression([], t.objectExpression(mapProperties))),
191
+ ]);
192
+ statements.push(createCommandMapFunc);
193
+ const usageLines = [
194
+ '',
195
+ `${toolName} <command>`,
196
+ '',
197
+ 'Commands:',
198
+ ` ${builtinNames.context.padEnd(20)} Manage API contexts`,
199
+ ` ${builtinNames.auth.padEnd(20)} Manage authentication`,
200
+ ];
201
+ for (const target of targets) {
202
+ usageLines.push('');
203
+ usageLines.push(` ${target.name}:`);
204
+ for (const table of target.tables) {
205
+ const { singularName } = getTableNames(table);
206
+ const kebab = toKebabCase(singularName);
207
+ const cmd = `${target.name}:${kebab}`;
208
+ usageLines.push(` ${cmd.padEnd(22)} ${singularName} CRUD operations`);
209
+ }
210
+ for (const op of target.customOperations) {
211
+ const kebab = toKebabCase(op.name);
212
+ const cmd = `${target.name}:${kebab}`;
213
+ usageLines.push(` ${cmd.padEnd(22)} ${op.description || op.name}`);
214
+ }
215
+ }
216
+ usageLines.push('');
217
+ usageLines.push(' --help, -h Show this help message');
218
+ usageLines.push(' --version, -v Show version');
219
+ usageLines.push('');
220
+ statements.push(t.variableDeclaration('const', [
221
+ t.variableDeclarator(t.identifier('usage'), t.stringLiteral(usageLines.join('\n'))),
222
+ ]));
223
+ const argvParam = t.identifier('argv');
224
+ argvParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Partial'), t.tsTypeParameterInstantiation([
225
+ t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
226
+ t.tsStringKeyword(),
227
+ t.tsUnknownKeyword(),
228
+ ])),
229
+ ])));
230
+ const prompterParam = t.identifier('prompter');
231
+ prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
232
+ const optionsParam = t.identifier('options');
233
+ optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('CLIOptions')));
234
+ const commandsBody = [
235
+ t.ifStatement(t.logicalExpression('||', t.memberExpression(t.identifier('argv'), t.identifier('help')), t.memberExpression(t.identifier('argv'), t.identifier('h'))), t.blockStatement([
236
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [t.identifier('usage')])),
237
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(0)])),
238
+ ])),
239
+ t.variableDeclaration('let', [
240
+ t.variableDeclarator(t.objectPattern([
241
+ t.objectProperty(t.identifier('first'), t.identifier('command')),
242
+ t.objectProperty(t.identifier('newArgv'), t.identifier('newArgv'), false, true),
243
+ ]), t.callExpression(t.identifier('extractFirst'), [
244
+ t.identifier('argv'),
245
+ ])),
246
+ ]),
247
+ t.variableDeclaration('const', [
248
+ t.variableDeclarator(t.identifier('commandMap'), t.callExpression(t.identifier('createCommandMap'), [])),
249
+ ]),
250
+ t.ifStatement(t.unaryExpression('!', t.identifier('command')), t.blockStatement([
251
+ t.variableDeclaration('const', [
252
+ t.variableDeclarator(t.identifier('answer'), t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('prompter'), t.identifier('prompt')), [
253
+ t.identifier('argv'),
254
+ t.arrayExpression([
255
+ t.objectExpression([
256
+ t.objectProperty(t.identifier('type'), t.stringLiteral('autocomplete')),
257
+ t.objectProperty(t.identifier('name'), t.stringLiteral('command')),
258
+ t.objectProperty(t.identifier('message'), t.stringLiteral('What do you want to do?')),
259
+ t.objectProperty(t.identifier('options'), t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('keys')), [t.identifier('commandMap')])),
260
+ ]),
261
+ ]),
262
+ ]))),
263
+ ]),
264
+ t.expressionStatement(t.assignmentExpression('=', t.identifier('command'), t.memberExpression(t.identifier('answer'), t.identifier('command')))),
265
+ ])),
266
+ t.variableDeclaration('const', [
267
+ t.variableDeclarator(t.identifier('commandFn'), t.memberExpression(t.identifier('commandMap'), t.identifier('command'), true)),
268
+ ]),
269
+ t.ifStatement(t.unaryExpression('!', t.identifier('commandFn')), t.blockStatement([
270
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [t.identifier('usage')])),
271
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('error')), [
272
+ t.templateLiteral([
273
+ t.templateElement({
274
+ raw: 'Unknown command: ',
275
+ cooked: 'Unknown command: ',
276
+ }),
277
+ t.templateElement({ raw: '', cooked: '' }, true),
278
+ ], [t.identifier('command')]),
279
+ ])),
280
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(1)])),
281
+ ])),
282
+ t.expressionStatement(t.awaitExpression(t.callExpression(t.identifier('commandFn'), [
283
+ t.identifier('newArgv'),
284
+ t.identifier('prompter'),
285
+ t.identifier('options'),
286
+ ]))),
287
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('prompter'), t.identifier('close')), [])),
288
+ t.returnStatement(t.identifier('argv')),
289
+ ];
290
+ const commandsFunc = t.arrowFunctionExpression([argvParam, prompterParam, optionsParam], t.blockStatement(commandsBody), true);
291
+ const commandsDecl = t.variableDeclaration('const', [
292
+ t.variableDeclarator(t.identifier('commands'), commandsFunc),
293
+ ]);
294
+ statements.push(t.exportNamedDeclaration(commandsDecl));
295
+ const header = getGeneratedFileHeader('Multi-target CLI command map and entry point');
296
+ const code = generateCode(statements);
297
+ return {
298
+ fileName: 'commands.ts',
299
+ content: header + '\n' + code,
300
+ };
301
+ }
@@ -0,0 +1,8 @@
1
+ import type { CleanOperation } from '../../../types/schema';
2
+ import type { GeneratedFile } from './executor-generator';
3
+ export interface CustomCommandOptions {
4
+ targetName?: string;
5
+ executorImportPath?: string;
6
+ saveToken?: boolean;
7
+ }
8
+ export declare function generateCustomCommand(op: CleanOperation, options?: CustomCommandOptions): GeneratedFile;