kustom-mc 0.1.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.
Files changed (50) hide show
  1. package/README.md +809 -0
  2. package/dist/commands/build.d.ts +2 -0
  3. package/dist/commands/build.js +447 -0
  4. package/dist/commands/bundle.d.ts +2 -0
  5. package/dist/commands/bundle.js +134 -0
  6. package/dist/commands/init.d.ts +2 -0
  7. package/dist/commands/init.js +219 -0
  8. package/dist/commands/list.d.ts +10 -0
  9. package/dist/commands/list.js +167 -0
  10. package/dist/commands/login.d.ts +9 -0
  11. package/dist/commands/login.js +167 -0
  12. package/dist/commands/new.d.ts +2 -0
  13. package/dist/commands/new.js +132 -0
  14. package/dist/commands/prepare.d.ts +9 -0
  15. package/dist/commands/prepare.js +267 -0
  16. package/dist/commands/push.d.ts +9 -0
  17. package/dist/commands/push.js +205 -0
  18. package/dist/commands/validate.d.ts +2 -0
  19. package/dist/commands/validate.js +191 -0
  20. package/dist/compiler/async-transform.d.ts +21 -0
  21. package/dist/compiler/async-transform.js +158 -0
  22. package/dist/compiler/inline.d.ts +32 -0
  23. package/dist/compiler/inline.js +87 -0
  24. package/dist/compiler/postprocess.d.ts +19 -0
  25. package/dist/compiler/postprocess.js +134 -0
  26. package/dist/compiler/rhino-plugin.d.ts +17 -0
  27. package/dist/compiler/rhino-plugin.js +324 -0
  28. package/dist/compiler/transform.d.ts +18 -0
  29. package/dist/compiler/transform.js +59 -0
  30. package/dist/config.d.ts +86 -0
  31. package/dist/config.js +166 -0
  32. package/dist/credentials.d.ts +65 -0
  33. package/dist/credentials.js +136 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.js +28 -0
  36. package/dist/runtime.d.ts +116 -0
  37. package/dist/runtime.js +96 -0
  38. package/dist/types/globals.d.ts +80 -0
  39. package/dist/types/globals.js +10 -0
  40. package/dist/types/index.d.ts +2094 -0
  41. package/dist/types/index.js +9 -0
  42. package/package.json +57 -0
  43. package/templates/project/kustom.config.json +26 -0
  44. package/templates/project/scripts/example.ts +17 -0
  45. package/templates/project/scripts/lib/utils.ts +19 -0
  46. package/templates/project/tsconfig.json +27 -0
  47. package/templates/scripts/block.ts.hbs +14 -0
  48. package/templates/scripts/gui.ts.hbs +28 -0
  49. package/templates/scripts/item.ts.hbs +13 -0
  50. package/templates/scripts/script.ts.hbs +18 -0
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const buildCommand: Command;
@@ -0,0 +1,447 @@
1
+ import { Command } from 'commander';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { glob } from 'glob';
5
+ import * as esbuild from 'esbuild';
6
+ import chokidar from 'chokidar';
7
+ import { transformForRhino } from '../compiler/rhino-plugin.js';
8
+ import { transformAsyncAwait } from '../compiler/async-transform.js';
9
+ import { loadConfig, validateManifest, generateManifest } from '../config.js';
10
+ import { prepare } from './prepare.js';
11
+ export const buildCommand = new Command('build')
12
+ .description('Compile TypeScript scripts to Rhino-compatible JavaScript')
13
+ .option('-w, --watch', 'Watch for changes and rebuild')
14
+ .option('-d, --deploy', 'Copy compiled files to deploy target')
15
+ .option('--no-validate', 'Skip validation')
16
+ .option('-v, --verbose', 'Verbose output')
17
+ .option('--clean', 'Clean output directory before building')
18
+ .action(async (options) => {
19
+ const config = loadConfig(process.cwd());
20
+ // Clean output directory if requested
21
+ if (options.clean) {
22
+ const outDir = config.outDir || 'dist';
23
+ const outPath = path.resolve(process.cwd(), outDir);
24
+ if (fs.existsSync(outPath)) {
25
+ fs.rmSync(outPath, { recursive: true });
26
+ console.log(`Cleaned output directory: ${outDir}`);
27
+ }
28
+ }
29
+ if (options.watch) {
30
+ await watchMode(config, options);
31
+ }
32
+ else {
33
+ await buildAll(config, options);
34
+ }
35
+ });
36
+ /**
37
+ * Get the output path for a source file based on config.outDir
38
+ */
39
+ function getOutputPath(sourceFile, config) {
40
+ const outDir = config.outDir || 'dist';
41
+ const relativePath = sourceFile.replace(/\.ts$/, '.js');
42
+ return path.resolve(process.cwd(), outDir, relativePath);
43
+ }
44
+ /**
45
+ * Get the output directory from config
46
+ */
47
+ function getOutDir(config) {
48
+ return path.resolve(process.cwd(), config.outDir || 'dist');
49
+ }
50
+ async function buildAll(config, options) {
51
+ // Validate manifest configuration
52
+ const manifestErrors = validateManifest(config);
53
+ if (manifestErrors.length > 0) {
54
+ console.error('Manifest validation errors:');
55
+ for (const error of manifestErrors) {
56
+ console.error(` - ${error}`);
57
+ }
58
+ process.exit(1);
59
+ }
60
+ // Run prepare first to generate types
61
+ console.log('Preparing types...');
62
+ await prepare(process.cwd());
63
+ console.log('Building kustompack scripts...');
64
+ console.log(` Pack ID: ${config.manifest?.id || 'unknown'}`);
65
+ const startTime = Date.now();
66
+ let successCount = 0;
67
+ let errorCount = 0;
68
+ // Find all TypeScript files
69
+ const patterns = config.include || ['scripts/**/*.ts'];
70
+ const excludePatterns = config.exclude || [];
71
+ const files = [];
72
+ for (const pattern of patterns) {
73
+ const matches = await glob(pattern, {
74
+ cwd: process.cwd(),
75
+ ignore: excludePatterns
76
+ });
77
+ files.push(...matches);
78
+ }
79
+ // Filter out lib files (they get inlined)
80
+ const libDirs = config.lib || ['scripts/lib'];
81
+ const scriptFiles = files.filter(file => {
82
+ return !libDirs.some(lib => file.startsWith(lib + '/') || file.startsWith(lib + '\\'));
83
+ });
84
+ if (scriptFiles.length === 0) {
85
+ console.log('No TypeScript files found to compile.');
86
+ return;
87
+ }
88
+ const outDir = getOutDir(config);
89
+ console.log(`Found ${scriptFiles.length} script(s) to compile...`);
90
+ console.log(`Output directory: ${outDir}`);
91
+ for (const file of scriptFiles) {
92
+ try {
93
+ await buildFile(file, config, options);
94
+ successCount++;
95
+ if (options.verbose) {
96
+ console.log(` [OK] ${file}`);
97
+ }
98
+ }
99
+ catch (error) {
100
+ errorCount++;
101
+ console.error(` [ERROR] ${file}: ${error instanceof Error ? error.message : error}`);
102
+ }
103
+ }
104
+ // Generate manifest in output directory
105
+ const manifestPath = path.resolve(outDir, 'kustompack.json');
106
+ try {
107
+ const manifestContent = generateManifest(config);
108
+ fs.writeFileSync(manifestPath, manifestContent);
109
+ if (options.verbose) {
110
+ console.log(` [OK] kustompack.json`);
111
+ }
112
+ }
113
+ catch (error) {
114
+ console.error(` [ERROR] kustompack.json: ${error instanceof Error ? error.message : error}`);
115
+ }
116
+ const elapsed = Date.now() - startTime;
117
+ console.log(`\nBuild completed in ${elapsed}ms: ${successCount} succeeded, ${errorCount} failed`);
118
+ if (options.deploy && config.deploy?.target) {
119
+ await deployFiles(scriptFiles, config, options);
120
+ }
121
+ }
122
+ /**
123
+ * esbuild plugin to mark relative script imports and cross-pack imports as external.
124
+ * This prevents esbuild from bundling imported scripts, allowing
125
+ * our rhino-plugin to transform them to __kustom_import() or __kustom_cross_import() calls.
126
+ */
127
+ function externalScriptImportsPlugin(config) {
128
+ const libDirs = config.lib || ['scripts/lib'];
129
+ const declaredDependencies = new Set((config.dependencies || []).map(dep => dep.split('@')[0]));
130
+ const crossPackImportsUsed = new Set();
131
+ return {
132
+ name: 'external-script-imports',
133
+ setup(build) {
134
+ // Mark cross-pack imports as external (packId:path format)
135
+ build.onResolve({ filter: /^[a-z0-9-]+:/ }, (args) => {
136
+ // Extract pack ID from the import path
137
+ const match = args.path.match(/^([a-z0-9-]+):/);
138
+ if (match) {
139
+ const packId = match[1];
140
+ crossPackImportsUsed.add(packId);
141
+ // Warn if importing from undeclared dependency
142
+ if (!declaredDependencies.has(packId)) {
143
+ console.warn(` Warning: Cross-pack import from "${packId}" but it's not declared in dependencies`);
144
+ }
145
+ }
146
+ // Mark as external - will be transformed by rhino-plugin
147
+ return { path: args.path, external: true };
148
+ });
149
+ // Mark relative imports as external, except for lib files
150
+ build.onResolve({ filter: /^\.\.?\// }, (args) => {
151
+ // Check if this is a lib import (should be bundled)
152
+ const isLibImport = libDirs.some(lib => {
153
+ const normalizedLib = lib.replace(/\\/g, '/');
154
+ const normalizedPath = args.path.replace(/\\/g, '/');
155
+ // Check if the resolved path would be in a lib directory
156
+ const resolvedDir = path.dirname(args.importer).replace(/\\/g, '/');
157
+ const fullPath = path.join(resolvedDir, normalizedPath).replace(/\\/g, '/');
158
+ return fullPath.includes(`/${normalizedLib}/`) || fullPath.startsWith(`${normalizedLib}/`);
159
+ });
160
+ if (isLibImport) {
161
+ // Let esbuild bundle lib files
162
+ return null;
163
+ }
164
+ // Mark as external - will be transformed by rhino-plugin
165
+ return { path: args.path, external: true };
166
+ });
167
+ // Report cross-pack imports at end of build
168
+ build.onEnd(() => {
169
+ if (crossPackImportsUsed.size > 0) {
170
+ console.log(` Cross-pack imports: ${Array.from(crossPackImportsUsed).join(', ')}`);
171
+ }
172
+ });
173
+ }
174
+ };
175
+ }
176
+ async function buildFile(file, config, options) {
177
+ const inputPath = path.resolve(process.cwd(), file);
178
+ const outputPath = getOutputPath(file, config);
179
+ // Ensure output directory exists
180
+ const outputDir = path.dirname(outputPath);
181
+ if (!fs.existsSync(outputDir)) {
182
+ fs.mkdirSync(outputDir, { recursive: true });
183
+ }
184
+ // Use esbuild to bundle and transform
185
+ const result = await esbuild.build({
186
+ entryPoints: [inputPath],
187
+ bundle: true,
188
+ write: false, // We'll write after transforming
189
+ format: 'esm',
190
+ target: 'es2020',
191
+ platform: 'neutral',
192
+ external: ['kustom-mc'],
193
+ loader: { '.ts': 'ts' },
194
+ plugins: [externalScriptImportsPlugin(config)],
195
+ });
196
+ if (result.errors.length > 0) {
197
+ throw new Error(result.errors.map(e => e.text).join('\n'));
198
+ }
199
+ const bundledCode = result.outputFiles[0].text;
200
+ // Transform async/await to generators (for Rhino compatibility)
201
+ const asyncTransformed = await transformAsyncAwait(bundledCode);
202
+ // Transform for Rhino compatibility
203
+ const rhinoCode = transformForRhino(asyncTransformed);
204
+ // Write output
205
+ fs.writeFileSync(outputPath, rhinoCode);
206
+ return outputPath;
207
+ }
208
+ async function watchMode(config, options) {
209
+ console.log('Starting watch mode...');
210
+ // Initial build (without deploy - we'll deploy separately)
211
+ await buildAll(config, { ...options, deploy: false });
212
+ // Initial deploy if requested (scripts + assets)
213
+ if (options.deploy && config.deploy?.target) {
214
+ const patterns = config.include || ['scripts/**/*.ts'];
215
+ const files = [];
216
+ for (const pattern of patterns) {
217
+ const matches = await glob(pattern, {
218
+ cwd: process.cwd(),
219
+ ignore: config.exclude || []
220
+ });
221
+ files.push(...matches);
222
+ }
223
+ const libDirs = config.lib || ['scripts/lib'];
224
+ const scriptFiles = files.filter(file => {
225
+ return !libDirs.some(lib => file.startsWith(lib + '/') || file.startsWith(lib + '\\'));
226
+ });
227
+ await deployFiles(scriptFiles, config, options);
228
+ }
229
+ // Watch for changes
230
+ const patterns = config.include || ['scripts/**/*.ts'];
231
+ const libDirs = config.lib || ['scripts/lib'];
232
+ const watcher = chokidar.watch(patterns, {
233
+ cwd: process.cwd(),
234
+ ignoreInitial: true,
235
+ ignored: config.exclude
236
+ });
237
+ // Watch sounds folder for type regeneration and deployment
238
+ const soundsWatcher = chokidar.watch('sounds/**/*.ogg', {
239
+ cwd: process.cwd(),
240
+ ignoreInitial: true
241
+ });
242
+ soundsWatcher.on('add', async (file) => {
243
+ console.log(`\nSound added: ${file}`);
244
+ console.log('Regenerating types...');
245
+ await prepare(process.cwd());
246
+ if (options.deploy && config.deploy?.target) {
247
+ await deploySingleAsset(file, config);
248
+ console.log(` Deployed: ${file}`);
249
+ }
250
+ });
251
+ soundsWatcher.on('change', async (file) => {
252
+ console.log(`\nSound changed: ${file}`);
253
+ if (options.deploy && config.deploy?.target) {
254
+ await deploySingleAsset(file, config);
255
+ console.log(` Deployed: ${file}`);
256
+ }
257
+ });
258
+ soundsWatcher.on('unlink', async (file) => {
259
+ console.log(`\nSound removed: ${file}`);
260
+ console.log('Regenerating types...');
261
+ await prepare(process.cwd());
262
+ });
263
+ // Watch other asset folders (textures, models, gui)
264
+ const assetPatterns = [
265
+ 'textures/**/*',
266
+ 'models/**/*',
267
+ 'gui/**/*',
268
+ 'items/**/*',
269
+ 'blocks/**/*'
270
+ ];
271
+ const assetsWatcher = chokidar.watch(assetPatterns, {
272
+ cwd: process.cwd(),
273
+ ignoreInitial: true
274
+ });
275
+ assetsWatcher.on('add', async (file) => {
276
+ if (options.deploy && config.deploy?.target) {
277
+ console.log(`\nAsset added: ${file}`);
278
+ await deploySingleAsset(file, config);
279
+ console.log(` Deployed: ${file}`);
280
+ }
281
+ });
282
+ assetsWatcher.on('change', async (file) => {
283
+ if (options.deploy && config.deploy?.target) {
284
+ console.log(`\nAsset changed: ${file}`);
285
+ await deploySingleAsset(file, config);
286
+ console.log(` Deployed: ${file}`);
287
+ }
288
+ });
289
+ const rebuild = async (changedFile) => {
290
+ const isLibFile = libDirs.some(lib => changedFile.startsWith(lib + '/') || changedFile.startsWith(lib + '\\'));
291
+ if (isLibFile) {
292
+ // Rebuild all files if a lib file changed
293
+ console.log(`\nLib file changed: ${changedFile}`);
294
+ console.log('Rebuilding all scripts...');
295
+ await buildAll(config, { ...options, deploy: options.deploy });
296
+ }
297
+ else {
298
+ // Rebuild just the changed file
299
+ console.log(`\nFile changed: ${changedFile}`);
300
+ try {
301
+ const outputPath = await buildFile(changedFile, config, options);
302
+ console.log(` [OK] ${changedFile} -> ${path.relative(process.cwd(), outputPath)}`);
303
+ if (options.deploy && config.deploy?.target) {
304
+ await deploySingleFile(changedFile, config);
305
+ }
306
+ }
307
+ catch (error) {
308
+ console.error(` [ERROR] ${changedFile}: ${error instanceof Error ? error.message : error}`);
309
+ }
310
+ }
311
+ };
312
+ watcher.on('change', rebuild);
313
+ watcher.on('add', rebuild);
314
+ watcher.on('unlink', (file) => {
315
+ console.log(`\nFile deleted: ${file}`);
316
+ const outputPath = getOutputPath(file, config);
317
+ if (fs.existsSync(outputPath)) {
318
+ fs.unlinkSync(outputPath);
319
+ console.log(` Deleted: ${path.relative(process.cwd(), outputPath)}`);
320
+ }
321
+ });
322
+ console.log('\nWatching for changes... (Ctrl+C to stop)');
323
+ // Keep process alive
324
+ process.on('SIGINT', () => {
325
+ console.log('\nStopping watch mode...');
326
+ watcher.close();
327
+ soundsWatcher.close();
328
+ assetsWatcher.close();
329
+ process.exit(0);
330
+ });
331
+ }
332
+ async function deployFiles(sourceFiles, config, options = {}) {
333
+ if (!config.deploy?.target)
334
+ return;
335
+ const targetDir = path.resolve(process.cwd(), config.deploy.target);
336
+ console.log(`\nDeploying to ${targetDir}...`);
337
+ // Ensure target directory exists
338
+ if (!fs.existsSync(targetDir)) {
339
+ fs.mkdirSync(targetDir, { recursive: true });
340
+ }
341
+ // Deploy manifest
342
+ const outDir = getOutDir(config);
343
+ const manifestSource = path.resolve(outDir, 'kustompack.json');
344
+ const manifestTarget = path.join(targetDir, 'kustompack.json');
345
+ if (fs.existsSync(manifestSource)) {
346
+ fs.copyFileSync(manifestSource, manifestTarget);
347
+ console.log(` Deployed kustompack.json`);
348
+ }
349
+ // Deploy compiled scripts
350
+ let deployedScripts = 0;
351
+ for (const sourceFile of sourceFiles) {
352
+ await deploySingleFile(sourceFile, config);
353
+ deployedScripts++;
354
+ }
355
+ console.log(` Deployed ${deployedScripts} script(s)`);
356
+ // Deploy asset folders (sounds, textures, models, gui, etc.)
357
+ const deployedAssets = await deployAssets(config, options);
358
+ if (deployedAssets > 0) {
359
+ console.log(` Deployed ${deployedAssets} asset folder(s)`);
360
+ }
361
+ }
362
+ /**
363
+ * Deploy a single file from outDir to deploy target
364
+ */
365
+ async function deploySingleFile(sourceFile, config) {
366
+ if (!config.deploy?.target)
367
+ return;
368
+ // Source is in outDir
369
+ const outputPath = getOutputPath(sourceFile, config);
370
+ // Target preserves relative path structure
371
+ const relativePath = sourceFile.replace(/\.ts$/, '.js');
372
+ const targetDir = path.resolve(process.cwd(), config.deploy.target);
373
+ const targetPath = path.join(targetDir, relativePath);
374
+ // Ensure target subdirectory exists
375
+ const targetSubDir = path.dirname(targetPath);
376
+ if (!fs.existsSync(targetSubDir)) {
377
+ fs.mkdirSync(targetSubDir, { recursive: true });
378
+ }
379
+ // Copy file from outDir to deploy target
380
+ if (fs.existsSync(outputPath)) {
381
+ fs.copyFileSync(outputPath, targetPath);
382
+ }
383
+ }
384
+ /**
385
+ * Asset folders that should be deployed alongside scripts
386
+ */
387
+ const ASSET_FOLDERS = ['sounds', 'textures', 'models', 'gui', 'items', 'blocks'];
388
+ /**
389
+ * Copy a folder recursively
390
+ */
391
+ function copyFolderRecursive(source, target) {
392
+ // Ensure target directory exists
393
+ if (!fs.existsSync(target)) {
394
+ fs.mkdirSync(target, { recursive: true });
395
+ }
396
+ const entries = fs.readdirSync(source, { withFileTypes: true });
397
+ for (const entry of entries) {
398
+ const sourcePath = path.join(source, entry.name);
399
+ const targetPath = path.join(target, entry.name);
400
+ if (entry.isDirectory()) {
401
+ copyFolderRecursive(sourcePath, targetPath);
402
+ }
403
+ else {
404
+ fs.copyFileSync(sourcePath, targetPath);
405
+ }
406
+ }
407
+ }
408
+ /**
409
+ * Deploy asset folders (sounds, textures, models, gui, etc.) to deploy target
410
+ */
411
+ async function deployAssets(config, options) {
412
+ if (!config.deploy?.target)
413
+ return 0;
414
+ const targetDir = path.resolve(process.cwd(), config.deploy.target);
415
+ let deployedCount = 0;
416
+ for (const folder of ASSET_FOLDERS) {
417
+ const sourceDir = path.resolve(process.cwd(), folder);
418
+ if (fs.existsSync(sourceDir)) {
419
+ const targetSubDir = path.join(targetDir, folder);
420
+ copyFolderRecursive(sourceDir, targetSubDir);
421
+ deployedCount++;
422
+ if (options.verbose) {
423
+ console.log(` Deployed ${folder}/`);
424
+ }
425
+ }
426
+ }
427
+ return deployedCount;
428
+ }
429
+ /**
430
+ * Deploy a single asset file to deploy target
431
+ */
432
+ async function deploySingleAsset(assetFile, config) {
433
+ if (!config.deploy?.target)
434
+ return;
435
+ const sourcePath = path.resolve(process.cwd(), assetFile);
436
+ const targetDir = path.resolve(process.cwd(), config.deploy.target);
437
+ const targetPath = path.join(targetDir, assetFile);
438
+ // Ensure target subdirectory exists
439
+ const targetSubDir = path.dirname(targetPath);
440
+ if (!fs.existsSync(targetSubDir)) {
441
+ fs.mkdirSync(targetSubDir, { recursive: true });
442
+ }
443
+ // Copy file
444
+ if (fs.existsSync(sourcePath)) {
445
+ fs.copyFileSync(sourcePath, targetPath);
446
+ }
447
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const bundleCommand: Command;
@@ -0,0 +1,134 @@
1
+ import { Command } from 'commander';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { glob } from 'glob';
5
+ import archiver from 'archiver';
6
+ import chalk from 'chalk';
7
+ import { loadConfig, generateManifest, validateManifest } from '../config.js';
8
+ export const bundleCommand = new Command('bundle')
9
+ .description('Create a distributable zip file with manifest')
10
+ .option('-o, --output <file>', 'Output file name')
11
+ .option('--include-source', 'Include TypeScript source files')
12
+ .option('--no-manifest', 'Skip manifest generation (use existing kustompack.json)')
13
+ .action(async (options) => {
14
+ const config = loadConfig(process.cwd());
15
+ // Validate manifest configuration
16
+ const errors = validateManifest(config);
17
+ if (errors.length > 0) {
18
+ console.error(chalk.red('Manifest validation failed:'));
19
+ for (const error of errors) {
20
+ console.error(chalk.red(` - ${error}`));
21
+ }
22
+ process.exit(1);
23
+ }
24
+ const packId = config.manifest?.id || 'kustompack';
25
+ const outputFile = options.output || config.bundle?.output || `dist/${packId}.zip`;
26
+ const outputPath = path.resolve(process.cwd(), outputFile);
27
+ console.log(chalk.blue(`Creating bundle for pack: ${chalk.bold(packId)}`));
28
+ console.log(` Version: ${config.manifest?.version || '1.0.0'}`);
29
+ console.log(` Output: ${outputFile}`);
30
+ // Ensure output directory exists
31
+ const outputDir = path.dirname(outputPath);
32
+ if (!fs.existsSync(outputDir)) {
33
+ fs.mkdirSync(outputDir, { recursive: true });
34
+ }
35
+ // Generate manifest if enabled
36
+ let manifestContent = null;
37
+ if (options.manifest !== false) {
38
+ try {
39
+ manifestContent = generateManifest(config);
40
+ console.log(chalk.green(' Generated kustompack.json'));
41
+ }
42
+ catch (error) {
43
+ console.error(chalk.red(`Failed to generate manifest: ${error}`));
44
+ process.exit(1);
45
+ }
46
+ }
47
+ // Create zip archive
48
+ const output = fs.createWriteStream(outputPath);
49
+ const archive = archiver('zip', { zlib: { level: 9 } });
50
+ let fileCount = 0;
51
+ output.on('close', () => {
52
+ const sizeKB = (archive.pointer() / 1024).toFixed(2);
53
+ console.log(chalk.green(`\nBundle created successfully!`));
54
+ console.log(` File: ${outputFile}`);
55
+ console.log(` Size: ${sizeKB} KB`);
56
+ console.log(` Files: ${fileCount}`);
57
+ });
58
+ archive.on('warning', (err) => {
59
+ if (err.code === 'ENOENT') {
60
+ console.warn(chalk.yellow('Warning:'), err.message);
61
+ }
62
+ else {
63
+ throw err;
64
+ }
65
+ });
66
+ archive.on('error', (err) => {
67
+ throw err;
68
+ });
69
+ archive.pipe(output);
70
+ // Add manifest first
71
+ if (manifestContent) {
72
+ archive.append(manifestContent, { name: 'kustompack.json' });
73
+ fileCount++;
74
+ }
75
+ else {
76
+ // Check for existing manifest
77
+ const existingManifest = path.resolve(process.cwd(), 'kustompack.json');
78
+ if (fs.existsSync(existingManifest)) {
79
+ archive.file(existingManifest, { name: 'kustompack.json' });
80
+ fileCount++;
81
+ }
82
+ else {
83
+ console.warn(chalk.yellow('Warning: No kustompack.json found. Pack may not load correctly.'));
84
+ }
85
+ }
86
+ // Add files based on bundle config
87
+ const includePatterns = config.bundle?.include || [
88
+ '**/*.js',
89
+ 'textures/**/*',
90
+ 'gui/**/*',
91
+ 'models/**/*',
92
+ 'sounds/**/*'
93
+ ];
94
+ const addedFiles = new Set();
95
+ for (const pattern of includePatterns) {
96
+ const files = await glob(pattern, {
97
+ cwd: process.cwd(),
98
+ nodir: true,
99
+ ignore: ['node_modules/**', 'dist/**', '*.ts', 'kustompack.json']
100
+ });
101
+ for (const file of files) {
102
+ if (addedFiles.has(file))
103
+ continue;
104
+ const filePath = path.resolve(process.cwd(), file);
105
+ if (fs.existsSync(filePath)) {
106
+ archive.file(filePath, { name: file });
107
+ addedFiles.add(file);
108
+ fileCount++;
109
+ }
110
+ }
111
+ }
112
+ // Optionally include source files
113
+ if (options.includeSource) {
114
+ const sourcePatterns = config.include || ['scripts/**/*.ts', 'blocks/**/*.ts', 'items/**/*.ts'];
115
+ for (const pattern of sourcePatterns) {
116
+ const files = await glob(pattern, {
117
+ cwd: process.cwd(),
118
+ nodir: true,
119
+ ignore: config.exclude
120
+ });
121
+ for (const file of files) {
122
+ if (addedFiles.has(file))
123
+ continue;
124
+ const filePath = path.resolve(process.cwd(), file);
125
+ if (fs.existsSync(filePath)) {
126
+ archive.file(filePath, { name: file });
127
+ addedFiles.add(file);
128
+ fileCount++;
129
+ }
130
+ }
131
+ }
132
+ }
133
+ await archive.finalize();
134
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const initCommand: Command;