@constructive-io/graphql-codegen 4.0.1 → 4.1.0
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 -60
- package/cli/shared.d.ts +1 -1
- package/cli/shared.js +2 -5
- 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 -60
- package/esm/cli/shared.d.ts +1 -1
- package/esm/cli/shared.js +2 -5
- 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,9 +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 { buildDbConfig, buildGenerateOptions, camelizeArgv, codegenQuestions, hasResolvedCodegenSource, normalizeCodegenListOptions, printResult, seedArgvFromConfig, } from './shared';
|
|
9
|
+
import { runCodegenHandler } from './handler';
|
|
12
10
|
const usage = `
|
|
13
11
|
graphql-codegen - GraphQL SDK generator for Constructive databases
|
|
14
12
|
|
|
@@ -19,10 +17,12 @@ Source Options (choose one):
|
|
|
19
17
|
-c, --config <path> Path to config file
|
|
20
18
|
-e, --endpoint <url> GraphQL endpoint URL
|
|
21
19
|
-s, --schema-file <path> Path to GraphQL schema file
|
|
20
|
+
--schema-dir <dir> Directory of .graphql files (auto-expands to multi-target)
|
|
22
21
|
|
|
23
22
|
Database Options:
|
|
24
23
|
--schemas <list> Comma-separated PostgreSQL schemas
|
|
25
24
|
--api-names <list> Comma-separated API names (mutually exclusive with --schemas)
|
|
25
|
+
Multiple apiNames auto-expand to multi-target (one schema per API).
|
|
26
26
|
|
|
27
27
|
Generator Options:
|
|
28
28
|
--react-query Generate React Query hooks
|
|
@@ -33,6 +33,11 @@ Generator Options:
|
|
|
33
33
|
--dry-run Preview without writing files
|
|
34
34
|
-v, --verbose Show detailed output
|
|
35
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
|
+
|
|
36
41
|
-h, --help Show this help message
|
|
37
42
|
--version Show version number
|
|
38
43
|
`;
|
|
@@ -46,60 +51,7 @@ export const commands = async (argv, prompter, _options) => {
|
|
|
46
51
|
console.log(usage);
|
|
47
52
|
process.exit(0);
|
|
48
53
|
}
|
|
49
|
-
|
|
50
|
-
argv.e ||
|
|
51
|
-
argv['schema-file'] ||
|
|
52
|
-
argv.s ||
|
|
53
|
-
argv.schemas ||
|
|
54
|
-
argv['api-names']);
|
|
55
|
-
const explicitConfigPath = (argv.config || argv.c);
|
|
56
|
-
const configPath = explicitConfigPath || (!hasSourceFlags ? findConfigFile() : undefined);
|
|
57
|
-
const targetName = (argv.target || argv.t);
|
|
58
|
-
let fileConfig = {};
|
|
59
|
-
if (configPath) {
|
|
60
|
-
const loaded = await loadConfigFile(configPath);
|
|
61
|
-
if (!loaded.success) {
|
|
62
|
-
console.error('x', loaded.error);
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
const config = loaded.config;
|
|
66
|
-
const isMulti = !('endpoint' in config ||
|
|
67
|
-
'schemaFile' in config ||
|
|
68
|
-
'db' in config);
|
|
69
|
-
if (isMulti) {
|
|
70
|
-
const targets = config;
|
|
71
|
-
const names = targetName ? [targetName] : Object.keys(targets);
|
|
72
|
-
if (targetName && !targets[targetName]) {
|
|
73
|
-
console.error('x', `Target "${targetName}" not found. Available: ${Object.keys(targets).join(', ')}`);
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
const cliOptions = buildDbConfig(normalizeCodegenListOptions(camelizeArgv(argv)));
|
|
77
|
-
let hasError = false;
|
|
78
|
-
for (const name of names) {
|
|
79
|
-
console.log(`\n[${name}]`);
|
|
80
|
-
const targetConfig = { ...targets[name], ...cliOptions };
|
|
81
|
-
if (targets[name].db && targetConfig.db) {
|
|
82
|
-
targetConfig.db = { ...targets[name].db, ...targetConfig.db };
|
|
83
|
-
}
|
|
84
|
-
const result = await generate(targetConfig);
|
|
85
|
-
printResult(result);
|
|
86
|
-
if (!result.success)
|
|
87
|
-
hasError = true;
|
|
88
|
-
}
|
|
89
|
-
prompter.close();
|
|
90
|
-
if (hasError)
|
|
91
|
-
process.exit(1);
|
|
92
|
-
return argv;
|
|
93
|
-
}
|
|
94
|
-
fileConfig = config;
|
|
95
|
-
}
|
|
96
|
-
const seeded = seedArgvFromConfig(argv, fileConfig);
|
|
97
|
-
const answers = hasResolvedCodegenSource(seeded)
|
|
98
|
-
? seeded
|
|
99
|
-
: await prompter.prompt(seeded, codegenQuestions);
|
|
100
|
-
const options = buildGenerateOptions(answers, fileConfig);
|
|
101
|
-
const result = await generate(options);
|
|
102
|
-
printResult(result);
|
|
54
|
+
await runCodegenHandler(argv, prompter);
|
|
103
55
|
prompter.close();
|
|
104
56
|
return argv;
|
|
105
57
|
};
|
|
@@ -115,16 +67,15 @@ export const options = {
|
|
|
115
67
|
a: 'authorization',
|
|
116
68
|
v: 'verbose',
|
|
117
69
|
},
|
|
70
|
+
boolean: ['schema-only'],
|
|
118
71
|
string: [
|
|
119
72
|
'config',
|
|
120
73
|
'endpoint',
|
|
121
74
|
'schema-file',
|
|
75
|
+
'schema-dir',
|
|
122
76
|
'output',
|
|
123
77
|
'target',
|
|
124
78
|
'authorization',
|
|
125
|
-
'pgpm-module-path',
|
|
126
|
-
'pgpm-workspace-path',
|
|
127
|
-
'pgpm-module-name',
|
|
128
79
|
'schemas',
|
|
129
80
|
'api-names',
|
|
130
81
|
],
|
package/esm/cli/shared.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Question } from 'inquirerer';
|
|
2
2
|
import type { GenerateResult } from '../core/generate';
|
|
3
|
-
import type
|
|
3
|
+
import { type GraphQLSDKConfigTarget } from '../types/config';
|
|
4
4
|
export declare const splitCommas: (input: string | undefined) => string[] | undefined;
|
|
5
5
|
export interface CodegenAnswers {
|
|
6
6
|
endpoint?: string;
|
package/esm/cli/shared.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { camelize } from 'inflekt';
|
|
8
8
|
import { inflektTree } from 'inflekt/transform-keys';
|
|
9
|
+
import { mergeConfig } from '../types/config';
|
|
9
10
|
export const splitCommas = (input) => {
|
|
10
11
|
if (!input)
|
|
11
12
|
return undefined;
|
|
@@ -191,9 +192,5 @@ export function buildGenerateOptions(answers, fileConfig = {}) {
|
|
|
191
192
|
const camelized = camelizeArgv(answers);
|
|
192
193
|
const normalized = normalizeCodegenListOptions(camelized);
|
|
193
194
|
const withDb = buildDbConfig(normalized);
|
|
194
|
-
|
|
195
|
-
if (fileConfig.db && merged.db) {
|
|
196
|
-
merged.db = { ...fileConfig.db, ...merged.db };
|
|
197
|
-
}
|
|
198
|
-
return merged;
|
|
195
|
+
return mergeConfig(fileConfig, withDb);
|
|
199
196
|
}
|
|
@@ -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;
|