@evitcastudio/kit 3.1.1 → 3.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/LICENSE +21 -21
- package/README.md +224 -123
- package/lib/bundle/cli/cli.js +379 -56
- package/lib/bundle/cli/kit-game-templates/multi/_gitignore +180 -180
- package/lib/bundle/cli/kit-game-templates/multi/package.json +3 -3
- package/lib/bundle/cli/kit-game-templates/multi/resource.json +1 -0
- 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 +3 -3
- package/lib/bundle/cli/kit-game-templates/single/resource.json +1 -0
- package/lib/cli/app-bundler.d.ts +29 -0
- package/lib/cli/app-bundler.d.ts.map +1 -0
- package/lib/cli/app-bundler.js +230 -0
- package/lib/cli/create.d.ts +11 -0
- package/lib/cli/create.d.ts.map +1 -0
- package/lib/cli/create.js +88 -0
- package/lib/cli/doctor.d.ts +9 -0
- package/lib/cli/doctor.d.ts.map +1 -0
- package/lib/cli/doctor.js +179 -0
- package/lib/cli/host.d.ts +49 -0
- package/lib/cli/host.d.ts.map +1 -0
- package/lib/cli/host.js +127 -0
- package/lib/cli/init.d.ts +2 -0
- package/lib/cli/init.d.ts.map +1 -1
- package/lib/cli/init.js +42 -15
- package/lib/cli/main.d.ts +22 -1
- package/lib/cli/main.d.ts.map +1 -1
- package/lib/cli/main.js +28 -1
- package/lib/cli/resource-builder.d.ts +1 -1
- package/lib/cli/resource-builder.d.ts.map +1 -1
- package/lib/cli/resource-builder.js +139 -19
- package/lib/cli/types.d.ts +12 -6
- package/lib/cli/types.d.ts.map +1 -1
- package/lib/types/vylo.d.ts +3848 -3848
- package/lib/vendor/vyi/index.js +26 -26
- package/package.json +73 -73
- package/lib/bundle/cli/kit-game-templates/multi/bun-build.ts +0 -109
- package/lib/bundle/cli/kit-game-templates/single/bun-build.ts +0 -33
- package/lib/bundle/cli/kit-game-templates/single/bun-serve.ts +0 -28
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
/**
|
|
5
|
+
* Formats a given name into a PascalCase class name.
|
|
6
|
+
* @param pName - Raw identifier input.
|
|
7
|
+
*/
|
|
8
|
+
function toPascalCase(pName) {
|
|
9
|
+
return pName
|
|
10
|
+
.replace(/[-_](\w)/g, (_, c) => c.toUpperCase())
|
|
11
|
+
.replace(/^\w/, c => c.toUpperCase());
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Formats a given name into a kebab-case file name.
|
|
15
|
+
* @param pName - Raw identifier input.
|
|
16
|
+
*/
|
|
17
|
+
function toKebabCase(pName) {
|
|
18
|
+
return pName
|
|
19
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
20
|
+
.replace(/[\s_]+/g, '-')
|
|
21
|
+
.toLowerCase();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generates the source code for a custom KitPlugin.
|
|
25
|
+
* @param pClassName - PascalCase class name.
|
|
26
|
+
* @param pPluginName - Plugin string identifier.
|
|
27
|
+
*/
|
|
28
|
+
function generatePluginSource(pClassName, pPluginName) {
|
|
29
|
+
return `import { KitPlugin } from '@evitcastudio/kit';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* ${pClassName} plugin for the Kit framework.
|
|
33
|
+
*/
|
|
34
|
+
export class ${pClassName} extends KitPlugin {
|
|
35
|
+
/**
|
|
36
|
+
* Unique name identifier of the plugin.
|
|
37
|
+
*/
|
|
38
|
+
readonly name = '${pPluginName}';
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Entry point for custom initialization when the plugin is registered with Kit.
|
|
42
|
+
*/
|
|
43
|
+
onRegistered(): void {
|
|
44
|
+
// Initialize plugin logic
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Handles creation of new boilerplate elements in a Kit project.
|
|
51
|
+
* @param pOptions - Creation configuration options.
|
|
52
|
+
*/
|
|
53
|
+
export async function processCreate(pOptions) {
|
|
54
|
+
const { type, name, verbose } = pOptions;
|
|
55
|
+
if (!type || !name) {
|
|
56
|
+
console.error(chalk.red('\nError: Both type and name are required. Usage: kit create <type> <name>'));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const normalizedType = type.toLowerCase();
|
|
60
|
+
if (normalizedType !== 'plugin') {
|
|
61
|
+
console.error(chalk.red(`\nError: Unknown create type '${type}'. Supported types: 'plugin'`));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const className = toPascalCase(name);
|
|
65
|
+
const fileName = `${toKebabCase(name)}.ts`;
|
|
66
|
+
// Standard location for plugins in a game or library project
|
|
67
|
+
const pluginsDir = join(process.cwd(), 'src', 'plugins');
|
|
68
|
+
const targetPath = join(pluginsDir, fileName);
|
|
69
|
+
try {
|
|
70
|
+
await fs.mkdir(pluginsDir, { recursive: true });
|
|
71
|
+
const fileExists = await fs.stat(targetPath).then(() => true).catch(() => false);
|
|
72
|
+
if (fileExists) {
|
|
73
|
+
console.error(chalk.red(`\nError: File already exists at ${targetPath}`));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const sourceCode = generatePluginSource(className, className);
|
|
77
|
+
await fs.writeFile(targetPath, sourceCode, 'utf8');
|
|
78
|
+
console.log(`\n ${chalk.green('✓')} Created plugin ${chalk.cyan(className)} at ${chalk.dim(targetPath)}\n`);
|
|
79
|
+
if (verbose) {
|
|
80
|
+
console.log(chalk.dim(sourceCode));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (pError) {
|
|
84
|
+
const message = pError instanceof Error ? pError.message : String(pError);
|
|
85
|
+
console.error(chalk.red(`\nError creating plugin: ${message}`));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface DoctorOptions {
|
|
2
|
+
verbose?: boolean;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Runs diagnostics on the local development environment and project health.
|
|
6
|
+
* @param pOptions - Options passed from the CLI.
|
|
7
|
+
*/
|
|
8
|
+
export declare function processDoctor(pOptions?: DoctorOptions): Promise<boolean>;
|
|
9
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AASD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,QAAQ,GAAE,aAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAiLlF"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
/**
|
|
6
|
+
* Runs diagnostics on the local development environment and project health.
|
|
7
|
+
* @param pOptions - Options passed from the CLI.
|
|
8
|
+
*/
|
|
9
|
+
export async function processDoctor(pOptions = {}) {
|
|
10
|
+
const isVerbose = Boolean(pOptions.verbose);
|
|
11
|
+
const results = [];
|
|
12
|
+
console.log(`\n ${chalk.cyan.bold('Kit Doctor')} ${chalk.dim('-')} Environment & Project Health Check\n`);
|
|
13
|
+
// 1. Runtime Check: Bun
|
|
14
|
+
const bunCheck = spawnSync('bun', ['--version'], { encoding: 'utf8', shell: true });
|
|
15
|
+
if (!bunCheck.error && bunCheck.status === 0) {
|
|
16
|
+
results.push({
|
|
17
|
+
category: 'Runtime',
|
|
18
|
+
title: 'Bun Runtime',
|
|
19
|
+
passed: true,
|
|
20
|
+
details: `Installed version v${bunCheck.stdout.trim()}`
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
results.push({
|
|
25
|
+
category: 'Runtime',
|
|
26
|
+
title: 'Bun Runtime',
|
|
27
|
+
passed: false,
|
|
28
|
+
details: 'Bun is not installed or not in PATH. Download at https://bun.sh/'
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// 2. Version Control Check: Git
|
|
32
|
+
const gitCheck = spawnSync('git', ['--version'], { encoding: 'utf8', shell: true });
|
|
33
|
+
if (!gitCheck.error && gitCheck.status === 0) {
|
|
34
|
+
results.push({
|
|
35
|
+
category: 'VCS',
|
|
36
|
+
title: 'Git Installation',
|
|
37
|
+
passed: true,
|
|
38
|
+
details: gitCheck.stdout.trim()
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
results.push({
|
|
43
|
+
category: 'VCS',
|
|
44
|
+
title: 'Git Installation',
|
|
45
|
+
passed: false,
|
|
46
|
+
details: 'Git was not found in your PATH.'
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// 3. Project Environment Check
|
|
50
|
+
const cwd = process.cwd();
|
|
51
|
+
const pkgPath = join(cwd, 'package.json');
|
|
52
|
+
const hasPackageJson = existsSync(pkgPath);
|
|
53
|
+
if (hasPackageJson) {
|
|
54
|
+
try {
|
|
55
|
+
const rawPkg = readFileSync(pkgPath, 'utf8');
|
|
56
|
+
const pkg = JSON.parse(rawPkg);
|
|
57
|
+
const hasKitDependency = Boolean((pkg.dependencies && pkg.dependencies['@evitcastudio/kit']) ||
|
|
58
|
+
(pkg.devDependencies && pkg.devDependencies['@evitcastudio/kit']) ||
|
|
59
|
+
pkg.name === '@evitcastudio/kit');
|
|
60
|
+
results.push({
|
|
61
|
+
category: 'Project',
|
|
62
|
+
title: 'package.json configuration',
|
|
63
|
+
passed: true,
|
|
64
|
+
details: `Project name: ${pkg.name || 'unnamed'}`
|
|
65
|
+
});
|
|
66
|
+
results.push({
|
|
67
|
+
category: 'Project',
|
|
68
|
+
title: 'Kit framework dependency',
|
|
69
|
+
passed: hasKitDependency,
|
|
70
|
+
details: hasKitDependency
|
|
71
|
+
? 'Found @evitcastudio/kit in project configuration'
|
|
72
|
+
: 'Missing @evitcastudio/kit dependency in package.json'
|
|
73
|
+
});
|
|
74
|
+
// If running inside a consumer game project, check game asset directory structure
|
|
75
|
+
const isKitCoreRepo = pkg.name === '@evitcastudio/kit';
|
|
76
|
+
if (!isKitCoreRepo) {
|
|
77
|
+
const resourcesDir = join(cwd, 'src', 'resources');
|
|
78
|
+
const hasResources = existsSync(resourcesDir);
|
|
79
|
+
results.push({
|
|
80
|
+
category: 'Project',
|
|
81
|
+
title: 'Asset Directory (src/resources)',
|
|
82
|
+
passed: hasResources,
|
|
83
|
+
details: hasResources ? 'Asset directory present' : 'src/resources not found'
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// Detect project type (multiplayer vs single-player)
|
|
87
|
+
const hasClientEntry = existsSync(join(cwd, 'src', 'client', 'index.ts'));
|
|
88
|
+
const hasServerEntry = existsSync(join(cwd, 'src', 'server', 'index.ts'));
|
|
89
|
+
const hasSingleEntry = existsSync(join(cwd, 'src', 'index.ts'));
|
|
90
|
+
let projectType = 'Unknown';
|
|
91
|
+
if (hasClientEntry && hasServerEntry) {
|
|
92
|
+
projectType = 'Multiplayer (Client & Server)';
|
|
93
|
+
}
|
|
94
|
+
else if (hasClientEntry) {
|
|
95
|
+
projectType = 'Multiplayer (Client)';
|
|
96
|
+
}
|
|
97
|
+
else if (hasServerEntry) {
|
|
98
|
+
projectType = 'Dedicated Server';
|
|
99
|
+
}
|
|
100
|
+
else if (hasSingleEntry) {
|
|
101
|
+
projectType = 'Singleplayer';
|
|
102
|
+
}
|
|
103
|
+
results.push({
|
|
104
|
+
category: 'Project',
|
|
105
|
+
title: 'Project Architecture',
|
|
106
|
+
passed: projectType !== 'Unknown' || isKitCoreRepo,
|
|
107
|
+
details: isKitCoreRepo ? 'Kit Core Framework Repository' : `Detected Type: ${projectType}`
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
results.push({
|
|
112
|
+
category: 'Project',
|
|
113
|
+
title: 'package.json format',
|
|
114
|
+
passed: false,
|
|
115
|
+
details: 'package.json is malformed or invalid JSON'
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
// Check for build script: either bun-build.ts exists, or package.json has a build script using kit build
|
|
119
|
+
const buildScript = join(cwd, 'bun-build.ts');
|
|
120
|
+
const hasBuildScript = existsSync(buildScript);
|
|
121
|
+
let hasKitBuildScript = false;
|
|
122
|
+
try {
|
|
123
|
+
const rawPkg = readFileSync(pkgPath, 'utf8');
|
|
124
|
+
const pkg = JSON.parse(rawPkg);
|
|
125
|
+
if (pkg.scripts && typeof pkg.scripts.build === 'string' && pkg.scripts.build.includes('kit build')) {
|
|
126
|
+
hasKitBuildScript = true;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Ignored, handled above
|
|
131
|
+
}
|
|
132
|
+
const buildPipelinePassed = hasBuildScript || hasKitBuildScript;
|
|
133
|
+
let buildDetails = 'No build pipeline configured';
|
|
134
|
+
if (hasKitBuildScript && hasBuildScript) {
|
|
135
|
+
buildDetails = 'Kit CLI build pipeline with bun-build.ts present';
|
|
136
|
+
}
|
|
137
|
+
else if (hasKitBuildScript) {
|
|
138
|
+
buildDetails = 'Kit CLI native build pipeline configured';
|
|
139
|
+
}
|
|
140
|
+
else if (hasBuildScript) {
|
|
141
|
+
buildDetails = 'Legacy bun-build.ts present';
|
|
142
|
+
}
|
|
143
|
+
results.push({
|
|
144
|
+
category: 'Project',
|
|
145
|
+
title: 'Build Pipeline',
|
|
146
|
+
passed: buildPipelinePassed,
|
|
147
|
+
details: buildDetails
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
results.push({
|
|
152
|
+
category: 'Project',
|
|
153
|
+
title: 'Project Context',
|
|
154
|
+
passed: true,
|
|
155
|
+
details: 'Not currently executed inside a Node/Bun project root.'
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
// Render results
|
|
159
|
+
let allPassed = true;
|
|
160
|
+
for (const res of results) {
|
|
161
|
+
const icon = res.passed ? chalk.green('✓') : chalk.red('✗');
|
|
162
|
+
const titleText = res.passed ? chalk.white(res.title) : chalk.red.bold(res.title);
|
|
163
|
+
console.log(` ${icon} [${chalk.cyan(res.category)}] ${titleText}`);
|
|
164
|
+
if (res.details && (!res.passed || isVerbose)) {
|
|
165
|
+
console.log(` ${chalk.dim(res.details)}`);
|
|
166
|
+
}
|
|
167
|
+
if (!res.passed) {
|
|
168
|
+
allPassed = false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
console.log('\n');
|
|
172
|
+
if (allPassed) {
|
|
173
|
+
console.log(` ${chalk.green.bold('All doctor diagnostics passed!')}\n`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
console.log(` ${chalk.yellow.bold('Some issues were detected. Check the items above.')}\n`);
|
|
177
|
+
}
|
|
178
|
+
return allPassed;
|
|
179
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for hosting the project.
|
|
3
|
+
*/
|
|
4
|
+
export interface HostOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Port to bind the server to (default: 8090).
|
|
7
|
+
*/
|
|
8
|
+
port?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Directory containing built files to serve (default: ./dist).
|
|
11
|
+
*/
|
|
12
|
+
directory?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Whether to trigger a build before hosting (default: false).
|
|
15
|
+
*/
|
|
16
|
+
build?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Whether to log detailed server events.
|
|
19
|
+
*/
|
|
20
|
+
verbose?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Result of the host process.
|
|
24
|
+
*/
|
|
25
|
+
export interface HostResult {
|
|
26
|
+
/**
|
|
27
|
+
* Whether the host process started successfully.
|
|
28
|
+
*/
|
|
29
|
+
success: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Explanation of the result or error message.
|
|
32
|
+
*/
|
|
33
|
+
message: string;
|
|
34
|
+
/**
|
|
35
|
+
* Server instance if static HTTP server was started.
|
|
36
|
+
*/
|
|
37
|
+
server?: {
|
|
38
|
+
stop(): void;
|
|
39
|
+
port: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Hosts the game application via local HTTP server (for singleplayer/client games)
|
|
44
|
+
* or launches the multiplayer node server. Handles missing files gracefully.
|
|
45
|
+
* @param pOptions - Host options.
|
|
46
|
+
* @returns HostResult indicating success or failure message.
|
|
47
|
+
*/
|
|
48
|
+
export declare function processHost(pOptions?: HostOptions): Promise<HostResult>;
|
|
49
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/cli/host.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,MAAM,CAAC,EAAE;QAAE,IAAI,IAAI,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAoBD;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,QAAQ,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiHjF"}
|
package/lib/cli/host.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { join, resolve } from 'path';
|
|
3
|
+
import { networkInterfaces } from 'os';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { detectArchitecture } from './app-bundler';
|
|
6
|
+
/**
|
|
7
|
+
* Retrieves the primary local IPv4 address for local network access.
|
|
8
|
+
* @returns Primary LAN IPv4 address or localhost fallback.
|
|
9
|
+
*/
|
|
10
|
+
function getNetworkAddress() {
|
|
11
|
+
const interfaces = networkInterfaces();
|
|
12
|
+
for (const name of Object.keys(interfaces)) {
|
|
13
|
+
const netList = interfaces[name];
|
|
14
|
+
if (!netList)
|
|
15
|
+
continue;
|
|
16
|
+
for (const net of netList) {
|
|
17
|
+
if (net.family === 'IPv4' && !net.internal) {
|
|
18
|
+
return net.address;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return 'localhost';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Hosts the game application via local HTTP server (for singleplayer/client games)
|
|
26
|
+
* or launches the multiplayer node server. Handles missing files gracefully.
|
|
27
|
+
* @param pOptions - Host options.
|
|
28
|
+
* @returns HostResult indicating success or failure message.
|
|
29
|
+
*/
|
|
30
|
+
export async function processHost(pOptions = {}) {
|
|
31
|
+
const cwd = process.cwd();
|
|
32
|
+
const defaultPort = 8090;
|
|
33
|
+
const port = pOptions.port || defaultPort;
|
|
34
|
+
const distDir = pOptions.directory ? resolve(pOptions.directory) : join(cwd, 'dist');
|
|
35
|
+
const architecture = detectArchitecture(join(cwd, 'src'));
|
|
36
|
+
if (!existsSync(distDir)) {
|
|
37
|
+
const message = `Target directory "${distDir}" does not exist. Run "kit build" or use "kit host -b" first.`;
|
|
38
|
+
console.error(chalk.red(`\n[Kit Host] ${message}\n`));
|
|
39
|
+
return { success: false, message };
|
|
40
|
+
}
|
|
41
|
+
// Multiplayer Hosting
|
|
42
|
+
if (architecture === 'multi') {
|
|
43
|
+
const serverJsPath = join(distDir, 'server.js');
|
|
44
|
+
if (!existsSync(serverJsPath)) {
|
|
45
|
+
const message = `Cannot host multiplayer project: "${serverJsPath}" was not found. Please compile the server first using "kit build".`;
|
|
46
|
+
console.error(chalk.red(`\n[Kit Host] ${message}\n`));
|
|
47
|
+
return { success: false, message };
|
|
48
|
+
}
|
|
49
|
+
console.log(chalk.cyan(`\n🎮 Starting Multiplayer Server from ${chalk.bold(distDir)}...\n`));
|
|
50
|
+
let serverSettingsPort = port;
|
|
51
|
+
const settingsPath = join(distDir, 'settings.json');
|
|
52
|
+
if (existsSync(settingsPath)) {
|
|
53
|
+
try {
|
|
54
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
55
|
+
if (settings.port)
|
|
56
|
+
serverSettingsPort = Number(settings.port);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Ignore fallback to default port
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const proc = Bun.spawn(['node', 'server.js'], {
|
|
63
|
+
cwd: distDir,
|
|
64
|
+
stdout: 'inherit',
|
|
65
|
+
stderr: 'inherit',
|
|
66
|
+
stdin: 'inherit'
|
|
67
|
+
});
|
|
68
|
+
console.log(chalk.green(`✓ Multiplayer server process spawned (configured port: ${serverSettingsPort})`));
|
|
69
|
+
process.on('SIGINT', () => {
|
|
70
|
+
proc.kill();
|
|
71
|
+
process.exit(0);
|
|
72
|
+
});
|
|
73
|
+
process.on('SIGTERM', () => {
|
|
74
|
+
proc.kill();
|
|
75
|
+
process.exit(0);
|
|
76
|
+
});
|
|
77
|
+
await proc.exited;
|
|
78
|
+
return { success: true, message: 'Multiplayer server finished running.' };
|
|
79
|
+
}
|
|
80
|
+
// Singleplayer / Client static web hosting
|
|
81
|
+
const indexPath = join(distDir, 'index.html');
|
|
82
|
+
if (!existsSync(indexPath)) {
|
|
83
|
+
const message = `Missing entrypoint: "${indexPath}" was not found in dist. Run "kit build" or use "kit host -b" to compile.`;
|
|
84
|
+
console.error(chalk.red(`\n[Kit Host] ${message}\n`));
|
|
85
|
+
return { success: false, message };
|
|
86
|
+
}
|
|
87
|
+
const lanIp = getNetworkAddress();
|
|
88
|
+
const server = Bun.serve({
|
|
89
|
+
port,
|
|
90
|
+
async fetch(pReq) {
|
|
91
|
+
const path = new URL(pReq.url).pathname;
|
|
92
|
+
const target = path === '/' ? '/index.html' : decodeURIComponent(path);
|
|
93
|
+
const file = Bun.file(join(distDir, target));
|
|
94
|
+
if (!await file.exists()) {
|
|
95
|
+
if (pOptions.verbose) {
|
|
96
|
+
console.warn(chalk.yellow(`[Kit Host] 404 Not Found: ${target}`));
|
|
97
|
+
}
|
|
98
|
+
return new Response('Not Found', { status: 404 });
|
|
99
|
+
}
|
|
100
|
+
return new Response(file);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
console.log(chalk.cyan('\n🎮 Kit Game Host Server\n'));
|
|
104
|
+
console.log(` ${chalk.bold('Local:')} ${chalk.green(`http://localhost:${server.port}`)}`);
|
|
105
|
+
if (lanIp !== 'localhost') {
|
|
106
|
+
console.log(` ${chalk.bold('Network:')} ${chalk.green(`http://${lanIp}:${server.port}`)}`);
|
|
107
|
+
}
|
|
108
|
+
console.log(chalk.dim(`\nServing files from: ${distDir}`));
|
|
109
|
+
console.log(chalk.dim('Press Ctrl+C to stop the server\n'));
|
|
110
|
+
process.on('SIGINT', () => {
|
|
111
|
+
console.log(chalk.yellow('\nShutting down host server...'));
|
|
112
|
+
server.stop();
|
|
113
|
+
process.exit(0);
|
|
114
|
+
});
|
|
115
|
+
process.on('SIGTERM', () => {
|
|
116
|
+
server.stop();
|
|
117
|
+
process.exit(0);
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
success: true,
|
|
121
|
+
message: `Serving files on port ${server.port}`,
|
|
122
|
+
server: {
|
|
123
|
+
stop: () => server.stop(),
|
|
124
|
+
port: server.port
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
package/lib/cli/init.d.ts
CHANGED
package/lib/cli/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AASA;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAyFD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AASA;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAyFD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAwLtE"}
|
package/lib/cli/init.js
CHANGED
|
@@ -115,8 +115,10 @@ export async function processInit(pOptions) {
|
|
|
115
115
|
}
|
|
116
116
|
let projectName = pOptions.projectName;
|
|
117
117
|
let gameType = 'single';
|
|
118
|
+
let shouldInstall = Boolean(pOptions.install);
|
|
119
|
+
const isInteractive = !pOptions.single && !pOptions.multi && !projectName;
|
|
118
120
|
// Interactive Walkthrough
|
|
119
|
-
if (
|
|
121
|
+
if (isInteractive) {
|
|
120
122
|
const name = await text({
|
|
121
123
|
message: 'What is the name of your project?',
|
|
122
124
|
placeholder: 'my-amazing-project',
|
|
@@ -145,6 +147,13 @@ export async function processInit(pOptions) {
|
|
|
145
147
|
process.exit(0);
|
|
146
148
|
}
|
|
147
149
|
gameType = type;
|
|
150
|
+
const installChoice = await confirm({
|
|
151
|
+
message: 'Install dependencies with Bun now?',
|
|
152
|
+
initialValue: true,
|
|
153
|
+
});
|
|
154
|
+
if (!isCancel(installChoice)) {
|
|
155
|
+
shouldInstall = Boolean(installChoice);
|
|
156
|
+
}
|
|
148
157
|
}
|
|
149
158
|
else {
|
|
150
159
|
// Handle flags
|
|
@@ -159,13 +168,19 @@ export async function processInit(pOptions) {
|
|
|
159
168
|
}
|
|
160
169
|
const projectDir = path.join(process.cwd(), projectName);
|
|
161
170
|
if (fs.existsSync(projectDir)) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
171
|
+
if (isInteractive) {
|
|
172
|
+
const overwrite = await confirm({
|
|
173
|
+
message: `Directory ${chalk.cyan(projectName)} already exists. Overwrite?`,
|
|
174
|
+
initialValue: false,
|
|
175
|
+
});
|
|
176
|
+
if (isCancel(overwrite) || !overwrite) {
|
|
177
|
+
cancel('Installation aborted.');
|
|
178
|
+
process.exit(0);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else if (!pOptions.force) {
|
|
182
|
+
console.error(chalk.red(`\nError: Destination directory '${projectName}' already exists. Use --force (-f) to overwrite.`));
|
|
183
|
+
process.exit(1);
|
|
169
184
|
}
|
|
170
185
|
}
|
|
171
186
|
const s = spinner();
|
|
@@ -173,8 +188,6 @@ export async function processInit(pOptions) {
|
|
|
173
188
|
try {
|
|
174
189
|
const projectPath = path.join(process.cwd(), projectName);
|
|
175
190
|
if (fs.existsSync(projectPath)) {
|
|
176
|
-
// This case should ideally be handled by the confirm prompt above,
|
|
177
|
-
// but good to have a fallback for non-interactive mode or race conditions.
|
|
178
191
|
fs.rmSync(projectPath, { recursive: true, force: true });
|
|
179
192
|
}
|
|
180
193
|
fs.mkdirSync(projectPath, { recursive: true });
|
|
@@ -183,12 +196,9 @@ export async function processInit(pOptions) {
|
|
|
183
196
|
if (gameType === 'both')
|
|
184
197
|
templateType = 'multi'; // Use multi for both for now
|
|
185
198
|
// Resolve template path
|
|
186
|
-
// When running from lib/bundle/cli/cli.js, templates are in ./kit-game-templates/
|
|
187
199
|
const templatesDir = path.join(path.dirname(fileURLToPath(import.meta.url)), 'kit-game-templates', templateType);
|
|
188
200
|
const author = getGitUser();
|
|
189
201
|
if (!fs.existsSync(templatesDir)) {
|
|
190
|
-
// Fallback for local development (src/cli/init.ts)
|
|
191
|
-
// Go up to root then templates/pType
|
|
192
202
|
const localTemplatesDir = path.join(process.cwd(), 'kit-game-templates', templateType);
|
|
193
203
|
if (!fs.existsSync(localTemplatesDir)) {
|
|
194
204
|
console.error(chalk.red(`Error: Templates not found at ${templatesDir} or ${localTemplatesDir}`));
|
|
@@ -212,11 +222,28 @@ export async function processInit(pOptions) {
|
|
|
212
222
|
// Silently fail if git is not installed or config is missing
|
|
213
223
|
}
|
|
214
224
|
s.stop(`Project ${chalk.green(projectName)} created!`);
|
|
225
|
+
// Automatically install dependencies if requested
|
|
226
|
+
if (shouldInstall) {
|
|
227
|
+
const installSpinner = spinner();
|
|
228
|
+
installSpinner.start('Installing project dependencies with Bun...');
|
|
229
|
+
const installResult = spawnSync('bun', ['install'], { cwd: projectPath, stdio: 'ignore', shell: true });
|
|
230
|
+
if (installResult.status === 0) {
|
|
231
|
+
installSpinner.stop(chalk.green('Dependencies installed successfully!'));
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
installSpinner.stop(chalk.yellow('Dependency installation finished with warnings.'));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
215
237
|
console.log(`${chalk.cyan('│')}`);
|
|
216
238
|
console.log(`${chalk.cyan('│')} ${chalk.white.bold('Next steps:')}`);
|
|
217
239
|
console.log(`${chalk.cyan('│')} ${chalk.dim('1.')} cd ${chalk.cyan(projectName)}`);
|
|
218
|
-
|
|
219
|
-
|
|
240
|
+
if (!shouldInstall) {
|
|
241
|
+
console.log(`${chalk.cyan('│')} ${chalk.dim('2.')} bun install`);
|
|
242
|
+
console.log(`${chalk.cyan('│')} ${chalk.dim('3.')} bun run build`);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
console.log(`${chalk.cyan('│')} ${chalk.dim('2.')} bun run build`);
|
|
246
|
+
}
|
|
220
247
|
console.log(`${chalk.cyan('│')}`);
|
|
221
248
|
outro(chalk.green.bold('Happy coding!'));
|
|
222
249
|
}
|
package/lib/cli/main.d.ts
CHANGED
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
import type { ProcessOptions } from './types';
|
|
2
2
|
import { type InitOptions } from './init';
|
|
3
|
+
import { type DoctorOptions } from './doctor';
|
|
4
|
+
import { type CreateOptions } from './create';
|
|
5
|
+
import { type HostOptions, type HostResult } from './host';
|
|
3
6
|
export declare class KitCLI {
|
|
4
7
|
/**
|
|
5
|
-
* Start resource builder.
|
|
8
|
+
* Start resource builder or watcher.
|
|
9
|
+
* @param pProcessOptions - Configuration options for resource processing.
|
|
6
10
|
*/
|
|
7
11
|
static processResources(pProcessOptions: ProcessOptions): Promise<void>;
|
|
8
12
|
/**
|
|
9
13
|
* Start project initialization.
|
|
14
|
+
* @param pInitOptions - Configuration options for project initialization.
|
|
10
15
|
*/
|
|
11
16
|
static init(pInitOptions: InitOptions): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Run environment and project diagnostics.
|
|
19
|
+
* @param pDoctorOptions - Configuration options for diagnostics.
|
|
20
|
+
*/
|
|
21
|
+
static doctor(pDoctorOptions?: DoctorOptions): Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* Create boilerplate code (e.g. plugins).
|
|
24
|
+
* @param pCreateOptions - Configuration options for code generation.
|
|
25
|
+
*/
|
|
26
|
+
static create(pCreateOptions: CreateOptions): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Host game project locally.
|
|
29
|
+
* @param pHostOptions - Configuration options for hosting.
|
|
30
|
+
* @returns HostResult indicating status and server reference if applicable.
|
|
31
|
+
*/
|
|
32
|
+
static host(pHostOptions?: HostOptions): Promise<HostResult>;
|
|
12
33
|
}
|
|
13
34
|
//# sourceMappingURL=main.d.ts.map
|
package/lib/cli/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EAAe,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAExE,qBAAa,MAAM;IACf;;;OAGG;WACU,gBAAgB,CAAC,eAAe,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7E;;;OAGG;WACU,IAAI,CAAC,YAAY,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D;;;OAGG;WACU,MAAM,CAAC,cAAc,GAAE,aAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzE;;;OAGG;WACU,MAAM,CAAC,cAAc,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjE;;;;OAIG;WACU,IAAI,CAAC,YAAY,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAGzE"}
|
package/lib/cli/main.js
CHANGED
|
@@ -1,16 +1,43 @@
|
|
|
1
1
|
import { processResources } from './resource-builder';
|
|
2
2
|
import { processInit } from './init';
|
|
3
|
+
import { processDoctor } from './doctor';
|
|
4
|
+
import { processCreate } from './create';
|
|
5
|
+
import { processHost } from './host';
|
|
3
6
|
export class KitCLI {
|
|
4
7
|
/**
|
|
5
|
-
* Start resource builder.
|
|
8
|
+
* Start resource builder or watcher.
|
|
9
|
+
* @param pProcessOptions - Configuration options for resource processing.
|
|
6
10
|
*/
|
|
7
11
|
static async processResources(pProcessOptions) {
|
|
8
12
|
await processResources(pProcessOptions);
|
|
9
13
|
}
|
|
10
14
|
/**
|
|
11
15
|
* Start project initialization.
|
|
16
|
+
* @param pInitOptions - Configuration options for project initialization.
|
|
12
17
|
*/
|
|
13
18
|
static async init(pInitOptions) {
|
|
14
19
|
await processInit(pInitOptions);
|
|
15
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Run environment and project diagnostics.
|
|
23
|
+
* @param pDoctorOptions - Configuration options for diagnostics.
|
|
24
|
+
*/
|
|
25
|
+
static async doctor(pDoctorOptions = {}) {
|
|
26
|
+
return await processDoctor(pDoctorOptions);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Create boilerplate code (e.g. plugins).
|
|
30
|
+
* @param pCreateOptions - Configuration options for code generation.
|
|
31
|
+
*/
|
|
32
|
+
static async create(pCreateOptions) {
|
|
33
|
+
await processCreate(pCreateOptions);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Host game project locally.
|
|
37
|
+
* @param pHostOptions - Configuration options for hosting.
|
|
38
|
+
* @returns HostResult indicating status and server reference if applicable.
|
|
39
|
+
*/
|
|
40
|
+
static async host(pHostOptions = {}) {
|
|
41
|
+
return await processHost(pHostOptions);
|
|
42
|
+
}
|
|
16
43
|
}
|