create-hackhub-mod 0.1.0

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.js ADDED
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import * as p from "@clack/prompts";
5
+ import pc from "picocolors";
6
+ import fs from "fs";
7
+ import path from "path";
8
+ import { fileURLToPath } from "url";
9
+ var __filename = fileURLToPath(import.meta.url);
10
+ var __dirname = path.dirname(__filename);
11
+ function toKebabCase(str) {
12
+ return str.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
13
+ }
14
+ function toPascalCase(str) {
15
+ return str.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
16
+ }
17
+ function inject(content, vars) {
18
+ return content.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? "");
19
+ }
20
+ function copyDir(src, dest, vars) {
21
+ fs.mkdirSync(dest, { recursive: true });
22
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
23
+ const srcPath = path.join(src, entry.name);
24
+ let destName = entry.name.replace(/\.hbs$/, "");
25
+ if (destName === "_gitignore") destName = ".gitignore";
26
+ const destPath = path.join(dest, destName);
27
+ if (entry.isDirectory()) {
28
+ copyDir(srcPath, destPath, vars);
29
+ } else {
30
+ let content = fs.readFileSync(srcPath, "utf-8");
31
+ if (entry.name.endsWith(".hbs") || entry.name.endsWith(".ts") || entry.name.endsWith(".json")) {
32
+ content = inject(content, vars);
33
+ }
34
+ fs.writeFileSync(destPath, content);
35
+ }
36
+ }
37
+ }
38
+ async function main() {
39
+ console.log();
40
+ p.intro(pc.bgCyan(pc.black(" Create HackHub Mod ")));
41
+ const project = await p.group(
42
+ {
43
+ name: () => p.text({
44
+ message: "Mod name",
45
+ placeholder: "my-awesome-mod",
46
+ validate(value) {
47
+ if (!value?.trim()) return "Mod name is required.";
48
+ if (fs.existsSync(toKebabCase(value))) return `Directory "${toKebabCase(value)}" already exists.`;
49
+ }
50
+ }),
51
+ template: () => p.select({
52
+ message: "Select a template",
53
+ options: [
54
+ { value: "basic", label: "Basic", hint: "Minimal mod setup" },
55
+ { value: "quest", label: "Quest", hint: "Includes an example quest" },
56
+ { value: "website", label: "Website", hint: "Includes an in-game website" },
57
+ { value: "full", label: "Full", hint: "Quest + Website + Command + App" }
58
+ ]
59
+ }),
60
+ author: () => p.text({
61
+ message: "Author name",
62
+ placeholder: "YourName"
63
+ }),
64
+ description: () => p.text({
65
+ message: "Description",
66
+ placeholder: "A HackHub mod"
67
+ })
68
+ },
69
+ {
70
+ onCancel() {
71
+ p.cancel("Cancelled.");
72
+ process.exit(0);
73
+ }
74
+ }
75
+ );
76
+ const modId = toKebabCase(project.name);
77
+ const modName = project.name.trim();
78
+ const className = toPascalCase(modId);
79
+ const author = project.author?.trim() || "Unknown";
80
+ const description = project.description?.trim() || "A HackHub mod";
81
+ const template = project.template;
82
+ const vars = {
83
+ modId,
84
+ modName,
85
+ className,
86
+ author,
87
+ description
88
+ };
89
+ const targetDir = path.resolve(process.cwd(), modId);
90
+ const templatesDir = path.resolve(__dirname, "..", "templates");
91
+ const baseDir = path.join(templatesDir, "base");
92
+ const templateDir = path.join(templatesDir, template);
93
+ const s = p.spinner();
94
+ s.start("Scaffolding mod project...");
95
+ copyDir(baseDir, targetDir, vars);
96
+ copyDir(templateDir, targetDir, vars);
97
+ s.stop("Project scaffolded.");
98
+ const nextSteps = [
99
+ `cd ${modId}`,
100
+ "npm install",
101
+ "npm run build"
102
+ ];
103
+ p.note(nextSteps.join("\n"), "Next steps");
104
+ p.outro(pc.green(`Done! Your mod "${modName}" is ready.`));
105
+ }
106
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "create-hackhub-mod",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold a new HackHub mod project with interactive prompts",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-hackhub-mod": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "templates"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup src/index.ts --format esm --clean",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "keywords": [
18
+ "hackhub",
19
+ "modding",
20
+ "scaffold",
21
+ "create",
22
+ "cli",
23
+ "hotbunny"
24
+ ],
25
+ "author": "Hot Bunny Games",
26
+ "license": "MIT",
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "devDependencies": {
31
+ "tsup": "^8.0.0",
32
+ "typescript": "^5.0.0"
33
+ },
34
+ "dependencies": {
35
+ "@clack/prompts": "^0.10.0",
36
+ "picocolors": "^1.1.0"
37
+ }
38
+ }
@@ -0,0 +1,6 @@
1
+ node_modules/
2
+ dist/
3
+ mod.js
4
+ *.tgz
5
+ .DS_Store
6
+ Thumbs.db
@@ -0,0 +1,22 @@
1
+ import { build, context } from "esbuild";
2
+
3
+ const isWatch = process.argv.includes("--watch");
4
+
5
+ const config = {
6
+ entryPoints: ["src/index.ts"],
7
+ bundle: true,
8
+ outfile: "mod.js",
9
+ format: "cjs" as const,
10
+ platform: "neutral" as const,
11
+ target: "es2020",
12
+ external: ["@hotbunny/hackhub-content-sdk"],
13
+ };
14
+
15
+ if (isWatch) {
16
+ const ctx = await context(config);
17
+ await ctx.watch();
18
+ console.log("Watching for changes...");
19
+ } else {
20
+ await build(config);
21
+ console.log("Build complete: mod.js");
22
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "{{modId}}",
3
+ "name": "{{modName}}",
4
+ "version": "1.0.0",
5
+ "author": "{{author}}",
6
+ "description": "{{description}}",
7
+ "entry": "mod.js",
8
+ "permissions": ["network", "files", "events"],
9
+ "dependencies": []
10
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "{{modId}}",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "build": "tsx esbuild.config.ts",
7
+ "dev": "tsx esbuild.config.ts --watch"
8
+ },
9
+ "devDependencies": {
10
+ "@hotbunny/hackhub-content-sdk": "latest",
11
+ "esbuild": "^0.25.0",
12
+ "tsx": "^4.0.0",
13
+ "typescript": "^5.0.0"
14
+ }
15
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "experimentalDecorators": true,
10
+ "emitDecoratorMetadata": true,
11
+ "outDir": "dist",
12
+ "rootDir": "src",
13
+ "declaration": false
14
+ },
15
+ "include": ["src"]
16
+ }
@@ -0,0 +1,8 @@
1
+ import { Bootstrap, RegisterModPackage } from "@hotbunny/hackhub-content-sdk";
2
+
3
+ @RegisterModPackage()
4
+ export default class {{className}} extends Bootstrap {
5
+ OnModPackageLoaded() {
6
+ console.log("{{modName}} loaded!");
7
+ }
8
+ }
@@ -0,0 +1,115 @@
1
+ import {
2
+ Bootstrap,
3
+ Quest,
4
+ Website,
5
+ Command,
6
+ App,
7
+ Events,
8
+ Network,
9
+ RegisterModPackage,
10
+ RegisterQuest,
11
+ RegisterWebsite,
12
+ RegisterCommand,
13
+ RegisterApp,
14
+ } from "@hotbunny/hackhub-content-sdk";
15
+
16
+ @RegisterModPackage()
17
+ export default class {{className}} extends Bootstrap {
18
+ OnModPackageLoaded() {
19
+ console.log("{{modName}} loaded!");
20
+ }
21
+ }
22
+
23
+ @RegisterQuest()
24
+ class ExampleQuest extends Quest {
25
+ definition = {
26
+ name: "ExampleQuest",
27
+ title: "Example Quest",
28
+ description: "An example quest from {{modName}}.",
29
+ rewards: { money: 100 },
30
+ objectives: [
31
+ { name: "scan", description: "Scan the target server" },
32
+ ],
33
+ };
34
+ }
35
+
36
+ @RegisterWebsite()
37
+ class ExampleWebsite extends Website {
38
+ definition = {
39
+ url: "example.mod",
40
+ title: "Example Website",
41
+ html: `
42
+ <!DOCTYPE html>
43
+ <html>
44
+ <head>
45
+ <style>
46
+ body {
47
+ font-family: monospace;
48
+ background: #0a0a0a;
49
+ color: #00ff41;
50
+ display: flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ height: 100vh;
54
+ margin: 0;
55
+ }
56
+ h1 { text-shadow: 0 0 10px #00ff41; }
57
+ </style>
58
+ </head>
59
+ <body>
60
+ <h1>Welcome to {{modName}}</h1>
61
+ </body>
62
+ </html>`,
63
+ };
64
+ }
65
+
66
+ @RegisterCommand()
67
+ class ExampleCommand extends Command {
68
+ definition = {
69
+ name: "hello",
70
+ description: "Says hello from {{modName}}",
71
+ };
72
+
73
+ execute(args: string[]) {
74
+ return `Hello from {{modName}}! Args: ${args.join(", ")}`;
75
+ }
76
+ }
77
+
78
+ @RegisterApp()
79
+ class ExampleApp extends App {
80
+ definition = {
81
+ id: "example-app",
82
+ name: "Example App",
83
+ icon: "terminal",
84
+ html: `
85
+ <!DOCTYPE html>
86
+ <html>
87
+ <head>
88
+ <style>
89
+ body {
90
+ font-family: sans-serif;
91
+ background: #1a1a2e;
92
+ color: #eee;
93
+ padding: 24px;
94
+ margin: 0;
95
+ }
96
+ h2 { color: #e94560; }
97
+ button {
98
+ background: #e94560;
99
+ color: white;
100
+ border: none;
101
+ padding: 8px 16px;
102
+ border-radius: 4px;
103
+ cursor: pointer;
104
+ }
105
+ button:hover { opacity: 0.85; }
106
+ </style>
107
+ </head>
108
+ <body>
109
+ <h2>{{modName}} App</h2>
110
+ <p>This is a custom in-game application.</p>
111
+ <button onclick="alert('Hello from {{modName}}!')">Click me</button>
112
+ </body>
113
+ </html>`,
114
+ };
115
+ }
@@ -0,0 +1,28 @@
1
+ import {
2
+ Bootstrap,
3
+ Quest,
4
+ Events,
5
+ Network,
6
+ RegisterModPackage,
7
+ RegisterQuest,
8
+ } from "@hotbunny/hackhub-content-sdk";
9
+
10
+ @RegisterModPackage()
11
+ export default class {{className}} extends Bootstrap {
12
+ OnModPackageLoaded() {
13
+ console.log("{{modName}} loaded!");
14
+ }
15
+ }
16
+
17
+ @RegisterQuest()
18
+ class ExampleQuest extends Quest {
19
+ definition = {
20
+ name: "ExampleQuest",
21
+ title: "Example Quest",
22
+ description: "An example quest from {{modName}}.",
23
+ rewards: { money: 100 },
24
+ objectives: [
25
+ { name: "scan", description: "Scan the target server" },
26
+ ],
27
+ };
28
+ }
@@ -0,0 +1,43 @@
1
+ import {
2
+ Bootstrap,
3
+ Website,
4
+ RegisterModPackage,
5
+ RegisterWebsite,
6
+ } from "@hotbunny/hackhub-content-sdk";
7
+
8
+ @RegisterModPackage()
9
+ export default class {{className}} extends Bootstrap {
10
+ OnModPackageLoaded() {
11
+ console.log("{{modName}} loaded!");
12
+ }
13
+ }
14
+
15
+ @RegisterWebsite()
16
+ class ExampleWebsite extends Website {
17
+ definition = {
18
+ url: "example.mod",
19
+ title: "Example Website",
20
+ html: `
21
+ <!DOCTYPE html>
22
+ <html>
23
+ <head>
24
+ <style>
25
+ body {
26
+ font-family: monospace;
27
+ background: #0a0a0a;
28
+ color: #00ff41;
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ height: 100vh;
33
+ margin: 0;
34
+ }
35
+ h1 { text-shadow: 0 0 10px #00ff41; }
36
+ </style>
37
+ </head>
38
+ <body>
39
+ <h1>Welcome to {{modName}}</h1>
40
+ </body>
41
+ </html>`,
42
+ };
43
+ }