hightjs 0.3.3 → 0.3.4
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/adapters/factory.js +8 -8
- package/dist/adapters/native.js +3 -3
- package/dist/auth/client.js +5 -5
- package/dist/auth/components.js +2 -2
- package/dist/auth/core.js +2 -2
- package/dist/auth/react.js +4 -4
- package/dist/auth/routes.js +1 -1
- package/dist/bin/hightjs.js +29 -328
- package/dist/builder.js +7 -19
- package/dist/client/DefaultNotFound.js +1 -1
- package/dist/client/entry.client.js +3 -3
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +90 -28
- package/dist/hotReload.js +12 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16 -30
- package/dist/router.js +133 -62
- package/dist/types.d.ts +42 -0
- package/docs/config.md +201 -0
- package/example/hightjs.config.ts +81 -0
- package/example/package-lock.json +633 -3054
- package/example/package.json +1 -1
- package/package.json +1 -1
- package/src/adapters/factory.ts +8 -8
- package/src/adapters/native.ts +3 -3
- package/src/auth/client.ts +5 -5
- package/src/auth/components.tsx +2 -2
- package/src/auth/core.ts +2 -2
- package/src/auth/react.tsx +4 -4
- package/src/auth/routes.ts +1 -1
- package/src/bin/hightjs.js +30 -391
- package/src/builder.js +7 -20
- package/src/client/DefaultNotFound.tsx +1 -1
- package/src/client/entry.client.tsx +3 -3
- package/src/helpers.ts +105 -29
- package/src/hotReload.ts +12 -12
- package/src/index.ts +20 -33
- package/src/router.ts +140 -63
- package/src/types.ts +52 -0
- package/example/.hweb/entry.client.js +0 -24
- package/example/hweb-dist/main-5KKAYNUU.js +0 -1137
package/dist/adapters/factory.js
CHANGED
|
@@ -50,40 +50,40 @@ class FrameworkAdapterFactory {
|
|
|
50
50
|
if (this.adapter) {
|
|
51
51
|
return this.adapter;
|
|
52
52
|
}
|
|
53
|
-
const msg = console_1.default.dynamicLine(` ${console_1.Colors.FgYellow}● ${console_1.Colors.Reset}
|
|
53
|
+
const msg = console_1.default.dynamicLine(` ${console_1.Colors.FgYellow}● ${console_1.Colors.Reset}Detecting web framework...`);
|
|
54
54
|
// Detecta Express
|
|
55
55
|
if (req.app && req.route && res.locals !== undefined) {
|
|
56
|
-
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Framework
|
|
56
|
+
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Framework detected: Express`);
|
|
57
57
|
this.adapter = new express_1.ExpressAdapter();
|
|
58
58
|
return this.adapter;
|
|
59
59
|
}
|
|
60
60
|
// Detecta Fastify
|
|
61
61
|
if (req.server && req.routerPath !== undefined && res.request) {
|
|
62
|
-
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Framework
|
|
62
|
+
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Framework detected: Fastify`);
|
|
63
63
|
this.adapter = new fastify_1.FastifyAdapter();
|
|
64
64
|
return this.adapter;
|
|
65
65
|
}
|
|
66
66
|
// Detecta HTTP nativo do Node.js
|
|
67
67
|
if (req.method !== undefined && req.url !== undefined && req.headers !== undefined &&
|
|
68
68
|
res.statusCode !== undefined && res.setHeader !== undefined && res.end !== undefined) {
|
|
69
|
-
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Framework
|
|
69
|
+
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Framework detected: HightJS Native (HTTP)`);
|
|
70
70
|
this.adapter = new native_1.NativeAdapter();
|
|
71
71
|
return this.adapter;
|
|
72
72
|
}
|
|
73
73
|
// Fallback mais específico para Express
|
|
74
74
|
if (res.status && res.send && res.json && res.cookie) {
|
|
75
|
-
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Framework
|
|
75
|
+
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Framework detected: Express (fallback)`);
|
|
76
76
|
this.adapter = new express_1.ExpressAdapter();
|
|
77
77
|
return this.adapter;
|
|
78
78
|
}
|
|
79
79
|
// Fallback mais específico para Fastify
|
|
80
80
|
if (res.code && res.send && res.type && res.setCookie) {
|
|
81
|
-
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Framework
|
|
81
|
+
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Framework detected: Fastify (fallback)`);
|
|
82
82
|
this.adapter = new fastify_1.FastifyAdapter();
|
|
83
83
|
return this.adapter;
|
|
84
84
|
}
|
|
85
85
|
// Default para HightJS Native se não conseguir detectar
|
|
86
|
-
msg.end(` ${console_1.Colors.FgYellow}● ${console_1.Colors.Reset}
|
|
86
|
+
msg.end(` ${console_1.Colors.FgYellow}● ${console_1.Colors.Reset}Unable to detect framework. Using HightJS Native as default.`);
|
|
87
87
|
this.adapter = new native_1.NativeAdapter();
|
|
88
88
|
return this.adapter;
|
|
89
89
|
}
|
|
@@ -102,7 +102,7 @@ class FrameworkAdapterFactory {
|
|
|
102
102
|
this.adapter = new native_1.NativeAdapter();
|
|
103
103
|
break;
|
|
104
104
|
default:
|
|
105
|
-
throw new Error(`
|
|
105
|
+
throw new Error(`Unsupported framework: ${framework}`);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
/**
|
package/dist/adapters/native.js
CHANGED
|
@@ -59,7 +59,7 @@ class NativeAdapter {
|
|
|
59
59
|
}
|
|
60
60
|
catch (e) {
|
|
61
61
|
// Prevenção de crash: Ignora cookies com valores malformados (e.g., URI inválida).
|
|
62
|
-
console.error(`
|
|
62
|
+
console.error(`Warning: Malformed cookie with name "${name}" was ignored.`);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
});
|
|
@@ -88,7 +88,7 @@ class NativeResponseWrapper {
|
|
|
88
88
|
const sanitizedName = sanitizeHeaderValue(name);
|
|
89
89
|
const sanitizedValue = sanitizeHeaderValue(value);
|
|
90
90
|
if (name !== sanitizedName || String(value) !== sanitizedValue) {
|
|
91
|
-
console.warn(`
|
|
91
|
+
console.warn(`Warning: Potential HTTP Header Injection attempt detected and sanitized. Original header: "${name}"`);
|
|
92
92
|
}
|
|
93
93
|
this.headers[sanitizedName] = sanitizedValue;
|
|
94
94
|
return this;
|
|
@@ -96,7 +96,7 @@ class NativeResponseWrapper {
|
|
|
96
96
|
cookie(name, value, options) {
|
|
97
97
|
// Medida de segurança: Valida o nome do cookie.
|
|
98
98
|
if (!isValidCookieName(name)) {
|
|
99
|
-
console.error(`
|
|
99
|
+
console.error(`Error: Invalid cookie name "${name}". The cookie will not be set.`);
|
|
100
100
|
return this;
|
|
101
101
|
}
|
|
102
102
|
let cookieString = `${name}=${encodeURIComponent(value)}`;
|
package/dist/auth/client.js
CHANGED
|
@@ -26,7 +26,7 @@ async function getSession() {
|
|
|
26
26
|
return data.session || null;
|
|
27
27
|
}
|
|
28
28
|
catch (error) {
|
|
29
|
-
console.error('[hweb-auth]
|
|
29
|
+
console.error('[hweb-auth] Error fetching session:', error);
|
|
30
30
|
return null;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -45,7 +45,7 @@ async function getCsrfToken() {
|
|
|
45
45
|
return data.csrfToken || null;
|
|
46
46
|
}
|
|
47
47
|
catch (error) {
|
|
48
|
-
console.error('[hweb-auth]
|
|
48
|
+
console.error('[hweb-auth] Error fetching CSRF token:', error);
|
|
49
49
|
return null;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -64,7 +64,7 @@ async function getProviders() {
|
|
|
64
64
|
return data.providers || [];
|
|
65
65
|
}
|
|
66
66
|
catch (error) {
|
|
67
|
-
console.error('[hweb-auth]
|
|
67
|
+
console.error('[hweb-auth] Error searching for providers:', error);
|
|
68
68
|
return null;
|
|
69
69
|
}
|
|
70
70
|
}
|
|
@@ -119,7 +119,7 @@ async function signIn(provider = 'credentials', options = {}) {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
catch (error) {
|
|
122
|
-
console.error('[hweb-auth]
|
|
122
|
+
console.error('[hweb-auth] Error on signIn:', error);
|
|
123
123
|
return {
|
|
124
124
|
error: 'Network error',
|
|
125
125
|
status: 500,
|
|
@@ -141,6 +141,6 @@ async function signOut(options = {}) {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
catch (error) {
|
|
144
|
-
console.error('[hweb-auth]
|
|
144
|
+
console.error('[hweb-auth] Error on signOut:', error);
|
|
145
145
|
}
|
|
146
146
|
}
|
package/dist/auth/components.js
CHANGED
|
@@ -34,7 +34,7 @@ function ProtectedRoute({ children, fallback, redirectTo = '/auth/signin', requi
|
|
|
34
34
|
const { isAuthenticated, isLoading } = (0, react_2.useAuth)();
|
|
35
35
|
// Ainda carregando
|
|
36
36
|
if (isLoading) {
|
|
37
|
-
return fallback || (0, jsx_runtime_1.jsx)("div", { children: "
|
|
37
|
+
return fallback || (0, jsx_runtime_1.jsx)("div", { children: "Loading..." });
|
|
38
38
|
}
|
|
39
39
|
// Requer auth mas não está autenticado
|
|
40
40
|
if (requireAuth && !isAuthenticated) {
|
|
@@ -42,7 +42,7 @@ function ProtectedRoute({ children, fallback, redirectTo = '/auth/signin', requi
|
|
|
42
42
|
window.location.href = redirectTo;
|
|
43
43
|
return null;
|
|
44
44
|
}
|
|
45
|
-
return fallback || (0, jsx_runtime_1.jsx)("div", { children: "
|
|
45
|
+
return fallback || (0, jsx_runtime_1.jsx)("div", { children: "Unauthorized" });
|
|
46
46
|
}
|
|
47
47
|
// Não requer auth mas está autenticado (ex: página de login)
|
|
48
48
|
if (!requireAuth && isAuthenticated && redirectTo) {
|
package/dist/auth/core.js
CHANGED
|
@@ -76,7 +76,7 @@ class HWebAuth {
|
|
|
76
76
|
return sessionResult;
|
|
77
77
|
}
|
|
78
78
|
catch (error) {
|
|
79
|
-
console.error(`[hweb-auth]
|
|
79
|
+
console.error(`[hweb-auth] Error signing in with provider ${providerId}:`, error);
|
|
80
80
|
return null;
|
|
81
81
|
}
|
|
82
82
|
}
|
|
@@ -93,7 +93,7 @@ class HWebAuth {
|
|
|
93
93
|
await provider.handleSignOut();
|
|
94
94
|
}
|
|
95
95
|
catch (error) {
|
|
96
|
-
console.error(`[hweb-auth]
|
|
96
|
+
console.error(`[hweb-auth] Signout error on provider ${provider.id}:`, error);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
}
|
package/dist/auth/react.js
CHANGED
|
@@ -50,7 +50,7 @@ function SessionProvider({ children, basePath = '/api/auth', refetchInterval = 0
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
catch (error) {
|
|
53
|
-
console.error('[hweb-auth]
|
|
53
|
+
console.error('[hweb-auth] Error fetching session:', error);
|
|
54
54
|
setSession(null);
|
|
55
55
|
setStatus('unauthenticated');
|
|
56
56
|
return null;
|
|
@@ -106,7 +106,7 @@ function SessionProvider({ children, basePath = '/api/auth', refetchInterval = 0
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
catch (error) {
|
|
109
|
-
console.error('[hweb-auth]
|
|
109
|
+
console.error('[hweb-auth] Error on signIn:', error);
|
|
110
110
|
return {
|
|
111
111
|
error: 'Network error',
|
|
112
112
|
status: 500,
|
|
@@ -133,7 +133,7 @@ function SessionProvider({ children, basePath = '/api/auth', refetchInterval = 0
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
catch (error) {
|
|
136
|
-
console.error('[hweb-auth]
|
|
136
|
+
console.error('[hweb-auth] Error on signOut:', error);
|
|
137
137
|
}
|
|
138
138
|
}, [basePath]);
|
|
139
139
|
// Update session
|
|
@@ -182,7 +182,7 @@ function SessionProvider({ children, basePath = '/api/auth', refetchInterval = 0
|
|
|
182
182
|
function useSession() {
|
|
183
183
|
const context = (0, react_1.useContext)(SessionContext);
|
|
184
184
|
if (context === undefined) {
|
|
185
|
-
throw new Error('useSession
|
|
185
|
+
throw new Error('useSession must be used inside a SessionProvider');
|
|
186
186
|
}
|
|
187
187
|
return context;
|
|
188
188
|
}
|
package/dist/auth/routes.js
CHANGED
|
@@ -140,7 +140,7 @@ async function handleSignIn(req, auth) {
|
|
|
140
140
|
});
|
|
141
141
|
}
|
|
142
142
|
catch (error) {
|
|
143
|
-
console.error('[hweb-auth]
|
|
143
|
+
console.error('[hweb-auth] Error on handleSignIn:', error);
|
|
144
144
|
return http_1.HightJSResponse.json({ error: 'Authentication failed' }, { status: 500 });
|
|
145
145
|
}
|
|
146
146
|
}
|
package/dist/bin/hightjs.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* you may not use this file except in compliance with the License.
|
|
9
9
|
* You may obtain a copy of the License at
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
12
|
*
|
|
13
13
|
* Unless required by applicable law or agreed to in writing, software
|
|
14
14
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
@@ -21,7 +21,7 @@ require('ts-node').register();
|
|
|
21
21
|
const { program } = require('commander');
|
|
22
22
|
program
|
|
23
23
|
.version('1.0.0')
|
|
24
|
-
.description('CLI
|
|
24
|
+
.description('CLI to manage the application.');
|
|
25
25
|
// --- Comando DEV ---
|
|
26
26
|
const fs = require('fs');
|
|
27
27
|
const path = require('path');
|
|
@@ -59,7 +59,7 @@ function initializeApp(options, isDev) {
|
|
|
59
59
|
appOptions.ssl.redirectPort = options.httpRedirectPort || 80;
|
|
60
60
|
}
|
|
61
61
|
else {
|
|
62
|
-
Console.logWithout(Levels.ERROR, null, `
|
|
62
|
+
Console.logWithout(Levels.ERROR, null, `Ensure that './certs/key.pem' and './certs/cert.pem' exist.`, `--ssl flag was used, but the files were not found.`);
|
|
63
63
|
process.exit(1); // Encerra o processo com erro
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -71,64 +71,64 @@ function initializeApp(options, isDev) {
|
|
|
71
71
|
// --- Comando DEV ---
|
|
72
72
|
program
|
|
73
73
|
.command('dev')
|
|
74
|
-
.description('
|
|
75
|
-
.option('-p, --port <number>', '
|
|
76
|
-
.option('-H, --hostname <string>', '
|
|
77
|
-
.option('--ssl', '
|
|
78
|
-
.option('--http-redirect-port <number>', '
|
|
74
|
+
.description('Starts the application in development mode.')
|
|
75
|
+
.option('-p, --port <number>', 'Specifies the port to run on', '3000')
|
|
76
|
+
.option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
|
|
77
|
+
.option('--ssl', 'Activates HTTPS/SSL mode (requires ./ssl/key.pem and ./ssl/cert.pem)')
|
|
78
|
+
.option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
|
|
79
79
|
.action((options) => {
|
|
80
80
|
initializeApp(options, true); // Chama a função com dev: true
|
|
81
81
|
});
|
|
82
82
|
// --- Comando START (Produção) ---
|
|
83
83
|
program
|
|
84
84
|
.command('start')
|
|
85
|
-
.description('
|
|
86
|
-
.option('-p, --port <number>', '
|
|
87
|
-
.option('-H, --hostname <string>', '
|
|
88
|
-
.option('--ssl', '
|
|
89
|
-
.option('--http-redirect-port <number>', '
|
|
85
|
+
.description('Starts the application in production mode.')
|
|
86
|
+
.option('-p, --port <number>', 'Specifies the port to run on', '3000')
|
|
87
|
+
.option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
|
|
88
|
+
.option('--ssl', 'Activates HTTPS/SSL mode (requires ./ssl/key.pem and ./ssl/cert.pem)')
|
|
89
|
+
.option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
|
|
90
90
|
.action((options) => {
|
|
91
91
|
initializeApp(options, false); // Chama a função com dev: false
|
|
92
92
|
});
|
|
93
93
|
// --- Comando EXPORT ---
|
|
94
94
|
program
|
|
95
95
|
.command('export')
|
|
96
|
-
.description('
|
|
97
|
-
.option('-o, --output <path>', '
|
|
96
|
+
.description('Exports the application as static HTML to the "exported" folder.')
|
|
97
|
+
.option('-o, --output <path>', 'Specifies the output directory', 'exported')
|
|
98
98
|
.action(async (options) => {
|
|
99
99
|
const projectDir = process.cwd();
|
|
100
100
|
const exportDir = path.join(projectDir, options.output);
|
|
101
|
-
console.log('🚀
|
|
101
|
+
console.log('🚀 Starting export...\n');
|
|
102
102
|
try {
|
|
103
103
|
// 1. Cria a pasta exported (limpa se já existir)
|
|
104
104
|
if (fs.existsSync(exportDir)) {
|
|
105
|
-
console.log('🗑️
|
|
105
|
+
console.log('🗑️ Cleaning existing export folder...');
|
|
106
106
|
fs.rmSync(exportDir, { recursive: true, force: true });
|
|
107
107
|
}
|
|
108
108
|
fs.mkdirSync(exportDir, { recursive: true });
|
|
109
|
-
console.log('✅
|
|
109
|
+
console.log('✅ Export folder created\n');
|
|
110
110
|
// 2. Inicializa e prepara o build
|
|
111
|
-
console.log('🔨
|
|
111
|
+
console.log('🔨 Building application...');
|
|
112
112
|
const teste = require("../helpers");
|
|
113
113
|
const app = teste.default({ dev: false, port: 3000, hostname: '0.0.0.0', framework: 'native' });
|
|
114
114
|
await app.prepare();
|
|
115
|
-
console.log('✅ Build
|
|
115
|
+
console.log('✅ Build complete\n');
|
|
116
116
|
// 3. Copia a pasta hweb-dist para exported
|
|
117
117
|
const distDir = path.join(projectDir, 'hweb-dist');
|
|
118
118
|
if (fs.existsSync(distDir)) {
|
|
119
|
-
console.log('📦
|
|
119
|
+
console.log('📦 Copying JavaScript files...');
|
|
120
120
|
const exportDistDir = path.join(exportDir, 'hweb-dist');
|
|
121
121
|
fs.mkdirSync(exportDistDir, { recursive: true });
|
|
122
122
|
const files = fs.readdirSync(distDir);
|
|
123
123
|
files.forEach(file => {
|
|
124
124
|
fs.copyFileSync(path.join(distDir, file), path.join(exportDistDir, file));
|
|
125
125
|
});
|
|
126
|
-
console.log('✅
|
|
126
|
+
console.log('✅ JavaScript files copied\n');
|
|
127
127
|
}
|
|
128
128
|
// 4. Copia a pasta public se existir
|
|
129
129
|
const publicDir = path.join(projectDir, 'public');
|
|
130
130
|
if (fs.existsSync(publicDir)) {
|
|
131
|
-
console.log('📁
|
|
131
|
+
console.log('📁 Copying public files...');
|
|
132
132
|
const exportPublicDir = path.join(exportDir, 'public');
|
|
133
133
|
function copyRecursive(src, dest) {
|
|
134
134
|
if (fs.statSync(src).isDirectory()) {
|
|
@@ -142,10 +142,10 @@ program
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
copyRecursive(publicDir, exportPublicDir);
|
|
145
|
-
console.log('✅
|
|
145
|
+
console.log('✅ Public files copied\n');
|
|
146
146
|
}
|
|
147
147
|
// 5. Gera o index.html
|
|
148
|
-
console.log('📝
|
|
148
|
+
console.log('📝 Generating index.html...');
|
|
149
149
|
const { render } = require('../renderer');
|
|
150
150
|
const { loadRoutes, loadLayout, loadNotFound } = require('../router');
|
|
151
151
|
// Carrega as rotas para gerar o HTML
|
|
@@ -172,314 +172,15 @@ program
|
|
|
172
172
|
});
|
|
173
173
|
const indexPath = path.join(exportDir, 'index.html');
|
|
174
174
|
fs.writeFileSync(indexPath, html, 'utf8');
|
|
175
|
-
console.log('✅ index.html
|
|
175
|
+
console.log('✅ index.html generated\n');
|
|
176
176
|
}
|
|
177
|
-
console.log('🎉
|
|
178
|
-
console.log(`📂
|
|
177
|
+
console.log('🎉 Export completed successfully!');
|
|
178
|
+
console.log(`📂 Files exported to: ${exportDir}\n`);
|
|
179
179
|
}
|
|
180
180
|
catch (error) {
|
|
181
|
-
console.error('❌
|
|
181
|
+
console.error('❌ Error during export:', error.message);
|
|
182
182
|
process.exit(1);
|
|
183
183
|
}
|
|
184
184
|
});
|
|
185
|
-
// --- Comando MIGRATE FROM NEXTJS ---
|
|
186
|
-
program
|
|
187
|
-
.command('migrate-from-nextjs')
|
|
188
|
-
.description('Migra um projeto Next.js para HightJS.')
|
|
189
|
-
.option('-s, --source <path>', 'Caminho do projeto Next.js', process.cwd())
|
|
190
|
-
.option('-o, --output <path>', 'Diretório de saída para o projeto HightJS', './hightjs-project')
|
|
191
|
-
.action(async (options) => {
|
|
192
|
-
const sourcePath = path.resolve(options.source);
|
|
193
|
-
const outputPath = path.resolve(options.output);
|
|
194
|
-
console.log('🚀 Iniciando migração de Next.js para HightJS...\n');
|
|
195
|
-
console.log(`📂 Origem: ${sourcePath}`);
|
|
196
|
-
console.log(`📂 Destino: ${outputPath}\n`);
|
|
197
|
-
try {
|
|
198
|
-
// Verifica se o diretório de origem existe
|
|
199
|
-
if (!fs.existsSync(sourcePath)) {
|
|
200
|
-
throw new Error(`Diretório de origem não encontrado: ${sourcePath}`);
|
|
201
|
-
}
|
|
202
|
-
// Verifica se é um projeto Next.js
|
|
203
|
-
const nextConfigPath = path.join(sourcePath, 'next.config.js');
|
|
204
|
-
const nextConfigMjsPath = path.join(sourcePath, 'next.config.mjs');
|
|
205
|
-
const packageJsonPath = path.join(sourcePath, 'package.json');
|
|
206
|
-
if (!fs.existsSync(nextConfigPath) && !fs.existsSync(nextConfigMjsPath) && !fs.existsSync(packageJsonPath)) {
|
|
207
|
-
throw new Error('Não foi encontrado um projeto Next.js válido no diretório especificado.');
|
|
208
|
-
}
|
|
209
|
-
// Cria o diretório de saída
|
|
210
|
-
if (fs.existsSync(outputPath)) {
|
|
211
|
-
console.log('⚠️ Diretório de destino já existe. Limpando...');
|
|
212
|
-
fs.rmSync(outputPath, { recursive: true, force: true });
|
|
213
|
-
}
|
|
214
|
-
fs.mkdirSync(outputPath, { recursive: true });
|
|
215
|
-
// 1. Cria estrutura básica do HightJS
|
|
216
|
-
console.log('📁 Criando estrutura de diretórios...');
|
|
217
|
-
const srcDir = path.join(outputPath, 'src');
|
|
218
|
-
const webDir = path.join(srcDir, 'web');
|
|
219
|
-
const routesDir = path.join(webDir, 'routes');
|
|
220
|
-
const publicDir = path.join(outputPath, 'public');
|
|
221
|
-
fs.mkdirSync(srcDir, { recursive: true });
|
|
222
|
-
fs.mkdirSync(webDir, { recursive: true });
|
|
223
|
-
fs.mkdirSync(routesDir, { recursive: true });
|
|
224
|
-
fs.mkdirSync(publicDir, { recursive: true });
|
|
225
|
-
// 2. Copia arquivos públicos
|
|
226
|
-
console.log('📦 Copiando arquivos públicos...');
|
|
227
|
-
const nextPublicDir = path.join(sourcePath, 'public');
|
|
228
|
-
if (fs.existsSync(nextPublicDir)) {
|
|
229
|
-
copyDirectory(nextPublicDir, publicDir);
|
|
230
|
-
}
|
|
231
|
-
// 3. Migra páginas do Next.js para rotas do HightJS
|
|
232
|
-
console.log('🔄 Migrando páginas para rotas...');
|
|
233
|
-
const nextPagesDir = path.join(sourcePath, 'pages');
|
|
234
|
-
const nextAppDir = path.join(sourcePath, 'app');
|
|
235
|
-
const nextSrcPagesDir = path.join(sourcePath, 'src', 'pages');
|
|
236
|
-
const nextSrcAppDir = path.join(sourcePath, 'src', 'app');
|
|
237
|
-
let pagesDir = null;
|
|
238
|
-
let isAppRouter = false;
|
|
239
|
-
if (fs.existsSync(nextAppDir)) {
|
|
240
|
-
pagesDir = nextAppDir;
|
|
241
|
-
isAppRouter = true;
|
|
242
|
-
console.log('✅ Detectado Next.js App Router');
|
|
243
|
-
}
|
|
244
|
-
else if (fs.existsSync(nextSrcAppDir)) {
|
|
245
|
-
pagesDir = nextSrcAppDir;
|
|
246
|
-
isAppRouter = true;
|
|
247
|
-
console.log('✅ Detectado Next.js App Router (em src/)');
|
|
248
|
-
}
|
|
249
|
-
else if (fs.existsSync(nextPagesDir)) {
|
|
250
|
-
pagesDir = nextPagesDir;
|
|
251
|
-
console.log('✅ Detectado Next.js Pages Router');
|
|
252
|
-
}
|
|
253
|
-
else if (fs.existsSync(nextSrcPagesDir)) {
|
|
254
|
-
pagesDir = nextSrcPagesDir;
|
|
255
|
-
console.log('✅ Detectado Next.js Pages Router (em src/)');
|
|
256
|
-
}
|
|
257
|
-
if (pagesDir) {
|
|
258
|
-
if (isAppRouter) {
|
|
259
|
-
migrateAppRouter(pagesDir, routesDir);
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
migratePagesRouter(pagesDir, routesDir);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
// 5. Cria package.json
|
|
266
|
-
console.log('📄 Criando package.json...');
|
|
267
|
-
let originalPackageJson = {};
|
|
268
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
269
|
-
originalPackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
270
|
-
}
|
|
271
|
-
const newPackageJson = {
|
|
272
|
-
name: originalPackageJson.name || 'hightjs-app',
|
|
273
|
-
version: '1.0.0',
|
|
274
|
-
description: originalPackageJson.description || 'HightJS application migrated from Next.js',
|
|
275
|
-
scripts: {
|
|
276
|
-
dev: 'hight dev',
|
|
277
|
-
start: 'hight start',
|
|
278
|
-
build: 'hight export',
|
|
279
|
-
...originalPackageJson.scripts
|
|
280
|
-
},
|
|
281
|
-
dependencies: {
|
|
282
|
-
hightjs: 'latest',
|
|
283
|
-
react: originalPackageJson.dependencies?.react || '^18.2.0',
|
|
284
|
-
'react-dom': originalPackageJson.dependencies?.['react-dom'] || '^18.2.0',
|
|
285
|
-
...filterDependencies(originalPackageJson.dependencies)
|
|
286
|
-
},
|
|
287
|
-
devDependencies: {
|
|
288
|
-
'@types/node': '^20.11.24',
|
|
289
|
-
'@types/react': '^18.2.0',
|
|
290
|
-
'@types/react-dom': '^18.2.0',
|
|
291
|
-
typescript: '^5.3.3',
|
|
292
|
-
...filterDependencies(originalPackageJson.devDependencies)
|
|
293
|
-
}
|
|
294
|
-
};
|
|
295
|
-
fs.writeFileSync(path.join(outputPath, 'package.json'), JSON.stringify(newPackageJson, null, 2), 'utf8');
|
|
296
|
-
// 6. Cria tsconfig.json
|
|
297
|
-
console.log('⚙️ Criando tsconfig.json...');
|
|
298
|
-
const tsConfig = {
|
|
299
|
-
compilerOptions: {
|
|
300
|
-
target: 'ES2020',
|
|
301
|
-
lib: ['ES2020', 'DOM', 'DOM.Iterable'],
|
|
302
|
-
jsx: 'react-jsx',
|
|
303
|
-
module: 'commonjs',
|
|
304
|
-
moduleResolution: 'node',
|
|
305
|
-
esModuleInterop: true,
|
|
306
|
-
strict: true,
|
|
307
|
-
skipLibCheck: true,
|
|
308
|
-
forceConsistentCasingInFileNames: true,
|
|
309
|
-
resolveJsonModule: true,
|
|
310
|
-
allowSyntheticDefaultImports: true
|
|
311
|
-
},
|
|
312
|
-
include: ['src/**/*'],
|
|
313
|
-
exclude: ['node_modules']
|
|
314
|
-
};
|
|
315
|
-
fs.writeFileSync(path.join(outputPath, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2), 'utf8');
|
|
316
|
-
// 7. Cria README
|
|
317
|
-
console.log('📖 Criando README...');
|
|
318
|
-
const readmeContent = `# ${originalPackageJson.name || 'HightJS App'}
|
|
319
|
-
|
|
320
|
-
Este projeto foi migrado de Next.js para HightJS.
|
|
321
|
-
|
|
322
|
-
## Comandos Disponíveis
|
|
323
|
-
|
|
324
|
-
- \`npm run dev\` - Inicia o servidor de desenvolvimento
|
|
325
|
-
- \`npm run start\` - Inicia o servidor em modo produção
|
|
326
|
-
- \`npm run build\` - Exporta a aplicação como HTML estático
|
|
327
|
-
|
|
328
|
-
## Próximos Passos
|
|
329
|
-
|
|
330
|
-
1. Instale as dependências: \`npm install\`
|
|
331
|
-
2. Revise os arquivos migrados em \`src/web/routes/\`
|
|
332
|
-
3. Ajuste manualmente qualquer código que precise de adaptação
|
|
333
|
-
4. Execute \`npm run dev\` para testar
|
|
334
|
-
|
|
335
|
-
## Notas de Migração
|
|
336
|
-
|
|
337
|
-
- Rotas dinâmicas do Next.js foram convertidas para o formato HightJS
|
|
338
|
-
- API Routes precisam ser migradas manualmente para HightJS API routes
|
|
339
|
-
- Server Components foram convertidos para componentes React padrão
|
|
340
|
-
- Revise imports e configurações específicas do Next.js
|
|
341
|
-
|
|
342
|
-
Para mais informações sobre HightJS, visite a documentação.
|
|
343
|
-
`;
|
|
344
|
-
fs.writeFileSync(path.join(outputPath, 'README.md'), readmeContent, 'utf8');
|
|
345
|
-
console.log('\n✅ Migração concluída com sucesso!');
|
|
346
|
-
console.log(`\n📂 Projeto criado em: ${outputPath}`);
|
|
347
|
-
console.log('\n📋 Próximos passos:');
|
|
348
|
-
console.log(` 1. cd ${path.relative(process.cwd(), outputPath)}`);
|
|
349
|
-
console.log(' 2. npm install');
|
|
350
|
-
console.log(' 3. npm run dev');
|
|
351
|
-
console.log('\n⚠️ IMPORTANTE: Revise os arquivos migrados e ajuste conforme necessário.\n');
|
|
352
|
-
}
|
|
353
|
-
catch (error) {
|
|
354
|
-
console.error('❌ Erro durante a migração:', error.message);
|
|
355
|
-
console.error(error.stack);
|
|
356
|
-
process.exit(1);
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
// Funções auxiliares para migração
|
|
360
|
-
function copyDirectory(src, dest) {
|
|
361
|
-
if (!fs.existsSync(src))
|
|
362
|
-
return;
|
|
363
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
364
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
365
|
-
for (const entry of entries) {
|
|
366
|
-
const srcPath = path.join(src, entry.name);
|
|
367
|
-
const destPath = path.join(dest, entry.name);
|
|
368
|
-
if (entry.isDirectory()) {
|
|
369
|
-
copyDirectory(srcPath, destPath);
|
|
370
|
-
}
|
|
371
|
-
else {
|
|
372
|
-
fs.copyFileSync(srcPath, destPath);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
function migratePagesRouter(pagesDir, routesDir) {
|
|
377
|
-
function processDirectory(dir, baseRoute = '') {
|
|
378
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
379
|
-
for (const entry of entries) {
|
|
380
|
-
const fullPath = path.join(dir, entry.name);
|
|
381
|
-
if (entry.isDirectory()) {
|
|
382
|
-
// Processa subdiretórios (rotas aninhadas)
|
|
383
|
-
const newBaseRoute = path.join(baseRoute, entry.name);
|
|
384
|
-
processDirectory(fullPath, newBaseRoute);
|
|
385
|
-
}
|
|
386
|
-
else if (entry.name.match(/\.(tsx?|jsx?)$/)) {
|
|
387
|
-
// Ignora arquivos especiais do Next.js
|
|
388
|
-
if (['_app', '_document', '_error', 'api'].some(special => entry.name.startsWith(special))) {
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
// Converte nome do arquivo para rota HightJS
|
|
392
|
-
let fileName = entry.name.replace(/\.(tsx?|jsx?)$/, '');
|
|
393
|
-
let routePath = baseRoute;
|
|
394
|
-
if (fileName === 'index') {
|
|
395
|
-
// index.tsx -> route vazia ou baseRoute
|
|
396
|
-
routePath = baseRoute || '/';
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
// [id].tsx -> $id.tsx
|
|
400
|
-
fileName = fileName.replace(/\[([^\]]+)\]/g, '$$$1');
|
|
401
|
-
routePath = path.join(baseRoute, fileName);
|
|
402
|
-
}
|
|
403
|
-
// Lê o conteúdo original
|
|
404
|
-
const originalContent = fs.readFileSync(fullPath, 'utf8');
|
|
405
|
-
// Transforma o conteúdo
|
|
406
|
-
const transformedContent = transformNextJsPage(originalContent);
|
|
407
|
-
// Cria estrutura de diretórios se necessário
|
|
408
|
-
const targetDir = path.join(routesDir, baseRoute);
|
|
409
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
410
|
-
// Salva o arquivo transformado
|
|
411
|
-
const targetFileName = fileName === 'index' ? 'route.tsx' : `${fileName}.route.tsx`;
|
|
412
|
-
const targetPath = path.join(targetDir, targetFileName);
|
|
413
|
-
fs.writeFileSync(targetPath, transformedContent, 'utf8');
|
|
414
|
-
console.log(` ✓ ${path.relative(pagesDir, fullPath)} -> ${path.relative(routesDir, targetPath)}`);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
processDirectory(pagesDir);
|
|
419
|
-
}
|
|
420
|
-
function migrateAppRouter(appDir, routesDir) {
|
|
421
|
-
function processDirectory(dir, baseRoute = '') {
|
|
422
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
423
|
-
let hasPage = false;
|
|
424
|
-
for (const entry of entries) {
|
|
425
|
-
const fullPath = path.join(dir, entry.name);
|
|
426
|
-
if (entry.isDirectory()) {
|
|
427
|
-
// Suporta rotas dinâmicas como [id]
|
|
428
|
-
let dirName = entry.name;
|
|
429
|
-
if (dirName.startsWith('[') && dirName.endsWith(']')) {
|
|
430
|
-
dirName = '$' + dirName.slice(1, -1);
|
|
431
|
-
}
|
|
432
|
-
const newBaseRoute = path.join(baseRoute, dirName);
|
|
433
|
-
processDirectory(fullPath, newBaseRoute);
|
|
434
|
-
}
|
|
435
|
-
else if (entry.name === 'page.tsx' || entry.name === 'page.jsx' || entry.name === 'page.ts' || entry.name === 'page.js') {
|
|
436
|
-
hasPage = true;
|
|
437
|
-
// Lê o conteúdo original
|
|
438
|
-
const originalContent = fs.readFileSync(fullPath, 'utf8');
|
|
439
|
-
// Transforma o conteúdo
|
|
440
|
-
const transformedContent = transformNextJsPage(originalContent);
|
|
441
|
-
// Cria estrutura de diretórios
|
|
442
|
-
const targetDir = path.join(routesDir, baseRoute);
|
|
443
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
444
|
-
// Salva como route.tsx
|
|
445
|
-
const targetPath = path.join(targetDir, 'route.tsx');
|
|
446
|
-
fs.writeFileSync(targetPath, transformedContent, 'utf8');
|
|
447
|
-
console.log(` ✓ ${path.relative(appDir, fullPath)} -> ${path.relative(routesDir, targetPath)}`);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
processDirectory(appDir);
|
|
452
|
-
}
|
|
453
|
-
function transformNextJsPage(content) {
|
|
454
|
-
// Remove 'use client' e 'use server'
|
|
455
|
-
content = content.replace(/['"]use (client|server)['"]\s*;\s*/g, '');
|
|
456
|
-
// Remove imports específicos do Next.js
|
|
457
|
-
content = content.replace(/import\s+.*?from\s+['"]next\/.*?['"];?\s*/g, '');
|
|
458
|
-
// Substitui Link do Next.js por Link do HightJS
|
|
459
|
-
if (content.includes('Link')) {
|
|
460
|
-
content = `import { Link } from 'hightjs/client';\n` + content;
|
|
461
|
-
}
|
|
462
|
-
// Substitui useRouter do Next.js
|
|
463
|
-
content = content.replace(/import\s*\{\s*useRouter\s*\}\s*from\s*['"]next\/router['"]/g, "import { useRouter } from 'hightjs/client'");
|
|
464
|
-
// Substitui Image do Next.js por img normal (com comentário para revisão)
|
|
465
|
-
content = content.replace(/<Image\s+/g, '<img /* TODO: Migrado de Next.js Image - revisar otimizações */ ');
|
|
466
|
-
// Remove getServerSideProps, getStaticProps, getStaticPaths
|
|
467
|
-
content = content.replace(/export\s+(async\s+)?function\s+get(ServerSideProps|StaticProps|StaticPaths)\s*\([^)]*\)\s*\{[\s\S]*?\n\}/g, '');
|
|
468
|
-
// Adiciona comentário sobre metadata se houver
|
|
469
|
-
if (content.includes('export const metadata')) {
|
|
470
|
-
content = '// TODO: Migrar metadata do Next.js para HightJS\n' + content;
|
|
471
|
-
}
|
|
472
|
-
return content.trim();
|
|
473
|
-
}
|
|
474
|
-
function filterDependencies(deps) {
|
|
475
|
-
if (!deps)
|
|
476
|
-
return {};
|
|
477
|
-
const filtered = { ...deps };
|
|
478
|
-
// Remove dependências específicas do Next.js
|
|
479
|
-
delete filtered.next;
|
|
480
|
-
delete filtered['@next/font'];
|
|
481
|
-
delete filtered['next-auth'];
|
|
482
|
-
return filtered;
|
|
483
|
-
}
|
|
484
185
|
// Faz o "parse" dos argumentos passados na linha de comando
|
|
485
186
|
program.parse(process.argv);
|