@evitcastudio/kit 3.1.1 → 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 (40) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +224 -123
  3. package/lib/bundle/cli/cli.js +905 -154
  4. package/lib/bundle/cli/kit-game-templates/multi/_gitignore +180 -180
  5. package/lib/bundle/cli/kit-game-templates/multi/package.json +3 -3
  6. package/lib/bundle/cli/kit-game-templates/multi/resource.json +1 -0
  7. package/lib/bundle/cli/kit-game-templates/multi/src/client/packets/s-packets.ts +4 -4
  8. package/lib/bundle/cli/kit-game-templates/multi/src/server/packets/c-packets.ts +4 -4
  9. package/lib/bundle/cli/kit-game-templates/single/_gitignore +180 -180
  10. package/lib/bundle/cli/kit-game-templates/single/package.json +3 -3
  11. package/lib/bundle/cli/kit-game-templates/single/resource.json +1 -0
  12. package/lib/cli/app-bundler.d.ts +29 -0
  13. package/lib/cli/app-bundler.d.ts.map +1 -0
  14. package/lib/cli/app-bundler.js +230 -0
  15. package/lib/cli/create.d.ts +11 -0
  16. package/lib/cli/create.d.ts.map +1 -0
  17. package/lib/cli/create.js +88 -0
  18. package/lib/cli/doctor.d.ts +9 -0
  19. package/lib/cli/doctor.d.ts.map +1 -0
  20. package/lib/cli/doctor.js +179 -0
  21. package/lib/cli/host.d.ts +49 -0
  22. package/lib/cli/host.d.ts.map +1 -0
  23. package/lib/cli/host.js +127 -0
  24. package/lib/cli/init.d.ts +2 -0
  25. package/lib/cli/init.d.ts.map +1 -1
  26. package/lib/cli/init.js +42 -15
  27. package/lib/cli/main.d.ts +22 -1
  28. package/lib/cli/main.d.ts.map +1 -1
  29. package/lib/cli/main.js +28 -1
  30. package/lib/cli/resource-builder.d.ts +1 -1
  31. package/lib/cli/resource-builder.d.ts.map +1 -1
  32. package/lib/cli/resource-builder.js +139 -19
  33. package/lib/cli/types.d.ts +12 -6
  34. package/lib/cli/types.d.ts.map +1 -1
  35. package/lib/types/vylo.d.ts +3848 -3848
  36. package/lib/vendor/vyi/index.js +26 -26
  37. package/package.json +73 -73
  38. package/lib/bundle/cli/kit-game-templates/multi/bun-build.ts +0 -109
  39. package/lib/bundle/cli/kit-game-templates/single/bun-build.ts +0 -33
  40. package/lib/bundle/cli/kit-game-templates/single/bun-serve.ts +0 -28
@@ -2,5 +2,5 @@ import type { ProcessOptions } from './types';
2
2
  /**
3
3
  * Main entry point for building resources.
4
4
  */
5
- export declare function processResources({ inDirectory, outDirectory, verbose, ignoreSound }: ProcessOptions): Promise<void>;
5
+ export declare function processResources({ inDirectory, outDirectory, manifestPath, watch, verbose, ignoreSound, app, minify, obfuscate, sourcemap, prod }: ProcessOptions): Promise<void>;
6
6
  //# sourceMappingURL=resource-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource-builder.d.ts","sourceRoot":"","sources":["../../src/cli/resource-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AA8X9C;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BzH"}
1
+ {"version":3,"file":"resource-builder.d.ts","sourceRoot":"","sources":["../../src/cli/resource-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAggB9C;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCvL"}
@@ -1,8 +1,9 @@
1
- import { promises as fs } from 'fs';
2
- import { join, extname, basename } from 'path';
1
+ import { promises as fs, watch as fsWatch, existsSync } from 'fs';
2
+ import { join, extname, basename, resolve } from 'path';
3
3
  import chalk from 'chalk';
4
4
  import { v4 as uuidv4 } from 'uuid';
5
5
  import { VYI } from '../vendor/vyi';
6
+ import { bundleApp } from './app-bundler';
6
7
  // Logging helpers
7
8
  const log = console.log;
8
9
  const info = chalk.hex('#ffa552');
@@ -17,7 +18,9 @@ let isVerbose = false;
17
18
  let ignoringSound = false;
18
19
  let resourceInDirectory = '';
19
20
  let resourceOutDirectory = '';
21
+ let customManifestPath = undefined;
20
22
  const resourcesToProcess = [];
23
+ const subdirectoriesToMirror = [];
21
24
  /**
22
25
  * Initializes the resource JSON structure.
23
26
  */
@@ -70,8 +73,16 @@ async function processDirectory(pDirectoryPath) {
70
73
  const contents = await fs.readdir(pDirectoryPath);
71
74
  for (const item of contents) {
72
75
  const itemPath = join(pDirectoryPath, item);
76
+ // Skip the output directory if it is inside the input directory
77
+ if (resolve(itemPath) === resourceOutDirectory) {
78
+ continue;
79
+ }
73
80
  const stats = await fs.stat(itemPath);
74
81
  if (stats.isDirectory()) {
82
+ // If top-level subdirectory inside inDirectory, track for mirroring
83
+ if (pDirectoryPath === resourceInDirectory) {
84
+ subdirectoriesToMirror.push(item);
85
+ }
75
86
  await processDirectory(itemPath);
76
87
  }
77
88
  else if (isValidExtension(extname(itemPath).slice(1))) {
@@ -89,6 +100,28 @@ async function processDirectory(pDirectoryPath) {
89
100
  function isValidExtension(pExtension) {
90
101
  return VALID_EXTENSIONS.includes(pExtension);
91
102
  }
103
+ /**
104
+ * Recursively copies a directory to destination, preserving subdirectories and files.
105
+ * Ignores engine resource files that are already handled by Vylocity obfuscation.
106
+ */
107
+ async function mirrorDirectory(pSourceDir, pDestDir) {
108
+ const entries = await fs.readdir(pSourceDir, { withFileTypes: true });
109
+ await fs.mkdir(pDestDir, { recursive: true });
110
+ for (const entry of entries) {
111
+ const srcPath = join(pSourceDir, entry.name);
112
+ const destPath = join(pDestDir, entry.name);
113
+ if (entry.isDirectory()) {
114
+ await mirrorDirectory(srcPath, destPath);
115
+ }
116
+ else {
117
+ const ext = extname(entry.name).slice(1);
118
+ // Only copy non-engine files (images, custom JSON, fonts, etc.)
119
+ if (!isValidExtension(ext)) {
120
+ await fs.copyFile(srcPath, destPath);
121
+ }
122
+ }
123
+ }
124
+ }
92
125
  /**
93
126
  * Executes all file copy operations in parallel after preparation.
94
127
  */
@@ -108,6 +141,12 @@ async function processAllFiles() {
108
141
  });
109
142
  // Execute all copy operations concurrently
110
143
  await Promise.all(copyOperations);
144
+ // Mirror subdirectories (images, fonts, emitters, particles, etc.)
145
+ for (const subDir of subdirectoriesToMirror) {
146
+ const srcPath = join(resourceInDirectory, subDir);
147
+ const destPath = join(resourceOutDirectory, 'resources', subDir);
148
+ await mirrorDirectory(srcPath, destPath);
149
+ }
111
150
  logVerbose(`[Kit CLI] All resources have been processed.`);
112
151
  await saveResourceJSON();
113
152
  const boundsData = await buildBoundsJSON();
@@ -332,33 +371,29 @@ async function copyFile(pSource, pDestinationDir, pNewName) {
332
371
  * Saves the resource JSON to a file.
333
372
  */
334
373
  async function saveResourceJSON() {
335
- const filePath = 'resource.json';
374
+ const jsonContent = JSON.stringify(resourceJSON, null, 4);
375
+ const targetPath = customManifestPath || 'resource.json';
336
376
  try {
337
- await fs.writeFile(filePath, JSON.stringify(resourceJSON, null, 4));
377
+ await fs.writeFile(targetPath, jsonContent, 'utf8');
338
378
  }
339
379
  catch (pError) {
340
- logError(`[Error] Saving resource JSON: ${pError}`);
380
+ logError(`[Error] Saving resource JSON to ${targetPath}: ${pError}`);
341
381
  }
342
382
  }
383
+ let shouldBundleApp = false;
384
+ let projectRootDirectory = '';
385
+ let appBundleOptions = {};
343
386
  /**
344
- * Main entry point for building resources.
387
+ * Executes a single build pass over the input assets.
345
388
  */
346
- export async function processResources({ inDirectory, outDirectory, verbose, ignoreSound }) {
347
- // Initialize state
348
- resourceInDirectory = inDirectory;
349
- resourceOutDirectory = outDirectory;
350
- isVerbose = verbose;
351
- ignoringSound = ignoreSound;
389
+ async function runBuild() {
352
390
  resourceJSON = initializeResourceJSON();
353
391
  resourcesToProcess.length = 0;
354
- // Validate inputs
355
- if (!resourceInDirectory || !resourceOutDirectory) {
356
- logError('[Error] Input and output directories must be specified');
357
- return;
392
+ subdirectoriesToMirror.length = 0;
393
+ if (existsSync(resourceInDirectory)) {
394
+ await processDirectory(resourceInDirectory);
358
395
  }
359
- // Process resources
360
- await processDirectory(resourceInDirectory);
361
- if (resourcesToProcess.length > 0) {
396
+ if (resourcesToProcess.length > 0 || subdirectoriesToMirror.length > 0) {
362
397
  await processAllFiles();
363
398
  }
364
399
  else {
@@ -368,6 +403,91 @@ export async function processResources({ inDirectory, outDirectory, verbose, ign
368
403
  await saveIconPointsJSON({});
369
404
  await saveSizesJSON({});
370
405
  }
406
+ // If app bundling is requested or enabled by default, compile app and copy web assets
407
+ if (shouldBundleApp && projectRootDirectory) {
408
+ await bundleApp(projectRootDirectory, resourceOutDirectory, {
409
+ ...appBundleOptions,
410
+ verbose: Boolean(isVerbose)
411
+ });
412
+ }
413
+ logVerbose(`[Kit CLI] Resources built successfully.`);
414
+ }
415
+ /**
416
+ * Watches the input directory for changes and triggers incremental rebuilds.
417
+ */
418
+ async function runWatch() {
419
+ await runBuild();
420
+ console.log(chalk.cyan(`\nšŸ‘€ Watching for changes in: ${chalk.bold(resourceInDirectory)}`));
421
+ let debounceTimer = null;
422
+ const triggerRebuild = (pFilename) => {
423
+ if (debounceTimer)
424
+ clearTimeout(debounceTimer);
425
+ debounceTimer = setTimeout(async () => {
426
+ console.log(chalk.dim(`\nFile changed: ${pFilename}, rebuilding...`));
427
+ await runBuild();
428
+ }, 150);
429
+ };
430
+ const watchers = [];
431
+ if (existsSync(resourceInDirectory)) {
432
+ const resWatcher = fsWatch(resourceInDirectory, { recursive: true }, (_eventType, pFilename) => {
433
+ if (pFilename)
434
+ triggerRebuild(pFilename);
435
+ });
436
+ watchers.push(resWatcher);
437
+ }
438
+ const srcDir = join(projectRootDirectory, 'src');
439
+ if (shouldBundleApp && existsSync(srcDir) && srcDir !== resourceInDirectory) {
440
+ const srcWatcher = fsWatch(srcDir, { recursive: true }, (_eventType, pFilename) => {
441
+ if (!pFilename)
442
+ return;
443
+ // Ignore resources folder if inside src to prevent double triggers
444
+ if (pFilename.startsWith('resources'))
445
+ return;
446
+ triggerRebuild(pFilename);
447
+ });
448
+ watchers.push(srcWatcher);
449
+ }
450
+ process.on('SIGINT', () => {
451
+ for (const w of watchers)
452
+ w.close();
453
+ process.exit(0);
454
+ });
455
+ }
456
+ /**
457
+ * Main entry point for building resources.
458
+ */
459
+ export async function processResources({ inDirectory, outDirectory, manifestPath, watch, verbose, ignoreSound, app, minify, obfuscate, sourcemap, prod }) {
460
+ projectRootDirectory = process.cwd();
461
+ // Smart defaults: ./src/resources -> ./dist
462
+ const resolvedIn = inDirectory || (existsSync(join(projectRootDirectory, 'src', 'resources')) ? 'src/resources' : '');
463
+ const resolvedOut = outDirectory || 'dist';
464
+ resourceInDirectory = resolvedIn ? resolve(resolvedIn) : '';
465
+ resourceOutDirectory = resolvedOut ? resolve(resolvedOut) : '';
466
+ customManifestPath = manifestPath;
467
+ isVerbose = verbose;
468
+ ignoringSound = ignoreSound;
469
+ appBundleOptions = {
470
+ minify,
471
+ obfuscate,
472
+ sourcemap,
473
+ prod
474
+ };
475
+ // Enable app bundling if explicitly requested or if app entrypoints exist and app is not false
476
+ const hasAppEntry = existsSync(join(projectRootDirectory, 'src', 'index.ts')) ||
477
+ existsSync(join(projectRootDirectory, 'src', 'client', 'index.ts')) ||
478
+ existsSync(join(projectRootDirectory, 'src', 'server', 'index.ts'));
479
+ shouldBundleApp = app !== undefined ? app : hasAppEntry;
480
+ // Validate inputs
481
+ if (!resourceInDirectory || !resourceOutDirectory) {
482
+ logError('[Error] Input and output directories must be specified');
483
+ return;
484
+ }
485
+ if (watch) {
486
+ await runWatch();
487
+ }
488
+ else {
489
+ await runBuild();
490
+ }
371
491
  }
372
492
  function logVerbose(pMessage) {
373
493
  if (isVerbose)
@@ -1,11 +1,17 @@
1
1
  /**
2
- * ProcessOptions for resource builder.
2
+ * Options for the resource builder and app build process.
3
3
  */
4
- type ProcessOptions = {
5
- inDirectory: string;
6
- outDirectory: string;
4
+ export interface ProcessOptions {
5
+ inDirectory?: string;
6
+ outDirectory?: string;
7
+ manifestPath?: string;
7
8
  verbose?: boolean;
8
9
  ignoreSound?: boolean;
9
- };
10
- export type { ProcessOptions };
10
+ watch?: boolean;
11
+ app?: boolean;
12
+ minify?: boolean;
13
+ obfuscate?: boolean;
14
+ sourcemap?: 'none' | 'linked' | 'inline' | 'external';
15
+ prod?: boolean;
16
+ }
11
17
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cli/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,KAAK,cAAc,GAAG;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,YAAY,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cli/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;IACtD,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB"}