@evitcastudio/kit 3.2.1 ā 3.3.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/LICENSE +21 -21
- package/README.md +224 -224
- package/lib/bundle/cli/cli.js +1877 -136
- package/lib/bundle/cli/kit-game-templates/multi/_gitignore +180 -180
- package/lib/bundle/cli/kit-game-templates/multi/package.json +7 -1
- package/lib/bundle/cli/kit-game-templates/multi/resource.json +7 -1
- package/lib/bundle/cli/kit-game-templates/multi/src/client/packets/s-packets.ts +4 -4
- package/lib/bundle/cli/kit-game-templates/multi/src/server/packets/c-packets.ts +4 -4
- package/lib/bundle/cli/kit-game-templates/single/_gitignore +180 -180
- package/lib/bundle/cli/kit-game-templates/single/package.json +7 -1
- package/lib/bundle/cli/kit-game-templates/single/resource.json +7 -1
- package/lib/cli/app-bundler.d.ts.map +1 -1
- package/lib/cli/app-bundler.js +23 -3
- package/lib/cli/host.js +2 -2
- package/lib/cli/resource-builder.d.ts.map +1 -1
- package/lib/cli/resource-builder.js +151 -32
- package/lib/cli-runner.cjs +18 -0
- package/lib/types/vylo.d.ts +3848 -3848
- package/lib/vendor/vyi/index.js +26 -26
- package/package.json +74 -73
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { promises as fs,
|
|
2
|
-
import { join, extname, basename, resolve } from 'path';
|
|
1
|
+
import { promises as fs, existsSync } from 'fs';
|
|
2
|
+
import { join, extname, basename, resolve, relative } from 'path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
+
import { watch as chokidarWatch } from 'chokidar';
|
|
4
5
|
import { v4 as uuidv4 } from 'uuid';
|
|
5
6
|
import { VYI } from '../vendor/vyi';
|
|
6
7
|
import { bundleApp } from './app-bundler';
|
|
@@ -100,9 +101,22 @@ async function processDirectory(pDirectoryPath) {
|
|
|
100
101
|
function isValidExtension(pExtension) {
|
|
101
102
|
return VALID_EXTENSIONS.includes(pExtension);
|
|
102
103
|
}
|
|
104
|
+
// Engine resource formats handled by Vylocity obfuscation
|
|
105
|
+
const ENGINE_EXTENSIONS = ['vyint', 'vyi', 'vym', 'vymac'];
|
|
106
|
+
/**
|
|
107
|
+
* Checks if a file extension is a Vylocity engine binary/definition format.
|
|
108
|
+
* @param pExtension - The file extension to test.
|
|
109
|
+
* @returns True if the extension is an engine format.
|
|
110
|
+
*/
|
|
111
|
+
function isEngineExtension(pExtension) {
|
|
112
|
+
return ENGINE_EXTENSIONS.includes(pExtension);
|
|
113
|
+
}
|
|
103
114
|
/**
|
|
104
115
|
* Recursively copies a directory to destination, preserving subdirectories and files.
|
|
105
|
-
* Ignores engine resource files that are
|
|
116
|
+
* Ignores engine resource files (vyint, vyi, vym, vymac) that are handled by Vylocity obfuscation,
|
|
117
|
+
* while ensuring media and custom assets (sounds, images, fonts, json) retain their structure.
|
|
118
|
+
* @param pSourceDir - The source directory to copy.
|
|
119
|
+
* @param pDestDir - The target destination directory.
|
|
106
120
|
*/
|
|
107
121
|
async function mirrorDirectory(pSourceDir, pDestDir) {
|
|
108
122
|
const entries = await fs.readdir(pSourceDir, { withFileTypes: true });
|
|
@@ -115,9 +129,10 @@ async function mirrorDirectory(pSourceDir, pDestDir) {
|
|
|
115
129
|
}
|
|
116
130
|
else {
|
|
117
131
|
const ext = extname(entry.name).slice(1);
|
|
118
|
-
//
|
|
119
|
-
if (!
|
|
120
|
-
await fs.
|
|
132
|
+
// Copy all assets except internal Vylocity engine binary formats
|
|
133
|
+
if (!isEngineExtension(ext)) {
|
|
134
|
+
const data = await fs.readFile(srcPath);
|
|
135
|
+
await fs.writeFile(destPath, data);
|
|
121
136
|
}
|
|
122
137
|
}
|
|
123
138
|
}
|
|
@@ -361,7 +376,11 @@ async function clearResourceTypeDirectories(pBaseDirectory) {
|
|
|
361
376
|
async function copyFile(pSource, pDestinationDir, pNewName) {
|
|
362
377
|
try {
|
|
363
378
|
await fs.mkdir(pDestinationDir, { recursive: true });
|
|
364
|
-
|
|
379
|
+
// Use readFile + writeFile instead of fs.copyFile to avoid macOS APFS
|
|
380
|
+
// clone operations, which emit phantom FSEvents on the source file and
|
|
381
|
+
// cause external editors (e.g. Viewer) to falsely detect modifications.
|
|
382
|
+
const data = await fs.readFile(pSource);
|
|
383
|
+
await fs.writeFile(join(pDestinationDir, pNewName), data);
|
|
365
384
|
}
|
|
366
385
|
catch (pError) {
|
|
367
386
|
logError(`[Error] Copying file ${pSource}: ${pError}`);
|
|
@@ -417,39 +436,139 @@ async function runBuild() {
|
|
|
417
436
|
*/
|
|
418
437
|
async function runWatch() {
|
|
419
438
|
await runBuild();
|
|
420
|
-
|
|
439
|
+
const pathsToWatch = [];
|
|
440
|
+
if (existsSync(resourceInDirectory)) {
|
|
441
|
+
pathsToWatch.push(resourceInDirectory);
|
|
442
|
+
}
|
|
443
|
+
const srcDir = join(projectRootDirectory, 'src');
|
|
444
|
+
if (shouldBundleApp && existsSync(srcDir) && srcDir !== resourceInDirectory) {
|
|
445
|
+
pathsToWatch.push(srcDir);
|
|
446
|
+
}
|
|
447
|
+
const displayPaths = pathsToWatch
|
|
448
|
+
.map(p => chalk.bold(relative(projectRootDirectory, p) || p))
|
|
449
|
+
.join(', ');
|
|
450
|
+
console.log(chalk.cyan(`\nWatching for changes in: ${displayPaths}`));
|
|
421
451
|
let debounceTimer = null;
|
|
452
|
+
let isRebuilding = false;
|
|
453
|
+
let queuedChange = null;
|
|
422
454
|
const triggerRebuild = (pFilename) => {
|
|
423
455
|
if (debounceTimer)
|
|
424
456
|
clearTimeout(debounceTimer);
|
|
425
457
|
debounceTimer = setTimeout(async () => {
|
|
426
|
-
|
|
427
|
-
|
|
458
|
+
if (isRebuilding) {
|
|
459
|
+
queuedChange = pFilename;
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
isRebuilding = true;
|
|
463
|
+
try {
|
|
464
|
+
console.log(chalk.dim(`\nFile changed: ${pFilename}, rebuilding...`));
|
|
465
|
+
await runBuild();
|
|
466
|
+
}
|
|
467
|
+
finally {
|
|
468
|
+
isRebuilding = false;
|
|
469
|
+
if (queuedChange) {
|
|
470
|
+
const next = queuedChange;
|
|
471
|
+
queuedChange = null;
|
|
472
|
+
triggerRebuild(next);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
428
475
|
}, 150);
|
|
429
476
|
};
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
477
|
+
const normalizedOutDir = resourceOutDirectory ? resourceOutDirectory.replace(/\\/g, '/') : '';
|
|
478
|
+
const isIgnored = (pPath) => {
|
|
479
|
+
const normalized = pPath.replace(/\\/g, '/');
|
|
480
|
+
// Ignore output directory
|
|
481
|
+
if (normalizedOutDir && (normalized === normalizedOutDir || normalized.startsWith(`${normalizedOutDir}/`))) {
|
|
482
|
+
return true;
|
|
483
|
+
}
|
|
484
|
+
// Ignore VCS and dependency folders
|
|
485
|
+
if (/(^|[/\\])(\.git|node_modules|\.DS_Store|Thumbs\.db)($|[/\\])/.test(normalized)) {
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
// Ignore vendor directory (precompiled/static vendor assets)
|
|
489
|
+
if (/(^|[/\\])vendor([/\\]|$)/.test(normalized)) {
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
// Ignore sourcemaps
|
|
493
|
+
if (normalized.endsWith('.map')) {
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
// Ignore generated manifest metadata files in project root
|
|
497
|
+
if (normalized.endsWith('resource.json') ||
|
|
498
|
+
normalized.endsWith('bounds.json') ||
|
|
499
|
+
normalized.endsWith('icon-points.json') ||
|
|
500
|
+
normalized.endsWith('sizes.json')) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
return false;
|
|
504
|
+
};
|
|
505
|
+
// Track known file modification times and sizes to eliminate phantom change events from copy/read operations
|
|
506
|
+
const fileStats = new Map();
|
|
507
|
+
const recordFileStat = async (pFilePath) => {
|
|
508
|
+
try {
|
|
509
|
+
const stats = await fs.stat(pFilePath);
|
|
510
|
+
if (stats.isFile()) {
|
|
511
|
+
fileStats.set(resolve(pFilePath), { mtime: stats.mtimeMs, size: stats.size });
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
catch {
|
|
515
|
+
// Ignored
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
const primeDirectoryStats = async (pDirPath) => {
|
|
519
|
+
try {
|
|
520
|
+
const entries = await fs.readdir(pDirPath, { withFileTypes: true });
|
|
521
|
+
for (const entry of entries) {
|
|
522
|
+
const fullPath = join(pDirPath, entry.name);
|
|
523
|
+
if (isIgnored(fullPath))
|
|
524
|
+
continue;
|
|
525
|
+
if (entry.isDirectory()) {
|
|
526
|
+
await primeDirectoryStats(fullPath);
|
|
527
|
+
}
|
|
528
|
+
else if (entry.isFile()) {
|
|
529
|
+
await recordFileStat(fullPath);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
catch {
|
|
534
|
+
// Ignored
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
for (const p of pathsToWatch) {
|
|
538
|
+
await primeDirectoryStats(p);
|
|
449
539
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
540
|
+
const watcher = chokidarWatch(pathsToWatch, {
|
|
541
|
+
ignored: isIgnored,
|
|
542
|
+
ignoreInitial: true,
|
|
543
|
+
awaitWriteFinish: {
|
|
544
|
+
stabilityThreshold: 100,
|
|
545
|
+
pollInterval: 50
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
watcher.on('all', async (event, filePath) => {
|
|
549
|
+
if (event === 'addDir' || event === 'unlinkDir')
|
|
550
|
+
return;
|
|
551
|
+
const absPath = resolve(filePath);
|
|
552
|
+
if (event === 'unlink') {
|
|
553
|
+
fileStats.delete(absPath);
|
|
554
|
+
const relativePath = relative(projectRootDirectory, filePath).replace(/\\/g, '/');
|
|
555
|
+
triggerRebuild(relativePath);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
// Verify mtime or size actually changed to eliminate phantom OS events
|
|
559
|
+
const stats = await fs.stat(absPath).catch(() => null);
|
|
560
|
+
if (!stats)
|
|
561
|
+
return;
|
|
562
|
+
const prev = fileStats.get(absPath);
|
|
563
|
+
if (prev && prev.mtime === stats.mtimeMs && prev.size === stats.size) {
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
fileStats.set(absPath, { mtime: stats.mtimeMs, size: stats.size });
|
|
567
|
+
const relativePath = relative(projectRootDirectory, filePath).replace(/\\/g, '/');
|
|
568
|
+
triggerRebuild(relativePath);
|
|
569
|
+
});
|
|
570
|
+
process.on('SIGINT', async () => {
|
|
571
|
+
await watcher.close();
|
|
453
572
|
process.exit(0);
|
|
454
573
|
});
|
|
455
574
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync } = require('child_process');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
// Target the compiled Bun bundle
|
|
6
|
+
const cliScript = path.join(__dirname, 'bundle', 'cli', 'cli.js');
|
|
7
|
+
|
|
8
|
+
// If currently running under Bun, import and execute directly
|
|
9
|
+
if (typeof Bun !== 'undefined') {
|
|
10
|
+
import(cliScript);
|
|
11
|
+
} else {
|
|
12
|
+
// Running under Node.js -> delegate to Bun runtime
|
|
13
|
+
const result = spawnSync('bun', [cliScript, ...process.argv.slice(2)], {
|
|
14
|
+
stdio: 'inherit',
|
|
15
|
+
shell: process.platform === 'win32'
|
|
16
|
+
});
|
|
17
|
+
process.exit(result.status ?? 0);
|
|
18
|
+
}
|