kustom-mc 0.1.2 → 0.1.4
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/dist/bundler.d.ts +33 -0
- package/dist/bundler.js +195 -0
- package/dist/commands/build.d.ts +16 -0
- package/dist/commands/build.js +14 -184
- package/dist/commands/bundle.js +22 -133
- package/dist/commands/init.js +10 -23
- package/dist/commands/login.js +27 -1
- package/dist/commands/new.js +29 -23
- package/dist/commands/push.d.ts +10 -3
- package/dist/commands/push.js +180 -101
- package/dist/commands/validate.js +1 -1
- package/dist/config.d.ts +6 -4
- package/dist/config.js +25 -6
- package/dist/runtime.d.ts +21 -3
- package/dist/runtime.js +2 -27
- package/dist/types/__tests__/debug-inference.d.ts +4 -0
- package/dist/types/__tests__/debug-inference.js +25 -0
- package/dist/types/__tests__/defineScript-methods-dx-test.d.ts +27 -0
- package/dist/types/__tests__/defineScript-methods-dx-test.js +76 -0
- package/dist/types/__tests__/defineScript-methods-negative-test.d.ts +16 -0
- package/dist/types/__tests__/defineScript-methods-negative-test.js +36 -0
- package/dist/types/__tests__/methods-type-test.d.ts +10 -0
- package/dist/types/__tests__/methods-type-test.js +39 -0
- package/dist/types/index.d.ts +91 -3
- package/dist/upload.d.ts +22 -0
- package/dist/upload.js +35 -0
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -26,11 +26,9 @@ export const initCommand = new Command('init')
|
|
|
26
26
|
process.exit(1);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
// Create directory structure
|
|
29
|
+
// Create directory structure (all scripts go in scripts/, type determined by __type)
|
|
30
30
|
const dirs = [
|
|
31
31
|
'scripts/lib',
|
|
32
|
-
'blocks',
|
|
33
|
-
'items',
|
|
34
32
|
'textures',
|
|
35
33
|
'gui',
|
|
36
34
|
'models',
|
|
@@ -68,8 +66,6 @@ export const initCommand = new Command('init')
|
|
|
68
66
|
},
|
|
69
67
|
include: [
|
|
70
68
|
"scripts/**/*.ts",
|
|
71
|
-
"blocks/**/*.ts",
|
|
72
|
-
"items/**/*.ts",
|
|
73
69
|
"dist/types/**/*.d.ts",
|
|
74
70
|
".kustom/types/**/*.d.ts",
|
|
75
71
|
"node_modules/kustom-mc/dist/types/globals.d.ts"
|
|
@@ -80,9 +76,7 @@ export const initCommand = new Command('init')
|
|
|
80
76
|
// Write kustom.config.json with manifest
|
|
81
77
|
const kustomConfig = {
|
|
82
78
|
include: [
|
|
83
|
-
"scripts/**/*.ts"
|
|
84
|
-
"blocks/**/*.ts",
|
|
85
|
-
"items/**/*.ts"
|
|
79
|
+
"scripts/**/*.ts"
|
|
86
80
|
],
|
|
87
81
|
exclude: [
|
|
88
82
|
"**/*.test.ts",
|
|
@@ -103,10 +97,6 @@ export const initCommand = new Command('init')
|
|
|
103
97
|
server: {
|
|
104
98
|
url: "http://localhost:8765"
|
|
105
99
|
},
|
|
106
|
-
deploy: {
|
|
107
|
-
target: "../run/plugins/kustom-plugin/packs",
|
|
108
|
-
reloadCommand: "/kustom pack reload"
|
|
109
|
-
},
|
|
110
100
|
bundle: {
|
|
111
101
|
output: `dist/${packId}.zip`,
|
|
112
102
|
include: [
|
|
@@ -127,12 +117,11 @@ export const initCommand = new Command('init')
|
|
|
127
117
|
version: "1.0.0",
|
|
128
118
|
type: "module",
|
|
129
119
|
scripts: {
|
|
120
|
+
push: "kustom push",
|
|
121
|
+
dev: "kustom push --watch",
|
|
130
122
|
build: "kustom build",
|
|
131
|
-
watch: "kustom build --watch",
|
|
132
|
-
deploy: "kustom build --deploy",
|
|
133
123
|
bundle: "kustom bundle",
|
|
134
|
-
validate: "kustom validate"
|
|
135
|
-
push: "kustom push"
|
|
124
|
+
validate: "kustom validate"
|
|
136
125
|
},
|
|
137
126
|
devDependencies: {
|
|
138
127
|
"kustom-mc": "^0.1.0",
|
|
@@ -194,11 +183,9 @@ dist/
|
|
|
194
183
|
├── kustom.config.json # Pack configuration & manifest
|
|
195
184
|
├── tsconfig.json # TypeScript configuration
|
|
196
185
|
├── package.json # npm configuration
|
|
197
|
-
|
|
186
|
+
├── scripts/ # All scripts (type determined by defineScript/defineBlock/defineItem)
|
|
198
187
|
│ ├── example.ts # Example script
|
|
199
188
|
│ └── lib/ # Shared utilities
|
|
200
|
-
├── blocks/ # Block definitions
|
|
201
|
-
├── items/ # Item definitions
|
|
202
189
|
├── textures/ # Texture files
|
|
203
190
|
├── models/ # Model files
|
|
204
191
|
├── sounds/ # Sound files
|
|
@@ -208,10 +195,10 @@ dist/
|
|
|
208
195
|
console.log(chalk.cyan(` cd ${projectName}`));
|
|
209
196
|
}
|
|
210
197
|
console.log(chalk.cyan(' npm install'));
|
|
211
|
-
console.log(chalk.cyan(' npm run
|
|
198
|
+
console.log(chalk.cyan(' npm run push'));
|
|
212
199
|
console.log('\n' + chalk.bold('Available commands:'));
|
|
213
|
-
console.log(' npm run
|
|
214
|
-
console.log(' npm run
|
|
215
|
-
console.log(' npm run
|
|
200
|
+
console.log(' npm run push - Build and push to server');
|
|
201
|
+
console.log(' npm run dev - Watch mode + auto-push');
|
|
202
|
+
console.log(' npm run build - Compile TypeScript only');
|
|
216
203
|
console.log(' npm run bundle - Create distributable zip');
|
|
217
204
|
});
|
package/dist/commands/login.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import * as readline from 'readline';
|
|
2
3
|
import chalk from 'chalk';
|
|
3
|
-
import { loadConfig } from '../config.js';
|
|
4
|
+
import { loadConfig, saveConfigServerUrl } from '../config.js';
|
|
4
5
|
import { saveServerToken, getServerCredential, normalizeServerUrl, listStoredServers, removeServerCredential } from '../credentials.js';
|
|
5
6
|
/**
|
|
6
7
|
* Validate a token with the server.
|
|
@@ -60,6 +61,7 @@ export const loginCommand = new Command('login')
|
|
|
60
61
|
// Determine server URL
|
|
61
62
|
let serverUrl;
|
|
62
63
|
let token;
|
|
64
|
+
let serverUrlExplicit = false; // true when user provided the URL as a CLI argument
|
|
63
65
|
// If only one argument provided, it might be the token (use config for server)
|
|
64
66
|
if (serverUrlArg && !tokenArg && !serverUrlArg.includes('://') && !serverUrlArg.includes(':')) {
|
|
65
67
|
// Looks like a token, not a URL
|
|
@@ -76,6 +78,7 @@ export const loginCommand = new Command('login')
|
|
|
76
78
|
else if (serverUrlArg) {
|
|
77
79
|
serverUrl = serverUrlArg;
|
|
78
80
|
token = tokenArg;
|
|
81
|
+
serverUrlExplicit = true;
|
|
79
82
|
}
|
|
80
83
|
else {
|
|
81
84
|
// No arguments - use config
|
|
@@ -159,6 +162,29 @@ export const loginCommand = new Command('login')
|
|
|
159
162
|
console.log(` Token expires: ${result.expiresAt}`);
|
|
160
163
|
}
|
|
161
164
|
console.log(`\nCredentials saved to ~/.kustom/credentials.json`);
|
|
165
|
+
// Offer to update kustom.config.json if the user explicitly provided a server URL
|
|
166
|
+
if (serverUrlExplicit) {
|
|
167
|
+
const config = loadConfig(process.cwd());
|
|
168
|
+
const configServerUrl = config.server?.url ? normalizeServerUrl(config.server.url) : undefined;
|
|
169
|
+
if (configServerUrl !== serverUrl) {
|
|
170
|
+
const rl = readline.createInterface({
|
|
171
|
+
input: process.stdin,
|
|
172
|
+
output: process.stdout
|
|
173
|
+
});
|
|
174
|
+
const answer = await new Promise((resolve) => {
|
|
175
|
+
rl.question(chalk.yellow(`Update server.url in kustom.config.json to ${serverUrl}? [y/N] `), resolve);
|
|
176
|
+
});
|
|
177
|
+
rl.close();
|
|
178
|
+
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
179
|
+
if (saveConfigServerUrl(process.cwd(), serverUrl)) {
|
|
180
|
+
console.log(chalk.green('Updated server.url in kustom.config.json'));
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
console.log(chalk.yellow('Could not update kustom.config.json (file not found or not writable)'));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
162
188
|
}
|
|
163
189
|
catch (error) {
|
|
164
190
|
console.error(chalk.red(`Login failed: ${error instanceof Error ? error.message : error}`));
|
package/dist/commands/new.js
CHANGED
|
@@ -22,32 +22,37 @@ export default defineScript({
|
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
`,
|
|
25
|
-
block: `import {
|
|
25
|
+
block: `import { defineBlock } from 'kustom-mc';
|
|
26
26
|
|
|
27
|
-
export default
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
export default defineBlock({
|
|
28
|
+
id: "{{name}}",
|
|
29
|
+
model: "kustom:block/{{name}}",
|
|
30
|
+
collision: "full",
|
|
31
|
+
autoPlace: true,
|
|
32
|
+
|
|
33
|
+
onClick(event) {
|
|
34
|
+
event.player.sendMessage("You clicked {{name}}!");
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
onBreak(event) {
|
|
38
|
+
event.player.sendMessage("{{name}} broken!");
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
onPlace(event) {
|
|
42
|
+
event.player.sendMessage("{{name}} placed!");
|
|
37
43
|
}
|
|
38
44
|
});
|
|
39
45
|
`,
|
|
40
|
-
item: `import {
|
|
46
|
+
item: `import { defineItem } from 'kustom-mc';
|
|
41
47
|
|
|
42
|
-
export default
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
.register();
|
|
48
|
+
export default defineItem({
|
|
49
|
+
id: "{{name}}",
|
|
50
|
+
material: "PAPER",
|
|
51
|
+
model: "kustom:item/{{name}}",
|
|
52
|
+
displayName: "{{name}}",
|
|
53
|
+
|
|
54
|
+
onRightClick(event) {
|
|
55
|
+
event.player.sendMessage("You used {{name}}!");
|
|
51
56
|
}
|
|
52
57
|
});
|
|
53
58
|
`,
|
|
@@ -94,10 +99,11 @@ export const newCommand = new Command('new')
|
|
|
94
99
|
process.exit(1);
|
|
95
100
|
}
|
|
96
101
|
// Determine output directory and file
|
|
102
|
+
// All script types go in scripts/ — type is determined by defineScript/defineBlock/defineItem
|
|
97
103
|
const dirMap = {
|
|
98
104
|
script: 'scripts',
|
|
99
|
-
block: '
|
|
100
|
-
item: '
|
|
105
|
+
block: 'scripts',
|
|
106
|
+
item: 'scripts',
|
|
101
107
|
gui: 'scripts'
|
|
102
108
|
};
|
|
103
109
|
const dir = dirMap[type];
|
package/dist/commands/push.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
/**
|
|
3
|
-
* Push command
|
|
3
|
+
* Push command — build, bundle, and upload pack to server.
|
|
4
|
+
*
|
|
5
|
+
* This is the primary deployment command. It:
|
|
6
|
+
* 1. Builds all TypeScript files
|
|
7
|
+
* 2. Bundles compiled JS + assets into a zip
|
|
8
|
+
* 3. Uploads the zip to the server via HTTP
|
|
4
9
|
*
|
|
5
10
|
* Usage:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
11
|
+
* kustom push # One-shot build + push
|
|
12
|
+
* kustom push --watch # Watch + auto-push on changes
|
|
13
|
+
* kustom push --server <url> # Push to specific server
|
|
14
|
+
* kustom push --dry-run # Build + bundle, don't upload
|
|
8
15
|
*/
|
|
9
16
|
export declare const pushCommand: Command;
|
package/dist/commands/push.js
CHANGED
|
@@ -2,113 +2,181 @@ import { Command } from 'commander';
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import { loadConfig, generateManifest, validateManifest } from '../config.js';
|
|
5
|
+
import chokidar from 'chokidar';
|
|
6
|
+
import { loadConfig } from '../config.js';
|
|
8
7
|
import { getServerToken, normalizeServerUrl, getServerCredential } from '../credentials.js';
|
|
8
|
+
import { bundleToBuffer } from '../bundler.js';
|
|
9
|
+
import { uploadPack } from '../upload.js';
|
|
10
|
+
import { buildAll, buildFile } from './build.js';
|
|
11
|
+
import { prepare } from './prepare.js';
|
|
12
|
+
/** Debounce delay in milliseconds before auto-pushing after a change */
|
|
13
|
+
const PUSH_DEBOUNCE_MS = 500;
|
|
9
14
|
/**
|
|
10
|
-
*
|
|
15
|
+
* Build, bundle, and upload the pack to the server.
|
|
16
|
+
* Returns true on success, false on failure.
|
|
11
17
|
*/
|
|
12
|
-
async function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
async function doPush(projectDir, serverUrl, token, options) {
|
|
19
|
+
try {
|
|
20
|
+
const { buffer, packId, version, fileCount } = await bundleToBuffer(projectDir);
|
|
21
|
+
const sizeKB = (buffer.length / 1024).toFixed(2);
|
|
22
|
+
if (options.verbose) {
|
|
23
|
+
console.log(` Bundle: ${fileCount} files, ${sizeKB} KB`);
|
|
24
|
+
}
|
|
25
|
+
const result = await uploadPack(serverUrl, token, buffer, packId);
|
|
26
|
+
if (result.success) {
|
|
27
|
+
console.log(chalk.green(` Pushed ${packId} v${version} (${sizeKB} KB)`));
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.error(chalk.red(` Push failed: ${result.error || 'Unknown error'}`));
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error(chalk.red(` Push failed: ${error instanceof Error ? error.message : error}`));
|
|
37
|
+
return false;
|
|
18
38
|
}
|
|
19
|
-
const packId = config.manifest?.id || 'kustompack';
|
|
20
|
-
const version = config.manifest?.version || '1.0.0';
|
|
21
|
-
return new Promise((resolve, reject) => {
|
|
22
|
-
const chunks = [];
|
|
23
|
-
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
24
|
-
archive.on('data', (chunk) => chunks.push(chunk));
|
|
25
|
-
archive.on('end', () => resolve({
|
|
26
|
-
buffer: Buffer.concat(chunks),
|
|
27
|
-
packId,
|
|
28
|
-
version
|
|
29
|
-
}));
|
|
30
|
-
archive.on('error', reject);
|
|
31
|
-
// Generate and add manifest
|
|
32
|
-
const manifestContent = generateManifest(config);
|
|
33
|
-
archive.append(manifestContent, { name: 'kustompack.json' });
|
|
34
|
-
// Add files based on bundle config
|
|
35
|
-
const includePatterns = config.bundle?.include || [
|
|
36
|
-
'**/*.js',
|
|
37
|
-
'textures/**/*',
|
|
38
|
-
'gui/**/*',
|
|
39
|
-
'models/**/*',
|
|
40
|
-
'sounds/**/*'
|
|
41
|
-
];
|
|
42
|
-
const addFiles = async () => {
|
|
43
|
-
const addedFiles = new Set();
|
|
44
|
-
for (const pattern of includePatterns) {
|
|
45
|
-
const files = await glob(pattern, {
|
|
46
|
-
cwd: projectDir,
|
|
47
|
-
nodir: true,
|
|
48
|
-
ignore: ['node_modules/**', 'dist/**', '*.ts', 'kustompack.json']
|
|
49
|
-
});
|
|
50
|
-
for (const file of files) {
|
|
51
|
-
if (addedFiles.has(file))
|
|
52
|
-
continue;
|
|
53
|
-
const filePath = path.resolve(projectDir, file);
|
|
54
|
-
if (fs.existsSync(filePath)) {
|
|
55
|
-
archive.file(filePath, { name: file });
|
|
56
|
-
addedFiles.add(file);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
archive.finalize();
|
|
61
|
-
};
|
|
62
|
-
addFiles().catch(reject);
|
|
63
|
-
});
|
|
64
39
|
}
|
|
65
40
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* Currently sends raw zip bytes for simplicity and performance.
|
|
69
|
-
*
|
|
70
|
-
* TODO: If a web UI is added later, consider supporting multipart/form-data
|
|
71
|
-
* uploads on the server side (Option 2). This would require:
|
|
72
|
-
* - Adding multipart parsing to PackRegistryServer.java (Apache Commons FileUpload or manual)
|
|
73
|
-
* - Keeping raw zip support for CLI (faster)
|
|
74
|
-
* - Adding multipart support for browser uploads
|
|
41
|
+
* Watch mode: rebuild and auto-push on file changes with debounce.
|
|
75
42
|
*/
|
|
76
|
-
async function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (!response.ok) {
|
|
96
|
-
throw new Error(result.error || result.message || `Server returned ${response.status}`);
|
|
43
|
+
async function pushWatchMode(config, projectDir, serverUrl, token, options) {
|
|
44
|
+
console.log(chalk.blue('Starting push watch mode...'));
|
|
45
|
+
console.log(` Server: ${serverUrl}`);
|
|
46
|
+
console.log();
|
|
47
|
+
// Initial build
|
|
48
|
+
await buildAll(config, options);
|
|
49
|
+
// Initial push
|
|
50
|
+
console.log('\nPushing initial build...');
|
|
51
|
+
await doPush(projectDir, serverUrl, token, options);
|
|
52
|
+
// Debounce timer
|
|
53
|
+
let pushTimer = null;
|
|
54
|
+
function schedulePush() {
|
|
55
|
+
if (pushTimer)
|
|
56
|
+
clearTimeout(pushTimer);
|
|
57
|
+
pushTimer = setTimeout(async () => {
|
|
58
|
+
pushTimer = null;
|
|
59
|
+
console.log(chalk.gray('\nPushing changes...'));
|
|
60
|
+
await doPush(projectDir, serverUrl, token, options);
|
|
61
|
+
}, PUSH_DEBOUNCE_MS);
|
|
97
62
|
}
|
|
98
|
-
|
|
63
|
+
// Watch script files
|
|
64
|
+
const patterns = config.include || ['scripts/**/*.ts'];
|
|
65
|
+
const libDirs = config.lib || ['scripts/lib'];
|
|
66
|
+
const scriptWatcher = chokidar.watch(patterns, {
|
|
67
|
+
cwd: projectDir,
|
|
68
|
+
ignoreInitial: true,
|
|
69
|
+
ignored: config.exclude
|
|
70
|
+
});
|
|
71
|
+
const rebuildAndPush = async (changedFile) => {
|
|
72
|
+
const isLibFile = libDirs.some(lib => changedFile.startsWith(lib + '/') || changedFile.startsWith(lib + '\\'));
|
|
73
|
+
if (isLibFile) {
|
|
74
|
+
console.log(`\nLib file changed: ${changedFile}`);
|
|
75
|
+
console.log('Rebuilding all scripts...');
|
|
76
|
+
await buildAll(config, options);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log(`\nFile changed: ${changedFile}`);
|
|
80
|
+
try {
|
|
81
|
+
const outputPath = await buildFile(changedFile, config, options);
|
|
82
|
+
console.log(` [OK] ${changedFile} -> ${path.relative(projectDir, outputPath)}`);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
console.error(` [ERROR] ${changedFile}: ${error instanceof Error ? error.message : error}`);
|
|
86
|
+
return; // Don't push if build failed
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
schedulePush();
|
|
90
|
+
};
|
|
91
|
+
scriptWatcher.on('change', rebuildAndPush);
|
|
92
|
+
scriptWatcher.on('add', rebuildAndPush);
|
|
93
|
+
scriptWatcher.on('unlink', (file) => {
|
|
94
|
+
console.log(`\nFile deleted: ${file}`);
|
|
95
|
+
// Delete the compiled output
|
|
96
|
+
const outDir = config.outDir || 'dist';
|
|
97
|
+
const outputPath = path.resolve(projectDir, outDir, file.replace(/\.ts$/, '.js'));
|
|
98
|
+
if (fs.existsSync(outputPath)) {
|
|
99
|
+
fs.unlinkSync(outputPath);
|
|
100
|
+
}
|
|
101
|
+
schedulePush();
|
|
102
|
+
});
|
|
103
|
+
// Watch sounds folder for type regeneration + push
|
|
104
|
+
const soundsWatcher = chokidar.watch('sounds/**/*.ogg', {
|
|
105
|
+
cwd: projectDir,
|
|
106
|
+
ignoreInitial: true
|
|
107
|
+
});
|
|
108
|
+
soundsWatcher.on('add', async (file) => {
|
|
109
|
+
console.log(`\nSound added: ${file}`);
|
|
110
|
+
console.log('Regenerating types...');
|
|
111
|
+
await prepare(projectDir);
|
|
112
|
+
schedulePush();
|
|
113
|
+
});
|
|
114
|
+
soundsWatcher.on('change', () => {
|
|
115
|
+
schedulePush();
|
|
116
|
+
});
|
|
117
|
+
soundsWatcher.on('unlink', async (file) => {
|
|
118
|
+
console.log(`\nSound removed: ${file}`);
|
|
119
|
+
console.log('Regenerating types...');
|
|
120
|
+
await prepare(projectDir);
|
|
121
|
+
schedulePush();
|
|
122
|
+
});
|
|
123
|
+
// Watch asset folders (textures, models, gui, etc.) — push on change
|
|
124
|
+
const assetPatterns = [
|
|
125
|
+
'textures/**/*',
|
|
126
|
+
'models/**/*',
|
|
127
|
+
'gui/**/*',
|
|
128
|
+
'lang/**/*',
|
|
129
|
+
'definitions/**/*'
|
|
130
|
+
];
|
|
131
|
+
const assetsWatcher = chokidar.watch(assetPatterns, {
|
|
132
|
+
cwd: projectDir,
|
|
133
|
+
ignoreInitial: true
|
|
134
|
+
});
|
|
135
|
+
assetsWatcher.on('add', (file) => {
|
|
136
|
+
console.log(`\nAsset added: ${file}`);
|
|
137
|
+
schedulePush();
|
|
138
|
+
});
|
|
139
|
+
assetsWatcher.on('change', (file) => {
|
|
140
|
+
console.log(`\nAsset changed: ${file}`);
|
|
141
|
+
schedulePush();
|
|
142
|
+
});
|
|
143
|
+
assetsWatcher.on('unlink', (file) => {
|
|
144
|
+
console.log(`\nAsset removed: ${file}`);
|
|
145
|
+
schedulePush();
|
|
146
|
+
});
|
|
147
|
+
console.log('\nWatching for changes... (Ctrl+C to stop)');
|
|
148
|
+
// Keep process alive
|
|
149
|
+
process.on('SIGINT', () => {
|
|
150
|
+
console.log('\nStopping push watch mode...');
|
|
151
|
+
if (pushTimer)
|
|
152
|
+
clearTimeout(pushTimer);
|
|
153
|
+
scriptWatcher.close();
|
|
154
|
+
soundsWatcher.close();
|
|
155
|
+
assetsWatcher.close();
|
|
156
|
+
process.exit(0);
|
|
157
|
+
});
|
|
99
158
|
}
|
|
100
159
|
/**
|
|
101
|
-
* Push command
|
|
160
|
+
* Push command — build, bundle, and upload pack to server.
|
|
161
|
+
*
|
|
162
|
+
* This is the primary deployment command. It:
|
|
163
|
+
* 1. Builds all TypeScript files
|
|
164
|
+
* 2. Bundles compiled JS + assets into a zip
|
|
165
|
+
* 3. Uploads the zip to the server via HTTP
|
|
102
166
|
*
|
|
103
167
|
* Usage:
|
|
104
|
-
*
|
|
105
|
-
*
|
|
168
|
+
* kustom push # One-shot build + push
|
|
169
|
+
* kustom push --watch # Watch + auto-push on changes
|
|
170
|
+
* kustom push --server <url> # Push to specific server
|
|
171
|
+
* kustom push --dry-run # Build + bundle, don't upload
|
|
106
172
|
*/
|
|
107
173
|
export const pushCommand = new Command('push')
|
|
108
|
-
.description('
|
|
174
|
+
.description('Build, bundle, and upload pack to the server')
|
|
109
175
|
.option('-s, --server <url>', 'Server URL (overrides config)')
|
|
110
|
-
.option('--
|
|
176
|
+
.option('-w, --watch', 'Watch for changes and auto-push')
|
|
177
|
+
.option('--dry-run', 'Build and bundle but do not upload')
|
|
111
178
|
.option('-y, --yes', 'Skip confirmation prompt')
|
|
179
|
+
.option('-v, --verbose', 'Verbose output')
|
|
112
180
|
.action(async (options) => {
|
|
113
181
|
const projectDir = process.cwd();
|
|
114
182
|
const config = loadConfig(projectDir);
|
|
@@ -135,22 +203,35 @@ export const pushCommand = new Command('push')
|
|
|
135
203
|
process.exit(1);
|
|
136
204
|
}
|
|
137
205
|
const cred = getServerCredential(serverUrl);
|
|
138
|
-
|
|
206
|
+
// Watch mode
|
|
207
|
+
if (options?.watch) {
|
|
208
|
+
if (!token) {
|
|
209
|
+
console.error(chalk.red('Error: --watch requires authentication'));
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
await pushWatchMode(config, projectDir, serverUrl, token, { verbose: options.verbose });
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
// One-shot mode: build → bundle → upload
|
|
216
|
+
console.log(chalk.blue('Building and pushing pack...'));
|
|
139
217
|
console.log(` Server: ${serverUrl}`);
|
|
140
218
|
if (cred?.playerName) {
|
|
141
219
|
console.log(` As: ${cred.playerName}`);
|
|
142
220
|
}
|
|
143
221
|
console.log();
|
|
144
222
|
try {
|
|
145
|
-
//
|
|
223
|
+
// Build
|
|
224
|
+
await buildAll(config, { verbose: options?.verbose });
|
|
225
|
+
console.log();
|
|
226
|
+
// Bundle
|
|
146
227
|
console.log('Creating bundle...');
|
|
147
|
-
const { buffer, packId, version } = await
|
|
228
|
+
const { buffer, packId, version, fileCount } = await bundleToBuffer(projectDir);
|
|
148
229
|
const sizeKB = (buffer.length / 1024).toFixed(2);
|
|
149
230
|
console.log(chalk.green(` Pack: ${packId} v${version}`));
|
|
150
|
-
console.log(chalk.green(` Size: ${sizeKB} KB`));
|
|
231
|
+
console.log(chalk.green(` Size: ${sizeKB} KB (${fileCount} files)`));
|
|
232
|
+
// Dry run — save to disk instead of uploading
|
|
151
233
|
if (options?.dryRun) {
|
|
152
234
|
console.log(chalk.yellow('\n--dry-run: Bundle created but not uploaded.'));
|
|
153
|
-
// Optionally save the bundle locally
|
|
154
235
|
const outputPath = path.join(projectDir, 'dist', `${packId}.zip`);
|
|
155
236
|
const outputDir = path.dirname(outputPath);
|
|
156
237
|
if (!fs.existsSync(outputDir)) {
|
|
@@ -164,7 +245,6 @@ export const pushCommand = new Command('push')
|
|
|
164
245
|
if (!options?.yes) {
|
|
165
246
|
console.log(chalk.yellow(`\nThis will upload ${packId} v${version} to ${serverUrl}`));
|
|
166
247
|
console.log(chalk.yellow('If a pack with this ID exists, it will be replaced.'));
|
|
167
|
-
// Simple confirmation using readline
|
|
168
248
|
const readline = await import('readline');
|
|
169
249
|
const rl = readline.createInterface({
|
|
170
250
|
input: process.stdin,
|
|
@@ -183,15 +263,14 @@ export const pushCommand = new Command('push')
|
|
|
183
263
|
console.log('\nUploading...');
|
|
184
264
|
const result = await uploadPack(serverUrl, token, buffer, packId);
|
|
185
265
|
if (result.success) {
|
|
186
|
-
console.log(chalk.green('\
|
|
266
|
+
console.log(chalk.green('\nPush successful!'));
|
|
187
267
|
if (result.message) {
|
|
188
268
|
console.log(` ${result.message}`);
|
|
189
269
|
}
|
|
190
|
-
console.log(`\nPack ${packId} v${version} is now
|
|
191
|
-
console.log(chalk.gray('Run /kustom pack reload to apply changes in-game.'));
|
|
270
|
+
console.log(`\nPack ${packId} v${version} is now live on the server.`);
|
|
192
271
|
}
|
|
193
272
|
else {
|
|
194
|
-
console.error(chalk.red('\
|
|
273
|
+
console.error(chalk.red('\nPush failed'));
|
|
195
274
|
if (result.error) {
|
|
196
275
|
console.error(chalk.red(` ${result.error}`));
|
|
197
276
|
}
|
|
@@ -30,7 +30,7 @@ export const validateCommand = new Command('validate')
|
|
|
30
30
|
}
|
|
31
31
|
console.log('Validating scripts...');
|
|
32
32
|
// Find all TypeScript files
|
|
33
|
-
const patterns = config.include || ['scripts/**/*.ts'
|
|
33
|
+
const patterns = config.include || ['scripts/**/*.ts'];
|
|
34
34
|
const excludePatterns = config.exclude || [];
|
|
35
35
|
const files = [];
|
|
36
36
|
for (const pattern of patterns) {
|
package/dist/config.d.ts
CHANGED
|
@@ -37,10 +37,6 @@ export interface KustomConfig {
|
|
|
37
37
|
dependencies?: string[];
|
|
38
38
|
/** Server connection settings */
|
|
39
39
|
server?: ServerConfig;
|
|
40
|
-
deploy?: {
|
|
41
|
-
target?: string;
|
|
42
|
-
reloadCommand?: string;
|
|
43
|
-
};
|
|
44
40
|
bundle?: {
|
|
45
41
|
output?: string;
|
|
46
42
|
include?: string[];
|
|
@@ -84,3 +80,9 @@ export declare function parseCrossPackImport(importPath: string): {
|
|
|
84
80
|
* Create a fully qualified namespaced ID.
|
|
85
81
|
*/
|
|
86
82
|
export declare function createNamespacedId(packId: string, type: string, name: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* Update the server.url in kustom.config.json.
|
|
85
|
+
* Preserves all other config properties.
|
|
86
|
+
* Returns true if the file was updated, false if no config file exists.
|
|
87
|
+
*/
|
|
88
|
+
export declare function saveConfigServerUrl(cwd: string, serverUrl: string): boolean;
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
const defaultConfig = {
|
|
4
|
-
include: ['scripts/**/*.ts'
|
|
4
|
+
include: ['scripts/**/*.ts'],
|
|
5
5
|
exclude: ['**/*.test.ts', '**/*.spec.ts'],
|
|
6
6
|
outDir: 'dist',
|
|
7
7
|
lib: ['scripts/lib'],
|
|
@@ -15,10 +15,6 @@ const defaultConfig = {
|
|
|
15
15
|
server: {
|
|
16
16
|
url: 'http://localhost:8765'
|
|
17
17
|
},
|
|
18
|
-
deploy: {
|
|
19
|
-
target: '../run/plugins/kustom-plugin/packs',
|
|
20
|
-
reloadCommand: '/kustom pack reload'
|
|
21
|
-
},
|
|
22
18
|
bundle: {
|
|
23
19
|
output: 'dist/kustompack.zip',
|
|
24
20
|
include: ['**/*.js', 'textures/**/*', 'gui/**/*', 'models/**/*', 'sounds/**/*']
|
|
@@ -41,7 +37,6 @@ export function loadConfig(cwd) {
|
|
|
41
37
|
id: userConfig.manifest?.id || defaultConfig.manifest?.id || 'my-pack'
|
|
42
38
|
},
|
|
43
39
|
server: { ...defaultConfig.server, ...userConfig.server },
|
|
44
|
-
deploy: { ...defaultConfig.deploy, ...userConfig.deploy },
|
|
45
40
|
bundle: { ...defaultConfig.bundle, ...userConfig.bundle }
|
|
46
41
|
};
|
|
47
42
|
return merged;
|
|
@@ -164,3 +159,27 @@ export function parseCrossPackImport(importPath) {
|
|
|
164
159
|
export function createNamespacedId(packId, type, name) {
|
|
165
160
|
return `${packId}:${type}/${name}`;
|
|
166
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Update the server.url in kustom.config.json.
|
|
164
|
+
* Preserves all other config properties.
|
|
165
|
+
* Returns true if the file was updated, false if no config file exists.
|
|
166
|
+
*/
|
|
167
|
+
export function saveConfigServerUrl(cwd, serverUrl) {
|
|
168
|
+
const configPath = path.join(cwd, 'kustom.config.json');
|
|
169
|
+
if (!fs.existsSync(configPath)) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
174
|
+
const config = JSON.parse(content);
|
|
175
|
+
if (!config.server || typeof config.server !== 'object') {
|
|
176
|
+
config.server = {};
|
|
177
|
+
}
|
|
178
|
+
config.server.url = serverUrl;
|
|
179
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|