kubernetes-fluent-client 3.0.3 → 3.0.4

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.
Files changed (38) hide show
  1. package/.prettierignore +4 -0
  2. package/README.md +24 -0
  3. package/dist/cli.js +21 -1
  4. package/dist/fileSystem.d.ts +11 -0
  5. package/dist/fileSystem.d.ts.map +1 -0
  6. package/dist/fileSystem.js +42 -0
  7. package/dist/fileSystem.test.d.ts +2 -0
  8. package/dist/fileSystem.test.d.ts.map +1 -0
  9. package/dist/fileSystem.test.js +75 -0
  10. package/dist/generate.d.ts +71 -11
  11. package/dist/generate.d.ts.map +1 -1
  12. package/dist/generate.js +130 -117
  13. package/dist/generate.test.js +293 -346
  14. package/dist/postProcessing.d.ts +246 -0
  15. package/dist/postProcessing.d.ts.map +1 -0
  16. package/dist/postProcessing.js +497 -0
  17. package/dist/postProcessing.test.d.ts +2 -0
  18. package/dist/postProcessing.test.d.ts.map +1 -0
  19. package/dist/postProcessing.test.js +550 -0
  20. package/e2e/cli.e2e.test.ts +123 -0
  21. package/e2e/crds/policyreports.default.expected/policyreport-v1alpha1.ts +332 -0
  22. package/e2e/crds/policyreports.default.expected/policyreport-v1alpha2.ts +360 -0
  23. package/e2e/crds/policyreports.default.expected/policyreport-v1beta1.ts +360 -0
  24. package/e2e/crds/policyreports.no.post.expected/policyreport-v1alpha1.ts +331 -0
  25. package/e2e/crds/policyreports.no.post.expected/policyreport-v1alpha2.ts +360 -0
  26. package/e2e/crds/policyreports.no.post.expected/policyreport-v1beta1.ts +360 -0
  27. package/e2e/crds/test.yaml/policyreports.test.yaml +1008 -0
  28. package/e2e/crds/test.yaml/uds-podmonitors.test.yaml +1245 -0
  29. package/e2e/crds/uds-podmonitors.default.expected/podmonitor-v1.ts +1333 -0
  30. package/e2e/crds/uds-podmonitors.no.post.expected/podmonitor-v1.ts +1360 -0
  31. package/package.json +6 -5
  32. package/src/cli.ts +25 -1
  33. package/src/fileSystem.test.ts +67 -0
  34. package/src/fileSystem.ts +25 -0
  35. package/src/generate.test.ts +368 -358
  36. package/src/generate.ts +173 -154
  37. package/src/postProcessing.test.ts +742 -0
  38. package/src/postProcessing.ts +568 -0
@@ -0,0 +1,4 @@
1
+ e2e/crds
2
+ e2e/crds/**/*.ts
3
+ e2e/generated
4
+ generated/**/*.ts
package/README.md CHANGED
@@ -105,6 +105,30 @@ Promise.all([
105
105
  });
106
106
  ```
107
107
 
108
+ ### Generating TypeScript Definitions from CRDs
109
+
110
+ The Kubernetes Fluent Client can generate TypeScript definitions from Custom Resource Definitions (CRDs) using the `generate` command. This command will generate TypeScript interfaces for the CRDs in the cluster and save them to a file.
111
+
112
+ To generate TypeScript definitions from CRDs, run the following command:
113
+
114
+ ```bash
115
+ kubernetes-fluent-client crd /path/to/input.yaml /path/to/output/folder
116
+ ```
117
+
118
+ If you have a CRD in a file named `crd.yaml` and you want to generate TypeScript definitions in a folder named `types`, you can run the following command:
119
+
120
+ ```bash
121
+ kubernetes-fluent-client crd crd.yaml types
122
+ ```
123
+
124
+ This will generate TypeScript interfaces for the CRD in the `crd.yaml` file and save them to the `types` folder.
125
+
126
+ By default, the generated TypeScript interfaces will be post-processed to make them more user-friendly. If you want to disable this post-processing, you can use the `--noPost` flag:
127
+
128
+ ```bash
129
+ kubernetes-fluent-client crd crd.yaml types --noPost
130
+ ```
131
+
108
132
  ### Community
109
133
 
110
134
  To chat with other users & see some examples of the fluent client in active use, go to [Kubernetes Slack](https://communityinviter.com/apps/kubernetes/community) and join `#pepr` channel.
package/dist/cli.js CHANGED
@@ -10,6 +10,8 @@ const helpers_1 = require("yargs/helpers");
10
10
  const yargs_1 = __importDefault(require("yargs/yargs"));
11
11
  const generate_1 = require("./generate");
12
12
  const package_json_1 = require("../package.json");
13
+ const postProcessing_1 = require("./postProcessing");
14
+ const fileSystem_1 = require("./fileSystem"); // Import your new file system
13
15
  void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
14
16
  .version("version", "Display version number", `kubernetes-fluent-client v${package_json_1.version}`)
15
17
  .alias("version", "V")
@@ -33,13 +35,31 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
33
35
  type: "string",
34
36
  default: "ts",
35
37
  description: "the language to generate types in, see https://github.com/glideapps/quicktype#target-languages for a list of supported languages",
38
+ })
39
+ .option("noPost", {
40
+ alias: "x",
41
+ type: "boolean",
42
+ default: false,
43
+ description: "disable post-processing after generating the types",
36
44
  })
37
45
  .demandOption(["source", "directory"]);
38
46
  }, async (argv) => {
39
47
  const opts = argv;
40
48
  opts.logFn = console.log;
49
+ // Pass the `post` flag to opts
50
+ opts.noPost = argv.noPost;
51
+ // Use NodeFileSystem as the file system for post-processing
52
+ const fileSystem = new fileSystem_1.NodeFileSystem(); // Create an instance of NodeFileSystem
53
+ if (!opts.noPost) {
54
+ console.log("\n✅ Post-processing has been enabled.\n");
55
+ }
41
56
  try {
42
- await (0, generate_1.generate)(opts);
57
+ // Capture the results returned by generate
58
+ const allResults = await (0, generate_1.generate)(opts);
59
+ // If noPost is false, run post-processing
60
+ if (!opts.noPost) {
61
+ await (0, postProcessing_1.postProcessing)(allResults, opts, fileSystem); // Pass the file system to postProcessing
62
+ }
43
63
  }
44
64
  catch (e) {
45
65
  console.log(`\n❌ ${e.message}`);
@@ -0,0 +1,11 @@
1
+ export interface FileSystem {
2
+ readFile(filePath: string): string;
3
+ writeFile(filePath: string, content: string): void;
4
+ readdirSync(directory: string): string[];
5
+ }
6
+ export declare class NodeFileSystem implements FileSystem {
7
+ readFile(filePath: string): string;
8
+ writeFile(filePath: string, content: string): void;
9
+ readdirSync(directory: string): string[];
10
+ }
11
+ //# sourceMappingURL=fileSystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileSystem.d.ts","sourceRoot":"","sources":["../src/fileSystem.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1C;AAGD,qBAAa,cAAe,YAAW,UAAU;IAC/C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAIlC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAIlD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;CAGzC"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || function (mod) {
21
+ if (mod && mod.__esModule) return mod;
22
+ var result = {};
23
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24
+ __setModuleDefault(result, mod);
25
+ return result;
26
+ };
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.NodeFileSystem = void 0;
29
+ const fs = __importStar(require("fs"));
30
+ /* eslint class-methods-use-this: "off" */
31
+ class NodeFileSystem {
32
+ readFile(filePath) {
33
+ return fs.readFileSync(filePath, "utf8");
34
+ }
35
+ writeFile(filePath, content) {
36
+ fs.writeFileSync(filePath, content, "utf8");
37
+ }
38
+ readdirSync(directory) {
39
+ return fs.readdirSync(directory);
40
+ }
41
+ }
42
+ exports.NodeFileSystem = NodeFileSystem;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fileSystem.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileSystem.test.d.ts","sourceRoot":"","sources":["../src/fileSystem.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || function (mod) {
21
+ if (mod && mod.__esModule) return mod;
22
+ var result = {};
23
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24
+ __setModuleDefault(result, mod);
25
+ return result;
26
+ };
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ const fs = __importStar(require("fs"));
29
+ const fileSystem_1 = require("./fileSystem");
30
+ const globals_1 = require("@jest/globals");
31
+ // Mock the fs module
32
+ globals_1.jest.mock("fs");
33
+ (0, globals_1.describe)("NodeFileSystem", () => {
34
+ let nodeFileSystem;
35
+ (0, globals_1.beforeEach)(() => {
36
+ nodeFileSystem = new fileSystem_1.NodeFileSystem();
37
+ globals_1.jest.clearAllMocks(); // Clear all mocks before each test
38
+ });
39
+ (0, globals_1.describe)("readFile", () => {
40
+ (0, globals_1.test)("should call fs.readFileSync with correct arguments", () => {
41
+ const mockFilePath = "test-file.txt";
42
+ const mockFileContent = "This is a test file";
43
+ // Mock the fs.readFileSync method to return the mock file content
44
+ fs.readFileSync.mockReturnValue(mockFileContent);
45
+ const result = nodeFileSystem.readFile(mockFilePath);
46
+ // Assert that fs.readFileSync was called with the correct file path and encoding
47
+ (0, globals_1.expect)(fs.readFileSync).toHaveBeenCalledWith(mockFilePath, "utf8");
48
+ // Assert that the returned content matches the mock file content
49
+ (0, globals_1.expect)(result).toBe(mockFileContent);
50
+ });
51
+ });
52
+ (0, globals_1.describe)("writeFile", () => {
53
+ (0, globals_1.test)("should call fs.writeFileSync with correct arguments", () => {
54
+ const mockFilePath = "test-file.txt";
55
+ const mockFileContent = "This is a test file";
56
+ // Call the writeFile method
57
+ nodeFileSystem.writeFile(mockFilePath, mockFileContent);
58
+ // Assert that fs.writeFileSync was called with the correct arguments
59
+ (0, globals_1.expect)(fs.writeFileSync).toHaveBeenCalledWith(mockFilePath, mockFileContent, "utf8");
60
+ });
61
+ });
62
+ (0, globals_1.describe)("readdirSync", () => {
63
+ (0, globals_1.test)("should call fs.readdirSync with correct arguments and return file list", () => {
64
+ const mockDirectoryPath = "test-directory";
65
+ const mockFileList = ["file1.txt", "file2.txt"];
66
+ // Mock the fs.readdirSync method to return the mock file list
67
+ fs.readdirSync.mockReturnValue(mockFileList);
68
+ const result = nodeFileSystem.readdirSync(mockDirectoryPath);
69
+ // Assert that fs.readdirSync was called with the correct directory path
70
+ (0, globals_1.expect)(fs.readdirSync).toHaveBeenCalledWith(mockDirectoryPath);
71
+ // Assert that the returned file list matches the mock file list
72
+ (0, globals_1.expect)(result).toEqual(mockFileList);
73
+ });
74
+ });
75
+ });
@@ -1,24 +1,84 @@
1
- import { TargetLanguage } from "quicktype-core";
1
+ import { InputData, TargetLanguage } from "quicktype-core";
2
+ import { CustomResourceDefinition } from "./upstream";
2
3
  import { LogFn } from "./types";
3
4
  export interface GenerateOptions {
4
- /** The source URL, yaml file path or K8s CRD name */
5
5
  source: string;
6
- /** The output directory path */
7
6
  directory?: string;
8
- /** Disable kubernetes-fluent-client wrapping */
9
7
  plain?: boolean;
10
- /** The language to generate types in */
11
8
  language?: string | TargetLanguage;
12
- /** Override the NPM package to import when generating formatted Typescript */
13
9
  npmPackage?: string;
14
- /** Log function callback */
15
10
  logFn: LogFn;
11
+ noPost?: boolean;
16
12
  }
17
13
  /**
18
- * Generate TypeScript types from a K8s CRD
14
+ * Converts a CustomResourceDefinition to TypeScript types
19
15
  *
20
- * @param opts The options to use when generating
21
- * @returns A promise that resolves when the TypeScript types have been generated
16
+ * @param crd - The CustomResourceDefinition object to convert.
17
+ * @param opts - The options for generating the TypeScript types.
18
+ * @returns A promise that resolves to a record of generated TypeScript types.
22
19
  */
23
- export declare function generate(opts: GenerateOptions): Promise<Record<string, string[]>>;
20
+ export declare function convertCRDtoTS(crd: CustomResourceDefinition, opts: GenerateOptions): Promise<{
21
+ results: Record<string, string[]>;
22
+ name: string;
23
+ crd: CustomResourceDefinition;
24
+ version: string;
25
+ }[]>;
26
+ /**
27
+ * Prepares the input data for quicktype from the provided schema.
28
+ *
29
+ * @param name - The name of the schema.
30
+ * @param schema - The JSON schema as a string.
31
+ * @returns A promise that resolves to the input data for quicktype.
32
+ */
33
+ export declare function prepareInputData(name: string, schema: string): Promise<InputData>;
34
+ /**
35
+ * Generates TypeScript types using quicktype.
36
+ *
37
+ * @param inputData - The input data for quicktype.
38
+ * @param opts - The options for generating the TypeScript types.
39
+ * @returns A promise that resolves to an array of generated TypeScript type lines.
40
+ */
41
+ export declare function generateTypes(inputData: InputData, opts: GenerateOptions): Promise<string[]>;
42
+ /**
43
+ * Writes the processed lines to the output file.
44
+ *
45
+ * @param fileName - The name of the file to write.
46
+ * @param directory - The directory where the file will be written.
47
+ * @param content - The content to write to the file.
48
+ * @param language - The programming language of the file.
49
+ */
50
+ export declare function writeGeneratedFile(fileName: string, directory: string, content: string[], language: string | TargetLanguage): void;
51
+ /**
52
+ * Reads or fetches a CustomResourceDefinition from a file, URL, or the cluster.
53
+ *
54
+ * @param opts - The options for generating the TypeScript types.
55
+ * @returns A promise that resolves to an array of CustomResourceDefinition objects.
56
+ */
57
+ export declare function readOrFetchCrd(opts: GenerateOptions): Promise<CustomResourceDefinition[]>;
58
+ /**
59
+ * Resolves the source file path, treating relative paths as local files.
60
+ *
61
+ * @param source - The source path to resolve.
62
+ * @returns The resolved file path.
63
+ */
64
+ export declare function resolveFilePath(source: string): string;
65
+ /**
66
+ * Tries to parse the source as a URL.
67
+ *
68
+ * @param source - The source string to parse as a URL.
69
+ * @returns The parsed URL object or null if parsing fails.
70
+ */
71
+ export declare function tryParseUrl(source: string): URL | null;
72
+ /**
73
+ * Main generate function to convert CRDs to TypeScript types.
74
+ *
75
+ * @param opts - The options for generating the TypeScript types.
76
+ * @returns A promise that resolves to a record of generated TypeScript types.
77
+ */
78
+ export declare function generate(opts: GenerateOptions): Promise<{
79
+ results: Record<string, string[]>;
80
+ name: string;
81
+ crd: CustomResourceDefinition;
82
+ version: string;
83
+ }[]>;
24
84
  //# sourceMappingURL=generate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAMA,OAAO,EAIL,cAAc,EAEf,MAAM,gBAAgB,CAAC;AAKxB,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC;IACnC,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,KAAK,EAAE,KAAK,CAAC;CACd;AA0KD;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,eAAe,qCA4BnD"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,SAAS,EAET,cAAc,EAEf,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,eAAe,GACpB,OAAO,CACR;IACE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,wBAAwB,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB,EAAE,CACJ,CAuCA;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAYvF;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CAYnB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,EAAE,MAAM,GAAG,cAAc,GAChC,IAAI,CAON;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC,CA0B/F;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAMtD;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAC5D;IACE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,wBAAwB,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB,EAAE,CACJ,CA8BA"}
package/dist/generate.js CHANGED
@@ -25,6 +25,13 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  return result;
26
26
  };
27
27
  Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.convertCRDtoTS = convertCRDtoTS;
29
+ exports.prepareInputData = prepareInputData;
30
+ exports.generateTypes = generateTypes;
31
+ exports.writeGeneratedFile = writeGeneratedFile;
32
+ exports.readOrFetchCrd = readOrFetchCrd;
33
+ exports.resolveFilePath = resolveFilePath;
34
+ exports.tryParseUrl = tryParseUrl;
28
35
  exports.generate = generate;
29
36
  const client_node_1 = require("@kubernetes/client-node");
30
37
  const fs = __importStar(require("fs"));
@@ -36,144 +43,150 @@ const upstream_1 = require("./upstream");
36
43
  /**
37
44
  * Converts a CustomResourceDefinition to TypeScript types
38
45
  *
39
- * @param crd The CustomResourceDefinition to convert
40
- * @param opts The options to use when converting
41
- * @returns A promise that resolves when the CustomResourceDefinition has been converted
46
+ * @param crd - The CustomResourceDefinition object to convert.
47
+ * @param opts - The options for generating the TypeScript types.
48
+ * @returns A promise that resolves to a record of generated TypeScript types.
42
49
  */
43
50
  async function convertCRDtoTS(crd, opts) {
44
- // Get the name of the kind
45
51
  const name = crd.spec.names.kind;
46
52
  const results = {};
53
+ const output = [];
54
+ // Check for missing versions or empty schema
55
+ if (!crd.spec.versions || crd.spec.versions.length === 0) {
56
+ opts.logFn(`Skipping ${crd.metadata?.name}, it does not appear to be a CRD`);
57
+ return [];
58
+ }
59
+ // Iterate through each version of the CRD
47
60
  for (const match of crd.spec.versions) {
48
- const version = match.name;
49
- // Get the schema from the matched version
50
- const schema = JSON.stringify(match?.schema?.openAPIV3Schema);
51
- // Create a new JSONSchemaInput
52
- const schemaInput = new quicktype_core_1.JSONSchemaInput(new quicktype_core_1.FetchingJSONSchemaStore());
53
- opts.logFn(`- Generating ${crd.spec.group}/${version} types for ${name}`);
54
- // Add the schema to the input
55
- await schemaInput.addSource({ name, schema });
56
- // Create a new InputData object
57
- const inputData = new quicktype_core_1.InputData();
58
- inputData.addInput(schemaInput);
59
- // If the language is not specified, default to TypeScript
60
- if (!opts.language) {
61
- opts.language = "ts";
62
- }
63
- // Generate the types
64
- const out = await (0, quicktype_core_1.quicktype)({
65
- inputData,
66
- lang: opts.language,
67
- rendererOptions: { "just-types": "true" },
68
- });
69
- let processedLines = out.lines;
70
- // If using typescript, remove the line containing `[property: string]: any;`
71
- if (opts.language === "ts" || opts.language === "typescript") {
72
- processedLines = out.lines.filter(line => !line.includes("[property: string]: any;"));
73
- }
74
- // If the language is TypeScript and plain is not specified, wire up the fluent client
75
- if (opts.language === "ts" && !opts.plain) {
76
- if (!opts.npmPackage) {
77
- opts.npmPackage = "kubernetes-fluent-client";
78
- }
79
- processedLines.unshift(
80
- // Add warning that the file is auto-generated
81
- `// This file is auto-generated by ${opts.npmPackage}, do not edit manually\n`,
82
- // Add the imports before any other lines
83
- `import { GenericKind, RegisterKind } from "${opts.npmPackage}";\n`);
84
- // Replace the interface with a named class that extends GenericKind
85
- const entryIdx = processedLines.findIndex(line => line.includes(`export interface ${name} {`));
86
- // Replace the interface with a named class that extends GenericKind
87
- processedLines[entryIdx] = `export class ${name} extends GenericKind {`;
88
- // Add the RegisterKind call
89
- processedLines.push(`RegisterKind(${name}, {`);
90
- processedLines.push(` group: "${crd.spec.group}",`);
91
- processedLines.push(` version: "${version}",`);
92
- processedLines.push(` kind: "${name}",`);
93
- processedLines.push(` plural: "${crd.spec.names.plural}",`);
94
- processedLines.push(`});`);
95
- }
96
- const finalContents = processedLines.join("\n");
97
- const fileName = `${name.toLowerCase()}-${version.toLowerCase()}`;
98
- // If an output file is specified, write the output to the file
99
- if (opts.directory) {
100
- // Create the directory if it doesn't exist
101
- fs.mkdirSync(opts.directory, { recursive: true });
102
- // Write the file
103
- const filePath = path.join(opts.directory, `${fileName}.${opts.language}`);
104
- fs.writeFileSync(filePath, finalContents);
61
+ if (!match.schema?.openAPIV3Schema) {
62
+ opts.logFn(`Skipping ${crd.metadata?.name ?? "unknown"}, it does not appear to have a valid schema`);
63
+ continue;
105
64
  }
106
- // Add the results to the array
107
- results[fileName] = processedLines;
65
+ const schema = JSON.stringify(match.schema.openAPIV3Schema);
66
+ opts.logFn(`- Generating ${crd.spec.group}/${match.name} types for ${name}`);
67
+ const inputData = await prepareInputData(name, schema);
68
+ const generatedTypes = await generateTypes(inputData, opts);
69
+ const fileName = `${name.toLowerCase()}-${match.name.toLowerCase()}`;
70
+ writeGeneratedFile(fileName, opts.directory || "", generatedTypes, opts.language || "ts");
71
+ results[fileName] = generatedTypes;
72
+ output.push({ results, name, crd, version: match.name });
108
73
  }
109
- return results;
74
+ return output;
75
+ }
76
+ /**
77
+ * Prepares the input data for quicktype from the provided schema.
78
+ *
79
+ * @param name - The name of the schema.
80
+ * @param schema - The JSON schema as a string.
81
+ * @returns A promise that resolves to the input data for quicktype.
82
+ */
83
+ async function prepareInputData(name, schema) {
84
+ // Create a new JSONSchemaInput
85
+ const schemaInput = new quicktype_core_1.JSONSchemaInput(new quicktype_core_1.FetchingJSONSchemaStore());
86
+ // Add the schema to the input
87
+ await schemaInput.addSource({ name, schema });
88
+ // Create a new InputData object
89
+ const inputData = new quicktype_core_1.InputData();
90
+ inputData.addInput(schemaInput);
91
+ return inputData;
110
92
  }
111
93
  /**
112
- * Reads a CustomResourceDefinition from a file, the cluster or the internet
94
+ * Generates TypeScript types using quicktype.
113
95
  *
114
- * @param opts The options to use when reading
115
- * @returns A promise that resolves when the CustomResourceDefinition has been read
96
+ * @param inputData - The input data for quicktype.
97
+ * @param opts - The options for generating the TypeScript types.
98
+ * @returns A promise that resolves to an array of generated TypeScript type lines.
99
+ */
100
+ async function generateTypes(inputData, opts) {
101
+ // If the language is not specified, default to TypeScript
102
+ const language = opts.language || "ts";
103
+ // Generate the types
104
+ const out = await (0, quicktype_core_1.quicktype)({
105
+ inputData,
106
+ lang: language,
107
+ rendererOptions: { "just-types": "true" },
108
+ });
109
+ return out.lines;
110
+ }
111
+ /**
112
+ * Writes the processed lines to the output file.
113
+ *
114
+ * @param fileName - The name of the file to write.
115
+ * @param directory - The directory where the file will be written.
116
+ * @param content - The content to write to the file.
117
+ * @param language - The programming language of the file.
118
+ */
119
+ function writeGeneratedFile(fileName, directory, content, language) {
120
+ language = language || "ts";
121
+ if (!directory)
122
+ return;
123
+ const filePath = path.join(directory, `${fileName}.${language}`);
124
+ fs.mkdirSync(directory, { recursive: true });
125
+ fs.writeFileSync(filePath, content.join("\n"));
126
+ }
127
+ /**
128
+ * Reads or fetches a CustomResourceDefinition from a file, URL, or the cluster.
129
+ *
130
+ * @param opts - The options for generating the TypeScript types.
131
+ * @returns A promise that resolves to an array of CustomResourceDefinition objects.
116
132
  */
117
133
  async function readOrFetchCrd(opts) {
118
- const { source, logFn } = opts;
119
- let filePath;
120
- if (source[0] === "/") {
121
- // If source is an absolute path
122
- filePath = source;
123
- }
124
- else {
125
- // If source is a relative path
126
- filePath = path.join(process.cwd(), source);
127
- }
128
- // First try to read the source as a file
129
134
  try {
135
+ const filePath = resolveFilePath(opts.source);
130
136
  if (fs.existsSync(filePath)) {
131
- logFn(`Attempting to load ${source} as a local file`);
132
- const payload = fs.readFileSync(filePath, "utf8");
133
- return (0, client_node_1.loadAllYaml)(payload);
137
+ opts.logFn(`Attempting to load ${opts.source} as a local file`);
138
+ const content = fs.readFileSync(filePath, "utf8");
139
+ return (0, client_node_1.loadAllYaml)(content);
134
140
  }
135
- }
136
- catch {
137
- // Ignore errors
138
- }
139
- // Next try to parse the source as a URL
140
- try {
141
- const url = new URL(source);
142
- // If the source is a URL, fetch it
143
- if (url.protocol === "http:" || url.protocol === "https:") {
144
- logFn(`Attempting to load ${source} as a URL`);
145
- const { ok, data } = await (0, fetch_1.fetch)(source);
146
- // If the request failed, throw an error
147
- if (!ok) {
148
- throw new Error(`Failed to fetch ${source}: ${data}`);
141
+ const url = tryParseUrl(opts.source);
142
+ if (url) {
143
+ opts.logFn(`Attempting to load ${opts.source} as a URL`);
144
+ const { ok, data } = await (0, fetch_1.fetch)(url.href);
145
+ if (ok) {
146
+ return (0, client_node_1.loadAllYaml)(data);
149
147
  }
150
- return (0, client_node_1.loadAllYaml)(data);
151
148
  }
149
+ // Fallback to Kubernetes cluster
150
+ opts.logFn(`Attempting to read ${opts.source} from the Kubernetes cluster`);
151
+ return [await (0, fluent_1.K8s)(upstream_1.CustomResourceDefinition).Get(opts.source)];
152
152
  }
153
- catch (e) {
154
- // If invalid, ignore the error
155
- if (e.code !== "ERR_INVALID_URL") {
156
- throw new Error(`Error parsing URL ${source}`);
157
- }
153
+ catch (error) {
154
+ opts.logFn(`Error loading CRD: ${error.message}`);
155
+ throw new Error(`Failed to read ${opts.source} as a file, URL, or Kubernetes CRD`);
158
156
  }
159
- // Finally, if the source is not a file or URL, try to read it as a CustomResourceDefinition from the cluster
157
+ }
158
+ /**
159
+ * Resolves the source file path, treating relative paths as local files.
160
+ *
161
+ * @param source - The source path to resolve.
162
+ * @returns The resolved file path.
163
+ */
164
+ function resolveFilePath(source) {
165
+ return source.startsWith("/") ? source : path.join(process.cwd(), source);
166
+ }
167
+ /**
168
+ * Tries to parse the source as a URL.
169
+ *
170
+ * @param source - The source string to parse as a URL.
171
+ * @returns The parsed URL object or null if parsing fails.
172
+ */
173
+ function tryParseUrl(source) {
160
174
  try {
161
- logFn(`Attempting to read ${source} from the current Kubernetes context`);
162
- return [await (0, fluent_1.K8s)(upstream_1.CustomResourceDefinition).Get(source)];
175
+ return new URL(source);
163
176
  }
164
- catch (e) {
165
- throw new Error(`Failed to read ${source} as a file, url or K8s CRD: ${e.data?.message || "Cluster not available"}`);
177
+ catch {
178
+ return null;
166
179
  }
167
180
  }
168
181
  /**
169
- * Generate TypeScript types from a K8s CRD
182
+ * Main generate function to convert CRDs to TypeScript types.
170
183
  *
171
- * @param opts The options to use when generating
172
- * @returns A promise that resolves when the TypeScript types have been generated
184
+ * @param opts - The options for generating the TypeScript types.
185
+ * @returns A promise that resolves to a record of generated TypeScript types.
173
186
  */
174
187
  async function generate(opts) {
175
188
  const crds = (await readOrFetchCrd(opts)).filter(crd => !!crd);
176
- const results = {};
189
+ const allResults = [];
177
190
  opts.logFn("");
178
191
  for (const crd of crds) {
179
192
  if (crd.kind !== "CustomResourceDefinition" || !crd.spec?.versions?.length) {
@@ -181,15 +194,15 @@ async function generate(opts) {
181
194
  // Ignore empty and non-CRD objects
182
195
  continue;
183
196
  }
184
- // Add the results to the record
185
- const out = await convertCRDtoTS(crd, opts);
186
- for (const key of Object.keys(out)) {
187
- results[key] = out[key];
188
- }
197
+ allResults.push(...(await convertCRDtoTS(crd, opts)));
189
198
  }
190
199
  if (opts.directory) {
191
200
  // Notify the user that the files have been generated
192
- opts.logFn(`\n✅ Generated ${Object.keys(results).length} files in the ${opts.directory} directory`);
201
+ opts.logFn(`\n✅ Generated ${allResults.length} files in the ${opts.directory} directory`);
202
+ }
203
+ else {
204
+ // Log a message about the number of generated files even when no directory is provided
205
+ opts.logFn(`\n✅ Generated ${allResults.length} files`);
193
206
  }
194
- return results;
207
+ return allResults;
195
208
  }