pepr 1.2.0 → 1.2.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/dist/cli/crd/generate/generators.d.ts +5 -4
- package/dist/cli/crd/generate/generators.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +0 -5
- package/dist/cli/init/templates.d.ts.map +1 -1
- package/dist/cli.js +169 -105
- package/dist/controller.js +13 -18
- package/package.json +13 -18
- package/src/cli/crd/generate/generators.ts +161 -79
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from "typescript";
|
|
2
2
|
import { V1JSONSchemaProps } from "@kubernetes/client-node";
|
|
3
3
|
export declare function generateCRDs(options: {
|
|
4
4
|
output: string;
|
|
5
5
|
}): Promise<void>;
|
|
6
6
|
export declare function getAPIVersions(apiRoot: string): string[];
|
|
7
|
-
export declare function
|
|
8
|
-
export declare function
|
|
7
|
+
export declare function loadVersionFilePaths(versionDir: string): string[];
|
|
8
|
+
export declare function createProgram(filePaths: string[]): ts.Program;
|
|
9
|
+
export declare function processSourceFile(sourceFile: ts.SourceFile, checker: ts.TypeChecker, version: string, outputDir: string): void;
|
|
9
10
|
export declare function extractSingleLineComment(content: string, label: string): string | undefined;
|
|
10
|
-
export declare function extractDetails(sourceFile: SourceFile): {
|
|
11
|
+
export declare function extractDetails(sourceFile: ts.SourceFile): {
|
|
11
12
|
plural: string;
|
|
12
13
|
scope: "Cluster" | "Namespaced";
|
|
13
14
|
shortName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../../../src/cli/crd/generate/generators.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../../../src/cli/crd/generate/generators.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,YAAY,CAAC;AAK5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AA8B5D,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB7E;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAExD;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAGjE;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAM7D;AAED,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,EAAE,CAAC,UAAU,EACzB,OAAO,EAAE,EAAE,CAAC,WAAW,EACvB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,IAAI,CAiCN;AAGD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI3F;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,GAAG,YAAY,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;CACnB,CA2CA;AA4ID,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,WAAW,IAAI;IAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC9C,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAEA"}
|
|
@@ -18,14 +18,9 @@ export declare const dependencies: {
|
|
|
18
18
|
"prom-client": string;
|
|
19
19
|
"quicktype-core": string;
|
|
20
20
|
ramda: string;
|
|
21
|
-
"ts-morph": string;
|
|
22
21
|
}, devDependencies: {
|
|
23
22
|
"@commitlint/cli": string;
|
|
24
23
|
"@commitlint/config-conventional": string;
|
|
25
|
-
"@semantic-release/commit-analyzer": string;
|
|
26
|
-
"@semantic-release/git": string;
|
|
27
|
-
"@semantic-release/npm": string;
|
|
28
|
-
"@semantic-release/release-notes-generator": string;
|
|
29
24
|
"@types/command-line-args": string;
|
|
30
25
|
"@types/express": string;
|
|
31
26
|
"@types/json-pointer": string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/cli/init/templates.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,YAAY,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAIrE,eAAO,MAAQ,YAAY
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/cli/init/templates.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,YAAY,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAIrE,eAAO,MAAQ,YAAY;;;;;;;;;;;;;;;;GAAE,eAAe;;;;;;;;;;;;;;;;;;;;;;GAAE,gBAAgB;;;;;;;;GAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAAE,OAAO,QAAgB,CAAC;AAEjG,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1B,IAAI,EAAE;YACJ,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,OAAO,CAAC;YACjB,cAAc,EAAE,MAAM,CAAC;YACvB,YAAY,EAAE,YAAY,CAAC;YAC3B,YAAY,EAAE;gBAAE,UAAU,EAAE,MAAM,EAAE,CAAA;aAAE,CAAC;YACvC,SAAS,EAAE;gBAAE,YAAY,EAAE;oBAAE,UAAU,EAAE,MAAM,EAAE,CAAA;iBAAE,CAAA;aAAE,CAAC;YACtD,KAAK,EAAE;gBAAE,YAAY,EAAE;oBAAE,UAAU,EAAE,MAAM,EAAE,CAAA;iBAAE,CAAA;aAAE,CAAC;YAClD,aAAa,EAAE,MAAM,EAAE,CAAC;YACxB,GAAG,EAAE,MAAM,CAAC;YACZ,IAAI,CAAC,EAAE,UAAU,EAAE,CAAC;YACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;SACrB,CAAC;QACF,OAAO,EAAE;YAAE,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;QACjC,YAAY,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC/C,eAAe,EAAE;YAAE,UAAU,EAAE,MAAM,CAAA;SAAE,CAAC;KACzC,CAAC;IACF,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,CA4D7D;AAED,eAAO,MAAM,cAAc;;;CAG1B,CAAC;AAEF,eAAO,MAAM,MAAM;;;CAGlB,CAAC;AAEF,eAAO,MAAM,SAAS;;;CAGrB,CAAC;AAEF,eAAO,MAAM,SAAS;;;CAGrB,CAAC;AAEF,eAAO,MAAM,WAAW;;;CAGvB,CAAC;AAEF,eAAO,MAAM,OAAO;;;;;;;;;CAGnB,CAAC;AAEF,eAAO,MAAM,YAAY;;;;;;;;CAGxB,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;CAGpB,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;CAGpB,CAAC;AAEF,eAAO,MAAM,MAAM;;;CAgBlB,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -29,13 +29,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
29
29
|
var require_identity = __commonJS({
|
|
30
30
|
"node_modules/yaml/dist/nodes/identity.js"(exports2) {
|
|
31
31
|
"use strict";
|
|
32
|
-
var ALIAS = Symbol.for("yaml.alias");
|
|
33
|
-
var DOC = Symbol.for("yaml.document");
|
|
34
|
-
var MAP = Symbol.for("yaml.map");
|
|
35
|
-
var PAIR = Symbol.for("yaml.pair");
|
|
36
|
-
var SCALAR = Symbol.for("yaml.scalar");
|
|
37
|
-
var SEQ = Symbol.for("yaml.seq");
|
|
38
|
-
var NODE_TYPE = Symbol.for("yaml.node.type");
|
|
32
|
+
var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias");
|
|
33
|
+
var DOC = /* @__PURE__ */ Symbol.for("yaml.document");
|
|
34
|
+
var MAP = /* @__PURE__ */ Symbol.for("yaml.map");
|
|
35
|
+
var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair");
|
|
36
|
+
var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar");
|
|
37
|
+
var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq");
|
|
38
|
+
var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type");
|
|
39
39
|
var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS;
|
|
40
40
|
var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC;
|
|
41
41
|
var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP;
|
|
@@ -87,9 +87,9 @@ var require_visit = __commonJS({
|
|
|
87
87
|
"node_modules/yaml/dist/visit.js"(exports2) {
|
|
88
88
|
"use strict";
|
|
89
89
|
var identity = require_identity();
|
|
90
|
-
var BREAK = Symbol("break visit");
|
|
91
|
-
var SKIP = Symbol("skip children");
|
|
92
|
-
var REMOVE = Symbol("remove node");
|
|
90
|
+
var BREAK = /* @__PURE__ */ Symbol("break visit");
|
|
91
|
+
var SKIP = /* @__PURE__ */ Symbol("skip children");
|
|
92
|
+
var REMOVE = /* @__PURE__ */ Symbol("remove node");
|
|
93
93
|
function visit(node, visitor) {
|
|
94
94
|
const visitor_ = initVisitor(visitor);
|
|
95
95
|
if (identity.isDocument(node)) {
|
|
@@ -609,9 +609,9 @@ var require_Alias = __commonJS({
|
|
|
609
609
|
var anchors = require_anchors();
|
|
610
610
|
var visit = require_visit();
|
|
611
611
|
var identity = require_identity();
|
|
612
|
-
var
|
|
612
|
+
var Node = require_Node();
|
|
613
613
|
var toJS = require_toJS();
|
|
614
|
-
var Alias = class extends
|
|
614
|
+
var Alias = class extends Node.NodeBase {
|
|
615
615
|
constructor(source) {
|
|
616
616
|
super(identity.ALIAS);
|
|
617
617
|
this.source = source;
|
|
@@ -721,10 +721,10 @@ var require_Scalar = __commonJS({
|
|
|
721
721
|
"node_modules/yaml/dist/nodes/Scalar.js"(exports2) {
|
|
722
722
|
"use strict";
|
|
723
723
|
var identity = require_identity();
|
|
724
|
-
var
|
|
724
|
+
var Node = require_Node();
|
|
725
725
|
var toJS = require_toJS();
|
|
726
726
|
var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object";
|
|
727
|
-
var Scalar = class extends
|
|
727
|
+
var Scalar = class extends Node.NodeBase {
|
|
728
728
|
constructor(value) {
|
|
729
729
|
super(identity.SCALAR);
|
|
730
730
|
this.value = value;
|
|
@@ -827,7 +827,7 @@ var require_Collection = __commonJS({
|
|
|
827
827
|
"use strict";
|
|
828
828
|
var createNode = require_createNode();
|
|
829
829
|
var identity = require_identity();
|
|
830
|
-
var
|
|
830
|
+
var Node = require_Node();
|
|
831
831
|
function collectionFromPath(schema, path4, value) {
|
|
832
832
|
let v = value;
|
|
833
833
|
for (let i = path4.length - 1; i >= 0; --i) {
|
|
@@ -851,7 +851,7 @@ var require_Collection = __commonJS({
|
|
|
851
851
|
});
|
|
852
852
|
}
|
|
853
853
|
var isEmptyPath = (path4) => path4 == null || typeof path4 === "object" && !!path4[Symbol.iterator]().next().done;
|
|
854
|
-
var Collection = class extends
|
|
854
|
+
var Collection = class extends Node.NodeBase {
|
|
855
855
|
constructor(type, schema) {
|
|
856
856
|
super(type);
|
|
857
857
|
Object.defineProperty(this, "schema", {
|
|
@@ -2104,8 +2104,8 @@ var require_YAMLMap = __commonJS({
|
|
|
2104
2104
|
* @param {Class} Type - If set, forces the returned collection type
|
|
2105
2105
|
* @returns Instance of Type, Map, or Object
|
|
2106
2106
|
*/
|
|
2107
|
-
toJSON(_, ctx,
|
|
2108
|
-
const map =
|
|
2107
|
+
toJSON(_, ctx, Type) {
|
|
2108
|
+
const map = Type ? new Type() : ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {};
|
|
2109
2109
|
if (ctx?.onCreate)
|
|
2110
2110
|
ctx.onCreate(map);
|
|
2111
2111
|
for (const item of this.items)
|
|
@@ -4584,9 +4584,9 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
4584
4584
|
default: {
|
|
4585
4585
|
const message = `Unexpected token in block scalar header: ${token.type}`;
|
|
4586
4586
|
onError(token, "UNEXPECTED_TOKEN", message);
|
|
4587
|
-
const
|
|
4588
|
-
if (
|
|
4589
|
-
length +=
|
|
4587
|
+
const ts2 = token.source;
|
|
4588
|
+
if (ts2 && typeof ts2 === "string")
|
|
4589
|
+
length += ts2.length;
|
|
4590
4590
|
}
|
|
4591
4591
|
}
|
|
4592
4592
|
}
|
|
@@ -4894,9 +4894,9 @@ var require_compose_scalar = __commonJS({
|
|
|
4894
4894
|
if (schema.compat) {
|
|
4895
4895
|
const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR];
|
|
4896
4896
|
if (tag.tag !== compat.tag) {
|
|
4897
|
-
const
|
|
4897
|
+
const ts2 = directives.tagString(tag.tag);
|
|
4898
4898
|
const cs = directives.tagString(compat.tag);
|
|
4899
|
-
const msg = `Value may be parsed as either ${
|
|
4899
|
+
const msg = `Value may be parsed as either ${ts2} or ${cs}`;
|
|
4900
4900
|
onError(token, "TAG_RESOLVE_FAILED", msg, true);
|
|
4901
4901
|
}
|
|
4902
4902
|
}
|
|
@@ -5541,9 +5541,9 @@ var require_cst_stringify = __commonJS({
|
|
|
5541
5541
|
var require_cst_visit = __commonJS({
|
|
5542
5542
|
"node_modules/yaml/dist/parse/cst-visit.js"(exports2) {
|
|
5543
5543
|
"use strict";
|
|
5544
|
-
var BREAK = Symbol("break visit");
|
|
5545
|
-
var SKIP = Symbol("skip children");
|
|
5546
|
-
var REMOVE = Symbol("remove item");
|
|
5544
|
+
var BREAK = /* @__PURE__ */ Symbol("break visit");
|
|
5545
|
+
var SKIP = /* @__PURE__ */ Symbol("skip children");
|
|
5546
|
+
var REMOVE = /* @__PURE__ */ Symbol("remove item");
|
|
5547
5547
|
function visit(cst, visitor) {
|
|
5548
5548
|
if ("type" in cst && cst.type === "document")
|
|
5549
5549
|
cst = { start: cst.start, value: cst.value };
|
|
@@ -9645,7 +9645,7 @@ var packageJSON = {
|
|
|
9645
9645
|
"!src/fixtures/**",
|
|
9646
9646
|
"!dist/**/*.test.d.ts*"
|
|
9647
9647
|
],
|
|
9648
|
-
version: "1.2.
|
|
9648
|
+
version: "1.2.1",
|
|
9649
9649
|
main: "dist/lib.js",
|
|
9650
9650
|
types: "dist/lib.d.ts",
|
|
9651
9651
|
scripts: {
|
|
@@ -9679,8 +9679,8 @@ var packageJSON = {
|
|
|
9679
9679
|
},
|
|
9680
9680
|
dependencies: {
|
|
9681
9681
|
"@types/ramda": "0.31.1",
|
|
9682
|
-
"@typescript-eslint/eslint-plugin": "8.59.
|
|
9683
|
-
"@typescript-eslint/parser": "8.59.
|
|
9682
|
+
"@typescript-eslint/eslint-plugin": "8.59.4",
|
|
9683
|
+
"@typescript-eslint/parser": "8.59.4",
|
|
9684
9684
|
commander: "14.0.3",
|
|
9685
9685
|
eslint: "9.39.4",
|
|
9686
9686
|
express: "5.2.1",
|
|
@@ -9692,16 +9692,11 @@ var packageJSON = {
|
|
|
9692
9692
|
"pino-pretty": "13.1.3",
|
|
9693
9693
|
"prom-client": "15.1.3",
|
|
9694
9694
|
"quicktype-core": "^23.2.6",
|
|
9695
|
-
ramda: "0.32.0"
|
|
9696
|
-
"ts-morph": "^27.0.2"
|
|
9695
|
+
ramda: "0.32.0"
|
|
9697
9696
|
},
|
|
9698
9697
|
devDependencies: {
|
|
9699
|
-
"@commitlint/cli": "
|
|
9700
|
-
"@commitlint/config-conventional": "
|
|
9701
|
-
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
9702
|
-
"@semantic-release/git": "^10.0.1",
|
|
9703
|
-
"@semantic-release/npm": "^13.0.0",
|
|
9704
|
-
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
9698
|
+
"@commitlint/cli": "21.0.1",
|
|
9699
|
+
"@commitlint/config-conventional": "21.0.1",
|
|
9705
9700
|
"@types/command-line-args": "^5.2.3",
|
|
9706
9701
|
"@types/express": "5.0.6",
|
|
9707
9702
|
"@types/json-pointer": "^1.0.34",
|
|
@@ -9727,13 +9722,13 @@ var packageJSON = {
|
|
|
9727
9722
|
rollup: "4.59.0"
|
|
9728
9723
|
},
|
|
9729
9724
|
peerDependencies: {
|
|
9730
|
-
"@types/prompts": "2.4.9",
|
|
9731
|
-
esbuild: "0.
|
|
9732
|
-
"node-forge": "1.4.0",
|
|
9733
|
-
prettier: "3.6.2",
|
|
9734
|
-
prompts: "2.4.2",
|
|
9735
|
-
typescript: "5.8.3",
|
|
9736
|
-
uuid: "13.0.0"
|
|
9725
|
+
"@types/prompts": "^2.4.9",
|
|
9726
|
+
esbuild: "^0.28.0",
|
|
9727
|
+
"node-forge": "^1.4.0",
|
|
9728
|
+
prettier: "^3.6.2",
|
|
9729
|
+
prompts: "^2.4.2",
|
|
9730
|
+
typescript: "^5.8.3",
|
|
9731
|
+
uuid: "^13.0.0"
|
|
9737
9732
|
}
|
|
9738
9733
|
};
|
|
9739
9734
|
|
|
@@ -11100,8 +11095,8 @@ var import_commander4 = require("commander");
|
|
|
11100
11095
|
// src/cli/crd/generate/generators.ts
|
|
11101
11096
|
var import_fs14 = __toESM(require("fs"));
|
|
11102
11097
|
var import_path8 = __toESM(require("path"));
|
|
11098
|
+
var import_typescript = __toESM(require("typescript"));
|
|
11103
11099
|
var import_yaml = __toESM(require_dist());
|
|
11104
|
-
var import_ts_morph = require("ts-morph");
|
|
11105
11100
|
|
|
11106
11101
|
// src/cli/crd/generate/messages.ts
|
|
11107
11102
|
var ErrorMessages = {
|
|
@@ -11118,6 +11113,9 @@ var WarningMessages = {
|
|
|
11118
11113
|
// src/cli/crd/generate/generators.ts
|
|
11119
11114
|
function extractCRDDetails(content, sourceFile) {
|
|
11120
11115
|
const kind8 = extractSingleLineComment(content, "Kind");
|
|
11116
|
+
if (!kind8) {
|
|
11117
|
+
return { kind: void 0, fqdn: "", scope: "Namespaced", plural: "", shortNames: void 0 };
|
|
11118
|
+
}
|
|
11121
11119
|
const group2 = extractSingleLineComment(content, "Group") ?? "example";
|
|
11122
11120
|
const domain = extractSingleLineComment(content, "Domain") ?? "pepr.dev";
|
|
11123
11121
|
const details = extractDetails(sourceFile);
|
|
@@ -11130,39 +11128,50 @@ async function generateCRDs(options) {
|
|
|
11130
11128
|
logger_default.warn("This feature is currently in alpha.\n");
|
|
11131
11129
|
const outputDir = import_path8.default.resolve(options.output);
|
|
11132
11130
|
await createDirectoryIfNotExists(outputDir);
|
|
11133
|
-
const project = new import_ts_morph.Project();
|
|
11134
11131
|
const apiRoot = import_path8.default.resolve("api");
|
|
11135
11132
|
const versions = getAPIVersions(apiRoot);
|
|
11136
11133
|
for (const version3 of versions) {
|
|
11137
|
-
const
|
|
11138
|
-
|
|
11139
|
-
|
|
11134
|
+
const versionDir = import_path8.default.join(apiRoot, version3);
|
|
11135
|
+
const filePaths = loadVersionFilePaths(versionDir);
|
|
11136
|
+
const program2 = createProgram(filePaths);
|
|
11137
|
+
const checker = program2.getTypeChecker();
|
|
11138
|
+
for (const filePath of filePaths) {
|
|
11139
|
+
const sourceFile = program2.getSourceFile(filePath);
|
|
11140
|
+
if (sourceFile) {
|
|
11141
|
+
processSourceFile(sourceFile, checker, version3, outputDir);
|
|
11142
|
+
}
|
|
11140
11143
|
}
|
|
11141
11144
|
}
|
|
11142
11145
|
}
|
|
11143
11146
|
function getAPIVersions(apiRoot) {
|
|
11144
11147
|
return import_fs14.default.readdirSync(apiRoot).filter((v) => import_fs14.default.statSync(import_path8.default.join(apiRoot, v)).isDirectory());
|
|
11145
11148
|
}
|
|
11146
|
-
function
|
|
11149
|
+
function loadVersionFilePaths(versionDir) {
|
|
11147
11150
|
const files = import_fs14.default.readdirSync(versionDir).filter((f) => f.endsWith(".ts"));
|
|
11148
|
-
|
|
11149
|
-
return project.addSourceFilesAtPaths(filePaths);
|
|
11151
|
+
return files.map((f) => import_path8.default.join(versionDir, f));
|
|
11150
11152
|
}
|
|
11151
|
-
function
|
|
11153
|
+
function createProgram(filePaths) {
|
|
11154
|
+
return import_typescript.default.createProgram(filePaths, {
|
|
11155
|
+
target: import_typescript.default.ScriptTarget.ESNext,
|
|
11156
|
+
module: import_typescript.default.ModuleKind.ESNext,
|
|
11157
|
+
strict: true
|
|
11158
|
+
});
|
|
11159
|
+
}
|
|
11160
|
+
function processSourceFile(sourceFile, checker, version3, outputDir) {
|
|
11152
11161
|
const content = sourceFile.getFullText();
|
|
11153
11162
|
const { kind: kind8, fqdn, scope, plural: plural2, shortNames } = extractCRDDetails(content, sourceFile);
|
|
11154
11163
|
if (!kind8) {
|
|
11155
|
-
logger_default.warn(WarningMessages.MISSING_KIND_COMMENT(sourceFile.
|
|
11164
|
+
logger_default.warn(WarningMessages.MISSING_KIND_COMMENT(import_path8.default.basename(sourceFile.fileName)));
|
|
11156
11165
|
return;
|
|
11157
11166
|
}
|
|
11158
|
-
const spec = sourceFile
|
|
11167
|
+
const spec = findInterface(sourceFile, `${kind8}Spec`);
|
|
11159
11168
|
if (!spec) {
|
|
11160
|
-
logger_default.warn(WarningMessages.MISSING_INTERFACE(sourceFile.
|
|
11169
|
+
logger_default.warn(WarningMessages.MISSING_INTERFACE(import_path8.default.basename(sourceFile.fileName), kind8));
|
|
11161
11170
|
return;
|
|
11162
11171
|
}
|
|
11163
|
-
const condition = sourceFile
|
|
11164
|
-
const specSchema = getSchemaFromType(spec);
|
|
11165
|
-
const conditionSchema = condition ? getSchemaFromType(condition) : emptySchema();
|
|
11172
|
+
const condition = findTypeAlias(sourceFile, `${kind8}StatusCondition`);
|
|
11173
|
+
const specSchema = getSchemaFromType(spec, checker);
|
|
11174
|
+
const conditionSchema = condition ? getSchemaFromType(condition, checker) : emptySchema();
|
|
11166
11175
|
const crd = buildCRD({
|
|
11167
11176
|
kind: kind8,
|
|
11168
11177
|
fqdn,
|
|
@@ -11182,18 +11191,31 @@ function extractSingleLineComment(content, label) {
|
|
|
11182
11191
|
return match?.[1].trim();
|
|
11183
11192
|
}
|
|
11184
11193
|
function extractDetails(sourceFile) {
|
|
11185
|
-
|
|
11186
|
-
|
|
11194
|
+
let detailsDecl;
|
|
11195
|
+
import_typescript.default.forEachChild(sourceFile, (node) => {
|
|
11196
|
+
if (import_typescript.default.isVariableStatement(node)) {
|
|
11197
|
+
for (const decl of node.declarationList.declarations) {
|
|
11198
|
+
if (import_typescript.default.isIdentifier(decl.name) && decl.name.text === "details") {
|
|
11199
|
+
detailsDecl = decl;
|
|
11200
|
+
}
|
|
11201
|
+
}
|
|
11202
|
+
}
|
|
11203
|
+
});
|
|
11204
|
+
if (!detailsDecl) {
|
|
11205
|
+
throw new Error(ErrorMessages.MISSING_DETAILS);
|
|
11206
|
+
}
|
|
11207
|
+
const init = detailsDecl.initializer;
|
|
11208
|
+
if (!init || !import_typescript.default.isObjectLiteralExpression(init)) {
|
|
11187
11209
|
throw new Error(ErrorMessages.MISSING_DETAILS);
|
|
11188
11210
|
}
|
|
11189
|
-
const init = decl.getInitializerIfKindOrThrow(import_ts_morph.SyntaxKind.ObjectLiteralExpression);
|
|
11190
11211
|
const getStr = (key) => {
|
|
11191
|
-
const
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11212
|
+
const match = init.properties.find(
|
|
11213
|
+
(prop) => import_typescript.default.isPropertyAssignment(prop) && import_typescript.default.isIdentifier(prop.name) && prop.name.text === key
|
|
11214
|
+
);
|
|
11215
|
+
if (match && import_typescript.default.isStringLiteral(match.initializer) && match.initializer.text) {
|
|
11216
|
+
return match.initializer.text;
|
|
11195
11217
|
}
|
|
11196
|
-
|
|
11218
|
+
throw new Error(ErrorMessages.MISSING_OR_INVALID_KEY(key));
|
|
11197
11219
|
};
|
|
11198
11220
|
const scope = getStr("scope");
|
|
11199
11221
|
if (scope === "Cluster" || scope === "Namespaced") {
|
|
@@ -11205,62 +11227,104 @@ function extractDetails(sourceFile) {
|
|
|
11205
11227
|
}
|
|
11206
11228
|
throw new Error(ErrorMessages.INVALID_SCOPE(scope));
|
|
11207
11229
|
}
|
|
11208
|
-
function
|
|
11209
|
-
|
|
11210
|
-
|
|
11230
|
+
function findInterface(sourceFile, name2) {
|
|
11231
|
+
let result;
|
|
11232
|
+
import_typescript.default.forEachChild(sourceFile, (node) => {
|
|
11233
|
+
if (import_typescript.default.isInterfaceDeclaration(node) && node.name.text === name2) {
|
|
11234
|
+
result = node;
|
|
11235
|
+
}
|
|
11236
|
+
});
|
|
11237
|
+
return result;
|
|
11211
11238
|
}
|
|
11212
|
-
function
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11239
|
+
function findTypeAlias(sourceFile, name2) {
|
|
11240
|
+
let result;
|
|
11241
|
+
import_typescript.default.forEachChild(sourceFile, (node) => {
|
|
11242
|
+
if (import_typescript.default.isTypeAliasDeclaration(node) && node.name.text === name2) {
|
|
11243
|
+
result = node;
|
|
11244
|
+
}
|
|
11245
|
+
});
|
|
11246
|
+
return result;
|
|
11247
|
+
}
|
|
11248
|
+
function getJsDocDescription(node) {
|
|
11249
|
+
const jsDocs = import_typescript.default.getJSDocCommentsAndTags(node);
|
|
11250
|
+
const comments = [];
|
|
11251
|
+
for (const doc of jsDocs) {
|
|
11252
|
+
if (import_typescript.default.isJSDoc(doc) && doc.comment) {
|
|
11253
|
+
if (typeof doc.comment === "string") {
|
|
11254
|
+
comments.push(doc.comment);
|
|
11255
|
+
} else {
|
|
11256
|
+
comments.push(doc.comment.map((part) => part.text).join(""));
|
|
11257
|
+
}
|
|
11258
|
+
}
|
|
11228
11259
|
}
|
|
11260
|
+
return comments.join(" ").trim();
|
|
11261
|
+
}
|
|
11262
|
+
function getSchemaFromType(decl, checker) {
|
|
11263
|
+
const type = checker.getTypeAtLocation(decl);
|
|
11264
|
+
const { properties, required } = extractObjectProperties(type, checker);
|
|
11229
11265
|
return { properties, required };
|
|
11230
11266
|
}
|
|
11231
|
-
function
|
|
11232
|
-
|
|
11233
|
-
if (
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
if (
|
|
11237
|
-
return {
|
|
11238
|
-
type: "array",
|
|
11239
|
-
items: mapTypeToSchema(type.getArrayElementTypeOrThrow())
|
|
11240
|
-
};
|
|
11267
|
+
function getPrimitiveSchemaType(type) {
|
|
11268
|
+
const { flags } = type;
|
|
11269
|
+
if (flags & import_typescript.default.TypeFlags.String || flags & import_typescript.default.TypeFlags.StringLiteral) {
|
|
11270
|
+
return { type: "string" };
|
|
11271
|
+
}
|
|
11272
|
+
if (flags & import_typescript.default.TypeFlags.Number || flags & import_typescript.default.TypeFlags.NumberLiteral) {
|
|
11273
|
+
return { type: "number" };
|
|
11241
11274
|
}
|
|
11242
|
-
if (
|
|
11275
|
+
if (flags & import_typescript.default.TypeFlags.Boolean || flags & import_typescript.default.TypeFlags.BooleanLiteral || flags & import_typescript.default.TypeFlags.BooleanLike) {
|
|
11276
|
+
return { type: "boolean" };
|
|
11277
|
+
}
|
|
11278
|
+
return void 0;
|
|
11279
|
+
}
|
|
11280
|
+
function unwrapOptionalType(type) {
|
|
11281
|
+
if (type.isUnion()) {
|
|
11282
|
+
const filtered = type.types.filter(
|
|
11283
|
+
(t) => !(t.flags & (import_typescript.default.TypeFlags.Undefined | import_typescript.default.TypeFlags.Null))
|
|
11284
|
+
);
|
|
11285
|
+
if (filtered.length === 1) return filtered[0];
|
|
11286
|
+
}
|
|
11287
|
+
return type;
|
|
11288
|
+
}
|
|
11289
|
+
function mapTypeToSchema(type, checker) {
|
|
11290
|
+
const resolved = unwrapOptionalType(type);
|
|
11291
|
+
if (checker.typeToString(resolved) === "Date") return { type: "string", format: "date-time" };
|
|
11292
|
+
const primitive = getPrimitiveSchemaType(resolved);
|
|
11293
|
+
if (primitive) return primitive;
|
|
11294
|
+
if (checker.isArrayType(resolved)) {
|
|
11295
|
+
const typeArgs = checker.getTypeArguments(resolved);
|
|
11296
|
+
if (typeArgs && typeArgs.length > 0) {
|
|
11297
|
+
return { type: "array", items: mapTypeToSchema(typeArgs[0], checker) };
|
|
11298
|
+
}
|
|
11299
|
+
return { type: "array" };
|
|
11300
|
+
}
|
|
11301
|
+
if (resolved.flags & import_typescript.default.TypeFlags.Object) return buildObjectSchema(resolved, checker);
|
|
11302
|
+
logger_default.warn(`Unsupported type "${checker.typeToString(resolved)}", defaulting to string`);
|
|
11243
11303
|
return { type: "string" };
|
|
11244
11304
|
}
|
|
11245
|
-
function
|
|
11246
|
-
const
|
|
11305
|
+
function extractObjectProperties(type, checker) {
|
|
11306
|
+
const properties = {};
|
|
11247
11307
|
const required = [];
|
|
11248
11308
|
for (const prop of type.getProperties()) {
|
|
11249
11309
|
const name2 = uncapitalize(prop.getName());
|
|
11250
11310
|
const declarations = prop.getDeclarations();
|
|
11251
|
-
if (!declarations.length) continue;
|
|
11311
|
+
if (!declarations || !declarations.length) continue;
|
|
11252
11312
|
const decl = declarations[0];
|
|
11253
11313
|
const description = getJsDocDescription(decl);
|
|
11254
|
-
const subType =
|
|
11255
|
-
|
|
11256
|
-
...mapTypeToSchema(subType),
|
|
11314
|
+
const subType = checker.getTypeOfSymbolAtLocation(prop, decl);
|
|
11315
|
+
properties[name2] = {
|
|
11316
|
+
...mapTypeToSchema(subType, checker),
|
|
11257
11317
|
...description ? { description } : {}
|
|
11258
11318
|
};
|
|
11259
|
-
if (!prop.
|
|
11319
|
+
if (!(prop.flags & import_typescript.default.SymbolFlags.Optional)) required.push(name2);
|
|
11260
11320
|
}
|
|
11321
|
+
return { properties, required };
|
|
11322
|
+
}
|
|
11323
|
+
function buildObjectSchema(type, checker) {
|
|
11324
|
+
const { properties, required } = extractObjectProperties(type, checker);
|
|
11261
11325
|
return {
|
|
11262
11326
|
type: "object",
|
|
11263
|
-
properties
|
|
11327
|
+
properties,
|
|
11264
11328
|
...required.length > 0 ? { required } : {}
|
|
11265
11329
|
};
|
|
11266
11330
|
}
|
package/dist/controller.js
CHANGED
|
@@ -78,7 +78,7 @@ var packageJSON = {
|
|
|
78
78
|
"!src/fixtures/**",
|
|
79
79
|
"!dist/**/*.test.d.ts*"
|
|
80
80
|
],
|
|
81
|
-
version: "1.2.
|
|
81
|
+
version: "1.2.1",
|
|
82
82
|
main: "dist/lib.js",
|
|
83
83
|
types: "dist/lib.d.ts",
|
|
84
84
|
scripts: {
|
|
@@ -112,8 +112,8 @@ var packageJSON = {
|
|
|
112
112
|
},
|
|
113
113
|
dependencies: {
|
|
114
114
|
"@types/ramda": "0.31.1",
|
|
115
|
-
"@typescript-eslint/eslint-plugin": "8.59.
|
|
116
|
-
"@typescript-eslint/parser": "8.59.
|
|
115
|
+
"@typescript-eslint/eslint-plugin": "8.59.4",
|
|
116
|
+
"@typescript-eslint/parser": "8.59.4",
|
|
117
117
|
commander: "14.0.3",
|
|
118
118
|
eslint: "9.39.4",
|
|
119
119
|
express: "5.2.1",
|
|
@@ -125,16 +125,11 @@ var packageJSON = {
|
|
|
125
125
|
"pino-pretty": "13.1.3",
|
|
126
126
|
"prom-client": "15.1.3",
|
|
127
127
|
"quicktype-core": "^23.2.6",
|
|
128
|
-
ramda: "0.32.0"
|
|
129
|
-
"ts-morph": "^27.0.2"
|
|
128
|
+
ramda: "0.32.0"
|
|
130
129
|
},
|
|
131
130
|
devDependencies: {
|
|
132
|
-
"@commitlint/cli": "
|
|
133
|
-
"@commitlint/config-conventional": "
|
|
134
|
-
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
135
|
-
"@semantic-release/git": "^10.0.1",
|
|
136
|
-
"@semantic-release/npm": "^13.0.0",
|
|
137
|
-
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
131
|
+
"@commitlint/cli": "21.0.1",
|
|
132
|
+
"@commitlint/config-conventional": "21.0.1",
|
|
138
133
|
"@types/command-line-args": "^5.2.3",
|
|
139
134
|
"@types/express": "5.0.6",
|
|
140
135
|
"@types/json-pointer": "^1.0.34",
|
|
@@ -160,13 +155,13 @@ var packageJSON = {
|
|
|
160
155
|
rollup: "4.59.0"
|
|
161
156
|
},
|
|
162
157
|
peerDependencies: {
|
|
163
|
-
"@types/prompts": "2.4.9",
|
|
164
|
-
esbuild: "0.
|
|
165
|
-
"node-forge": "1.4.0",
|
|
166
|
-
prettier: "3.6.2",
|
|
167
|
-
prompts: "2.4.2",
|
|
168
|
-
typescript: "5.8.3",
|
|
169
|
-
uuid: "13.0.0"
|
|
158
|
+
"@types/prompts": "^2.4.9",
|
|
159
|
+
esbuild: "^0.28.0",
|
|
160
|
+
"node-forge": "^1.4.0",
|
|
161
|
+
prettier: "^3.6.2",
|
|
162
|
+
prompts: "^2.4.2",
|
|
163
|
+
typescript: "^5.8.3",
|
|
164
|
+
uuid: "^13.0.0"
|
|
170
165
|
}
|
|
171
166
|
};
|
|
172
167
|
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"!src/fixtures/**",
|
|
17
17
|
"!dist/**/*.test.d.ts*"
|
|
18
18
|
],
|
|
19
|
-
"version": "1.2.
|
|
19
|
+
"version": "1.2.1",
|
|
20
20
|
"main": "dist/lib.js",
|
|
21
21
|
"types": "dist/lib.d.ts",
|
|
22
22
|
"scripts": {
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@types/ramda": "0.31.1",
|
|
53
|
-
"@typescript-eslint/eslint-plugin": "8.59.
|
|
54
|
-
"@typescript-eslint/parser": "8.59.
|
|
53
|
+
"@typescript-eslint/eslint-plugin": "8.59.4",
|
|
54
|
+
"@typescript-eslint/parser": "8.59.4",
|
|
55
55
|
"commander": "14.0.3",
|
|
56
56
|
"eslint": "9.39.4",
|
|
57
57
|
"express": "5.2.1",
|
|
@@ -63,16 +63,11 @@
|
|
|
63
63
|
"pino-pretty": "13.1.3",
|
|
64
64
|
"prom-client": "15.1.3",
|
|
65
65
|
"quicktype-core": "^23.2.6",
|
|
66
|
-
"ramda": "0.32.0"
|
|
67
|
-
"ts-morph": "^27.0.2"
|
|
66
|
+
"ramda": "0.32.0"
|
|
68
67
|
},
|
|
69
68
|
"devDependencies": {
|
|
70
|
-
"@commitlint/cli": "
|
|
71
|
-
"@commitlint/config-conventional": "
|
|
72
|
-
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
73
|
-
"@semantic-release/git": "^10.0.1",
|
|
74
|
-
"@semantic-release/npm": "^13.0.0",
|
|
75
|
-
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
69
|
+
"@commitlint/cli": "21.0.1",
|
|
70
|
+
"@commitlint/config-conventional": "21.0.1",
|
|
76
71
|
"@types/command-line-args": "^5.2.3",
|
|
77
72
|
"@types/express": "5.0.6",
|
|
78
73
|
"@types/json-pointer": "^1.0.34",
|
|
@@ -98,12 +93,12 @@
|
|
|
98
93
|
"rollup": "4.59.0"
|
|
99
94
|
},
|
|
100
95
|
"peerDependencies": {
|
|
101
|
-
"@types/prompts": "2.4.9",
|
|
102
|
-
"esbuild": "0.
|
|
103
|
-
"node-forge": "1.4.0",
|
|
104
|
-
"prettier": "3.6.2",
|
|
105
|
-
"prompts": "2.4.2",
|
|
106
|
-
"typescript": "5.8.3",
|
|
107
|
-
"uuid": "13.0.0"
|
|
96
|
+
"@types/prompts": "^2.4.9",
|
|
97
|
+
"esbuild": "^0.28.0",
|
|
98
|
+
"node-forge": "^1.4.0",
|
|
99
|
+
"prettier": "^3.6.2",
|
|
100
|
+
"prompts": "^2.4.2",
|
|
101
|
+
"typescript": "^5.8.3",
|
|
102
|
+
"uuid": "^13.0.0"
|
|
108
103
|
}
|
|
109
104
|
}
|
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
+
import ts from "typescript";
|
|
3
4
|
import Log from "../../../lib/telemetry/logger";
|
|
4
5
|
import { stringify } from "yaml";
|
|
5
|
-
import {
|
|
6
|
-
Project,
|
|
7
|
-
InterfaceDeclaration,
|
|
8
|
-
TypeAliasDeclaration,
|
|
9
|
-
SyntaxKind,
|
|
10
|
-
Node,
|
|
11
|
-
SourceFile,
|
|
12
|
-
Type,
|
|
13
|
-
} from "ts-morph";
|
|
14
6
|
import { createDirectoryIfNotExists } from "../../../lib/filesystemService";
|
|
15
7
|
import { kind as k } from "kubernetes-fluent-client";
|
|
16
8
|
import { V1JSONSchemaProps } from "@kubernetes/client-node";
|
|
@@ -18,7 +10,7 @@ import { WarningMessages, ErrorMessages } from "./messages";
|
|
|
18
10
|
|
|
19
11
|
function extractCRDDetails(
|
|
20
12
|
content: string,
|
|
21
|
-
sourceFile: SourceFile,
|
|
13
|
+
sourceFile: ts.SourceFile,
|
|
22
14
|
): {
|
|
23
15
|
kind: string | undefined;
|
|
24
16
|
fqdn: string;
|
|
@@ -27,6 +19,10 @@ function extractCRDDetails(
|
|
|
27
19
|
shortNames?: string[];
|
|
28
20
|
} {
|
|
29
21
|
const kind = extractSingleLineComment(content, "Kind");
|
|
22
|
+
if (!kind) {
|
|
23
|
+
return { kind: undefined, fqdn: "", scope: "Namespaced", plural: "", shortNames: undefined };
|
|
24
|
+
}
|
|
25
|
+
|
|
30
26
|
const group = extractSingleLineComment(content, "Group") ?? "example";
|
|
31
27
|
const domain = extractSingleLineComment(content, "Domain") ?? "pepr.dev";
|
|
32
28
|
const details = extractDetails(sourceFile);
|
|
@@ -44,14 +40,20 @@ export async function generateCRDs(options: { output: string }): Promise<void> {
|
|
|
44
40
|
const outputDir = path.resolve(options.output);
|
|
45
41
|
await createDirectoryIfNotExists(outputDir);
|
|
46
42
|
|
|
47
|
-
const project = new Project();
|
|
48
43
|
const apiRoot = path.resolve("api");
|
|
49
44
|
const versions = getAPIVersions(apiRoot);
|
|
50
45
|
|
|
51
46
|
for (const version of versions) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
const versionDir = path.join(apiRoot, version);
|
|
48
|
+
const filePaths = loadVersionFilePaths(versionDir);
|
|
49
|
+
const program = createProgram(filePaths);
|
|
50
|
+
const checker = program.getTypeChecker();
|
|
51
|
+
|
|
52
|
+
for (const filePath of filePaths) {
|
|
53
|
+
const sourceFile = program.getSourceFile(filePath);
|
|
54
|
+
if (sourceFile) {
|
|
55
|
+
processSourceFile(sourceFile, checker, version, outputDir);
|
|
56
|
+
}
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
}
|
|
@@ -60,14 +62,22 @@ export function getAPIVersions(apiRoot: string): string[] {
|
|
|
60
62
|
return fs.readdirSync(apiRoot).filter(v => fs.statSync(path.join(apiRoot, v)).isDirectory());
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
export function
|
|
65
|
+
export function loadVersionFilePaths(versionDir: string): string[] {
|
|
64
66
|
const files = fs.readdirSync(versionDir).filter(f => f.endsWith(".ts"));
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
return files.map(f => path.join(versionDir, f));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function createProgram(filePaths: string[]): ts.Program {
|
|
71
|
+
return ts.createProgram(filePaths, {
|
|
72
|
+
target: ts.ScriptTarget.ESNext,
|
|
73
|
+
module: ts.ModuleKind.ESNext,
|
|
74
|
+
strict: true,
|
|
75
|
+
});
|
|
67
76
|
}
|
|
68
77
|
|
|
69
78
|
export function processSourceFile(
|
|
70
|
-
sourceFile: SourceFile,
|
|
79
|
+
sourceFile: ts.SourceFile,
|
|
80
|
+
checker: ts.TypeChecker,
|
|
71
81
|
version: string,
|
|
72
82
|
outputDir: string,
|
|
73
83
|
): void {
|
|
@@ -75,19 +85,19 @@ export function processSourceFile(
|
|
|
75
85
|
const { kind, fqdn, scope, plural, shortNames } = extractCRDDetails(content, sourceFile);
|
|
76
86
|
|
|
77
87
|
if (!kind) {
|
|
78
|
-
Log.warn(WarningMessages.MISSING_KIND_COMMENT(sourceFile.
|
|
88
|
+
Log.warn(WarningMessages.MISSING_KIND_COMMENT(path.basename(sourceFile.fileName)));
|
|
79
89
|
return;
|
|
80
90
|
}
|
|
81
91
|
|
|
82
|
-
const spec = sourceFile
|
|
92
|
+
const spec = findInterface(sourceFile, `${kind}Spec`);
|
|
83
93
|
if (!spec) {
|
|
84
|
-
Log.warn(WarningMessages.MISSING_INTERFACE(sourceFile.
|
|
94
|
+
Log.warn(WarningMessages.MISSING_INTERFACE(path.basename(sourceFile.fileName), kind));
|
|
85
95
|
return;
|
|
86
96
|
}
|
|
87
97
|
|
|
88
|
-
const condition = sourceFile
|
|
89
|
-
const specSchema = getSchemaFromType(spec);
|
|
90
|
-
const conditionSchema = condition ? getSchemaFromType(condition) : emptySchema();
|
|
98
|
+
const condition = findTypeAlias(sourceFile, `${kind}StatusCondition`);
|
|
99
|
+
const specSchema = getSchemaFromType(spec, checker);
|
|
100
|
+
const conditionSchema = condition ? getSchemaFromType(condition, checker) : emptySchema();
|
|
91
101
|
|
|
92
102
|
const crd = buildCRD({
|
|
93
103
|
kind,
|
|
@@ -112,25 +122,41 @@ export function extractSingleLineComment(content: string, label: string): string
|
|
|
112
122
|
return match?.[1].trim();
|
|
113
123
|
}
|
|
114
124
|
|
|
115
|
-
export function extractDetails(sourceFile: SourceFile): {
|
|
125
|
+
export function extractDetails(sourceFile: ts.SourceFile): {
|
|
116
126
|
plural: string;
|
|
117
127
|
scope: "Cluster" | "Namespaced";
|
|
118
128
|
shortName: string;
|
|
119
129
|
} {
|
|
120
|
-
|
|
121
|
-
|
|
130
|
+
let detailsDecl: ts.VariableDeclaration | undefined;
|
|
131
|
+
ts.forEachChild(sourceFile, node => {
|
|
132
|
+
if (ts.isVariableStatement(node)) {
|
|
133
|
+
for (const decl of node.declarationList.declarations) {
|
|
134
|
+
if (ts.isIdentifier(decl.name) && decl.name.text === "details") {
|
|
135
|
+
detailsDecl = decl;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (!detailsDecl) {
|
|
122
142
|
throw new Error(ErrorMessages.MISSING_DETAILS);
|
|
123
143
|
}
|
|
124
144
|
|
|
125
|
-
const init =
|
|
145
|
+
const init = detailsDecl.initializer;
|
|
146
|
+
if (!init || !ts.isObjectLiteralExpression(init)) {
|
|
147
|
+
throw new Error(ErrorMessages.MISSING_DETAILS);
|
|
148
|
+
}
|
|
126
149
|
|
|
127
150
|
const getStr = (key: string): string => {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
151
|
+
const match = init.properties.find(
|
|
152
|
+
(prop): prop is ts.PropertyAssignment =>
|
|
153
|
+
ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === key,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
if (match && ts.isStringLiteral(match.initializer) && match.initializer.text) {
|
|
157
|
+
return match.initializer.text;
|
|
132
158
|
}
|
|
133
|
-
|
|
159
|
+
throw new Error(ErrorMessages.MISSING_OR_INVALID_KEY(key));
|
|
134
160
|
};
|
|
135
161
|
|
|
136
162
|
const scope = getStr("scope");
|
|
@@ -145,84 +171,140 @@ export function extractDetails(sourceFile: SourceFile): {
|
|
|
145
171
|
throw new Error(ErrorMessages.INVALID_SCOPE(scope));
|
|
146
172
|
}
|
|
147
173
|
|
|
148
|
-
function
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
.
|
|
155
|
-
|
|
174
|
+
function findInterface(
|
|
175
|
+
sourceFile: ts.SourceFile,
|
|
176
|
+
name: string,
|
|
177
|
+
): ts.InterfaceDeclaration | undefined {
|
|
178
|
+
let result: ts.InterfaceDeclaration | undefined;
|
|
179
|
+
ts.forEachChild(sourceFile, node => {
|
|
180
|
+
if (ts.isInterfaceDeclaration(node) && node.name.text === name) {
|
|
181
|
+
result = node;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function findTypeAlias(
|
|
188
|
+
sourceFile: ts.SourceFile,
|
|
189
|
+
name: string,
|
|
190
|
+
): ts.TypeAliasDeclaration | undefined {
|
|
191
|
+
let result: ts.TypeAliasDeclaration | undefined;
|
|
192
|
+
ts.forEachChild(sourceFile, node => {
|
|
193
|
+
if (ts.isTypeAliasDeclaration(node) && node.name.text === name) {
|
|
194
|
+
result = node;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getJsDocDescription(node: ts.Node): string {
|
|
201
|
+
const jsDocs = ts.getJSDocCommentsAndTags(node);
|
|
202
|
+
const comments: string[] = [];
|
|
203
|
+
for (const doc of jsDocs) {
|
|
204
|
+
if (ts.isJSDoc(doc) && doc.comment) {
|
|
205
|
+
if (typeof doc.comment === "string") {
|
|
206
|
+
comments.push(doc.comment);
|
|
207
|
+
} else {
|
|
208
|
+
comments.push(doc.comment.map(part => part.text).join(""));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return comments.join(" ").trim();
|
|
156
213
|
}
|
|
157
214
|
|
|
158
|
-
function getSchemaFromType(
|
|
215
|
+
function getSchemaFromType(
|
|
216
|
+
decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration,
|
|
217
|
+
checker: ts.TypeChecker,
|
|
218
|
+
): {
|
|
159
219
|
properties: Record<string, V1JSONSchemaProps>;
|
|
160
220
|
required: string[];
|
|
161
221
|
} {
|
|
162
|
-
const type =
|
|
163
|
-
const properties
|
|
164
|
-
|
|
222
|
+
const type = checker.getTypeAtLocation(decl);
|
|
223
|
+
const { properties, required } = extractObjectProperties(type, checker);
|
|
224
|
+
return { properties, required };
|
|
225
|
+
}
|
|
165
226
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
227
|
+
function getPrimitiveSchemaType(type: ts.Type): V1JSONSchemaProps | undefined {
|
|
228
|
+
const { flags } = type;
|
|
229
|
+
if (flags & ts.TypeFlags.String || flags & ts.TypeFlags.StringLiteral) {
|
|
230
|
+
return { type: "string" };
|
|
231
|
+
}
|
|
232
|
+
if (flags & ts.TypeFlags.Number || flags & ts.TypeFlags.NumberLiteral) {
|
|
233
|
+
return { type: "number" };
|
|
234
|
+
}
|
|
235
|
+
if (
|
|
236
|
+
flags & ts.TypeFlags.Boolean ||
|
|
237
|
+
flags & ts.TypeFlags.BooleanLiteral ||
|
|
238
|
+
flags & ts.TypeFlags.BooleanLike
|
|
239
|
+
) {
|
|
240
|
+
return { type: "boolean" };
|
|
241
|
+
}
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
170
244
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const
|
|
245
|
+
function unwrapOptionalType(type: ts.Type): ts.Type {
|
|
246
|
+
if (type.isUnion()) {
|
|
247
|
+
const filtered = type.types.filter(
|
|
248
|
+
t => !(t.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null)),
|
|
249
|
+
);
|
|
250
|
+
if (filtered.length === 1) return filtered[0];
|
|
251
|
+
}
|
|
252
|
+
return type;
|
|
253
|
+
}
|
|
174
254
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
...(description ? { description } : {}),
|
|
178
|
-
};
|
|
255
|
+
function mapTypeToSchema(type: ts.Type, checker: ts.TypeChecker): V1JSONSchemaProps {
|
|
256
|
+
const resolved = unwrapOptionalType(type);
|
|
179
257
|
|
|
180
|
-
|
|
181
|
-
}
|
|
258
|
+
if (checker.typeToString(resolved) === "Date") return { type: "string", format: "date-time" };
|
|
182
259
|
|
|
183
|
-
|
|
184
|
-
|
|
260
|
+
const primitive = getPrimitiveSchemaType(resolved);
|
|
261
|
+
if (primitive) return primitive;
|
|
185
262
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
type: "array",
|
|
194
|
-
items: mapTypeToSchema(type.getArrayElementTypeOrThrow()),
|
|
195
|
-
};
|
|
263
|
+
if (checker.isArrayType(resolved)) {
|
|
264
|
+
const typeArgs = checker.getTypeArguments(resolved as ts.TypeReference);
|
|
265
|
+
if (typeArgs && typeArgs.length > 0) {
|
|
266
|
+
return { type: "array", items: mapTypeToSchema(typeArgs[0], checker) };
|
|
267
|
+
}
|
|
268
|
+
return { type: "array" };
|
|
196
269
|
}
|
|
197
270
|
|
|
198
|
-
if (
|
|
271
|
+
if (resolved.flags & ts.TypeFlags.Object) return buildObjectSchema(resolved, checker);
|
|
272
|
+
Log.warn(`Unsupported type "${checker.typeToString(resolved)}", defaulting to string`);
|
|
199
273
|
return { type: "string" };
|
|
200
274
|
}
|
|
201
275
|
|
|
202
|
-
function
|
|
203
|
-
|
|
276
|
+
function extractObjectProperties(
|
|
277
|
+
type: ts.Type,
|
|
278
|
+
checker: ts.TypeChecker,
|
|
279
|
+
): { properties: Record<string, V1JSONSchemaProps>; required: string[] } {
|
|
280
|
+
const properties: Record<string, V1JSONSchemaProps> = {};
|
|
204
281
|
const required: string[] = [];
|
|
205
282
|
|
|
206
283
|
for (const prop of type.getProperties()) {
|
|
207
284
|
const name = uncapitalize(prop.getName());
|
|
208
285
|
const declarations = prop.getDeclarations();
|
|
209
|
-
if (!declarations.length) continue;
|
|
286
|
+
if (!declarations || !declarations.length) continue;
|
|
210
287
|
|
|
211
288
|
const decl = declarations[0];
|
|
212
289
|
const description = getJsDocDescription(decl);
|
|
213
|
-
const subType =
|
|
290
|
+
const subType = checker.getTypeOfSymbolAtLocation(prop, decl);
|
|
214
291
|
|
|
215
|
-
|
|
216
|
-
...mapTypeToSchema(subType),
|
|
292
|
+
properties[name] = {
|
|
293
|
+
...mapTypeToSchema(subType, checker),
|
|
217
294
|
...(description ? { description } : {}),
|
|
218
295
|
};
|
|
219
296
|
|
|
220
|
-
if (!prop.
|
|
297
|
+
if (!(prop.flags & ts.SymbolFlags.Optional)) required.push(name);
|
|
221
298
|
}
|
|
222
299
|
|
|
300
|
+
return { properties, required };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function buildObjectSchema(type: ts.Type, checker: ts.TypeChecker): V1JSONSchemaProps {
|
|
304
|
+
const { properties, required } = extractObjectProperties(type, checker);
|
|
223
305
|
return {
|
|
224
306
|
type: "object",
|
|
225
|
-
properties
|
|
307
|
+
properties,
|
|
226
308
|
...(required.length > 0 ? { required } : {}),
|
|
227
309
|
};
|
|
228
310
|
}
|