@palantir/pack.sdkgen 0.0.1-beta.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/LICENSE.txt +13 -0
- package/README.md +589 -0
- package/bin/cli.ts +7 -0
- package/build/esm/__tests__/createCommand.test.js +157 -0
- package/build/esm/__tests__/createCommand.test.js.map +1 -0
- package/build/esm/__tests__/generator.test.js +190 -0
- package/build/esm/__tests__/generator.test.js.map +1 -0
- package/build/esm/__tests__/schemaParser.test.js +67 -0
- package/build/esm/__tests__/schemaParser.test.js.map +1 -0
- package/build/esm/__tests__/templateLoader.test.js +67 -0
- package/build/esm/__tests__/templateLoader.test.js.map +1 -0
- package/build/esm/__tests__/templateUtils.test.js +94 -0
- package/build/esm/__tests__/templateUtils.test.js.map +1 -0
- package/build/esm/__tests__/types/testTypes.js +2 -0
- package/build/esm/__tests__/types/testTypes.js.map +1 -0
- package/build/esm/cli.js +25 -0
- package/build/esm/cli.js.map +1 -0
- package/build/esm/commands/create.js +173 -0
- package/build/esm/commands/create.js.map +1 -0
- package/build/esm/core/contextBuilder.js +33 -0
- package/build/esm/core/contextBuilder.js.map +1 -0
- package/build/esm/core/generator.js +248 -0
- package/build/esm/core/generator.js.map +1 -0
- package/build/esm/core/hookRunner.js +68 -0
- package/build/esm/core/hookRunner.js.map +1 -0
- package/build/esm/core/schemaParser.js +50 -0
- package/build/esm/core/schemaParser.js.map +1 -0
- package/build/esm/core/templateLoader.js +99 -0
- package/build/esm/core/templateLoader.js.map +1 -0
- package/build/esm/index.js +26 -0
- package/build/esm/index.js.map +1 -0
- package/build/esm/types/index.js +2 -0
- package/build/esm/types/index.js.map +1 -0
- package/build/esm/utils/fileSystem.js +72 -0
- package/build/esm/utils/fileSystem.js.map +1 -0
- package/build/esm/utils/logger.js +44 -0
- package/build/esm/utils/logger.js.map +1 -0
- package/build/esm/utils/prompts.js +25 -0
- package/build/esm/utils/prompts.js.map +1 -0
- package/build/esm/utils/templateUtils.js +61 -0
- package/build/esm/utils/templateUtils.js.map +1 -0
- package/build/types/__tests__/createCommand.test.d.ts +2 -0
- package/build/types/__tests__/createCommand.test.d.ts.map +1 -0
- package/build/types/__tests__/generator.test.d.ts +2 -0
- package/build/types/__tests__/generator.test.d.ts.map +1 -0
- package/build/types/__tests__/schemaParser.test.d.ts +2 -0
- package/build/types/__tests__/schemaParser.test.d.ts.map +1 -0
- package/build/types/__tests__/templateLoader.test.d.ts +2 -0
- package/build/types/__tests__/templateLoader.test.d.ts.map +1 -0
- package/build/types/__tests__/templateUtils.test.d.ts +2 -0
- package/build/types/__tests__/templateUtils.test.d.ts.map +1 -0
- package/build/types/__tests__/types/testTypes.d.ts +38 -0
- package/build/types/__tests__/types/testTypes.d.ts.map +1 -0
- package/build/types/cli.d.ts +2 -0
- package/build/types/cli.d.ts.map +1 -0
- package/build/types/commands/create.d.ts +3 -0
- package/build/types/commands/create.d.ts.map +1 -0
- package/build/types/core/contextBuilder.d.ts +5 -0
- package/build/types/core/contextBuilder.d.ts.map +1 -0
- package/build/types/core/generator.d.ts +15 -0
- package/build/types/core/generator.d.ts.map +1 -0
- package/build/types/core/hookRunner.d.ts +3 -0
- package/build/types/core/hookRunner.d.ts.map +1 -0
- package/build/types/core/schemaParser.d.ts +7 -0
- package/build/types/core/schemaParser.d.ts.map +1 -0
- package/build/types/core/templateLoader.d.ts +12 -0
- package/build/types/core/templateLoader.d.ts.map +1 -0
- package/build/types/index.d.ts +11 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/types/index.d.ts +68 -0
- package/build/types/types/index.d.ts.map +1 -0
- package/build/types/utils/fileSystem.d.ts +8 -0
- package/build/types/utils/fileSystem.d.ts.map +1 -0
- package/build/types/utils/logger.d.ts +11 -0
- package/build/types/utils/logger.d.ts.map +1 -0
- package/build/types/utils/prompts.d.ts +3 -0
- package/build/types/utils/prompts.d.ts.map +1 -0
- package/build/types/utils/templateUtils.d.ts +3 -0
- package/build/types/utils/templateUtils.d.ts.map +1 -0
- package/package.json +61 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2025 Palantir Technologies, Inc.
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
# @palantir/pack.sdkgen
|
|
2
|
+
|
|
3
|
+
A flexible CLI tool for generating TypeScript/JavaScript SDKs from schema definitions using customizable templates. The intention is for this to remain generic and not pack-specific,
|
|
4
|
+
the pack-specific parts are up to the template scripts that are used!
|
|
5
|
+
|
|
6
|
+
## Table of Contents
|
|
7
|
+
|
|
8
|
+
- [Overview](#overview)
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Usage](#usage)
|
|
11
|
+
- [Creating Templates](#creating-templates)
|
|
12
|
+
- [CLI Development Guide](#cli-development-guide)
|
|
13
|
+
- [API Reference](#api-reference)
|
|
14
|
+
- [Examples](#examples)
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
`@palantir/pack.sdkgen` is a scaffolding tool that generates SDK packages from schema files using pluggable templates. Similar to `create-react-app`, it allows you to:
|
|
19
|
+
|
|
20
|
+
- Transform schema definitions into fully-configured SDK projects
|
|
21
|
+
- Customize generation through configuration and hooks
|
|
22
|
+
- Apply template substitutions and run initialization scripts
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Basic Usage
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Create a new SDK using the default template
|
|
30
|
+
npx @palantir/pack.sdkgen create my-sdk --schema ./my-schema.json
|
|
31
|
+
|
|
32
|
+
# Use a specific template
|
|
33
|
+
npx @palantir/pack.sdkgen create my-sdk \
|
|
34
|
+
--template @myorg/custom-template \
|
|
35
|
+
--schema ./my-schema.json
|
|
36
|
+
|
|
37
|
+
# Use a local template
|
|
38
|
+
npx @palantir/pack.sdkgen create my-sdk \
|
|
39
|
+
--template ./path/to/template \
|
|
40
|
+
--schema ./my-schema.yaml
|
|
41
|
+
|
|
42
|
+
# Skip dependency installation
|
|
43
|
+
npx @palantir/pack.sdkgen create my-sdk \
|
|
44
|
+
--schema ./my-schema.json \
|
|
45
|
+
--skip-install
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Available Commands
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Usage: sdkgen [options] [command]
|
|
52
|
+
|
|
53
|
+
Generate TypeScript/JavaScript SDKs from schema definitions
|
|
54
|
+
|
|
55
|
+
Options:
|
|
56
|
+
-V, --version output the version number
|
|
57
|
+
-h, --help display help for command
|
|
58
|
+
|
|
59
|
+
Commands:
|
|
60
|
+
create [options] <project-name> Create a new SDK project
|
|
61
|
+
help [command] display help for command
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Create Command Options
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
Usage: sdkgen create [options] <project-name>
|
|
68
|
+
|
|
69
|
+
Create a new SDK project
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
-t, --template <template> template to use (default: "default")
|
|
73
|
+
-s, --schema <path> path to schema file
|
|
74
|
+
--skip-install skip dependency installation
|
|
75
|
+
--verbose enable verbose logging
|
|
76
|
+
--dry-run preview without writing files
|
|
77
|
+
--non-interactive run in non-interactive mode using defaults
|
|
78
|
+
--config <path> path to JSON config file with template configuration
|
|
79
|
+
--overwrite overwrite existing directory
|
|
80
|
+
-h, --help display help for command
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Creating Templates
|
|
84
|
+
|
|
85
|
+
Templates are npm packages or local directories with a specific structure that defines how to generate SDK code.
|
|
86
|
+
|
|
87
|
+
### Template Structure
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
my-sdk-template/
|
|
91
|
+
├── package.json # Template package metadata
|
|
92
|
+
├── template.config.js # Template configuration
|
|
93
|
+
├── template/ # File templates
|
|
94
|
+
│ ├── package.json.ejs
|
|
95
|
+
│ ├── src/
|
|
96
|
+
│ │ ├── index.ts.ejs
|
|
97
|
+
│ │ └── types.ts.ejs
|
|
98
|
+
│ ├── README.md.ejs
|
|
99
|
+
│ └── .gitignore
|
|
100
|
+
├── scripts/ # Hook scripts
|
|
101
|
+
│ ├── pre-generate.js
|
|
102
|
+
│ └── post-generate.js
|
|
103
|
+
└── transformers/ # Schema transformers (optional)
|
|
104
|
+
└── default.js
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Minimal Template Example
|
|
108
|
+
|
|
109
|
+
Here's a simple template that generates a "Hello World" SDK:
|
|
110
|
+
|
|
111
|
+
#### `template.config.js`
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
module.exports = {
|
|
115
|
+
name: "hello-world-template",
|
|
116
|
+
description: "A simple Hello World SDK template",
|
|
117
|
+
|
|
118
|
+
// Define prompts for user input
|
|
119
|
+
prompts: [
|
|
120
|
+
{
|
|
121
|
+
type: "input",
|
|
122
|
+
name: "greeting",
|
|
123
|
+
message: "What greeting would you like to use?",
|
|
124
|
+
default: "Hello, World!",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
type: "input",
|
|
128
|
+
name: "author",
|
|
129
|
+
message: "Author name?",
|
|
130
|
+
default: "Palantir SDK Generator",
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
|
|
134
|
+
// Files to process as EJS templates
|
|
135
|
+
templateFiles: ["**/*.ejs"],
|
|
136
|
+
|
|
137
|
+
// Files to copy without processing
|
|
138
|
+
staticFiles: [".gitignore", "**/*.md"],
|
|
139
|
+
|
|
140
|
+
// Hooks for custom logic
|
|
141
|
+
hooks: {
|
|
142
|
+
beforeGenerate: "./scripts/pre-generate.js",
|
|
143
|
+
afterGenerate: "./scripts/post-generate.js",
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### `template/package.json.ejs`
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"name": "<%= projectName %>",
|
|
153
|
+
"version": "1.0.0",
|
|
154
|
+
"description": "SDK generated from schema",
|
|
155
|
+
"main": "dist/index.js",
|
|
156
|
+
"types": "dist/index.d.ts",
|
|
157
|
+
"author": "<%= author %>",
|
|
158
|
+
"license": "<%= license %>",
|
|
159
|
+
"scripts": {
|
|
160
|
+
"build": "tsc",
|
|
161
|
+
"test": "jest"
|
|
162
|
+
},
|
|
163
|
+
"devDependencies": {
|
|
164
|
+
"typescript": "^5.0.0",
|
|
165
|
+
"@types/node": "^20.0.0"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### `template/src/helloWorld.ts.ejs`
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
/**
|
|
174
|
+
* Generated on <%= new Date().toISOString() %>
|
|
175
|
+
* Author: <%= author %>
|
|
176
|
+
*/
|
|
177
|
+
|
|
178
|
+
export class HelloWorld {
|
|
179
|
+
private greeting: string = '<%= greeting %>';
|
|
180
|
+
|
|
181
|
+
constructor(customGreeting?: string) {
|
|
182
|
+
if (customGreeting) {
|
|
183
|
+
this.greeting = customGreeting;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
greet(name: string = 'World'): string {
|
|
188
|
+
return `${this.greeting}, ${name}!`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Schema information
|
|
192
|
+
getSchemaInfo(): object {
|
|
193
|
+
return <%- JSON.stringify(schema, null, 2) %>;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export default HelloWorld;
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### `scripts/pre-generate.js`
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
module.exports = async function preGenerate(context) {
|
|
204
|
+
const { projectName, schema, answers, templateConfig } = context;
|
|
205
|
+
|
|
206
|
+
console.log(`🚀 Preparing to generate ${projectName}`);
|
|
207
|
+
console.log(`📋 Using template: ${templateConfig.name}`);
|
|
208
|
+
console.log(`👤 Author: ${answers.author}`);
|
|
209
|
+
|
|
210
|
+
// Validate schema
|
|
211
|
+
if (!schema || Object.keys(schema).length === 0) {
|
|
212
|
+
console.warn("⚠️ Warning: Schema is empty");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Modify context if needed
|
|
216
|
+
context.additionalData = {
|
|
217
|
+
generatedAt: new Date().toISOString(),
|
|
218
|
+
nodeVersion: process.version,
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
return context;
|
|
222
|
+
};
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
#### `scripts/post-generate.js`
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
const fs = require("fs-extra");
|
|
229
|
+
const path = require("path");
|
|
230
|
+
|
|
231
|
+
module.exports = async function postGenerate(context, outputPath) {
|
|
232
|
+
const { projectName, answers } = context;
|
|
233
|
+
|
|
234
|
+
console.log(`✨ Finalizing ${projectName}`);
|
|
235
|
+
|
|
236
|
+
// Create additional directories
|
|
237
|
+
await fs.ensureDir(path.join(outputPath, "dist"));
|
|
238
|
+
await fs.ensureDir(path.join(outputPath, "tests"));
|
|
239
|
+
|
|
240
|
+
// Create a simple test file
|
|
241
|
+
const testContent = `
|
|
242
|
+
import HelloWorld from '../src/helloWorld';
|
|
243
|
+
|
|
244
|
+
describe('HelloWorld', () => {
|
|
245
|
+
it('should greet correctly', () => {
|
|
246
|
+
const hw = new HelloWorld();
|
|
247
|
+
expect(hw.greet('SDK')).toBe('${answers.greeting}, SDK!');
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
`;
|
|
251
|
+
|
|
252
|
+
await fs.writeFile(
|
|
253
|
+
path.join(outputPath, "tests", "helloWorld.test.ts"),
|
|
254
|
+
testContent.trim(),
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
// Create TypeScript config
|
|
258
|
+
const tsConfig = {
|
|
259
|
+
compilerOptions: {
|
|
260
|
+
target: "ES2020",
|
|
261
|
+
module: "commonjs",
|
|
262
|
+
declaration: true,
|
|
263
|
+
outDir: "./dist",
|
|
264
|
+
rootDir: "./src",
|
|
265
|
+
strict: true,
|
|
266
|
+
esModuleInterop: true,
|
|
267
|
+
skipLibCheck: true,
|
|
268
|
+
forceConsistentCasingInFileNames: true,
|
|
269
|
+
},
|
|
270
|
+
include: ["src/**/*"],
|
|
271
|
+
exclude: ["node_modules", "dist", "tests"],
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
await fs.writeJSON(
|
|
275
|
+
path.join(outputPath, "tsconfig.json"),
|
|
276
|
+
tsConfig,
|
|
277
|
+
{ spaces: 2 },
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
console.log("📦 Package structure created successfully!");
|
|
281
|
+
};
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Hook Execution Model
|
|
285
|
+
|
|
286
|
+
**Important**: Template hooks run as separate Node.js processes in the template's directory context. This means:
|
|
287
|
+
|
|
288
|
+
1. **Dependency Isolation**: Hooks have access to the template package's `node_modules`, not the CLI's
|
|
289
|
+
2. **Version Independence**: The template can depend on a specific version of `@palantir/pack.sdkgen` and other tools
|
|
290
|
+
3. **Context Passing**: Hook context is serialized to JSON and passed via command-line arguments
|
|
291
|
+
4. **Return Values**: Hooks can return modified context (for `beforeGenerate`) via stdout
|
|
292
|
+
|
|
293
|
+
Example hook that uses template dependencies:
|
|
294
|
+
|
|
295
|
+
```javascript
|
|
296
|
+
// hooks/afterGenerate.js
|
|
297
|
+
import { execSync } from "child_process";
|
|
298
|
+
|
|
299
|
+
export default async function afterGenerate(context) {
|
|
300
|
+
const { outputPath, schemaPath } = context;
|
|
301
|
+
|
|
302
|
+
// This uses the template's own dependencies
|
|
303
|
+
const typeGenPath = require.resolve("@my-org/type-generator/bin/cli.js");
|
|
304
|
+
|
|
305
|
+
execSync(
|
|
306
|
+
`node "${typeGenPath}" -i "${schemaPath}" -o "${outputPath}/types"`,
|
|
307
|
+
{
|
|
308
|
+
stdio: "inherit",
|
|
309
|
+
cwd: outputPath,
|
|
310
|
+
},
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Advanced Template Features
|
|
316
|
+
|
|
317
|
+
#### Custom Schema Transformers
|
|
318
|
+
|
|
319
|
+
```javascript
|
|
320
|
+
// transformers/default.js
|
|
321
|
+
module.exports = {
|
|
322
|
+
async transform(schema, context) {
|
|
323
|
+
// Transform the raw schema into a format your templates can use
|
|
324
|
+
return {
|
|
325
|
+
version: schema.version || "1.0.0",
|
|
326
|
+
endpoints: extractEndpoints(schema),
|
|
327
|
+
types: extractTypes(schema),
|
|
328
|
+
metadata: {
|
|
329
|
+
generatedAt: new Date().toISOString(),
|
|
330
|
+
sourceFile: context.schemaPath,
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
function extractEndpoints(schema) {
|
|
337
|
+
// Your schema parsing logic
|
|
338
|
+
return [];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function extractTypes(schema) {
|
|
342
|
+
// Your type extraction logic
|
|
343
|
+
return [];
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
#### Template Variables
|
|
348
|
+
|
|
349
|
+
Templates have access to these variables:
|
|
350
|
+
|
|
351
|
+
- `projectName` - The name of the project being created
|
|
352
|
+
- `schema` - The transformed schema data
|
|
353
|
+
- `answers` - User responses to prompts
|
|
354
|
+
- `utils` - Helper functions (see below)
|
|
355
|
+
- Any additional data added by hooks
|
|
356
|
+
|
|
357
|
+
#### Utility Functions
|
|
358
|
+
|
|
359
|
+
```javascript
|
|
360
|
+
// Available in templates as `utils`
|
|
361
|
+
{
|
|
362
|
+
camelCase: (str) => string, // Convert to camelCase
|
|
363
|
+
pascalCase: (str) => string, // Convert to PascalCase
|
|
364
|
+
kebabCase: (str) => string, // Convert to kebab-case
|
|
365
|
+
snakeCase: (str) => string, // Convert to snake_case
|
|
366
|
+
pluralize: (str) => string, // Pluralize a word
|
|
367
|
+
singularize: (str) => string, // Singularize a word
|
|
368
|
+
capitalize: (str) => string, // Capitalize first letter
|
|
369
|
+
lower: (str) => string, // Convert to lowercase
|
|
370
|
+
upper: (str) => string // Convert to uppercase
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## CLI Development Guide
|
|
375
|
+
|
|
376
|
+
### Project Structure
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
sdkgen/
|
|
380
|
+
├── src/
|
|
381
|
+
│ ├── cli.ts # CLI entry point
|
|
382
|
+
│ ├── commands/
|
|
383
|
+
│ │ └── create.ts # Create command implementation
|
|
384
|
+
│ ├── core/
|
|
385
|
+
│ │ ├── generator.ts # Core generation engine
|
|
386
|
+
│ │ ├── template-loader.ts # Template loading logic
|
|
387
|
+
│ │ └── schema-parser.ts # Schema parsing utilities
|
|
388
|
+
│ ├── utils/
|
|
389
|
+
│ │ ├── prompts.ts # User prompt utilities
|
|
390
|
+
│ │ ├── logger.ts # Logging utilities
|
|
391
|
+
│ │ └── file-system.ts # File system helpers
|
|
392
|
+
│ └── types/
|
|
393
|
+
│ └── index.ts # TypeScript type definitions
|
|
394
|
+
├── templates/
|
|
395
|
+
│ └── default/ # Default template
|
|
396
|
+
├── bin/
|
|
397
|
+
│ └── cli.js # Executable entry point
|
|
398
|
+
├── package.json
|
|
399
|
+
├── tsconfig.json
|
|
400
|
+
└── README.md
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Implementation Steps
|
|
404
|
+
|
|
405
|
+
1. **Set up the project**
|
|
406
|
+
```bash
|
|
407
|
+
mkdir sdkgen
|
|
408
|
+
cd sdkgen
|
|
409
|
+
npm init -y
|
|
410
|
+
npm install commander inquirer ejs fs-extra glob chalk
|
|
411
|
+
npm install -D typescript @types/node @types/inquirer @types/ejs
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
2. **Create the CLI entry point**
|
|
415
|
+
```typescript
|
|
416
|
+
// src/cli.ts
|
|
417
|
+
#!/usr/bin/env node
|
|
418
|
+
import { Command } from 'commander';
|
|
419
|
+
import { create } from './commands/create';
|
|
420
|
+
|
|
421
|
+
const program = new Command();
|
|
422
|
+
|
|
423
|
+
program
|
|
424
|
+
.name('sdkgen')
|
|
425
|
+
.description('Generate SDKs from schema definitions')
|
|
426
|
+
.version('1.0.0');
|
|
427
|
+
|
|
428
|
+
program
|
|
429
|
+
.command('create <project-name>')
|
|
430
|
+
.description('Create a new SDK project')
|
|
431
|
+
.option('-t, --template <template>', 'template to use', 'default')
|
|
432
|
+
.option('-s, --schema <path>', 'path to schema file')
|
|
433
|
+
.option('--skip-install', 'skip dependency installation')
|
|
434
|
+
.option('--verbose', 'enable verbose logging')
|
|
435
|
+
.option('--dry-run', 'preview without writing files')
|
|
436
|
+
.action(create);
|
|
437
|
+
|
|
438
|
+
program.parse();
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
3. **Implement the generator core**
|
|
442
|
+
```typescript
|
|
443
|
+
// src/core/generator.ts
|
|
444
|
+
import ejs from "ejs";
|
|
445
|
+
import fs from "fs-extra";
|
|
446
|
+
import glob from "glob";
|
|
447
|
+
import path from "path";
|
|
448
|
+
|
|
449
|
+
export class Generator {
|
|
450
|
+
constructor(private context: GeneratorContext) {}
|
|
451
|
+
|
|
452
|
+
async generate(outputPath: string): Promise<void> {
|
|
453
|
+
// Implementation as shown in architecture section
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
4. **Build and link for testing**
|
|
459
|
+
```bash
|
|
460
|
+
npm run build
|
|
461
|
+
npm link
|
|
462
|
+
npx @palantir/pack.sdkgen create test-project --schema ./test.json
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## API Reference
|
|
466
|
+
|
|
467
|
+
### Generator Context
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
interface GeneratorContext {
|
|
471
|
+
projectName: string;
|
|
472
|
+
schema: any;
|
|
473
|
+
answers: Record<string, any>;
|
|
474
|
+
templateConfig: TemplateConfig;
|
|
475
|
+
schemaPath: string;
|
|
476
|
+
outputPath: string;
|
|
477
|
+
options: {
|
|
478
|
+
skipInstall?: boolean;
|
|
479
|
+
verbose?: boolean;
|
|
480
|
+
dryRun?: boolean;
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Template Configuration
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
interface TemplateConfig {
|
|
489
|
+
name: string;
|
|
490
|
+
description: string;
|
|
491
|
+
prompts?: PromptQuestion[];
|
|
492
|
+
templateFiles?: string[];
|
|
493
|
+
staticFiles?: string[];
|
|
494
|
+
hooks?: {
|
|
495
|
+
beforeGenerate?: string | Function;
|
|
496
|
+
afterGenerate?: string | Function;
|
|
497
|
+
beforeInstall?: string | Function;
|
|
498
|
+
afterInstall?: string | Function;
|
|
499
|
+
};
|
|
500
|
+
transformers?: {
|
|
501
|
+
[key: string]: string | Function;
|
|
502
|
+
};
|
|
503
|
+
dependencies?: Record<string, string>;
|
|
504
|
+
devDependencies?: Record<string, string>;
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Hook Functions
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
// Pre-generate hook
|
|
512
|
+
type PreGenerateHook = (context: GeneratorContext) => Promise<GeneratorContext>;
|
|
513
|
+
|
|
514
|
+
// Post-generate hook
|
|
515
|
+
type PostGenerateHook = (
|
|
516
|
+
context: GeneratorContext,
|
|
517
|
+
outputPath: string,
|
|
518
|
+
) => Promise<void>;
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## Examples
|
|
522
|
+
|
|
523
|
+
### Creating a REST API Client Template
|
|
524
|
+
|
|
525
|
+
```javascript
|
|
526
|
+
// template.config.js
|
|
527
|
+
module.exports = {
|
|
528
|
+
name: "rest-api-client",
|
|
529
|
+
prompts: [
|
|
530
|
+
{
|
|
531
|
+
type: "input",
|
|
532
|
+
name: "baseUrl",
|
|
533
|
+
message: "API base URL?",
|
|
534
|
+
default: "https://api.example.com",
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
type: "list",
|
|
538
|
+
name: "httpClient",
|
|
539
|
+
message: "HTTP client library?",
|
|
540
|
+
choices: ["fetch", "axios", "node-fetch"],
|
|
541
|
+
},
|
|
542
|
+
],
|
|
543
|
+
hooks: {
|
|
544
|
+
afterGenerate: async (context, outputPath) => {
|
|
545
|
+
// Install selected HTTP client
|
|
546
|
+
const { httpClient } = context.answers;
|
|
547
|
+
if (httpClient === "axios") {
|
|
548
|
+
// Add axios to package.json dependencies
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
};
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Using Environment-Specific Configuration
|
|
556
|
+
|
|
557
|
+
```bash
|
|
558
|
+
# Development
|
|
559
|
+
TEMPLATE_ENV=dev npx @palantir/pack.sdkgen create my-sdk \
|
|
560
|
+
--schema ./schema.json
|
|
561
|
+
|
|
562
|
+
# Production
|
|
563
|
+
TEMPLATE_ENV=prod npx @palantir/pack.sdkgen create my-sdk \
|
|
564
|
+
--schema ./schema.json
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## Troubleshooting
|
|
570
|
+
|
|
571
|
+
### Common Issues
|
|
572
|
+
|
|
573
|
+
**Template not found**
|
|
574
|
+
|
|
575
|
+
- Ensure the template package is published to npm
|
|
576
|
+
- Check that the template path is correct for local templates
|
|
577
|
+
- Verify the template has a valid `template.config.js`
|
|
578
|
+
|
|
579
|
+
**Schema parsing errors**
|
|
580
|
+
|
|
581
|
+
- Validate your schema file is valid JSON/YAML
|
|
582
|
+
- Check that the schema transformer matches your schema format
|
|
583
|
+
- Use `--verbose` flag for detailed error messages
|
|
584
|
+
|
|
585
|
+
**Generation fails**
|
|
586
|
+
|
|
587
|
+
- Run with `--dry-run` first to preview changes
|
|
588
|
+
- Check hook scripts for errors
|
|
589
|
+
- Ensure all required template variables are provided
|
package/bin/cli.ts
ADDED