@evitcastudio/kit 3.2.0 → 3.2.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.
Files changed (2) hide show
  1. package/lib/bundle/cli/cli.js +573 -145
  2. package/package.json +1 -1
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ // @bun
2
3
 
3
4
  /*!
4
- * @evitcastudio/kit@3.1.1 git+https://github.com/EvitcaStudio/Kit.git
5
- * Compiled Wed, 09 Sep 2026 07:29:03 UTC
5
+ * @evitcastudio/kit@3.2.1 git+https://github.com/EvitcaStudio/Kit.git
6
+ * Compiled Wed, 09 Sep 2026 08:34:23 UTC
6
7
  * Copyright (c) 2026 Jared Bates, Evitca Studio, "doubleactii"
7
8
  *
8
9
  * @evitcastudio/kit is licensed under the MIT License.
9
10
  * http://www.opensource.org/licenses/mit-license
10
11
  */
11
- import { createRequire } from "node:module";
12
12
  var __create = Object.create;
13
13
  var __getProtoOf = Object.getPrototypeOf;
14
14
  var __defProp = Object.defineProperty;
@@ -26,7 +26,6 @@ var __toESM = (mod, isNodeMode, target) => {
26
26
  return to;
27
27
  };
28
28
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
29
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
30
29
 
31
30
  // node_modules/commander/lib/error.js
32
31
  var require_error = __commonJS((exports) => {
@@ -351,7 +350,7 @@ var require_help = __commonJS((exports) => {
351
350
  return Math.max(helper.longestOptionTermLength(cmd, helper), helper.longestGlobalOptionTermLength(cmd, helper), helper.longestSubcommandTermLength(cmd, helper), helper.longestArgumentTermLength(cmd, helper));
352
351
  }
353
352
  wrap(str, width, indent, minColumnWidth = 40) {
354
- const indents = " \\f\\t\\v   -    \uFEFF";
353
+ const indents = " \\f\\t\\v\xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF";
355
354
  const manualIndent = new RegExp(`[\\n][${indents}]+`);
356
355
  if (str.match(manualIndent))
357
356
  return str;
@@ -363,7 +362,7 @@ var require_help = __commonJS((exports) => {
363
362
  `, `
364
363
  `);
365
364
  const indentString = " ".repeat(indent);
366
- const zeroWidthSpace = "";
365
+ const zeroWidthSpace = "\u200B";
367
366
  const breaks = `\\s${zeroWidthSpace}`;
368
367
  const regex = new RegExp(`
369
368
  |.{1,${columnWidth - 1}}([${breaks}]|$)|[^${breaks}]+?([${breaks}]|$)`, "g");
@@ -606,11 +605,11 @@ var require_suggestSimilar = __commonJS((exports) => {
606
605
 
607
606
  // node_modules/commander/lib/command.js
608
607
  var require_command = __commonJS((exports) => {
609
- var EventEmitter = __require("node:events").EventEmitter;
610
- var childProcess = __require("node:child_process");
611
- var path = __require("node:path");
612
- var fs = __require("node:fs");
613
- var process2 = __require("node:process");
608
+ var EventEmitter = import.meta.require("events").EventEmitter;
609
+ var childProcess = import.meta.require("child_process");
610
+ var path = import.meta.require("path");
611
+ var fs = import.meta.require("fs");
612
+ var process2 = import.meta.require("process");
614
613
  var { Argument, humanReadableArgName } = require_argument();
615
614
  var { CommanderError } = require_error();
616
615
  var { Help } = require_help();
@@ -1943,8 +1942,8 @@ var {
1943
1942
  } = import__.default;
1944
1943
 
1945
1944
  // src/cli/resource-builder.ts
1946
- import { promises as fs, watch as fsWatch } from "fs";
1947
- import { join, extname, basename, resolve } from "path";
1945
+ import { promises as fs2, watch as fsWatch, existsSync as existsSync2 } from "fs";
1946
+ import { join as join2, extname as extname2, basename, resolve } from "path";
1948
1947
 
1949
1948
  // node_modules/chalk/source/vendor/ansi-styles/index.js
1950
1949
  var ANSI_BACKGROUND_OFFSET = 10;
@@ -2124,9 +2123,9 @@ var ansiStyles = assembleStyles();
2124
2123
  var ansi_styles_default = ansiStyles;
2125
2124
 
2126
2125
  // node_modules/chalk/source/vendor/supports-color/index.js
2127
- import process2 from "node:process";
2128
- import os from "node:os";
2129
- import tty from "node:tty";
2126
+ import process2 from "process";
2127
+ import os from "os";
2128
+ import tty from "tty";
2130
2129
  function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
2131
2130
  const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
2132
2131
  const position = argv.indexOf(prefix + flag);
@@ -7987,6 +7986,209 @@ class WorkerPool {
7987
7986
  }
7988
7987
  }
7989
7988
 
7989
+ // src/cli/app-bundler.ts
7990
+ import { promises as fs, existsSync } from "fs";
7991
+ import { join, dirname, extname } from "path";
7992
+ var Bun2 = globalThis.Bun;
7993
+ function detectArchitecture(pSrcDir) {
7994
+ const hasClientEntry = existsSync(join(pSrcDir, "client", "index.ts"));
7995
+ const hasServerEntry = existsSync(join(pSrcDir, "server", "index.ts"));
7996
+ const hasSingleEntry = existsSync(join(pSrcDir, "index.ts"));
7997
+ if (hasClientEntry || hasServerEntry) {
7998
+ return "multi";
7999
+ }
8000
+ if (hasSingleEntry) {
8001
+ return "single";
8002
+ }
8003
+ return "none";
8004
+ }
8005
+ async function copyDirectoryRecursive(pSourceDir, pDestDir) {
8006
+ if (!existsSync(pSourceDir))
8007
+ return;
8008
+ const entries = await fs.readdir(pSourceDir, { withFileTypes: true });
8009
+ await fs.mkdir(pDestDir, { recursive: true });
8010
+ for (const entry of entries) {
8011
+ const srcPath = join(pSourceDir, entry.name);
8012
+ const destPath = join(pDestDir, entry.name);
8013
+ if (entry.isDirectory()) {
8014
+ await copyDirectoryRecursive(srcPath, destPath);
8015
+ } else {
8016
+ await fs.copyFile(srcPath, destPath);
8017
+ }
8018
+ }
8019
+ }
8020
+ async function copyStaticWebFiles(pSourceDir, pDestDir, pBaseDir) {
8021
+ if (!existsSync(pSourceDir))
8022
+ return;
8023
+ const entries = await fs.readdir(pSourceDir, { withFileTypes: true });
8024
+ for (const entry of entries) {
8025
+ const fullPath = join(pSourceDir, entry.name);
8026
+ if (entry.isDirectory()) {
8027
+ if (entry.name === "vendor" || entry.name === "resources" || entry.name === "node_modules") {
8028
+ continue;
8029
+ }
8030
+ await copyStaticWebFiles(fullPath, pDestDir, pBaseDir);
8031
+ } else {
8032
+ const ext = extname(entry.name).toLowerCase();
8033
+ if ([".html", ".css", ".ico"].includes(ext)) {
8034
+ const relativePath = fullPath.slice(pBaseDir.length).replace(/^[/\\]+/, "");
8035
+ const targetPath = join(pDestDir, relativePath);
8036
+ await fs.mkdir(dirname(targetPath), { recursive: true });
8037
+ await fs.copyFile(fullPath, targetPath);
8038
+ }
8039
+ }
8040
+ }
8041
+ }
8042
+ async function bundleApp(pProjectRoot, pOutDir, pOptions = {}) {
8043
+ const srcDir = join(pProjectRoot, "src");
8044
+ const architecture = detectArchitecture(srcDir);
8045
+ const isVerbose = Boolean(pOptions.verbose);
8046
+ if (architecture === "none") {
8047
+ if (isVerbose) {
8048
+ console.log(source_default.yellow("[Kit CLI] No application entrypoint detected (src/index.ts or src/client/index.ts). Skipping code bundle."));
8049
+ }
8050
+ return { architecture: "none", success: true };
8051
+ }
8052
+ const isProd = Boolean(pOptions.prod);
8053
+ const shouldMinify = isProd || Boolean(pOptions.minify);
8054
+ const shouldObfuscate = isProd || Boolean(pOptions.obfuscate);
8055
+ const sourcemapMode = pOptions.sourcemap ?? (isProd ? "none" : "linked");
8056
+ let banner = "";
8057
+ const pkgPath = join(pProjectRoot, "package.json");
8058
+ if (existsSync(pkgPath)) {
8059
+ try {
8060
+ const pkg = JSON.parse(await fs.readFile(pkgPath, "utf8"));
8061
+ banner = [
8062
+ "/*!",
8063
+ ` * ${pkg.name || "game"}@${pkg.version || "1.0.0"}`,
8064
+ ` * Compiled ${new Date().toUTCString().replace(/GMT/g, "UTC")}`,
8065
+ " *",
8066
+ ` * ${pkg.name || "game"} is privately licensed. All rights reserved.`,
8067
+ ` * Author: ${pkg.author ?? "Unnamed"}`,
8068
+ " */"
8069
+ ].join(`
8070
+ `);
8071
+ } catch {
8072
+ }
8073
+ }
8074
+ try {
8075
+ if (architecture === "single") {
8076
+ const startStamp = Date.now();
8077
+ const clientResult = await Bun2.build({
8078
+ entrypoints: [join(srcDir, "index.ts")],
8079
+ naming: "index.js",
8080
+ outdir: pOutDir,
8081
+ target: "browser",
8082
+ banner,
8083
+ sourcemap: sourcemapMode,
8084
+ minify: shouldMinify ? {
8085
+ identifiers: shouldObfuscate,
8086
+ syntax: true,
8087
+ whitespace: true
8088
+ } : false
8089
+ });
8090
+ if (!clientResult.success) {
8091
+ console.error(clientResult.logs);
8092
+ throw new AggregateError(clientResult.logs, "Client build failed");
8093
+ }
8094
+ await copyStaticWebFiles(srcDir, pOutDir, srcDir);
8095
+ const rootFavicon = join(pProjectRoot, "favicon.ico");
8096
+ if (existsSync(rootFavicon)) {
8097
+ await fs.copyFile(rootFavicon, join(pOutDir, "favicon.ico"));
8098
+ }
8099
+ const vendorDir = join(srcDir, "vendor");
8100
+ if (existsSync(vendorDir)) {
8101
+ await copyDirectoryRecursive(vendorDir, join(pOutDir, "vendor"));
8102
+ }
8103
+ const elapsed = Date.now() - startStamp;
8104
+ if (isVerbose) {
8105
+ console.log(source_default.hex("#ffa552")(`[Kit CLI] Singleplayer Client Build took: ${elapsed}ms`));
8106
+ }
8107
+ return { architecture: "single", clientBuildTime: elapsed, success: true };
8108
+ }
8109
+ if (architecture === "multi") {
8110
+ const clientEntry = join(srcDir, "client", "index.ts");
8111
+ const serverEntry = join(srcDir, "server", "index.ts");
8112
+ let clientElapsed = 0;
8113
+ let serverElapsed = 0;
8114
+ if (existsSync(clientEntry)) {
8115
+ const clientStart = Date.now();
8116
+ const clientResult = await Bun2.build({
8117
+ entrypoints: [clientEntry],
8118
+ naming: "index.js",
8119
+ outdir: pOutDir,
8120
+ target: "browser",
8121
+ banner,
8122
+ sourcemap: sourcemapMode,
8123
+ minify: shouldMinify ? {
8124
+ identifiers: shouldObfuscate,
8125
+ syntax: true,
8126
+ whitespace: true
8127
+ } : false
8128
+ });
8129
+ if (!clientResult.success) {
8130
+ console.error(clientResult.logs);
8131
+ throw new AggregateError(clientResult.logs, "Multiplayer client build failed");
8132
+ }
8133
+ const clientSrc = join(srcDir, "client");
8134
+ await copyStaticWebFiles(clientSrc, pOutDir, clientSrc);
8135
+ const clientVendor = join(clientSrc, "vendor");
8136
+ if (existsSync(clientVendor)) {
8137
+ await copyDirectoryRecursive(clientVendor, join(pOutDir, "vendor"));
8138
+ }
8139
+ clientElapsed = Date.now() - clientStart;
8140
+ }
8141
+ if (existsSync(serverEntry)) {
8142
+ const serverStart = Date.now();
8143
+ const serverResult = await Bun2.build({
8144
+ entrypoints: [serverEntry],
8145
+ naming: "server.js",
8146
+ outdir: pOutDir,
8147
+ target: "node",
8148
+ banner,
8149
+ sourcemap: sourcemapMode,
8150
+ minify: shouldMinify ? {
8151
+ identifiers: shouldObfuscate,
8152
+ syntax: true,
8153
+ whitespace: true
8154
+ } : false
8155
+ });
8156
+ if (!serverResult.success) {
8157
+ console.error(serverResult.logs);
8158
+ throw new AggregateError(serverResult.logs, "Multiplayer server build failed");
8159
+ }
8160
+ const settingsFile = join(srcDir, "server", "settings.json");
8161
+ if (existsSync(settingsFile)) {
8162
+ await fs.copyFile(settingsFile, join(pOutDir, "settings.json"));
8163
+ }
8164
+ serverElapsed = Date.now() - serverStart;
8165
+ }
8166
+ const rootFavicon = join(pProjectRoot, "favicon.ico");
8167
+ if (existsSync(rootFavicon)) {
8168
+ await fs.copyFile(rootFavicon, join(pOutDir, "favicon.ico"));
8169
+ }
8170
+ if (isVerbose) {
8171
+ if (clientElapsed) {
8172
+ console.log(source_default.hex("#ffa552")(`[Kit CLI] Multiplayer Client Build took: ${clientElapsed}ms`));
8173
+ }
8174
+ if (serverElapsed) {
8175
+ console.log(source_default.hex("#ffa552")(`[Kit CLI] Multiplayer Server Build took: ${serverElapsed}ms`));
8176
+ }
8177
+ }
8178
+ return {
8179
+ architecture: "multi",
8180
+ clientBuildTime: clientElapsed,
8181
+ serverBuildTime: serverElapsed,
8182
+ success: true
8183
+ };
8184
+ }
8185
+ return { architecture: "none", success: true };
8186
+ } catch (pError) {
8187
+ console.error(source_default.hex("#c42847")(`[Kit CLI Build Error] ${pError}`));
8188
+ return { architecture, success: false };
8189
+ }
8190
+ }
8191
+
7990
8192
  // src/cli/resource-builder.ts
7991
8193
  var log = console.log;
7992
8194
  var info = source_default.hex("#ffa552");
@@ -8009,7 +8211,7 @@ function initializeResourceJSON() {
8009
8211
  }, {});
8010
8212
  }
8011
8213
  function prepareFileForProcessing(pFilePath) {
8012
- const extension = extname(pFilePath).slice(1);
8214
+ const extension = extname2(pFilePath).slice(1);
8013
8215
  const fileName = basename(pFilePath);
8014
8216
  const resourceIdentifier = `${v4_default()}.vyr`;
8015
8217
  const type = getResourceType(extension);
@@ -8045,19 +8247,19 @@ function getResourceType(pExtension) {
8045
8247
  }
8046
8248
  async function processDirectory(pDirectoryPath) {
8047
8249
  try {
8048
- const contents = await fs.readdir(pDirectoryPath);
8250
+ const contents = await fs2.readdir(pDirectoryPath);
8049
8251
  for (const item of contents) {
8050
- const itemPath = join(pDirectoryPath, item);
8252
+ const itemPath = join2(pDirectoryPath, item);
8051
8253
  if (resolve(itemPath) === resourceOutDirectory) {
8052
8254
  continue;
8053
8255
  }
8054
- const stats = await fs.stat(itemPath);
8256
+ const stats = await fs2.stat(itemPath);
8055
8257
  if (stats.isDirectory()) {
8056
8258
  if (pDirectoryPath === resourceInDirectory) {
8057
8259
  subdirectoriesToMirror.push(item);
8058
8260
  }
8059
8261
  await processDirectory(itemPath);
8060
- } else if (isValidExtension(extname(itemPath).slice(1))) {
8262
+ } else if (isValidExtension(extname2(itemPath).slice(1))) {
8061
8263
  prepareFileForProcessing(itemPath);
8062
8264
  }
8063
8265
  }
@@ -8069,17 +8271,17 @@ function isValidExtension(pExtension) {
8069
8271
  return VALID_EXTENSIONS.includes(pExtension);
8070
8272
  }
8071
8273
  async function mirrorDirectory(pSourceDir, pDestDir) {
8072
- const entries = await fs.readdir(pSourceDir, { withFileTypes: true });
8073
- await fs.mkdir(pDestDir, { recursive: true });
8274
+ const entries = await fs2.readdir(pSourceDir, { withFileTypes: true });
8275
+ await fs2.mkdir(pDestDir, { recursive: true });
8074
8276
  for (const entry of entries) {
8075
- const srcPath = join(pSourceDir, entry.name);
8076
- const destPath = join(pDestDir, entry.name);
8277
+ const srcPath = join2(pSourceDir, entry.name);
8278
+ const destPath = join2(pDestDir, entry.name);
8077
8279
  if (entry.isDirectory()) {
8078
8280
  await mirrorDirectory(srcPath, destPath);
8079
8281
  } else {
8080
- const ext = extname(entry.name).slice(1);
8282
+ const ext = extname2(entry.name).slice(1);
8081
8283
  if (!isValidExtension(ext)) {
8082
- await fs.copyFile(srcPath, destPath);
8284
+ await fs2.copyFile(srcPath, destPath);
8083
8285
  }
8084
8286
  }
8085
8287
  }
@@ -8093,14 +8295,14 @@ async function processAllFiles() {
8093
8295
  if (!resource) {
8094
8296
  throw new Error(`Resource not found for file: ${fileName}`);
8095
8297
  }
8096
- const destination = join(resourceOutDirectory, "resources");
8298
+ const destination = join2(resourceOutDirectory, "resources");
8097
8299
  const resourceName = resource.resourceIdentifier;
8098
8300
  return copyFile(filePath, destination, resourceName);
8099
8301
  });
8100
8302
  await Promise.all(copyOperations);
8101
8303
  for (const subDir of subdirectoriesToMirror) {
8102
- const srcPath = join(resourceInDirectory, subDir);
8103
- const destPath = join(resourceOutDirectory, "resources", subDir);
8304
+ const srcPath = join2(resourceInDirectory, subDir);
8305
+ const destPath = join2(resourceOutDirectory, "resources", subDir);
8104
8306
  await mirrorDirectory(srcPath, destPath);
8105
8307
  }
8106
8308
  logVerbose(`[Kit CLI] All resources have been processed.`);
@@ -8122,7 +8324,7 @@ async function buildBoundsJSON() {
8122
8324
  if (type !== "icon")
8123
8325
  continue;
8124
8326
  try {
8125
- const fileBuffer = await fs.readFile(filePath);
8327
+ const fileBuffer = await fs2.readFile(filePath);
8126
8328
  const vyi = new VYI().parse(fileBuffer);
8127
8329
  const atlasName = basename(filePath, ".vyi");
8128
8330
  const atlasEntry = {};
@@ -8163,7 +8365,7 @@ async function buildBoundsJSON() {
8163
8365
  async function saveBoundsJSON(pBoundsData) {
8164
8366
  const filePath = "bounds.json";
8165
8367
  try {
8166
- await fs.writeFile(filePath, JSON.stringify(pBoundsData));
8368
+ await fs2.writeFile(filePath, JSON.stringify(pBoundsData));
8167
8369
  } catch (pError) {
8168
8370
  logError(`[Error] Saving bounds JSON: ${pError}`);
8169
8371
  }
@@ -8174,7 +8376,7 @@ async function buildIconPointsJSON() {
8174
8376
  if (type !== "icon")
8175
8377
  continue;
8176
8378
  try {
8177
- const fileBuffer = await fs.readFile(filePath);
8379
+ const fileBuffer = await fs2.readFile(filePath);
8178
8380
  const vyi = new VYI().parse(fileBuffer);
8179
8381
  const atlasName = basename(filePath, ".vyi");
8180
8382
  const atlasEntry = {};
@@ -8233,7 +8435,7 @@ async function buildIconPointsJSON() {
8233
8435
  async function saveIconPointsJSON(pPointsData) {
8234
8436
  const filePath = "icon-points.json";
8235
8437
  try {
8236
- await fs.writeFile(filePath, JSON.stringify(pPointsData));
8438
+ await fs2.writeFile(filePath, JSON.stringify(pPointsData));
8237
8439
  } catch (pError) {
8238
8440
  logError(`[Error] Saving icon points JSON: ${pError}`);
8239
8441
  }
@@ -8244,7 +8446,7 @@ async function buildSizesJSON() {
8244
8446
  if (type !== "icon")
8245
8447
  continue;
8246
8448
  try {
8247
- const fileBuffer = await fs.readFile(filePath);
8449
+ const fileBuffer = await fs2.readFile(filePath);
8248
8450
  const vyi = new VYI().parse(fileBuffer);
8249
8451
  const atlasName = basename(filePath, ".vyi");
8250
8452
  const atlasEntry = {};
@@ -8266,16 +8468,16 @@ async function buildSizesJSON() {
8266
8468
  async function saveSizesJSON(pSizesData) {
8267
8469
  const filePath = "sizes.json";
8268
8470
  try {
8269
- await fs.writeFile(filePath, JSON.stringify(pSizesData));
8471
+ await fs2.writeFile(filePath, JSON.stringify(pSizesData));
8270
8472
  } catch (pError) {
8271
8473
  logError(`[Error] Saving sizes JSON: ${pError}`);
8272
8474
  }
8273
8475
  }
8274
8476
  async function clearResourceTypeDirectories(pBaseDirectory) {
8275
8477
  try {
8276
- const directoryExists = await fs.stat(pBaseDirectory).then((stat) => stat.isDirectory()).catch(() => false);
8478
+ const directoryExists = await fs2.stat(pBaseDirectory).then((stat) => stat.isDirectory()).catch(() => false);
8277
8479
  if (directoryExists) {
8278
- await fs.rm(pBaseDirectory, { recursive: true });
8480
+ await fs2.rm(pBaseDirectory, { recursive: true });
8279
8481
  }
8280
8482
  } catch (pError) {
8281
8483
  log(`${error(`[Error]`)} clearing resource directory: ${pError}`);
@@ -8283,8 +8485,8 @@ async function clearResourceTypeDirectories(pBaseDirectory) {
8283
8485
  }
8284
8486
  async function copyFile(pSource, pDestinationDir, pNewName) {
8285
8487
  try {
8286
- await fs.mkdir(pDestinationDir, { recursive: true });
8287
- await fs.copyFile(pSource, join(pDestinationDir, pNewName));
8488
+ await fs2.mkdir(pDestinationDir, { recursive: true });
8489
+ await fs2.copyFile(pSource, join2(pDestinationDir, pNewName));
8288
8490
  } catch (pError) {
8289
8491
  logError(`[Error] Copying file ${pSource}: ${pError}`);
8290
8492
  }
@@ -8293,16 +8495,21 @@ async function saveResourceJSON() {
8293
8495
  const jsonContent = JSON.stringify(resourceJSON, null, 4);
8294
8496
  const targetPath = customManifestPath || "resource.json";
8295
8497
  try {
8296
- await fs.writeFile(targetPath, jsonContent, "utf8");
8498
+ await fs2.writeFile(targetPath, jsonContent, "utf8");
8297
8499
  } catch (pError) {
8298
8500
  logError(`[Error] Saving resource JSON to ${targetPath}: ${pError}`);
8299
8501
  }
8300
8502
  }
8503
+ var shouldBundleApp = false;
8504
+ var projectRootDirectory = "";
8505
+ var appBundleOptions = {};
8301
8506
  async function runBuild() {
8302
8507
  resourceJSON = initializeResourceJSON();
8303
8508
  resourcesToProcess.length = 0;
8304
8509
  subdirectoriesToMirror.length = 0;
8305
- await processDirectory(resourceInDirectory);
8510
+ if (existsSync2(resourceInDirectory)) {
8511
+ await processDirectory(resourceInDirectory);
8512
+ }
8306
8513
  if (resourcesToProcess.length > 0 || subdirectoriesToMirror.length > 0) {
8307
8514
  await processAllFiles();
8308
8515
  } else {
@@ -8312,6 +8519,12 @@ async function runBuild() {
8312
8519
  await saveIconPointsJSON({});
8313
8520
  await saveSizesJSON({});
8314
8521
  }
8522
+ if (shouldBundleApp && projectRootDirectory) {
8523
+ await bundleApp(projectRootDirectory, resourceOutDirectory, {
8524
+ ...appBundleOptions,
8525
+ verbose: Boolean(isVerbose)
8526
+ });
8527
+ }
8315
8528
  logVerbose(`[Kit CLI] Resources built successfully.`);
8316
8529
  }
8317
8530
  async function runWatch() {
@@ -8319,28 +8532,57 @@ async function runWatch() {
8319
8532
  console.log(source_default.cyan(`
8320
8533
  \uD83D\uDC40 Watching for changes in: ${source_default.bold(resourceInDirectory)}`));
8321
8534
  let debounceTimer = null;
8322
- const watcher = fsWatch(resourceInDirectory, { recursive: true }, (_eventType, pFilename) => {
8323
- if (!pFilename)
8324
- return;
8535
+ const triggerRebuild = (pFilename) => {
8325
8536
  if (debounceTimer)
8326
8537
  clearTimeout(debounceTimer);
8327
8538
  debounceTimer = setTimeout(async () => {
8328
8539
  console.log(source_default.dim(`
8329
- Asset changed: ${pFilename}, rebuilding...`));
8540
+ File changed: ${pFilename}, rebuilding...`));
8330
8541
  await runBuild();
8331
8542
  }, 150);
8332
- });
8543
+ };
8544
+ const watchers = [];
8545
+ if (existsSync2(resourceInDirectory)) {
8546
+ const resWatcher = fsWatch(resourceInDirectory, { recursive: true }, (_eventType, pFilename) => {
8547
+ if (pFilename)
8548
+ triggerRebuild(pFilename);
8549
+ });
8550
+ watchers.push(resWatcher);
8551
+ }
8552
+ const srcDir = join2(projectRootDirectory, "src");
8553
+ if (shouldBundleApp && existsSync2(srcDir) && srcDir !== resourceInDirectory) {
8554
+ const srcWatcher = fsWatch(srcDir, { recursive: true }, (_eventType, pFilename) => {
8555
+ if (!pFilename)
8556
+ return;
8557
+ if (pFilename.startsWith("resources"))
8558
+ return;
8559
+ triggerRebuild(pFilename);
8560
+ });
8561
+ watchers.push(srcWatcher);
8562
+ }
8333
8563
  process.on("SIGINT", () => {
8334
- watcher.close();
8564
+ for (const w of watchers)
8565
+ w.close();
8335
8566
  process.exit(0);
8336
8567
  });
8337
8568
  }
8338
- async function processResources({ inDirectory, outDirectory, manifestPath, watch, verbose, ignoreSound }) {
8339
- resourceInDirectory = inDirectory ? resolve(inDirectory) : "";
8340
- resourceOutDirectory = outDirectory ? resolve(outDirectory) : "";
8569
+ async function processResources({ inDirectory, outDirectory, manifestPath, watch, verbose, ignoreSound, app, minify, obfuscate, sourcemap, prod }) {
8570
+ projectRootDirectory = process.cwd();
8571
+ const resolvedIn = inDirectory || (existsSync2(join2(projectRootDirectory, "src", "resources")) ? "src/resources" : "");
8572
+ const resolvedOut = outDirectory || "dist";
8573
+ resourceInDirectory = resolvedIn ? resolve(resolvedIn) : "";
8574
+ resourceOutDirectory = resolvedOut ? resolve(resolvedOut) : "";
8341
8575
  customManifestPath = manifestPath;
8342
8576
  isVerbose = verbose;
8343
8577
  ignoringSound = ignoreSound;
8578
+ appBundleOptions = {
8579
+ minify,
8580
+ obfuscate,
8581
+ sourcemap,
8582
+ prod
8583
+ };
8584
+ const hasAppEntry = existsSync2(join2(projectRootDirectory, "src", "index.ts")) || existsSync2(join2(projectRootDirectory, "src", "client", "index.ts")) || existsSync2(join2(projectRootDirectory, "src", "server", "index.ts"));
8585
+ shouldBundleApp = app !== undefined ? app : hasAppEntry;
8344
8586
  if (!resourceInDirectory || !resourceOutDirectory) {
8345
8587
  logError("[Error] Input and output directories must be specified");
8346
8588
  return;
@@ -8364,11 +8606,11 @@ function logAlert(pMessage) {
8364
8606
 
8365
8607
  // node_modules/@clack/core/dist/index.mjs
8366
8608
  var import_sisteransi = __toESM(require_src(), 1);
8367
- import { styleText as D } from "node:util";
8368
- import { stdout as R, stdin as q } from "node:process";
8369
- import * as k from "node:readline";
8370
- import ot from "node:readline";
8371
- import { ReadStream as J } from "node:tty";
8609
+ import { styleText as D } from "util";
8610
+ import { stdout as R, stdin as q } from "process";
8611
+ import * as k from "readline";
8612
+ import ot from "readline";
8613
+ import { ReadStream as J } from "tty";
8372
8614
  function x(t, e, s) {
8373
8615
  if (!s.some((u) => !u.disabled))
8374
8616
  return t;
@@ -8452,7 +8694,7 @@ var X = (t, e = {}, s = {}) => {
8452
8694
  var pt = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 };
8453
8695
  var S = (t, e = {}) => X(t, pt, e).width;
8454
8696
  var T = "\x1B";
8455
- var Z = "›";
8697
+ var Z = "\x9B";
8456
8698
  var Ft = 39;
8457
8699
  var j = "\x07";
8458
8700
  var Q = "[";
@@ -8773,7 +9015,7 @@ class Vt extends B {
8773
9015
  if (!this.userInput)
8774
9016
  return D(["inverse", "hidden"], "_");
8775
9017
  if (this._cursor >= this.userInput.length)
8776
- return `${this.userInput}█`;
9018
+ return `${this.userInput}\u2588`;
8777
9019
  const e = this.userInput.slice(0, this._cursor), [s, ...i] = this.userInput.slice(this._cursor);
8778
9020
  return `${e}${D("inverse", s)}${i.join("")}`;
8779
9021
  }
@@ -8916,7 +9158,7 @@ class $t extends B {
8916
9158
  return this.userInput;
8917
9159
  const e = this.userInput;
8918
9160
  if (this.cursor >= e.length)
8919
- return `${this.userInput}█`;
9161
+ return `${this.userInput}\u2588`;
8920
9162
  const s = e.slice(0, this.cursor), [i, ...r] = e.slice(this.cursor);
8921
9163
  return `${s}${D("inverse", i)}${r.join("")}`;
8922
9164
  }
@@ -8933,8 +9175,8 @@ class $t extends B {
8933
9175
  }
8934
9176
 
8935
9177
  // node_modules/@clack/prompts/dist/index.mjs
8936
- import { styleText as t, stripVTControlCharacters as ue } from "node:util";
8937
- import N2 from "node:process";
9178
+ import { styleText as t, stripVTControlCharacters as ue } from "util";
9179
+ import N2 from "process";
8938
9180
  var import_sisteransi2 = __toESM(require_src(), 1);
8939
9181
  function pt2() {
8940
9182
  return N2.platform !== "win32" ? N2.env.TERM !== "linux" : !!N2.env.CI || !!N2.env.WT_SESSION || !!N2.env.TERMINUS_SUBLIME || N2.env.ConEmuTask === "{cmd::Cmder}" || N2.env.TERM_PROGRAM === "Terminus-Sublime" || N2.env.TERM_PROGRAM === "vscode" || N2.env.TERM === "xterm-256color" || N2.env.TERM === "alacritty" || N2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
@@ -8942,31 +9184,31 @@ function pt2() {
8942
9184
  var ee = pt2();
8943
9185
  var ce = () => process.env.CI === "true";
8944
9186
  var I2 = (e, r) => ee ? e : r;
8945
- var Re = I2("", "*");
8946
- var $e = I2("", "x");
8947
- var de = I2("", "x");
8948
- var V = I2("", "o");
8949
- var he = I2("", "T");
8950
- var h = I2("", "|");
8951
- var x2 = I2("", "");
8952
- var Oe = I2("", "T");
8953
- var Pe = I2("", "");
8954
- var z2 = I2("", ">");
8955
- var H2 = I2("", " ");
8956
- var te = I2("", "[]");
8957
- var U2 = I2("", "[+]");
8958
- var q2 = I2("", "[ ]");
8959
- var Ne = I2("", "");
8960
- var se = I2("", "-");
8961
- var pe = I2("", "+");
8962
- var We = I2("", "+");
8963
- var me = I2("", "+");
8964
- var ge = I2("", "+");
8965
- var Ge = I2("", "+");
8966
- var fe = I2("", "");
8967
- var Fe = I2("", "*");
8968
- var ye = I2("", "!");
8969
- var Ee = I2("", "x");
9187
+ var Re = I2("\u25C6", "*");
9188
+ var $e = I2("\u25A0", "x");
9189
+ var de = I2("\u25B2", "x");
9190
+ var V = I2("\u25C7", "o");
9191
+ var he = I2("\u250C", "T");
9192
+ var h = I2("\u2502", "|");
9193
+ var x2 = I2("\u2514", "\u2014");
9194
+ var Oe = I2("\u2510", "T");
9195
+ var Pe = I2("\u2518", "\u2014");
9196
+ var z2 = I2("\u25CF", ">");
9197
+ var H2 = I2("\u25CB", " ");
9198
+ var te = I2("\u25FB", "[\u2022]");
9199
+ var U2 = I2("\u25FC", "[+]");
9200
+ var q2 = I2("\u25FB", "[ ]");
9201
+ var Ne = I2("\u25AA", "\u2022");
9202
+ var se = I2("\u2500", "-");
9203
+ var pe = I2("\u256E", "+");
9204
+ var We = I2("\u251C", "+");
9205
+ var me = I2("\u256F", "+");
9206
+ var ge = I2("\u2570", "+");
9207
+ var Ge = I2("\u256D", "+");
9208
+ var fe = I2("\u25CF", "\u2022");
9209
+ var Fe = I2("\u25C6", "*");
9210
+ var ye = I2("\u25B2", "!");
9211
+ var Ee = I2("\u25A0", "x");
8970
9212
  var W2 = (e) => {
8971
9213
  switch (e) {
8972
9214
  case "initial":
@@ -9070,7 +9312,7 @@ var Le = (e, r = {}, s = {}) => {
9070
9312
  var Et2 = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 };
9071
9313
  var D2 = (e, r = {}) => Le(e, Et2, r).width;
9072
9314
  var ae = "\x1B";
9073
- var je = "›";
9315
+ var je = "\x9B";
9074
9316
  var vt2 = 39;
9075
9317
  var Ce = "\x07";
9076
9318
  var ke = "[";
@@ -9273,7 +9515,7 @@ ${t("gray", f + se.repeat(n + 2) + me)}
9273
9515
  `);
9274
9516
  };
9275
9517
  var Kt = (e) => t("magenta", e);
9276
- var be = ({ indicator: e = "dots", onCancel: r, output: s = process.stdout, cancelMessage: i, errorMessage: a, frames: o = ee ? ["", "", "", ""] : ["", "o", "O", "0"], delay: u = ee ? 80 : 120, signal: l, ...n } = {}) => {
9518
+ var be = ({ indicator: e = "dots", onCancel: r, output: s = process.stdout, cancelMessage: i, errorMessage: a, frames: o = ee ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], delay: u = ee ? 80 : 120, signal: l, ...n } = {}) => {
9277
9519
  const c = ce();
9278
9520
  let p, f, g = false, E = false, $ = "", m, d = performance.now();
9279
9521
  const F = rt(s), y2 = n?.styleFrame ?? Kt, v = (B2) => {
@@ -9330,7 +9572,7 @@ var be = ({ indicator: e = "dots", onCancel: r, output: s = process.stdout, canc
9330
9572
  return E;
9331
9573
  } };
9332
9574
  };
9333
- var ze = { light: I2("", "-"), heavy: I2("", "="), block: I2("", "#") };
9575
+ var ze = { light: I2("\u2500", "-"), heavy: I2("\u2501", "="), block: I2("\u2588", "#") };
9334
9576
  var oe = (e, r) => e.includes(`
9335
9577
  `) ? e.split(`
9336
9578
  `).map((s) => r(s)).join(`
@@ -9408,15 +9650,15 @@ ${l}
9408
9650
  } }).prompt();
9409
9651
 
9410
9652
  // src/cli/init.ts
9411
- import fs2 from "node:fs";
9412
- import path from "node:path";
9413
- import { fileURLToPath } from "node:url";
9414
- import { spawnSync, execSync } from "node:child_process";
9415
- import os2 from "node:os";
9653
+ import fs3 from "fs";
9654
+ import path from "path";
9655
+ import { fileURLToPath } from "url";
9656
+ import { spawnSync, execSync } from "child_process";
9657
+ import os2 from "os";
9416
9658
  // package.json
9417
9659
  var package_default = {
9418
9660
  name: "@evitcastudio/kit",
9419
- version: "3.1.1",
9661
+ version: "3.2.1",
9420
9662
  author: "doubleactii 56242467+doubleactii@users.noreply.github.com (https://evitcastudio.com)",
9421
9663
  main: "./lib/index.js",
9422
9664
  types: "./lib/index.d.ts",
@@ -9516,15 +9758,15 @@ function installBun() {
9516
9758
  }
9517
9759
  }
9518
9760
  function copyTemplate(pSrc, pDest, pProjectName, pVersion, pAuthor) {
9519
- if (!fs2.existsSync(pDest)) {
9520
- fs2.mkdirSync(pDest, { recursive: true });
9761
+ if (!fs3.existsSync(pDest)) {
9762
+ fs3.mkdirSync(pDest, { recursive: true });
9521
9763
  }
9522
- const files = fs2.readdirSync(pSrc);
9764
+ const files = fs3.readdirSync(pSrc);
9523
9765
  for (const file of files) {
9524
9766
  const srcPath = path.join(pSrc, file);
9525
9767
  const destFile = file === "_gitignore" ? ".gitignore" : file;
9526
9768
  const destPath = path.join(pDest, destFile);
9527
- const stats = fs2.statSync(srcPath);
9769
+ const stats = fs3.statSync(srcPath);
9528
9770
  if (stats.isDirectory()) {
9529
9771
  if (file === ".git")
9530
9772
  continue;
@@ -9533,13 +9775,13 @@ function copyTemplate(pSrc, pDest, pProjectName, pVersion, pAuthor) {
9533
9775
  const ext = path.extname(file).toLowerCase();
9534
9776
  const textExtensions = [".ts", ".js", ".json", ".html", ".css", ".md", ".txt"];
9535
9777
  if (textExtensions.includes(ext)) {
9536
- let content = fs2.readFileSync(srcPath, "utf8");
9778
+ let content = fs3.readFileSync(srcPath, "utf8");
9537
9779
  content = content.replace(/{{PROJECT_NAME}}/g, pProjectName);
9538
9780
  content = content.replace(/{{VERSION}}/g, pVersion);
9539
9781
  content = content.replace(/{{PROJECT_AUTHOR}}/g, pAuthor);
9540
- fs2.writeFileSync(destPath, content);
9782
+ fs3.writeFileSync(destPath, content);
9541
9783
  } else {
9542
- fs2.copyFileSync(srcPath, destPath);
9784
+ fs3.copyFileSync(srcPath, destPath);
9543
9785
  }
9544
9786
  }
9545
9787
  }
@@ -9619,7 +9861,7 @@ Note: Kit projects cannot build or run without Bun.`);
9619
9861
  projectName = "kit-project";
9620
9862
  }
9621
9863
  const projectDir = path.join(process.cwd(), projectName);
9622
- if (fs2.existsSync(projectDir)) {
9864
+ if (fs3.existsSync(projectDir)) {
9623
9865
  if (isInteractive) {
9624
9866
  const overwrite = await Rt({
9625
9867
  message: `Directory ${source_default.cyan(projectName)} already exists. Overwrite?`,
@@ -9639,18 +9881,18 @@ Error: Destination directory '${projectName}' already exists. Use --force (-f) t
9639
9881
  s.start(`Scaffolding ${source_default.cyan(projectName)}...`);
9640
9882
  try {
9641
9883
  const projectPath = path.join(process.cwd(), projectName);
9642
- if (fs2.existsSync(projectPath)) {
9643
- fs2.rmSync(projectPath, { recursive: true, force: true });
9884
+ if (fs3.existsSync(projectPath)) {
9885
+ fs3.rmSync(projectPath, { recursive: true, force: true });
9644
9886
  }
9645
- fs2.mkdirSync(projectPath, { recursive: true });
9887
+ fs3.mkdirSync(projectPath, { recursive: true });
9646
9888
  let templateType = gameType;
9647
9889
  if (gameType === "both")
9648
9890
  templateType = "multi";
9649
9891
  const templatesDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "kit-game-templates", templateType);
9650
9892
  const author = getGitUser();
9651
- if (!fs2.existsSync(templatesDir)) {
9893
+ if (!fs3.existsSync(templatesDir)) {
9652
9894
  const localTemplatesDir = path.join(process.cwd(), "kit-game-templates", templateType);
9653
- if (!fs2.existsSync(localTemplatesDir)) {
9895
+ if (!fs3.existsSync(localTemplatesDir)) {
9654
9896
  console.error(source_default.red(`Error: Templates not found at ${templatesDir} or ${localTemplatesDir}`));
9655
9897
  process.exit(1);
9656
9898
  }
@@ -9678,23 +9920,23 @@ Error: Destination directory '${projectName}' already exists. Use --force (-f) t
9678
9920
  installSpinner.stop(source_default.yellow("Dependency installation finished with warnings."));
9679
9921
  }
9680
9922
  }
9681
- console.log(`${source_default.cyan("")}`);
9682
- console.log(`${source_default.cyan("")} ${source_default.white.bold("Next steps:")}`);
9683
- console.log(`${source_default.cyan("")} ${source_default.dim("1.")} cd ${source_default.cyan(projectName)}`);
9923
+ console.log(`${source_default.cyan("\u2502")}`);
9924
+ console.log(`${source_default.cyan("\u2502")} ${source_default.white.bold("Next steps:")}`);
9925
+ console.log(`${source_default.cyan("\u2502")} ${source_default.dim("1.")} cd ${source_default.cyan(projectName)}`);
9684
9926
  if (!shouldInstall) {
9685
- console.log(`${source_default.cyan("")} ${source_default.dim("2.")} bun install`);
9686
- console.log(`${source_default.cyan("")} ${source_default.dim("3.")} bun run build`);
9927
+ console.log(`${source_default.cyan("\u2502")} ${source_default.dim("2.")} bun install`);
9928
+ console.log(`${source_default.cyan("\u2502")} ${source_default.dim("3.")} bun run build`);
9687
9929
  } else {
9688
- console.log(`${source_default.cyan("")} ${source_default.dim("2.")} bun run build`);
9930
+ console.log(`${source_default.cyan("\u2502")} ${source_default.dim("2.")} bun run build`);
9689
9931
  }
9690
- console.log(`${source_default.cyan("")}`);
9932
+ console.log(`${source_default.cyan("\u2502")}`);
9691
9933
  Gt(source_default.green.bold("Happy coding!"));
9692
9934
  } catch (pError) {
9693
9935
  s.stop(source_default.red("Scaffolding failed."));
9694
9936
  if (projectName) {
9695
9937
  const projectPath = path.join(process.cwd(), projectName);
9696
- if (fs2.existsSync(projectPath)) {
9697
- fs2.rmSync(projectPath, { recursive: true, force: true });
9938
+ if (fs3.existsSync(projectPath)) {
9939
+ fs3.rmSync(projectPath, { recursive: true, force: true });
9698
9940
  }
9699
9941
  }
9700
9942
  console.error(pError);
@@ -9703,9 +9945,9 @@ Error: Destination directory '${projectName}' already exists. Use --force (-f) t
9703
9945
  }
9704
9946
 
9705
9947
  // src/cli/doctor.ts
9706
- import { spawnSync as spawnSync2 } from "node:child_process";
9707
- import { existsSync, readFileSync } from "node:fs";
9708
- import { join as join2 } from "node:path";
9948
+ import { spawnSync as spawnSync2 } from "child_process";
9949
+ import { existsSync as existsSync3, readFileSync } from "fs";
9950
+ import { join as join3 } from "path";
9709
9951
  async function processDoctor(pOptions = {}) {
9710
9952
  const isVerbose2 = Boolean(pOptions.verbose);
9711
9953
  const results = [];
@@ -9745,8 +9987,8 @@ async function processDoctor(pOptions = {}) {
9745
9987
  });
9746
9988
  }
9747
9989
  const cwd = process.cwd();
9748
- const pkgPath = join2(cwd, "package.json");
9749
- const hasPackageJson = existsSync(pkgPath);
9990
+ const pkgPath = join3(cwd, "package.json");
9991
+ const hasPackageJson = existsSync3(pkgPath);
9750
9992
  if (hasPackageJson) {
9751
9993
  try {
9752
9994
  const rawPkg = readFileSync(pkgPath, "utf8");
@@ -9766,8 +10008,8 @@ async function processDoctor(pOptions = {}) {
9766
10008
  });
9767
10009
  const isKitCoreRepo = pkg.name === "@evitcastudio/kit";
9768
10010
  if (!isKitCoreRepo) {
9769
- const resourcesDir = join2(cwd, "src", "resources");
9770
- const hasResources = existsSync(resourcesDir);
10011
+ const resourcesDir = join3(cwd, "src", "resources");
10012
+ const hasResources = existsSync3(resourcesDir);
9771
10013
  results.push({
9772
10014
  category: "Project",
9773
10015
  title: "Asset Directory (src/resources)",
@@ -9775,6 +10017,25 @@ async function processDoctor(pOptions = {}) {
9775
10017
  details: hasResources ? "Asset directory present" : "src/resources not found"
9776
10018
  });
9777
10019
  }
10020
+ const hasClientEntry = existsSync3(join3(cwd, "src", "client", "index.ts"));
10021
+ const hasServerEntry = existsSync3(join3(cwd, "src", "server", "index.ts"));
10022
+ const hasSingleEntry = existsSync3(join3(cwd, "src", "index.ts"));
10023
+ let projectType = "Unknown";
10024
+ if (hasClientEntry && hasServerEntry) {
10025
+ projectType = "Multiplayer (Client & Server)";
10026
+ } else if (hasClientEntry) {
10027
+ projectType = "Multiplayer (Client)";
10028
+ } else if (hasServerEntry) {
10029
+ projectType = "Dedicated Server";
10030
+ } else if (hasSingleEntry) {
10031
+ projectType = "Singleplayer";
10032
+ }
10033
+ results.push({
10034
+ category: "Project",
10035
+ title: "Project Architecture",
10036
+ passed: projectType !== "Unknown" || isKitCoreRepo,
10037
+ details: isKitCoreRepo ? "Kit Core Framework Repository" : `Detected Type: ${projectType}`
10038
+ });
9778
10039
  } catch {
9779
10040
  results.push({
9780
10041
  category: "Project",
@@ -9783,13 +10044,31 @@ async function processDoctor(pOptions = {}) {
9783
10044
  details: "package.json is malformed or invalid JSON"
9784
10045
  });
9785
10046
  }
9786
- const buildScript = join2(cwd, "bun-build.ts");
9787
- const hasBuildScript = existsSync(buildScript);
10047
+ const buildScript = join3(cwd, "bun-build.ts");
10048
+ const hasBuildScript = existsSync3(buildScript);
10049
+ let hasKitBuildScript = false;
10050
+ try {
10051
+ const rawPkg = readFileSync(pkgPath, "utf8");
10052
+ const pkg = JSON.parse(rawPkg);
10053
+ if (pkg.scripts && typeof pkg.scripts.build === "string" && pkg.scripts.build.includes("kit build")) {
10054
+ hasKitBuildScript = true;
10055
+ }
10056
+ } catch {
10057
+ }
10058
+ const buildPipelinePassed = hasBuildScript || hasKitBuildScript;
10059
+ let buildDetails = "No build pipeline configured";
10060
+ if (hasKitBuildScript && hasBuildScript) {
10061
+ buildDetails = "Kit CLI build pipeline with bun-build.ts present";
10062
+ } else if (hasKitBuildScript) {
10063
+ buildDetails = "Kit CLI native build pipeline configured";
10064
+ } else if (hasBuildScript) {
10065
+ buildDetails = "Legacy bun-build.ts present";
10066
+ }
9788
10067
  results.push({
9789
10068
  category: "Project",
9790
- title: "Build Pipeline (bun-build.ts)",
9791
- passed: hasBuildScript,
9792
- details: hasBuildScript ? "Build script present" : "bun-build.ts not found"
10069
+ title: "Build Pipeline",
10070
+ passed: buildPipelinePassed,
10071
+ details: buildDetails
9793
10072
  });
9794
10073
  } else {
9795
10074
  results.push({
@@ -9801,7 +10080,7 @@ async function processDoctor(pOptions = {}) {
9801
10080
  }
9802
10081
  let allPassed = true;
9803
10082
  for (const res of results) {
9804
- const icon = res.passed ? source_default.green("") : source_default.red("");
10083
+ const icon = res.passed ? source_default.green("\u2713") : source_default.red("\u2717");
9805
10084
  const titleText = res.passed ? source_default.white(res.title) : source_default.red.bold(res.title);
9806
10085
  console.log(` ${icon} [${source_default.cyan(res.category)}] ${titleText}`);
9807
10086
  if (res.details && (!res.passed || isVerbose2)) {
@@ -9824,8 +10103,8 @@ async function processDoctor(pOptions = {}) {
9824
10103
  }
9825
10104
 
9826
10105
  // src/cli/create.ts
9827
- import { promises as fs3 } from "node:fs";
9828
- import { join as join3 } from "node:path";
10106
+ import { promises as fs4 } from "fs";
10107
+ import { join as join4 } from "path";
9829
10108
  function toPascalCase(pName) {
9830
10109
  return pName.replace(/[-_](\w)/g, (_2, c) => c.toUpperCase()).replace(/^\w/, (c) => c.toUpperCase());
9831
10110
  }
@@ -9868,20 +10147,20 @@ Error: Unknown create type '${type}'. Supported types: 'plugin'`));
9868
10147
  }
9869
10148
  const className = toPascalCase(name);
9870
10149
  const fileName = `${toKebabCase(name)}.ts`;
9871
- const pluginsDir = join3(process.cwd(), "src", "plugins");
9872
- const targetPath = join3(pluginsDir, fileName);
10150
+ const pluginsDir = join4(process.cwd(), "src", "plugins");
10151
+ const targetPath = join4(pluginsDir, fileName);
9873
10152
  try {
9874
- await fs3.mkdir(pluginsDir, { recursive: true });
9875
- const fileExists = await fs3.stat(targetPath).then(() => true).catch(() => false);
10153
+ await fs4.mkdir(pluginsDir, { recursive: true });
10154
+ const fileExists = await fs4.stat(targetPath).then(() => true).catch(() => false);
9876
10155
  if (fileExists) {
9877
10156
  console.error(source_default.red(`
9878
10157
  Error: File already exists at ${targetPath}`));
9879
10158
  process.exit(1);
9880
10159
  }
9881
10160
  const sourceCode = generatePluginSource(className, className);
9882
- await fs3.writeFile(targetPath, sourceCode, "utf8");
10161
+ await fs4.writeFile(targetPath, sourceCode, "utf8");
9883
10162
  console.log(`
9884
- ${source_default.green("")} Created plugin ${source_default.cyan(className)} at ${source_default.dim(targetPath)}
10163
+ ${source_default.green("\u2713")} Created plugin ${source_default.cyan(className)} at ${source_default.dim(targetPath)}
9885
10164
  `);
9886
10165
  if (verbose) {
9887
10166
  console.log(source_default.dim(sourceCode));
@@ -9894,6 +10173,132 @@ Error creating plugin: ${message}`));
9894
10173
  }
9895
10174
  }
9896
10175
 
10176
+ // src/cli/host.ts
10177
+ import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
10178
+ import { join as join5, resolve as resolve2 } from "path";
10179
+ import { networkInterfaces } from "os";
10180
+ function getNetworkAddress() {
10181
+ const interfaces = networkInterfaces();
10182
+ for (const name of Object.keys(interfaces)) {
10183
+ const netList = interfaces[name];
10184
+ if (!netList)
10185
+ continue;
10186
+ for (const net of netList) {
10187
+ if (net.family === "IPv4" && !net.internal) {
10188
+ return net.address;
10189
+ }
10190
+ }
10191
+ }
10192
+ return "localhost";
10193
+ }
10194
+ async function processHost(pOptions = {}) {
10195
+ const cwd = process.cwd();
10196
+ const defaultPort = 8090;
10197
+ const port = pOptions.port || defaultPort;
10198
+ const distDir = pOptions.directory ? resolve2(pOptions.directory) : join5(cwd, "dist");
10199
+ const architecture = detectArchitecture(join5(cwd, "src"));
10200
+ if (!existsSync4(distDir)) {
10201
+ const message = `Target directory "${distDir}" does not exist. Run "kit build" or use "kit host -b" first.`;
10202
+ console.error(source_default.red(`
10203
+ [Kit Host] ${message}
10204
+ `));
10205
+ return { success: false, message };
10206
+ }
10207
+ if (architecture === "multi") {
10208
+ const serverJsPath = join5(distDir, "server.js");
10209
+ if (!existsSync4(serverJsPath)) {
10210
+ const message = `Cannot host multiplayer project: "${serverJsPath}" was not found. Please compile the server first using "kit build".`;
10211
+ console.error(source_default.red(`
10212
+ [Kit Host] ${message}
10213
+ `));
10214
+ return { success: false, message };
10215
+ }
10216
+ console.log(source_default.cyan(`
10217
+ \uD83C\uDFAE Starting Multiplayer Server from ${source_default.bold(distDir)}...
10218
+ `));
10219
+ let serverSettingsPort = port;
10220
+ const settingsPath = join5(distDir, "settings.json");
10221
+ if (existsSync4(settingsPath)) {
10222
+ try {
10223
+ const settings = JSON.parse(readFileSync2(settingsPath, "utf8"));
10224
+ if (settings.port)
10225
+ serverSettingsPort = Number(settings.port);
10226
+ } catch {
10227
+ }
10228
+ }
10229
+ const proc = Bun.spawn(["node", "server.js"], {
10230
+ cwd: distDir,
10231
+ stdout: "inherit",
10232
+ stderr: "inherit",
10233
+ stdin: "inherit"
10234
+ });
10235
+ console.log(source_default.green(`\u2713 Multiplayer server process spawned (configured port: ${serverSettingsPort})`));
10236
+ process.on("SIGINT", () => {
10237
+ proc.kill();
10238
+ process.exit(0);
10239
+ });
10240
+ process.on("SIGTERM", () => {
10241
+ proc.kill();
10242
+ process.exit(0);
10243
+ });
10244
+ await proc.exited;
10245
+ return { success: true, message: "Multiplayer server finished running." };
10246
+ }
10247
+ const indexPath = join5(distDir, "index.html");
10248
+ if (!existsSync4(indexPath)) {
10249
+ const message = `Missing entrypoint: "${indexPath}" was not found in dist. Run "kit build" or use "kit host -b" to compile.`;
10250
+ console.error(source_default.red(`
10251
+ [Kit Host] ${message}
10252
+ `));
10253
+ return { success: false, message };
10254
+ }
10255
+ const lanIp = getNetworkAddress();
10256
+ const server = Bun.serve({
10257
+ port,
10258
+ async fetch(pReq) {
10259
+ const path2 = new URL(pReq.url).pathname;
10260
+ const target = path2 === "/" ? "/index.html" : decodeURIComponent(path2);
10261
+ const file = Bun.file(join5(distDir, target));
10262
+ if (!await file.exists()) {
10263
+ if (pOptions.verbose) {
10264
+ console.warn(source_default.yellow(`[Kit Host] 404 Not Found: ${target}`));
10265
+ }
10266
+ return new Response("Not Found", { status: 404 });
10267
+ }
10268
+ return new Response(file);
10269
+ }
10270
+ });
10271
+ console.log(source_default.cyan(`
10272
+ \uD83C\uDFAE Kit Game Host Server
10273
+ `));
10274
+ console.log(` ${source_default.bold("Local:")} ${source_default.green(`http://localhost:${server.port}`)}`);
10275
+ if (lanIp !== "localhost") {
10276
+ console.log(` ${source_default.bold("Network:")} ${source_default.green(`http://${lanIp}:${server.port}`)}`);
10277
+ }
10278
+ console.log(source_default.dim(`
10279
+ Serving files from: ${distDir}`));
10280
+ console.log(source_default.dim(`Press Ctrl+C to stop the server
10281
+ `));
10282
+ process.on("SIGINT", () => {
10283
+ console.log(source_default.yellow(`
10284
+ Shutting down host server...`));
10285
+ server.stop();
10286
+ process.exit(0);
10287
+ });
10288
+ process.on("SIGTERM", () => {
10289
+ server.stop();
10290
+ process.exit(0);
10291
+ });
10292
+ return {
10293
+ success: true,
10294
+ message: `Serving files on port ${server.port}`,
10295
+ server: {
10296
+ stop: () => server.stop(),
10297
+ port: server.port
10298
+ }
10299
+ };
10300
+ }
10301
+
9897
10302
  // src/cli/main.ts
9898
10303
  class KitCLI {
9899
10304
  static async processResources(pProcessOptions) {
@@ -9908,6 +10313,9 @@ class KitCLI {
9908
10313
  static async create(pCreateOptions) {
9909
10314
  await processCreate(pCreateOptions);
9910
10315
  }
10316
+ static async host(pHostOptions = {}) {
10317
+ return await processHost(pHostOptions);
10318
+ }
9911
10319
  }
9912
10320
 
9913
10321
  // bin/cli.ts
@@ -9991,6 +10399,12 @@ var formatHelp = (pCmd) => {
9991
10399
  }
9992
10400
  if (isRoot || name === "doctor") {
9993
10401
  output += ` ${source_default.cyan("kit doctor")}
10402
+ `;
10403
+ }
10404
+ if (isRoot || name === "host") {
10405
+ output += ` ${source_default.cyan("kit host")}
10406
+ `;
10407
+ output += ` ${source_default.cyan("kit host -p 8080 -b")}
9994
10408
  `;
9995
10409
  }
9996
10410
  if (isRoot) {
@@ -10023,24 +10437,23 @@ var initCommand = program2.command("init [name]").description("Initialize a new
10023
10437
  });
10024
10438
  });
10025
10439
  initCommand.helpInformation = () => formatHelp(initCommand);
10026
- var buildCommand = program2.command("build [help]").description("Build resources from the specified directory").option("-i, --in <path>", "Input directory (required)").option("-o, --out <path>", "Output directory (required)").option("-m, --manifest <path>", "Custom manifest file path").option("-w, --watch", "Watch input directory for changes and rebuild", false).option("-is, --ignore-sound", "Ignore sound files", false).action((pHelp, pCmdOptions) => {
10440
+ var buildCommand = program2.command("build [help]").description("Build resources and bundle game application").option("-i, --in <path>", "Input directory (defaults to ./src/resources)").option("-o, --out <path>", "Output directory (defaults to ./dist)").option("-m, --manifest <path>", "Custom manifest file path").option("-w, --watch", "Watch files for changes and rebuild automatically", false).option("-is, --ignore-sound", "Ignore sound files", false).option("--app", "Bundle game code and static assets (enabled by default when project entrypoints exist)").option("--no-app", "Disable game code bundling and only process resources").option("-p, --prod", "Production build: enables minification, identifier obfuscation, and strips sourcemaps", false).option("--minify", "Minify bundled JavaScript syntax, whitespace, and identifiers", false).option("--obfuscate", "Mangle and obfuscate variable and property identifiers", false).option("--sourcemap <mode>", "Sourcemap generation mode (none, linked, inline, external)").action((pHelp, pCmdOptions) => {
10027
10441
  if (pHelp === "help") {
10028
10442
  console.log(buildCommand.helpInformation());
10029
10443
  return;
10030
10444
  }
10031
10445
  const verbose = program2.opts().verbose;
10032
- if (!pCmdOptions.in || !pCmdOptions.out) {
10033
- console.error(source_default.red(`
10034
- Error: --in and --out are required flags for the build command.`));
10035
- console.log(buildCommand.helpInformation());
10036
- process.exit(1);
10037
- }
10038
10446
  const processOptions = {
10039
10447
  inDirectory: pCmdOptions.in,
10040
10448
  outDirectory: pCmdOptions.out,
10041
10449
  manifestPath: pCmdOptions.manifest,
10042
10450
  watch: pCmdOptions.watch || false,
10043
10451
  ignoreSound: pCmdOptions.ignoreSound || false,
10452
+ app: pCmdOptions.app,
10453
+ minify: pCmdOptions.minify || false,
10454
+ obfuscate: pCmdOptions.obfuscate || false,
10455
+ sourcemap: pCmdOptions.sourcemap,
10456
+ prod: pCmdOptions.prod || false,
10044
10457
  verbose: verbose || false
10045
10458
  };
10046
10459
  KitCLI.processResources(processOptions);
@@ -10067,6 +10480,21 @@ var doctorCommand = program2.command("doctor").description("Inspect environment
10067
10480
  }
10068
10481
  });
10069
10482
  doctorCommand.helpInformation = () => formatHelp(doctorCommand);
10483
+ var hostCommand = program2.command("host").description("Host the game application locally").option("-p, --port <number>", "Port to run host server on (default: 8090)", "8090").option("-d, --dir <path>", "Directory to serve (default: ./dist)").option("-b, --build", "Build the project before hosting", false).action(async (pCmdOptions) => {
10484
+ const verbose = program2.opts().verbose;
10485
+ if (pCmdOptions.build) {
10486
+ await KitCLI.processResources({ verbose });
10487
+ }
10488
+ const result = await KitCLI.host({
10489
+ port: Number(pCmdOptions.port) || 8090,
10490
+ directory: pCmdOptions.dir,
10491
+ verbose
10492
+ });
10493
+ if (!result.success) {
10494
+ process.exitCode = 1;
10495
+ }
10496
+ });
10497
+ hostCommand.helpInformation = () => formatHelp(hostCommand);
10070
10498
  program2.command("help [command]").description("Display help for a command").action((pCommandName) => {
10071
10499
  if (pCommandName) {
10072
10500
  const cmd = program2.commands.find((c) => c.name() === pCommandName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evitcastudio/kit",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "author": "doubleactii 56242467+doubleactii@users.noreply.github.com (https://evitcastudio.com)",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",