feat(cli): new command to generate RT namespace + on init (#7317)
This commit is contained in:
@@ -1,5 +1,17 @@
|
||||
import type { ScriptLang } from "../gen/types.gen.ts";
|
||||
|
||||
export type EnumType = string[] | undefined;
|
||||
|
||||
export type Schema = {
|
||||
$schema: string | undefined;
|
||||
type: string;
|
||||
"x-windmill-dyn-select-code"?: string;
|
||||
"x-windmill-dyn-select-lang"?: ScriptLang;
|
||||
properties: { [name: string]: SchemaProperty };
|
||||
order?: string[];
|
||||
required: string[];
|
||||
};
|
||||
|
||||
export interface SchemaProperty {
|
||||
type: string | undefined;
|
||||
description?: string;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { readLockfile } from "../../utils/metadata.ts";
|
||||
import { SCRIPT_GUIDANCE } from "../../guidance/script_guidance.ts";
|
||||
import { FLOW_GUIDANCE } from "../../guidance/flow_guidance.ts";
|
||||
import { getActiveWorkspaceOrFallback } from "../workspace/workspace.ts";
|
||||
import { generateRTNamespace } from "../resource-type/resource-type.ts";
|
||||
|
||||
export interface InitOptions {
|
||||
useDefault?: boolean;
|
||||
@@ -280,6 +281,17 @@ ${flowGuidanceContent}
|
||||
log.warn(`Could not create guidance files: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate resource type namespace
|
||||
try {
|
||||
await generateRTNamespace(opts as GlobalOptions);
|
||||
} catch (error) {
|
||||
log.warn(
|
||||
`Could not pull resource types and generate TypeScript namespace: ${
|
||||
error instanceof Error ? error.message : error
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const command = new Command()
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
|
||||
import { writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
|
||||
import {
|
||||
GlobalOptions,
|
||||
isSuperset,
|
||||
@@ -10,6 +15,8 @@ import { resolveWorkspace } from "../../core/context.ts";
|
||||
import { colors, Command, log, Table } from "../../../deps.ts";
|
||||
import * as wmill from "../../../gen/services.gen.ts";
|
||||
import { ResourceType } from "../../../gen/types.gen.ts";
|
||||
import { compileResourceTypeToTsType } from "../../utils/resource_types.ts";
|
||||
import { capitalize, toCamel } from "../../utils/utils.ts";
|
||||
|
||||
export interface ResourceTypeFile {
|
||||
schema?: any;
|
||||
@@ -88,7 +95,13 @@ async function list(opts: GlobalOptions & { schema?: boolean }) {
|
||||
.header(["Workspace", "Name", "Schema"])
|
||||
.padding(2)
|
||||
.border(true)
|
||||
.body(res.map((x) => [x.workspace_id ?? "Global", x.name, JSON.stringify(x.schema, null, 2)]))
|
||||
.body(
|
||||
res.map((x) => [
|
||||
x.workspace_id ?? "Global",
|
||||
x.name,
|
||||
JSON.stringify(x.schema, null, 2),
|
||||
])
|
||||
)
|
||||
.render();
|
||||
} else {
|
||||
new Table()
|
||||
@@ -100,11 +113,38 @@ async function list(opts: GlobalOptions & { schema?: boolean }) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateRTNamespace(opts: GlobalOptions) {
|
||||
const workspace = await resolveWorkspace(opts);
|
||||
await requireLogin(opts);
|
||||
const rts = await wmill.listResourceType({
|
||||
workspace: workspace.workspaceId,
|
||||
});
|
||||
|
||||
let namespaceContent = "declare namespace RT {\n";
|
||||
namespaceContent += rts
|
||||
.map((resourceType) => {
|
||||
return ` type ${toCamel(
|
||||
capitalize(resourceType.name)
|
||||
)} = ${compileResourceTypeToTsType(resourceType.schema as any).replaceAll(
|
||||
"\n",
|
||||
"\n "
|
||||
)}`;
|
||||
})
|
||||
.join("\n\n");
|
||||
namespaceContent += "\n}";
|
||||
|
||||
writeFileSync(path.join(process.cwd(), "rt.d.ts"), namespaceContent);
|
||||
|
||||
log.info(
|
||||
colors.green(
|
||||
"Created rt.d.ts with resource types namespace (RT) for TypeScript."
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const command = new Command()
|
||||
.description("resource type related commands")
|
||||
.action(() =>
|
||||
log.info("2 actions available, list and push.")
|
||||
)
|
||||
.action(() => log.info("2 actions available, list and push."))
|
||||
.command("list", "list all resource types")
|
||||
.option("--schema", "Show schema in the output")
|
||||
.action(list as any)
|
||||
@@ -113,6 +153,11 @@ const command = new Command()
|
||||
"push a local resource spec. This overrides any remote versions."
|
||||
)
|
||||
.arguments("<file_path:string> <name:string>")
|
||||
.action(push as any);
|
||||
.action(push as any)
|
||||
.command(
|
||||
"generate-namespace",
|
||||
"Create a TypeScript definition file with the RT namespace generated from the resource types"
|
||||
)
|
||||
.action(generateRTNamespace as any);
|
||||
|
||||
export default command;
|
||||
|
||||
33
cli/src/utils/resource_types.ts
Normal file
33
cli/src/utils/resource_types.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Schema, SchemaProperty } from "../../bootstrap/common.ts";
|
||||
|
||||
export function compileResourceTypeToTsType(schema: Schema) {
|
||||
function rec(x: { [name: string]: SchemaProperty }, root = false) {
|
||||
let res = "{\n";
|
||||
const entries = Object.entries(x);
|
||||
if (entries.length == 0) {
|
||||
return "any";
|
||||
}
|
||||
let i = 0;
|
||||
for (let [name, prop] of entries) {
|
||||
if (prop.type == "object") {
|
||||
res += ` ${name}: ${rec(prop.properties ?? {})}`;
|
||||
} else if (prop.type == "array") {
|
||||
res += ` ${name}: ${prop?.items?.type ?? "any"}[]`;
|
||||
} else {
|
||||
let typ = prop?.type ?? "any";
|
||||
if (typ == "integer") {
|
||||
typ = "number";
|
||||
}
|
||||
res += ` ${name}: ${typ}`;
|
||||
}
|
||||
i++;
|
||||
if (i < entries.length) {
|
||||
res += ",\n";
|
||||
}
|
||||
}
|
||||
res += "\n}";
|
||||
return res;
|
||||
}
|
||||
|
||||
return rec(schema.properties, true);
|
||||
}
|
||||
@@ -275,3 +275,13 @@ export async function fetchRemoteVersion(
|
||||
}
|
||||
log.info(colors.gray("Remote version: " + version));
|
||||
}
|
||||
|
||||
export function toCamel(s: string) {
|
||||
return s.replace(/([-_][a-z])/gi, ($1) => {
|
||||
return $1.toUpperCase().replace("-", "").replace("_", "");
|
||||
});
|
||||
}
|
||||
|
||||
export function capitalize(str: string): string {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user