bun_plugins 1.1.2 → 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/README.md +2 -2
- package/dist/plugins/ActionRegistryPlugin.d.ts +23 -0
- package/dist/plugins/ActionRegistryPlugin.d.ts.map +1 -0
- package/dist/plugins/ActionRegistryPlugin.js +56 -0
- package/dist/plugins/ActionRegistryPlugin.js.map +1 -0
- package/dist/plugins/DynamicJSActionsPlugin.d.ts +21 -0
- package/dist/plugins/DynamicJSActionsPlugin.d.ts.map +1 -0
- package/dist/plugins/DynamicJSActionsPlugin.js +57 -0
- package/dist/plugins/DynamicJSActionsPlugin.js.map +1 -0
- package/dist/plugins/DynamicMathActionsPlugin.d.ts +22 -0
- package/dist/plugins/DynamicMathActionsPlugin.d.ts.map +1 -0
- package/dist/plugins/DynamicMathActionsPlugin.js +64 -0
- package/dist/plugins/DynamicMathActionsPlugin.js.map +1 -0
- package/dist/plugins/DynamicTextActionsPlugin.d.ts +22 -0
- package/dist/plugins/DynamicTextActionsPlugin.d.ts.map +1 -0
- package/dist/plugins/DynamicTextActionsPlugin.js +58 -0
- package/dist/plugins/DynamicTextActionsPlugin.js.map +1 -0
- package/dist/plugins/DynamicUtilityActionsPlugin.d.ts +22 -0
- package/dist/plugins/DynamicUtilityActionsPlugin.d.ts.map +1 -0
- package/dist/plugins/DynamicUtilityActionsPlugin.js +75 -0
- package/dist/plugins/DynamicUtilityActionsPlugin.js.map +1 -0
- package/dist/plugins/ExamplePlugin.d.ts +3 -0
- package/dist/plugins/ExamplePlugin.d.ts.map +1 -0
- package/dist/plugins/ExamplePlugin.js +41 -0
- package/dist/plugins/ExamplePlugin.js.map +1 -0
- package/dist/plugins/LifecycleDemoPlugin.d.ts +20 -0
- package/dist/plugins/LifecycleDemoPlugin.d.ts.map +1 -0
- package/dist/plugins/LifecycleDemoPlugin.js +34 -0
- package/dist/plugins/LifecycleDemoPlugin.js.map +1 -0
- package/dist/plugins/MathPlugin.d.ts +16 -0
- package/dist/plugins/MathPlugin.d.ts.map +1 -0
- package/dist/plugins/MathPlugin.js +25 -0
- package/dist/plugins/MathPlugin.js.map +1 -0
- package/dist/plugins/MyJSPlugin.d.ts +7 -0
- package/dist/plugins/MyJSPlugin.d.ts.map +1 -0
- package/dist/plugins/MyJSPlugin.js +12 -0
- package/dist/plugins/MyJSPlugin.js.map +1 -0
- package/dist/plugins/arktype/index.d.ts +8 -0
- package/dist/plugins/arktype/index.d.ts.map +1 -0
- package/dist/plugins/arktype/index.js +25 -0
- package/dist/plugins/arktype/index.js.map +1 -0
- package/dist/src/Plugin.d.ts +28 -0
- package/dist/src/Plugin.d.ts.map +1 -0
- package/dist/src/Plugin.js +36 -0
- package/dist/src/Plugin.js.map +1 -0
- package/dist/{PluginManager.d.ts → src/PluginManager.d.ts} +5 -0
- package/dist/src/PluginManager.d.ts.map +1 -0
- package/dist/{PluginManager.js → src/PluginManager.js} +104 -43
- package/dist/src/PluginManager.js.map +1 -0
- package/dist/{index.d.ts → src/index.d.ts} +2 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/{index.js → src/index.js} +2 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/logger/LoggerAdapter.d.ts +77 -0
- package/dist/src/logger/LoggerAdapter.d.ts.map +1 -0
- package/dist/src/logger/LoggerAdapter.js +242 -0
- package/dist/src/logger/LoggerAdapter.js.map +1 -0
- package/dist/src/logger/LoggerFactory.d.ts +73 -0
- package/dist/src/logger/LoggerFactory.d.ts.map +1 -0
- package/dist/src/logger/LoggerFactory.js +99 -0
- package/dist/src/logger/LoggerFactory.js.map +1 -0
- package/dist/src/logger/index.d.ts +3 -0
- package/dist/src/logger/index.d.ts.map +1 -0
- package/dist/src/logger/index.js +3 -0
- package/dist/src/logger/index.js.map +1 -0
- package/dist/src/managers/ContextFactory.d.ts.map +1 -0
- package/dist/{managers → src/managers}/ContextFactory.js +51 -27
- package/dist/src/managers/ContextFactory.js.map +1 -0
- package/dist/src/managers/DependencyManager.d.ts.map +1 -0
- package/dist/{managers → src/managers}/DependencyManager.js +2 -1
- package/dist/src/managers/DependencyManager.js.map +1 -0
- package/dist/{managers → src/managers}/HooksManager.d.ts +4 -1
- package/dist/src/managers/HooksManager.d.ts.map +1 -0
- package/dist/{managers → src/managers}/HooksManager.js +36 -8
- package/dist/src/managers/HooksManager.js.map +1 -0
- package/dist/src/managers/ResourceManager.d.ts.map +1 -0
- package/dist/{managers → src/managers}/ResourceManager.js +2 -1
- package/dist/src/managers/ResourceManager.js.map +1 -0
- package/dist/src/storage/JsonPluginStorage.d.ts.map +1 -0
- package/dist/{storage → src/storage}/JsonPluginStorage.js +3 -2
- package/dist/src/storage/JsonPluginStorage.js.map +1 -0
- package/dist/src/types/arktype_converter.d.ts +19 -0
- package/dist/src/types/arktype_converter.d.ts.map +1 -0
- package/dist/src/types/arktype_converter.js +73 -0
- package/dist/src/types/arktype_converter.js.map +1 -0
- package/dist/src/types/generator.d.ts +8 -0
- package/dist/src/types/generator.d.ts.map +1 -0
- package/dist/src/types/generator.js +9 -0
- package/dist/src/types/generator.js.map +1 -0
- package/dist/src/types/interfaces.d.ts +55 -0
- package/dist/src/types/interfaces.d.ts.map +1 -0
- package/dist/src/types/interfaces.js +5 -0
- package/dist/src/types/interfaces.js.map +1 -0
- package/dist/src/types/plugin-registry.d.ts +21 -0
- package/dist/src/types/plugin-registry.d.ts.map +1 -0
- package/dist/src/types/plugin-registry.js +4 -0
- package/dist/src/types/plugin-registry.js.map +1 -0
- package/dist/src/types/plugin_generator.d.ts +56 -0
- package/dist/src/types/plugin_generator.d.ts.map +1 -0
- package/dist/src/types/plugin_generator.js +659 -0
- package/dist/src/types/plugin_generator.js.map +1 -0
- package/dist/{types.d.ts → src/types.d.ts} +29 -10
- package/dist/src/types.d.ts.map +1 -0
- package/dist/{types.js → src/types.js} +4 -1
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/errorParser.d.ts.map +1 -0
- package/dist/src/utils/errorParser.js.map +1 -0
- package/dist/src/utils/pluginValidator.d.ts.map +1 -0
- package/dist/{utils → src/utils}/pluginValidator.js +1 -1
- package/dist/src/utils/pluginValidator.js.map +1 -0
- package/dist/src/utils/security.d.ts.map +1 -0
- package/dist/src/utils/security.js.map +1 -0
- package/dist/src/worker/WorkerRunner.d.ts.map +1 -0
- package/dist/{worker → src/worker}/WorkerRunner.js +21 -9
- package/dist/src/worker/WorkerRunner.js.map +1 -0
- package/package.json +6 -4
- package/dist/PluginManager.d.ts.map +0 -1
- package/dist/PluginManager.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/managers/ContextFactory.d.ts.map +0 -1
- package/dist/managers/ContextFactory.js.map +0 -1
- package/dist/managers/DependencyManager.d.ts.map +0 -1
- package/dist/managers/DependencyManager.js.map +0 -1
- package/dist/managers/HooksManager.d.ts.map +0 -1
- package/dist/managers/HooksManager.js.map +0 -1
- package/dist/managers/ResourceManager.d.ts.map +0 -1
- package/dist/managers/ResourceManager.js.map +0 -1
- package/dist/storage/JsonPluginStorage.d.ts.map +0 -1
- package/dist/storage/JsonPluginStorage.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils/errorParser.d.ts.map +0 -1
- package/dist/utils/errorParser.js.map +0 -1
- package/dist/utils/pluginValidator.d.ts.map +0 -1
- package/dist/utils/pluginValidator.js.map +0 -1
- package/dist/utils/security.d.ts.map +0 -1
- package/dist/utils/security.js.map +0 -1
- package/dist/worker/WorkerRunner.d.ts.map +0 -1
- package/dist/worker/WorkerRunner.js.map +0 -1
- /package/dist/{managers → src/managers}/ContextFactory.d.ts +0 -0
- /package/dist/{managers → src/managers}/DependencyManager.d.ts +0 -0
- /package/dist/{managers → src/managers}/ResourceManager.d.ts +0 -0
- /package/dist/{storage → src/storage}/JsonPluginStorage.d.ts +0 -0
- /package/dist/{utils → src/utils}/errorParser.d.ts +0 -0
- /package/dist/{utils → src/utils}/errorParser.js +0 -0
- /package/dist/{utils → src/utils}/pluginValidator.d.ts +0 -0
- /package/dist/{utils → src/utils}/security.d.ts +0 -0
- /package/dist/{utils → src/utils}/security.js +0 -0
- /package/dist/{worker → src/worker}/WorkerRunner.d.ts +0 -0
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
import { readdir, readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { join, relative, extname } from "node:path";
|
|
3
|
+
import ts from "typescript";
|
|
4
|
+
import { ArkTypeConverter } from "./arktype_converter";
|
|
5
|
+
/**
|
|
6
|
+
* Main generator for handling conversion between classes and arktype schemas.
|
|
7
|
+
* Provides dynamic typing and autocomplete without static KnownPluginNames.
|
|
8
|
+
*/
|
|
9
|
+
export class PluginTypeGenerator {
|
|
10
|
+
pluginsDir;
|
|
11
|
+
outputDir;
|
|
12
|
+
packageName;
|
|
13
|
+
compilerOptions;
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.pluginsDir = options.pluginsDir;
|
|
16
|
+
this.outputDir = options.outputDir;
|
|
17
|
+
this.packageName = options.packageName || "bun_plugins";
|
|
18
|
+
this.compilerOptions = this.getCompilerOptions();
|
|
19
|
+
}
|
|
20
|
+
getCompilerOptions() {
|
|
21
|
+
const configPath = join(process.cwd(), "tsconfig.json");
|
|
22
|
+
try {
|
|
23
|
+
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
24
|
+
if (configFile.error) {
|
|
25
|
+
return this.getDefaultOptions();
|
|
26
|
+
}
|
|
27
|
+
const { options } = ts.parseJsonConfigFileContent(configFile.config, ts.sys, process.cwd());
|
|
28
|
+
return options;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return this.getDefaultOptions();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
getDefaultOptions() {
|
|
35
|
+
return {
|
|
36
|
+
target: ts.ScriptTarget.ESNext,
|
|
37
|
+
module: ts.ModuleKind.ESNext,
|
|
38
|
+
strict: true,
|
|
39
|
+
esModuleInterop: true,
|
|
40
|
+
skipLibCheck: true,
|
|
41
|
+
noEmit: true
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async scanPlugins() {
|
|
45
|
+
const pluginFiles = await this.scanDirectory(this.pluginsDir);
|
|
46
|
+
const plugins = [];
|
|
47
|
+
const seenClasses = new Set();
|
|
48
|
+
for (const filePath of pluginFiles) {
|
|
49
|
+
try {
|
|
50
|
+
const content = await readFile(filePath, "utf-8");
|
|
51
|
+
const pluginInfo = this.parsePlugin(filePath, content);
|
|
52
|
+
if (pluginInfo && pluginInfo.className && !seenClasses.has(pluginInfo.className)) {
|
|
53
|
+
seenClasses.add(pluginInfo.className);
|
|
54
|
+
plugins.push(pluginInfo);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
console.warn(`Warning: Failed to parse ${filePath}: ${err}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return plugins;
|
|
62
|
+
}
|
|
63
|
+
async scanDirectory(dir) {
|
|
64
|
+
const files = [];
|
|
65
|
+
try {
|
|
66
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
const fullPath = join(dir, entry.name);
|
|
69
|
+
if (entry.isDirectory()) {
|
|
70
|
+
if (entry.name !== "node_modules" &&
|
|
71
|
+
entry.name !== "dist" &&
|
|
72
|
+
entry.name !== "build" &&
|
|
73
|
+
entry.name !== ".git" &&
|
|
74
|
+
!entry.name.startsWith(".")) {
|
|
75
|
+
files.push(...(await this.scanDirectory(fullPath)));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (entry.isFile()) {
|
|
79
|
+
const ext = extname(entry.name);
|
|
80
|
+
if (ext === ".ts" || ext === ".js") {
|
|
81
|
+
files.push(fullPath);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
console.warn(`Warning: Could not read directory ${dir}`);
|
|
88
|
+
}
|
|
89
|
+
return files;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Extracts the properties and methods from a TypeScript class.
|
|
93
|
+
*/
|
|
94
|
+
extractClassProperties(sourceFile) {
|
|
95
|
+
const properties = [];
|
|
96
|
+
const visit = (node) => {
|
|
97
|
+
if (ts.isClassDeclaration(node)) {
|
|
98
|
+
node.members.forEach(member => {
|
|
99
|
+
if (ts.isPropertyDeclaration(member) && member.name && ts.isIdentifier(member.name)) {
|
|
100
|
+
const propName = member.name.text;
|
|
101
|
+
const propInfo = this.extractPropertyType(member, sourceFile);
|
|
102
|
+
if (propInfo) {
|
|
103
|
+
properties.push(propInfo);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (ts.isMethodDeclaration(member) && member.name && ts.isIdentifier(member.name)) {
|
|
107
|
+
const methodInfo = this.extractMethodInfo(member, sourceFile);
|
|
108
|
+
if (methodInfo) {
|
|
109
|
+
properties.push(methodInfo);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (ts.isConstructorDeclaration(member)) {
|
|
113
|
+
member.parameters.forEach(param => {
|
|
114
|
+
if (ts.isIdentifier(param.name) && param.type) {
|
|
115
|
+
const propInfo = {
|
|
116
|
+
name: param.name.text,
|
|
117
|
+
type: param.type.getText(sourceFile),
|
|
118
|
+
isOptional: param.questionToken !== undefined,
|
|
119
|
+
isArray: this.isArrayType(param.type, sourceFile),
|
|
120
|
+
nestedType: this.extractNestedType(param.type, sourceFile)
|
|
121
|
+
};
|
|
122
|
+
properties.push(propInfo);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
ts.forEachChild(node, visit);
|
|
129
|
+
};
|
|
130
|
+
ts.forEachChild(sourceFile, visit);
|
|
131
|
+
return properties;
|
|
132
|
+
}
|
|
133
|
+
extractMethodInfo(node, sourceFile) {
|
|
134
|
+
const name = node.name.getText(sourceFile);
|
|
135
|
+
if (name.startsWith("_") || name.startsWith("#")) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const lifecycleMethods = ["onLoad", "onUnload", "onEnable", "onDisable", "onReload"];
|
|
139
|
+
if (lifecycleMethods.includes(name)) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const params = [];
|
|
143
|
+
node.parameters.forEach(param => {
|
|
144
|
+
if (ts.isIdentifier(param.name)) {
|
|
145
|
+
params.push({
|
|
146
|
+
name: param.name.text,
|
|
147
|
+
type: param.type ? param.type.getText(sourceFile) : "any",
|
|
148
|
+
isOptional: param.questionToken !== undefined
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
const returnType = node.type ? node.type.getText(sourceFile) : "void";
|
|
153
|
+
return {
|
|
154
|
+
name,
|
|
155
|
+
type: returnType,
|
|
156
|
+
isOptional: false,
|
|
157
|
+
isArray: false,
|
|
158
|
+
isMethod: true,
|
|
159
|
+
params
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
extractPropertyType(node, sourceFile) {
|
|
163
|
+
if (!node.name || !ts.isIdentifier(node.name)) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
const name = node.name.text;
|
|
167
|
+
if (name.startsWith("_") || name.startsWith("#")) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
let type = "any";
|
|
171
|
+
let isOptional = node.questionToken !== undefined || node.initializer !== undefined;
|
|
172
|
+
let isArray = false;
|
|
173
|
+
let nestedType;
|
|
174
|
+
if (node.type) {
|
|
175
|
+
type = node.type.getText(sourceFile);
|
|
176
|
+
isArray = this.isArrayType(node.type, sourceFile);
|
|
177
|
+
nestedType = this.extractNestedType(node.type, sourceFile);
|
|
178
|
+
}
|
|
179
|
+
else if (node.initializer) {
|
|
180
|
+
type = this.inferTypeFromInitializer(node.initializer, sourceFile);
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
name,
|
|
184
|
+
type: this.mapTypeName(type),
|
|
185
|
+
isOptional,
|
|
186
|
+
isArray,
|
|
187
|
+
nestedType
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
isArrayType(typeNode, sourceFile) {
|
|
191
|
+
if (ts.isArrayTypeNode(typeNode)) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
if (ts.isUnionTypeNode(typeNode)) {
|
|
195
|
+
return typeNode.types.some(t => this.isArrayType(t, sourceFile));
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
extractNestedType(typeNode, sourceFile) {
|
|
200
|
+
if (ts.isTypeReferenceNode(typeNode)) {
|
|
201
|
+
const typeName = typeNode.typeName.getText(sourceFile);
|
|
202
|
+
const nestedProperties = this.findTypeProperties(typeName, sourceFile);
|
|
203
|
+
if (nestedProperties.length > 0) {
|
|
204
|
+
return {
|
|
205
|
+
schemaName: typeName,
|
|
206
|
+
schemaDefinition: "",
|
|
207
|
+
typeDefinition: "",
|
|
208
|
+
properties: nestedProperties
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (ts.isTypeLiteralNode(typeNode)) {
|
|
213
|
+
const properties = [];
|
|
214
|
+
typeNode.members.forEach(member => {
|
|
215
|
+
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
|
|
216
|
+
const propInfo = {
|
|
217
|
+
name: member.name.text,
|
|
218
|
+
type: member.type ? member.type.getText(sourceFile) : "any",
|
|
219
|
+
isOptional: member.questionToken !== undefined,
|
|
220
|
+
isArray: member.type ? this.isArrayType(member.type, sourceFile) : false
|
|
221
|
+
};
|
|
222
|
+
properties.push(propInfo);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
return {
|
|
226
|
+
schemaName: "InlineType",
|
|
227
|
+
schemaDefinition: "",
|
|
228
|
+
typeDefinition: "",
|
|
229
|
+
properties
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Generate a type string for public methods of a plugin class.
|
|
236
|
+
*/
|
|
237
|
+
generatePublicMethodsType(p) {
|
|
238
|
+
if (p.arkTypeSchema && p.arkTypeSchema.properties.length > 0) {
|
|
239
|
+
const props = p.arkTypeSchema.properties
|
|
240
|
+
.filter(prop => !prop.name.startsWith("_") && !prop.name.startsWith("#"))
|
|
241
|
+
.map(prop => {
|
|
242
|
+
if (prop.isMethod) {
|
|
243
|
+
const params = prop.params?.map(param => `${param.name}: ${param.type}`).join(", ") || "";
|
|
244
|
+
return ` ${prop.name}(${params}): ${prop.type};`;
|
|
245
|
+
}
|
|
246
|
+
return ` ${prop.name}${prop.isOptional ? "?" : ""}: ${prop.type};`;
|
|
247
|
+
})
|
|
248
|
+
.join("\n");
|
|
249
|
+
return `{\n${props}\n }`;
|
|
250
|
+
}
|
|
251
|
+
return p.className;
|
|
252
|
+
}
|
|
253
|
+
findTypeProperties(typeName, sourceFile) {
|
|
254
|
+
const properties = [];
|
|
255
|
+
const visit = (node) => {
|
|
256
|
+
if (ts.isInterfaceDeclaration(node) && node.name.text === typeName) {
|
|
257
|
+
node.members.forEach(member => {
|
|
258
|
+
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
|
|
259
|
+
const propInfo = {
|
|
260
|
+
name: member.name.text,
|
|
261
|
+
type: member.type ? this.mapTypeName(member.type.getText(sourceFile)) : "any",
|
|
262
|
+
isOptional: member.questionToken !== undefined,
|
|
263
|
+
isArray: member.type ? this.isArrayType(member.type, sourceFile) : false
|
|
264
|
+
};
|
|
265
|
+
properties.push(propInfo);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
if (ts.isTypeAliasDeclaration(node) && node.name.text === typeName) {
|
|
270
|
+
if (ts.isTypeLiteralNode(node.type)) {
|
|
271
|
+
node.type.members.forEach(member => {
|
|
272
|
+
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
|
|
273
|
+
const propInfo = {
|
|
274
|
+
name: member.name.text,
|
|
275
|
+
type: member.type ? this.mapTypeName(member.type.getText(sourceFile)) : "any",
|
|
276
|
+
isOptional: member.questionToken !== undefined,
|
|
277
|
+
isArray: member.type ? this.isArrayType(member.type, sourceFile) : false
|
|
278
|
+
};
|
|
279
|
+
properties.push(propInfo);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
ts.forEachChild(node, visit);
|
|
285
|
+
};
|
|
286
|
+
ts.forEachChild(sourceFile, visit);
|
|
287
|
+
return properties;
|
|
288
|
+
}
|
|
289
|
+
mapTypeName(type) {
|
|
290
|
+
const typeMap = {
|
|
291
|
+
"string": "string",
|
|
292
|
+
"String": "string",
|
|
293
|
+
"number": "number",
|
|
294
|
+
"Number": "number",
|
|
295
|
+
"boolean": "boolean",
|
|
296
|
+
"Boolean": "boolean",
|
|
297
|
+
"any": "any",
|
|
298
|
+
"unknown": "unknown",
|
|
299
|
+
"void": "void",
|
|
300
|
+
"null": "null",
|
|
301
|
+
"Date": "Date",
|
|
302
|
+
"Promise": "Promise",
|
|
303
|
+
"Array": "Array",
|
|
304
|
+
"Record": "Record"
|
|
305
|
+
};
|
|
306
|
+
const baseType = type.split("<")[0]?.split("|")[0]?.trim() || type;
|
|
307
|
+
return typeMap[baseType] || baseType;
|
|
308
|
+
}
|
|
309
|
+
inferTypeFromInitializer(initializer, sourceFile) {
|
|
310
|
+
if (ts.isStringLiteral(initializer)) {
|
|
311
|
+
return "string";
|
|
312
|
+
}
|
|
313
|
+
if (ts.isNumericLiteral(initializer)) {
|
|
314
|
+
return "number";
|
|
315
|
+
}
|
|
316
|
+
if (ts.isIdentifier(initializer)) {
|
|
317
|
+
const symbol = this.getSymbolAtLocation(initializer, sourceFile);
|
|
318
|
+
if (symbol) {
|
|
319
|
+
return symbol;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return "any";
|
|
323
|
+
}
|
|
324
|
+
getSymbolAtLocation(node, sourceFile) {
|
|
325
|
+
const typeChecker = this.getTypeChecker();
|
|
326
|
+
const symbol = typeChecker.getSymbolAtLocation(node);
|
|
327
|
+
if (symbol && symbol.declarations && symbol.declarations.length > 0) {
|
|
328
|
+
const decl = symbol.declarations[0];
|
|
329
|
+
if (decl && (ts.isTypeAliasDeclaration(decl) || ts.isInterfaceDeclaration(decl))) {
|
|
330
|
+
return decl.name.text;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
getTypeChecker() {
|
|
336
|
+
const program = ts.createProgram([join(process.cwd(), "src/types/plugin_generator.ts")], this.compilerOptions);
|
|
337
|
+
return program.getTypeChecker();
|
|
338
|
+
}
|
|
339
|
+
parseTypeScriptPlugin(filePath, source) {
|
|
340
|
+
const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true);
|
|
341
|
+
let pluginName = "";
|
|
342
|
+
let pluginVersion = "1.0.0";
|
|
343
|
+
let className = "";
|
|
344
|
+
let apiInterface;
|
|
345
|
+
let dependencies;
|
|
346
|
+
let arkTypeSchema;
|
|
347
|
+
function visit(node) {
|
|
348
|
+
if (ts.isClassDeclaration(node)) {
|
|
349
|
+
if (node.name) {
|
|
350
|
+
className = node.name.text;
|
|
351
|
+
const converter = new PluginTypeGenerator({
|
|
352
|
+
pluginsDir: "",
|
|
353
|
+
outputDir: ""
|
|
354
|
+
});
|
|
355
|
+
const properties = converter.extractClassProperties(sourceFile);
|
|
356
|
+
if (properties.length > 0) {
|
|
357
|
+
const schemaName = className + "Schema";
|
|
358
|
+
arkTypeSchema = {
|
|
359
|
+
schemaName,
|
|
360
|
+
schemaDefinition: ArkTypeConverter.classToArkType(className, properties),
|
|
361
|
+
typeDefinition: "",
|
|
362
|
+
properties
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (ts.isPropertyDeclaration(node)) {
|
|
368
|
+
const name = node.name.getText(sourceFile);
|
|
369
|
+
if (name === "name" && node.initializer) {
|
|
370
|
+
pluginName = node.initializer.getText(sourceFile).replace(/['"]/g, "");
|
|
371
|
+
}
|
|
372
|
+
if (name === "version" && node.initializer) {
|
|
373
|
+
pluginVersion = node.initializer.getText(sourceFile).replace(/['"]/g, "");
|
|
374
|
+
}
|
|
375
|
+
if (name === "dependencies" && node.initializer) {
|
|
376
|
+
try {
|
|
377
|
+
const depsText = node.initializer.getText(sourceFile);
|
|
378
|
+
dependencies = eval(`(${depsText})`);
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (ts.isMethodDeclaration(node)) {
|
|
385
|
+
const name = node.name.getText(sourceFile);
|
|
386
|
+
if (name === "getApi" && node.type) {
|
|
387
|
+
const returnType = node.type.getText(sourceFile);
|
|
388
|
+
const match = returnType.match(/:\s*(\w+Api)/);
|
|
389
|
+
if (match) {
|
|
390
|
+
apiInterface = match[1];
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
ts.forEachChild(node, visit);
|
|
395
|
+
}
|
|
396
|
+
ts.forEachChild(sourceFile, visit);
|
|
397
|
+
if (!className) {
|
|
398
|
+
const exportDefaultMatch = source.match(/export\s+default\s+definePlugin\s*\(/);
|
|
399
|
+
if (exportDefaultMatch) {
|
|
400
|
+
className = "DefaultPlugin";
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const fileName = filePath.split(/[/\\]/).pop()?.replace(/\.(ts|js)$/, "") || "";
|
|
404
|
+
const name = pluginName || fileName.toLowerCase().replace(/plugin$/i, "");
|
|
405
|
+
if (!name)
|
|
406
|
+
return null;
|
|
407
|
+
return {
|
|
408
|
+
name,
|
|
409
|
+
version: pluginVersion,
|
|
410
|
+
className,
|
|
411
|
+
filePath,
|
|
412
|
+
isTypeScript: true,
|
|
413
|
+
apiInterface,
|
|
414
|
+
dependencies,
|
|
415
|
+
arkTypeSchema
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
parseJavaScriptPlugin(filePath, source) {
|
|
419
|
+
const fileName = filePath.split(/[/\\]/).pop()?.replace(/\.(ts|js)$/, "") || "";
|
|
420
|
+
const classMatch = source.match(/export\s+class\s+(\w+)/);
|
|
421
|
+
const className = classMatch?.[1] || fileName;
|
|
422
|
+
const nameMatch = source.match(/this\.name\s*=\s*["']([^"']+)["']/) ||
|
|
423
|
+
source.match(/name\s*=\s*["']([^"']+)["']/);
|
|
424
|
+
const name = nameMatch?.[1] || fileName.toLowerCase().replace(/plugin$/i, "");
|
|
425
|
+
const versionMatch = source.match(/version\s*=\s*["']([^"']+)["']/);
|
|
426
|
+
const version = versionMatch?.[1] || "1.0.0";
|
|
427
|
+
const hasSharedApi = /getApi\s*\(/.test(source);
|
|
428
|
+
if (!name)
|
|
429
|
+
return null;
|
|
430
|
+
return {
|
|
431
|
+
name,
|
|
432
|
+
version,
|
|
433
|
+
className,
|
|
434
|
+
filePath,
|
|
435
|
+
isTypeScript: false,
|
|
436
|
+
apiInterface: hasSharedApi ? `${className}Api` : undefined
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
parsePlugin(filePath, content) {
|
|
440
|
+
const ext = extname(filePath);
|
|
441
|
+
if (ext === ".ts") {
|
|
442
|
+
return this.parseTypeScriptPlugin(filePath, content);
|
|
443
|
+
}
|
|
444
|
+
else if (ext === ".js") {
|
|
445
|
+
return this.parseJavaScriptPlugin(filePath, content);
|
|
446
|
+
}
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Generates dynamic type declarations using a factory pattern.
|
|
451
|
+
* No static KnownPluginNames - types are computed at compile time.
|
|
452
|
+
*
|
|
453
|
+
* The API type includes:
|
|
454
|
+
* - Base plugin properties (name, version)
|
|
455
|
+
* - Public methods from the class
|
|
456
|
+
* - Shared API methods if getApi is defined
|
|
457
|
+
*/
|
|
458
|
+
generateDeclarations(plugins, baseApiInterface = "BasePluginApi") {
|
|
459
|
+
// Dynamic plugin factory map - each plugin is indexed by name
|
|
460
|
+
const pluginFactoryEntries = plugins.map(p => {
|
|
461
|
+
// Generate public methods type from the class
|
|
462
|
+
const publicMethodsType = this.generatePublicMethodsType(p);
|
|
463
|
+
// Use the shared API interface if available, otherwise use the public methods
|
|
464
|
+
const apiType = p.apiInterface ? p.apiInterface : publicMethodsType;
|
|
465
|
+
return ` "${p.name}": {
|
|
466
|
+
name: "${p.name}";
|
|
467
|
+
version: "${p.version}";
|
|
468
|
+
class: ${p.className};
|
|
469
|
+
api: ${apiType};
|
|
470
|
+
dependencies?: ${p.dependencies ? `Record<${JSON.stringify(Object.keys(p.dependencies))}, string>` : "undefined"};
|
|
471
|
+
}`;
|
|
472
|
+
}).join(",\n");
|
|
473
|
+
// Dynamic type lookup using the factory map
|
|
474
|
+
const typeLookups = plugins.map(p => {
|
|
475
|
+
const apiType = p.apiInterface || baseApiInterface;
|
|
476
|
+
return ` T extends "${p.name}" ? ${apiType} :`;
|
|
477
|
+
}).join("\n");
|
|
478
|
+
// Dynamic class lookups
|
|
479
|
+
const classLookups = plugins.map(p => {
|
|
480
|
+
return ` T extends "${p.name}" ? ${p.className} :`;
|
|
481
|
+
}).join("\n");
|
|
482
|
+
// Plugin names as tuple for autocomplete
|
|
483
|
+
const pluginNamesTuple = plugins.map(p => `"${p.name}"`).join(" | ");
|
|
484
|
+
return `import type { IPlugin } from "${this.packageName}/types";
|
|
485
|
+
|
|
486
|
+
// Plugin namespace for type-safe plugin access
|
|
487
|
+
export namespace bun_plugins {
|
|
488
|
+
export interface ${baseApiInterface} {
|
|
489
|
+
name: string;
|
|
490
|
+
version: string;
|
|
491
|
+
actions?: string[];
|
|
492
|
+
type?: string;
|
|
493
|
+
loaded?: string;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Dynamic plugin factory map - all plugins are indexed here.
|
|
498
|
+
* This replaces the static KnownPluginNames type.
|
|
499
|
+
*/
|
|
500
|
+
export interface PluginFactory {
|
|
501
|
+
${pluginFactoryEntries}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Registry that aggregates all plugin types for global availability.
|
|
506
|
+
* This enables autocomplete without explicit imports.
|
|
507
|
+
*/
|
|
508
|
+
export interface PluginTypeRegistry {
|
|
509
|
+
/** All discovered plugin names */
|
|
510
|
+
PluginNames: PluginNames;
|
|
511
|
+
/** Get API type for a plugin */
|
|
512
|
+
GetPluginApi: GetPluginApi;
|
|
513
|
+
/** Get class type for a plugin */
|
|
514
|
+
GetPluginClass: GetPluginClass;
|
|
515
|
+
/** Type-safe plugin factory */
|
|
516
|
+
PluginFactory: PluginFactory;
|
|
517
|
+
/** Check if a plugin name is valid */
|
|
518
|
+
IsValidPlugin: IsValidPlugin;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Union type of all plugin names for autocomplete.
|
|
523
|
+
* Generated dynamically from discovered plugins.
|
|
524
|
+
*/
|
|
525
|
+
export type PluginNames = ${pluginNamesTuple};
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Type lookup using the factory map for autocomplete.
|
|
529
|
+
*/
|
|
530
|
+
export type PluginApiType<T extends PluginNames> =
|
|
531
|
+
T extends keyof PluginFactory ? PluginFactory[T]["api"] : ${baseApiInterface};
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Class type lookup using the factory map.
|
|
535
|
+
*/
|
|
536
|
+
export type PluginClassType<T extends PluginNames> =
|
|
537
|
+
T extends keyof PluginFactory ? PluginFactory[T]["class"] : IPlugin;
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Get the API type for a specific plugin.
|
|
541
|
+
*/
|
|
542
|
+
export type GetPluginApi<T extends PluginNames> = PluginApiType<T>;
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Get the class type for a specific plugin.
|
|
546
|
+
*/
|
|
547
|
+
export type GetPluginClass<T extends PluginNames> = PluginClassType<T>;
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Helper to check if a plugin name is valid.
|
|
551
|
+
*/
|
|
552
|
+
export type IsValidPlugin<T extends string> = T extends PluginNames ? true : false;
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Type-safe plugin retrieval from the factory.
|
|
556
|
+
*/
|
|
557
|
+
export type PluginFromFactory<T extends PluginNames> = PluginFactory[T];
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Export types outside namespace for easier import
|
|
561
|
+
export type PluginNames = bun_plugins.PluginNames;
|
|
562
|
+
export type PluginApiType<T extends bun_plugins.PluginNames> = bun_plugins.PluginApiType<T>;
|
|
563
|
+
export type PluginClassType<T extends bun_plugins.PluginNames> = bun_plugins.PluginClassType<T>;
|
|
564
|
+
export type GetPluginApi<T extends bun_plugins.PluginNames> = bun_plugins.GetPluginApi<T>;
|
|
565
|
+
export type GetPluginClass<T extends bun_plugins.PluginNames> = bun_plugins.GetPluginClass<T>;
|
|
566
|
+
export type IsValidPlugin<T extends string> = bun_plugins.IsValidPlugin<T>;
|
|
567
|
+
export type PluginFromFactory<T extends bun_plugins.PluginNames> = bun_plugins.PluginFromFactory<T>;
|
|
568
|
+
export type PluginFactory = bun_plugins.PluginFactory;
|
|
569
|
+
`;
|
|
570
|
+
}
|
|
571
|
+
generateModuleExports(plugins, baseApiInterface = "BasePluginApi") {
|
|
572
|
+
const pluginExports = plugins.map(p => {
|
|
573
|
+
const relPath = relative(this.outputDir, p.filePath);
|
|
574
|
+
return `export { ${p.className} } from "./${relPath.replace(/\\/g, '/').replace(/\.(ts|js)$/, "")}";`;
|
|
575
|
+
}).join("\n");
|
|
576
|
+
return `import type { IPlugin } from "${this.packageName}/types";
|
|
577
|
+
|
|
578
|
+
export type {
|
|
579
|
+
PluginNames,
|
|
580
|
+
PluginApiType,
|
|
581
|
+
PluginClassType,
|
|
582
|
+
GetPluginApi,
|
|
583
|
+
GetPluginClass,
|
|
584
|
+
IsValidPlugin,
|
|
585
|
+
PluginFromFactory,
|
|
586
|
+
PluginFactory
|
|
587
|
+
} from "./plugin-registry";
|
|
588
|
+
|
|
589
|
+
export interface ${baseApiInterface} {
|
|
590
|
+
name: string;
|
|
591
|
+
version: string;
|
|
592
|
+
actions?: string[];
|
|
593
|
+
type?: string;
|
|
594
|
+
loaded?: string;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
${pluginExports}
|
|
598
|
+
`;
|
|
599
|
+
}
|
|
600
|
+
async generate() {
|
|
601
|
+
await mkdir(this.outputDir, { recursive: true });
|
|
602
|
+
const plugins = await this.scanPlugins();
|
|
603
|
+
const baseApiInterface = "BasePluginApi";
|
|
604
|
+
const declarations = this.generateDeclarations(plugins, baseApiInterface);
|
|
605
|
+
const moduleExports = this.generateModuleExports(plugins);
|
|
606
|
+
const dtsPath = join(this.outputDir, "plugin-registry.d.ts");
|
|
607
|
+
const indexPath = join(this.outputDir, "index.d.ts");
|
|
608
|
+
await writeFile(dtsPath, declarations, "utf-8");
|
|
609
|
+
await writeFile(indexPath, moduleExports, "utf-8");
|
|
610
|
+
console.log(`Generated ${dtsPath} with ${plugins.length} plugins`);
|
|
611
|
+
console.log(`Generated ${indexPath}`);
|
|
612
|
+
return plugins;
|
|
613
|
+
}
|
|
614
|
+
async getPlugins() {
|
|
615
|
+
return this.scanPlugins();
|
|
616
|
+
}
|
|
617
|
+
static async convertClassToArkType(classFilePath) {
|
|
618
|
+
try {
|
|
619
|
+
const content = await readFile(classFilePath, "utf-8");
|
|
620
|
+
const sourceFile = ts.createSourceFile(classFilePath, content, ts.ScriptTarget.Latest, true);
|
|
621
|
+
let className = "";
|
|
622
|
+
let properties = [];
|
|
623
|
+
const visit = (node) => {
|
|
624
|
+
if (ts.isClassDeclaration(node) && node.name) {
|
|
625
|
+
className = node.name.text;
|
|
626
|
+
const tempGenerator = new PluginTypeGenerator({
|
|
627
|
+
pluginsDir: "",
|
|
628
|
+
outputDir: ""
|
|
629
|
+
});
|
|
630
|
+
properties = tempGenerator.extractClassProperties(sourceFile);
|
|
631
|
+
}
|
|
632
|
+
ts.forEachChild(node, visit);
|
|
633
|
+
};
|
|
634
|
+
ts.forEachChild(sourceFile, visit);
|
|
635
|
+
if (!className || properties.length === 0) {
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
const schemaName = className + "Schema";
|
|
639
|
+
return {
|
|
640
|
+
schemaName,
|
|
641
|
+
schemaDefinition: ArkTypeConverter.classToArkType(className, properties),
|
|
642
|
+
typeDefinition: "",
|
|
643
|
+
properties
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
catch (error) {
|
|
647
|
+
console.error(`Error converting class to ArkType: ${error}`);
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Generates plugin types from a plugins directory.
|
|
654
|
+
*/
|
|
655
|
+
export async function generatePluginTypes(pluginsDir = join(process.cwd(), "plugins"), outputDir = join(process.cwd(), "plugin-types"), packageName = "bun_plugins") {
|
|
656
|
+
const generator = new PluginTypeGenerator({ pluginsDir, outputDir, packageName });
|
|
657
|
+
return generator.generate();
|
|
658
|
+
}
|
|
659
|
+
//# sourceMappingURL=plugin_generator.js.map
|