@pgsql/cli 1.30.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/CHANGELOG.md +64 -0
- package/LICENSE +21 -0
- package/README.md +268 -0
- package/dist/LICENSE +21 -0
- package/dist/README.md +268 -0
- package/dist/commands/deparse.d.ts +1 -0
- package/dist/commands/deparse.js +66 -0
- package/dist/commands/parse.d.ts +1 -0
- package/dist/commands/parse.js +60 -0
- package/dist/commands/proto-fetch/cli.d.ts +14 -0
- package/dist/commands/proto-fetch/cli.js +41 -0
- package/dist/commands/proto-fetch/helpers.d.ts +3 -0
- package/dist/commands/proto-fetch/helpers.js +73 -0
- package/dist/commands/proto-fetch.d.ts +1 -0
- package/dist/commands/proto-fetch.js +42 -0
- package/dist/commands/proto-gen.d.ts +1 -0
- package/dist/commands/proto-gen.js +59 -0
- package/dist/commands/runtime-schema.d.ts +1 -0
- package/dist/commands/runtime-schema.js +70 -0
- package/dist/esm/commands/deparse.js +60 -0
- package/dist/esm/commands/parse.js +54 -0
- package/dist/esm/commands/proto-fetch/cli.js +37 -0
- package/dist/esm/commands/proto-fetch/helpers.js +64 -0
- package/dist/esm/commands/proto-fetch.js +36 -0
- package/dist/esm/commands/proto-gen.js +53 -0
- package/dist/esm/commands/runtime-schema.js +64 -0
- package/dist/esm/index.js +56 -0
- package/dist/esm/package.js +26 -0
- package/dist/esm/utils/help.js +190 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +61 -0
- package/dist/package.d.ts +1 -0
- package/dist/package.js +29 -0
- package/dist/package.json +60 -0
- package/dist/utils/help.d.ts +2 -0
- package/dist/utils/help.js +197 -0
- package/jest.config.js +18 -0
- package/package.json +60 -0
- package/src/commands/deparse.ts +60 -0
- package/src/commands/parse.ts +60 -0
- package/src/commands/proto-fetch/cli.ts +52 -0
- package/src/commands/proto-fetch/helpers.ts +72 -0
- package/src/commands/proto-fetch.ts +42 -0
- package/src/commands/proto-gen.ts +49 -0
- package/src/commands/runtime-schema.ts +74 -0
- package/src/index.ts +69 -0
- package/src/package.ts +33 -0
- package/src/utils/help.ts +198 -0
- package/tsconfig.esm.json +9 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.showVersion = showVersion;
|
|
7
|
+
exports.showHelp = showHelp;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
function showVersion() {
|
|
12
|
+
// Try to find package.json in various locations
|
|
13
|
+
const possiblePaths = [
|
|
14
|
+
(0, path_1.join)(process.cwd(), 'package.json'),
|
|
15
|
+
(0, path_1.join)(process.argv[1], '../../package.json'),
|
|
16
|
+
(0, path_1.join)(process.argv[1], '../../../package.json')
|
|
17
|
+
];
|
|
18
|
+
for (const path of possiblePaths) {
|
|
19
|
+
try {
|
|
20
|
+
const packageJson = JSON.parse((0, fs_1.readFileSync)(path, 'utf-8'));
|
|
21
|
+
if (packageJson.name === '@pgsql/cli') {
|
|
22
|
+
console.log(packageJson.version);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Continue to next path
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Fallback: just show the version we know
|
|
31
|
+
console.log('1.29.2');
|
|
32
|
+
}
|
|
33
|
+
function showHelp(command) {
|
|
34
|
+
if (command) {
|
|
35
|
+
switch (command) {
|
|
36
|
+
case 'parse':
|
|
37
|
+
showParseHelp();
|
|
38
|
+
break;
|
|
39
|
+
case 'deparse':
|
|
40
|
+
showDeparseHelp();
|
|
41
|
+
break;
|
|
42
|
+
case 'proto-gen':
|
|
43
|
+
showProtoGenHelp();
|
|
44
|
+
break;
|
|
45
|
+
case 'proto-fetch':
|
|
46
|
+
showProtoFetchHelp();
|
|
47
|
+
break;
|
|
48
|
+
case 'runtime-schema':
|
|
49
|
+
showRuntimeSchemaHelp();
|
|
50
|
+
break;
|
|
51
|
+
default:
|
|
52
|
+
showGeneralHelp();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
showGeneralHelp();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function showGeneralHelp() {
|
|
60
|
+
console.log(`
|
|
61
|
+
${chalk_1.default.bold('pgsql')} - Unified CLI for PostgreSQL AST operations
|
|
62
|
+
|
|
63
|
+
${chalk_1.default.yellow('Usage:')}
|
|
64
|
+
pgsql <command> [options]
|
|
65
|
+
|
|
66
|
+
${chalk_1.default.yellow('Commands:')}
|
|
67
|
+
${chalk_1.default.green('parse')} Parse SQL to AST
|
|
68
|
+
${chalk_1.default.green('deparse')} Convert AST to SQL
|
|
69
|
+
${chalk_1.default.green('proto-gen')} Generate TypeScript from protobuf
|
|
70
|
+
${chalk_1.default.green('proto-fetch')} Download and process proto files
|
|
71
|
+
${chalk_1.default.green('runtime-schema')} Generate runtime schema for AST nodes
|
|
72
|
+
|
|
73
|
+
${chalk_1.default.yellow('Global Options:')}
|
|
74
|
+
-h, --help Show help
|
|
75
|
+
-v, --version Show version
|
|
76
|
+
|
|
77
|
+
${chalk_1.default.yellow('Examples:')}
|
|
78
|
+
pgsql parse query.sql
|
|
79
|
+
pgsql deparse ast.json
|
|
80
|
+
pgsql proto-gen --inFile pg_query.proto --outDir out
|
|
81
|
+
pgsql proto-fetch --url https://example.com/pg_query.proto --inFile pg_query.proto
|
|
82
|
+
|
|
83
|
+
Run 'pgsql <command> --help' for detailed help on a specific command.
|
|
84
|
+
`);
|
|
85
|
+
}
|
|
86
|
+
function showParseHelp() {
|
|
87
|
+
console.log(`
|
|
88
|
+
${chalk_1.default.bold('pgsql parse')} - Parse SQL to AST
|
|
89
|
+
|
|
90
|
+
${chalk_1.default.yellow('Usage:')}
|
|
91
|
+
pgsql parse <sqlfile> [options]
|
|
92
|
+
|
|
93
|
+
${chalk_1.default.yellow('Options:')}
|
|
94
|
+
-o, --output <file> Output to file instead of stdout
|
|
95
|
+
-f, --format <format> Output format: json, pretty (default: pretty)
|
|
96
|
+
--clean Clean the AST tree (remove location info)
|
|
97
|
+
-h, --help Show help
|
|
98
|
+
|
|
99
|
+
${chalk_1.default.yellow('Examples:')}
|
|
100
|
+
pgsql parse query.sql
|
|
101
|
+
pgsql parse query.sql -o ast.json
|
|
102
|
+
pgsql parse query.sql --format json --clean
|
|
103
|
+
`);
|
|
104
|
+
}
|
|
105
|
+
function showDeparseHelp() {
|
|
106
|
+
console.log(`
|
|
107
|
+
${chalk_1.default.bold('pgsql deparse')} - Convert AST to SQL
|
|
108
|
+
|
|
109
|
+
${chalk_1.default.yellow('Usage:')}
|
|
110
|
+
pgsql deparse [options]
|
|
111
|
+
|
|
112
|
+
${chalk_1.default.yellow('Options:')}
|
|
113
|
+
-i, --input <file> Input JSON file (or use stdin)
|
|
114
|
+
-o, --output <file> Output to file instead of stdout
|
|
115
|
+
-h, --help Show help
|
|
116
|
+
|
|
117
|
+
${chalk_1.default.yellow('Examples:')}
|
|
118
|
+
pgsql deparse -i ast.json
|
|
119
|
+
pgsql deparse -i ast.json -o query.sql
|
|
120
|
+
cat ast.json | pgsql deparse
|
|
121
|
+
pgsql parse query.sql | pgsql deparse
|
|
122
|
+
`);
|
|
123
|
+
}
|
|
124
|
+
function showProtoGenHelp() {
|
|
125
|
+
console.log(`
|
|
126
|
+
${chalk_1.default.bold('pgsql proto-gen')} - Generate TypeScript from protobuf
|
|
127
|
+
|
|
128
|
+
${chalk_1.default.yellow('Usage:')}
|
|
129
|
+
pgsql proto-gen --inFile <proto> --outDir <dir> [options]
|
|
130
|
+
|
|
131
|
+
${chalk_1.default.yellow('Required Options:')}
|
|
132
|
+
--inFile <file> Input .proto file
|
|
133
|
+
--outDir <dir> Output directory
|
|
134
|
+
|
|
135
|
+
${chalk_1.default.yellow('Optional Flags:')}
|
|
136
|
+
--enums Generate TypeScript enums
|
|
137
|
+
--enums-json Generate JSON enum mappings
|
|
138
|
+
--types Generate TypeScript interfaces
|
|
139
|
+
--utils Generate utility functions
|
|
140
|
+
--ast-helpers Generate AST helper methods
|
|
141
|
+
--wrapped-helpers Generate wrapped AST helpers
|
|
142
|
+
--optional Make all fields optional
|
|
143
|
+
--keep-case Keep original field casing
|
|
144
|
+
--remove-undefined Remove UNDEFINED enum at position 0
|
|
145
|
+
-h, --help Show help
|
|
146
|
+
|
|
147
|
+
${chalk_1.default.yellow('Examples:')}
|
|
148
|
+
pgsql proto-gen --inFile pg_query.proto --outDir out --types --enums
|
|
149
|
+
pgsql proto-gen --inFile pg_query.proto --outDir out --types --utils --ast-helpers
|
|
150
|
+
pgsql proto-gen --inFile pg_query.proto --outDir out --types --optional --keep-case
|
|
151
|
+
`);
|
|
152
|
+
}
|
|
153
|
+
function showProtoFetchHelp() {
|
|
154
|
+
console.log(`
|
|
155
|
+
${chalk_1.default.bold('pgsql proto-fetch')} - Download and process proto files
|
|
156
|
+
|
|
157
|
+
${chalk_1.default.yellow('Usage:')}
|
|
158
|
+
pgsql proto-fetch [options]
|
|
159
|
+
|
|
160
|
+
${chalk_1.default.yellow('Options:')}
|
|
161
|
+
--url <url> Proto file URL to download
|
|
162
|
+
--inFile <file> Where to save the proto file
|
|
163
|
+
--outFile <file> Generated JS output file
|
|
164
|
+
--replace-pkg <old> Original package name to replace
|
|
165
|
+
--with-pkg <new> New package name
|
|
166
|
+
-h, --help Show help
|
|
167
|
+
|
|
168
|
+
${chalk_1.default.yellow('Examples:')}
|
|
169
|
+
pgsql proto-fetch --url https://raw.githubusercontent.com/pganalyze/libpg_query/16-latest/protobuf/pg_query.proto \\
|
|
170
|
+
--inFile pg_query.proto \\
|
|
171
|
+
--outFile pg_query.js \\
|
|
172
|
+
--replace-pkg "protobufjs/minimal" \\
|
|
173
|
+
--with-pkg "@launchql/protobufjs/minimal"
|
|
174
|
+
`);
|
|
175
|
+
}
|
|
176
|
+
function showRuntimeSchemaHelp() {
|
|
177
|
+
console.log(`
|
|
178
|
+
${chalk_1.default.bold('pgsql runtime-schema')} - Generate runtime schema for AST nodes
|
|
179
|
+
|
|
180
|
+
${chalk_1.default.yellow('Usage:')}
|
|
181
|
+
pgsql runtime-schema --inFile <proto> --outDir <dir> [options]
|
|
182
|
+
|
|
183
|
+
${chalk_1.default.yellow('Required Options:')}
|
|
184
|
+
--inFile <file> Input .proto file
|
|
185
|
+
--outDir <dir> Output directory
|
|
186
|
+
|
|
187
|
+
${chalk_1.default.yellow('Optional Options:')}
|
|
188
|
+
--format <format> Output format: json, typescript (default: json)
|
|
189
|
+
--filename <name> Output filename (default: runtime-schema)
|
|
190
|
+
-h, --help Show help
|
|
191
|
+
|
|
192
|
+
${chalk_1.default.yellow('Examples:')}
|
|
193
|
+
pgsql runtime-schema --inFile pg_query.proto --outDir out
|
|
194
|
+
pgsql runtime-schema --inFile pg_query.proto --outDir out --format typescript
|
|
195
|
+
pgsql runtime-schema --inFile pg_query.proto --outDir out --filename ast-schema
|
|
196
|
+
`);
|
|
197
|
+
}
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
preset: "ts-jest",
|
|
4
|
+
testEnvironment: "node",
|
|
5
|
+
transform: {
|
|
6
|
+
"^.+\\.tsx?$": [
|
|
7
|
+
"ts-jest",
|
|
8
|
+
{
|
|
9
|
+
babelConfig: false,
|
|
10
|
+
tsconfig: "tsconfig.json",
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
},
|
|
14
|
+
transformIgnorePatterns: [`/node_modules/*`],
|
|
15
|
+
testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
|
16
|
+
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
|
|
17
|
+
modulePathIgnorePatterns: ["dist/*"]
|
|
18
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pgsql/cli",
|
|
3
|
+
"version": "1.30.0",
|
|
4
|
+
"description": "Unified CLI for PostgreSQL AST parsing, deparsing, and code generation",
|
|
5
|
+
"author": "Dan Lynch <pyramation@gmail.com>",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"module": "esm/index.js",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"homepage": "https://github.com/launchql/pgsql-parser/tree/master/packages/pgsql-cli#readme",
|
|
10
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/launchql/pgsql-parser"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/launchql/pgsql-parser/issues"
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"pgsql": "index.js"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"copy": "copyfiles -f ../../LICENSE README.md package.json dist",
|
|
26
|
+
"clean": "rimraf dist",
|
|
27
|
+
"prepare": "npm run build",
|
|
28
|
+
"build": "npm run clean && tsc && tsc -p tsconfig.esm.json && npm run copy",
|
|
29
|
+
"build:dev": "npm run clean && tsc --declarationMap && tsc -p tsconfig.esm.json && npm run copy",
|
|
30
|
+
"dev": "ts-node src/index",
|
|
31
|
+
"lint": "eslint . --fix",
|
|
32
|
+
"test": "jest",
|
|
33
|
+
"test:watch": "jest --watch"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"sql",
|
|
37
|
+
"postgres",
|
|
38
|
+
"postgresql",
|
|
39
|
+
"pg",
|
|
40
|
+
"query",
|
|
41
|
+
"ast",
|
|
42
|
+
"proto",
|
|
43
|
+
"parser",
|
|
44
|
+
"deparser",
|
|
45
|
+
"database"
|
|
46
|
+
],
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@launchql/protobufjs": "7.2.6",
|
|
49
|
+
"@launchql/protobufjs-cli": "1.1.5",
|
|
50
|
+
"chalk": "^4.1.0",
|
|
51
|
+
"glob": "8.0.3",
|
|
52
|
+
"minimist": "1.2.8",
|
|
53
|
+
"mkdirp": "3.0.1",
|
|
54
|
+
"nested-obj": "^0.0.1",
|
|
55
|
+
"pg-proto-parser": "^1.29.0",
|
|
56
|
+
"pgsql-deparser": "^17.7.0",
|
|
57
|
+
"pgsql-parser": "^17.6.0"
|
|
58
|
+
},
|
|
59
|
+
"gitHead": "e0f9b0579328f4206c315d499e0e2045d3b8f845"
|
|
60
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { resolve, join } from 'path';
|
|
3
|
+
import { deparse } from 'pgsql-deparser';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { showHelp } from '../utils/help';
|
|
6
|
+
|
|
7
|
+
export async function deparseCommand(argv: any) {
|
|
8
|
+
if (argv.help) {
|
|
9
|
+
showHelp('deparse');
|
|
10
|
+
process.exit(0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
let ast;
|
|
15
|
+
|
|
16
|
+
// Read AST from file or stdin
|
|
17
|
+
if (argv.input || argv.i) {
|
|
18
|
+
const inputFile = argv.input || argv.i;
|
|
19
|
+
const filePath = inputFile.startsWith('/')
|
|
20
|
+
? inputFile
|
|
21
|
+
: resolve(join(process.cwd(), inputFile));
|
|
22
|
+
|
|
23
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
24
|
+
ast = JSON.parse(content);
|
|
25
|
+
} else if (!process.stdin.isTTY) {
|
|
26
|
+
// Read from stdin
|
|
27
|
+
const chunks = [];
|
|
28
|
+
for await (const chunk of process.stdin) {
|
|
29
|
+
chunks.push(chunk);
|
|
30
|
+
}
|
|
31
|
+
const content = Buffer.concat(chunks).toString();
|
|
32
|
+
ast = JSON.parse(content);
|
|
33
|
+
} else {
|
|
34
|
+
console.error(chalk.red('Error: No input provided. Use -i <file> or pipe input via stdin'));
|
|
35
|
+
showHelp('deparse');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Deparse AST to SQL
|
|
40
|
+
const sql = await deparse(ast);
|
|
41
|
+
|
|
42
|
+
// Write output
|
|
43
|
+
if (argv.output || argv.o) {
|
|
44
|
+
const outputFile = argv.output || argv.o;
|
|
45
|
+
writeFileSync(outputFile, sql);
|
|
46
|
+
console.log(chalk.green(`✓ SQL written to ${outputFile}`));
|
|
47
|
+
} else {
|
|
48
|
+
process.stdout.write(sql);
|
|
49
|
+
}
|
|
50
|
+
} catch (error: any) {
|
|
51
|
+
if (error.code === 'ENOENT') {
|
|
52
|
+
console.error(chalk.red(`Error: File not found: ${argv.input || argv.i}`));
|
|
53
|
+
} else if (error instanceof SyntaxError) {
|
|
54
|
+
console.error(chalk.red('Error: Invalid JSON input'));
|
|
55
|
+
} else {
|
|
56
|
+
console.error(chalk.red('Deparse error:'), error.message || error);
|
|
57
|
+
}
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { resolve, join } from 'path';
|
|
3
|
+
import { parse } from 'pgsql-parser';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { showHelp } from '../utils/help';
|
|
6
|
+
|
|
7
|
+
export async function parseCommand(argv: any) {
|
|
8
|
+
if (argv.help) {
|
|
9
|
+
showHelp('parse');
|
|
10
|
+
process.exit(0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const sqlFile = argv._[1];
|
|
14
|
+
|
|
15
|
+
if (!sqlFile) {
|
|
16
|
+
console.error(chalk.red('Error: SQL file required'));
|
|
17
|
+
showHelp('parse');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// Resolve file path
|
|
23
|
+
const filePath = sqlFile.startsWith('/')
|
|
24
|
+
? sqlFile
|
|
25
|
+
: resolve(join(process.cwd(), sqlFile));
|
|
26
|
+
|
|
27
|
+
// Read SQL content
|
|
28
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
29
|
+
|
|
30
|
+
// Parse SQL
|
|
31
|
+
const ast = await parse(content);
|
|
32
|
+
|
|
33
|
+
// Clean AST if requested
|
|
34
|
+
if (argv.clean) {
|
|
35
|
+
// For now, we'll skip the clean functionality until we can import it properly
|
|
36
|
+
console.warn(chalk.yellow('Warning: --clean flag is not yet implemented'));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Format output
|
|
40
|
+
const format = argv.format || 'pretty';
|
|
41
|
+
const output = format === 'json'
|
|
42
|
+
? JSON.stringify(ast)
|
|
43
|
+
: JSON.stringify(ast, null, 2);
|
|
44
|
+
|
|
45
|
+
// Write output
|
|
46
|
+
if (argv.output) {
|
|
47
|
+
writeFileSync(argv.output, output);
|
|
48
|
+
console.log(chalk.green(`✓ AST written to ${argv.output}`));
|
|
49
|
+
} else {
|
|
50
|
+
process.stdout.write(output);
|
|
51
|
+
}
|
|
52
|
+
} catch (error: any) {
|
|
53
|
+
if (error.code === 'ENOENT') {
|
|
54
|
+
console.error(chalk.red(`Error: File not found: ${sqlFile}`));
|
|
55
|
+
} else {
|
|
56
|
+
console.error(chalk.red('Parse error:'), error.message || error);
|
|
57
|
+
}
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { downloadProtoFile, generateProtoJS, replaceTextInProtoJS } from './helpers';
|
|
2
|
+
|
|
3
|
+
export interface CommandOptions {
|
|
4
|
+
help?: boolean;
|
|
5
|
+
h?: boolean;
|
|
6
|
+
version?: boolean;
|
|
7
|
+
v?: boolean;
|
|
8
|
+
protoUrl?: string;
|
|
9
|
+
inFile: string;
|
|
10
|
+
outFile: string;
|
|
11
|
+
originalPackageName: string;
|
|
12
|
+
newPackageName: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const help = (): void => {
|
|
16
|
+
console.log(`
|
|
17
|
+
Usage:
|
|
18
|
+
|
|
19
|
+
pg-proto-parser protogen --protoUrl <URL to proto file>
|
|
20
|
+
--inFile <path to proto file>
|
|
21
|
+
--outFile <path to output JS file>
|
|
22
|
+
--originalPackageName <original package name>
|
|
23
|
+
--newPackageName <new package name>
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
|
|
27
|
+
--help, -h Show this help message.
|
|
28
|
+
--version, -v Show the version number.
|
|
29
|
+
--protoUrl Full URL to download the proto file (optional).
|
|
30
|
+
--inFile Path where the proto file will be saved or path to an existing proto file.
|
|
31
|
+
--outFile Path where the generated JavaScript file will be saved.
|
|
32
|
+
--originalPackageName Original package name to be replaced in the JS file.
|
|
33
|
+
--newPackageName New package name to replace in the JS file.
|
|
34
|
+
`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default async (argv: CommandOptions): Promise<CommandOptions> => {
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
if (argv.protoUrl) {
|
|
41
|
+
await downloadProtoFile(argv.protoUrl, argv.inFile);
|
|
42
|
+
}
|
|
43
|
+
await generateProtoJS(argv.inFile, argv.outFile);
|
|
44
|
+
await replaceTextInProtoJS(argv.outFile, argv.originalPackageName, argv.newPackageName);
|
|
45
|
+
console.log('All operations completed successfully.');
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// @ts-ignore
|
|
48
|
+
console.error('An error occurred:', error.message);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return argv;
|
|
52
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import https from 'https';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { exec } from 'child_process';
|
|
4
|
+
import { sync as mkdirp } from 'mkdirp';
|
|
5
|
+
import { dirname } from 'path';
|
|
6
|
+
|
|
7
|
+
// Download .proto file if URL
|
|
8
|
+
export const downloadProtoFile = (protoUrl: string, filePath: string): Promise<void> => {
|
|
9
|
+
mkdirp(dirname(filePath));
|
|
10
|
+
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
https.get(protoUrl, (response) => {
|
|
13
|
+
if (response.statusCode !== 200) {
|
|
14
|
+
console.error(`Failed to download file: Status Code: ${response.statusCode}`);
|
|
15
|
+
response.resume(); // consume response data to free up memory
|
|
16
|
+
reject(new Error('Failed to download file'));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const fileStream = fs.createWriteStream(filePath);
|
|
21
|
+
response.pipe(fileStream);
|
|
22
|
+
fileStream.on('finish', () => {
|
|
23
|
+
fileStream.close();
|
|
24
|
+
console.log('Downloaded proto file.');
|
|
25
|
+
resolve();
|
|
26
|
+
});
|
|
27
|
+
}).on('error', (err) => {
|
|
28
|
+
console.error(`Error downloading the file: ${err.message}`);
|
|
29
|
+
fs.unlink(filePath, () => {}); // Delete the file async. (No need to check error here)
|
|
30
|
+
reject(err);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Generate JavaScript from proto file using pbjs
|
|
36
|
+
export const generateProtoJS = (inFile: string, outFile: string): Promise<void> => new Promise((resolve, reject) => {
|
|
37
|
+
const command = `pbjs --keep-case -t static-module -o ${outFile} ${inFile}`;
|
|
38
|
+
mkdirp(dirname(outFile));
|
|
39
|
+
|
|
40
|
+
exec(command, (error, _stdout, _stderr) => {
|
|
41
|
+
if (error) {
|
|
42
|
+
console.error(`Error during code generation: ${error.message}`);
|
|
43
|
+
reject(error);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
console.log('Generated proto.js from proto file.');
|
|
47
|
+
resolve();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Replace text in generated JS file
|
|
52
|
+
export const replaceTextInProtoJS = (filePath: string, originalPackage: string, newPackage: string): Promise<void> => new Promise((resolve, reject) => {
|
|
53
|
+
fs.readFile(filePath, 'utf8', (err, data) => {
|
|
54
|
+
if (err) {
|
|
55
|
+
console.error(`Error reading proto.js: ${err.message}`);
|
|
56
|
+
reject(err);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = data.replace(new RegExp(originalPackage, 'g'), newPackage);
|
|
61
|
+
|
|
62
|
+
fs.writeFile(filePath, result, 'utf8', (err) => {
|
|
63
|
+
if (err) {
|
|
64
|
+
console.error(`Error writing back to proto.js: ${err.message}`);
|
|
65
|
+
reject(err);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
console.log('Replaced text in proto.js successfully.');
|
|
69
|
+
resolve();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { showHelp } from '../utils/help';
|
|
3
|
+
import { downloadProtoFile, generateProtoJS, replaceTextInProtoJS } from './proto-fetch/helpers';
|
|
4
|
+
|
|
5
|
+
export async function protoFetchCommand(argv: any) {
|
|
6
|
+
if (argv.help) {
|
|
7
|
+
showHelp('proto-fetch');
|
|
8
|
+
process.exit(0);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const url = argv.url;
|
|
12
|
+
const inFile = argv.inFile;
|
|
13
|
+
const outFile = argv.outFile;
|
|
14
|
+
const replacePkg = argv['replace-pkg'] || 'protobufjs/minimal';
|
|
15
|
+
const withPkg = argv['with-pkg'] || '@launchql/protobufjs/minimal';
|
|
16
|
+
|
|
17
|
+
if (!inFile || !outFile) {
|
|
18
|
+
console.error(chalk.red('Error: --inFile and --outFile are required'));
|
|
19
|
+
showHelp('proto-fetch');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
if (url) {
|
|
25
|
+
console.log(chalk.blue(`Downloading proto file from ${url}...`));
|
|
26
|
+
await downloadProtoFile(url, inFile);
|
|
27
|
+
console.log(chalk.green(`✓ Proto file saved to ${inFile}`));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log(chalk.blue('Generating JavaScript from proto file...'));
|
|
31
|
+
await generateProtoJS(inFile, outFile);
|
|
32
|
+
|
|
33
|
+
console.log(chalk.blue(`Replacing package references...`));
|
|
34
|
+
await replaceTextInProtoJS(outFile, replacePkg, withPkg);
|
|
35
|
+
|
|
36
|
+
console.log(chalk.green(`✓ All operations completed successfully`));
|
|
37
|
+
console.log(chalk.green(`✓ Generated file: ${outFile}`));
|
|
38
|
+
} catch (error: any) {
|
|
39
|
+
console.error(chalk.red('Proto fetch error:'), error.message || error);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { PgProtoParser, PgProtoParserOptions, getOptionsWithDefaults } from 'pg-proto-parser';
|
|
2
|
+
import o from 'nested-obj';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { showHelp } from '../utils/help';
|
|
5
|
+
|
|
6
|
+
export async function protoGenCommand(argv: any) {
|
|
7
|
+
if (argv.help) {
|
|
8
|
+
showHelp('proto-gen');
|
|
9
|
+
process.exit(0);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (!argv.inFile || !argv.outDir) {
|
|
13
|
+
console.error(chalk.red('Error: --inFile and --outDir are required'));
|
|
14
|
+
showHelp('proto-gen');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const options: PgProtoParserOptions = getOptionsWithDefaults({
|
|
20
|
+
outDir: argv.outDir
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Set options based on flags
|
|
24
|
+
if (argv.enums) o.set(options, 'enums.enabled', true);
|
|
25
|
+
if (argv['enums-json']) o.set(options, 'enums.json.enabled', true);
|
|
26
|
+
if (argv.types) o.set(options, 'types.enabled', true);
|
|
27
|
+
if (argv.utils) o.set(options, 'utils.enums.enabled', true);
|
|
28
|
+
if (argv['ast-helpers']) o.set(options, 'utils.astHelpers.enabled', true);
|
|
29
|
+
if (argv['wrapped-helpers']) o.set(options, 'utils.wrappedAstHelpers.enabled', true);
|
|
30
|
+
if (argv.optional) o.set(options, 'types.optionalFields', true);
|
|
31
|
+
if (argv['keep-case']) o.set(options, 'parser.keepCase', true);
|
|
32
|
+
if (argv['remove-undefined']) o.set(options, 'enums.removeUndefinedAt0', true);
|
|
33
|
+
|
|
34
|
+
// Additional options that might be useful
|
|
35
|
+
if (argv['type-union']) o.set(options, 'enums.enumsAsTypeUnion', true);
|
|
36
|
+
if (argv.header) o.set(options, 'includeHeader', true);
|
|
37
|
+
|
|
38
|
+
console.log(chalk.blue('Parsing protobuf file...'));
|
|
39
|
+
const parser = new PgProtoParser(argv.inFile, options);
|
|
40
|
+
|
|
41
|
+
console.log(chalk.blue('Generating TypeScript files...'));
|
|
42
|
+
await parser.write();
|
|
43
|
+
|
|
44
|
+
console.log(chalk.green(`✓ Files generated in ${argv.outDir}`));
|
|
45
|
+
} catch (error: any) {
|
|
46
|
+
console.error(chalk.red('Proto generation error:'), error.message || error);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { PgProtoParser, PgProtoParserOptions, getOptionsWithDefaults } from 'pg-proto-parser';
|
|
2
|
+
import { writeFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { showHelp } from '../utils/help';
|
|
6
|
+
|
|
7
|
+
export async function runtimeSchemaCommand(argv: any) {
|
|
8
|
+
if (argv.help) {
|
|
9
|
+
showHelp('runtime-schema');
|
|
10
|
+
process.exit(0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!argv.inFile || !argv.outDir) {
|
|
14
|
+
console.error(chalk.red('Error: --inFile and --outDir are required'));
|
|
15
|
+
showHelp('runtime-schema');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const format = argv.format || 'json';
|
|
20
|
+
const filename = argv.filename || 'runtime-schema';
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
console.log(chalk.blue('Parsing protobuf file...'));
|
|
24
|
+
|
|
25
|
+
const options: PgProtoParserOptions = getOptionsWithDefaults({
|
|
26
|
+
outDir: argv.outDir
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const parser = new PgProtoParser(argv.inFile, options);
|
|
30
|
+
|
|
31
|
+
// Generate runtime schema
|
|
32
|
+
console.log(chalk.blue(`Generating runtime schema in ${format} format...`));
|
|
33
|
+
console.log(chalk.yellow('Warning: Runtime schema generation is not yet fully implemented'));
|
|
34
|
+
|
|
35
|
+
// Generate placeholder schema based on format
|
|
36
|
+
let content: string;
|
|
37
|
+
let fileExt: string;
|
|
38
|
+
|
|
39
|
+
if (format === 'typescript') {
|
|
40
|
+
// Generate TypeScript runtime schema placeholder
|
|
41
|
+
content = `// Runtime schema for PostgreSQL AST nodes
|
|
42
|
+
// Generated from: ${argv.inFile}
|
|
43
|
+
|
|
44
|
+
export interface RuntimeSchema {
|
|
45
|
+
// TODO: Implement runtime schema generation
|
|
46
|
+
nodes: Record<string, any>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const runtimeSchema: RuntimeSchema = {
|
|
50
|
+
nodes: {}
|
|
51
|
+
};
|
|
52
|
+
`;
|
|
53
|
+
fileExt = '.ts';
|
|
54
|
+
} else {
|
|
55
|
+
// Generate JSON runtime schema placeholder
|
|
56
|
+
content = JSON.stringify({
|
|
57
|
+
generated: new Date().toISOString(),
|
|
58
|
+
source: argv.inFile,
|
|
59
|
+
nodes: {}
|
|
60
|
+
}, null, 2);
|
|
61
|
+
fileExt = '.json';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Write file
|
|
65
|
+
const outputPath = join(argv.outDir, `${filename}${fileExt}`);
|
|
66
|
+
writeFileSync(outputPath, content);
|
|
67
|
+
|
|
68
|
+
console.log(chalk.green(`✓ Runtime schema generated: ${outputPath}`));
|
|
69
|
+
} catch (error: any) {
|
|
70
|
+
console.error(chalk.red('Runtime schema error:'), error.message || error);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|