@mafly/bdev-cli 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +225 -2
- package/dist/commands/build.d.ts +6 -0
- package/dist/commands/build.js +94 -0
- package/dist/commands/config.d.ts +7 -0
- package/dist/commands/config.js +46 -0
- package/dist/commands/dev.d.ts +8 -0
- package/dist/commands/dev.js +165 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +84 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.js +164 -0
- package/dist/commands/package.d.ts +5 -0
- package/dist/commands/package.js +103 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +149 -0
- package/dist/utils/bundler.d.ts +2 -0
- package/dist/utils/bundler.js +29 -0
- package/dist/utils/config.d.ts +14 -0
- package/dist/utils/config.js +58 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.js +19 -0
- package/dist/utils/manifest.d.ts +42 -0
- package/dist/utils/manifest.js +146 -0
- package/dist/utils/mojang.d.ts +8 -0
- package/dist/utils/mojang.js +122 -0
- package/dist/utils/version.d.ts +1 -0
- package/dist/utils/version.js +23 -0
- package/dist/utils/wsserver.d.ts +25 -0
- package/dist/utils/wsserver.js +109 -0
- package/package.json +52 -4
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { generateManifests } from "../utils/manifest.js";
|
|
4
|
+
import { saveConfig } from "../utils/config.js";
|
|
5
|
+
import { logger } from "../utils/logger.js";
|
|
6
|
+
const DEFAULT_ICON_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
|
|
7
|
+
export async function initCommand(targetDirArg, options) {
|
|
8
|
+
const targetDir = path.resolve(targetDirArg || ".");
|
|
9
|
+
const projectName = options.name || path.basename(targetDir) || "my-bedrock-addon";
|
|
10
|
+
const projectDesc = options.desc || `${projectName} - Bedrock Addon created with bdev`;
|
|
11
|
+
const isTs = options.lang !== "js";
|
|
12
|
+
const hasRp = options.rp !== false;
|
|
13
|
+
logger.info(`Scaffolding Bedrock Addon project: ${projectName}`);
|
|
14
|
+
logger.info(`Directory: ${targetDir}`);
|
|
15
|
+
logger.info(`Language: ${isTs ? "TypeScript" : "JavaScript"} | Resource Pack: ${hasRp ? "Yes" : "No"}`);
|
|
16
|
+
if (!fs.existsSync(targetDir)) {
|
|
17
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
const { bpManifest, rpManifest } = generateManifests({
|
|
20
|
+
name: projectName,
|
|
21
|
+
description: projectDesc,
|
|
22
|
+
hasScript: true,
|
|
23
|
+
hasRp,
|
|
24
|
+
minEngineVersion: [1, 21, 0],
|
|
25
|
+
serverVersion: "1.16.0",
|
|
26
|
+
});
|
|
27
|
+
// 1. Behavior Pack
|
|
28
|
+
const bpDir = path.join(targetDir, "behavior_pack");
|
|
29
|
+
fs.mkdirSync(bpDir, { recursive: true });
|
|
30
|
+
fs.writeFileSync(path.join(bpDir, "manifest.json"), JSON.stringify(bpManifest, null, 2) + "\n");
|
|
31
|
+
fs.writeFileSync(path.join(bpDir, "pack_icon.png"), Buffer.from(DEFAULT_ICON_BASE64, "base64"));
|
|
32
|
+
// 2. Resource Pack (if enabled)
|
|
33
|
+
if (hasRp && rpManifest) {
|
|
34
|
+
const rpDir = path.join(targetDir, "resource_pack");
|
|
35
|
+
fs.mkdirSync(rpDir, { recursive: true });
|
|
36
|
+
fs.writeFileSync(path.join(rpDir, "manifest.json"), JSON.stringify(rpManifest, null, 2) + "\n");
|
|
37
|
+
fs.writeFileSync(path.join(rpDir, "pack_icon.png"), Buffer.from(DEFAULT_ICON_BASE64, "base64"));
|
|
38
|
+
const textsDir = path.join(rpDir, "texts");
|
|
39
|
+
fs.mkdirSync(textsDir, { recursive: true });
|
|
40
|
+
fs.writeFileSync(path.join(textsDir, "en_US.lang"), `pack.name=${projectName}\npack.description=${projectDesc}\n`);
|
|
41
|
+
}
|
|
42
|
+
// 3. Scripts / Source code
|
|
43
|
+
const starterCode = `import { world, system } from "@minecraft/server";
|
|
44
|
+
|
|
45
|
+
world.afterEvents.worldInitialize.subscribe(() => {
|
|
46
|
+
world.sendMessage("Hello from ${projectName}!");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
let ticks = 0;
|
|
50
|
+
system.runInterval(() => {
|
|
51
|
+
ticks++;
|
|
52
|
+
if (ticks % 100 === 0) {
|
|
53
|
+
// Periodic tick logic
|
|
54
|
+
}
|
|
55
|
+
}, 20);
|
|
56
|
+
`;
|
|
57
|
+
if (isTs) {
|
|
58
|
+
const srcDir = path.join(targetDir, "src");
|
|
59
|
+
fs.mkdirSync(srcDir, { recursive: true });
|
|
60
|
+
fs.writeFileSync(path.join(srcDir, "main.ts"), starterCode);
|
|
61
|
+
// tsconfig.json
|
|
62
|
+
const tsconfig = {
|
|
63
|
+
compilerOptions: {
|
|
64
|
+
target: "ES2020",
|
|
65
|
+
module: "ESNext",
|
|
66
|
+
moduleResolution: "Bundler",
|
|
67
|
+
strict: true,
|
|
68
|
+
skipLibCheck: true,
|
|
69
|
+
noEmit: true,
|
|
70
|
+
},
|
|
71
|
+
include: ["src/**/*"],
|
|
72
|
+
};
|
|
73
|
+
fs.writeFileSync(path.join(targetDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n");
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const scriptsDir = path.join(bpDir, "scripts");
|
|
77
|
+
fs.mkdirSync(scriptsDir, { recursive: true });
|
|
78
|
+
fs.writeFileSync(path.join(scriptsDir, "main.js"), starterCode);
|
|
79
|
+
}
|
|
80
|
+
// 4. package.json
|
|
81
|
+
const pkgJson = {
|
|
82
|
+
name: projectName.toLowerCase().replace(/[^a-z0-9-_]/g, "-"),
|
|
83
|
+
version: "1.0.0",
|
|
84
|
+
description: projectDesc,
|
|
85
|
+
type: "module",
|
|
86
|
+
scripts: {
|
|
87
|
+
build: "bdev build",
|
|
88
|
+
dev: "bdev dev",
|
|
89
|
+
package: "bdev package",
|
|
90
|
+
},
|
|
91
|
+
dependencies: {
|
|
92
|
+
"@minecraft/server": "1.16.0",
|
|
93
|
+
"@minecraft/server-ui": "1.7.0",
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
if (isTs) {
|
|
97
|
+
pkgJson.devDependencies = {
|
|
98
|
+
"@types/node": "^22.0.0",
|
|
99
|
+
typescript: "^5.0.0",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
fs.writeFileSync(path.join(targetDir, "package.json"), JSON.stringify(pkgJson, null, 2) + "\n");
|
|
103
|
+
// 5. bdev.json config
|
|
104
|
+
saveConfig({
|
|
105
|
+
name: projectName,
|
|
106
|
+
mojangPath: options.path ? path.resolve(options.path) : undefined,
|
|
107
|
+
}, { dir: targetDir });
|
|
108
|
+
// 6. Comprehensive .gitignore
|
|
109
|
+
const gitignoreLines = [
|
|
110
|
+
"# Dependencies",
|
|
111
|
+
"node_modules/",
|
|
112
|
+
"",
|
|
113
|
+
"# Build outputs",
|
|
114
|
+
"dist/",
|
|
115
|
+
];
|
|
116
|
+
if (isTs) {
|
|
117
|
+
gitignoreLines.push("behavior_pack/scripts/");
|
|
118
|
+
}
|
|
119
|
+
gitignoreLines.push("", "# bdev cache & local configs", ".bdev/", "*.local.json", "", "# Logs", "*.log", "npm-debug.log*", "yarn-debug.log*", "yarn-error.log*", "", "# OS files", ".DS_Store", ".DS_Store?", "._*", ".Spotlight-V100", ".Trashes", "ehthumbs.db", "Thumbs.db", "", "# Editor files", ".vscode/", ".idea/");
|
|
120
|
+
fs.writeFileSync(path.join(targetDir, ".gitignore"), gitignoreLines.join("\n") + "\n");
|
|
121
|
+
// 7. README.md
|
|
122
|
+
const readmeContent = `# ${projectName}
|
|
123
|
+
|
|
124
|
+
${projectDesc}
|
|
125
|
+
|
|
126
|
+
## Development
|
|
127
|
+
|
|
128
|
+
Run development watcher (auto builds, symlinks, and starts WebSocket hot reload):
|
|
129
|
+
\`\`\`bash
|
|
130
|
+
bdev dev
|
|
131
|
+
\`\`\`
|
|
132
|
+
|
|
133
|
+
In your Minecraft world (cheats enabled), connect the game to bdev hot reload:
|
|
134
|
+
\`\`\`
|
|
135
|
+
/connect localhost:8000
|
|
136
|
+
\`\`\`
|
|
137
|
+
Whenever you save script files, bdev will automatically execute \`/reload\` in game.
|
|
138
|
+
|
|
139
|
+
To configure your \`com.mojang\` Bedrock directory:
|
|
140
|
+
\`\`\`bash
|
|
141
|
+
bdev set-path "C:\\path\\to\\com.mojang"
|
|
142
|
+
# or
|
|
143
|
+
bdev dev -p "C:\\path\\to\\com.mojang"
|
|
144
|
+
\`\`\`
|
|
145
|
+
|
|
146
|
+
## Build & Package
|
|
147
|
+
|
|
148
|
+
Compile scripts and validate manifest:
|
|
149
|
+
\`\`\`bash
|
|
150
|
+
bdev build
|
|
151
|
+
\`\`\`
|
|
152
|
+
|
|
153
|
+
Bundle into \`.mcaddon\` and \`.mcpack\` for distribution:
|
|
154
|
+
\`\`\`bash
|
|
155
|
+
bdev package
|
|
156
|
+
\`\`\`
|
|
157
|
+
`;
|
|
158
|
+
fs.writeFileSync(path.join(targetDir, "README.md"), readmeContent);
|
|
159
|
+
logger.success(`Project initialized successfully at ${targetDir}`);
|
|
160
|
+
if (options.path) {
|
|
161
|
+
logger.success(`Configured com.mojang path: ${path.resolve(options.path)}`);
|
|
162
|
+
}
|
|
163
|
+
logger.info("Run: cd " + (targetDirArg || ".") + " && bdev dev");
|
|
164
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import archiver from "archiver";
|
|
4
|
+
import { buildCommand } from "./build.js";
|
|
5
|
+
import { logger } from "../utils/logger.js";
|
|
6
|
+
function archiveDirectory(sourceDir, destZipPath, prefix = "") {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const output = fs.createWriteStream(destZipPath);
|
|
9
|
+
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
10
|
+
output.on("close", () => {
|
|
11
|
+
resolve(archive.pointer());
|
|
12
|
+
});
|
|
13
|
+
archive.on("error", (err) => {
|
|
14
|
+
reject(err);
|
|
15
|
+
});
|
|
16
|
+
archive.pipe(output);
|
|
17
|
+
archive.directory(sourceDir, prefix || false);
|
|
18
|
+
archive.finalize();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function archiveMultiple(entries, destZipPath) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const output = fs.createWriteStream(destZipPath);
|
|
24
|
+
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
25
|
+
output.on("close", () => {
|
|
26
|
+
resolve(archive.pointer());
|
|
27
|
+
});
|
|
28
|
+
archive.on("error", (err) => {
|
|
29
|
+
reject(err);
|
|
30
|
+
});
|
|
31
|
+
archive.pipe(output);
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
archive.directory(entry.dir, entry.prefix);
|
|
34
|
+
}
|
|
35
|
+
archive.finalize();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function formatBytes(bytes) {
|
|
39
|
+
if (bytes < 1024)
|
|
40
|
+
return `${bytes} B`;
|
|
41
|
+
if (bytes < 1024 * 1024)
|
|
42
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
43
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
44
|
+
}
|
|
45
|
+
export async function packageCommand(dirArg, options = {}) {
|
|
46
|
+
const rootDir = path.resolve(dirArg || ".");
|
|
47
|
+
const bpDir = path.join(rootDir, "behavior_pack");
|
|
48
|
+
const rpDir = path.join(rootDir, "resource_pack");
|
|
49
|
+
const hasBp = fs.existsSync(bpDir);
|
|
50
|
+
const hasRp = fs.existsSync(rpDir);
|
|
51
|
+
if (!hasBp && !hasRp) {
|
|
52
|
+
logger.error(`No behavior_pack or resource_pack found in ${rootDir}.`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
logger.info("Building project before packaging...");
|
|
56
|
+
const buildOk = await buildCommand(rootDir);
|
|
57
|
+
if (!buildOk) {
|
|
58
|
+
logger.error("Build failed. Aborting package.");
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const outDir = options.output
|
|
62
|
+
? path.resolve(rootDir, options.output)
|
|
63
|
+
: path.join(rootDir, "dist");
|
|
64
|
+
if (!fs.existsSync(outDir)) {
|
|
65
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
66
|
+
}
|
|
67
|
+
const baseName = options.name || path.basename(rootDir);
|
|
68
|
+
const spinner = logger.spinner("Packaging Bedrock Addon...");
|
|
69
|
+
try {
|
|
70
|
+
const generatedFiles = [];
|
|
71
|
+
// Package BP if present
|
|
72
|
+
if (hasBp) {
|
|
73
|
+
const bpZipPath = path.join(outDir, `${baseName}_BP.mcpack`);
|
|
74
|
+
const size = await archiveDirectory(bpDir, bpZipPath);
|
|
75
|
+
generatedFiles.push({ path: bpZipPath, size });
|
|
76
|
+
}
|
|
77
|
+
// Package RP if present
|
|
78
|
+
if (hasRp) {
|
|
79
|
+
const rpZipPath = path.join(outDir, `${baseName}_RP.mcpack`);
|
|
80
|
+
const size = await archiveDirectory(rpDir, rpZipPath);
|
|
81
|
+
generatedFiles.push({ path: rpZipPath, size });
|
|
82
|
+
}
|
|
83
|
+
// If both exist, package into .mcaddon
|
|
84
|
+
if (hasBp && hasRp) {
|
|
85
|
+
const mcaddonPath = path.join(outDir, `${baseName}.mcaddon`);
|
|
86
|
+
const size = await archiveMultiple([
|
|
87
|
+
{ dir: bpDir, prefix: `${baseName}_BP` },
|
|
88
|
+
{ dir: rpDir, prefix: `${baseName}_RP` },
|
|
89
|
+
], mcaddonPath);
|
|
90
|
+
generatedFiles.push({ path: mcaddonPath, size });
|
|
91
|
+
}
|
|
92
|
+
spinner.succeed("Packaging complete!");
|
|
93
|
+
logger.info("Generated distribution packages:");
|
|
94
|
+
for (const f of generatedFiles) {
|
|
95
|
+
logger.success(` ${path.relative(rootDir, f.path)} (${formatBytes(f.size)})`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
spinner.fail("Packaging failed");
|
|
100
|
+
logger.error(err.message);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { initCommand } from "./commands/init.js";
|
|
4
|
+
import { buildCommand } from "./commands/build.js";
|
|
5
|
+
import { devCommand } from "./commands/dev.js";
|
|
6
|
+
import { packageCommand } from "./commands/package.js";
|
|
7
|
+
import { doctorCommand } from "./commands/doctor.js";
|
|
8
|
+
import { setConfigCommand, getConfigCommand, setPathCommand, } from "./commands/config.js";
|
|
9
|
+
import { getVersion } from "./utils/version.js";
|
|
10
|
+
const program = new Command();
|
|
11
|
+
program
|
|
12
|
+
.name("bdev")
|
|
13
|
+
.description("Modern CLI tool for Minecraft Bedrock Edition Addon development")
|
|
14
|
+
.version(getVersion());
|
|
15
|
+
program
|
|
16
|
+
.command("init [directory]")
|
|
17
|
+
.description("Scaffold a new Bedrock addon project")
|
|
18
|
+
.option("-n, --name <name>", "Project / pack name")
|
|
19
|
+
.option("-d, --desc <description>", "Pack description")
|
|
20
|
+
.option("-p, --path <path>", "Custom com.mojang path to save in bdev.json")
|
|
21
|
+
.option("--no-rp", "Scaffold Behavior Pack only (skip Resource Pack)")
|
|
22
|
+
.option("--lang <lang>", "Script language: ts or js", "ts")
|
|
23
|
+
.action(async (directory, options) => {
|
|
24
|
+
try {
|
|
25
|
+
await initCommand(directory, options);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
console.error(err.message);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
program
|
|
33
|
+
.command("build [directory]")
|
|
34
|
+
.description("Compile TypeScript scripts and validate manifests")
|
|
35
|
+
.option("-m, --minify", "Minify bundled JavaScript")
|
|
36
|
+
.option("--no-sourcemap", "Disable sourcemap generation")
|
|
37
|
+
.action(async (directory, options) => {
|
|
38
|
+
try {
|
|
39
|
+
const ok = await buildCommand(directory, options);
|
|
40
|
+
if (!ok)
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
console.error(err.message);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
program
|
|
49
|
+
.command("dev [directory]")
|
|
50
|
+
.description("Symlink packs to com.mojang, start hot-reload WebSocket, and watch for changes")
|
|
51
|
+
.option("-p, --path <path>", "Custom com.mojang directory path")
|
|
52
|
+
.option("-s, --save", "Save custom path to bdev.json for future use")
|
|
53
|
+
.option("-P, --port <port>", "WebSocket server port for Minecraft hot reload", "8000")
|
|
54
|
+
.option("--no-ws", "Disable WebSocket hot reload server")
|
|
55
|
+
.option("--no-link", "Skip creating symlinks to development directories")
|
|
56
|
+
.action(async (directory, options) => {
|
|
57
|
+
try {
|
|
58
|
+
await devCommand(directory, options);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
console.error(err.message);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
program
|
|
66
|
+
.command("package [directory]")
|
|
67
|
+
.alias("deploy")
|
|
68
|
+
.description("Build and compress packs into .mcaddon and .mcpack files")
|
|
69
|
+
.option("-o, --output <dir>", "Output directory for packages", "dist")
|
|
70
|
+
.option("-n, --name <name>", "Package file name prefix")
|
|
71
|
+
.action(async (directory, options) => {
|
|
72
|
+
try {
|
|
73
|
+
await packageCommand(directory, options);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
console.error(err.message);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
program
|
|
81
|
+
.command("set-path <path>")
|
|
82
|
+
.alias("path")
|
|
83
|
+
.description("Set com.mojang Bedrock path in configuration")
|
|
84
|
+
.option("-g, --global", "Save path globally in ~/.bdev/config.json")
|
|
85
|
+
.action(async (pathInput, options) => {
|
|
86
|
+
try {
|
|
87
|
+
await setPathCommand(pathInput, options);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
console.error(err.message);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const configCmd = program
|
|
95
|
+
.command("config")
|
|
96
|
+
.description("Manage bdev configuration");
|
|
97
|
+
configCmd
|
|
98
|
+
.command("set <key> <value>")
|
|
99
|
+
.description("Set configuration key (e.g. mojangPath /path/to/com.mojang)")
|
|
100
|
+
.option("-g, --global", "Set globally in ~/.bdev/config.json")
|
|
101
|
+
.action(async (key, value, options) => {
|
|
102
|
+
try {
|
|
103
|
+
await setConfigCommand(key, value, options);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
console.error(err.message);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
configCmd
|
|
111
|
+
.command("get [key]")
|
|
112
|
+
.description("Get configuration key or list all")
|
|
113
|
+
.option("-g, --global", "Get from ~/.bdev/config.json only")
|
|
114
|
+
.action(async (key, options) => {
|
|
115
|
+
try {
|
|
116
|
+
await getConfigCommand(key, options);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
console.error(err.message);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
configCmd
|
|
124
|
+
.command("path <path>")
|
|
125
|
+
.description("Set com.mojang directory path")
|
|
126
|
+
.option("-g, --global", "Set globally in ~/.bdev/config.json")
|
|
127
|
+
.action(async (pathInput, options) => {
|
|
128
|
+
try {
|
|
129
|
+
await setPathCommand(pathInput, options);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
console.error(err.message);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
program
|
|
137
|
+
.command("doctor")
|
|
138
|
+
.description("Verify environment requirements and Bedrock paths")
|
|
139
|
+
.option("-p, --path <path>", "Custom com.mojang path to check")
|
|
140
|
+
.action(async (options) => {
|
|
141
|
+
try {
|
|
142
|
+
await doctorCommand(options.path);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
console.error(err.message);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
program.parse();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as esbuildNative from "esbuild";
|
|
2
|
+
import * as esbuildWasm from "esbuild-wasm";
|
|
3
|
+
let wasmInitialized = false;
|
|
4
|
+
export async function runBuild(options) {
|
|
5
|
+
try {
|
|
6
|
+
return await esbuildNative.build(options);
|
|
7
|
+
}
|
|
8
|
+
catch (err) {
|
|
9
|
+
const errorObj = err;
|
|
10
|
+
const message = typeof errorObj?.message === "string" ? errorObj.message : "";
|
|
11
|
+
// If it's a code/syntax error from esbuild itself, rethrow directly
|
|
12
|
+
if (Array.isArray(errorObj?.errors) && errorObj.errors.length > 0) {
|
|
13
|
+
throw err;
|
|
14
|
+
}
|
|
15
|
+
// If missing native executable or module resolution in compiled binary, fallback to wasm
|
|
16
|
+
const isBinaryResolutionError = message.includes("Cannot find module 'esbuild'") ||
|
|
17
|
+
message.includes("could not be found") ||
|
|
18
|
+
message.includes("cannot be bundled") ||
|
|
19
|
+
message.includes("Failed to resolve entry for package");
|
|
20
|
+
if (isBinaryResolutionError) {
|
|
21
|
+
if (!wasmInitialized) {
|
|
22
|
+
await esbuildWasm.initialize({});
|
|
23
|
+
wasmInitialized = true;
|
|
24
|
+
}
|
|
25
|
+
return (await esbuildWasm.build(options));
|
|
26
|
+
}
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface BDevConfig {
|
|
2
|
+
name?: string;
|
|
3
|
+
mojangPath?: string;
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
}
|
|
6
|
+
export declare function getGlobalConfigPath(): string;
|
|
7
|
+
export declare function getLocalConfigPath(targetDir?: string): string;
|
|
8
|
+
export declare function loadGlobalConfig(): BDevConfig;
|
|
9
|
+
export declare function loadLocalConfig(targetDir?: string): BDevConfig;
|
|
10
|
+
export declare function loadMergedConfig(targetDir?: string): BDevConfig;
|
|
11
|
+
export declare function saveConfig(updates: Partial<BDevConfig>, opts?: {
|
|
12
|
+
global?: boolean;
|
|
13
|
+
dir?: string;
|
|
14
|
+
}): string;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const GLOBAL_CONFIG_DIR = path.join(os.homedir(), ".bdev");
|
|
5
|
+
const GLOBAL_CONFIG_PATH = path.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
6
|
+
export function getGlobalConfigPath() {
|
|
7
|
+
return GLOBAL_CONFIG_PATH;
|
|
8
|
+
}
|
|
9
|
+
export function getLocalConfigPath(targetDir = process.cwd()) {
|
|
10
|
+
return path.join(path.resolve(targetDir), "bdev.json");
|
|
11
|
+
}
|
|
12
|
+
export function loadGlobalConfig() {
|
|
13
|
+
try {
|
|
14
|
+
if (fs.existsSync(GLOBAL_CONFIG_PATH)) {
|
|
15
|
+
const raw = fs.readFileSync(GLOBAL_CONFIG_PATH, "utf-8");
|
|
16
|
+
return JSON.parse(raw);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Ignore read errors
|
|
21
|
+
}
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
export function loadLocalConfig(targetDir = process.cwd()) {
|
|
25
|
+
try {
|
|
26
|
+
const localPath = getLocalConfigPath(targetDir);
|
|
27
|
+
if (fs.existsSync(localPath)) {
|
|
28
|
+
const raw = fs.readFileSync(localPath, "utf-8");
|
|
29
|
+
return JSON.parse(raw);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Ignore read errors
|
|
34
|
+
}
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
export function loadMergedConfig(targetDir = process.cwd()) {
|
|
38
|
+
const globalConf = loadGlobalConfig();
|
|
39
|
+
const localConf = loadLocalConfig(targetDir);
|
|
40
|
+
return { ...globalConf, ...localConf };
|
|
41
|
+
}
|
|
42
|
+
export function saveConfig(updates, opts = {}) {
|
|
43
|
+
if (opts.global) {
|
|
44
|
+
if (!fs.existsSync(GLOBAL_CONFIG_DIR)) {
|
|
45
|
+
fs.mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
const current = loadGlobalConfig();
|
|
48
|
+
const merged = { ...current, ...updates };
|
|
49
|
+
fs.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(merged, null, 2) + "\n");
|
|
50
|
+
return GLOBAL_CONFIG_PATH;
|
|
51
|
+
}
|
|
52
|
+
const targetDir = opts.dir ? path.resolve(opts.dir) : process.cwd();
|
|
53
|
+
const localPath = getLocalConfigPath(targetDir);
|
|
54
|
+
const current = loadLocalConfig(targetDir);
|
|
55
|
+
const merged = { ...current, ...updates };
|
|
56
|
+
fs.writeFileSync(localPath, JSON.stringify(merged, null, 2) + "\n");
|
|
57
|
+
return localPath;
|
|
58
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
export const logger = {
|
|
4
|
+
info(msg) {
|
|
5
|
+
console.log(`${pc.cyan("ℹ")} ${msg}`);
|
|
6
|
+
},
|
|
7
|
+
success(msg) {
|
|
8
|
+
console.log(`${pc.green("✔")} ${msg}`);
|
|
9
|
+
},
|
|
10
|
+
warn(msg) {
|
|
11
|
+
console.log(`${pc.yellow("⚠")} ${msg}`);
|
|
12
|
+
},
|
|
13
|
+
error(msg) {
|
|
14
|
+
console.error(`${pc.red("✖")} ${msg}`);
|
|
15
|
+
},
|
|
16
|
+
spinner(text) {
|
|
17
|
+
return ora({ text, color: "cyan" }).start();
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface ManifestHeader {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
uuid: string;
|
|
5
|
+
version: [number, number, number];
|
|
6
|
+
min_engine_version?: [number, number, number];
|
|
7
|
+
}
|
|
8
|
+
export interface ManifestModule {
|
|
9
|
+
type: "data" | "script" | "resources" | "client_data" | string;
|
|
10
|
+
uuid: string;
|
|
11
|
+
version: [number, number, number];
|
|
12
|
+
language?: string;
|
|
13
|
+
entry?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ManifestDependency {
|
|
17
|
+
uuid?: string;
|
|
18
|
+
version?: [number, number, number] | string;
|
|
19
|
+
module_name?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface BedrockManifest {
|
|
22
|
+
format_version: number;
|
|
23
|
+
header: ManifestHeader;
|
|
24
|
+
modules: ManifestModule[];
|
|
25
|
+
dependencies?: ManifestDependency[];
|
|
26
|
+
}
|
|
27
|
+
export interface ValidationResult {
|
|
28
|
+
valid: boolean;
|
|
29
|
+
errors: string[];
|
|
30
|
+
}
|
|
31
|
+
export declare function validateManifest(manifestPath: string): ValidationResult;
|
|
32
|
+
export declare function generateManifests(opts: {
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
hasScript: boolean;
|
|
36
|
+
hasRp: boolean;
|
|
37
|
+
minEngineVersion?: [number, number, number];
|
|
38
|
+
serverVersion?: string;
|
|
39
|
+
}): {
|
|
40
|
+
bpManifest: BedrockManifest;
|
|
41
|
+
rpManifest?: BedrockManifest;
|
|
42
|
+
};
|