sandstone-cli 2.1.2 → 2.2.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/lib/create.js CHANGED
@@ -5680,12 +5680,12 @@ var {
5680
5680
  import figlet from "figlet";
5681
5681
 
5682
5682
  // src/version.ts
5683
- var CLI_VERSION = "2.1.2";
5683
+ var CLI_VERSION = "2.2.0";
5684
5684
 
5685
5685
  // src/commands/create.ts
5686
5686
  var import_semver = __toESM(require_semver2(), 1);
5687
5687
  var import_fs_extra = __toESM(require_lib(), 1);
5688
- import path2 from "path";
5688
+ import path5 from "path";
5689
5689
 
5690
5690
  // node_modules/chalk/source/vendor/ansi-styles/index.js
5691
5691
  var ANSI_BACKGROUND_OFFSET = 10;
@@ -6410,6 +6410,298 @@ function getWorldsList(clientPath) {
6410
6410
  return fs.readdirSync(savesPath, { withFileTypes: true }).filter((f) => f.isDirectory).map((f) => f.name);
6411
6411
  }
6412
6412
 
6413
+ // src/launchers/registry.ts
6414
+ var providers = new Map;
6415
+ function registerProvider(provider) {
6416
+ providers.set(provider.type, provider);
6417
+ }
6418
+ function getProviders() {
6419
+ return Array.from(providers.values());
6420
+ }
6421
+ async function discoverAllInstances() {
6422
+ const result = {
6423
+ instances: [],
6424
+ errors: new Map
6425
+ };
6426
+ const discoveries = await Promise.allSettled(getProviders().map(async (provider) => {
6427
+ const instances = await provider.discoverInstances();
6428
+ return { type: provider.type, instances };
6429
+ }));
6430
+ for (const discovery of discoveries) {
6431
+ if (discovery.status === "fulfilled") {
6432
+ result.instances.push(...discovery.value.instances);
6433
+ } else {
6434
+ const error = discovery.reason;
6435
+ console.error("Discovery error:", error.message);
6436
+ }
6437
+ }
6438
+ return result;
6439
+ }
6440
+ // src/launchers/providers/vanilla.ts
6441
+ import fs2 from "fs";
6442
+ import path2 from "path";
6443
+ import os3 from "os";
6444
+ function getVanillaPath() {
6445
+ let mcPath;
6446
+ switch (os3.platform()) {
6447
+ case "win32":
6448
+ mcPath = path2.join(os3.homedir(), "AppData/Roaming/.minecraft");
6449
+ break;
6450
+ case "darwin":
6451
+ mcPath = path2.join(os3.homedir(), "Library/Application Support/minecraft");
6452
+ break;
6453
+ case "linux":
6454
+ default:
6455
+ mcPath = path2.join(os3.homedir(), ".minecraft");
6456
+ break;
6457
+ }
6458
+ return fs2.existsSync(mcPath) ? mcPath : null;
6459
+ }
6460
+ var vanillaProvider = {
6461
+ type: "vanilla",
6462
+ displayName: "Vanilla Minecraft",
6463
+ async isInstalled() {
6464
+ return getVanillaPath() !== null;
6465
+ },
6466
+ getDataPath() {
6467
+ return getVanillaPath();
6468
+ },
6469
+ async discoverInstances() {
6470
+ const dataPath = getVanillaPath();
6471
+ if (!dataPath)
6472
+ return [];
6473
+ return [{
6474
+ id: "vanilla",
6475
+ name: "Vanilla Minecraft",
6476
+ launcher: "vanilla",
6477
+ minecraftPath: dataPath
6478
+ }];
6479
+ }
6480
+ };
6481
+
6482
+ // src/launchers/providers/prism.ts
6483
+ import fs3 from "fs";
6484
+ import path3 from "path";
6485
+ import os4 from "os";
6486
+ function getPrismCandidatePaths() {
6487
+ const home = os4.homedir();
6488
+ const paths = [];
6489
+ switch (os4.platform()) {
6490
+ case "win32":
6491
+ paths.push(path3.join(os4.homedir(), "AppData/Roaming/PrismLauncher"));
6492
+ break;
6493
+ case "darwin":
6494
+ paths.push(path3.join(home, "Library/Application Support/PrismLauncher"));
6495
+ break;
6496
+ case "linux":
6497
+ default: {
6498
+ const xdgDataHome = process.env.XDG_DATA_HOME;
6499
+ if (xdgDataHome) {
6500
+ paths.push(path3.join(xdgDataHome, "PrismLauncher"));
6501
+ }
6502
+ paths.push(path3.join(home, ".local/share/PrismLauncher"));
6503
+ paths.push(path3.join(home, ".var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher"));
6504
+ break;
6505
+ }
6506
+ }
6507
+ return paths;
6508
+ }
6509
+ function getPrismDataPath() {
6510
+ for (const candidate of getPrismCandidatePaths()) {
6511
+ if (fs3.existsSync(candidate)) {
6512
+ return candidate;
6513
+ }
6514
+ }
6515
+ return null;
6516
+ }
6517
+ function parseInstanceConfig(configPath) {
6518
+ try {
6519
+ const content = fs3.readFileSync(configPath, "utf-8");
6520
+ const result = {};
6521
+ for (const line of content.split(`
6522
+ `)) {
6523
+ const trimmed = line.trim();
6524
+ if (trimmed.startsWith("name=")) {
6525
+ result.name = trimmed.slice(5);
6526
+ break;
6527
+ }
6528
+ }
6529
+ return result;
6530
+ } catch {
6531
+ return {};
6532
+ }
6533
+ }
6534
+ function parsePackJson(packPath) {
6535
+ try {
6536
+ const content = fs3.readFileSync(packPath, "utf-8");
6537
+ const pack = JSON.parse(content);
6538
+ const components = pack.components;
6539
+ if (components) {
6540
+ const minecraft = components.find((c) => c.uid === "net.minecraft");
6541
+ if (minecraft?.version) {
6542
+ return { version: minecraft.version };
6543
+ }
6544
+ }
6545
+ return {};
6546
+ } catch {
6547
+ return {};
6548
+ }
6549
+ }
6550
+ var prismProvider = {
6551
+ type: "prism",
6552
+ displayName: "Prism Launcher",
6553
+ async isInstalled() {
6554
+ return getPrismDataPath() !== null;
6555
+ },
6556
+ getDataPath() {
6557
+ return getPrismDataPath();
6558
+ },
6559
+ async discoverInstances() {
6560
+ const dataPath = getPrismDataPath();
6561
+ if (!dataPath)
6562
+ return [];
6563
+ const instancesDir = path3.join(dataPath, "instances");
6564
+ if (!fs3.existsSync(instancesDir))
6565
+ return [];
6566
+ const instances = [];
6567
+ try {
6568
+ const entries = fs3.readdirSync(instancesDir, { withFileTypes: true });
6569
+ for (const entry of entries) {
6570
+ if (!entry.isDirectory())
6571
+ continue;
6572
+ if (entry.name.startsWith(".") || entry.name.startsWith("_"))
6573
+ continue;
6574
+ const instanceDir = path3.join(instancesDir, entry.name);
6575
+ const minecraftDir = path3.join(instanceDir, "minecraft");
6576
+ const dotMinecraftDir = path3.join(instanceDir, ".minecraft");
6577
+ let minecraftPath = null;
6578
+ if (fs3.existsSync(minecraftDir)) {
6579
+ minecraftPath = minecraftDir;
6580
+ } else if (fs3.existsSync(dotMinecraftDir)) {
6581
+ minecraftPath = dotMinecraftDir;
6582
+ }
6583
+ if (!minecraftPath)
6584
+ continue;
6585
+ const configPath = path3.join(instanceDir, "instance.cfg");
6586
+ const config = parseInstanceConfig(configPath);
6587
+ const packPath = path3.join(instanceDir, "mmc-pack.json");
6588
+ const pack = parsePackJson(packPath);
6589
+ instances.push({
6590
+ id: `prism-${entry.name}`,
6591
+ name: config.name || entry.name,
6592
+ launcher: "prism",
6593
+ minecraftPath,
6594
+ version: pack.version
6595
+ });
6596
+ }
6597
+ } catch {}
6598
+ return instances;
6599
+ }
6600
+ };
6601
+
6602
+ // src/launchers/providers/modrinth.ts
6603
+ import fs4 from "fs";
6604
+ import path4 from "path";
6605
+ import os5 from "os";
6606
+ import { Database } from "bun:sqlite";
6607
+ function getModrinthCandidatePaths() {
6608
+ const home = os5.homedir();
6609
+ const paths = [];
6610
+ switch (os5.platform()) {
6611
+ case "win32":
6612
+ paths.push(path4.join(os5.homedir(), "AppData/Roaming/ModrinthApp"));
6613
+ break;
6614
+ case "darwin":
6615
+ paths.push(path4.join(home, "Library/Application Support/ModrinthApp"));
6616
+ break;
6617
+ case "linux":
6618
+ default: {
6619
+ const xdgDataHome = process.env.XDG_DATA_HOME;
6620
+ if (xdgDataHome) {
6621
+ paths.push(path4.join(xdgDataHome, "ModrinthApp"));
6622
+ }
6623
+ paths.push(path4.join(home, ".local/share/ModrinthApp"));
6624
+ paths.push(path4.join(home, ".var/app/com.modrinth.ModrinthApp/data/ModrinthApp"));
6625
+ break;
6626
+ }
6627
+ }
6628
+ return paths;
6629
+ }
6630
+ function getModrinthDataPath() {
6631
+ for (const candidate of getModrinthCandidatePaths()) {
6632
+ if (fs4.existsSync(candidate)) {
6633
+ return candidate;
6634
+ }
6635
+ }
6636
+ return null;
6637
+ }
6638
+ function getProfilesFromDb(dataPath) {
6639
+ const profiles = new Map;
6640
+ const dbPath = path4.join(dataPath, "app.db");
6641
+ if (!fs4.existsSync(dbPath))
6642
+ return profiles;
6643
+ try {
6644
+ const db = new Database(dbPath, { readonly: true });
6645
+ const rows = db.query("SELECT path, name, game_version FROM profiles").all();
6646
+ for (const row of rows) {
6647
+ profiles.set(row.path, {
6648
+ name: row.name,
6649
+ version: row.game_version ?? undefined
6650
+ });
6651
+ }
6652
+ db.close();
6653
+ } catch {}
6654
+ return profiles;
6655
+ }
6656
+ var modrinthProvider = {
6657
+ type: "modrinth",
6658
+ displayName: "Modrinth App",
6659
+ async isInstalled() {
6660
+ return getModrinthDataPath() !== null;
6661
+ },
6662
+ getDataPath() {
6663
+ return getModrinthDataPath();
6664
+ },
6665
+ async discoverInstances() {
6666
+ const dataPath = getModrinthDataPath();
6667
+ if (!dataPath)
6668
+ return [];
6669
+ const profilesDir = path4.join(dataPath, "profiles");
6670
+ if (!fs4.existsSync(profilesDir))
6671
+ return [];
6672
+ const profileMetadata = getProfilesFromDb(dataPath);
6673
+ const instances = [];
6674
+ try {
6675
+ const entries = fs4.readdirSync(profilesDir, { withFileTypes: true });
6676
+ for (const entry of entries) {
6677
+ if (!entry.isDirectory())
6678
+ continue;
6679
+ if (entry.name.startsWith("."))
6680
+ continue;
6681
+ const minecraftPath = path4.join(profilesDir, entry.name);
6682
+ const hasSaves = fs4.existsSync(path4.join(minecraftPath, "saves"));
6683
+ const hasMods = fs4.existsSync(path4.join(minecraftPath, "mods"));
6684
+ if (!hasSaves && !hasMods)
6685
+ continue;
6686
+ const metadata = profileMetadata.get(entry.name);
6687
+ instances.push({
6688
+ id: `modrinth-${entry.name}`,
6689
+ name: metadata?.name || entry.name,
6690
+ launcher: "modrinth",
6691
+ minecraftPath,
6692
+ version: metadata?.version
6693
+ });
6694
+ }
6695
+ } catch {}
6696
+ return instances;
6697
+ }
6698
+ };
6699
+
6700
+ // src/launchers/index.ts
6701
+ registerProvider(vanillaProvider);
6702
+ registerProvider(prismProvider);
6703
+ registerProvider(modrinthProvider);
6704
+
6413
6705
  // src/commands/create.ts
6414
6706
  function toJson(obj, pretty = false) {
6415
6707
  return util.inspect(obj, {
@@ -6420,9 +6712,78 @@ function toJson(obj, pretty = false) {
6420
6712
  maxArrayLength: Number(Infinity)
6421
6713
  });
6422
6714
  }
6715
+ function parseVersion(version) {
6716
+ if (!version)
6717
+ return null;
6718
+ const match = version.match(/^(\d+)\.(\d+)(?:\.(\d+))?/);
6719
+ if (match) {
6720
+ return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3] || "0")];
6721
+ }
6722
+ const snapshotMatch = version.match(/^(\d+)w(\d+)/);
6723
+ if (snapshotMatch) {
6724
+ return [1, parseInt(snapshotMatch[1]), parseInt(snapshotMatch[2])];
6725
+ }
6726
+ return null;
6727
+ }
6728
+ function compareVersions(a, b) {
6729
+ if (!a && !b)
6730
+ return 0;
6731
+ if (!a)
6732
+ return 1;
6733
+ if (!b)
6734
+ return -1;
6735
+ for (let i = 0;i < 3; i++) {
6736
+ if (a[i] !== b[i])
6737
+ return b[i] - a[i];
6738
+ }
6739
+ return 0;
6740
+ }
6741
+ async function selectClientInstance() {
6742
+ const { instances } = await discoverAllInstances();
6743
+ if (instances.length === 0) {
6744
+ return await input({ message: "No Minecraft installations detected. Enter path to .minecraft folder:" });
6745
+ }
6746
+ const vanilla = instances.find((i) => i.launcher === "vanilla");
6747
+ const otherInstances = instances.filter((i) => i.launcher !== "vanilla");
6748
+ otherInstances.sort((a, b) => {
6749
+ const versionCmp = compareVersions(parseVersion(a.version), parseVersion(b.version));
6750
+ if (versionCmp !== 0)
6751
+ return versionCmp;
6752
+ return a.name.localeCompare(b.name);
6753
+ });
6754
+ const choices = [];
6755
+ choices.push({ name: "Custom path...", value: "custom", short: "Custom" });
6756
+ choices.push({ name: "None (configure later)", value: "none", short: "None" });
6757
+ if (vanilla) {
6758
+ choices.push({
6759
+ name: `${vanilla.name} [${vanilla.launcher}]`,
6760
+ value: vanilla,
6761
+ short: vanilla.name
6762
+ });
6763
+ }
6764
+ for (const i of otherInstances) {
6765
+ choices.push({
6766
+ name: `${i.name}${i.version ? ` (${i.version})` : ""} [${i.launcher}]`,
6767
+ value: i,
6768
+ short: i.name
6769
+ });
6770
+ }
6771
+ const selected = await select({
6772
+ message: "Select Minecraft installation:",
6773
+ choices,
6774
+ default: vanilla ?? "none"
6775
+ });
6776
+ if (selected === "none") {
6777
+ return;
6778
+ }
6779
+ if (selected === "custom") {
6780
+ return await input({ message: "Enter path to .minecraft folder:" });
6781
+ }
6782
+ return selected.minecraftPath;
6783
+ }
6423
6784
  async function createCommand2(_project, opts) {
6424
- const projectPath = path2.resolve(_project);
6425
- const projectName = path2.basename(projectPath);
6785
+ const projectPath = path5.resolve(_project);
6786
+ const projectName = path5.basename(projectPath);
6426
6787
  const projectType = await confirm({
6427
6788
  message: "Whether your project will be a library for use in other Sandstone projects >",
6428
6789
  default: false
@@ -6490,22 +6851,33 @@ async function createCommand2(_project, opts) {
6490
6851
  }]
6491
6852
  });
6492
6853
  switch (saveChoice) {
6493
- case "root":
6854
+ case "root": {
6855
+ const clientPath = await selectClientInstance();
6856
+ if (clientPath) {
6857
+ saveOptions.clientPath = clientPath;
6858
+ }
6494
6859
  saveOptions.root = true;
6495
6860
  break;
6496
- case "world":
6861
+ }
6862
+ case "world": {
6863
+ const clientPath = await selectClientInstance();
6864
+ if (clientPath) {
6865
+ saveOptions.clientPath = clientPath;
6866
+ }
6497
6867
  const world = await select({
6498
6868
  message: "What world do you want to save the packs in? >",
6499
6869
  choices: getWorldsList(saveOptions.clientPath)
6500
6870
  });
6501
6871
  saveOptions.world = world;
6502
6872
  break;
6503
- case "server-path":
6873
+ }
6874
+ case "server-path": {
6504
6875
  const serverPath = await input({
6505
6876
  message: "Where is the server to save the packs in? Relative paths are accepted. >"
6506
6877
  });
6507
6878
  saveOptions.serverPath = serverPath;
6508
6879
  break;
6880
+ }
6509
6881
  case "none":
6510
6882
  break;
6511
6883
  }
@@ -6533,9 +6905,9 @@ async function createCommand2(_project, opts) {
6533
6905
  const exec = (cmd) => child.execSync(cmd, { cwd: projectPath });
6534
6906
  exec("git clone https://github.com/sandstone-mc/sandstone-template.git .");
6535
6907
  exec(`git checkout ${projectType}-${version[0]}`);
6536
- await import_fs_extra.default.rm(".git", { force: true, recursive: true });
6908
+ await import_fs_extra.default.rm(path5.join(projectPath, ".git"), { force: true, recursive: true });
6537
6909
  exec(`${packageManager} install`);
6538
- const configPath = path2.join(projectPath, `${projectType === "library" ? "test/" : ""}sandstone.config.ts`);
6910
+ const configPath = path5.join(projectPath, `${projectType === "library" ? "test/" : ""}sandstone.config.ts`);
6539
6911
  let templateConfig = await import_fs_extra.default.readFile(configPath, "utf8");
6540
6912
  templateConfig = templateConfig.replace("packUid: 'kZZpDK67'", `packUid: ${toJson(nanoid(8))}`);
6541
6913
  templateConfig = templateConfig.replace("name: 'template'", `name: ${toJson(packName)}`);