vatts 2.0.2 → 2.1.0-canary.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.
Files changed (40) hide show
  1. package/LICENSE +12 -12
  2. package/README.md +63 -63
  3. package/dist/api/console.d.ts +28 -5
  4. package/dist/api/console.js +305 -137
  5. package/dist/api/native-server.js +25 -4
  6. package/dist/bin/vatts.js +192 -4
  7. package/dist/builder.js +70 -69
  8. package/dist/core-go/core-linux-arm64.node +0 -0
  9. package/dist/core-go/core-linux-x64.node +0 -0
  10. package/dist/core-go/core-win-x64.node +0 -0
  11. package/dist/global/global.d.ts +176 -176
  12. package/dist/helpers.d.ts +2 -2
  13. package/dist/helpers.js +20 -41
  14. package/dist/hotReload.js +205 -205
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.js +104 -19
  17. package/dist/loaders.js +15 -15
  18. package/dist/react/BuildingPage.js +202 -202
  19. package/dist/react/DefaultNotFound.js +16 -16
  20. package/dist/react/DevIndicator.js +101 -101
  21. package/dist/react/entry.client.js +7 -8
  22. package/dist/react/image/Image.js +1 -1
  23. package/dist/react/renderer-react.js +270 -33
  24. package/dist/react/server-error.d.ts +8 -0
  25. package/dist/react/server-error.js +346 -0
  26. package/dist/router.js +1 -65
  27. package/dist/types.d.ts +1 -5
  28. package/dist/utils/utils.d.ts +1 -0
  29. package/dist/utils/utils.js +68 -0
  30. package/dist/vue/App.vue +191 -191
  31. package/dist/vue/BuildingPage.vue +280 -280
  32. package/dist/vue/DefaultNotFound.vue +328 -328
  33. package/dist/vue/DevIndicator.vue +225 -225
  34. package/dist/vue/ErrorModal.vue +316 -316
  35. package/dist/vue/Link.vue +38 -38
  36. package/dist/vue/entry.client.js +7 -7
  37. package/dist/vue/image/Image.vue +106 -106
  38. package/dist/vue/renderer.vue.js +266 -46
  39. package/dist/vue/server-error.vue +351 -0
  40. package/package.json +1 -1
package/dist/bin/vatts.js CHANGED
@@ -27,18 +27,103 @@ const ConsoleModule = require('../api/console');
27
27
  const { loadVattsConfig, config, setConfig } = require("../helpers");
28
28
  const { default: detectFramework } = require("../api/framework");
29
29
  const { renderAsStream } = require("../renderer");
30
+ const { showTitle } = require("../utils/utils");
30
31
  const Console = ConsoleModule.default;
31
32
  const { Levels, Colors } = ConsoleModule;
32
33
  program
33
34
  .version('1.0.0')
34
35
  .description('CLI to manage the application.');
35
36
  // --- Helpers ---
36
- function initializeApp(options, isDev) {
37
+ /**
38
+ * Cria um arquivo de marcação de build de produção
39
+ */
40
+ function createBuildInfoFile(projectDir) {
41
+ const buildInfoPath = path.join(projectDir, '.vatts', '.build-info.json');
42
+ const buildInfo = {
43
+ type: 'production',
44
+ timestamp: new Date().toISOString(),
45
+ nodeVersion: process.version,
46
+ platform: process.platform,
47
+ arch: process.arch,
48
+ vattsVersion: '1.0.0', // Pode pegar do package.json se necessário
49
+ buildDate: new Date().getTime()
50
+ };
51
+ try {
52
+ fs.mkdirSync(path.dirname(buildInfoPath), { recursive: true });
53
+ fs.writeFileSync(buildInfoPath, JSON.stringify(buildInfo, null, 2), 'utf8');
54
+ }
55
+ catch (error) {
56
+ Console.error('Failed to create build info file:', error);
57
+ }
58
+ }
59
+ /**
60
+ * Verifica se existe uma build de produção válida
61
+ */
62
+ function hasValidProductionBuild(projectDir) {
63
+ const buildInfoPath = path.join(projectDir, '.vatts', '.build-info.json');
64
+ const buildDir = path.join(projectDir, '.vatts');
65
+ // Verifica se o diretório de build existe e tem conteúdo
66
+ if (!fs.existsSync(buildDir)) {
67
+ return false;
68
+ }
69
+ // Verifica se o arquivo de info de build existe
70
+ if (!fs.existsSync(buildInfoPath)) {
71
+ return false;
72
+ }
73
+ try {
74
+ const buildInfo = JSON.parse(fs.readFileSync(buildInfoPath, 'utf8'));
75
+ // Verifica se é uma build de produção
76
+ if (buildInfo.type !== 'production') {
77
+ return false;
78
+ }
79
+ // Verifica se tem arquivos no diretório (além do .build-info.json)
80
+ const files = fs.readdirSync(buildDir);
81
+ if (files.length <= 1) {
82
+ return false;
83
+ }
84
+ return true;
85
+ }
86
+ catch (error) {
87
+ Console.warn('Invalid build info file:', error.message);
88
+ return false;
89
+ }
90
+ }
91
+ /**
92
+ * Obtém informações da build de produção
93
+ */
94
+ function getBuildInfo(projectDir) {
95
+ const buildInfoPath = path.join(projectDir, '.vatts', '.build-info.json');
96
+ try {
97
+ if (fs.existsSync(buildInfoPath)) {
98
+ return JSON.parse(fs.readFileSync(buildInfoPath, 'utf8'));
99
+ }
100
+ }
101
+ catch (error) {
102
+ Console.warn('Could not read build info:', error.message);
103
+ }
104
+ return null;
105
+ }
106
+ /**
107
+ * Remove o arquivo de info de build (usado no modo dev)
108
+ */
109
+ function removeBuildInfoFile(projectDir) {
110
+ const buildInfoPath = path.join(projectDir, '.vatts', '.build-info.json');
111
+ try {
112
+ if (fs.existsSync(buildInfoPath)) {
113
+ fs.unlinkSync(buildInfoPath);
114
+ }
115
+ }
116
+ catch (error) {
117
+ // Silencioso, não é crítico
118
+ }
119
+ }
120
+ function initializeApp(options, isDev, skipBuild = false) {
37
121
  const appOptions = {
38
122
  dev: isDev,
39
123
  port: options.port,
40
124
  hostname: options.hostname,
41
125
  framework: 'native',
126
+ skipBuild: skipBuild,
42
127
  };
43
128
  const helperModule = require("../helpers");
44
129
  const helper = helperModule.default(appOptions);
@@ -72,9 +157,42 @@ program
72
157
  .option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
73
158
  .option('--ssl', 'Activates HTTPS/SSL mode')
74
159
  .option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
75
- .action((options) => {
160
+ .action(async (options) => {
161
+ await showTitle();
162
+ const projectDir = process.cwd();
163
+ // Remove o arquivo de build info se existir (modo dev não usa builds de produção)
164
+ removeBuildInfoFile(projectDir);
76
165
  initializeApp(options, true);
77
166
  });
167
+ program
168
+ .command('build')
169
+ .description('Builds the application for production.')
170
+ .action(async (options) => {
171
+ await showTitle();
172
+ process.env.NODE_ENV = 'production';
173
+ const projectDir = process.cwd();
174
+ Console.info('Starting production build...');
175
+ try {
176
+ const { loadVattsConfig, setConfig } = require("../helpers");
177
+ setConfig(await loadVattsConfig(projectDir, 'production'));
178
+ const helperModule = require("../helpers");
179
+ const app = helperModule.default({
180
+ dev: false,
181
+ port: 3000,
182
+ hostname: '0.0.0.0',
183
+ framework: 'native'
184
+ });
185
+ await app.prepare();
186
+ // Cria o arquivo de informações da build
187
+ createBuildInfoFile(projectDir);
188
+ Console.success('Production build completed successfully!');
189
+ Console.info('You can now start the application with: vatts start');
190
+ }
191
+ catch (error) {
192
+ Console.error('Build failed:', error);
193
+ process.exit(1);
194
+ }
195
+ });
78
196
  program
79
197
  .command('start')
80
198
  .description('Starts the application in production mode.')
@@ -82,9 +200,78 @@ program
82
200
  .option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
83
201
  .option('--ssl', 'Activates HTTPS/SSL mode')
84
202
  .option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
85
- .action((options) => {
203
+ .option('--build', 'Force rebuild before starting')
204
+ .action(async (options) => {
205
+ await showTitle();
86
206
  process.env.NODE_ENV = 'production';
87
- initializeApp(options, false);
207
+ const projectDir = process.cwd();
208
+ const buildDir = path.join(projectDir, '.vatts');
209
+ // Verifica se existe uma build de produção válida
210
+ const hasValidBuild = hasValidProductionBuild(projectDir);
211
+ if (!hasValidBuild || options.build) {
212
+ if (!hasValidBuild) {
213
+ const dim = Colors.Dim;
214
+ const bright = Colors.Bright;
215
+ const title = bright + Colors.FgCyan;
216
+ const err = bright + Colors.FgRed;
217
+ const label = Colors.FgGray;
218
+ const cmd = bright + Colors.FgCyan;
219
+ const reset = Colors.Reset;
220
+ const line = `${dim}${"━".repeat(70)}${reset}`;
221
+ Console.logCustomLevel("", false, undefined, `${err}✖ No valid production build found!${reset}`, `${label}It looks like there is no build output to run in production.${reset}`, " ", `${label}Build the application first:${reset}`, ` ${cmd}vatts build${reset}`, " ", `${label}Or build and start in one command:${reset}`, ` ${cmd}vatts start --build${reset}`);
222
+ process.exitCode = 0;
223
+ return;
224
+ }
225
+ else {
226
+ Console.info('Rebuilding application...');
227
+ }
228
+ try {
229
+ const { loadVattsConfig, setConfig } = require("../helpers");
230
+ setConfig(await loadVattsConfig(projectDir, 'production'));
231
+ const helperModule = require("../helpers");
232
+ const app = helperModule.default({
233
+ dev: false,
234
+ port: options.port || 3000,
235
+ hostname: options.hostname || '0.0.0.0',
236
+ framework: 'native'
237
+ });
238
+ await app.prepare();
239
+ // Cria o arquivo de informações da build
240
+ createBuildInfoFile(projectDir);
241
+ Console.success('Build completed!');
242
+ }
243
+ catch (error) {
244
+ Console.error('Build failed:', error);
245
+ process.exitCode = 0;
246
+ return;
247
+ }
248
+ // Inicia a aplicação sem rebuildar (já foi buildado acima)
249
+ initializeApp(options, false, true);
250
+ }
251
+ else {
252
+ // Mostra informações da build existente
253
+ const buildInfo = getBuildInfo(projectDir);
254
+ if (buildInfo) {
255
+ const dim = Colors.Dim;
256
+ const bright = Colors.Bright;
257
+ const title = bright + Colors.FgCyan;
258
+ const ok = bright + Colors.FgGreen;
259
+ const label = Colors.FgGray;
260
+ const value = bright + Colors.FgWhite;
261
+ const reset = Colors.Reset;
262
+ const sysLocale = process.env.LC_ALL ||
263
+ process.env.LC_TIME ||
264
+ process.env.LANG ||
265
+ undefined;
266
+ const date = new Date(buildInfo.buildDate).toLocaleString(sysLocale, {
267
+ dateStyle: 'full',
268
+ timeStyle: 'medium'
269
+ });
270
+ Console.logCustomLevel("", false, undefined, `${ok}✔${reset} ${bright}Using existing production build${reset}`, " ", `${label}Built on:${reset} ${value}${date}${reset}`, `${label}Node version:${reset} ${value}${buildInfo.nodeVersion}${reset}`, `${label}Platform:${reset} ${value}${buildInfo.platform}-${buildInfo.arch}${reset}`, " ");
271
+ }
272
+ // Inicia a aplicação sem rebuildar
273
+ initializeApp(options, false, true);
274
+ }
88
275
  });
89
276
  program
90
277
  .command('export')
@@ -94,6 +281,7 @@ program
94
281
  .option('--no-html', 'Do not generate index.html (assets only)')
95
282
  .option('--output-h-data <file>', 'Write the data-h value into this file')
96
283
  .action(async (options) => {
284
+ await showTitle();
97
285
  process.env.NODE_ENV = 'production';
98
286
  const projectDir = process.cwd();
99
287
  const outputInput = (typeof options.output === 'string' && options.output.trim()) || 'exported';
package/dist/builder.js CHANGED
@@ -28,7 +28,7 @@ const path = require('path');
28
28
  const Console = require("./api/console").default;
29
29
  const fs = require('fs');
30
30
  const crypto = require('crypto');
31
- const { readdir, stat, rm, rename } = require("node:fs/promises");
31
+ const { readdir, stat, rm, rename, readFile } = require("node:fs/promises");
32
32
  const { loadTsConfigPaths, resolveTsConfigAlias } = require('./tsconfigPaths');
33
33
  const { config } = require("./helpers");
34
34
  // --- Optimization Plugins ---
@@ -46,7 +46,7 @@ const { createVueConfig } = require('./vue/vue.build');
46
46
  const excludedFiles = ['vatts.sock'];
47
47
  // --- Virtual Entry Plugin ---
48
48
  const virtualEntryPlugin = (options) => {
49
- const { routes, layout, notFound, framework, projectDir, pathRouter } = options;
49
+ const { routes, layout, notFound, framework } = options;
50
50
  const virtualEntryId = 'virtual:vatts-entry';
51
51
  const resolvedEntryId = '\0' + virtualEntryId;
52
52
  return {
@@ -80,33 +80,12 @@ const virtualEntryPlugin = (options) => {
80
80
  const defaultNotFoundFilename = framework === 'vue' ? 'DefaultNotFound.vue' : 'DefaultNotFound.js';
81
81
  const defaultNotFoundPath = path.join(__dirname, framework, defaultNotFoundFilename).replace(/\\/g, '/');
82
82
  // Component Registration Logic
83
- let componentRegistration;
84
- if (pathRouter === true) {
85
- componentRegistration = routes
86
- .map((route, index) => {
87
- const key = route.componentPath.replace(/\\/g, '/');
88
- return ` '${key}': route${index} || route${index}.default,`;
89
- })
90
- .join('\n');
91
- }
92
- else {
93
- if (framework === 'vue') {
94
- componentRegistration = routes
95
- .map((route, index) => {
96
- const key = route.componentPath.replace(/\\/g, '/');
97
- return ` '${key}': route${index} || route${index}.default,`;
98
- })
99
- .join('\n');
100
- }
101
- else {
102
- componentRegistration = routes
103
- .map((route, index) => {
104
- const key = route.componentPath.replace(/\\/g, '/');
105
- return ` '${key}': route${index} || route${index}.default,`;
106
- })
107
- .join('\n');
108
- }
109
- }
83
+ let componentRegistration = routes
84
+ .map((route, index) => {
85
+ const key = route.componentPath.replace(/\\/g, '/');
86
+ return ` '${key}': route${index} || route${index}.default,`;
87
+ })
88
+ .join('\n');
110
89
  const layoutRegistration = layout
111
90
  ? `window.__VATTS_LAYOUT__ = LayoutComponent.default || LayoutComponent;`
112
91
  : `window.__VATTS_LAYOUT__ = null;`;
@@ -117,22 +96,22 @@ const virtualEntryPlugin = (options) => {
117
96
  // [FIX] Removido 'dist' extra do caminho.
118
97
  const entryClientFilename = 'entry.client.js';
119
98
  const entryClientPath = path.join(__dirname, framework, entryClientFilename).replace(/\\/g, '/');
120
- return `// Arquivo gerado virtualmente pelo vatts
121
- ${imports}
122
- ${layoutImport}
123
- ${notFoundImport}
124
- import DefaultNotFound from '${defaultNotFoundPath}';
125
-
126
- window.__VATTS_COMPONENTS__ = {
127
- ${componentRegistration}
128
- };
129
-
130
- ${layoutRegistration}
131
- ${notFoundRegistration}
132
-
133
- window.__VATTS_DEFAULT_NOT_FOUND__ = DefaultNotFound;
134
-
135
- import '${entryClientPath}';
99
+ return `// Arquivo gerado virtualmente pelo vatts
100
+ ${imports}
101
+ ${layoutImport}
102
+ ${notFoundImport}
103
+ import DefaultNotFound from '${defaultNotFoundPath}';
104
+
105
+ window.__VATTS_COMPONENTS__ = {
106
+ ${componentRegistration}
107
+ };
108
+
109
+ ${layoutRegistration}
110
+ ${notFoundRegistration}
111
+
112
+ window.__VATTS_DEFAULT_NOT_FOUND__ = DefaultNotFound;
113
+
114
+ import '${entryClientPath}';
136
115
  `;
137
116
  }
138
117
  return null;
@@ -172,7 +151,10 @@ const customPostCssPlugin = (isProduction, isWatch = false) => {
172
151
  let cachedProcessor = null;
173
152
  let configLoaded = false;
174
153
  const initPostCss = async (projectDir) => {
175
- if (configLoaded)
154
+ // [FIX] Se estiver em modo Watch, NÃO retorna o processador cacheado.
155
+ // Isso força o PostCSS/Tailwind a reinicializar e escanear os arquivos novamente
156
+ // para encontrar classes novas (JIT/Arbitrary values).
157
+ if (configLoaded && !isWatch)
176
158
  return cachedProcessor;
177
159
  process.env.NODE_ENV = isProduction ? 'production' : 'development';
178
160
  const postcssConfigPath = path.join(projectDir, 'postcss.config.js');
@@ -194,6 +176,8 @@ const customPostCssPlugin = (isProduction, isWatch = false) => {
194
176
  }
195
177
  }
196
178
  if (postcss) {
179
+ // Limpa o cache do require para garantir que a config seja lida novamente se editada (opcional, mas seguro)
180
+ delete require.cache[require.resolve(configPath)];
197
181
  const config = require(__rewriteRelativeImportExtension(configPath));
198
182
  const postcssConfig = config.default || config;
199
183
  const plugins = [];
@@ -235,6 +219,22 @@ const customPostCssPlugin = (isProduction, isWatch = false) => {
235
219
  };
236
220
  return {
237
221
  name: 'custom-postcss-plugin',
222
+ // [FIX] Hook load adicionado para modo watch.
223
+ // Ele lê o CSS e injeta um comentário com timestamp. Isso faz o Rollup pensar que o arquivo mudou,
224
+ // forçando a execução do transform(), que por sua vez roda o Tailwind novamente.
225
+ async load(id) {
226
+ if (isWatch && id.endsWith('.css')) {
227
+ try {
228
+ const content = await readFile(id, 'utf8');
229
+ // Injeta comentário para invalidar cache do Rollup para este arquivo
230
+ return content + `\n/* v-watch-${Date.now()} */`;
231
+ }
232
+ catch (e) {
233
+ return null; // Deixa o Rollup carregar normalmente se falhar
234
+ }
235
+ }
236
+ return null; // Comportamento padrão para build normal ou outros arquivos
237
+ },
238
238
  async transform(code, id) {
239
239
  if (!id.endsWith('.css'))
240
240
  return null;
@@ -245,6 +245,7 @@ const customPostCssPlugin = (isProduction, isWatch = false) => {
245
245
  let processedCss = code;
246
246
  if (processor) {
247
247
  try {
248
+ // Sempre passa o 'from' para que o Tailwind saiba onde está o arquivo
248
249
  const result = await processor.process(code, { from: id, to: id, map: false });
249
250
  processedCss = result.css;
250
251
  }
@@ -258,23 +259,23 @@ const customPostCssPlugin = (isProduction, isWatch = false) => {
258
259
  const safeId = uniqueName.replace(/[^a-zA-Z0-9-_]/g, '_');
259
260
  const referenceId = this.emitFile({ type: 'asset', name: uniqueName, source: processedCss });
260
261
  return {
261
- code: `
262
- const cssUrl = String(import.meta.ROLLUP_FILE_URL_${referenceId});
263
- if (typeof document !== 'undefined') {
264
- const linkId = 'vatts-css-' + "${safeId}";
265
- let link = document.getElementById(linkId);
266
-
267
- if (!link) {
268
- link = document.createElement('link');
269
- link.id = linkId;
270
- link.rel = 'stylesheet';
271
- document.head.appendChild(link);
272
- }
273
-
274
- const timestamp = ${isWatch ? 'Date.now()' : 'null'};
275
- link.href = timestamp ? (cssUrl + '?t=' + timestamp) : cssUrl;
276
- }
277
- export default cssUrl;
262
+ code: `
263
+ const cssUrl = String(import.meta.ROLLUP_FILE_URL_${referenceId});
264
+ if (typeof document !== 'undefined') {
265
+ const linkId = 'vatts-css-' + "${safeId}";
266
+ let link = document.getElementById(linkId);
267
+
268
+ if (!link) {
269
+ link = document.createElement('link');
270
+ link.id = linkId;
271
+ link.rel = 'stylesheet';
272
+ document.head.appendChild(link);
273
+ }
274
+
275
+ const timestamp = ${isWatch ? 'Date.now()' : 'null'};
276
+ link.href = timestamp ? (cssUrl + '?t=' + timestamp) : cssUrl;
277
+ }
278
+ export default cssUrl;
278
279
  `,
279
280
  map: { mappings: '' }
280
281
  };
@@ -317,17 +318,17 @@ const smartAssetPlugin = (isProduction) => {
317
318
  if (size < INLINE_LIMIT) {
318
319
  const content = buffer.toString('utf8');
319
320
  const base64 = buffer.toString('base64');
320
- return `
321
- export default "data:image/svg+xml;base64,${base64}";
322
- export const svgContent = ${JSON.stringify(content)};
321
+ return `
322
+ export default "data:image/svg+xml;base64,${base64}";
323
+ export const svgContent = ${JSON.stringify(content)};
323
324
  `;
324
325
  }
325
326
  else {
326
327
  const referenceId = this.emitFile({ type: 'asset', name: uniqueName, source: buffer });
327
328
  const content = buffer.toString('utf8');
328
- return `
329
- export default String(import.meta.ROLLUP_FILE_URL_${referenceId});
330
- export const svgContent = ${JSON.stringify(content)};
329
+ return `
330
+ export default String(import.meta.ROLLUP_FILE_URL_${referenceId});
331
+ export const svgContent = ${JSON.stringify(content)};
331
332
  `;
332
333
  }
333
334
  }
Binary file
Binary file
Binary file