vatts 1.2.5 → 1.3.1-alpha.1
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/bin/vatts.js +0 -17
- package/dist/builder.js +190 -33
- package/dist/helpers.d.ts +3 -3
- package/dist/helpers.js +38 -26
- package/dist/index.js +52 -8
- package/dist/react/react.build.d.ts +2 -0
- package/dist/react/react.build.js +20 -11
- package/dist/react/renderer-react.js +2 -25
- package/dist/types.d.ts +7 -7
- package/dist/vue/renderer.vue.js +6 -1
- package/dist/vue/vue.build.d.ts +2 -0
- package/dist/vue/vue.build.js +23 -12
- package/package.json +2 -1
package/dist/bin/vatts.js
CHANGED
|
@@ -39,24 +39,7 @@ function initializeApp(options, isDev) {
|
|
|
39
39
|
port: options.port,
|
|
40
40
|
hostname: options.hostname,
|
|
41
41
|
framework: 'native',
|
|
42
|
-
ssl: null,
|
|
43
42
|
};
|
|
44
|
-
if (options.ssl) {
|
|
45
|
-
const sslDir = path.resolve(process.cwd(), 'certs');
|
|
46
|
-
const keyPath = path.join(sslDir, 'key.pem');
|
|
47
|
-
const certPath = path.join(sslDir, 'cert.pem');
|
|
48
|
-
if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {
|
|
49
|
-
appOptions.ssl = {
|
|
50
|
-
key: keyPath,
|
|
51
|
-
cert: certPath
|
|
52
|
-
};
|
|
53
|
-
appOptions.ssl.redirectPort = options.httpRedirectPort || 80;
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
Console.error(`SSL Error: Ensure that './certs/key.pem' and './certs/cert.pem' exist.`);
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
43
|
const helperModule = require("../helpers");
|
|
61
44
|
const helper = helperModule.default(appOptions);
|
|
62
45
|
helper.init();
|
package/dist/builder.js
CHANGED
|
@@ -29,6 +29,15 @@ const Console = require("./api/console").default;
|
|
|
29
29
|
const fs = require('fs');
|
|
30
30
|
const { readdir, stat, rm } = require("node:fs/promises");
|
|
31
31
|
const { loadTsConfigPaths, resolveTsConfigAlias } = require('./tsconfigPaths');
|
|
32
|
+
// --- Optimization Plugins ---
|
|
33
|
+
let terser, replace;
|
|
34
|
+
try {
|
|
35
|
+
terser = require('@rollup/plugin-terser');
|
|
36
|
+
replace = require('@rollup/plugin-replace');
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
Console.warn("Optimization plugins (@rollup/plugin-terser, @rollup/plugin-replace) not found. Build will be larger.");
|
|
40
|
+
}
|
|
32
41
|
// Import Framework specific builders
|
|
33
42
|
const { createReactConfig } = require('./react/react.build');
|
|
34
43
|
const { createVueConfig } = require('./vue/vue.build');
|
|
@@ -61,7 +70,7 @@ const markdownPlugin = () => {
|
|
|
61
70
|
}
|
|
62
71
|
};
|
|
63
72
|
};
|
|
64
|
-
const customPostCssPlugin = (isProduction) => {
|
|
73
|
+
const customPostCssPlugin = (isProduction, isWatch = false) => {
|
|
65
74
|
let cachedProcessor = null;
|
|
66
75
|
let configLoaded = false;
|
|
67
76
|
const initPostCss = async (projectDir) => {
|
|
@@ -128,9 +137,15 @@ const customPostCssPlugin = (isProduction) => {
|
|
|
128
137
|
};
|
|
129
138
|
return {
|
|
130
139
|
name: 'custom-postcss-plugin',
|
|
140
|
+
// O load hook anterior estava causando conflitos com o watch interno do Rollup.
|
|
141
|
+
// Removemos ele e usamos addWatchFile no transform.
|
|
131
142
|
async transform(code, id) {
|
|
132
143
|
if (!id.endsWith('.css'))
|
|
133
144
|
return null;
|
|
145
|
+
// Garante que o Rollup vigie este arquivo explicitamente
|
|
146
|
+
if (isWatch) {
|
|
147
|
+
this.addWatchFile(id);
|
|
148
|
+
}
|
|
134
149
|
const processor = await initPostCss(process.cwd());
|
|
135
150
|
let processedCss = code;
|
|
136
151
|
if (processor) {
|
|
@@ -143,15 +158,29 @@ const customPostCssPlugin = (isProduction) => {
|
|
|
143
158
|
}
|
|
144
159
|
}
|
|
145
160
|
const cleanName = path.basename(id).split('?')[0];
|
|
161
|
+
// Sanitiza o nome para usar como ID no DOM
|
|
162
|
+
const safeId = cleanName.replace(/[^a-zA-Z0-9-_]/g, '_');
|
|
146
163
|
const referenceId = this.emitFile({ type: 'asset', name: cleanName, source: processedCss });
|
|
164
|
+
// Lógica melhorada para injetar o CSS:
|
|
165
|
+
// 1. Usa um ID único para evitar tags <link> duplicadas.
|
|
166
|
+
// 2. Adiciona um timestamp (?t=...) no href se estiver em dev para quebrar o cache do navegador.
|
|
147
167
|
return {
|
|
148
168
|
code: `
|
|
149
169
|
const cssUrl = String(import.meta.ROLLUP_FILE_URL_${referenceId});
|
|
150
170
|
if (typeof document !== 'undefined') {
|
|
151
|
-
const
|
|
152
|
-
link
|
|
153
|
-
|
|
154
|
-
|
|
171
|
+
const linkId = 'vatts-css-' + "${safeId}";
|
|
172
|
+
let link = document.getElementById(linkId);
|
|
173
|
+
|
|
174
|
+
if (!link) {
|
|
175
|
+
link = document.createElement('link');
|
|
176
|
+
link.id = linkId;
|
|
177
|
+
link.rel = 'stylesheet';
|
|
178
|
+
document.head.appendChild(link);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Em dev, força o reload do CSS adicionando timestamp
|
|
182
|
+
const timestamp = ${isWatch ? 'Date.now()' : 'null'};
|
|
183
|
+
link.href = timestamp ? (cssUrl + '?t=' + timestamp) : cssUrl;
|
|
155
184
|
}
|
|
156
185
|
export default cssUrl;
|
|
157
186
|
`,
|
|
@@ -224,6 +253,54 @@ const nodeBuiltIns = [
|
|
|
224
253
|
'querystring', 'readline', 'stream', 'string_decoder', 'tls', 'tty', 'url',
|
|
225
254
|
'util', 'v8', 'vm', 'zlib', 'module', 'worker_threads', 'perf_hooks'
|
|
226
255
|
];
|
|
256
|
+
// --- Optimization Logic ---
|
|
257
|
+
function getOptimizationPlugins(isProduction) {
|
|
258
|
+
const plugins = [];
|
|
259
|
+
const env = isProduction ? 'production' : 'development';
|
|
260
|
+
if (replace) {
|
|
261
|
+
plugins.push(replace({
|
|
262
|
+
'process.env.NODE_ENV': JSON.stringify(env),
|
|
263
|
+
'process.env': JSON.stringify({ NODE_ENV: env }),
|
|
264
|
+
'process.browser': 'true',
|
|
265
|
+
'__REACT_DEVTOOLS_GLOBAL_HOOK__': '({ isDisabled: true })',
|
|
266
|
+
preventAssignment: true,
|
|
267
|
+
objectGuards: true
|
|
268
|
+
}));
|
|
269
|
+
}
|
|
270
|
+
if (isProduction && terser) {
|
|
271
|
+
plugins.push(terser({
|
|
272
|
+
ecma: 2020,
|
|
273
|
+
module: true,
|
|
274
|
+
toplevel: true,
|
|
275
|
+
compress: {
|
|
276
|
+
passes: 3,
|
|
277
|
+
pure_getters: true,
|
|
278
|
+
unsafe: true,
|
|
279
|
+
unsafe_arrows: true,
|
|
280
|
+
unsafe_methods: true,
|
|
281
|
+
unsafe_proto: true,
|
|
282
|
+
booleans_as_integers: true,
|
|
283
|
+
drop_console: true,
|
|
284
|
+
drop_debugger: true,
|
|
285
|
+
keep_fargs: false,
|
|
286
|
+
hoist_funs: true,
|
|
287
|
+
hoist_vars: true,
|
|
288
|
+
reduce_funcs: true,
|
|
289
|
+
reduce_vars: true,
|
|
290
|
+
pure_funcs: ['console.info', 'console.debug', 'console.warn', 'console.log', 'Object.freeze']
|
|
291
|
+
},
|
|
292
|
+
mangle: {
|
|
293
|
+
properties: false,
|
|
294
|
+
toplevel: true,
|
|
295
|
+
},
|
|
296
|
+
format: {
|
|
297
|
+
comments: false,
|
|
298
|
+
wrap_func_args: false,
|
|
299
|
+
}
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
302
|
+
return plugins;
|
|
303
|
+
}
|
|
227
304
|
// --- Core Logic ---
|
|
228
305
|
function detectFramework(projectDir = process.cwd()) {
|
|
229
306
|
try {
|
|
@@ -240,15 +317,13 @@ function detectFramework(projectDir = process.cwd()) {
|
|
|
240
317
|
catch (e) { }
|
|
241
318
|
return 'react';
|
|
242
319
|
}
|
|
243
|
-
async function getFrameworkConfig(entryPoint, outdir, isProduction) {
|
|
320
|
+
async function getFrameworkConfig(entryPoint, outdir, isProduction, isWatch = false) {
|
|
244
321
|
const framework = detectFramework();
|
|
245
|
-
// Plugins que rodam ANTES da resolução de módulos (Configuração, Bloqueios de Framework incorreto)
|
|
246
322
|
const prePlugins = [
|
|
247
323
|
tsconfigPathsPlugin(process.cwd()),
|
|
248
324
|
{
|
|
249
325
|
name: 'block-vue-artifacts-generic',
|
|
250
326
|
load(id) {
|
|
251
|
-
// Se não for vue, bloqueia artifacts. O build.vue lida com o inverso.
|
|
252
327
|
if (framework !== 'vue' && (id.endsWith('.vue') || id.endsWith('.vue.js'))) {
|
|
253
328
|
return 'export default {};';
|
|
254
329
|
}
|
|
@@ -256,47 +331,102 @@ async function getFrameworkConfig(entryPoint, outdir, isProduction) {
|
|
|
256
331
|
}
|
|
257
332
|
}
|
|
258
333
|
];
|
|
259
|
-
// Plugins que rodam DEPOIS da transformação do Framework (Markdown, CSS, Assets)
|
|
260
|
-
// Isso é crucial para o Vue, pois o CSS é gerado dinamicamente e precisa ser pego aqui.
|
|
261
334
|
const postPlugins = [
|
|
262
335
|
markdownPlugin(),
|
|
263
|
-
customPostCssPlugin(isProduction),
|
|
336
|
+
customPostCssPlugin(isProduction, isWatch),
|
|
264
337
|
smartAssetPlugin(isProduction)
|
|
265
338
|
];
|
|
266
339
|
const pluginConfig = { prePlugins, postPlugins };
|
|
340
|
+
let config;
|
|
267
341
|
if (framework === 'vue') {
|
|
268
|
-
|
|
342
|
+
config = await createVueConfig(entryPoint, outdir, isProduction, pluginConfig);
|
|
269
343
|
}
|
|
270
344
|
else {
|
|
271
|
-
|
|
345
|
+
config = await createReactConfig(entryPoint, outdir, isProduction, pluginConfig);
|
|
272
346
|
}
|
|
347
|
+
return config;
|
|
273
348
|
}
|
|
274
349
|
// --- Build Functions ---
|
|
275
350
|
async function buildWithChunks(entryPoint, outdir, isProduction = false) {
|
|
276
351
|
await cleanDirectoryExcept(outdir, 'temp');
|
|
277
352
|
try {
|
|
278
|
-
const inputOptions = await getFrameworkConfig(entryPoint, outdir, isProduction);
|
|
353
|
+
const inputOptions = await getFrameworkConfig(entryPoint, outdir, isProduction, false);
|
|
279
354
|
inputOptions.external = nodeBuiltIns;
|
|
355
|
+
if (isProduction) {
|
|
356
|
+
inputOptions.treeshake = {
|
|
357
|
+
preset: 'smallest',
|
|
358
|
+
moduleSideEffects: (id) => !id.includes('node_modules') || id.endsWith('.css') || id.includes('entry.client'),
|
|
359
|
+
propertyReadSideEffects: false,
|
|
360
|
+
tryCatchDeoptimization: false
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
const optimizationPlugins = getOptimizationPlugins(isProduction);
|
|
364
|
+
const replacePlugin = optimizationPlugins.find(p => p.name === 'replace');
|
|
365
|
+
const otherPlugins = optimizationPlugins.filter(p => p.name !== 'replace');
|
|
366
|
+
if (replacePlugin)
|
|
367
|
+
inputOptions.plugins.unshift(replacePlugin);
|
|
368
|
+
inputOptions.plugins.push(...otherPlugins);
|
|
369
|
+
const processPolyfill = `var process = { env: { NODE_ENV: "${isProduction ? 'production' : 'development'}" } };`;
|
|
280
370
|
const outputOptions = {
|
|
281
371
|
dir: outdir,
|
|
282
372
|
format: 'es',
|
|
283
|
-
entryFileNames: isProduction ? 'main
|
|
284
|
-
chunkFileNames: 'chunks/[name]
|
|
285
|
-
assetFileNames: 'assets/[name]
|
|
373
|
+
entryFileNames: isProduction ? 'main.[hash].js' : 'main.js',
|
|
374
|
+
chunkFileNames: 'chunks/[name].[hash].js',
|
|
375
|
+
assetFileNames: 'assets/[name].[hash][extname]',
|
|
286
376
|
sourcemap: !isProduction,
|
|
287
377
|
compact: isProduction,
|
|
378
|
+
intro: processPolyfill,
|
|
288
379
|
manualChunks(id) {
|
|
289
380
|
if (id.includes('node_modules')) {
|
|
290
381
|
const normalizedId = id.replace(/\\/g, '/');
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
|
|
382
|
+
// --- VUE SPLITTING AVANÇADO ---
|
|
383
|
+
if (/\/node_modules\/vue-router\//.test(normalizedId))
|
|
384
|
+
return 'vendor-vue-router';
|
|
385
|
+
if (/\/node_modules\/(pinia|vuex)\//.test(normalizedId))
|
|
386
|
+
return 'vendor-vue-store';
|
|
387
|
+
// Separa os modulos internos do Vue para evitar um chunk gigante
|
|
388
|
+
if (/\/node_modules\/(vue|@vue)\//.test(normalizedId)) {
|
|
389
|
+
if (normalizedId.includes('/runtime-core'))
|
|
390
|
+
return 'vendor-vue-runtime-core';
|
|
391
|
+
if (normalizedId.includes('/runtime-dom'))
|
|
392
|
+
return 'vendor-vue-runtime-dom';
|
|
393
|
+
if (normalizedId.includes('/reactivity'))
|
|
394
|
+
return 'vendor-vue-reactivity';
|
|
395
|
+
if (normalizedId.includes('/shared'))
|
|
396
|
+
return 'vendor-vue-shared';
|
|
397
|
+
if (normalizedId.includes('/compiler-'))
|
|
398
|
+
return 'vendor-vue-compiler';
|
|
399
|
+
return 'vendor-vue-core';
|
|
400
|
+
}
|
|
401
|
+
// --- REACT SPLITTING AVANÇADO ---
|
|
402
|
+
// Separa DOM de Core e Scheduler
|
|
403
|
+
if (/\/node_modules\/react-dom\//.test(normalizedId))
|
|
404
|
+
return 'vendor-react-dom';
|
|
405
|
+
if (/\/node_modules\/scheduler\//.test(normalizedId))
|
|
406
|
+
return 'vendor-react-scheduler';
|
|
407
|
+
if (/\/node_modules\/react-router/.test(normalizedId))
|
|
408
|
+
return 'vendor-react-router';
|
|
409
|
+
if (/\/node_modules\/react\//.test(normalizedId))
|
|
410
|
+
return 'vendor-react-core';
|
|
411
|
+
// --- UI LIBS (Granular) ---
|
|
412
|
+
if (id.includes('framer-motion'))
|
|
413
|
+
return 'vendor-framer';
|
|
414
|
+
if (id.includes('@radix-ui'))
|
|
415
|
+
return 'vendor-radix';
|
|
416
|
+
if (id.includes('@headlessui'))
|
|
417
|
+
return 'vendor-headless';
|
|
418
|
+
if (id.includes('@heroicons'))
|
|
419
|
+
return 'vendor-icons';
|
|
420
|
+
// --- UTILS ---
|
|
421
|
+
if (id.includes('lodash'))
|
|
422
|
+
return 'vendor-lodash';
|
|
423
|
+
if (id.includes('date-fns') || id.includes('moment'))
|
|
424
|
+
return 'vendor-date';
|
|
425
|
+
if (id.includes('axios'))
|
|
426
|
+
return 'vendor-axios';
|
|
427
|
+
// Resto cai em vendor-libs genérico para não criar 1 arquivo por pacote,
|
|
428
|
+
// mas já tiramos o peso pesado acima.
|
|
429
|
+
return 'vendor-libs';
|
|
300
430
|
}
|
|
301
431
|
}
|
|
302
432
|
};
|
|
@@ -313,15 +443,31 @@ async function build(entryPoint, outfile, isProduction = false) {
|
|
|
313
443
|
const outdir = path.dirname(outfile);
|
|
314
444
|
await cleanDirectoryExcept(outdir, 'temp');
|
|
315
445
|
try {
|
|
316
|
-
const inputOptions = await getFrameworkConfig(entryPoint, outdir, isProduction);
|
|
446
|
+
const inputOptions = await getFrameworkConfig(entryPoint, outdir, isProduction, false);
|
|
317
447
|
inputOptions.external = nodeBuiltIns;
|
|
448
|
+
if (isProduction) {
|
|
449
|
+
inputOptions.treeshake = {
|
|
450
|
+
preset: 'smallest',
|
|
451
|
+
moduleSideEffects: (id) => !id.includes('node_modules') || id.endsWith('.css') || id.includes('entry.client'),
|
|
452
|
+
propertyReadSideEffects: false
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
const optimizationPlugins = getOptimizationPlugins(isProduction);
|
|
456
|
+
const replacePlugin = optimizationPlugins.find(p => p.name === 'replace');
|
|
457
|
+
const otherPlugins = optimizationPlugins.filter(p => p.name !== 'replace');
|
|
458
|
+
if (replacePlugin)
|
|
459
|
+
inputOptions.plugins.unshift(replacePlugin);
|
|
460
|
+
inputOptions.plugins.push(...otherPlugins);
|
|
461
|
+
const processPolyfill = `var process = { env: { NODE_ENV: "${isProduction ? 'production' : 'development'}" } };`;
|
|
318
462
|
const outputOptions = {
|
|
319
463
|
file: outfile,
|
|
320
464
|
format: 'iife',
|
|
321
465
|
name: 'Vattsjs',
|
|
322
466
|
sourcemap: !isProduction,
|
|
323
467
|
inlineDynamicImports: true,
|
|
324
|
-
compact: true
|
|
468
|
+
compact: true,
|
|
469
|
+
annotations: true,
|
|
470
|
+
intro: processPolyfill
|
|
325
471
|
};
|
|
326
472
|
const bundle = await rollup(inputOptions);
|
|
327
473
|
await bundle.write(outputOptions);
|
|
@@ -332,7 +478,6 @@ async function build(entryPoint, outfile, isProduction = false) {
|
|
|
332
478
|
process.exit(1);
|
|
333
479
|
}
|
|
334
480
|
}
|
|
335
|
-
// --- Watch Functions ---
|
|
336
481
|
function handleWatcherEvents(watcher, hotReloadManager, resolveFirstBuild) {
|
|
337
482
|
let currentBuildId = 0;
|
|
338
483
|
let lastStartedBuildId = 0;
|
|
@@ -387,13 +532,19 @@ function handleWatcherEvents(watcher, hotReloadManager, resolveFirstBuild) {
|
|
|
387
532
|
async function watchWithChunks(entryPoint, outdir, hotReloadManager = null) {
|
|
388
533
|
await cleanDirectoryExcept(outdir, 'temp');
|
|
389
534
|
try {
|
|
390
|
-
const inputOptions = await getFrameworkConfig(entryPoint, outdir, false);
|
|
535
|
+
const inputOptions = await getFrameworkConfig(entryPoint, outdir, false, true);
|
|
391
536
|
inputOptions.external = nodeBuiltIns;
|
|
537
|
+
const optimizationPlugins = getOptimizationPlugins(false);
|
|
538
|
+
inputOptions.plugins = [...inputOptions.plugins, ...optimizationPlugins];
|
|
539
|
+
const processPolyfill = `var process = { env: { NODE_ENV: "development" } };`;
|
|
392
540
|
const outputOptions = {
|
|
393
541
|
dir: outdir,
|
|
394
542
|
format: 'es',
|
|
395
543
|
entryFileNames: 'main.js',
|
|
396
|
-
|
|
544
|
+
// CHANGE: Remove hash in watch mode to prevent file accumulation in assets folder
|
|
545
|
+
assetFileNames: 'assets/[name][extname]',
|
|
546
|
+
sourcemap: true,
|
|
547
|
+
intro: processPolyfill
|
|
397
548
|
};
|
|
398
549
|
const watchOptions = {
|
|
399
550
|
...inputOptions,
|
|
@@ -414,12 +565,18 @@ async function watchWithChunks(entryPoint, outdir, hotReloadManager = null) {
|
|
|
414
565
|
async function watch(entryPoint, outfile, hotReloadManager = null) {
|
|
415
566
|
const outdir = path.dirname(outfile);
|
|
416
567
|
try {
|
|
417
|
-
const inputOptions = await getFrameworkConfig(entryPoint, outdir, false);
|
|
568
|
+
const inputOptions = await getFrameworkConfig(entryPoint, outdir, false, true);
|
|
418
569
|
inputOptions.external = nodeBuiltIns;
|
|
570
|
+
const optimizationPlugins = getOptimizationPlugins(false);
|
|
571
|
+
inputOptions.plugins = [...inputOptions.plugins, ...optimizationPlugins];
|
|
572
|
+
const processPolyfill = `var process = { env: { NODE_ENV: "development" } };`;
|
|
419
573
|
const outputOptions = {
|
|
420
574
|
file: outfile,
|
|
421
575
|
format: 'es',
|
|
422
|
-
|
|
576
|
+
// CHANGE: Remove hash in watch mode to prevent file accumulation
|
|
577
|
+
assetFileNames: 'assets/[name][extname]',
|
|
578
|
+
sourcemap: true,
|
|
579
|
+
intro: processPolyfill
|
|
423
580
|
};
|
|
424
581
|
const watchOptions = {
|
|
425
582
|
...inputOptions,
|
package/dist/helpers.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import http, { Server } from 'http';
|
|
3
3
|
import type { VattsOptions, VattsConfig } from './types';
|
|
4
|
-
import
|
|
4
|
+
import http2 from 'http2';
|
|
5
5
|
/**
|
|
6
6
|
* Carrega o arquivo de configuração vatts.config.ts ou vatts.config.js do projeto
|
|
7
7
|
* @param projectDir Diretório raiz do projeto
|
|
@@ -18,9 +18,9 @@ export declare function app(options?: VattsOptions): {
|
|
|
18
18
|
*/
|
|
19
19
|
integrate: (serverApp: any) => Promise<any>;
|
|
20
20
|
/**
|
|
21
|
-
|
|
21
|
+
* Inicia um servidor Vatts.js fechado (o usuário não tem acesso ao framework)
|
|
22
22
|
*/
|
|
23
|
-
init: () => Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse> |
|
|
23
|
+
init: () => Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse> | http2.Http2SecureServer<typeof http.IncomingMessage, typeof http.ServerResponse, typeof http2.Http2ServerRequest, typeof http2.Http2ServerResponse>>;
|
|
24
24
|
prepare: () => Promise<void>;
|
|
25
25
|
getRequestHandler: () => (req: any, res: any, next?: any) => Promise<void> | void;
|
|
26
26
|
setupWebSocket: (server: Server | any) => void;
|
package/dist/helpers.js
CHANGED
|
@@ -65,8 +65,8 @@ const path_1 = __importDefault(require("path"));
|
|
|
65
65
|
// Helpers para integração com diferentes frameworks
|
|
66
66
|
const index_js_1 = __importStar(require("./index.js")); // Importando o tipo
|
|
67
67
|
const console_1 = __importStar(require("./api/console"));
|
|
68
|
-
const
|
|
69
|
-
const fs_1 = __importDefault(require("fs"));
|
|
68
|
+
const http2_1 = __importDefault(require("http2")); // <-- ADICIONADO: Import do HTTP/2
|
|
69
|
+
const fs_1 = __importDefault(require("fs"));
|
|
70
70
|
// Registra loaders customizados para importar arquivos não-JS
|
|
71
71
|
const { registerLoaders } = require('./loaders');
|
|
72
72
|
registerLoaders({ projectDir: process.cwd() });
|
|
@@ -90,7 +90,8 @@ function getLocalExternalIp() {
|
|
|
90
90
|
}
|
|
91
91
|
const sendBox = (options) => {
|
|
92
92
|
const isDev = options.dev;
|
|
93
|
-
|
|
93
|
+
// @ts-ignore
|
|
94
|
+
const isSSL = exports.config.ssl && exports.config.ssl.key && exports.config.ssl.cert;
|
|
94
95
|
const protocol = isSSL ? 'https' : 'http';
|
|
95
96
|
const localIp = getLocalExternalIp();
|
|
96
97
|
// Estilos Clean
|
|
@@ -104,14 +105,16 @@ const sendBox = (options) => {
|
|
|
104
105
|
console.log(timer + labelStyle + ' Access on:');
|
|
105
106
|
console.log(' ');
|
|
106
107
|
// 1. Local (Alinhamento: Local tem 6 letras + 4 espaços = 10)
|
|
107
|
-
console.info(timer + `${labelStyle} ┃ Local:${console_1.Colors.Reset} ${urlStyle}${protocol}://localhost:${
|
|
108
|
+
console.info(timer + `${labelStyle} ┃ Local:${console_1.Colors.Reset} ${urlStyle}${protocol}://localhost:${exports.config?.port}${console_1.Colors.Reset}`);
|
|
108
109
|
// 2. Network (Alinhamento: Network tem 8 letras + 2 espaços = 10)
|
|
109
110
|
if (localIp) {
|
|
110
|
-
console.info(timer + `${labelStyle} ┃ Network:${console_1.Colors.Reset} ${urlStyle}${protocol}://${localIp}:${
|
|
111
|
+
console.info(timer + `${labelStyle} ┃ Network:${console_1.Colors.Reset} ${urlStyle}${protocol}://${localIp}:${exports.config?.port}${console_1.Colors.Reset}`);
|
|
111
112
|
}
|
|
112
113
|
// 3. Infos Extras (Redirect HTTP -> HTTPS)
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
// @ts-ignore
|
|
115
|
+
if (isSSL && exports.config.ssl?.redirectPort) {
|
|
116
|
+
// @ts-ignore
|
|
117
|
+
console.info(timer + `${labelStyle} ┃ Redirect:${console_1.Colors.Reset} ${labelStyle}port ${exports.config.ssl.redirectPort} ➜ https${console_1.Colors.Reset}`);
|
|
115
118
|
}
|
|
116
119
|
// 4. Info de Ambiente
|
|
117
120
|
if (isDev) {
|
|
@@ -136,7 +139,8 @@ async function loadVattsConfig(projectDir, phase) {
|
|
|
136
139
|
maxUrlLength: 2048,
|
|
137
140
|
accessLogging: true,
|
|
138
141
|
envFiles: [],
|
|
139
|
-
pathRouter: false
|
|
142
|
+
pathRouter: false,
|
|
143
|
+
port: 3000
|
|
140
144
|
};
|
|
141
145
|
try {
|
|
142
146
|
// Tenta primeiro .ts, depois .js
|
|
@@ -246,7 +250,7 @@ function applyCors(req, res, corsConfig) {
|
|
|
246
250
|
}
|
|
247
251
|
/**
|
|
248
252
|
* Middleware para parsing do body com proteções de segurança (versão melhorada).
|
|
249
|
-
*/
|
|
253
|
+
*/
|
|
250
254
|
const parseBody = (req) => {
|
|
251
255
|
// Constantes para limites de segurança
|
|
252
256
|
const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB limite total
|
|
@@ -331,7 +335,7 @@ async function initNativeServer(vattsApp, options, port, hostname) {
|
|
|
331
335
|
options.envFiles = vattsConfig.envFiles;
|
|
332
336
|
await vattsApp.prepare();
|
|
333
337
|
const handler = vattsApp.getRequestHandler();
|
|
334
|
-
const msg = console_1.default.dynamicLine(`${console_1.Colors.Bright}Starting Vatts.js on port ${
|
|
338
|
+
const msg = console_1.default.dynamicLine(`${console_1.Colors.Bright}Starting Vatts.js on port ${exports.config?.port}${console_1.Colors.Reset}`);
|
|
335
339
|
// --- LÓGICA DO LISTENER (REUTILIZÁVEL) ---
|
|
336
340
|
// Extraímos a lógica principal para uma variável
|
|
337
341
|
// para que possa ser usada tanto pelo servidor HTTP quanto HTTPS.
|
|
@@ -461,18 +465,20 @@ async function initNativeServer(vattsApp, options, port, hostname) {
|
|
|
461
465
|
}
|
|
462
466
|
};
|
|
463
467
|
// --- FIM DO LISTENER ---
|
|
464
|
-
let server; //
|
|
465
|
-
const isSSL =
|
|
466
|
-
if (isSSL &&
|
|
468
|
+
let server; // <-- ADICIONADO: Suporte a Http2SecureServer
|
|
469
|
+
const isSSL = exports.config.ssl && exports.config.ssl.key && exports.config.ssl.cert;
|
|
470
|
+
if (isSSL && exports.config.ssl) {
|
|
467
471
|
const sslOptions = {
|
|
468
|
-
key: fs_1.default.readFileSync(
|
|
469
|
-
cert: fs_1.default.readFileSync(
|
|
470
|
-
ca:
|
|
472
|
+
key: fs_1.default.readFileSync(exports.config.ssl.key, 'utf8'),
|
|
473
|
+
cert: fs_1.default.readFileSync(exports.config.ssl.cert, 'utf8'),
|
|
474
|
+
ca: exports.config.ssl.ca ? fs_1.default.readFileSync(exports.config.ssl.ca, 'utf8') : undefined,
|
|
475
|
+
allowHTTP1: true // <-- IMPORTANTE: Garante compatibilidade com clientes HTTP/1.1
|
|
471
476
|
};
|
|
472
|
-
// 1. Cria o servidor HTTPS
|
|
473
|
-
|
|
477
|
+
// 1. Cria o servidor HTTPS com HTTP/2
|
|
478
|
+
// Substituído https.createServer por http2.createSecureServer
|
|
479
|
+
server = http2_1.default.createSecureServer(sslOptions, requestListener);
|
|
474
480
|
// 2. Cria o servidor de REDIRECIONAMENTO (HTTP -> HTTPS)
|
|
475
|
-
const httpRedirectPort =
|
|
481
|
+
const httpRedirectPort = exports.config.ssl.redirectPort;
|
|
476
482
|
http_1.default.createServer((req, res) => {
|
|
477
483
|
// Evita host header injection/open redirect: prefere hostname configurado
|
|
478
484
|
const rawHost = String(req.headers['host'] || '').trim();
|
|
@@ -503,12 +509,18 @@ async function initNativeServer(vattsApp, options, port, hostname) {
|
|
|
503
509
|
}
|
|
504
510
|
// Configurações de segurança do servidor (usa configuração personalizada)
|
|
505
511
|
server.setTimeout(vattsConfig.serverTimeout || 35000); // Timeout geral do servidor
|
|
506
|
-
|
|
507
|
-
server.
|
|
508
|
-
|
|
512
|
+
// @ts-ignore (Propriedades específicas do HTTP/1, HTTP/2 gerencia isso de forma diferente, mas mantemos para o server HTTP padrão)
|
|
513
|
+
if (server.maxHeadersCount)
|
|
514
|
+
server.maxHeadersCount = vattsConfig.maxHeadersCount || 100;
|
|
515
|
+
// @ts-ignore
|
|
516
|
+
if (server.headersTimeout)
|
|
517
|
+
server.headersTimeout = vattsConfig.headersTimeout || 60000;
|
|
518
|
+
// @ts-ignore
|
|
519
|
+
if (server.requestTimeout)
|
|
520
|
+
server.requestTimeout = vattsConfig.requestTimeout || 30000;
|
|
509
521
|
server.listen(port, hostname, () => {
|
|
510
|
-
sendBox({ ...options
|
|
511
|
-
msg.end(`${console_1.Colors.Bright}Ready on port ${console_1.Colors.BgGreen} ${
|
|
522
|
+
sendBox({ ...options });
|
|
523
|
+
msg.end(`${console_1.Colors.Bright}Ready on port ${console_1.Colors.BgGreen} ${exports.config?.port} ${console_1.Colors.Reset}${console_1.Colors.Bright} in ${Date.now() - time}ms${console_1.Colors.Reset}\n`);
|
|
512
524
|
});
|
|
513
525
|
// Configura WebSocket para hot reload (Comum a ambos)
|
|
514
526
|
vattsApp.setupWebSocket(server);
|
|
@@ -574,7 +586,7 @@ function app(options = {}) {
|
|
|
574
586
|
return serverApp;
|
|
575
587
|
},
|
|
576
588
|
/**
|
|
577
|
-
|
|
589
|
+
* Inicia um servidor Vatts.js fechado (o usuário não tem acesso ao framework)
|
|
578
590
|
*/
|
|
579
591
|
init: async () => {
|
|
580
592
|
const currentVersion = require('../package.json').version;
|
|
@@ -606,7 +618,7 @@ ${console_1.Colors.Bright + console_1.Colors.FgCyan} \\ / /\\ | | /__\
|
|
|
606
618
|
${console_1.Colors.Bright + console_1.Colors.FgCyan} \\/ /~~\\ | | .__/ .${console_1.Colors.FgWhite} \\__/ .__/ ${message}
|
|
607
619
|
|
|
608
620
|
`);
|
|
609
|
-
const actualPort =
|
|
621
|
+
const actualPort = exports.config?.port || 3000;
|
|
610
622
|
const actualHostname = options.hostname || "0.0.0.0";
|
|
611
623
|
if (framework !== 'native') {
|
|
612
624
|
console_1.default.warn(`The "${framework}" framework was selected, but the init() method only works with the "native" framework. Starting native server...`);
|
package/dist/index.js
CHANGED
|
@@ -347,7 +347,7 @@ import '${relativeEntryPath}';
|
|
|
347
347
|
}
|
|
348
348
|
}
|
|
349
349
|
function vatts(options) {
|
|
350
|
-
const { dev = true, dir = process.cwd(),
|
|
350
|
+
const { dev = true, dir = process.cwd(), envFiles } = options;
|
|
351
351
|
(0, env_1.loadEnv)({ dir, dev, envFiles });
|
|
352
352
|
// @ts-ignore
|
|
353
353
|
process.vatts = options;
|
|
@@ -482,7 +482,7 @@ function vatts(options) {
|
|
|
482
482
|
genericReq.hotReloadManager = hotReloadManager;
|
|
483
483
|
const { hostname } = req.headers;
|
|
484
484
|
const method = (genericReq.method || 'GET').toUpperCase();
|
|
485
|
-
const urlObj = new URL(genericReq.url, `http://${hostname}:${port}`);
|
|
485
|
+
const urlObj = new URL(genericReq.url, `http://${hostname}:${helpers_1.config?.port}`);
|
|
486
486
|
const pathname = urlObj.pathname;
|
|
487
487
|
if (pathname === types_1.RPC_ENDPOINT && method === 'POST') {
|
|
488
488
|
try {
|
|
@@ -557,27 +557,71 @@ function vatts(options) {
|
|
|
557
557
|
const requestPath = pathname.replace('/_vatts/', '');
|
|
558
558
|
if (!isSuspiciousPathname(requestPath)) {
|
|
559
559
|
const filePath = resolveWithin(staticPath, requestPath);
|
|
560
|
+
// Verifica se existe E se é arquivo
|
|
560
561
|
if (filePath && fs_1.default.existsSync(filePath) && fs_1.default.statSync(filePath).isFile()) {
|
|
562
|
+
const stats = fs_1.default.statSync(filePath);
|
|
561
563
|
const ext = path_1.default.extname(filePath).toLowerCase();
|
|
562
564
|
const contentTypes = {
|
|
563
565
|
'.js': 'application/javascript',
|
|
564
566
|
'.css': 'text/css',
|
|
565
567
|
'.map': 'application/json',
|
|
566
|
-
'.vue': 'text/css'
|
|
568
|
+
'.vue': 'text/css',
|
|
569
|
+
'.png': 'image/png',
|
|
570
|
+
'.jpg': 'image/jpeg',
|
|
571
|
+
'.svg': 'image/svg+xml'
|
|
567
572
|
};
|
|
573
|
+
// CORREÇÃO 1: Cache diferenciado para Dev vs Produção
|
|
574
|
+
if (options.dev) {
|
|
575
|
+
// Em dev, proibimos o cache para garantir que main.js atualize sempre
|
|
576
|
+
genericRes.header('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
|
577
|
+
genericRes.header('Pragma', 'no-cache');
|
|
578
|
+
genericRes.header('Expires', '0');
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
// Em produção, mantemos o cache agressivo (assumindo hash nos arquivos exceto entry points)
|
|
582
|
+
// Dica: Se o main.js de prod também não tiver hash, use 'no-cache' nele também
|
|
583
|
+
genericRes.header('Cache-Control', 'public, max-age=31536000, immutable');
|
|
584
|
+
}
|
|
585
|
+
const lastModified = stats.mtime.toUTCString();
|
|
586
|
+
genericRes.header('Last-Modified', lastModified);
|
|
568
587
|
genericRes.header('Content-Type', contentTypes[ext] || 'text/plain');
|
|
588
|
+
// Lógica 304 (Mantida igual, pois ajuda na performance mesmo em dev se o arquivo n mudou nada)
|
|
589
|
+
const ifModifiedSince = req.headers['if-modified-since'];
|
|
590
|
+
if (ifModifiedSince) {
|
|
591
|
+
const requestDate = new Date(ifModifiedSince).getTime();
|
|
592
|
+
const fileDate = new Date(lastModified).getTime();
|
|
593
|
+
if (requestDate >= fileDate) {
|
|
594
|
+
if (adapter.type === 'express') {
|
|
595
|
+
res.status(304).end();
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
genericRes.status(304);
|
|
599
|
+
genericRes.send(null);
|
|
600
|
+
}
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// Envia o arquivo
|
|
569
605
|
if (adapter.type === 'express') {
|
|
570
606
|
res.sendFile(filePath);
|
|
571
607
|
}
|
|
572
|
-
else
|
|
608
|
+
else {
|
|
573
609
|
const fileContent = fs_1.default.readFileSync(filePath);
|
|
574
610
|
genericRes.send(fileContent);
|
|
575
611
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
612
|
+
return; // Encerra aqui com sucesso
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
// CORREÇÃO 2: Se o arquivo não existe, retornamos 404 AQUI.
|
|
616
|
+
// Isso impede que o código continue e caia na rota de renderização do HTML (SPA Fallback)
|
|
617
|
+
if (adapter.type === 'express') {
|
|
618
|
+
res.status(404).send('Vatts Asset Not Found');
|
|
579
619
|
}
|
|
580
|
-
|
|
620
|
+
else {
|
|
621
|
+
genericRes.status(404);
|
|
622
|
+
genericRes.send('Vatts Asset Not Found');
|
|
623
|
+
}
|
|
624
|
+
return; // Mata a requisição
|
|
581
625
|
}
|
|
582
626
|
}
|
|
583
627
|
}
|
|
@@ -15,6 +15,8 @@ export function createReactConfig(entryPoint: string, outdir: string, isProducti
|
|
|
15
15
|
treeshake: {
|
|
16
16
|
moduleSideEffects: string;
|
|
17
17
|
preset: string;
|
|
18
|
+
propertyReadSideEffects: boolean;
|
|
19
|
+
tryCatchDeoptimization: boolean;
|
|
18
20
|
};
|
|
19
21
|
cache: boolean;
|
|
20
22
|
perf: boolean;
|
|
@@ -32,7 +32,8 @@ const jsonPlugin = require("@rollup/plugin-json").default;
|
|
|
32
32
|
async function createReactConfig(entryPoint, outdir, isProduction, { prePlugins = [], postPlugins = [] } = {}) {
|
|
33
33
|
const replaceValues = {
|
|
34
34
|
'process.env.NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development'),
|
|
35
|
-
'process.env.PORT': JSON.stringify(process.vatts?.port || 3000)
|
|
35
|
+
'process.env.PORT': JSON.stringify(process.vatts?.port || 3000),
|
|
36
|
+
preventAssignment: true
|
|
36
37
|
};
|
|
37
38
|
const extensions = ['.mjs', '.js', '.json', '.node', '.jsx', '.tsx', '.ts'];
|
|
38
39
|
const esbuildLoaders = {
|
|
@@ -42,39 +43,47 @@ async function createReactConfig(entryPoint, outdir, isProduction, { prePlugins
|
|
|
42
43
|
};
|
|
43
44
|
return {
|
|
44
45
|
input: entryPoint,
|
|
46
|
+
// [OPTIMIZATION] Preset 'smallest' é o mais agressivo do Rollup
|
|
45
47
|
treeshake: {
|
|
46
48
|
moduleSideEffects: 'no-external',
|
|
47
|
-
preset: isProduction ? '
|
|
49
|
+
preset: isProduction ? 'smallest' : 'recommended',
|
|
50
|
+
propertyReadSideEffects: false,
|
|
51
|
+
tryCatchDeoptimization: false
|
|
48
52
|
},
|
|
49
53
|
cache: isProduction ? true : false,
|
|
50
54
|
perf: false,
|
|
51
55
|
maxParallelFileOps: 20,
|
|
52
56
|
plugins: [
|
|
53
|
-
replace(
|
|
54
|
-
preventAssignment: true,
|
|
55
|
-
values: replaceValues
|
|
56
|
-
}),
|
|
57
|
+
replace(replaceValues),
|
|
57
58
|
...prePlugins,
|
|
58
59
|
nodeResolve({
|
|
59
60
|
extensions,
|
|
60
61
|
preferBuiltins: true,
|
|
61
62
|
browser: true,
|
|
63
|
+
// [FIX] Apenas dedupe React principal para evitar conflitos, mas deixe libs internas resolverem
|
|
62
64
|
dedupe: ['react', 'react-dom']
|
|
63
65
|
}),
|
|
64
66
|
commonjs({
|
|
65
67
|
sourceMap: !isProduction,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
// [FIX] 'preferred' ajuda o Rollup a escolher o export default correto para React
|
|
69
|
+
// Isso muitas vezes resolve o erro de "undefined reading createElement"
|
|
70
|
+
requireReturnsDefault: 'preferred',
|
|
71
|
+
ignoreTryCatch: true,
|
|
72
|
+
transformMixedEsModules: true,
|
|
73
|
+
esmExternals: false
|
|
68
74
|
}),
|
|
69
75
|
...postPlugins,
|
|
70
|
-
jsonPlugin(
|
|
76
|
+
jsonPlugin({
|
|
77
|
+
compact: true
|
|
78
|
+
}),
|
|
71
79
|
esbuild({
|
|
72
80
|
include: /\.[jt]sx?$/,
|
|
73
81
|
exclude: /node_modules/,
|
|
74
82
|
sourceMap: !isProduction,
|
|
75
|
-
|
|
83
|
+
// [OPTIMIZATION] Mantemos false aqui pois o Terser (no bundler principal) fará o trabalho pesado
|
|
84
|
+
minify: false,
|
|
76
85
|
legalComments: 'none',
|
|
77
|
-
treeShaking:
|
|
86
|
+
treeShaking: true,
|
|
78
87
|
target: 'esnext',
|
|
79
88
|
jsx: 'automatic',
|
|
80
89
|
define: { __VERSION__: '"1.0.0"' },
|
|
@@ -57,22 +57,6 @@ function requireWithoutStyles(modulePath) {
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
// --- Gerenciamento de Console (Silenciador) ---
|
|
61
|
-
const originalConsole = {
|
|
62
|
-
log: console.log,
|
|
63
|
-
info: console.info,
|
|
64
|
-
debug: console.debug
|
|
65
|
-
};
|
|
66
|
-
function silenceConsole() {
|
|
67
|
-
console.log = () => { };
|
|
68
|
-
console.info = () => { };
|
|
69
|
-
console.debug = () => { };
|
|
70
|
-
}
|
|
71
|
-
function restoreConsole() {
|
|
72
|
-
console.log = originalConsole.log;
|
|
73
|
-
console.info = originalConsole.info;
|
|
74
|
-
console.debug = originalConsole.debug;
|
|
75
|
-
}
|
|
76
60
|
// --- Funções de Metadata e Scripts ---
|
|
77
61
|
function generateMetaTags(metadata) {
|
|
78
62
|
const tags = [];
|
|
@@ -173,6 +157,7 @@ function getBuildAssets(req) {
|
|
|
173
157
|
const projectDir = process.cwd();
|
|
174
158
|
const distDir = path_1.default.join(projectDir, '.vatts');
|
|
175
159
|
const assetsDir = path_1.default.join(distDir, 'assets');
|
|
160
|
+
const chunksDir = path_1.default.join(distDir, 'chunks');
|
|
176
161
|
if (!fs_1.default.existsSync(distDir))
|
|
177
162
|
return null;
|
|
178
163
|
let scripts = [];
|
|
@@ -216,6 +201,7 @@ function getBuildAssets(req) {
|
|
|
216
201
|
processDirectory(distDir, '/_vatts');
|
|
217
202
|
// Scan em .vatts/assets/ (Assets estáticos, chunks, CSS extraído)
|
|
218
203
|
processDirectory(assetsDir, '/_vatts/assets');
|
|
204
|
+
processDirectory(chunksDir, '/_vatts/chunks');
|
|
219
205
|
// Ordenação básica para garantir que o main carregue
|
|
220
206
|
scripts.sort((a, b) => {
|
|
221
207
|
if (a.includes('main'))
|
|
@@ -255,13 +241,11 @@ async function render({ req, res, route, params, allRoutes }) {
|
|
|
255
241
|
const isProduction = !req.hwebDev;
|
|
256
242
|
const hotReloadManager = req.hotReloadManager;
|
|
257
243
|
// SILENCIAR CONSOLE: Inicia o silêncio para evitar logs de renderização
|
|
258
|
-
silenceConsole();
|
|
259
244
|
try {
|
|
260
245
|
// 1. Verificar Build - Se não tiver scripts, retorna tela de Loading
|
|
261
246
|
const assets = getBuildAssets(req);
|
|
262
247
|
if (!assets || assets.scripts.length === 0) {
|
|
263
248
|
// Se falhar o build, restauramos o console para o erro aparecer se necessário
|
|
264
|
-
restoreConsole();
|
|
265
249
|
// Usando stream para a tela de loading também, agora via React Component
|
|
266
250
|
const { pipe } = (0, server_1.renderToPipeableStream)(react_1.default.createElement(BuildingPage_1.default, null), {
|
|
267
251
|
onShellReady() {
|
|
@@ -281,10 +265,7 @@ async function render({ req, res, route, params, allRoutes }) {
|
|
|
281
265
|
LayoutComponent = layoutModule.default;
|
|
282
266
|
}
|
|
283
267
|
catch (e) {
|
|
284
|
-
// Usamos console.error original aqui, pois erro de layout é crítico
|
|
285
|
-
restoreConsole();
|
|
286
268
|
console.error("Error loading layout component for SSR:", e);
|
|
287
|
-
silenceConsole(); // Volta a silenciar
|
|
288
269
|
}
|
|
289
270
|
}
|
|
290
271
|
// 3. Preparar Metadata
|
|
@@ -339,14 +320,11 @@ async function render({ req, res, route, params, allRoutes }) {
|
|
|
339
320
|
// Usar bootstrapModules para scripts tipo módulo (ESM)
|
|
340
321
|
bootstrapModules: assets.scripts,
|
|
341
322
|
onShellReady() {
|
|
342
|
-
// Restaurar console assim que o shell estiver pronto (cabeçalho enviado)
|
|
343
|
-
restoreConsole();
|
|
344
323
|
res.setHeader('Content-Type', 'text/html');
|
|
345
324
|
pipe(res);
|
|
346
325
|
resolve();
|
|
347
326
|
},
|
|
348
327
|
onShellError(error) {
|
|
349
|
-
restoreConsole(); // Restaura para mostrar o erro real
|
|
350
328
|
console.error('Streaming Shell Error:', error);
|
|
351
329
|
res.statusCode = 500;
|
|
352
330
|
res.setHeader('Content-Type', 'text/html');
|
|
@@ -363,7 +341,6 @@ async function render({ req, res, route, params, allRoutes }) {
|
|
|
363
341
|
});
|
|
364
342
|
}
|
|
365
343
|
catch (err) {
|
|
366
|
-
restoreConsole();
|
|
367
344
|
console.error("Critical Render Error:", err);
|
|
368
345
|
throw err;
|
|
369
346
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -16,15 +16,8 @@ export interface WebSocketContext {
|
|
|
16
16
|
export interface VattsOptions {
|
|
17
17
|
dev?: boolean;
|
|
18
18
|
hostname?: string;
|
|
19
|
-
port?: number;
|
|
20
19
|
dir?: string;
|
|
21
20
|
framework?: 'express' | 'fastify' | 'native';
|
|
22
|
-
ssl?: {
|
|
23
|
-
redirectPort: number;
|
|
24
|
-
key: string;
|
|
25
|
-
cert: string;
|
|
26
|
-
ca?: string;
|
|
27
|
-
};
|
|
28
21
|
envFiles?: string[];
|
|
29
22
|
}
|
|
30
23
|
/**
|
|
@@ -32,6 +25,13 @@ export interface VattsOptions {
|
|
|
32
25
|
* Essas configurações podem ser definidas no arquivo vatts.config.js
|
|
33
26
|
*/
|
|
34
27
|
export interface VattsConfig {
|
|
28
|
+
port: number;
|
|
29
|
+
ssl?: {
|
|
30
|
+
redirectPort: number;
|
|
31
|
+
key: string;
|
|
32
|
+
cert: string;
|
|
33
|
+
ca?: string;
|
|
34
|
+
};
|
|
35
35
|
/**
|
|
36
36
|
* Prefere utilizar rotas por path, sem precisar registrar?
|
|
37
37
|
* Padrão: false
|
package/dist/vue/renderer.vue.js
CHANGED
|
@@ -101,6 +101,10 @@ function extractComponentPreloads(componentPath) {
|
|
|
101
101
|
tags.add(`<link rel="preload" as="style" href="${publicUrl}">`);
|
|
102
102
|
tags.add(`<link rel="stylesheet" href="${publicUrl}">`);
|
|
103
103
|
}
|
|
104
|
+
else if (['.js'].includes(ext)) {
|
|
105
|
+
// Adicionado suporte para JS
|
|
106
|
+
tags.add(`<link rel="preload" as="script" href="${publicUrl}">`);
|
|
107
|
+
}
|
|
104
108
|
else if (['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.avif'].includes(ext)) {
|
|
105
109
|
tags.add(`<link rel="preload" as="image" href="${publicUrl}">`);
|
|
106
110
|
}
|
|
@@ -110,7 +114,8 @@ function extractComponentPreloads(componentPath) {
|
|
|
110
114
|
// - import foo from './foo.png' (Named import)
|
|
111
115
|
// - import './style.css' (Side-effect import)
|
|
112
116
|
// - require('./image.jpg') (CommonJS)
|
|
113
|
-
|
|
117
|
+
// Adicionado |js na lista de extensões
|
|
118
|
+
const importRegex = /(?:import(?:\s+[^;'"]+\s+from)?\s+|require\(\s*)['"]([^'"]+\.(png|jpg|jpeg|gif|svg|webp|avif|mp4|webm|css|js))['"]/g;
|
|
114
119
|
let match;
|
|
115
120
|
while ((match = importRegex.exec(content)) !== null) {
|
|
116
121
|
processPath(match[1]);
|
package/dist/vue/vue.build.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export function createVueConfig(entryPoint: string, outdir: string, isProduction
|
|
|
15
15
|
treeshake: {
|
|
16
16
|
moduleSideEffects: string;
|
|
17
17
|
preset: string;
|
|
18
|
+
propertyReadSideEffects: boolean;
|
|
19
|
+
tryCatchDeoptimization: boolean;
|
|
18
20
|
};
|
|
19
21
|
cache: boolean;
|
|
20
22
|
perf: boolean;
|
package/dist/vue/vue.build.js
CHANGED
|
@@ -56,7 +56,8 @@ async function createVueConfig(entryPoint, outdir, isProduction, { prePlugins =
|
|
|
56
56
|
'process.env.NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development'),
|
|
57
57
|
'process.env.PORT': JSON.stringify(process.vatts?.port || 3000),
|
|
58
58
|
'__VUE_OPTIONS_API__': JSON.stringify(true),
|
|
59
|
-
'__VUE_PROD_DEVTOOLS__': JSON.stringify(!isProduction)
|
|
59
|
+
'__VUE_PROD_DEVTOOLS__': JSON.stringify(!isProduction),
|
|
60
|
+
preventAssignment: true
|
|
60
61
|
};
|
|
61
62
|
let vuePlugin = null;
|
|
62
63
|
try {
|
|
@@ -75,8 +76,12 @@ async function createVueConfig(entryPoint, outdir, isProduction, { prePlugins =
|
|
|
75
76
|
const vueFactory = vuePkg.default || vuePkg;
|
|
76
77
|
if (typeof vueFactory === 'function') {
|
|
77
78
|
vuePlugin = vueFactory({
|
|
79
|
+
// [OPTIMIZATION] Informa explicitamente que é produção para otimizações internas do Vue
|
|
80
|
+
isProduction: isProduction,
|
|
78
81
|
compilerOptions: {
|
|
79
|
-
isCustomElement: (tag) => tag.includes('-')
|
|
82
|
+
isCustomElement: (tag) => tag.includes('-'),
|
|
83
|
+
// [FIX] Remove comentários HTML (<!-- ... -->) do template compilado
|
|
84
|
+
comments: false
|
|
80
85
|
}
|
|
81
86
|
});
|
|
82
87
|
}
|
|
@@ -93,18 +98,18 @@ async function createVueConfig(entryPoint, outdir, isProduction, { prePlugins =
|
|
|
93
98
|
};
|
|
94
99
|
return {
|
|
95
100
|
input: entryPoint,
|
|
101
|
+
// [OPTIMIZATION] Preset 'smallest' é o mais agressivo do Rollup
|
|
96
102
|
treeshake: {
|
|
97
103
|
moduleSideEffects: 'no-external',
|
|
98
|
-
preset: isProduction ? '
|
|
104
|
+
preset: isProduction ? 'smallest' : 'recommended',
|
|
105
|
+
propertyReadSideEffects: false,
|
|
106
|
+
tryCatchDeoptimization: false
|
|
99
107
|
},
|
|
100
108
|
cache: isProduction ? true : false,
|
|
101
109
|
perf: false,
|
|
102
110
|
maxParallelFileOps: 20,
|
|
103
111
|
plugins: [
|
|
104
|
-
replace(
|
|
105
|
-
preventAssignment: true,
|
|
106
|
-
values: replaceValues
|
|
107
|
-
}),
|
|
112
|
+
replace(replaceValues),
|
|
108
113
|
// Plugins de Infra (TSConfig, etc)
|
|
109
114
|
...prePlugins,
|
|
110
115
|
{
|
|
@@ -127,19 +132,25 @@ async function createVueConfig(entryPoint, outdir, isProduction, { prePlugins =
|
|
|
127
132
|
vueScriptFixPlugin(),
|
|
128
133
|
commonjs({
|
|
129
134
|
sourceMap: !isProduction,
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
// [FIX] 'preferred' ajuda o Rollup a escolher o export default correto para Vue/Libs
|
|
136
|
+
requireReturnsDefault: 'preferred',
|
|
137
|
+
ignoreTryCatch: true,
|
|
138
|
+
transformMixedEsModules: true,
|
|
139
|
+
esmExternals: false // Garante que deps CommonJS sejam empacotadas
|
|
132
140
|
}),
|
|
133
141
|
// Plugins de Assets/CSS rodam DEPOIS do Vue ter gerado os arquivos virtuais de estilo
|
|
134
142
|
...postPlugins,
|
|
135
|
-
jsonPlugin(
|
|
143
|
+
jsonPlugin({
|
|
144
|
+
compact: true
|
|
145
|
+
}),
|
|
136
146
|
esbuild({
|
|
137
147
|
include: /\.[jt]sx?$|\.vue\?vue.*lang\.ts/,
|
|
138
148
|
exclude: /node_modules/,
|
|
139
149
|
sourceMap: !isProduction,
|
|
140
|
-
|
|
150
|
+
// [OPTIMIZATION] Mantemos false aqui pois o Terser (no bundler principal) fará o trabalho pesado
|
|
151
|
+
minify: false,
|
|
141
152
|
legalComments: 'none',
|
|
142
|
-
treeShaking:
|
|
153
|
+
treeShaking: true,
|
|
143
154
|
target: 'esnext',
|
|
144
155
|
jsx: 'automatic',
|
|
145
156
|
define: { __VERSION__: '"1.0.0"' },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vatts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1-alpha.1",
|
|
4
4
|
"description": "Vatts.js is a high-level framework for building web applications with ease and speed. It provides a robust set of tools and features to streamline development and enhance productivity.",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"author": "mfraz",
|
|
@@ -105,6 +105,7 @@
|
|
|
105
105
|
"@rollup/plugin-json": "^6.1.0",
|
|
106
106
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
107
107
|
"@rollup/plugin-replace": "^6.0.3",
|
|
108
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
108
109
|
"@vue/server-renderer": "^3.5.27",
|
|
109
110
|
"chokidar": "^3.6.0",
|
|
110
111
|
"commander": "^14.0.2",
|