create-gitton-plugin 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +839 -0
- package/package.json +45 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import * as p2 from "@clack/prompts";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import path2 from "path";
|
|
8
|
+
|
|
9
|
+
// src/prompts.ts
|
|
10
|
+
import * as p from "@clack/prompts";
|
|
11
|
+
var EXTENSION_POINTS = [
|
|
12
|
+
{ value: "sidebar", label: "Sidebar Panel", hint: "Add a panel to the sidebar" },
|
|
13
|
+
{ value: "settingsTab", label: "Settings Tab", hint: "Add a tab in the settings page" },
|
|
14
|
+
{ value: "repositorySettings", label: "Repository Settings", hint: "Add settings specific to repository" },
|
|
15
|
+
{ value: "markdown", label: "Markdown Renderer", hint: "Custom markdown rendering" },
|
|
16
|
+
{ value: "editor", label: "Editor", hint: "Custom file editor" }
|
|
17
|
+
];
|
|
18
|
+
var PERMISSIONS = [
|
|
19
|
+
{ value: "ui:sidebar", label: "UI: Sidebar", hint: "Display UI in sidebar" },
|
|
20
|
+
{ value: "ui:settings", label: "UI: Settings", hint: "Display UI in settings" },
|
|
21
|
+
{ value: "ui:repositorySettings", label: "UI: Repository Settings", hint: "Display UI in repository settings" },
|
|
22
|
+
{ value: "ui:markdown", label: "UI: Markdown", hint: "Render markdown content" },
|
|
23
|
+
{ value: "ui:editor", label: "UI: Editor", hint: "Provide custom editor" },
|
|
24
|
+
{ value: "settings:read", label: "Settings: Read", hint: "Read plugin settings" },
|
|
25
|
+
{ value: "settings:write", label: "Settings: Write", hint: "Write plugin settings" },
|
|
26
|
+
{ value: "network:fetch", label: "Network: Fetch", hint: "Make network requests" },
|
|
27
|
+
{ value: "git:read", label: "Git: Read", hint: "Read git repository" },
|
|
28
|
+
{ value: "git:write", label: "Git: Write", hint: "Write to git repository" },
|
|
29
|
+
{ value: "git:hooks", label: "Git: Hooks", hint: "Manage git hooks" }
|
|
30
|
+
];
|
|
31
|
+
function getDefaultPermissions(extensionPoints) {
|
|
32
|
+
const permissions = [];
|
|
33
|
+
for (const ep of extensionPoints) {
|
|
34
|
+
switch (ep) {
|
|
35
|
+
case "sidebar":
|
|
36
|
+
permissions.push("ui:sidebar");
|
|
37
|
+
break;
|
|
38
|
+
case "settingsTab":
|
|
39
|
+
permissions.push("ui:settings");
|
|
40
|
+
break;
|
|
41
|
+
case "repositorySettings":
|
|
42
|
+
permissions.push("ui:repositorySettings");
|
|
43
|
+
break;
|
|
44
|
+
case "markdown":
|
|
45
|
+
permissions.push("ui:markdown");
|
|
46
|
+
break;
|
|
47
|
+
case "editor":
|
|
48
|
+
permissions.push("ui:editor");
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return [...new Set(permissions)];
|
|
53
|
+
}
|
|
54
|
+
async function runPrompts(options) {
|
|
55
|
+
p.intro("Create Gitton Plugin");
|
|
56
|
+
if (options.yes) {
|
|
57
|
+
const name2 = options.name || "my-plugin";
|
|
58
|
+
const extensionPoints2 = options.extensionPoints ? options.extensionPoints.split(",") : ["sidebar"];
|
|
59
|
+
const permissions2 = options.permissions ? options.permissions.split(",") : getDefaultPermissions(extensionPoints2);
|
|
60
|
+
return {
|
|
61
|
+
name: name2,
|
|
62
|
+
displayName: options.displayName || name2.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" "),
|
|
63
|
+
description: options.description || `A Gitton plugin`,
|
|
64
|
+
author: options.author || "Your Name",
|
|
65
|
+
extensionPoints: extensionPoints2,
|
|
66
|
+
permissions: permissions2,
|
|
67
|
+
useTailwind: options.tailwind ?? true,
|
|
68
|
+
useReact: options.react ?? true
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const name = options.name ?? await p.text({
|
|
72
|
+
message: "Plugin name (kebab-case):",
|
|
73
|
+
placeholder: "my-awesome-plugin",
|
|
74
|
+
validate: (value) => {
|
|
75
|
+
if (!value) return "Name is required";
|
|
76
|
+
if (!/^[a-z][a-z0-9-]*$/.test(value)) {
|
|
77
|
+
return "Name must be kebab-case (lowercase letters, numbers, hyphens)";
|
|
78
|
+
}
|
|
79
|
+
return void 0;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
if (p.isCancel(name)) {
|
|
83
|
+
p.cancel("Operation cancelled");
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const displayName = options.displayName ?? await p.text({
|
|
87
|
+
message: "Display name:",
|
|
88
|
+
placeholder: "My Awesome Plugin",
|
|
89
|
+
initialValue: String(name).split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ")
|
|
90
|
+
});
|
|
91
|
+
if (p.isCancel(displayName)) {
|
|
92
|
+
p.cancel("Operation cancelled");
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const description = options.description ?? await p.text({
|
|
96
|
+
message: "Description:",
|
|
97
|
+
placeholder: "A brief description of your plugin"
|
|
98
|
+
});
|
|
99
|
+
if (p.isCancel(description)) {
|
|
100
|
+
p.cancel("Operation cancelled");
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const author = options.author ?? await p.text({
|
|
104
|
+
message: "Author:",
|
|
105
|
+
placeholder: "Your Name"
|
|
106
|
+
});
|
|
107
|
+
if (p.isCancel(author)) {
|
|
108
|
+
p.cancel("Operation cancelled");
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const extensionPoints = options.extensionPoints ? options.extensionPoints.split(",") : await p.multiselect({
|
|
112
|
+
message: "Extension points (select with space):",
|
|
113
|
+
options: EXTENSION_POINTS,
|
|
114
|
+
required: true
|
|
115
|
+
});
|
|
116
|
+
if (p.isCancel(extensionPoints)) {
|
|
117
|
+
p.cancel("Operation cancelled");
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const defaultPermissions = getDefaultPermissions(extensionPoints);
|
|
121
|
+
const permissions = options.permissions ? options.permissions.split(",") : await p.multiselect({
|
|
122
|
+
message: "Permissions (select with space):",
|
|
123
|
+
options: PERMISSIONS,
|
|
124
|
+
initialValues: defaultPermissions,
|
|
125
|
+
required: true
|
|
126
|
+
});
|
|
127
|
+
if (p.isCancel(permissions)) {
|
|
128
|
+
p.cancel("Operation cancelled");
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const useReact = options.react ?? await p.confirm({
|
|
132
|
+
message: "Use React?",
|
|
133
|
+
initialValue: true
|
|
134
|
+
});
|
|
135
|
+
if (p.isCancel(useReact)) {
|
|
136
|
+
p.cancel("Operation cancelled");
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const useTailwind = options.tailwind ?? await p.confirm({
|
|
140
|
+
message: "Use Tailwind CSS?",
|
|
141
|
+
initialValue: true
|
|
142
|
+
});
|
|
143
|
+
if (p.isCancel(useTailwind)) {
|
|
144
|
+
p.cancel("Operation cancelled");
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
name: String(name),
|
|
149
|
+
displayName: String(displayName),
|
|
150
|
+
description: String(description),
|
|
151
|
+
author: String(author),
|
|
152
|
+
extensionPoints,
|
|
153
|
+
permissions,
|
|
154
|
+
useTailwind: Boolean(useTailwind),
|
|
155
|
+
useReact: Boolean(useReact)
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/generator.ts
|
|
160
|
+
import fs from "fs/promises";
|
|
161
|
+
import path from "path";
|
|
162
|
+
async function generatePlugin(config, outputDir) {
|
|
163
|
+
const pluginDir = path.join(outputDir, `plugin-${config.name}`);
|
|
164
|
+
await fs.mkdir(pluginDir, { recursive: true });
|
|
165
|
+
await fs.mkdir(path.join(pluginDir, "src"), { recursive: true });
|
|
166
|
+
await fs.writeFile(
|
|
167
|
+
path.join(pluginDir, "package.json"),
|
|
168
|
+
generatePackageJson(config)
|
|
169
|
+
);
|
|
170
|
+
await fs.writeFile(
|
|
171
|
+
path.join(pluginDir, "tsconfig.json"),
|
|
172
|
+
generateTsConfig()
|
|
173
|
+
);
|
|
174
|
+
await fs.writeFile(
|
|
175
|
+
path.join(pluginDir, "vite.config.ts"),
|
|
176
|
+
generateViteConfig(config)
|
|
177
|
+
);
|
|
178
|
+
if (config.useTailwind) {
|
|
179
|
+
await fs.writeFile(
|
|
180
|
+
path.join(pluginDir, "tailwind.config.js"),
|
|
181
|
+
generateTailwindConfig()
|
|
182
|
+
);
|
|
183
|
+
await fs.writeFile(
|
|
184
|
+
path.join(pluginDir, "postcss.config.js"),
|
|
185
|
+
generatePostcssConfig()
|
|
186
|
+
);
|
|
187
|
+
await fs.writeFile(
|
|
188
|
+
path.join(pluginDir, "src", "globals.css"),
|
|
189
|
+
generateGlobalsCss()
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
await fs.mkdir(path.join(pluginDir, "src", "types"), { recursive: true });
|
|
193
|
+
await fs.writeFile(
|
|
194
|
+
path.join(pluginDir, "src", "types", "gitton.ts"),
|
|
195
|
+
generateGittonTypes()
|
|
196
|
+
);
|
|
197
|
+
if (config.useTailwind) {
|
|
198
|
+
await fs.mkdir(path.join(pluginDir, "src", "lib"), { recursive: true });
|
|
199
|
+
await fs.writeFile(
|
|
200
|
+
path.join(pluginDir, "src", "lib", "utils.ts"),
|
|
201
|
+
generateUtils()
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
await fs.writeFile(
|
|
205
|
+
path.join(pluginDir, ".gitignore"),
|
|
206
|
+
generateGitignore()
|
|
207
|
+
);
|
|
208
|
+
await fs.writeFile(
|
|
209
|
+
path.join(pluginDir, ".npmignore"),
|
|
210
|
+
generateNpmignore()
|
|
211
|
+
);
|
|
212
|
+
for (const ep of config.extensionPoints) {
|
|
213
|
+
await generateExtensionPoint(pluginDir, ep, config);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function generatePackageJson(config) {
|
|
217
|
+
const gittonConfig = {
|
|
218
|
+
displayName: config.displayName,
|
|
219
|
+
version: "0.0.0",
|
|
220
|
+
description: config.description,
|
|
221
|
+
author: config.author,
|
|
222
|
+
permissions: config.permissions,
|
|
223
|
+
extensionPoints: {}
|
|
224
|
+
};
|
|
225
|
+
const extensionPoints = {};
|
|
226
|
+
for (const ep of config.extensionPoints) {
|
|
227
|
+
switch (ep) {
|
|
228
|
+
case "sidebar":
|
|
229
|
+
extensionPoints.sidebar = {
|
|
230
|
+
entry: "ui/src/sidebar/index.html",
|
|
231
|
+
icon: "puzzle",
|
|
232
|
+
position: "bottom"
|
|
233
|
+
};
|
|
234
|
+
break;
|
|
235
|
+
case "settingsTab":
|
|
236
|
+
extensionPoints.settingsTab = {
|
|
237
|
+
entry: "ui/src/settings/index.html",
|
|
238
|
+
label: config.displayName
|
|
239
|
+
};
|
|
240
|
+
break;
|
|
241
|
+
case "repositorySettings":
|
|
242
|
+
extensionPoints.repositorySettings = {
|
|
243
|
+
entry: "ui/src/repository-settings/index.html",
|
|
244
|
+
label: config.displayName
|
|
245
|
+
};
|
|
246
|
+
break;
|
|
247
|
+
case "markdown":
|
|
248
|
+
extensionPoints.markdown = {
|
|
249
|
+
name: config.displayName,
|
|
250
|
+
filePatterns: ["*.md"],
|
|
251
|
+
priority: 10,
|
|
252
|
+
entry: "ui/src/markdown/index.html"
|
|
253
|
+
};
|
|
254
|
+
break;
|
|
255
|
+
case "editor":
|
|
256
|
+
extensionPoints.editor = {
|
|
257
|
+
name: config.displayName,
|
|
258
|
+
filePatterns: ["*"],
|
|
259
|
+
priority: 1,
|
|
260
|
+
entry: "ui/src/editor/index.html"
|
|
261
|
+
};
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
gittonConfig.extensionPoints = extensionPoints;
|
|
266
|
+
const devDependencies = {
|
|
267
|
+
typescript: "^5.7.3",
|
|
268
|
+
vite: "^6.0.11"
|
|
269
|
+
};
|
|
270
|
+
const dependencies = {};
|
|
271
|
+
if (config.useReact) {
|
|
272
|
+
devDependencies["@types/react"] = "^18.3.18";
|
|
273
|
+
devDependencies["@types/react-dom"] = "^18.3.5";
|
|
274
|
+
devDependencies["@vitejs/plugin-react"] = "^4.3.4";
|
|
275
|
+
dependencies.react = "^18.3.1";
|
|
276
|
+
dependencies["react-dom"] = "^18.3.1";
|
|
277
|
+
}
|
|
278
|
+
if (config.useTailwind) {
|
|
279
|
+
devDependencies.autoprefixer = "^10.4.20";
|
|
280
|
+
devDependencies.postcss = "^8.5.1";
|
|
281
|
+
devDependencies.tailwindcss = "^3.4.17";
|
|
282
|
+
dependencies.clsx = "^2.1.1";
|
|
283
|
+
dependencies["tailwind-merge"] = "^2.6.0";
|
|
284
|
+
}
|
|
285
|
+
const pkg = {
|
|
286
|
+
name: `@gitton-dev/plugin-${config.name}`,
|
|
287
|
+
version: "0.0.1",
|
|
288
|
+
description: config.description,
|
|
289
|
+
type: "module",
|
|
290
|
+
scripts: {
|
|
291
|
+
dev: "vite",
|
|
292
|
+
build: "vite build",
|
|
293
|
+
preview: "vite preview",
|
|
294
|
+
clean: "rm -rf dist ui node_modules/.vite"
|
|
295
|
+
},
|
|
296
|
+
gitton: gittonConfig,
|
|
297
|
+
files: ["ui", "package.json"],
|
|
298
|
+
keywords: ["gitton", "gitton-plugin", config.name],
|
|
299
|
+
author: config.author,
|
|
300
|
+
license: "MIT",
|
|
301
|
+
repository: {
|
|
302
|
+
type: "git",
|
|
303
|
+
url: "git@github.com:gitton-dev/gitton-plugins.git",
|
|
304
|
+
directory: `packages/plugin-${config.name}`
|
|
305
|
+
},
|
|
306
|
+
bugs: {
|
|
307
|
+
url: "https://github.com/gitton-dev/gitton-plugins/issues"
|
|
308
|
+
},
|
|
309
|
+
homepage: "https://jsers.dev/service/gitton",
|
|
310
|
+
devDependencies,
|
|
311
|
+
dependencies
|
|
312
|
+
};
|
|
313
|
+
return JSON.stringify(pkg, null, 2) + "\n";
|
|
314
|
+
}
|
|
315
|
+
function generateTsConfig() {
|
|
316
|
+
const tsconfig = {
|
|
317
|
+
compilerOptions: {
|
|
318
|
+
target: "ES2020",
|
|
319
|
+
useDefineForClassFields: true,
|
|
320
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
321
|
+
module: "ESNext",
|
|
322
|
+
skipLibCheck: true,
|
|
323
|
+
moduleResolution: "bundler",
|
|
324
|
+
allowImportingTsExtensions: true,
|
|
325
|
+
resolveJsonModule: true,
|
|
326
|
+
isolatedModules: true,
|
|
327
|
+
noEmit: true,
|
|
328
|
+
jsx: "react-jsx",
|
|
329
|
+
strict: true,
|
|
330
|
+
noUnusedLocals: true,
|
|
331
|
+
noUnusedParameters: true,
|
|
332
|
+
noFallthroughCasesInSwitch: true,
|
|
333
|
+
baseUrl: ".",
|
|
334
|
+
paths: {
|
|
335
|
+
"@/*": ["src/*"]
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
include: ["src"]
|
|
339
|
+
};
|
|
340
|
+
return JSON.stringify(tsconfig, null, 2) + "\n";
|
|
341
|
+
}
|
|
342
|
+
function generateViteConfig(config) {
|
|
343
|
+
const inputs = [];
|
|
344
|
+
for (const ep of config.extensionPoints) {
|
|
345
|
+
switch (ep) {
|
|
346
|
+
case "sidebar":
|
|
347
|
+
inputs.push(`sidebar: resolve(__dirname, 'src/sidebar/index.html')`);
|
|
348
|
+
break;
|
|
349
|
+
case "settingsTab":
|
|
350
|
+
inputs.push(`settings: resolve(__dirname, 'src/settings/index.html')`);
|
|
351
|
+
break;
|
|
352
|
+
case "repositorySettings":
|
|
353
|
+
inputs.push(`repositorySettings: resolve(__dirname, 'src/repository-settings/index.html')`);
|
|
354
|
+
break;
|
|
355
|
+
case "markdown":
|
|
356
|
+
inputs.push(`markdown: resolve(__dirname, 'src/markdown/index.html')`);
|
|
357
|
+
break;
|
|
358
|
+
case "editor":
|
|
359
|
+
inputs.push(`editor: resolve(__dirname, 'src/editor/index.html')`);
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const plugins = config.useReact ? `[react()]` : `[]`;
|
|
364
|
+
const imports = config.useReact ? `import { defineConfig } from 'vite'
|
|
365
|
+
import react from '@vitejs/plugin-react'
|
|
366
|
+
import { resolve } from 'path'` : `import { defineConfig } from 'vite'
|
|
367
|
+
import { resolve } from 'path'`;
|
|
368
|
+
return `${imports}
|
|
369
|
+
|
|
370
|
+
export default defineConfig({
|
|
371
|
+
plugins: ${plugins},
|
|
372
|
+
base: './',
|
|
373
|
+
server: {
|
|
374
|
+
port: 5273
|
|
375
|
+
},
|
|
376
|
+
build: {
|
|
377
|
+
outDir: 'ui',
|
|
378
|
+
emptyOutDir: true,
|
|
379
|
+
rollupOptions: {
|
|
380
|
+
input: {
|
|
381
|
+
${inputs.join(",\n ")}
|
|
382
|
+
},
|
|
383
|
+
output: {
|
|
384
|
+
entryFileNames: 'assets/[name]-[hash].js',
|
|
385
|
+
chunkFileNames: 'assets/[name]-[hash].js',
|
|
386
|
+
assetFileNames: 'assets/[name]-[hash].[ext]'
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
resolve: {
|
|
391
|
+
alias: {
|
|
392
|
+
'@': resolve(__dirname, 'src')
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
})
|
|
396
|
+
`;
|
|
397
|
+
}
|
|
398
|
+
function generateTailwindConfig() {
|
|
399
|
+
return `/** @type {import('tailwindcss').Config} */
|
|
400
|
+
export default {
|
|
401
|
+
darkMode: 'media',
|
|
402
|
+
content: [
|
|
403
|
+
'./src/**/*.{js,ts,jsx,tsx,html}'
|
|
404
|
+
],
|
|
405
|
+
theme: {
|
|
406
|
+
extend: {
|
|
407
|
+
colors: {
|
|
408
|
+
border: 'hsl(var(--border))',
|
|
409
|
+
input: 'hsl(var(--input))',
|
|
410
|
+
ring: 'hsl(var(--ring))',
|
|
411
|
+
background: 'hsl(var(--background))',
|
|
412
|
+
foreground: 'hsl(var(--foreground))',
|
|
413
|
+
primary: {
|
|
414
|
+
DEFAULT: 'hsl(var(--primary))',
|
|
415
|
+
foreground: 'hsl(var(--primary-foreground))'
|
|
416
|
+
},
|
|
417
|
+
secondary: {
|
|
418
|
+
DEFAULT: 'hsl(var(--secondary))',
|
|
419
|
+
foreground: 'hsl(var(--secondary-foreground))'
|
|
420
|
+
},
|
|
421
|
+
destructive: {
|
|
422
|
+
DEFAULT: 'hsl(var(--destructive))',
|
|
423
|
+
foreground: 'hsl(var(--destructive-foreground))'
|
|
424
|
+
},
|
|
425
|
+
muted: {
|
|
426
|
+
DEFAULT: 'hsl(var(--muted))',
|
|
427
|
+
foreground: 'hsl(var(--muted-foreground))'
|
|
428
|
+
},
|
|
429
|
+
accent: {
|
|
430
|
+
DEFAULT: 'hsl(var(--accent))',
|
|
431
|
+
foreground: 'hsl(var(--accent-foreground))'
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
borderRadius: {
|
|
435
|
+
lg: 'var(--radius)',
|
|
436
|
+
md: 'calc(var(--radius) - 2px)',
|
|
437
|
+
sm: 'calc(var(--radius) - 4px)'
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
plugins: []
|
|
442
|
+
}
|
|
443
|
+
`;
|
|
444
|
+
}
|
|
445
|
+
function generatePostcssConfig() {
|
|
446
|
+
return `export default {
|
|
447
|
+
plugins: {
|
|
448
|
+
tailwindcss: {},
|
|
449
|
+
autoprefixer: {}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
`;
|
|
453
|
+
}
|
|
454
|
+
function generateGitignore() {
|
|
455
|
+
return `# Dependencies
|
|
456
|
+
node_modules/
|
|
457
|
+
|
|
458
|
+
# Build output
|
|
459
|
+
dist/
|
|
460
|
+
ui/
|
|
461
|
+
|
|
462
|
+
# Editor
|
|
463
|
+
.vscode/
|
|
464
|
+
.idea/
|
|
465
|
+
*.swp
|
|
466
|
+
*.swo
|
|
467
|
+
|
|
468
|
+
# OS
|
|
469
|
+
.DS_Store
|
|
470
|
+
Thumbs.db
|
|
471
|
+
|
|
472
|
+
# Logs
|
|
473
|
+
*.log
|
|
474
|
+
npm-debug.log*
|
|
475
|
+
|
|
476
|
+
# Cache
|
|
477
|
+
.vite/
|
|
478
|
+
*.tsbuildinfo
|
|
479
|
+
`;
|
|
480
|
+
}
|
|
481
|
+
function generateNpmignore() {
|
|
482
|
+
return `# Source files (ui/ contains built assets, so don't ignore it)
|
|
483
|
+
src/
|
|
484
|
+
|
|
485
|
+
# Config files
|
|
486
|
+
tsconfig.json
|
|
487
|
+
vite.config.ts
|
|
488
|
+
tailwind.config.js
|
|
489
|
+
postcss.config.js
|
|
490
|
+
.gitignore
|
|
491
|
+
|
|
492
|
+
# Development
|
|
493
|
+
node_modules/
|
|
494
|
+
.vite/
|
|
495
|
+
*.tsbuildinfo
|
|
496
|
+
|
|
497
|
+
# Editor
|
|
498
|
+
.vscode/
|
|
499
|
+
.idea/
|
|
500
|
+
*.swp
|
|
501
|
+
*.swo
|
|
502
|
+
|
|
503
|
+
# OS
|
|
504
|
+
.DS_Store
|
|
505
|
+
Thumbs.db
|
|
506
|
+
|
|
507
|
+
# Logs
|
|
508
|
+
*.log
|
|
509
|
+
npm-debug.log*
|
|
510
|
+
`;
|
|
511
|
+
}
|
|
512
|
+
function generateGlobalsCss() {
|
|
513
|
+
return `@tailwind base;
|
|
514
|
+
@tailwind components;
|
|
515
|
+
@tailwind utilities;
|
|
516
|
+
|
|
517
|
+
@layer base {
|
|
518
|
+
:root {
|
|
519
|
+
--background: 0 0% 100%;
|
|
520
|
+
--foreground: 0 0% 3.9%;
|
|
521
|
+
--muted: 0 0% 96.1%;
|
|
522
|
+
--muted-foreground: 0 0% 45.1%;
|
|
523
|
+
--popover: 0 0% 100%;
|
|
524
|
+
--popover-foreground: 0 0% 3.9%;
|
|
525
|
+
--card: 0 0% 100%;
|
|
526
|
+
--card-foreground: 0 0% 3.9%;
|
|
527
|
+
--border: 0 0% 89.8%;
|
|
528
|
+
--input: 0 0% 89.8%;
|
|
529
|
+
--primary: 0 0% 9%;
|
|
530
|
+
--primary-foreground: 0 0% 98%;
|
|
531
|
+
--secondary: 0 0% 96.1%;
|
|
532
|
+
--secondary-foreground: 0 0% 9%;
|
|
533
|
+
--accent: 0 0% 96.1%;
|
|
534
|
+
--accent-foreground: 0 0% 9%;
|
|
535
|
+
--destructive: 0 84.2% 60.2%;
|
|
536
|
+
--destructive-foreground: 0 0% 98%;
|
|
537
|
+
--ring: 0 0% 3.9%;
|
|
538
|
+
--radius: 0.5rem;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
:root.dark,
|
|
542
|
+
.dark {
|
|
543
|
+
--background: 0 0% 3.9%;
|
|
544
|
+
--foreground: 0 0% 98%;
|
|
545
|
+
--muted: 0 0% 14.9%;
|
|
546
|
+
--muted-foreground: 0 0% 63.9%;
|
|
547
|
+
--popover: 0 0% 3.9%;
|
|
548
|
+
--popover-foreground: 0 0% 98%;
|
|
549
|
+
--card: 0 0% 3.9%;
|
|
550
|
+
--card-foreground: 0 0% 98%;
|
|
551
|
+
--border: 0 0% 14.9%;
|
|
552
|
+
--input: 0 0% 14.9%;
|
|
553
|
+
--primary: 0 0% 98%;
|
|
554
|
+
--primary-foreground: 0 0% 9%;
|
|
555
|
+
--secondary: 0 0% 14.9%;
|
|
556
|
+
--secondary-foreground: 0 0% 98%;
|
|
557
|
+
--accent: 0 0% 14.9%;
|
|
558
|
+
--accent-foreground: 0 0% 98%;
|
|
559
|
+
--destructive: 0 62.8% 30.6%;
|
|
560
|
+
--destructive-foreground: 0 0% 98%;
|
|
561
|
+
--ring: 0 0% 83.1%;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
@media (prefers-color-scheme: dark) {
|
|
566
|
+
:root:not(.light) {
|
|
567
|
+
--background: 0 0% 3.9%;
|
|
568
|
+
--foreground: 0 0% 98%;
|
|
569
|
+
--muted: 0 0% 14.9%;
|
|
570
|
+
--muted-foreground: 0 0% 63.9%;
|
|
571
|
+
--popover: 0 0% 3.9%;
|
|
572
|
+
--popover-foreground: 0 0% 98%;
|
|
573
|
+
--card: 0 0% 3.9%;
|
|
574
|
+
--card-foreground: 0 0% 98%;
|
|
575
|
+
--border: 0 0% 14.9%;
|
|
576
|
+
--input: 0 0% 14.9%;
|
|
577
|
+
--primary: 0 0% 98%;
|
|
578
|
+
--primary-foreground: 0 0% 9%;
|
|
579
|
+
--secondary: 0 0% 14.9%;
|
|
580
|
+
--secondary-foreground: 0 0% 98%;
|
|
581
|
+
--accent: 0 0% 14.9%;
|
|
582
|
+
--accent-foreground: 0 0% 98%;
|
|
583
|
+
--destructive: 0 62.8% 30.6%;
|
|
584
|
+
--destructive-foreground: 0 0% 98%;
|
|
585
|
+
--ring: 0 0% 83.1%;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
@layer base {
|
|
590
|
+
* {
|
|
591
|
+
@apply border-border;
|
|
592
|
+
}
|
|
593
|
+
body {
|
|
594
|
+
@apply bg-background text-foreground;
|
|
595
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
`;
|
|
599
|
+
}
|
|
600
|
+
function generateGittonTypes() {
|
|
601
|
+
return `export interface GhRunResult {
|
|
602
|
+
success: boolean
|
|
603
|
+
stdout: string
|
|
604
|
+
stderr: string
|
|
605
|
+
error?: string
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
export interface FsResult {
|
|
609
|
+
success: boolean
|
|
610
|
+
error?: string
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
export interface FsReadResult extends FsResult {
|
|
614
|
+
content?: string
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
export interface FsReaddirEntry {
|
|
618
|
+
name: string
|
|
619
|
+
isDirectory: boolean
|
|
620
|
+
isFile: boolean
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
export interface FsReaddirResult extends FsResult {
|
|
624
|
+
entries: FsReaddirEntry[]
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
export interface FsExistsResult {
|
|
628
|
+
exists: boolean
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
export interface FsStatResult extends FsResult {
|
|
632
|
+
stat?: {
|
|
633
|
+
size: number
|
|
634
|
+
mode: number
|
|
635
|
+
isFile: boolean
|
|
636
|
+
isDirectory: boolean
|
|
637
|
+
mtime: string
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
export interface GittonSettings {
|
|
642
|
+
get(key: string): Promise<unknown>
|
|
643
|
+
set(key: string, value: unknown): Promise<void>
|
|
644
|
+
getAll(): Promise<Record<string, unknown>>
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
export interface GittonUI {
|
|
648
|
+
showNotification(message: string, type?: 'info' | 'error' | 'warning'): void
|
|
649
|
+
openExternal(url: string): Promise<{ success: boolean }>
|
|
650
|
+
getTheme(): Promise<{ theme: 'light' | 'dark' }>
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
export interface GittonGh {
|
|
654
|
+
run(args: string[]): Promise<GhRunResult>
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
export interface GittonFs {
|
|
658
|
+
readFile(path: string): Promise<FsReadResult>
|
|
659
|
+
writeFile(path: string, content: string): Promise<FsResult>
|
|
660
|
+
readdir(path: string): Promise<FsReaddirResult>
|
|
661
|
+
exists(path: string): Promise<FsExistsResult>
|
|
662
|
+
unlink(path: string): Promise<FsResult>
|
|
663
|
+
chmod(path: string, mode: number): Promise<FsResult>
|
|
664
|
+
stat(path: string): Promise<FsStatResult>
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
export interface GittonContext {
|
|
668
|
+
repoPath: string | null
|
|
669
|
+
theme: 'light' | 'dark'
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
export interface GittonAPI {
|
|
673
|
+
pluginId: string
|
|
674
|
+
settings: GittonSettings
|
|
675
|
+
ui: GittonUI
|
|
676
|
+
gh: GittonGh
|
|
677
|
+
fs: GittonFs
|
|
678
|
+
context: GittonContext
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
declare global {
|
|
682
|
+
interface Window {
|
|
683
|
+
gitton: GittonAPI
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
`;
|
|
687
|
+
}
|
|
688
|
+
function generateUtils() {
|
|
689
|
+
return `import { type ClassValue, clsx } from 'clsx'
|
|
690
|
+
import { twMerge } from 'tailwind-merge'
|
|
691
|
+
|
|
692
|
+
export function cn(...inputs: ClassValue[]) {
|
|
693
|
+
return twMerge(clsx(inputs))
|
|
694
|
+
}
|
|
695
|
+
`;
|
|
696
|
+
}
|
|
697
|
+
async function generateExtensionPoint(pluginDir, extensionPoint, config) {
|
|
698
|
+
const dirName = getExtensionPointDir(extensionPoint);
|
|
699
|
+
const dir = path.join(pluginDir, "src", dirName);
|
|
700
|
+
await fs.mkdir(dir, { recursive: true });
|
|
701
|
+
await fs.writeFile(
|
|
702
|
+
path.join(dir, "index.html"),
|
|
703
|
+
generateIndexHtml(config.displayName, extensionPoint)
|
|
704
|
+
);
|
|
705
|
+
if (config.useReact) {
|
|
706
|
+
await fs.writeFile(
|
|
707
|
+
path.join(dir, "main.tsx"),
|
|
708
|
+
generateReactMain(config, extensionPoint)
|
|
709
|
+
);
|
|
710
|
+
await fs.mkdir(path.join(dir, "components"), { recursive: true });
|
|
711
|
+
await fs.writeFile(
|
|
712
|
+
path.join(dir, "components", `${getComponentName(extensionPoint)}.tsx`),
|
|
713
|
+
generateReactComponent(config, extensionPoint)
|
|
714
|
+
);
|
|
715
|
+
} else {
|
|
716
|
+
await fs.writeFile(
|
|
717
|
+
path.join(dir, "main.ts"),
|
|
718
|
+
generateVanillaMain(config, extensionPoint)
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
function getExtensionPointDir(ep) {
|
|
723
|
+
switch (ep) {
|
|
724
|
+
case "sidebar":
|
|
725
|
+
return "sidebar";
|
|
726
|
+
case "settingsTab":
|
|
727
|
+
return "settings";
|
|
728
|
+
case "repositorySettings":
|
|
729
|
+
return "repository-settings";
|
|
730
|
+
case "markdown":
|
|
731
|
+
return "markdown";
|
|
732
|
+
case "editor":
|
|
733
|
+
return "editor";
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
function getComponentName(ep) {
|
|
737
|
+
switch (ep) {
|
|
738
|
+
case "sidebar":
|
|
739
|
+
return "Sidebar";
|
|
740
|
+
case "settingsTab":
|
|
741
|
+
return "Settings";
|
|
742
|
+
case "repositorySettings":
|
|
743
|
+
return "RepositorySettings";
|
|
744
|
+
case "markdown":
|
|
745
|
+
return "MarkdownRenderer";
|
|
746
|
+
case "editor":
|
|
747
|
+
return "Editor";
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
function generateIndexHtml(displayName, ep) {
|
|
751
|
+
const title = `${displayName} - ${getComponentName(ep)}`;
|
|
752
|
+
return `<!DOCTYPE html>
|
|
753
|
+
<html lang="en">
|
|
754
|
+
<head>
|
|
755
|
+
<meta charset="UTF-8" />
|
|
756
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
757
|
+
<title>${title}</title>
|
|
758
|
+
</head>
|
|
759
|
+
<body>
|
|
760
|
+
<div id="root"></div>
|
|
761
|
+
<script type="module" src="./main.tsx"></script>
|
|
762
|
+
</body>
|
|
763
|
+
</html>
|
|
764
|
+
`;
|
|
765
|
+
}
|
|
766
|
+
function generateReactMain(config, ep) {
|
|
767
|
+
const componentName = getComponentName(ep);
|
|
768
|
+
const cssImport = config.useTailwind ? `import '../globals.css'
|
|
769
|
+
` : "";
|
|
770
|
+
return `import React from 'react'
|
|
771
|
+
import ReactDOM from 'react-dom/client'
|
|
772
|
+
import { ${componentName} } from './components/${componentName}'
|
|
773
|
+
${cssImport}
|
|
774
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
775
|
+
<React.StrictMode>
|
|
776
|
+
<${componentName} />
|
|
777
|
+
</React.StrictMode>
|
|
778
|
+
)
|
|
779
|
+
`;
|
|
780
|
+
}
|
|
781
|
+
function generateReactComponent(config, ep) {
|
|
782
|
+
const componentName = getComponentName(ep);
|
|
783
|
+
const baseClasses = config.useTailwind ? ' className="p-4"' : "";
|
|
784
|
+
return `export function ${componentName}() {
|
|
785
|
+
return (
|
|
786
|
+
<div${baseClasses}>
|
|
787
|
+
<h1>${config.displayName}</h1>
|
|
788
|
+
<p>${config.description}</p>
|
|
789
|
+
</div>
|
|
790
|
+
)
|
|
791
|
+
}
|
|
792
|
+
`;
|
|
793
|
+
}
|
|
794
|
+
function generateVanillaMain(config, _ep) {
|
|
795
|
+
return `const root = document.getElementById('root')
|
|
796
|
+
|
|
797
|
+
if (root) {
|
|
798
|
+
root.innerHTML = \`
|
|
799
|
+
<div>
|
|
800
|
+
<h1>${config.displayName}</h1>
|
|
801
|
+
<p>${config.description}</p>
|
|
802
|
+
</div>
|
|
803
|
+
\`
|
|
804
|
+
}
|
|
805
|
+
`;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// src/index.ts
|
|
809
|
+
var program = new Command();
|
|
810
|
+
program.name("create-gitton-plugin").description("Create a new Gitton plugin").version("0.0.1").argument("[directory]", "Output directory", ".").option("-n, --name <name>", "Plugin name (kebab-case)").option("-d, --display-name <displayName>", "Display name").option("--description <description>", "Plugin description").option("-a, --author <author>", "Author name").option("-e, --extension-points <points>", "Extension points (comma-separated: sidebar,settingsTab,repositorySettings,markdown,editor)").option("-p, --permissions <permissions>", "Permissions (comma-separated)").option("--tailwind", "Use Tailwind CSS").option("--no-tailwind", "Do not use Tailwind CSS").option("--react", "Use React").option("--no-react", "Do not use React").option("-y, --yes", "Skip prompts and use defaults").action(async (directory, options) => {
|
|
811
|
+
try {
|
|
812
|
+
const config = await runPrompts(options);
|
|
813
|
+
if (!config) {
|
|
814
|
+
process.exit(1);
|
|
815
|
+
}
|
|
816
|
+
const outputDir = path2.resolve(directory);
|
|
817
|
+
const spinner2 = p2.spinner();
|
|
818
|
+
spinner2.start("Generating plugin...");
|
|
819
|
+
await generatePlugin(config, outputDir);
|
|
820
|
+
spinner2.stop("Plugin generated!");
|
|
821
|
+
const pluginDir = path2.join(outputDir, `plugin-${config.name}`);
|
|
822
|
+
p2.note(
|
|
823
|
+
[
|
|
824
|
+
`${chalk.green("Plugin created at:")} ${pluginDir}`,
|
|
825
|
+
"",
|
|
826
|
+
`${chalk.cyan("Next steps:")}`,
|
|
827
|
+
` cd plugin-${config.name}`,
|
|
828
|
+
" pnpm install",
|
|
829
|
+
" pnpm dev"
|
|
830
|
+
].join("\n"),
|
|
831
|
+
"Done!"
|
|
832
|
+
);
|
|
833
|
+
p2.outro("Happy coding!");
|
|
834
|
+
} catch (error) {
|
|
835
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
836
|
+
process.exit(1);
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-gitton-plugin",
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "Create a new Gitton plugin with interactive prompts",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-gitton-plugin": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"gitton",
|
|
14
|
+
"gitton-plugin",
|
|
15
|
+
"create",
|
|
16
|
+
"scaffold",
|
|
17
|
+
"cli"
|
|
18
|
+
],
|
|
19
|
+
"author": "godai",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git@github.com:gitton-dev/gitton-plugins.git",
|
|
24
|
+
"directory": "packages/create-gitton-plugin"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/gitton-dev/gitton-plugins/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://jsers.dev/service/gitton",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@clack/prompts": "^0.8.0",
|
|
32
|
+
"chalk": "^5.4.1",
|
|
33
|
+
"commander": "^13.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.13.4",
|
|
37
|
+
"tsup": "^8.4.0",
|
|
38
|
+
"typescript": "^5.7.3"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
42
|
+
"dev": "tsup src/index.ts --format esm --watch",
|
|
43
|
+
"clean": "rm -rf dist"
|
|
44
|
+
}
|
|
45
|
+
}
|