nyte 1.2.3 → 1.2.5

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/src/builder.js CHANGED
@@ -15,15 +15,20 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
- const { build: viteBuild, createServer, defineConfig } = require('vite');
19
- const react = require('@vitejs/plugin-react');
18
+ const { rollup, watch: rollupWatch } = require('rollup');
20
19
  const path = require('path');
21
20
  const Console = require("./api/console").default;
22
21
  const fs = require('fs');
23
22
  const { readdir, stat } = require("node:fs/promises");
24
- const { rm } = require("fs-extra");
25
23
 
26
- // Lista de módulos nativos do Node.js para marcar como externos
24
+
25
+ // Plugins Oficiais do Rollup
26
+ const nodeResolve = require('@rollup/plugin-node-resolve').default;
27
+ const commonjs = require('@rollup/plugin-commonjs').default;
28
+ const replace = require('@rollup/plugin-replace').default;
29
+ const esbuild = require('rollup-plugin-esbuild').default;
30
+
31
+ // Lista de módulos nativos do Node.js
27
32
  const nodeBuiltIns = [
28
33
  'assert', 'buffer', 'child_process', 'cluster', 'crypto', 'dgram', 'dns',
29
34
  'domain', 'events', 'fs', 'http', 'https', 'net', 'os', 'path', 'punycode',
@@ -31,49 +36,6 @@ const nodeBuiltIns = [
31
36
  'util', 'v8', 'vm', 'zlib', 'module', 'worker_threads', 'perf_hooks'
32
37
  ];
33
38
 
34
- /**
35
- * Plugin para replicar o comportamento de Aliases do TSConfig
36
- */
37
- const customAliasPlugin = () => {
38
- return {
39
- name: 'custom-tsconfig-paths',
40
- config(config) {
41
- const projectDir = process.cwd();
42
- const tsconfigPath = path.join(projectDir, 'tsconfig.json');
43
- let aliasMap = {};
44
-
45
- if (fs.existsSync(tsconfigPath)) {
46
- try {
47
- const tsconfigContent = fs.readFileSync(tsconfigPath, 'utf8');
48
- const jsonContent = tsconfigContent.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
49
- const tsconfig = JSON.parse(jsonContent);
50
-
51
- if (tsconfig.compilerOptions?.paths) {
52
- const baseUrl = tsconfig.compilerOptions.baseUrl || '.';
53
- const basePath = path.resolve(projectDir, baseUrl);
54
-
55
- for (const [alias, paths] of Object.entries(tsconfig.compilerOptions.paths)) {
56
- if (Array.isArray(paths) && paths.length > 0) {
57
- const cleanAlias = alias.replace(/\/\*$/, '');
58
- const cleanPath = paths[0].replace(/\/\*$/, '');
59
- aliasMap[cleanAlias] = path.resolve(basePath, cleanPath);
60
- }
61
- }
62
- }
63
- } catch (error) {
64
- Console.warn('Error reading tsconfig.json for aliases:', error.message);
65
- }
66
- }
67
-
68
- return {
69
- resolve: {
70
- alias: aliasMap
71
- }
72
- };
73
- }
74
- };
75
- };
76
-
77
39
  /**
78
40
  * Plugin para Markdown
79
41
  */
@@ -92,19 +54,83 @@ const markdownPlugin = () => {
92
54
  };
93
55
 
94
56
  /**
95
- * Plugin para CSS/PostCSS Manual (Virtual Module Strategy)
57
+ * Plugin para CSS/PostCSS Manual (Smart Extraction + Tailwind Fix)
96
58
  */
97
- const customPostCssPlugin = () => {
59
+ const customPostCssPlugin = (isProduction) => {
60
+ let cachedProcessor = null;
61
+ let configLoaded = false;
62
+
63
+ // Função auxiliar para inicializar o PostCSS apenas uma vez
64
+ const initPostCss = async (projectDir) => {
65
+ if (configLoaded) return cachedProcessor;
66
+
67
+ // CRÍTICO: Garante que o Tailwind saiba que é produção para purgar CSS não usado
68
+ process.env.NODE_ENV = isProduction ? 'production' : 'development';
69
+
70
+ const postcssConfigPath = path.join(projectDir, 'postcss.config.js');
71
+ const postcssConfigMjsPath = path.join(projectDir, 'postcss.config.mjs');
72
+ const configPath = fs.existsSync(postcssConfigPath) ? postcssConfigPath :
73
+ (fs.existsSync(postcssConfigMjsPath) ? postcssConfigMjsPath : null);
74
+
75
+ if (configPath) {
76
+ try {
77
+ let postcss;
78
+ try {
79
+ postcss = require(path.join(projectDir, 'node_modules', 'postcss'));
80
+ } catch {
81
+ try { postcss = require('postcss'); } catch (e) { return null; }
82
+ }
83
+
84
+ if (postcss) {
85
+ delete require.cache[require.resolve(configPath)];
86
+ const config = require(configPath);
87
+ const postcssConfig = config.default || config;
88
+
89
+ const plugins = [];
90
+ if (postcssConfig.plugins) {
91
+ if (Array.isArray(postcssConfig.plugins)) {
92
+ plugins.push(...postcssConfig.plugins.map(p => {
93
+ if (typeof p === 'string') {
94
+ try {
95
+ const resolved = require.resolve(p, { paths: [projectDir] });
96
+ return require(resolved);
97
+ } catch { return require(p); }
98
+ }
99
+ return p;
100
+ }));
101
+ } else {
102
+ for (const [name, options] of Object.entries(postcssConfig.plugins)) {
103
+ try {
104
+ const resolvedPath = require.resolve(name, { paths: [projectDir] });
105
+ const pluginModule = require(resolvedPath);
106
+ plugins.push(pluginModule(options || {}));
107
+ } catch (e) {
108
+ Console.warn(`Unable to load plugin ${name}:`, e.message);
109
+ }
110
+ }
111
+ }
112
+ }
113
+ cachedProcessor = postcss(plugins);
114
+ }
115
+ } catch (e) {
116
+ Console.warn(`Error initializing PostCSS:`, e.message);
117
+ }
118
+ }
119
+ configLoaded = true;
120
+ return cachedProcessor;
121
+ };
122
+
98
123
  return {
99
124
  name: 'custom-postcss-plugin',
100
- enforce: 'pre',
101
125
 
102
126
  async resolveId(source, importer) {
127
+ if (source.startsWith('\0custom-css:')) return null;
128
+
103
129
  if (source.endsWith('.css')) {
104
- if (source.startsWith('\0custom-css:')) return null;
105
130
  const resolution = await this.resolve(source, importer, { skipSelf: true });
106
131
  if (resolution && resolution.id) {
107
- return `\0custom-css:${resolution.id}.js`;
132
+ if (resolution.id.startsWith('\0custom-css:')) return resolution.id;
133
+ return `\0custom-css:${resolution.id}`;
108
134
  }
109
135
  }
110
136
  return null;
@@ -112,68 +138,53 @@ const customPostCssPlugin = () => {
112
138
 
113
139
  async load(id) {
114
140
  if (id.startsWith('\0custom-css:')) {
115
- const filePath = id.slice('\0custom-css:'.length, -3);
141
+ let filePath = id.slice('\0custom-css:'.length);
142
+ while (filePath.startsWith('\0custom-css:')) {
143
+ filePath = filePath.slice('\0custom-css:'.length);
144
+ }
145
+
116
146
  const cssContent = await fs.promises.readFile(filePath, 'utf8');
117
- const projectDir = process.cwd();
118
- const postcssConfigPath = path.join(projectDir, 'postcss.config.js');
119
- const postcssConfigMjsPath = path.join(projectDir, 'postcss.config.mjs');
120
147
 
148
+ // Usa o processador em cache ou inicializa se for a primeira vez
149
+ const processor = await initPostCss(process.cwd());
121
150
  let processedCss = cssContent;
122
151
 
123
- if (fs.existsSync(postcssConfigPath) || fs.existsSync(postcssConfigMjsPath)) {
152
+ if (processor) {
124
153
  try {
125
- let postcss;
126
- try {
127
- const postcssPath = path.join(projectDir, 'node_modules', 'postcss');
128
- postcss = require(postcssPath);
129
- } catch {
130
- try { postcss = require('postcss'); } catch (e) {
131
- Console.warn('PostCSS not found. Using raw CSS.');
132
- }
133
- }
134
-
135
- if (postcss) {
136
- const configPath = fs.existsSync(postcssConfigPath) ? postcssConfigPath : postcssConfigMjsPath;
137
- delete require.cache[require.resolve(configPath)];
138
- const config = require(configPath);
139
- const postcssConfig = config.default || config;
140
-
141
- const plugins = [];
142
- if (postcssConfig.plugins) {
143
- if (Array.isArray(postcssConfig.plugins)) {
144
- plugins.push(...postcssConfig.plugins.map(p => {
145
- if (typeof p === 'string') {
146
- try {
147
- const resolved = require.resolve(p, { paths: [projectDir] });
148
- return require(resolved);
149
- } catch { return require(p); }
150
- }
151
- return p;
152
- }));
153
- } else {
154
- for (const [name, options] of Object.entries(postcssConfig.plugins)) {
155
- try {
156
- const resolvedPath = require.resolve(name, { paths: [projectDir] });
157
- const pluginModule = require(resolvedPath);
158
- plugins.push(pluginModule(options || {}));
159
- } catch (e) {
160
- Console.warn(`Unable to load plugin ${name}:`, e.message);
161
- }
162
- }
163
- }
164
- }
165
-
166
- const result = await postcss(plugins).process(cssContent, {
167
- from: filePath,
168
- to: filePath.replace(/\.css$/, '.processed.css')
169
- });
170
- processedCss = result.css;
171
- }
154
+ const result = await processor.process(cssContent, {
155
+ from: filePath,
156
+ to: filePath
157
+ });
158
+ processedCss = result.css;
172
159
  } catch (e) {
173
- Console.warn(`Error processing CSS with PostCSS:`, e.message);
160
+ Console.warn(`PostCSS process error:`, e.message);
174
161
  }
175
162
  }
176
163
 
164
+ // ESTRATÉGIA DE EXTRAÇÃO INTELIGENTE
165
+ if (isProduction) {
166
+ // Emite arquivo físico (Melhora Cache e Tamanho do JS)
167
+ const referenceId = this.emitFile({
168
+ type: 'asset',
169
+ name: path.basename(filePath),
170
+ source: processedCss
171
+ });
172
+
173
+ // Retorna código JS que auto-injeta o <link>
174
+ // Isso mantém compatibilidade (não quebra o app) mas usa arquivo externo
175
+ return `
176
+ const cssUrl = import.meta.ROLLUP_FILE_URL_${referenceId};
177
+ if (typeof document !== 'undefined') {
178
+ const link = document.createElement('link');
179
+ link.rel = 'stylesheet';
180
+ link.href = cssUrl;
181
+ document.head.appendChild(link);
182
+ }
183
+ export default cssUrl;
184
+ `;
185
+ }
186
+
187
+ // Modo DEV (Inline para Hot Reload mais rápido)
177
188
  return `
178
189
  const css = ${JSON.stringify(processedCss)};
179
190
  if (typeof document !== 'undefined') {
@@ -190,99 +201,156 @@ const customPostCssPlugin = () => {
190
201
  };
191
202
 
192
203
  /**
193
- * Plugin para forçar Assets como Base64
204
+ * Plugin Inteligente para Assets (Substitui forceBase64Plugin)
205
+ * - Dev: Base64 (Rápido)
206
+ * - Prod: Arquivos > 4KB viram URL (Melhora LCP), < 4KB Base64 (Menos requests)
194
207
  */
195
- const forceBase64Plugin = () => {
208
+ const smartAssetPlugin = (isProduction) => {
209
+ const INLINE_LIMIT = 4096; // 4KB
210
+
196
211
  return {
197
- name: 'force-base64-assets',
212
+ name: 'smart-asset-loader',
198
213
  async load(id) {
199
214
  const cleanId = id.split('?')[0];
215
+ if (cleanId.startsWith('\0')) return null;
216
+
200
217
  const ext = path.extname(cleanId).slice(1).toLowerCase();
201
218
 
202
219
  const mimeTypes = {
203
220
  'png': 'image/png', 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg',
204
221
  'gif': 'image/gif', 'webp': 'image/webp', 'avif': 'image/avif',
205
- 'ico': 'image/x-icon', 'bmp': 'image/bmp', 'tif': 'image/tiff', 'tiff': 'image/tiff',
206
- 'woff': 'font/woff', 'woff2': 'font/woff2', 'ttf': 'font/ttf',
207
- 'otf': 'font/otf', 'eot': 'application/vnd.ms-fontobject',
222
+ 'ico': 'image/x-icon', 'bmp': 'image/bmp', 'tif': 'image/tiff',
223
+ 'tiff': 'image/tiff', 'woff': 'font/woff', 'woff2': 'font/woff2',
224
+ 'ttf': 'font/ttf', 'otf': 'font/otf', 'eot': 'application/vnd.ms-fontobject',
208
225
  'mp3': 'audio/mpeg', 'wav': 'audio/wav', 'ogg': 'audio/ogg',
209
226
  'm4a': 'audio/mp4', 'aac': 'audio/aac', 'flac': 'audio/flac',
210
- 'mp4': 'video/mp4', 'webm': 'video/webm', 'ogv': 'video/ogg'
227
+ 'mp4': 'video/mp4', 'webm': 'video/webm', 'ogv': 'video/ogg',
228
+ 'svg': 'svg', 'txt': 'txt'
211
229
  };
212
230
 
213
- if (mimeTypes[ext]) {
214
- const buffer = await fs.promises.readFile(cleanId);
215
- const base64 = buffer.toString('base64');
216
- return `export default "data:${mimeTypes[ext]};base64,${base64}";`;
217
- }
231
+ const type = mimeTypes[ext];
232
+ if (!type) return null;
218
233
 
219
- if (ext === 'svg') {
234
+ // Text files always strings
235
+ if (type === 'txt') {
220
236
  const content = await fs.promises.readFile(cleanId, 'utf8');
221
- const base64 = Buffer.from(content).toString('base64');
237
+ return `export default ${JSON.stringify(content)};`;
238
+ }
239
+
240
+ const buffer = await fs.promises.readFile(cleanId);
241
+ const size = buffer.length;
242
+
243
+ // MODO PRODUÇÃO: Otimização de Assets
244
+ if (isProduction) {
245
+ // SVG: Se for pequeno inlina, se grande emite arquivo
246
+ if (type === 'svg') {
247
+ if (size < INLINE_LIMIT) {
248
+ const content = buffer.toString('utf8');
249
+ const base64 = buffer.toString('base64');
250
+ return `
251
+ export default "data:image/svg+xml;base64,${base64}";
252
+ export const svgContent = ${JSON.stringify(content)};
253
+ `;
254
+ } else {
255
+ // Emite arquivo físico
256
+ const referenceId = this.emitFile({
257
+ type: 'asset',
258
+ name: path.basename(cleanId),
259
+ source: buffer
260
+ });
261
+ const content = buffer.toString('utf8');
262
+ return `
263
+ export default import.meta.ROLLUP_FILE_URL_${referenceId};
264
+ export const svgContent = ${JSON.stringify(content)};
265
+ `;
266
+ }
267
+ }
268
+
269
+ // Outros assets
270
+ if (size < INLINE_LIMIT) {
271
+ return `export default "data:${type};base64,${buffer.toString('base64')}";`;
272
+ } else {
273
+ const referenceId = this.emitFile({
274
+ type: 'asset',
275
+ name: path.basename(cleanId),
276
+ source: buffer
277
+ });
278
+ return `export default import.meta.ROLLUP_FILE_URL_${referenceId};`;
279
+ }
280
+ }
281
+
282
+ // MODO DESENVOLVIMENTO: Tudo Base64 para performance de build
283
+ if (type === 'svg') {
284
+ const content = buffer.toString('utf8');
285
+ const base64 = buffer.toString('base64');
222
286
  return `
223
287
  export default "data:image/svg+xml;base64,${base64}";
224
288
  export const svgContent = ${JSON.stringify(content)};
225
289
  `;
226
290
  }
227
-
228
- if (ext === 'txt') {
229
- const content = await fs.promises.readFile(cleanId, 'utf8');
230
- return `export default ${JSON.stringify(content)};`;
231
- }
291
+ return `export default "data:${type};base64,${buffer.toString('base64')}";`;
232
292
  }
233
293
  };
234
294
  };
235
295
 
236
296
  /**
237
- * Helper para criar configuração base do Vite
297
+ * Gera a configuração base do Rollup
238
298
  */
239
- function createBaseConfig(entryPoint, outdir, isProduction) {
240
- const projectDir = process.cwd();
241
-
242
- return defineConfig({
243
- root: path.dirname(entryPoint),
244
- // Base relativa para garantir carregamento correto
245
- base: '/_nyte/',
246
- configFile: false,
247
- publicDir: false,
248
- logLevel: 'error',
249
- clearScreen: false,
250
- define: {
251
- 'process.env.NODE_ENV': isProduction ? '"production"' : '"development"'
252
- },
253
- resolve: {
254
- extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json']
255
- },
299
+ function createRollupConfig(entryPoint, outdir, isProduction) {
300
+ return {
301
+ input: entryPoint,
302
+ external: nodeBuiltIns,
303
+ // Otimização: Em prod usa 'recommended' para limpar código morto
304
+ treeshake: isProduction ? 'recommended' : false,
305
+ cache: true,
306
+ perf: false,
256
307
  plugins: [
257
- customPostCssPlugin(),
258
- react(),
259
- customAliasPlugin(),
308
+ nodeResolve({
309
+ extensions: ['.mjs', '.js', '.json', '.node', '.jsx', '.tsx', '.ts'],
310
+ preferBuiltins: true,
311
+ browser: false,
312
+ dedupe: ['react', 'react-dom'] // Otimização: Evita duplicar React no bundle
313
+ }),
314
+
315
+ commonjs({
316
+ sourceMap: !isProduction
317
+ }),
318
+
260
319
  markdownPlugin(),
261
- forceBase64Plugin(),
262
- ],
263
- build: {
264
- outDir: outdir,
265
- emptyOutDir: false,
266
- minify: isProduction ? 'esbuild' : false,
267
- sourcemap: !isProduction,
268
- target: 'es2020',
269
- reportCompressedSize: false,
270
- chunkSizeWarningLimit: 10000,
271
- commonjsOptions: {
272
- transformMixedEsModules: true,
273
- include: [/node_modules/, /packages/],
274
- },
275
- rollupOptions: {
276
- input: entryPoint,
277
- external: nodeBuiltIns,
278
- onwarn(warning, warn) {
279
- if (warning.code === 'MODULE_LEVEL_DIRECTIVE') return;
280
- if (warning.code === 'INVALID_ANNOTATION') return;
281
- warn(warning);
320
+
321
+ // Passamos isProduction para ativar a Extração Inteligente
322
+ customPostCssPlugin(isProduction),
323
+
324
+ // Substitui forceBase64Plugin pelo Smart
325
+ smartAssetPlugin(isProduction),
326
+
327
+ replace({
328
+ preventAssignment: true,
329
+ values: {
330
+ 'process.env.NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development')
282
331
  }
283
- }
332
+ }),
333
+
334
+ esbuild({
335
+ include: /\.[jt]sx?$/,
336
+ exclude: /node_modules/,
337
+ sourceMap: !isProduction,
338
+ minify: isProduction,
339
+ // Otimização: Remove comentários legais em produção
340
+ legalComments: isProduction ? 'none' : 'eof',
341
+ treeShaking: isProduction,
342
+ target: isProduction ? 'es2020' : 'esnext',
343
+ jsx: 'automatic',
344
+ define: { __VERSION__: '"1.0.0"' },
345
+ loaders: { '.json': 'json', '.js': 'jsx' }
346
+ })
347
+ ],
348
+ onwarn(warning, warn) {
349
+ if (warning.code === 'MODULE_LEVEL_DIRECTIVE') return;
350
+ if (warning.code === 'THIS_IS_UNDEFINED') return;
351
+ warn(warning);
284
352
  }
285
- });
353
+ };
286
354
  }
287
355
 
288
356
  /**
@@ -292,22 +360,44 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
292
360
  await cleanDirectoryExcept(outdir, 'temp');
293
361
 
294
362
  try {
295
- const config = createBaseConfig(entryPoint, outdir, isProduction);
363
+ const inputOptions = createRollupConfig(entryPoint, outdir, isProduction);
296
364
 
297
- config.build.lib = false;
298
- config.build.rollupOptions.output = {
365
+ const outputOptions = {
366
+ dir: outdir,
299
367
  format: 'es',
368
+ // Padrão de nomes mantido, mas com estrutura para vários arquivos
300
369
  entryFileNames: isProduction ? 'main-[hash].js' : 'main.js',
301
370
  chunkFileNames: 'chunks/[name]-[hash].js',
302
371
  assetFileNames: 'assets/[name]-[hash][extname]',
372
+ sourcemap: !isProduction,
373
+
374
+ // OTIMIZAÇÃO: Separação granular para melhor Cache e Load Time
303
375
  manualChunks(id) {
304
376
  if (id.includes('node_modules')) {
377
+ // React Core isolado (muda pouco, cache longo)
378
+ if (id.includes('react') || id.includes('scheduler') || id.includes('prop-types')) {
379
+ return 'vendor-react';
380
+ }
381
+
382
+ // UI Libs comuns (opcional, pode ajustar conforme necessidade)
383
+ if (id.includes('framer-motion') || id.includes('@radix-ui')) {
384
+ return 'vendor-ui';
385
+ }
386
+
387
+ // Utils comuns
388
+ if (id.includes('lodash') || id.includes('date-fns') || id.includes('axios')) {
389
+ return 'vendor-utils';
390
+ }
391
+
392
+ // Resto das dependências
305
393
  return 'vendor';
306
394
  }
307
395
  }
308
396
  };
309
397
 
310
- await viteBuild(config);
398
+ const bundle = await rollup(inputOptions);
399
+ await bundle.write(outputOptions);
400
+ await bundle.close();
311
401
 
312
402
  } catch (error) {
313
403
  Console.error('An error occurred while building with chunks:', error);
@@ -320,22 +410,21 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
320
410
  */
321
411
  async function build(entryPoint, outfile, isProduction = false) {
322
412
  const outdir = path.dirname(outfile);
323
- const filename = path.basename(outfile);
324
-
325
413
  await cleanDirectoryExcept(outdir, 'temp');
326
414
 
327
415
  try {
328
- const config = createBaseConfig(entryPoint, outdir, isProduction);
329
-
330
- config.build.cssCodeSplit = false;
331
- config.build.rollupOptions.output = {
416
+ const inputOptions = createRollupConfig(entryPoint, outdir, isProduction);
417
+ const outputOptions = {
418
+ file: outfile,
332
419
  format: 'iife',
333
420
  name: 'HwebApp',
334
- entryFileNames: filename,
335
- inlineDynamicImports: true,
421
+ sourcemap: !isProduction,
422
+ inlineDynamicImports: true
336
423
  };
337
424
 
338
- await viteBuild(config);
425
+ const bundle = await rollup(inputOptions);
426
+ await bundle.write(outputOptions);
427
+ await bundle.close();
339
428
 
340
429
  } catch (error) {
341
430
  Console.error('An error occurred during build:', error);
@@ -344,127 +433,78 @@ async function build(entryPoint, outfile, isProduction = false) {
344
433
  }
345
434
 
346
435
  /**
347
- * Watches with code splitting enabled (MODIFICADO: No DEV MODE, desativa splitting)
436
+ * Helper para lidar com notificações do Watcher
437
+ */
438
+ function handleWatcherEvents(watcher, hotReloadManager, resolveFirstBuild) {
439
+ let hasError = false;
440
+
441
+ watcher.on('event', event => {
442
+ if (event.code === 'START') hasError = false;
443
+
444
+ if (event.code === 'ERROR') {
445
+ hasError = true;
446
+ const errDetails = {
447
+ message: event.error?.message || 'Unknown build error',
448
+ name: event.error?.name,
449
+ stack: event.error?.stack,
450
+ id: event.error?.id,
451
+ loc: event.error?.loc
452
+ };
453
+
454
+ if (hotReloadManager) hotReloadManager.onBuildComplete(false, errDetails);
455
+ else Console.error("Build Error:", event.error);
456
+
457
+ if (resolveFirstBuild) resolveFirstBuild();
458
+ }
459
+
460
+ if (event.code === 'BUNDLE_END') event.result.close();
461
+
462
+ if (event.code === 'END') {
463
+ if (!hasError && hotReloadManager) hotReloadManager.onBuildComplete(true);
464
+ if (resolveFirstBuild) {
465
+ resolveFirstBuild();
466
+ resolveFirstBuild = null;
467
+ }
468
+ }
469
+ });
470
+ }
471
+
472
+ /**
473
+ * Watches with code splitting enabled
348
474
  */
349
475
  async function watchWithChunks(entryPoint, outdir, hotReloadManager = null) {
350
476
  await cleanDirectoryExcept(outdir, 'temp');
351
477
 
352
478
  try {
353
- const config = createBaseConfig(entryPoint, outdir, false);
479
+ // DEV MODE: isProduction = false
480
+ const inputOptions = createRollupConfig(entryPoint, outdir, false);
354
481
 
355
- config.build.watch = {};
356
-
357
- // MODIFICAÇÃO SOLICITADA:
358
- // No modo DEV (Watch), forçamos um único arquivo (IIFE) para evitar
359
- // problemas de 404 com chunks e simplificar o debug.
360
- // O splitting fica restrito ao build de produção.
361
- config.build.cssCodeSplit = false;
362
- config.build.rollupOptions.output = {
482
+ const outputOptions = {
483
+ dir: outdir,
363
484
  format: 'iife',
364
485
  name: 'HwebApp',
365
486
  entryFileNames: 'main.js',
366
- inlineDynamicImports: true // <--- Força tudo em um arquivo só
367
- // Removido manualChunks para evitar conflito com inlineDynamicImports
487
+ inlineDynamicImports: true,
488
+ sourcemap: true
368
489
  };
369
490
 
370
- // Adiciona notificação de build via plugin
371
- config.plugins.push({
372
- name: 'build-notifier',
373
- // Controle de ciclo: um ciclo começa no buildStart e termina no buildEnd.
374
- // Se houve erro no ciclo, não podemos considerar "complete".
375
- buildStart() {
376
- this.__nyteCycleHadError = false;
377
- },
378
- closeBundle() {
379
- // closeBundle pode rodar em situações inesperadas; não usamos isso pra limpar erro.
380
- },
381
- buildEnd(error) {
382
- if (error) {
383
- this.__nyteCycleHadError = true;
384
- if (hotReloadManager) {
385
- hotReloadManager.onBuildComplete(false, {
386
- message: error.message,
387
- name: error.name,
388
- stack: error.stack,
389
- // Vite/Rollup às vezes coloca infos adicionais
390
- cause: error.cause,
391
- loc: error.loc,
392
- frame: error.frame,
393
- id: error.id,
394
- plugin: error.plugin,
395
- pluginCode: error.pluginCode,
396
- watchFiles: error.watchFiles
397
- });
398
- }
399
- }
491
+ const watchOptions = {
492
+ ...inputOptions,
493
+ output: outputOptions,
494
+ watch: {
495
+ exclude: 'node_modules/**',
496
+ clearScreen: false,
497
+ skipWrite: false
400
498
  }
401
- });
402
-
403
- const watcher = await viteBuild(config);
404
-
405
- // Flags do ciclo do watcher (mais confiável para decidir sucesso)
406
- let watcherCycleHadError = false;
407
-
408
- if (watcher && typeof watcher.on === 'function') {
409
- watcher.on('event', (event) => {
410
- if (!event) return;
411
-
412
- if (event.code === 'ERROR') {
413
- watcherCycleHadError = true;
414
-
415
- if (hotReloadManager) {
416
- hotReloadManager.onBuildComplete(false, {
417
- message: event.error?.message || 'Unknown build error',
418
- name: event.error?.name,
419
- stack: event.error?.stack,
420
- cause: event.error?.cause,
421
- loc: event.error?.loc,
422
- frame: event.error?.frame,
423
- id: event.error?.id,
424
- plugin: event.error?.plugin,
425
- pluginCode: event.error?.pluginCode,
426
- watchFiles: event.error?.watchFiles
427
- });
428
- }
429
- }
430
-
431
- if (event.code === 'BUNDLE_END') {
432
- if (hotReloadManager) {
433
- if (!watcherCycleHadError) {
434
- hotReloadManager.onBuildComplete(true);
435
- }
436
- }
437
-
438
- // Próximo ciclo
439
- watcherCycleHadError = false;
440
- }
441
- });
442
- }
443
-
444
- // Aguarda o primeiro build terminar
445
- await new Promise((resolve) => {
446
- let initialBuild = true;
447
- watcher.on('event', (event) => {
448
- if (event.code === 'BUNDLE_END') {
449
- if (initialBuild) {
450
- initialBuild = false;
451
- resolve();
452
- }
453
- }
454
- });
455
- });
499
+ };
456
500
 
501
+ const watcher = rollupWatch(watchOptions);
502
+ await new Promise((resolve) => handleWatcherEvents(watcher, hotReloadManager, resolve));
457
503
  return watcher;
458
504
 
459
505
  } catch (error) {
460
506
  Console.error('Error starting watch mode with chunks:', error);
461
- if (hotReloadManager) {
462
- hotReloadManager.onBuildComplete(false, {
463
- message: error.message,
464
- name: error.name,
465
- stack: error.stack
466
- });
467
- }
507
+ if (hotReloadManager) hotReloadManager.onBuildComplete(false, { message: error.message });
468
508
  throw error;
469
509
  }
470
510
  }
@@ -474,98 +514,34 @@ async function watchWithChunks(entryPoint, outdir, hotReloadManager = null) {
474
514
  */
475
515
  async function watch(entryPoint, outfile, hotReloadManager = null) {
476
516
  const outdir = path.dirname(outfile);
477
- const filename = path.basename(outfile);
478
517
 
479
518
  try {
480
- const config = createBaseConfig(entryPoint, outdir, false);
519
+ const inputOptions = createRollupConfig(entryPoint, outdir, false);
481
520
 
482
- config.build.watch = {};
483
- config.build.cssCodeSplit = false;
484
- config.build.rollupOptions.output = {
521
+ const outputOptions = {
522
+ file: outfile,
485
523
  format: 'iife',
486
524
  name: 'HwebApp',
487
- entryFileNames: filename,
488
- inlineDynamicImports: true
525
+ inlineDynamicImports: true,
526
+ sourcemap: true
489
527
  };
490
528
 
491
- config.plugins.push({
492
- name: 'build-notifier',
493
- buildStart() {
494
- this.__nyteCycleHadError = false;
495
- },
496
- closeBundle() {
497
- // Ignorado para sinalização de sucesso
498
- },
499
- buildEnd(error) {
500
- if (error) {
501
- this.__nyteCycleHadError = true;
502
- if (hotReloadManager) {
503
- hotReloadManager.onBuildComplete(false, {
504
- message: error.message,
505
- name: error.name,
506
- stack: error.stack,
507
- cause: error.cause,
508
- loc: error.loc,
509
- frame: error.frame,
510
- id: error.id,
511
- plugin: error.plugin,
512
- pluginCode: error.pluginCode,
513
- watchFiles: error.watchFiles
514
- });
515
- }
516
- }
529
+ const watchOptions = {
530
+ ...inputOptions,
531
+ output: outputOptions,
532
+ watch: {
533
+ exclude: 'node_modules/**',
534
+ clearScreen: false
517
535
  }
518
- });
519
-
520
- const watcher = await viteBuild(config);
521
-
522
- let watcherCycleHadError = false;
523
-
524
- if (watcher && typeof watcher.on === 'function') {
525
- watcher.on('event', (event) => {
526
- if (!event) return;
527
-
528
- if (event.code === 'ERROR') {
529
- watcherCycleHadError = true;
530
-
531
- if (hotReloadManager) {
532
- hotReloadManager.onBuildComplete(false, {
533
- message: event.error?.message || 'Unknown build error',
534
- name: event.error?.name,
535
- stack: event.error?.stack,
536
- cause: event.error?.cause,
537
- loc: event.error?.loc,
538
- frame: event.error?.frame,
539
- id: event.error?.id,
540
- plugin: event.error?.plugin,
541
- pluginCode: event.error?.pluginCode,
542
- watchFiles: event.error?.watchFiles
543
- });
544
- }
545
- }
546
-
547
- if (event.code === 'BUNDLE_END') {
548
- if (hotReloadManager) {
549
- if (!watcherCycleHadError) {
550
- hotReloadManager.onBuildComplete(true);
551
- }
552
- }
553
- watcherCycleHadError = false;
554
- }
555
- });
556
- }
536
+ };
557
537
 
558
- // (removido) await viteBuild(config); // já estamos em watch mode
538
+ const watcher = rollupWatch(watchOptions);
539
+ await new Promise((resolve) => handleWatcherEvents(watcher, hotReloadManager, resolve));
540
+ return watcher;
559
541
 
560
542
  } catch (error) {
561
543
  Console.error('Error starting watch mode:', error);
562
- if (hotReloadManager) {
563
- hotReloadManager.onBuildComplete(false, {
564
- message: error.message,
565
- name: error.name,
566
- stack: error.stack
567
- });
568
- }
544
+ if (hotReloadManager) hotReloadManager.onBuildComplete(false, { message: error.message });
569
545
  throw error;
570
546
  }
571
547
  }
@@ -573,20 +549,19 @@ async function watch(entryPoint, outfile, hotReloadManager = null) {
573
549
  async function cleanDirectoryExcept(dirPath, excludeFolder) {
574
550
  try {
575
551
  if (!fs.existsSync(dirPath)) return;
576
-
577
552
  const excludes = Array.isArray(excludeFolder) ? excludeFolder : [excludeFolder];
578
553
  const items = await readdir(dirPath);
579
554
 
580
- for (const item of items) {
581
- if (excludes.includes(item)) continue;
555
+ // Paraleliza a limpeza
556
+ await Promise.all(items.map(async (item) => {
557
+ if (excludes.includes(item)) return;
582
558
  const itemPath = path.join(dirPath, item);
583
559
  const info = await stat(itemPath);
584
- await rm(itemPath, { recursive: info.isDirectory(), force: true });
585
- }
560
+ fs.rm(itemPath, {recursive: info.isDirectory(), force: true});
561
+ }));
586
562
  } catch (e) {
587
563
  Console.warn(`Warning cleaning directory: ${e.message}`);
588
564
  }
589
565
  }
590
566
 
591
- module.exports = { build, watch, buildWithChunks, watchWithChunks };
592
-
567
+ module.exports = { build, watch, buildWithChunks, watchWithChunks };