@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.
- package/cli/handler.d.ts +13 -0
- package/cli/handler.js +74 -0
- package/cli/index.js +11 -57
- package/core/codegen/barrel.d.ts +1 -0
- package/core/codegen/barrel.js +5 -2
- package/core/codegen/cli/arg-mapper.d.ts +4 -0
- package/core/codegen/cli/arg-mapper.js +117 -0
- package/core/codegen/cli/command-map-generator.d.ts +16 -0
- package/core/codegen/cli/command-map-generator.js +338 -0
- package/core/codegen/cli/custom-command-generator.d.ts +8 -0
- package/core/codegen/cli/custom-command-generator.js +155 -0
- package/core/codegen/cli/docs-generator.d.ts +26 -0
- package/core/codegen/cli/docs-generator.js +1399 -0
- package/core/codegen/cli/executor-generator.d.ts +11 -0
- package/core/codegen/cli/executor-generator.js +217 -0
- package/core/codegen/cli/index.d.ts +53 -0
- package/core/codegen/cli/index.js +153 -0
- package/core/codegen/cli/infra-generator.d.ts +9 -0
- package/core/codegen/cli/infra-generator.js +1195 -0
- package/core/codegen/cli/table-command-generator.d.ts +7 -0
- package/core/codegen/cli/table-command-generator.js +323 -0
- package/core/codegen/docs-utils.d.ts +30 -0
- package/core/codegen/docs-utils.js +122 -0
- package/core/codegen/hooks-docs-generator.d.ts +6 -0
- package/core/codegen/hooks-docs-generator.js +468 -0
- package/core/codegen/orm/docs-generator.d.ts +6 -0
- package/core/codegen/orm/docs-generator.js +416 -0
- package/core/codegen/target-docs-generator.d.ts +20 -0
- package/core/codegen/target-docs-generator.js +110 -0
- package/core/database/index.d.ts +0 -12
- package/core/database/index.js +2 -19
- package/core/generate.d.ts +34 -2
- package/core/generate.js +453 -12
- package/core/index.d.ts +0 -2
- package/core/index.js +0 -2
- package/core/introspect/source/database.js +2 -2
- package/core/introspect/source/pgpm-module.js +2 -2
- package/core/output/index.d.ts +1 -1
- package/core/output/index.js +1 -2
- package/core/output/writer.d.ts +0 -10
- package/core/output/writer.js +0 -31
- package/esm/cli/handler.d.ts +13 -0
- package/esm/cli/handler.js +71 -0
- package/esm/cli/index.js +11 -57
- package/esm/core/codegen/barrel.d.ts +1 -0
- package/esm/core/codegen/barrel.js +5 -2
- package/esm/core/codegen/cli/arg-mapper.d.ts +4 -0
- package/esm/core/codegen/cli/arg-mapper.js +80 -0
- package/esm/core/codegen/cli/command-map-generator.d.ts +16 -0
- package/esm/core/codegen/cli/command-map-generator.js +301 -0
- package/esm/core/codegen/cli/custom-command-generator.d.ts +8 -0
- package/esm/core/codegen/cli/custom-command-generator.js +119 -0
- package/esm/core/codegen/cli/docs-generator.d.ts +26 -0
- package/esm/core/codegen/cli/docs-generator.js +1387 -0
- package/esm/core/codegen/cli/executor-generator.d.ts +11 -0
- package/esm/core/codegen/cli/executor-generator.js +180 -0
- package/esm/core/codegen/cli/index.d.ts +53 -0
- package/esm/core/codegen/cli/index.js +128 -0
- package/esm/core/codegen/cli/infra-generator.d.ts +9 -0
- package/esm/core/codegen/cli/infra-generator.js +1156 -0
- package/esm/core/codegen/cli/table-command-generator.d.ts +7 -0
- package/esm/core/codegen/cli/table-command-generator.js +287 -0
- package/esm/core/codegen/docs-utils.d.ts +30 -0
- package/esm/core/codegen/docs-utils.js +112 -0
- package/esm/core/codegen/hooks-docs-generator.d.ts +6 -0
- package/esm/core/codegen/hooks-docs-generator.js +462 -0
- package/esm/core/codegen/orm/docs-generator.d.ts +6 -0
- package/esm/core/codegen/orm/docs-generator.js +410 -0
- package/esm/core/codegen/target-docs-generator.d.ts +20 -0
- package/esm/core/codegen/target-docs-generator.js +105 -0
- package/esm/core/database/index.d.ts +0 -12
- package/esm/core/database/index.js +1 -17
- package/esm/core/generate.d.ts +34 -2
- package/esm/core/generate.js +417 -12
- package/esm/core/index.d.ts +0 -2
- package/esm/core/index.js +0 -2
- package/esm/core/introspect/source/database.js +2 -2
- package/esm/core/introspect/source/pgpm-module.js +2 -2
- package/esm/core/output/index.d.ts +1 -1
- package/esm/core/output/index.js +1 -1
- package/esm/core/output/writer.d.ts +0 -10
- package/esm/core/output/writer.js +0 -30
- package/esm/generators/index.d.ts +0 -3
- package/esm/generators/index.js +0 -3
- package/esm/index.d.ts +4 -3
- package/esm/index.js +4 -2
- package/esm/types/config.d.ts +78 -0
- package/generators/index.d.ts +0 -3
- package/generators/index.js +0 -3
- package/index.d.ts +4 -3
- package/index.js +7 -2
- package/package.json +8 -7
- 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 {
|
|
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
|
-
|
|
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
|
],
|
|
@@ -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
|
-
|
|
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,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;
|