spawnpack 0.1.1 → 0.1.3

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 CHANGED
@@ -36,6 +36,12 @@ Install Bun first:
36
36
  bun add -g spawnpack
37
37
  ```
38
38
 
39
+ ### Global install with npm
40
+
41
+ ```bash
42
+ npm i -g spawnpack
43
+ ```
44
+
39
45
  ### One-off run with Bun
40
46
 
41
47
  ```bash
@@ -55,13 +61,20 @@ bun run src/index.ts
55
61
  spawnpack
56
62
  ```
57
63
 
64
+ Show the installed version:
65
+
66
+ ```bash
67
+ spawnpack -v
68
+ spawnpack --version
69
+ ```
70
+
58
71
  The wizard walks through:
59
72
 
60
73
  1. Project name and author
61
- 2. Namespace, addon identifier, and project ID
74
+ 2. Marketplace add-on structure toggle
62
75
  3. Destination folder
63
- 4. Marketplace add-on structure toggle
64
- 5. Scripting mode
76
+ 4. Script API setup
77
+ 5. Publisher ID and Project ID when needed
65
78
  6. Script packages
66
79
  7. `rgl` toggle
67
80
  8. Rockide toggle
@@ -75,7 +88,7 @@ Depending on your choices, Spawnpack can generate:
75
88
  - `packs/BP`
76
89
  - `packs/RP`
77
90
  - `data/scripts/main.ts`
78
- - `packs/BP/scripts/<namespace>/<projectId>/main.js`
91
+ - `packs/BP/scripts/<publisher-id>/<project-id>/main.js`
79
92
  - `package.json`
80
93
  - `tsconfig.json`
81
94
  - `dprint.json`
@@ -122,7 +135,7 @@ bun run build
122
135
  The npm package is configured to publish only:
123
136
 
124
137
  - `dist/spawnpack.js`
125
- - `templates/CLAUDE.md`
138
+ - `templates/CLAUDE.md`
126
139
  - `README.md`
127
140
  - `LICENSE`
128
141
  - `package.json`
package/dist/spawnpack.js CHANGED
@@ -1748,7 +1748,7 @@ function generatePackageJson(config, versions) {
1748
1748
  scripts.check = "bun --smol packs/BP/scripts";
1749
1749
  }
1750
1750
  const packageJson = {
1751
- name: `${config.namespace}-${config.projectId}`,
1751
+ name: `${config.identifier}-${config.projectId}`,
1752
1752
  version: PACK_VERSION2,
1753
1753
  private: true,
1754
1754
  type: "module",
@@ -1861,7 +1861,7 @@ function generateMainTs(config) {
1861
1861
  return `import { world } from "@minecraft/server";
1862
1862
 
1863
1863
  world.afterEvents.worldLoad.subscribe(() => {
1864
- console.warn("[${config.namespace}:${config.identifier}] Addon loaded!");
1864
+ console.warn("[${config.identifier}:${config.projectId}] Addon loaded!");
1865
1865
  });
1866
1866
  `;
1867
1867
  }
@@ -1869,7 +1869,7 @@ function generateMainJs(config) {
1869
1869
  return `import { world } from "@minecraft/server";
1870
1870
 
1871
1871
  world.afterEvents.worldLoad.subscribe(() => {
1872
- console.warn("[${config.namespace}:${config.identifier}] Addon loaded!");
1872
+ console.warn("[${config.identifier}:${config.projectId}] Addon loaded!");
1873
1873
  });
1874
1874
  `;
1875
1875
  }
@@ -1911,7 +1911,7 @@ Update the placeholder values in \`.mcp.json\` with your own tokens.
1911
1911
 
1912
1912
  - Behavior Pack: \`packs/BP\`
1913
1913
  - Resource Pack: \`packs/RP\`
1914
- - Scripting: ${config.scripting}
1914
+ - Script API setup: ${config.scripting}
1915
1915
  - Script source: ${scriptSource}
1916
1916
  - Runtime entry: ${runtimeEntry}
1917
1917
 
@@ -1942,10 +1942,10 @@ The generated \`config.json\` already points the script bundle to \`${runtimeEnt
1942
1942
  `;
1943
1943
  }
1944
1944
  function generateBlocksJson() {
1945
- return {};
1945
+ return { format_version: "1.21.40" };
1946
1946
  }
1947
1947
  function generateSoundsJson() {
1948
- return {};
1948
+ return { entity_sounds: {} };
1949
1949
  }
1950
1950
  function generateSoundDefinitions() {
1951
1951
  return { format_version: "1.14.0", sound_definitions: {} };
@@ -2059,12 +2059,12 @@ async function generateProject(config) {
2059
2059
  directories.push(...BP_NAMESPACED_DIRECTORIES.map((folder) => getScopedContentDirectory(bpPath, folder, config)), ...RP_NAMESPACED_DIRECTORIES.map((folder) => getScopedContentDirectory(rpPath, folder, config)), ...RP_NAMESPACED_NESTED_DIRECTORIES.map((segments) => getScopedNestedContentDirectory(rpPath, segments, config)));
2060
2060
  }
2061
2061
  if (config.scripting === "javascript") {
2062
- directories.push(join(bpPath, "scripts"), join(bpPath, "scripts", config.namespace), bpScriptPath);
2062
+ directories.push(join(bpPath, "scripts"), join(bpPath, "scripts", config.identifier), bpScriptPath);
2063
2063
  }
2064
2064
  if (config.scripting === "typescript") {
2065
2065
  directories.push(join(destination, "data"), dataScriptsPath);
2066
2066
  if (!config.useRgl) {
2067
- directories.push(join(bpPath, "scripts"), join(bpPath, "scripts", config.namespace), bpScriptPath);
2067
+ directories.push(join(bpPath, "scripts"), join(bpPath, "scripts", config.identifier), bpScriptPath);
2068
2068
  }
2069
2069
  }
2070
2070
  await Promise.all(directories.filter(shouldCreateDirectory).map((directory) => mkdir(directory, { recursive: true })));
@@ -2441,7 +2441,6 @@ function showReview(config) {
2441
2441
  "",
2442
2442
  `${border} ${import_picocolors.default.dim(padLabel("Project:"))} ${formatValue(config.projectName)}`,
2443
2443
  `${border} ${import_picocolors.default.dim(padLabel("Author:"))} ${config.author ? formatValue(config.author) : import_picocolors.default.dim("—")}`,
2444
- `${border} ${import_picocolors.default.dim(padLabel("Namespace:"))} ${formatValue(config.namespace)}`,
2445
2444
  ...config.useMarketplaceStructure ? [
2446
2445
  `${border} ${import_picocolors.default.dim(padLabel("Publisher ID:"))} ${formatValue(config.identifier)}`,
2447
2446
  `${border} ${import_picocolors.default.dim(padLabel("Project ID:"))} ${formatValue(config.projectId)}`,
@@ -2536,7 +2535,6 @@ async function runWizard() {
2536
2535
  return abortWizard();
2537
2536
  }
2538
2537
  const trimmedProjectName = projectName.trim();
2539
- const defaultNamespace = "sample";
2540
2538
  const suggestedProjectId = generateProjectId(trimmedProjectName);
2541
2539
  const author = await Ot({
2542
2540
  message: `${import_picocolors2.default.bold("Author")} ${import_picocolors2.default.dim("optional")}`,
@@ -2545,39 +2543,55 @@ async function runWizard() {
2545
2543
  if (q(author)) {
2546
2544
  return abortWizard();
2547
2545
  }
2548
- const namespace = await Ot({
2549
- message: `${import_picocolors2.default.bold("Namespace")} ${import_picocolors2.default.dim("used in identifiers like namespace:item_name")}`,
2550
- placeholder: defaultNamespace,
2551
- initialValue: defaultNamespace,
2546
+ const useMarketplaceStructure = await ot2({
2547
+ message: `${import_picocolors2.default.bold("Marketplace Add-On structure?")} ${import_picocolors2.default.dim("Nest BP/RP content under publisher_id/project_id for better coexistence")}`,
2548
+ initialValue: false
2549
+ });
2550
+ if (q(useMarketplaceStructure)) {
2551
+ return abortWizard();
2552
+ }
2553
+ const defaultPublisherId = sanitizeIdentifier(author.trim()) || sanitizeIdentifier(trimmedProjectName) || "sample";
2554
+ let identifierValue = defaultPublisherId;
2555
+ let projectIdValue = suggestedProjectId;
2556
+ const destination = await Ot({
2557
+ message: import_picocolors2.default.bold("Destination folder"),
2558
+ placeholder: ".",
2559
+ initialValue: ".",
2552
2560
  validate(value) {
2553
2561
  const trimmed = (value ?? "").trim();
2554
2562
  if (trimmed.length === 0) {
2555
- return "Namespace is required.";
2563
+ return "Destination folder is required.";
2556
2564
  }
2557
- if (!/^[a-z0-9_]+$/.test(trimmed)) {
2558
- return "Use lowercase letters, numbers, and underscores only.";
2565
+ if (/[<>"|?*]/.test(trimmed)) {
2566
+ return 'Invalid characters in path (avoid < > " | ? *).';
2567
+ }
2568
+ if (trimmed.length > 200) {
2569
+ return "Path is too long (max 200 characters).";
2559
2570
  }
2560
2571
  return;
2561
2572
  }
2562
2573
  });
2563
- if (q(namespace)) {
2574
+ if (q(destination)) {
2564
2575
  return abortWizard();
2565
2576
  }
2566
- const namespaceValue = namespace.trim();
2567
- const useMarketplaceStructure = await ot2({
2568
- message: `${import_picocolors2.default.bold("Marketplace Add-On structure?")} ${import_picocolors2.default.dim("Nest BP/RP content under publisher_id/project_id for better coexistence")}`,
2569
- initialValue: false
2577
+ const scripting = await _t({
2578
+ message: import_picocolors2.default.bold("Scripting API"),
2579
+ initialValue: "typescript",
2580
+ options: [
2581
+ { value: "none", label: "None", hint: "manifest-only addon scaffold" },
2582
+ { value: "javascript", label: "JavaScript", hint: "script entry with JS tooling" },
2583
+ { value: "typescript", label: "TypeScript", hint: "recommended" }
2584
+ ]
2570
2585
  });
2571
- if (q(useMarketplaceStructure)) {
2586
+ if (q(scripting)) {
2572
2587
  return abortWizard();
2573
2588
  }
2574
- let identifierValue = namespaceValue;
2575
- let projectIdValue = suggestedProjectId;
2576
- if (useMarketplaceStructure) {
2589
+ if (useMarketplaceStructure || scripting !== "none") {
2590
+ const publisherHint = useMarketplaceStructure ? "used for scoped folders like publisher_id/project_id" : "used for script folders and package naming";
2577
2591
  const publisherId = await Ot({
2578
- message: `${import_picocolors2.default.bold("Publisher ID")} ${import_picocolors2.default.dim("used for scoped folders like publisher_id/project_id")}`,
2579
- placeholder: namespaceValue,
2580
- initialValue: namespaceValue,
2592
+ message: `${import_picocolors2.default.bold("Publisher ID")} ${import_picocolors2.default.dim(publisherHint)}`,
2593
+ placeholder: defaultPublisherId,
2594
+ initialValue: defaultPublisherId,
2581
2595
  validate(value) {
2582
2596
  const trimmed = (value ?? "").trim();
2583
2597
  if (trimmed.length === 0) {
@@ -2610,39 +2624,6 @@ async function runWizard() {
2610
2624
  }
2611
2625
  projectIdValue = projectId.trim();
2612
2626
  }
2613
- const destination = await Ot({
2614
- message: import_picocolors2.default.bold("Destination folder"),
2615
- placeholder: ".",
2616
- initialValue: ".",
2617
- validate(value) {
2618
- const trimmed = (value ?? "").trim();
2619
- if (trimmed.length === 0) {
2620
- return "Destination folder is required.";
2621
- }
2622
- if (/[<>"|?*]/.test(trimmed)) {
2623
- return 'Invalid characters in path (avoid < > " | ? *).';
2624
- }
2625
- if (trimmed.length > 200) {
2626
- return "Path is too long (max 200 characters).";
2627
- }
2628
- return;
2629
- }
2630
- });
2631
- if (q(destination)) {
2632
- return abortWizard();
2633
- }
2634
- const scripting = await _t({
2635
- message: import_picocolors2.default.bold("Scripting API"),
2636
- initialValue: "typescript",
2637
- options: [
2638
- { value: "none", label: "None", hint: "manifest-only addon scaffold" },
2639
- { value: "javascript", label: "JavaScript", hint: "script entry with JS tooling" },
2640
- { value: "typescript", label: "TypeScript", hint: "recommended" }
2641
- ]
2642
- });
2643
- if (q(scripting)) {
2644
- return abortWizard();
2645
- }
2646
2627
  let scriptPackages = { ...DEFAULT_SCRIPT_PACKAGES };
2647
2628
  let useRgl = false;
2648
2629
  if (scripting !== "none") {
@@ -2687,7 +2668,6 @@ async function runWizard() {
2687
2668
  const config = {
2688
2669
  projectName: trimmedProjectName,
2689
2670
  author: author.trim(),
2690
- namespace: namespaceValue,
2691
2671
  identifier: identifierValue,
2692
2672
  projectId: projectIdValue,
2693
2673
  destination: destination.trim(),
@@ -2711,6 +2691,12 @@ async function runWizard() {
2711
2691
 
2712
2692
  // src/index.ts
2713
2693
  var teal3 = (value) => `\x1B[38;2;47;208;181m${value}\x1B[39m`;
2694
+ var args = new Set(process.argv.slice(2));
2695
+ if (args.has("-v") || args.has("--version")) {
2696
+ const packageJson = await Bun.file(new URL("../package.json", import.meta.url)).json();
2697
+ console.log(packageJson.version);
2698
+ process.exit(0);
2699
+ }
2714
2700
  var config = await runWizard();
2715
2701
  if (config === null) {
2716
2702
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spawnpack",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Minecraft Bedrock addon project generator — scaffold your BP+RP in seconds",
5
5
  "author": "veedy-dev",
6
6
  "license": "MIT",