refactor: added type generation from schema for sdk with comments
This commit is contained in:
parent
2683d46b16
commit
04e332d91e
7 changed files with 65 additions and 20 deletions
3
bun.lock
3
bun.lock
|
|
@ -101,6 +101,7 @@
|
||||||
"vite-plugin-svg-icons-ng": "^1.5.2",
|
"vite-plugin-svg-icons-ng": "^1.5.2",
|
||||||
"vite-static-assets-plugin": "^1.2.2",
|
"vite-static-assets-plugin": "^1.2.2",
|
||||||
"vite-tsconfig-paths": "^6.1.1",
|
"vite-tsconfig-paths": "^6.1.1",
|
||||||
|
"zod-to-ts": "^2.0.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -1923,6 +1924,8 @@
|
||||||
|
|
||||||
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
||||||
|
|
||||||
|
"zod-to-ts": ["zod-to-ts@2.0.0", "", { "peerDependencies": { "typescript": "^5.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-aHsUgIl+CQutKAxtRNeZslLCLXoeuSq+j5HU7q3kvi/c2KIAo6q4YjT7/lwFfACxLB923ELHYMkHmlxiqFy4lw=="],
|
||||||
|
|
||||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||||
|
|
||||||
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vite-plugin-svg-icons-ng": "^1.5.2",
|
"vite-plugin-svg-icons-ng": "^1.5.2",
|
||||||
"vite-static-assets-plugin": "^1.2.2",
|
"vite-static-assets-plugin": "^1.2.2",
|
||||||
"vite-tsconfig-paths": "^6.1.1"
|
"vite-tsconfig-paths": "^6.1.1",
|
||||||
|
"zod-to-ts": "^2.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,11 @@ import sdkTsConfig from './sdk/sdk.tsconfig.json';
|
||||||
import sdkPackage from './sdk/package.json';
|
import sdkPackage from './sdk/package.json';
|
||||||
import { emptyDir } from 'fs-extra';
|
import { emptyDir } from 'fs-extra';
|
||||||
import { generateDtsBundle } from 'dts-bundle-generator';
|
import { generateDtsBundle } from 'dts-bundle-generator';
|
||||||
|
import { zodToTs, createAuxiliaryTypeStore, printNode } from 'zod-to-ts';
|
||||||
|
|
||||||
|
import * as types from './sdk/sdk';
|
||||||
|
|
||||||
|
const zodTypeRegex = /z\.infer<typeof? ([\w\d]+)>/gm;
|
||||||
|
|
||||||
async function generateApiDeclarations ()
|
async function generateApiDeclarations ()
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +24,29 @@ async function generateApiDeclarations ()
|
||||||
}
|
}
|
||||||
},], { preferredConfigPath: './scripts/sdk/sdk.tsconfig.json' });
|
},], { preferredConfigPath: './scripts/sdk/sdk.tsconfig.json' });
|
||||||
|
|
||||||
await Bun.write('./dist-sdk/index.d.ts', results);
|
const auxiliaryTypeStore = createAuxiliaryTypeStore();
|
||||||
|
|
||||||
|
await Bun.write('./dist-sdk/index.d.ts', results.map(r =>
|
||||||
|
{
|
||||||
|
const result = r;
|
||||||
|
return result.replaceAll(zodTypeRegex, (e, name) =>
|
||||||
|
{
|
||||||
|
const schema = types[name as keyof typeof types];
|
||||||
|
if (schema)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const { node } = zodToTs(schema as any, { auxiliaryTypeStore, unrepresentable: 'any' });
|
||||||
|
return printNode(node);
|
||||||
|
} catch (error)
|
||||||
|
{
|
||||||
|
console.error(error);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
const pkg = {
|
const pkg = {
|
||||||
...sdkPackage,
|
...sdkPackage,
|
||||||
|
|
@ -29,7 +56,7 @@ async function generateApiDeclarations ()
|
||||||
author: appPkg.author,
|
author: appPkg.author,
|
||||||
peerDependencies: appPkg.dependencies
|
peerDependencies: appPkg.dependencies
|
||||||
};
|
};
|
||||||
await Bun.write(path.join(outDir, '..', 'package.json'), JSON.stringify(pkg, null, 3));
|
await Bun.write(path.join(outDir, 'package.json'), JSON.stringify(pkg, null, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
await generateApiDeclarations();
|
await generateApiDeclarations();
|
||||||
14
scripts/sdk/README.md
Normal file
14
scripts/sdk/README.md
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Gameflow Deck SDK
|
||||||
|
|
||||||
|
This is the type definitions for Gameflow Deck plugins.
|
||||||
|
|
||||||
|
## Developing a plugin
|
||||||
|
|
||||||
|
The plugin must have a default export class of type `PluginType`. It exposes the context and all the hooks to be tapped.
|
||||||
|
Gameflow uses the [Tapable Hooks](https://github.com/webpack/tapable).
|
||||||
|
|
||||||
|
The package must expose a main script gameflow will import and validate. It must implement the type fields on `PluginDescriptionType`.
|
||||||
|
|
||||||
|
## Publishing
|
||||||
|
|
||||||
|
For the plugin to show up in the UI for download. It must be published to NPM with the `gameflow-plugin` keyword. Gameflow uses bun to install plugins as packages from npmjs.
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "gameflow-sdk",
|
"name": "gameflow-sdk",
|
||||||
"types": "index.d.ts"
|
"types": "index.d.ts",
|
||||||
|
"description": "plugin SDK for the Gameflow Deck Launcher",
|
||||||
|
"keywords": [
|
||||||
|
"gameflow",
|
||||||
|
"sdk"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
"emitDeclarationOnly": true,
|
"emitDeclarationOnly": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"outDir": "../../dist-sdk/sdk",
|
"outDir": "../../dist-sdk",
|
||||||
"types": [
|
"types": [
|
||||||
"node"
|
"node"
|
||||||
],
|
],
|
||||||
|
|
@ -38,10 +38,5 @@
|
||||||
"../../src/mainview/scripts/queries/*"
|
"../../src/mainview/scripts/queries/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"include": [
|
|
||||||
"../../src/bun/api/hooks",
|
|
||||||
"../../src/bun/types",
|
|
||||||
"../../src/shared"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
@ -9,8 +9,8 @@ export const PluginContextSchema = z.object({
|
||||||
|
|
||||||
export const PluginLoadingContextSchema = z.object({
|
export const PluginLoadingContextSchema = z.object({
|
||||||
setProgress: z.function().input([z.number(), z.string()]).output(z.void()),
|
setProgress: z.function().input([z.number(), z.string()]).output(z.void()),
|
||||||
config: z.instanceof(Conf),
|
config: z.instanceof(Conf).describe("Per plugin config. It will use the settings schema defined in the plugin class"),
|
||||||
zodRegistry: z.instanceof($ZodRegistry)
|
zodRegistry: z.instanceof($ZodRegistry).describe("Used by the settings to register metadata for the UI")
|
||||||
}).extend(PluginContextSchema.shape);
|
}).extend(PluginContextSchema.shape);
|
||||||
|
|
||||||
export const PluginDescriptionSchema = z.object({
|
export const PluginDescriptionSchema = z.object({
|
||||||
|
|
@ -18,24 +18,24 @@ export const PluginDescriptionSchema = z.object({
|
||||||
displayName: z.string(),
|
displayName: z.string(),
|
||||||
version: z.string(),
|
version: z.string(),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
icon: z.url().optional(),
|
icon: z.url().optional().describe("Can be an external URL to an image or a data url"),
|
||||||
keywords: z.array(z.string()).optional(),
|
keywords: z.array(z.string()).optional(),
|
||||||
category: z.string().default("other"),
|
category: z.string().default("other"),
|
||||||
main: z.string(),
|
main: z.string().describe("The main entry. It must export a default class implementing PluginType"),
|
||||||
canDisable: z.boolean().default(true).optional()
|
canDisable: z.boolean().default(true).optional().describe("Can the plugin be disabled or enabled by the user")
|
||||||
});
|
});
|
||||||
|
|
||||||
export const PluginSchema = z.object({
|
export const PluginSchema = z.object({
|
||||||
load: z.function().input([PluginLoadingContextSchema]).output(z.promise(z.void())),
|
load: z.function().input([PluginLoadingContextSchema]).output(z.promise(z.void())).describe("Called when the plugin is loaded or reloaded"),
|
||||||
cleanup: z.function().output(z.promise(z.void())).optional(),
|
cleanup: z.function().output(z.promise(z.void())).optional().describe("Called when the plugin is unloaded or before it's reloaded"),
|
||||||
settingsSchema: z.instanceof(z.ZodObject).optional(),
|
settingsSchema: z.instanceof(z.ZodObject).optional().describe("The settings schema. Gameflow will show settings in the UI."),
|
||||||
settingsMigrations: z.record(z.string(), z.function().input([z.instanceof(Conf)]).output(z.void())).optional(),
|
settingsMigrations: z.record(z.string(), z.function().input([z.instanceof(Conf)]).output(z.void())).optional(),
|
||||||
eventsNames: z.object({
|
eventsNames: z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
action: z.string()
|
action: z.string()
|
||||||
}).array().optional(),
|
}).array().optional().describe("Events will be called when the user presses the button in plugin settings. Each event creates a button."),
|
||||||
onEvent: z.function().input([z.string()]).output(z.object({
|
onEvent: z.function().input([z.string()]).output(z.object({
|
||||||
openTab: z.string().optional(),
|
openTab: z.string().optional(),
|
||||||
reload: z.boolean().optional()
|
reload: z.boolean().optional()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue