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.
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Options for creating a bundle.
3
+ */
4
+ export interface BundleOptions {
5
+ /** Whether to generate and include the manifest (default: true) */
6
+ includeManifest?: boolean;
7
+ /** Whether to include TypeScript source files (default: false) */
8
+ includeSource?: boolean;
9
+ }
10
+ /**
11
+ * Result of a bundle operation.
12
+ */
13
+ export interface BundleResult {
14
+ packId: string;
15
+ version: string;
16
+ fileCount: number;
17
+ }
18
+ /**
19
+ * Result of bundling to a buffer.
20
+ */
21
+ export interface BufferBundleResult extends BundleResult {
22
+ buffer: Buffer;
23
+ }
24
+ /**
25
+ * Create a zip bundle in memory and return as a Buffer.
26
+ * Used by the push command for HTTP upload.
27
+ */
28
+ export declare function bundleToBuffer(projectDir: string, options?: BundleOptions): Promise<BufferBundleResult>;
29
+ /**
30
+ * Create a zip bundle and write it to a file on disk.
31
+ * Used by the bundle command for distribution.
32
+ */
33
+ export declare function bundleToFile(projectDir: string, outputPath: string, options?: BundleOptions): Promise<BundleResult>;
@@ -0,0 +1,195 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { glob } from 'glob';
4
+ import archiver from 'archiver';
5
+ import { loadConfig, generateManifest, validateManifest } from './config.js';
6
+ /**
7
+ * Collect all files that should be included in the bundle.
8
+ * Returns a list of { absolutePath, archiveName } entries.
9
+ */
10
+ async function collectBundleFiles(projectDir, config) {
11
+ const includePatterns = config.bundle?.include || [
12
+ '**/*.js',
13
+ 'textures/**/*',
14
+ 'gui/**/*',
15
+ 'models/**/*',
16
+ 'sounds/**/*'
17
+ ];
18
+ const outDir = config.outDir || 'dist';
19
+ const outDirResolved = path.resolve(projectDir, outDir);
20
+ const addedFiles = new Set();
21
+ const files = [];
22
+ for (const pattern of includePatterns) {
23
+ const isJsPattern = pattern.includes('*.js');
24
+ if (isJsPattern) {
25
+ // JS files live in outDir (e.g. dist/)
26
+ // Exclude types/ directory (generated .d.ts files) and zip files
27
+ const matches = await glob(pattern, {
28
+ cwd: outDirResolved,
29
+ nodir: true,
30
+ ignore: ['types/**', '*.zip', 'kustompack.json']
31
+ });
32
+ for (const file of matches) {
33
+ if (addedFiles.has(file))
34
+ continue;
35
+ const absolutePath = path.resolve(outDirResolved, file);
36
+ if (fs.existsSync(absolutePath)) {
37
+ files.push({ absolutePath, archiveName: file });
38
+ addedFiles.add(file);
39
+ }
40
+ }
41
+ }
42
+ else {
43
+ // Asset files live in projectDir (source directory)
44
+ const matches = await glob(pattern, {
45
+ cwd: projectDir,
46
+ nodir: true,
47
+ ignore: ['node_modules/**', 'dist/**', '*.ts', 'kustompack.json']
48
+ });
49
+ for (const file of matches) {
50
+ if (addedFiles.has(file))
51
+ continue;
52
+ const absolutePath = path.resolve(projectDir, file);
53
+ if (fs.existsSync(absolutePath)) {
54
+ files.push({ absolutePath, archiveName: file });
55
+ addedFiles.add(file);
56
+ }
57
+ }
58
+ }
59
+ }
60
+ return files;
61
+ }
62
+ /**
63
+ * Collect TypeScript source files for optional inclusion.
64
+ */
65
+ async function collectSourceFiles(projectDir, config) {
66
+ const sourcePatterns = config.include || ['scripts/**/*.ts', 'blocks/**/*.ts', 'items/**/*.ts'];
67
+ const addedFiles = new Set();
68
+ const files = [];
69
+ for (const pattern of sourcePatterns) {
70
+ const matches = await glob(pattern, {
71
+ cwd: projectDir,
72
+ nodir: true,
73
+ ignore: config.exclude
74
+ });
75
+ for (const file of matches) {
76
+ if (addedFiles.has(file))
77
+ continue;
78
+ const absolutePath = path.resolve(projectDir, file);
79
+ if (fs.existsSync(absolutePath)) {
80
+ files.push({ absolutePath, archiveName: file });
81
+ addedFiles.add(file);
82
+ }
83
+ }
84
+ }
85
+ return files;
86
+ }
87
+ /**
88
+ * Populate an archiver instance with all bundle files.
89
+ * Returns the file count.
90
+ */
91
+ async function populateArchive(archive, projectDir, config, options = {}) {
92
+ let fileCount = 0;
93
+ // Add manifest
94
+ if (options.includeManifest !== false) {
95
+ const manifestContent = generateManifest(config);
96
+ archive.append(manifestContent, { name: 'kustompack.json' });
97
+ fileCount++;
98
+ }
99
+ // Add compiled JS + assets
100
+ const bundleFiles = await collectBundleFiles(projectDir, config);
101
+ for (const file of bundleFiles) {
102
+ archive.file(file.absolutePath, { name: file.archiveName });
103
+ fileCount++;
104
+ }
105
+ // Optionally add source files
106
+ if (options.includeSource) {
107
+ const sourceFiles = await collectSourceFiles(projectDir, config);
108
+ for (const file of sourceFiles) {
109
+ archive.file(file.absolutePath, { name: file.archiveName });
110
+ fileCount++;
111
+ }
112
+ }
113
+ return { fileCount };
114
+ }
115
+ /**
116
+ * Validate config and return pack metadata.
117
+ * Throws on validation errors.
118
+ */
119
+ function validateConfig(config) {
120
+ const errors = validateManifest(config);
121
+ if (errors.length > 0) {
122
+ throw new Error(`Manifest validation failed:\n ${errors.join('\n ')}`);
123
+ }
124
+ return {
125
+ packId: config.manifest?.id || 'kustompack',
126
+ version: config.manifest?.version || '1.0.0'
127
+ };
128
+ }
129
+ /**
130
+ * Create a zip bundle in memory and return as a Buffer.
131
+ * Used by the push command for HTTP upload.
132
+ */
133
+ export async function bundleToBuffer(projectDir, options = {}) {
134
+ const config = loadConfig(projectDir);
135
+ const { packId, version } = validateConfig(config);
136
+ return new Promise((resolve, reject) => {
137
+ const chunks = [];
138
+ const archive = archiver('zip', { zlib: { level: 9 } });
139
+ archive.on('data', (chunk) => chunks.push(chunk));
140
+ archive.on('error', reject);
141
+ archive.on('end', () => {
142
+ // fileCount is set by the async populate below before finalize
143
+ });
144
+ const run = async () => {
145
+ const { fileCount } = await populateArchive(archive, projectDir, config, options);
146
+ // Listen for end after we know fileCount
147
+ return new Promise((res) => {
148
+ archive.on('end', () => {
149
+ res({
150
+ buffer: Buffer.concat(chunks),
151
+ packId,
152
+ version,
153
+ fileCount
154
+ });
155
+ });
156
+ archive.finalize();
157
+ });
158
+ };
159
+ run().then(resolve).catch(reject);
160
+ });
161
+ }
162
+ /**
163
+ * Create a zip bundle and write it to a file on disk.
164
+ * Used by the bundle command for distribution.
165
+ */
166
+ export async function bundleToFile(projectDir, outputPath, options = {}) {
167
+ const config = loadConfig(projectDir);
168
+ const { packId, version } = validateConfig(config);
169
+ // Ensure output directory exists
170
+ const outputDir = path.dirname(outputPath);
171
+ if (!fs.existsSync(outputDir)) {
172
+ fs.mkdirSync(outputDir, { recursive: true });
173
+ }
174
+ return new Promise((resolve, reject) => {
175
+ const output = fs.createWriteStream(outputPath);
176
+ const archive = archiver('zip', { zlib: { level: 9 } });
177
+ archive.on('warning', (err) => {
178
+ if (err.code !== 'ENOENT') {
179
+ reject(err);
180
+ }
181
+ });
182
+ archive.on('error', reject);
183
+ archive.pipe(output);
184
+ const run = async () => {
185
+ const { fileCount } = await populateArchive(archive, projectDir, config, options);
186
+ return new Promise((res) => {
187
+ output.on('close', () => {
188
+ res({ packId, version, fileCount });
189
+ });
190
+ archive.finalize();
191
+ });
192
+ };
193
+ run().then(resolve).catch(reject);
194
+ });
195
+ }
@@ -1,2 +1,18 @@
1
1
  import { Command } from 'commander';
2
+ import { type KustomConfig } from '../config.js';
2
3
  export declare const buildCommand: Command;
4
+ /**
5
+ * Build all TypeScript files in the project.
6
+ * Exported for use by push and bundle commands.
7
+ */
8
+ export declare function buildAll(config: KustomConfig, options: {
9
+ validate?: boolean;
10
+ verbose?: boolean;
11
+ }): Promise<void>;
12
+ /**
13
+ * Build a single TypeScript file to Rhino-compatible JavaScript.
14
+ * Exported for use by push --watch for incremental rebuilds.
15
+ */
16
+ export declare function buildFile(file: string, config: KustomConfig, options: {
17
+ verbose?: boolean;
18
+ }): Promise<string>;
@@ -11,7 +11,6 @@ import { prepare } from './prepare.js';
11
11
  export const buildCommand = new Command('build')
12
12
  .description('Compile TypeScript scripts to Rhino-compatible JavaScript')
13
13
  .option('-w, --watch', 'Watch for changes and rebuild')
14
- .option('-d, --deploy', 'Copy compiled files to deploy target')
15
14
  .option('--no-validate', 'Skip validation')
16
15
  .option('-v, --verbose', 'Verbose output')
17
16
  .option('--clean', 'Clean output directory before building')
@@ -47,7 +46,11 @@ function getOutputPath(sourceFile, config) {
47
46
  function getOutDir(config) {
48
47
  return path.resolve(process.cwd(), config.outDir || 'dist');
49
48
  }
50
- async function buildAll(config, options) {
49
+ /**
50
+ * Build all TypeScript files in the project.
51
+ * Exported for use by push and bundle commands.
52
+ */
53
+ export async function buildAll(config, options) {
51
54
  // Validate manifest configuration
52
55
  const manifestErrors = validateManifest(config);
53
56
  if (manifestErrors.length > 0) {
@@ -115,9 +118,6 @@ async function buildAll(config, options) {
115
118
  }
116
119
  const elapsed = Date.now() - startTime;
117
120
  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
121
  }
122
122
  /**
123
123
  * esbuild plugin to mark relative script imports and cross-pack imports as external.
@@ -173,7 +173,11 @@ function externalScriptImportsPlugin(config) {
173
173
  }
174
174
  };
175
175
  }
176
- async function buildFile(file, config, options) {
176
+ /**
177
+ * Build a single TypeScript file to Rhino-compatible JavaScript.
178
+ * Exported for use by push --watch for incremental rebuilds.
179
+ */
180
+ export async function buildFile(file, config, options) {
177
181
  const inputPath = path.resolve(process.cwd(), file);
178
182
  const outputPath = getOutputPath(file, config);
179
183
  // Ensure output directory exists
@@ -207,25 +211,8 @@ async function buildFile(file, config, options) {
207
211
  }
208
212
  async function watchMode(config, options) {
209
213
  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
- }
214
+ // Initial build
215
+ await buildAll(config, options);
229
216
  // Watch for changes
230
217
  const patterns = config.include || ['scripts/**/*.ts'];
231
218
  const libDirs = config.lib || ['scripts/lib'];
@@ -234,7 +221,7 @@ async function watchMode(config, options) {
234
221
  ignoreInitial: true,
235
222
  ignored: config.exclude
236
223
  });
237
- // Watch sounds folder for type regeneration and deployment
224
+ // Watch sounds folder for type regeneration
238
225
  const soundsWatcher = chokidar.watch('sounds/**/*.ogg', {
239
226
  cwd: process.cwd(),
240
227
  ignoreInitial: true
@@ -243,56 +230,19 @@ async function watchMode(config, options) {
243
230
  console.log(`\nSound added: ${file}`);
244
231
  console.log('Regenerating types...');
245
232
  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
233
  });
258
234
  soundsWatcher.on('unlink', async (file) => {
259
235
  console.log(`\nSound removed: ${file}`);
260
236
  console.log('Regenerating types...');
261
237
  await prepare(process.cwd());
262
238
  });
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
239
  const rebuild = async (changedFile) => {
290
240
  const isLibFile = libDirs.some(lib => changedFile.startsWith(lib + '/') || changedFile.startsWith(lib + '\\'));
291
241
  if (isLibFile) {
292
242
  // Rebuild all files if a lib file changed
293
243
  console.log(`\nLib file changed: ${changedFile}`);
294
244
  console.log('Rebuilding all scripts...');
295
- await buildAll(config, { ...options, deploy: options.deploy });
245
+ await buildAll(config, options);
296
246
  }
297
247
  else {
298
248
  // Rebuild just the changed file
@@ -300,9 +250,6 @@ async function watchMode(config, options) {
300
250
  try {
301
251
  const outputPath = await buildFile(changedFile, config, options);
302
252
  console.log(` [OK] ${changedFile} -> ${path.relative(process.cwd(), outputPath)}`);
303
- if (options.deploy && config.deploy?.target) {
304
- await deploySingleFile(changedFile, config);
305
- }
306
253
  }
307
254
  catch (error) {
308
255
  console.error(` [ERROR] ${changedFile}: ${error instanceof Error ? error.message : error}`);
@@ -325,123 +272,6 @@ async function watchMode(config, options) {
325
272
  console.log('\nStopping watch mode...');
326
273
  watcher.close();
327
274
  soundsWatcher.close();
328
- assetsWatcher.close();
329
275
  process.exit(0);
330
276
  });
331
277
  }
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
- }
@@ -1,17 +1,17 @@
1
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
2
  import chalk from 'chalk';
7
- import { loadConfig, generateManifest, validateManifest } from '../config.js';
3
+ import { loadConfig, validateManifest } from '../config.js';
4
+ import { bundleToFile } from '../bundler.js';
5
+ import { buildAll } from './build.js';
8
6
  export const bundleCommand = new Command('bundle')
9
- .description('Create a distributable zip file with manifest')
7
+ .description('Build and create a distributable zip file')
10
8
  .option('-o, --output <file>', 'Output file name')
11
9
  .option('--include-source', 'Include TypeScript source files')
12
- .option('--no-manifest', 'Skip manifest generation (use existing kustompack.json)')
10
+ .option('--no-manifest', 'Skip manifest generation')
11
+ .option('-v, --verbose', 'Verbose output')
13
12
  .action(async (options) => {
14
- const config = loadConfig(process.cwd());
13
+ const projectDir = process.cwd();
14
+ const config = loadConfig(projectDir);
15
15
  // Validate manifest configuration
16
16
  const errors = validateManifest(config);
17
17
  if (errors.length > 0) {
@@ -23,136 +23,25 @@ export const bundleCommand = new Command('bundle')
23
23
  }
24
24
  const packId = config.manifest?.id || 'kustompack';
25
25
  const outputFile = options.output || config.bundle?.output || `dist/${packId}.zip`;
26
- const outputPath = path.resolve(process.cwd(), outputFile);
26
+ // Auto-build before bundling
27
+ console.log(chalk.blue('Building before bundle...\n'));
28
+ await buildAll(config, { verbose: options.verbose });
29
+ console.log();
30
+ // Create bundle
27
31
  console.log(chalk.blue(`Creating bundle for pack: ${chalk.bold(packId)}`));
28
32
  console.log(` Version: ${config.manifest?.version || '1.0.0'}`);
29
33
  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);
34
+ try {
35
+ const result = await bundleToFile(projectDir, outputFile, {
36
+ includeManifest: options.manifest !== false,
37
+ includeSource: options.includeSource
38
+ });
53
39
  console.log(chalk.green(`\nBundle created successfully!`));
54
40
  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
- }
41
+ console.log(` Files: ${result.fileCount}`);
85
42
  }
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 outDir = config.outDir || 'dist';
95
- const outDirResolved = path.resolve(process.cwd(), outDir);
96
- const addedFiles = new Set();
97
- for (const pattern of includePatterns) {
98
- const isJsPattern = pattern.includes('*.js');
99
- if (isJsPattern) {
100
- // JS files live in outDir (e.g. dist/)
101
- const files = await glob(pattern, {
102
- cwd: outDirResolved,
103
- nodir: true,
104
- ignore: ['node_modules/**', '*.ts', 'kustompack.json']
105
- });
106
- for (const file of files) {
107
- if (addedFiles.has(file))
108
- continue;
109
- const filePath = path.resolve(outDirResolved, file);
110
- if (fs.existsSync(filePath)) {
111
- archive.file(filePath, { name: file });
112
- addedFiles.add(file);
113
- fileCount++;
114
- }
115
- }
116
- }
117
- else {
118
- // Asset files live in cwd (source directory)
119
- const files = await glob(pattern, {
120
- cwd: process.cwd(),
121
- nodir: true,
122
- ignore: ['node_modules/**', 'dist/**', '*.ts', 'kustompack.json']
123
- });
124
- for (const file of files) {
125
- if (addedFiles.has(file))
126
- continue;
127
- const filePath = path.resolve(process.cwd(), file);
128
- if (fs.existsSync(filePath)) {
129
- archive.file(filePath, { name: file });
130
- addedFiles.add(file);
131
- fileCount++;
132
- }
133
- }
134
- }
135
- }
136
- // Optionally include source files
137
- if (options.includeSource) {
138
- const sourcePatterns = config.include || ['scripts/**/*.ts', 'blocks/**/*.ts', 'items/**/*.ts'];
139
- for (const pattern of sourcePatterns) {
140
- const files = await glob(pattern, {
141
- cwd: process.cwd(),
142
- nodir: true,
143
- ignore: config.exclude
144
- });
145
- for (const file of files) {
146
- if (addedFiles.has(file))
147
- continue;
148
- const filePath = path.resolve(process.cwd(), file);
149
- if (fs.existsSync(filePath)) {
150
- archive.file(filePath, { name: file });
151
- addedFiles.add(file);
152
- fileCount++;
153
- }
154
- }
155
- }
43
+ catch (error) {
44
+ console.error(chalk.red(`\nFailed to create bundle: ${error instanceof Error ? error.message : error}`));
45
+ process.exit(1);
156
46
  }
157
- await archive.finalize();
158
47
  });