hightjs 0.4.0 → 0.5.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 (117) hide show
  1. package/README.md +42 -126
  2. package/dist/bin/hightjs.js +51 -23
  3. package/dist/builder.js +139 -4
  4. package/dist/client/DefaultNotFound.d.ts +1 -1
  5. package/dist/client/DefaultNotFound.js +72 -46
  6. package/dist/client/client.d.ts +3 -0
  7. package/dist/{client.js → client/client.js} +4 -4
  8. package/dist/client/entry.client.js +39 -29
  9. package/dist/global/global.d.ts +117 -0
  10. package/dist/{auth/types.js → global/global.js} +0 -1
  11. package/dist/helpers.js +80 -2
  12. package/dist/hotReload.js +2 -2
  13. package/dist/index.js +16 -16
  14. package/dist/loaders.d.ts +1 -0
  15. package/dist/loaders.js +46 -0
  16. package/dist/renderer.js +158 -4
  17. package/dist/types.d.ts +44 -0
  18. package/package.json +37 -31
  19. package/src/bin/hightjs.js +59 -29
  20. package/src/builder.js +163 -4
  21. package/src/client/DefaultNotFound.tsx +88 -53
  22. package/src/{client.ts → client/client.ts} +4 -3
  23. package/src/client/entry.client.tsx +44 -29
  24. package/src/global/global.ts +171 -0
  25. package/src/helpers.ts +91 -2
  26. package/src/hotReload.ts +2 -2
  27. package/src/index.ts +2 -0
  28. package/src/loaders.js +53 -0
  29. package/src/renderer.tsx +162 -4
  30. package/src/types.ts +51 -0
  31. package/.idea/HightJS.iml +0 -9
  32. package/.idea/copilot.data.migration.agent.xml +0 -6
  33. package/.idea/copilot.data.migration.ask.xml +0 -6
  34. package/.idea/copilot.data.migration.ask2agent.xml +0 -6
  35. package/.idea/copilot.data.migration.edit.xml +0 -6
  36. package/.idea/copilotDiffState.xml +0 -67
  37. package/.idea/inspectionProfiles/Project_Default.xml +0 -13
  38. package/.idea/libraries/test_package.xml +0 -9
  39. package/.idea/libraries/ts_commonjs_default_export.xml +0 -9
  40. package/.idea/misc.xml +0 -7
  41. package/.idea/modules.xml +0 -8
  42. package/.idea/vcs.xml +0 -6
  43. package/dist/auth/client.d.ts +0 -24
  44. package/dist/auth/client.js +0 -146
  45. package/dist/auth/components.d.ts +0 -29
  46. package/dist/auth/components.js +0 -100
  47. package/dist/auth/core.d.ts +0 -55
  48. package/dist/auth/core.js +0 -189
  49. package/dist/auth/index.d.ts +0 -7
  50. package/dist/auth/index.js +0 -45
  51. package/dist/auth/jwt.d.ts +0 -41
  52. package/dist/auth/jwt.js +0 -185
  53. package/dist/auth/providers/credentials.d.ts +0 -60
  54. package/dist/auth/providers/credentials.js +0 -97
  55. package/dist/auth/providers/discord.d.ts +0 -63
  56. package/dist/auth/providers/discord.js +0 -190
  57. package/dist/auth/providers/google.d.ts +0 -63
  58. package/dist/auth/providers/google.js +0 -186
  59. package/dist/auth/providers/index.d.ts +0 -2
  60. package/dist/auth/providers/index.js +0 -35
  61. package/dist/auth/providers.d.ts +0 -3
  62. package/dist/auth/providers.js +0 -26
  63. package/dist/auth/react/index.d.ts +0 -6
  64. package/dist/auth/react/index.js +0 -48
  65. package/dist/auth/react.d.ts +0 -22
  66. package/dist/auth/react.js +0 -199
  67. package/dist/auth/routes.d.ts +0 -16
  68. package/dist/auth/routes.js +0 -152
  69. package/dist/auth/types.d.ts +0 -76
  70. package/dist/client.d.ts +0 -3
  71. package/docs/README.md +0 -58
  72. package/docs/arquivos-especiais.md +0 -10
  73. package/docs/autenticacao.md +0 -212
  74. package/docs/checklist.md +0 -9
  75. package/docs/cli.md +0 -72
  76. package/docs/config.md +0 -216
  77. package/docs/estrutura.md +0 -20
  78. package/docs/faq.md +0 -10
  79. package/docs/hot-reload.md +0 -5
  80. package/docs/integracoes.md +0 -240
  81. package/docs/middlewares.md +0 -73
  82. package/docs/rotas-backend.md +0 -45
  83. package/docs/rotas-frontend.md +0 -66
  84. package/docs/seguranca.md +0 -8
  85. package/docs/websocket.md +0 -45
  86. package/example/certs/cert.pem +0 -20
  87. package/example/certs/key.pem +0 -27
  88. package/example/hightjs.config.ts +0 -87
  89. package/example/package-lock.json +0 -1174
  90. package/example/package.json +0 -26
  91. package/example/postcss.config.js +0 -8
  92. package/example/src/backend/auth.ts +0 -42
  93. package/example/src/backend/routes/auth.ts +0 -3
  94. package/example/src/backend/routes/version.ts +0 -13
  95. package/example/src/web/components/Home.tsx +0 -140
  96. package/example/src/web/components/LoginPage.tsx +0 -149
  97. package/example/src/web/globals.css +0 -5
  98. package/example/src/web/layout.tsx +0 -100
  99. package/example/src/web/routes/index.tsx +0 -13
  100. package/example/src/web/routes/login.tsx +0 -30
  101. package/example/tailwind.config.js +0 -12
  102. package/example/tsconfig.json +0 -15
  103. package/src/auth/client.ts +0 -171
  104. package/src/auth/components.tsx +0 -125
  105. package/src/auth/core.ts +0 -215
  106. package/src/auth/index.ts +0 -25
  107. package/src/auth/jwt.ts +0 -210
  108. package/src/auth/providers/credentials.ts +0 -139
  109. package/src/auth/providers/discord.ts +0 -239
  110. package/src/auth/providers/google.ts +0 -234
  111. package/src/auth/providers/index.ts +0 -20
  112. package/src/auth/providers.ts +0 -20
  113. package/src/auth/react/index.ts +0 -25
  114. package/src/auth/react.tsx +0 -234
  115. package/src/auth/routes.ts +0 -183
  116. package/src/auth/types.ts +0 -108
  117. package/tsconfig.json +0 -17
@@ -31,14 +31,53 @@ interface AppProps {
31
31
  function App({ componentMap, routes, initialComponentPath, initialParams, layoutComponent }: AppProps) {
32
32
  // Estado que guarda o componente a ser renderizado atualmente
33
33
  const [hmrTimestamp, setHmrTimestamp] = useState(Date.now());
34
+
35
+ // Helper para encontrar rota baseado no path
36
+ const findRouteForPath = useCallback((path: string) => {
37
+ for (const route of routes) {
38
+ const regexPattern = route.pattern
39
+ // [[...param]] → opcional catch-all
40
+ .replace(/\[\[\.\.\.(\w+)\]\]/g, '(?<$1>.+)?')
41
+ // [...param] → obrigatório catch-all
42
+ .replace(/\[\.\.\.(\w+)\]/g, '(?<$1>.+)')
43
+ // /[[param]] → opcional com barra também opcional
44
+ .replace(/\/\[\[(\w+)\]\]/g, '(?:/(?<$1>[^/]+))?')
45
+ // [[param]] → segmento opcional (sem barra anterior)
46
+ .replace(/\[\[(\w+)\]\]/g, '(?<$1>[^/]+)?')
47
+ // [param] → segmento obrigatório
48
+ .replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
49
+ const regex = new RegExp(`^${regexPattern}/?$`);
50
+ const match = path.match(regex);
51
+ if (match) {
52
+ return {
53
+ componentPath: route.componentPath,
54
+ params: match.groups || {}
55
+ };
56
+ }
57
+ }
58
+ return null;
59
+ }, [routes]);
60
+
61
+ // Inicializa o componente e params baseado na URL ATUAL (não no initialComponentPath)
34
62
  const [CurrentPageComponent, setCurrentPageComponent] = useState(() => {
35
- // Se for a rota especial __404__, não busca no componentMap
36
- if (initialComponentPath === '__404__') {
37
- return null;
63
+ // Pega a rota atual da URL
64
+ const currentPath = window.location.pathname;
65
+ const match = findRouteForPath(currentPath);
66
+
67
+ if (match) {
68
+ return componentMap[match.componentPath];
38
69
  }
39
- return componentMap[initialComponentPath];
70
+
71
+ // Se não encontrou rota, retorna null para mostrar 404
72
+ return null;
73
+ });
74
+
75
+ const [params, setParams] = useState(() => {
76
+ // Pega os params da URL atual
77
+ const currentPath = window.location.pathname;
78
+ const match = findRouteForPath(currentPath);
79
+ return match ? match.params : {};
40
80
  });
41
- const [params, setParams] = useState(initialParams);
42
81
 
43
82
  // HMR: Escuta eventos de hot reload
44
83
  useEffect(() => {
@@ -106,30 +145,6 @@ function App({ componentMap, routes, initialComponentPath, initialParams, layout
106
145
  };
107
146
  }, []);
108
147
 
109
- const findRouteForPath = useCallback((path: string) => {
110
- for (const route of routes) {
111
- const regexPattern = route.pattern
112
- // [[...param]] → opcional catch-all
113
- .replace(/\[\[\.\.\.(\w+)\]\]/g, '(?<$1>.+)?')
114
- // [...param] → obrigatório catch-all
115
- .replace(/\[\.\.\.(\w+)\]/g, '(?<$1>.+)')
116
- // /[[param]] → opcional com barra também opcional
117
- .replace(/\/\[\[(\w+)\]\]/g, '(?:/(?<$1>[^/]+))?')
118
- // [[param]] → segmento opcional (sem barra anterior)
119
- .replace(/\[\[(\w+)\]\]/g, '(?<$1>[^/]+)?')
120
- // [param] → segmento obrigatório
121
- .replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
122
- const regex = new RegExp(`^${regexPattern}/?$`);
123
- const match = path.match(regex);
124
- if (match) {
125
- return {
126
- componentPath: route.componentPath,
127
- params: match.groups || {}
128
- };
129
- }
130
- }
131
- return null;
132
- }, [routes]);
133
148
 
134
149
  const updateRoute = useCallback(() => {
135
150
  const currentPath = router.pathname;
@@ -0,0 +1,171 @@
1
+ /*
2
+ * This file is part of the HightJS Project.
3
+ * Copyright (c) 2025 itsmuzin
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ /**
19
+ * Type declarations for asset imports
20
+ * This allows TypeScript to understand imports of various file types
21
+ */
22
+
23
+ // Markdown files
24
+ declare module "*.md" {
25
+ const content: string;
26
+ export default content;
27
+ }
28
+
29
+ // Images
30
+ declare module "*.png" {
31
+ const src: string;
32
+ export default src;
33
+ }
34
+
35
+ declare module "*.jpg" {
36
+ const src: string;
37
+ export default src;
38
+ }
39
+
40
+ declare module "*.jpeg" {
41
+ const src: string;
42
+ export default src;
43
+ }
44
+
45
+ declare module "*.gif" {
46
+ const src: string;
47
+ export default src;
48
+ }
49
+
50
+ declare module "*.webp" {
51
+ const src: string;
52
+ export default src;
53
+ }
54
+
55
+ declare module "*.avif" {
56
+ const src: string;
57
+ export default src;
58
+ }
59
+
60
+ declare module "*.ico" {
61
+ const src: string;
62
+ export default src;
63
+ }
64
+
65
+ declare module "*.bmp" {
66
+ const src: string;
67
+ export default src;
68
+ }
69
+
70
+ declare module "*.tif" {
71
+ const src: string;
72
+ export default src;
73
+ }
74
+
75
+ declare module "*.tiff" {
76
+ const src: string;
77
+ export default src;
78
+ }
79
+
80
+ // SVG (with additional export for raw content)
81
+ declare module "*.svg" {
82
+ const src: string;
83
+ export const svgContent: string;
84
+ export default src;
85
+ }
86
+
87
+ // JSON files
88
+ declare module "*.json" {
89
+ const value: any;
90
+ export default value;
91
+ }
92
+
93
+ // Text files
94
+ declare module "*.txt" {
95
+ const content: string;
96
+ export default content;
97
+ }
98
+
99
+ // Fonts
100
+ declare module "*.woff" {
101
+ const src: string;
102
+ export default src;
103
+ }
104
+
105
+ declare module "*.woff2" {
106
+ const src: string;
107
+ export default src;
108
+ }
109
+
110
+ declare module "*.ttf" {
111
+ const src: string;
112
+ export default src;
113
+ }
114
+
115
+ declare module "*.otf" {
116
+ const src: string;
117
+ export default src;
118
+ }
119
+
120
+ declare module "*.eot" {
121
+ const src: string;
122
+ export default src;
123
+ }
124
+
125
+ // Audio files
126
+ declare module "*.mp3" {
127
+ const src: string;
128
+ export default src;
129
+ }
130
+
131
+ declare module "*.wav" {
132
+ const src: string;
133
+ export default src;
134
+ }
135
+
136
+ declare module "*.ogg" {
137
+ const src: string;
138
+ export default src;
139
+ }
140
+
141
+ declare module "*.m4a" {
142
+ const src: string;
143
+ export default src;
144
+ }
145
+
146
+ declare module "*.aac" {
147
+ const src: string;
148
+ export default src;
149
+ }
150
+
151
+ declare module "*.flac" {
152
+ const src: string;
153
+ export default src;
154
+ }
155
+
156
+ // Video files
157
+ declare module "*.mp4" {
158
+ const src: string;
159
+ export default src;
160
+ }
161
+
162
+ declare module "*.webm" {
163
+ const src: string;
164
+ export default src;
165
+ }
166
+
167
+ declare module "*.ogv" {
168
+ const src: string;
169
+ export default src;
170
+ }
171
+
package/src/helpers.ts CHANGED
@@ -28,6 +28,11 @@ import type {HightJSOptions, HightConfig, HightConfigFunction} from './types';
28
28
  import Console, {Colors} from "./api/console";
29
29
  import https, { Server as HttpsServer } from 'https'; // <-- ADICIONAR
30
30
  import fs from 'fs'; // <-- ADICIONAR
31
+
32
+ // Registra loaders customizados para importar arquivos não-JS
33
+ const { registerLoaders } = require('./loaders');
34
+ registerLoaders();
35
+
31
36
  // --- Tipagem ---
32
37
 
33
38
  /**
@@ -168,9 +173,82 @@ async function loadHightConfig(projectDir: string, phase: string): Promise<Hight
168
173
  }
169
174
 
170
175
  /**
171
- * Middleware para parsing do body com proteções de segurança (versão melhorada).
172
- * Rejeita a promise em caso de erro de parsing ou estouro de limite.
176
+ * Aplica headers CORS na resposta baseado na configuração.
177
+ * @param req Requisição HTTP
178
+ * @param res Resposta HTTP
179
+ * @param corsConfig Configuração de CORS
180
+ * @returns true se a requisição foi finalizada (OPTIONS), false caso contrário
173
181
  */
182
+ function applyCors(req: IncomingMessage, res: ServerResponse, corsConfig?: HightConfig['cors']): boolean {
183
+ if (!corsConfig || !corsConfig.enabled) {
184
+ return false;
185
+ }
186
+
187
+ const origin = req.headers.origin || req.headers.referer;
188
+
189
+ // Verifica se a origem é permitida
190
+ let allowOrigin = false;
191
+ if (corsConfig.origin === '*') {
192
+ res.setHeader('Access-Control-Allow-Origin', '*');
193
+ allowOrigin = true;
194
+ } else if (typeof corsConfig.origin === 'string' && origin === corsConfig.origin) {
195
+ res.setHeader('Access-Control-Allow-Origin', corsConfig.origin);
196
+ allowOrigin = true;
197
+ } else if (Array.isArray(corsConfig.origin) && origin && corsConfig.origin.includes(origin)) {
198
+ res.setHeader('Access-Control-Allow-Origin', origin);
199
+ allowOrigin = true;
200
+ } else if (typeof corsConfig.origin === 'function' && origin) {
201
+ try {
202
+ if (corsConfig.origin(origin)) {
203
+ res.setHeader('Access-Control-Allow-Origin', origin);
204
+ allowOrigin = true;
205
+ }
206
+ } catch (error) {
207
+ Console.warn(`${Colors.FgYellow}[CORS]${Colors.Reset} Error validating origin: ${error instanceof Error ? error.message : 'Unknown error'}`);
208
+ }
209
+ }
210
+
211
+ // Se a origem não for permitida e não for wildcard, não aplica outros headers
212
+ if (!allowOrigin && corsConfig.origin !== '*') {
213
+ return false;
214
+ }
215
+
216
+ // Credenciais (não pode ser usado com origin: '*')
217
+ if (corsConfig.credentials && corsConfig.origin !== '*') {
218
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
219
+ }
220
+
221
+ // Métodos permitidos
222
+ const methods = corsConfig.methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'];
223
+ res.setHeader('Access-Control-Allow-Methods', methods.join(', '));
224
+
225
+ // Headers permitidos
226
+ const allowedHeaders = corsConfig.allowedHeaders || ['Content-Type', 'Authorization'];
227
+ res.setHeader('Access-Control-Allow-Headers', allowedHeaders.join(', '));
228
+
229
+ // Headers expostos
230
+ if (corsConfig.exposedHeaders && corsConfig.exposedHeaders.length > 0) {
231
+ res.setHeader('Access-Control-Expose-Headers', corsConfig.exposedHeaders.join(', '));
232
+ }
233
+
234
+ // Max age para cache de preflight
235
+ const maxAge = corsConfig.maxAge !== undefined ? corsConfig.maxAge : 86400;
236
+ res.setHeader('Access-Control-Max-Age', maxAge.toString());
237
+
238
+ // Responde requisições OPTIONS (preflight)
239
+ if (req.method === 'OPTIONS') {
240
+ res.statusCode = 204; // No Content
241
+ res.end();
242
+ return true;
243
+ }
244
+
245
+ return false;
246
+ }
247
+
248
+ /**
249
+ * Middleware para parsing do body com proteções de segurança (versão melhorada).
250
+ */
251
+
174
252
  const parseBody = (req: IncomingMessage): Promise<object | string | null> => {
175
253
  // Constantes para limites de segurança
176
254
  const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB limite total
@@ -270,6 +348,17 @@ async function initNativeServer(hwebApp: HWebApp, options: HightJSOptions, port:
270
348
  const method = req.method || 'GET';
271
349
  const url = req.url || '/';
272
350
 
351
+ // Aplica CORS se configurado
352
+ const corsHandled = applyCors(req, res, hightConfig.cors);
353
+ if (corsHandled) {
354
+ // Requisição OPTIONS foi respondida pelo CORS
355
+ if (hightConfig.accessLogging) {
356
+ const duration = Date.now() - requestStartTime;
357
+ Console.logCustomLevel('OPTIONS', true, Colors.BgMagenta, `${url} ${Colors.FgGreen}204${Colors.Reset} ${Colors.FgGray}${duration}ms${Colors.Reset} ${Colors.FgCyan}[CORS]${Colors.Reset}`);
358
+ }
359
+ return;
360
+ }
361
+
273
362
  // Configurações de segurança básicas
274
363
  res.setHeader('X-Content-Type-Options', 'nosniff');
275
364
  res.setHeader('X-Frame-Options', 'DENY');
package/src/hotReload.ts CHANGED
@@ -195,8 +195,7 @@ export class HotReloadManager {
195
195
  filePath.endsWith('.tsx') ||
196
196
  filePath.endsWith('.jsx');
197
197
 
198
- const isBackendFile = filePath.includes(path.join('src', 'backend')) ||
199
- (filePath.includes(path.join('src', 'web')) && !isFrontendFile);
198
+ const isBackendFile = filePath.includes(path.join('src', 'backend')) && !isFrontendFile;
200
199
 
201
200
  // Limpa o cache do arquivo alterado
202
201
  clearFileCache(filePath);
@@ -220,6 +219,7 @@ export class HotReloadManager {
220
219
  });
221
220
 
222
221
  try {
222
+ this.frontendChangeCallback?.();
223
223
  await Promise.race([buildPromise, timeoutPromise]);
224
224
  Console.logWithout(Levels.INFO, Colors.BgRed,`✅ Build complete, reloading frontend...`);
225
225
  this.frontendChangeCallback?.();
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+
17
18
  import path from 'path';
18
19
  import fs from 'fs';
19
20
  import {ExpressAdapter} from './adapters/express';
@@ -553,3 +554,4 @@ export default function hweb(options: HightJSOptions) {
553
554
  }
554
555
  };
555
556
  }
557
+
package/src/loaders.js ADDED
@@ -0,0 +1,53 @@
1
+ /*
2
+ * This file is part of the HightJS Project.
3
+ * Copyright (c) 2025 itsmuzin
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ const fs = require('fs');
19
+
20
+ /**
21
+ * Registra loaders customizados para Node.js
22
+ * Permite importar arquivos não-JS diretamente no servidor
23
+ */
24
+ function registerLoaders() {
25
+ // Loader para arquivos Markdown (.md)
26
+ require.extensions['.md'] = function (module, filename) {
27
+ const content = fs.readFileSync(filename, 'utf8');
28
+ module.exports = content;
29
+ };
30
+
31
+ // Loader para arquivos de texto (.txt)
32
+ require.extensions['.txt'] = function (module, filename) {
33
+ const content = fs.readFileSync(filename, 'utf8');
34
+ module.exports = content;
35
+ };
36
+
37
+ // Loader para arquivos JSON (já existe nativamente, mas garantimos consistência)
38
+ // require.extensions['.json'] já existe
39
+
40
+ // Loader para imagens - retorna o caminho do arquivo
41
+ const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.avif', '.ico', '.bmp', '.svg'];
42
+
43
+ imageExtensions.forEach(ext => {
44
+ require.extensions[ext] = function (module, filename) {
45
+ // No servidor, retornamos o caminho do arquivo
46
+ // O frontend usará o plugin do esbuild para converter em base64
47
+ module.exports = filename;
48
+ };
49
+ });
50
+ }
51
+
52
+ module.exports = { registerLoaders };
53
+
package/src/renderer.tsx CHANGED
@@ -220,6 +220,12 @@ function getJavaScriptFiles(req: GenericRequest): string {
220
220
  const projectDir = process.cwd();
221
221
  const distDir = path.join(projectDir, '.hight');
222
222
 
223
+ // Verifica se o diretório de build existe
224
+ if (!fs.existsSync(distDir)) {
225
+ // Diretório não existe - build ainda não foi executado
226
+ return getBuildingHTML();
227
+ }
228
+
223
229
  try {
224
230
  // Verifica se existe um manifesto de chunks (gerado pelo ESBuild com splitting)
225
231
  const manifestPath = path.join(distDir, 'manifest.json');
@@ -231,6 +237,12 @@ function getJavaScriptFiles(req: GenericRequest): string {
231
237
  .filter((file: any) => file.endsWith('.js'))
232
238
  .map((file: any) => `<script src="/_hight/${file}"></script>`)
233
239
  .join('');
240
+
241
+ // Se não há arquivos JS no manifesto, build em andamento
242
+ if (!scripts) {
243
+ return getBuildingHTML();
244
+ }
245
+
234
246
  return scripts;
235
247
  } else {
236
248
  // Verifica se existem múltiplos arquivos JS (chunks sem manifesto)
@@ -252,12 +264,158 @@ function getJavaScriptFiles(req: GenericRequest): string {
252
264
  .map(file => `<script src="/_hight/${file}"></script>`)
253
265
  .join('');
254
266
  } else {
255
- // Modo tradicional - único arquivo
256
- return '<script src="/_hight/main.js"></script>';
267
+ // Nenhum arquivo JS encontrado - build em andamento ou erro
268
+ return getBuildingHTML();
257
269
  }
258
270
  }
259
271
  } catch (error) {
260
- // Fallback para o modo tradicional
261
- return '<script src="/_hight/main.js"></script>';
272
+ // Erro ao ler diretório - build em andamento ou erro
273
+ return getBuildingHTML();
262
274
  }
263
275
  }
276
+
277
+ // Função para retornar HTML de "Build em andamento" com auto-refresh
278
+ function getBuildingHTML(): string {
279
+ return `
280
+ <style>
281
+ /*
282
+ * Estilo combinado:
283
+ * - Tema (light/dark) adaptativo como o Next.js
284
+ * - Ícone personalizado
285
+ * - Efeito Glassmorphism para o card
286
+ */
287
+
288
+ html, body {
289
+ margin: 0;
290
+ padding: 0;
291
+ width: 100%;
292
+ height: 100%;
293
+ font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
294
+ -webkit-font-smoothing: antialiased;
295
+ -moz-osx-font-smoothing: grayscale;
296
+ box-sizing: border-box;
297
+ }
298
+
299
+ *, *:before, *:after {
300
+ box-sizing: inherit;
301
+ }
302
+
303
+ /* Tema Claro (Default) */
304
+ body {
305
+ color: #000;
306
+ background: linear-gradient(to bottom, #e9e9e9, #ffffff);
307
+ background-attachment: fixed;
308
+
309
+ display: flex;
310
+ align-items: center;
311
+ justify-content: center;
312
+ min-height: 100vh;
313
+ text-align: center;
314
+ padding: 20px;
315
+ }
316
+
317
+ /* Contêiner com Glassmorphism */
318
+ .building-container {
319
+ width: 100%;
320
+ max-width: 500px;
321
+ padding: 40px 50px;
322
+
323
+ /* Efeito de vidro */
324
+ background: rgba(255, 255, 255, 0.15); /* Mais transparente no modo claro */
325
+ backdrop-filter: blur(12px); /* Um pouco mais de blur */
326
+ -webkit-backdrop-filter: blur(12px);
327
+
328
+ border-radius: 16px;
329
+ border: 1px solid rgba(255, 255, 255, 0.3); /* Borda mais visível no claro */
330
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); /* Sombra mais leve no claro */
331
+ }
332
+
333
+ /* Ícone */
334
+ .building-icon {
335
+ width: 70px; /* Tamanho do ícone */
336
+ height: 70px;
337
+ margin-bottom: 20px; /* Espaço abaixo do ícone */
338
+ vertical-align: middle;
339
+ filter: drop-shadow(0 0 5px rgba(0,0,0,0.1)); /* Leve sombra para destacar */
340
+ }
341
+
342
+ /* Título */
343
+ .building-title {
344
+ font-size: 2.8rem;
345
+ font-weight: 700;
346
+ margin-top: 0; /* Ajusta a margem superior após o ícone */
347
+ margin-bottom: 20px;
348
+ text-shadow: 0 0 10px rgba(0, 0, 0, 0.05); /* Sombra de texto sutil */
349
+ color: inherit; /* Garante que a cor se adapta ao tema */
350
+ }
351
+
352
+ /* Texto de apoio */
353
+ .building-text {
354
+ font-size: 1.15rem;
355
+ margin-bottom: 35px;
356
+ font-weight: 400;
357
+ opacity: 0.9;
358
+ color: inherit; /* Garante que a cor se adapta ao tema */
359
+ }
360
+
361
+ /* Spinner adaptado para light/dark */
362
+ .spinner {
363
+ width: 50px;
364
+ height: 50px;
365
+ margin: 0 auto;
366
+ border-radius: 50%;
367
+
368
+ /* Estilo para Modo Claro */
369
+ border: 5px solid rgba(0, 0, 0, 0.1);
370
+ border-top-color: #000;
371
+
372
+ animation: spin 1s linear infinite;
373
+ }
374
+
375
+ /* Animação de rotação */
376
+ @keyframes spin {
377
+ 0% { transform: rotate(0deg); }
378
+ 100% { transform: rotate(360deg); }
379
+ }
380
+
381
+ /* Tema Escuro (via @media query) */
382
+ @media (prefers-color-scheme: dark) {
383
+ body {
384
+ color: #fff;
385
+ background: linear-gradient(to bottom, #222, #000);
386
+ }
387
+
388
+ .building-container {
389
+ background: rgba(255, 255, 255, 0.05); /* Mais opaco no modo escuro */
390
+ border: 1px solid rgba(255, 255, 255, 0.1); /* Borda mais sutil no escuro */
391
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); /* Sombra mais forte no escuro */
392
+ }
393
+
394
+ .building-title {
395
+ text-shadow: 0 0 10px rgba(255, 255, 255, 0.1);
396
+ }
397
+
398
+ .building-icon {
399
+ filter: drop-shadow(0 0 5px rgba(255,255,255,0.1));
400
+ }
401
+
402
+ .spinner {
403
+ border: 5px solid rgba(255, 255, 255, 0.1);
404
+ border-top-color: #fff;
405
+ }
406
+ }
407
+ </style>
408
+ <div class="building-container">
409
+ <!-- Ícone da imagem --><img src="https://repository-images.githubusercontent.com/1069175740/e5c59d3a-e1fd-446c-a89f-785ed08f6a16" alt="HightJS Logo" class="building-icon">
410
+
411
+ <div class="building-title">HightJS</div>
412
+ <div class="building-text">Build in progress...</div>
413
+ <div class="spinner"></div>
414
+ </div>
415
+ <script>
416
+ // Auto-refresh a cada 2 segundos para verificar se o build terminou
417
+ setTimeout(() => {
418
+ window.location.reload();
419
+ }, 2000);
420
+ </script>`;
421
+ }