elit 3.0.1 → 3.0.2

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 (87) hide show
  1. package/dist/build.d.ts +4 -12
  2. package/dist/build.d.ts.map +1 -0
  3. package/dist/chokidar.d.ts +7 -9
  4. package/dist/chokidar.d.ts.map +1 -0
  5. package/dist/cli.d.ts +6 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +17 -4
  8. package/dist/config.d.ts +29 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/dom.d.ts +7 -14
  11. package/dist/dom.d.ts.map +1 -0
  12. package/dist/el.d.ts +19 -191
  13. package/dist/el.d.ts.map +1 -0
  14. package/dist/fs.d.ts +35 -35
  15. package/dist/fs.d.ts.map +1 -0
  16. package/dist/hmr.d.ts +3 -3
  17. package/dist/hmr.d.ts.map +1 -0
  18. package/dist/http.d.ts +20 -22
  19. package/dist/http.d.ts.map +1 -0
  20. package/dist/https.d.ts +12 -15
  21. package/dist/https.d.ts.map +1 -0
  22. package/dist/index.d.ts +10 -629
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/mime-types.d.ts +9 -9
  25. package/dist/mime-types.d.ts.map +1 -0
  26. package/dist/path.d.ts +22 -19
  27. package/dist/path.d.ts.map +1 -0
  28. package/dist/router.d.ts +10 -17
  29. package/dist/router.d.ts.map +1 -0
  30. package/dist/runtime.d.ts +5 -6
  31. package/dist/runtime.d.ts.map +1 -0
  32. package/dist/server.d.ts +105 -7
  33. package/dist/server.d.ts.map +1 -0
  34. package/dist/server.js +14 -2
  35. package/dist/server.mjs +14 -2
  36. package/dist/state.d.ts +21 -27
  37. package/dist/state.d.ts.map +1 -0
  38. package/dist/style.d.ts +14 -55
  39. package/dist/style.d.ts.map +1 -0
  40. package/dist/types.d.ts +26 -240
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/ws.d.ts +14 -17
  43. package/dist/ws.d.ts.map +1 -0
  44. package/dist/wss.d.ts +16 -16
  45. package/dist/wss.d.ts.map +1 -0
  46. package/package.json +3 -2
  47. package/src/build.ts +337 -0
  48. package/src/chokidar.ts +401 -0
  49. package/src/cli.ts +638 -0
  50. package/src/config.ts +205 -0
  51. package/src/dom.ts +817 -0
  52. package/src/el.ts +164 -0
  53. package/src/fs.ts +727 -0
  54. package/src/hmr.ts +137 -0
  55. package/src/http.ts +775 -0
  56. package/src/https.ts +411 -0
  57. package/src/index.ts +14 -0
  58. package/src/mime-types.ts +222 -0
  59. package/src/path.ts +493 -0
  60. package/src/router.ts +237 -0
  61. package/src/runtime.ts +97 -0
  62. package/src/server.ts +1290 -0
  63. package/src/state.ts +468 -0
  64. package/src/style.ts +524 -0
  65. package/{dist/types-Du6kfwTm.d.ts → src/types.ts} +58 -141
  66. package/src/ws.ts +506 -0
  67. package/src/wss.ts +241 -0
  68. package/dist/build.d.mts +0 -20
  69. package/dist/chokidar.d.mts +0 -134
  70. package/dist/dom.d.mts +0 -87
  71. package/dist/el.d.mts +0 -207
  72. package/dist/fs.d.mts +0 -255
  73. package/dist/hmr.d.mts +0 -38
  74. package/dist/http.d.mts +0 -163
  75. package/dist/https.d.mts +0 -108
  76. package/dist/index.d.mts +0 -629
  77. package/dist/mime-types.d.mts +0 -48
  78. package/dist/path.d.mts +0 -163
  79. package/dist/router.d.mts +0 -47
  80. package/dist/runtime.d.mts +0 -97
  81. package/dist/server.d.mts +0 -7
  82. package/dist/state.d.mts +0 -111
  83. package/dist/style.d.mts +0 -159
  84. package/dist/types-C0nGi6MX.d.mts +0 -346
  85. package/dist/types.d.mts +0 -452
  86. package/dist/ws.d.mts +0 -195
  87. package/dist/wss.d.mts +0 -108
package/dist/wss.d.ts CHANGED
@@ -1,9 +1,3 @@
1
- import { EventEmitter } from 'events';
2
- import { IncomingMessage } from './http.js';
3
- import { WebSocket, ServerOptions, ReadyState } from './ws.js';
4
- export { CLOSE_CODES, Data } from './ws.js';
5
- import 'node:events';
6
-
7
1
  /**
8
2
  * WebSocket Secure (WSS) module with unified API across runtimes
9
3
  * Pure implementation without external dependencies
@@ -12,11 +6,13 @@ import 'node:events';
12
6
  * - Bun: uses native WebSocket with TLS
13
7
  * - Deno: uses native WebSocket with TLS
14
8
  */
15
-
9
+ import { EventEmitter } from 'events';
10
+ import type { IncomingMessage } from './http';
11
+ import { WebSocket, ServerOptions, Data, ReadyState, CLOSE_CODES } from './ws';
16
12
  /**
17
13
  * WSS Server options (extends WebSocket ServerOptions with TLS)
18
14
  */
19
- interface WSSServerOptions extends ServerOptions {
15
+ export interface WSSServerOptions extends ServerOptions {
20
16
  key?: string | Buffer;
21
17
  cert?: string | Buffer;
22
18
  ca?: string | Buffer;
@@ -28,13 +24,13 @@ interface WSSServerOptions extends ServerOptions {
28
24
  /**
29
25
  * WebSocket Secure client class
30
26
  */
31
- declare class WSSClient extends WebSocket {
27
+ export declare class WSSClient extends WebSocket {
32
28
  constructor(address: string | URL, protocols?: string | string[], options?: any);
33
29
  }
34
30
  /**
35
31
  * WebSocket Secure Server class
36
32
  */
37
- declare class WSSServer extends EventEmitter {
33
+ export declare class WSSServer extends EventEmitter {
38
34
  clients: Set<WebSocket>;
39
35
  options: WSSServerOptions;
40
36
  path: string;
@@ -66,16 +62,20 @@ declare class WSSServer extends EventEmitter {
66
62
  /**
67
63
  * Create WebSocket Secure client
68
64
  */
69
- declare function createWSSClient(address: string | URL, protocols?: string | string[], options?: any): WSSClient;
65
+ export declare function createWSSClient(address: string | URL, protocols?: string | string[], options?: any): WSSClient;
70
66
  /**
71
67
  * Create WebSocket Secure server
72
68
  */
73
- declare function createWSSServer(options?: WSSServerOptions, callback?: () => void): WSSServer;
69
+ export declare function createWSSServer(options?: WSSServerOptions, callback?: () => void): WSSServer;
74
70
  /**
75
71
  * Get current runtime
76
72
  */
77
- declare function getRuntime(): 'node' | 'bun' | 'deno';
78
-
73
+ export declare function getRuntime(): 'node' | 'bun' | 'deno';
74
+ /**
75
+ * Re-export types and constants from ws module
76
+ */
77
+ export type { ServerOptions, Data };
78
+ export { ReadyState, CLOSE_CODES };
79
79
  /**
80
80
  * Default export
81
81
  */
@@ -104,5 +104,5 @@ declare const _default: {
104
104
  };
105
105
  getRuntime: typeof getRuntime;
106
106
  };
107
-
108
- export { ReadyState, ServerOptions, WSSClient, WSSServer, type WSSServerOptions, createWSSClient, createWSSServer, _default as default, getRuntime };
107
+ export default _default;
108
+ //# sourceMappingURL=wss.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wss.d.ts","sourceRoot":"","sources":["../src/wss.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,SAAS,EAAmB,aAAa,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAkChG;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IAErD,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IAGtB,WAAW,CAAC,EAAE,GAAG,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,SAAU,SAAQ,SAAS;gBAC1B,OAAO,EAAE,MAAM,GAAG,GAAG,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,GAAG;CAOhF;AAED;;GAEG;AACH,qBAAa,SAAU,SAAQ,YAAY;IAClC,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,CAAa;IACpC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IAEpB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,SAAS,CAAmB;gBAExB,OAAO,CAAC,EAAE,gBAAgB,EAAE,QAAQ,CAAC,EAAE,MAAM,IAAI;IA8B7D,OAAO,CAAC,YAAY;IA4BpB;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;IAM/G;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO;IAU/C;;OAEG;IACH,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAmB7C;;OAEG;IACH,OAAO,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;CASpE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,SAAS,CAE9G;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,EAAE,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,SAAS,CAE5F;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,GAAG,KAAK,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,YAAY,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AAEnC;;GAEG;;;;;;;;;;;;;;;;;;;;;;;;;;AACH,wBAQE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elit",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "Optimized lightweight library for creating DOM elements with reactive state",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -101,7 +101,7 @@
101
101
  }
102
102
  },
103
103
  "scripts": {
104
- "build": "tsup",
104
+ "build": "tsup && tsc --emitDeclarationOnly",
105
105
  "dev": "tsup --watch",
106
106
  "typecheck": "tsc --noEmit",
107
107
  "benchmark": "node benchmark/server-benchmark.js",
@@ -162,6 +162,7 @@
162
162
  },
163
163
  "files": [
164
164
  "dist",
165
+ "src",
165
166
  "README.md",
166
167
  "LICENSE"
167
168
  ]
package/src/build.ts ADDED
@@ -0,0 +1,337 @@
1
+ /**
2
+ * Build module for bundling applications
3
+ * Pure implementation with cross-runtime support
4
+ * Compatible with standard build tools API
5
+ * - Node.js: uses esbuild
6
+ * - Bun: uses Bun.build
7
+ * - Deno: uses Deno.emit
8
+ */
9
+
10
+ import { statSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, existsSync } from './fs';
11
+ import { resolve, join, basename, extname, dirname } from './path';
12
+ import { runtime } from './runtime';
13
+ import type { BuildOptions, BuildResult } from './types';
14
+
15
+ /**
16
+ * Helper: Ensure directory exists (eliminates duplication in directory creation)
17
+ */
18
+ function ensureDir(dirPath: string): void {
19
+ try {
20
+ mkdirSync(dirPath, { recursive: true });
21
+ } catch (error) {
22
+ // Directory might already exist
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Helper: Calculate build time and size (eliminates duplication in build result)
28
+ */
29
+ function calculateBuildMetrics(startTime: number, outputPath: string): { buildTime: number; size: number } {
30
+ const buildTime = Date.now() - startTime;
31
+ const stats = statSync(outputPath);
32
+ return { buildTime, size: stats.size };
33
+ }
34
+
35
+ /**
36
+ * Helper: Read file and ensure string output (eliminates duplication in file reading)
37
+ */
38
+ function readFileAsString(filePath: string): string {
39
+ const contentBuffer = readFileSync(filePath, 'utf-8');
40
+ return typeof contentBuffer === 'string' ? contentBuffer : contentBuffer.toString('utf-8');
41
+ }
42
+
43
+ /**
44
+ * Helper: Get esbuild minify options (eliminates duplication in esbuild config)
45
+ */
46
+ function getMinifyOptions(minify?: boolean): object {
47
+ return minify ? {
48
+ minifyWhitespace: true,
49
+ minifyIdentifiers: true,
50
+ minifySyntax: true,
51
+ legalComments: 'none',
52
+ mangleProps: /^_/,
53
+ keepNames: false
54
+ } : {};
55
+ }
56
+
57
+ /**
58
+ * Helper: Log build info (eliminates duplication in logging)
59
+ */
60
+ function logBuildInfo(config: BuildOptions, outputPath: string): void {
61
+ console.log('\nšŸ”Ø Building...');
62
+ console.log(` Entry: ${config.entry}`);
63
+ console.log(` Output: ${outputPath}`);
64
+ console.log(` Format: ${config.format}`);
65
+ console.log(` Target: ${config.target}`);
66
+ }
67
+
68
+ /**
69
+ * Helper: Log build success (eliminates duplication in success logging)
70
+ */
71
+ function logBuildSuccess(buildTime: number, size: number): void {
72
+ console.log(`\nāœ… Build successful!`);
73
+ console.log(` Time: ${buildTime}ms`);
74
+ console.log(` Size: ${formatBytes(size)}`);
75
+ }
76
+
77
+ const defaultOptions: Omit<BuildOptions, 'entry'> = {
78
+ outDir: 'dist',
79
+ minify: true,
80
+ sourcemap: false,
81
+ target: 'es2020',
82
+ format: 'esm',
83
+ treeshake: true,
84
+ logging: true,
85
+ external: []
86
+ };
87
+
88
+ export async function build(options: BuildOptions): Promise<BuildResult> {
89
+ const config = { ...defaultOptions, ...options };
90
+ const startTime = Date.now();
91
+
92
+ if (!config.entry) {
93
+ throw new Error('Entry file is required');
94
+ }
95
+
96
+ const entryPath = resolve(config.entry);
97
+ const outDir = resolve(config.outDir!);
98
+
99
+ // Determine output filename
100
+ let outFile = config.outFile;
101
+ if (!outFile) {
102
+ const baseName = basename(config.entry, extname(config.entry));
103
+ const ext = config.format === 'cjs' ? '.cjs' : '.js';
104
+ outFile = baseName + ext;
105
+ }
106
+
107
+ const outputPath = join(outDir, outFile);
108
+
109
+ // Ensure output directory exists
110
+ ensureDir(outDir);
111
+
112
+ if (config.logging) {
113
+ logBuildInfo(config, outputPath);
114
+ }
115
+
116
+ // Browser-only plugin for automatic client/server separation
117
+ const browserOnlyPlugin = {
118
+ name: 'browser-only',
119
+ setup(build: any) {
120
+ // External all Node.js built-in modules
121
+ build.onResolve({ filter: /^(node:.*|fs|path|http|https|url|os|child_process|net|tls|crypto|stream|util|events|buffer|zlib|readline|process|assert|constants|dns|domain|punycode|querystring|repl|string_decoder|sys|timers|tty|v8|vm)$/ }, () => {
122
+ return { path: 'node-builtin', external: true, sideEffects: false };
123
+ });
124
+
125
+ // External common server-side dependencies
126
+ build.onResolve({ filter: /^(chokidar|esbuild|mime-types|open|ws|fs\/promises)$/ }, () => {
127
+ return { path: 'server-dep', external: true, sideEffects: false };
128
+ });
129
+
130
+ // Intercept imports from elit server-side modules
131
+ build.onLoad({ filter: /[\\/](server|config|cli)\.ts$/ }, () => {
132
+ return {
133
+ contents: 'export {}',
134
+ loader: 'js',
135
+ };
136
+ });
137
+ },
138
+ };
139
+
140
+ try {
141
+ const platform = config.platform || (config.format === 'cjs' ? 'node' : 'browser');
142
+ const plugins = platform === 'browser' ? [browserOnlyPlugin] : [];
143
+
144
+ // Prepare environment variables for injection
145
+ const define: Record<string, string> = {};
146
+ if (config.env) {
147
+ Object.entries(config.env).forEach(([key, value]) => {
148
+ // Only inject variables prefixed with VITE_ to client code
149
+ if (key.startsWith('VITE_')) {
150
+ define[`import.meta.env.${key}`] = JSON.stringify(value);
151
+ }
152
+ });
153
+ // Add MODE
154
+ if (config.env.MODE) {
155
+ define['import.meta.env.MODE'] = JSON.stringify(config.env.MODE);
156
+ }
157
+ // Add DEV/PROD flags
158
+ define['import.meta.env.DEV'] = JSON.stringify(config.env.MODE !== 'production');
159
+ define['import.meta.env.PROD'] = JSON.stringify(config.env.MODE === 'production');
160
+ }
161
+
162
+ let result: any;
163
+ let buildTime: number;
164
+ let size: number;
165
+
166
+ if (runtime === 'node') {
167
+ // Node.js - use esbuild
168
+ const { build: esbuild } = await import('esbuild');
169
+
170
+ result = await esbuild({
171
+ entryPoints: [entryPath],
172
+ bundle: true,
173
+ outfile: outputPath,
174
+ format: config.format,
175
+ target: config.target,
176
+ minify: config.minify,
177
+ sourcemap: config.sourcemap,
178
+ external: config.external,
179
+ treeShaking: config.treeshake,
180
+ globalName: config.globalName,
181
+ platform,
182
+ plugins,
183
+ define,
184
+ logLevel: config.logging ? 'info' : 'silent',
185
+ metafile: true,
186
+ // Additional optimizations
187
+ ...getMinifyOptions(config.minify)
188
+ });
189
+
190
+ ({ buildTime, size } = calculateBuildMetrics(startTime, outputPath));
191
+ } else if (runtime === 'bun') {
192
+ // Bun - use Bun.build
193
+ // @ts-ignore
194
+ result = await Bun.build({
195
+ entrypoints: [entryPath],
196
+ outdir: outDir,
197
+ target: 'bun',
198
+ format: config.format === 'cjs' ? 'cjs' : 'esm',
199
+ minify: config.minify,
200
+ sourcemap: config.sourcemap ? 'external' : 'none',
201
+ external: config.external,
202
+ naming: outFile,
203
+ define
204
+ });
205
+
206
+ if (!result.success) {
207
+ throw new Error('Bun build failed: ' + JSON.stringify(result.logs));
208
+ }
209
+
210
+ ({ buildTime, size } = calculateBuildMetrics(startTime, outputPath));
211
+ } else {
212
+ // Deno - use Deno.emit
213
+ // @ts-ignore
214
+ result = await Deno.emit(entryPath, {
215
+ bundle: 'module',
216
+ check: false,
217
+ compilerOptions: {
218
+ target: config.target,
219
+ module: config.format === 'cjs' ? 'commonjs' : 'esnext',
220
+ sourceMap: config.sourcemap
221
+ }
222
+ });
223
+
224
+ // Write the bundled output
225
+ const bundledCode = result.files['deno:///bundle.js'];
226
+ if (bundledCode) {
227
+ // @ts-ignore
228
+ await Deno.writeTextFile(outputPath, bundledCode);
229
+ }
230
+
231
+ ({ buildTime, size } = calculateBuildMetrics(startTime, outputPath));
232
+ }
233
+
234
+ if (config.logging) {
235
+ logBuildSuccess(buildTime, size);
236
+
237
+ // Show metafile info (Node.js esbuild only)
238
+ if (runtime === 'node' && result.metafile) {
239
+ const inputs = Object.keys(result.metafile.inputs).length;
240
+ console.log(` Files: ${inputs} input(s)`);
241
+
242
+ // Show largest modules
243
+ const outputKeys = Object.keys(result.metafile.outputs);
244
+ if (outputKeys.length > 0) {
245
+ const mainOutput = result.metafile.outputs[outputKeys[0]];
246
+ if (mainOutput && mainOutput.inputs) {
247
+ const sortedInputs = Object.entries(mainOutput.inputs)
248
+ .sort(([, a], [, b]) => {
249
+ const aBytes = (a as any).bytesInOutput || 0;
250
+ const bBytes = (b as any).bytesInOutput || 0;
251
+ return bBytes - aBytes;
252
+ })
253
+ .slice(0, 5);
254
+
255
+ if (sortedInputs.length > 0) {
256
+ console.log('\n šŸ“Š Top 5 largest modules:');
257
+ sortedInputs.forEach(([file, info]) => {
258
+ const fileName = file.split(/[/\\]/).pop() || file;
259
+ const infoBytes = (info as any).bytesInOutput || 0;
260
+ console.log(` ${fileName.padEnd(30)} ${formatBytes(infoBytes)}`);
261
+ });
262
+ }
263
+ }
264
+ }
265
+ }
266
+ }
267
+
268
+ const buildResult: BuildResult = {
269
+ outputPath,
270
+ buildTime,
271
+ size
272
+ };
273
+
274
+ // Handle copy files
275
+ if (config.copy && config.copy.length > 0) {
276
+ if (config.logging) {
277
+ console.log('\nšŸ“¦ Copying files...');
278
+ }
279
+
280
+ for (const copyItem of config.copy) {
281
+ const fromPath = resolve(copyItem.from);
282
+ const toPath = resolve(outDir, copyItem.to);
283
+
284
+ // Ensure target directory exists
285
+ const targetDir = dirname(toPath);
286
+ if (!existsSync(targetDir)) {
287
+ ensureDir(targetDir);
288
+ }
289
+
290
+ if (existsSync(fromPath)) {
291
+ if (copyItem.transform) {
292
+ // Read, transform, and write
293
+ const content = readFileAsString(fromPath);
294
+ const transformed = copyItem.transform(content, config);
295
+ writeFileSync(toPath, transformed);
296
+ } else {
297
+ // Direct copy
298
+ copyFileSync(fromPath, toPath);
299
+ }
300
+
301
+ if (config.logging) {
302
+ console.log(` āœ“ ${copyItem.from} → ${copyItem.to}`);
303
+ }
304
+ } else if (config.logging) {
305
+ console.warn(` ⚠ File not found: ${copyItem.from}`);
306
+ }
307
+ }
308
+ }
309
+
310
+ // Call post-build hook
311
+ if (config.onBuildEnd) {
312
+ await config.onBuildEnd(buildResult);
313
+ }
314
+
315
+ if (config.logging) {
316
+ console.log('');
317
+ }
318
+
319
+ return buildResult;
320
+ } catch (error) {
321
+ if (config.logging) {
322
+ console.error('\nāŒ Build failed:');
323
+ console.error(error);
324
+ }
325
+ throw error;
326
+ }
327
+ }
328
+
329
+ function formatBytes(bytes: number): string {
330
+ if (bytes === 0) return '0 B';
331
+ const k = 1024;
332
+ const sizes = ['B', 'KB', 'MB', 'GB'];
333
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
334
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
335
+ }
336
+
337
+ export type { BuildOptions, BuildResult } from './types';