nyte 1.2.4 → 1.2.6

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