@@ -15,6 +15,7 @@ import {
15
15
import { fetch } from "./fetch" ;
16
16
import { K8s } from "./fluent" ;
17
17
import { CustomResourceDefinition } from "./upstream" ;
18
+ import { LogFn } from "./types" ;
18
19
19
20
export interface GenerateOptions {
20
21
/** The source URL, yaml file path or K8s CRD name */
@@ -25,6 +26,10 @@ export interface GenerateOptions {
25
26
plain ?: boolean ;
26
27
/** The language to generate types in */
27
28
language ?: string | TargetLanguage ;
29
+ /** Override the NPM package to import when generating formatted Typescript */
30
+ npmPackage ?: string ;
31
+ /** Log function callback */
32
+ logFn : LogFn ;
28
33
}
29
34
30
35
/**
@@ -44,23 +49,32 @@ async function convertCRDtoTS(
44
49
const results : Record < string , string [ ] > = { } ;
45
50
46
51
for ( const match of crd . spec . versions ) {
52
+ const version = match . name ;
53
+
47
54
// Get the schema from the matched version
48
55
const schema = JSON . stringify ( match ?. schema ?. openAPIV3Schema ) ;
49
56
50
57
// Create a new JSONSchemaInput
51
58
const schemaInput = new JSONSchemaInput ( new FetchingJSONSchemaStore ( ) ) ;
52
59
60
+ opts . logFn ( `- Generating ${ crd . spec . group } /${ version } types for ${ name } ` ) ;
61
+
53
62
// Add the schema to the input
54
63
await schemaInput . addSource ( { name, schema } ) ;
55
64
56
65
// Create a new InputData object
57
66
const inputData = new InputData ( ) ;
58
67
inputData . addInput ( schemaInput ) ;
59
68
69
+ // If the language is not specified, default to TypeScript
70
+ if ( ! opts . language ) {
71
+ opts . language = "ts" ;
72
+ }
73
+
60
74
// Generate the types
61
75
const out = await quicktype ( {
62
76
inputData,
63
- lang : opts . language || "ts" ,
77
+ lang : opts . language ,
64
78
rendererOptions : { "just-types" : "true" } ,
65
79
} ) ;
66
80
@@ -73,11 +87,15 @@ async function convertCRDtoTS(
73
87
74
88
// If the language is TypeScript and plain is not specified, wire up the fluent client
75
89
if ( opts . language === "ts" && ! opts . plain ) {
90
+ if ( ! opts . npmPackage ) {
91
+ opts . npmPackage = "kubernetes-fluent-client" ;
92
+ }
93
+
76
94
processedLines . unshift (
77
95
// Add warning that the file is auto-generated
78
- `// This file is auto-generated by kubernetes-fluent-client , do not edit manually\n` ,
96
+ `// This file is auto-generated by ${ opts . npmPackage } , do not edit manually\n` ,
79
97
// Add the imports before any other lines
80
- `import { GenericKind, RegisterKind } from "kubernetes-fluent-client ";\n` ,
98
+ `import { GenericKind, RegisterKind } from "${ opts . npmPackage } ";\n` ,
81
99
) ;
82
100
83
101
// Replace the interface with a named class that extends GenericKind
@@ -91,13 +109,13 @@ async function convertCRDtoTS(
91
109
// Add the RegisterKind call
92
110
processedLines . push ( `RegisterKind(${ name } , {` ) ;
93
111
processedLines . push ( ` group: "${ crd . spec . group } ",` ) ;
94
- processedLines . push ( ` version: "${ match . name } ",` ) ;
112
+ processedLines . push ( ` version: "${ version } ",` ) ;
95
113
processedLines . push ( ` kind: "${ name } ",` ) ;
96
114
processedLines . push ( `});` ) ;
97
115
}
98
116
99
117
const finalContents = processedLines . join ( "\n" ) ;
100
- const fileName = `${ name . toLowerCase ( ) } -${ match . name . toLowerCase ( ) } ` ;
118
+ const fileName = `${ name . toLowerCase ( ) } -${ version . toLowerCase ( ) } ` ;
101
119
102
120
// If an output file is specified, write the output to the file
103
121
if ( opts . directory ) {
@@ -119,15 +137,17 @@ async function convertCRDtoTS(
119
137
/**
120
138
* Reads a CustomResourceDefinition from a file, the cluster or the internet
121
139
*
122
- * @param source The source to read from (file path, cluster or URL)
140
+ * @param opts The options to use when reading
123
141
* @returns A promise that resolves when the CustomResourceDefinition has been read
124
142
*/
125
- async function readOrFetchCrd ( source : string ) : Promise < CustomResourceDefinition [ ] > {
143
+ async function readOrFetchCrd ( opts : GenerateOptions ) : Promise < CustomResourceDefinition [ ] > {
144
+ const { source, logFn } = opts ;
126
145
const filePath = path . join ( process . cwd ( ) , source ) ;
127
146
128
147
// First try to read the source as a file
129
148
try {
130
149
if ( fs . existsSync ( filePath ) ) {
150
+ logFn ( `Attempting to load ${ source } as a local file` ) ;
131
151
const payload = fs . readFileSync ( filePath , "utf8" ) ;
132
152
return loadAllYaml ( payload ) as CustomResourceDefinition [ ] ;
133
153
}
@@ -141,6 +161,7 @@ async function readOrFetchCrd(source: string): Promise<CustomResourceDefinition[
141
161
142
162
// If the source is a URL, fetch it
143
163
if ( url . protocol === "http:" || url . protocol === "https:" ) {
164
+ logFn ( `Attempting to load ${ source } as a URL` ) ;
144
165
const { ok, data } = await fetch < string > ( source ) ;
145
166
146
167
// If the request failed, throw an error
@@ -151,14 +172,22 @@ async function readOrFetchCrd(source: string): Promise<CustomResourceDefinition[
151
172
return loadAllYaml ( data ) as CustomResourceDefinition [ ] ;
152
173
}
153
174
} catch ( e ) {
154
- // Ignore errors
175
+ // If invalid, ignore the error
176
+ if ( e . code !== "ERR_INVALID_URL" ) {
177
+ throw new Error ( e ) ;
178
+ }
155
179
}
156
180
157
181
// Finally, if the source is not a file or URL, try to read it as a CustomResourceDefinition from the cluster
158
182
try {
183
+ logFn ( `Attempting to read ${ source } from the current Kubernetes context` ) ;
159
184
return [ await K8s ( CustomResourceDefinition ) . Get ( source ) ] ;
160
185
} catch ( e ) {
161
- throw new Error ( `Failed to read ${ source } as a file, url or K8s CRD: ${ e } ` ) ;
186
+ throw new Error (
187
+ `Failed to read ${ source } as a file, url or K8s CRD: ${
188
+ e . data ?. message || "Cluster not available"
189
+ } `,
190
+ ) ;
162
191
}
163
192
}
164
193
@@ -169,11 +198,14 @@ async function readOrFetchCrd(source: string): Promise<CustomResourceDefinition[
169
198
* @returns A promise that resolves when the TypeScript types have been generated
170
199
*/
171
200
export async function generate ( opts : GenerateOptions ) {
172
- const crds = await readOrFetchCrd ( opts . source ) ;
201
+ const crds = ( await readOrFetchCrd ( opts ) ) . filter ( crd => ! ! crd ) ;
173
202
const results : Record < string , string [ ] > = { } ;
174
203
204
+ opts . logFn ( "" ) ;
205
+
175
206
for ( const crd of crds ) {
176
- if ( ! crd || crd . kind !== "CustomResourceDefinition" || ! crd . spec ?. versions ?. length ) {
207
+ if ( crd . kind !== "CustomResourceDefinition" || ! crd . spec ?. versions ?. length ) {
208
+ opts . logFn ( `Skipping ${ crd ?. metadata ?. name } , it does not appear to be a CRD` ) ;
177
209
// Ignore empty and non-CRD objects
178
210
continue ;
179
211
}
@@ -185,5 +217,12 @@ export async function generate(opts: GenerateOptions) {
185
217
}
186
218
}
187
219
220
+ if ( opts . directory ) {
221
+ // Notify the user that the files have been generated
222
+ opts . logFn (
223
+ `\n✅ Generated ${ Object . keys ( results ) . length } files in the ${ opts . directory } directory` ,
224
+ ) ;
225
+ }
226
+
188
227
return results ;
189
228
}
0 commit comments