create-nextify 0.1.10 → 0.1.13
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/devServer.js +156 -0
- package/dist/index.js +47 -26
- package/package.json +11 -5
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { createServer as createHttpServer } from 'node:http';
|
|
4
|
+
const PORT = Number(process.env.PORT ?? 3000);
|
|
5
|
+
const PROJECT_ROOT = process.env.NEXTIFY_ROOT ?? process.cwd();
|
|
6
|
+
const PAGES_DIR = path.join(PROJECT_ROOT, 'pages');
|
|
7
|
+
function toRoutePath(filePath) {
|
|
8
|
+
const clean = filePath
|
|
9
|
+
.replace(/\\/g, '/')
|
|
10
|
+
.replace(/^pages\//, '')
|
|
11
|
+
.replace(/\.(t|j)sx?$/, '')
|
|
12
|
+
.replace(/index$/, '');
|
|
13
|
+
if (!clean)
|
|
14
|
+
return '/';
|
|
15
|
+
return '/' + clean.split('/').filter(Boolean)
|
|
16
|
+
.map((seg) => seg.replace(/^\[(.+)\]$/, ':$1')).join('/');
|
|
17
|
+
}
|
|
18
|
+
function buildRouteManifest(files) {
|
|
19
|
+
return files.map((file) => {
|
|
20
|
+
const normalized = file.replace(/\\/g, '/');
|
|
21
|
+
const routePath = toRoutePath(normalized);
|
|
22
|
+
const base = path.basename(normalized);
|
|
23
|
+
const kind = normalized.includes('/api/') ? 'api'
|
|
24
|
+
: base.startsWith('middleware') ? 'middleware' : 'page';
|
|
25
|
+
return { file: normalized, routePath, kind };
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function getPageFiles() {
|
|
29
|
+
if (!fs.existsSync(PAGES_DIR))
|
|
30
|
+
return [];
|
|
31
|
+
const files = [];
|
|
32
|
+
function walk(dir, base = '') {
|
|
33
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
34
|
+
const rel = base ? `${base}/${entry.name}` : entry.name;
|
|
35
|
+
if (entry.isDirectory())
|
|
36
|
+
walk(path.join(dir, entry.name), rel);
|
|
37
|
+
else if (/\.(tsx|ts|jsx|js)$/.test(entry.name))
|
|
38
|
+
files.push(`pages/${rel}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
walk(PAGES_DIR);
|
|
42
|
+
return files;
|
|
43
|
+
}
|
|
44
|
+
function buildHtmlShell(routePath) {
|
|
45
|
+
const fileHint = routePath === '/' ? '/pages/index' : `/pages${routePath}`;
|
|
46
|
+
const candidates = JSON.stringify([
|
|
47
|
+
`${fileHint}.tsx`, `${fileHint}.jsx`, `${fileHint}.ts`, `${fileHint}.js`,
|
|
48
|
+
]);
|
|
49
|
+
return `<!DOCTYPE html>
|
|
50
|
+
<html lang="pt-BR">
|
|
51
|
+
<head>
|
|
52
|
+
<meta charset="UTF-8" />
|
|
53
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
54
|
+
<title>Nextify.js</title>
|
|
55
|
+
</head>
|
|
56
|
+
<body>
|
|
57
|
+
<div id="root"></div>
|
|
58
|
+
<script type="module">
|
|
59
|
+
import { createElement } from 'react';
|
|
60
|
+
import { createRoot } from 'react-dom/client';
|
|
61
|
+
const candidates = ${candidates};
|
|
62
|
+
async function loadPage() {
|
|
63
|
+
let mod;
|
|
64
|
+
for (const c of candidates) { try { mod = await import(c); break; } catch {} }
|
|
65
|
+
const root = document.getElementById('root');
|
|
66
|
+
if (!mod?.default) {
|
|
67
|
+
root.innerHTML = '<div style="font-family:monospace;padding:2rem;color:#e53e3e"><h2>404 — Página não encontrada</h2></div>';
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
createRoot(root).render(createElement(mod.default));
|
|
71
|
+
}
|
|
72
|
+
loadPage().catch((err) => {
|
|
73
|
+
document.getElementById('root').innerHTML = '<pre style="color:red;padding:2rem">' + err.stack + '</pre>';
|
|
74
|
+
});
|
|
75
|
+
</script>
|
|
76
|
+
</body>
|
|
77
|
+
</html>`;
|
|
78
|
+
}
|
|
79
|
+
export async function startDevServer(options = {}) {
|
|
80
|
+
const root = options.root ?? PROJECT_ROOT;
|
|
81
|
+
const port = options.port ?? PORT;
|
|
82
|
+
// Resolve vite e plugin-react a partir do projeto do usuário (node_modules local)
|
|
83
|
+
const vitePath = path.join(root, 'node_modules', 'vite', 'dist', 'node', 'index.js');
|
|
84
|
+
const reactPluginPath = path.join(root, 'node_modules', '@vitejs', 'plugin-react', 'dist', 'index.mjs');
|
|
85
|
+
let createViteServer, react;
|
|
86
|
+
try {
|
|
87
|
+
({ createServer: createViteServer } = await import(vitePath));
|
|
88
|
+
({ default: react } = await import(reactPluginPath));
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
console.error('[nextify] Vite não encontrado. Rode: npm install vite @vitejs/plugin-react');
|
|
92
|
+
console.error(err.message);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
const vite = await createViteServer({
|
|
96
|
+
root,
|
|
97
|
+
server: { middlewareMode: true },
|
|
98
|
+
appType: 'custom',
|
|
99
|
+
plugins: [react()],
|
|
100
|
+
resolve: { alias: { '@': path.join(root, 'src') } },
|
|
101
|
+
});
|
|
102
|
+
const manifest = buildRouteManifest(getPageFiles());
|
|
103
|
+
console.log('\n nextify dev\n');
|
|
104
|
+
for (const r of manifest)
|
|
105
|
+
console.log(` ${r.kind === 'api' ? '⚡' : '○'} ${r.routePath}`);
|
|
106
|
+
console.log('');
|
|
107
|
+
const server = createHttpServer(async (req, res) => {
|
|
108
|
+
const host = req.headers.host ?? `localhost:${port}`;
|
|
109
|
+
const pathname = new URL(`http://${host}${req.url}`).pathname;
|
|
110
|
+
if (pathname.startsWith('/@') || pathname.startsWith('/node_modules') ||
|
|
111
|
+
/\.(js|ts|tsx|jsx|css|svg|png|ico|woff2?|map)$/.test(pathname)) {
|
|
112
|
+
vite.middlewares(req, res, () => { res.statusCode = 404; res.end(); });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const apiRoute = manifest.find((r) => r.kind === 'api' && r.routePath === pathname);
|
|
116
|
+
if (apiRoute) {
|
|
117
|
+
try {
|
|
118
|
+
const mod = await vite.ssrLoadModule(path.join(root, apiRoute.file));
|
|
119
|
+
if (typeof mod.default !== 'function')
|
|
120
|
+
throw new Error('API route sem default export');
|
|
121
|
+
const webReq = new globalThis.Request(`http://${host}${req.url}`, { method: req.method ?? 'GET' });
|
|
122
|
+
const webRes = await mod.default(webReq);
|
|
123
|
+
res.statusCode = webRes.status;
|
|
124
|
+
for (const [k, v] of webRes.headers.entries())
|
|
125
|
+
res.setHeader(k, v);
|
|
126
|
+
res.end(Buffer.from(await webRes.arrayBuffer()));
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
vite.ssrFixStacktrace(err);
|
|
130
|
+
res.statusCode = 500;
|
|
131
|
+
res.setHeader('content-type', 'application/json');
|
|
132
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const html = await vite.transformIndexHtml(req.url ?? '/', buildHtmlShell(pathname));
|
|
138
|
+
const found = manifest.some((r) => r.kind === 'page' && r.routePath === pathname);
|
|
139
|
+
res.statusCode = found ? 200 : 404;
|
|
140
|
+
res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
141
|
+
res.end(html);
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
vite.ssrFixStacktrace(err);
|
|
145
|
+
res.statusCode = 500;
|
|
146
|
+
res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
147
|
+
res.end(`<pre style="color:red;padding:2rem">${err.stack}</pre>`);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
server.listen(port, () => {
|
|
151
|
+
console.log(` ➜ Local: \x1b[36mhttp://localhost:${port}\x1b[0m`);
|
|
152
|
+
console.log(` ➜ HMR: ativo\n`);
|
|
153
|
+
});
|
|
154
|
+
return { server, vite };
|
|
155
|
+
}
|
|
156
|
+
startDevServer();
|
package/dist/index.js
CHANGED
|
@@ -12,43 +12,69 @@ function createProject(target = 'nextify-app') {
|
|
|
12
12
|
writeFileSync(join(root, 'package.json'), JSON.stringify({
|
|
13
13
|
name: target,
|
|
14
14
|
private: true,
|
|
15
|
+
type: 'module',
|
|
15
16
|
scripts: {
|
|
16
17
|
dev: 'nextify dev',
|
|
17
18
|
build: 'nextify build',
|
|
18
|
-
start: 'nextify start'
|
|
19
|
+
start: 'nextify start',
|
|
20
|
+
},
|
|
21
|
+
dependencies: {
|
|
22
|
+
react: '^18.3.1',
|
|
23
|
+
'react-dom': '^18.3.1',
|
|
19
24
|
},
|
|
20
25
|
devDependencies: {
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
'create-nextify': 'latest',
|
|
27
|
+
vite: '^5.4.19',
|
|
28
|
+
'@vitejs/plugin-react': '^4.3.4',
|
|
29
|
+
'@types/react': '^18.3.1',
|
|
30
|
+
'@types/react-dom': '^18.3.1',
|
|
31
|
+
typescript: '^5.0.0',
|
|
32
|
+
},
|
|
23
33
|
}, null, 2));
|
|
24
34
|
writeFileSync(join(root, 'pages', 'index.tsx'), `export default function Home() {
|
|
25
35
|
return (
|
|
26
|
-
<main>
|
|
36
|
+
<main style={{ fontFamily: 'sans-serif', padding: '2rem' }}>
|
|
27
37
|
<h1>Bem-vindo ao Nextify.js 🚀</h1>
|
|
38
|
+
<p>Edite <code>pages/index.tsx</code> para começar.</p>
|
|
28
39
|
</main>
|
|
29
40
|
);
|
|
30
41
|
}
|
|
31
42
|
`);
|
|
32
43
|
writeFileSync(join(root, 'pages', 'api', 'health.ts'), `export default async function handler() {
|
|
33
44
|
return new Response(JSON.stringify({ ok: true }), {
|
|
34
|
-
headers: { 'content-type': 'application/json' }
|
|
45
|
+
headers: { 'content-type': 'application/json' },
|
|
35
46
|
});
|
|
36
47
|
}
|
|
37
48
|
`);
|
|
49
|
+
writeFileSync(join(root, 'tsconfig.json'), JSON.stringify({
|
|
50
|
+
compilerOptions: {
|
|
51
|
+
target: 'ES2022',
|
|
52
|
+
module: 'ESNext',
|
|
53
|
+
moduleResolution: 'bundler',
|
|
54
|
+
jsx: 'react-jsx',
|
|
55
|
+
strict: true,
|
|
56
|
+
esModuleInterop: true,
|
|
57
|
+
skipLibCheck: true,
|
|
58
|
+
},
|
|
59
|
+
include: ['pages', 'src'],
|
|
60
|
+
}, null, 2));
|
|
38
61
|
console.log(`\n✔ Projeto criado em: ${root}`);
|
|
39
62
|
console.log('\nPróximos passos:\n');
|
|
40
63
|
console.log(` cd ${target}`);
|
|
41
64
|
console.log(' npm install');
|
|
42
65
|
console.log(' npm run dev\n');
|
|
43
66
|
}
|
|
44
|
-
function runDevServer(port) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
async function runDevServer(port) {
|
|
68
|
+
try {
|
|
69
|
+
// devServer.js está embutido no próprio pacote create-nextify (dist/devServer.js)
|
|
70
|
+
const devServerUrl = new URL('./devServer.js', import.meta.url).href;
|
|
71
|
+
const { startDevServer } = await import(devServerUrl);
|
|
72
|
+
await startDevServer({ root: process.cwd(), port });
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
console.error('[nextify] Erro ao iniciar dev server:', err.message);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
52
78
|
}
|
|
53
79
|
function runProdServer(port) {
|
|
54
80
|
const server = http.createServer((_req, res) => {
|
|
@@ -56,16 +82,13 @@ function runProdServer(port) {
|
|
|
56
82
|
res.end('Nextify production server ativo 🚀');
|
|
57
83
|
});
|
|
58
84
|
server.listen(port, () => {
|
|
59
|
-
console.log(`Nextify start
|
|
85
|
+
console.log(`Nextify start em http://localhost:${port}`);
|
|
60
86
|
});
|
|
61
87
|
}
|
|
62
88
|
function runBuild() {
|
|
63
89
|
mkdirSync(join(process.cwd(), 'dist'), { recursive: true });
|
|
64
|
-
writeFileSync(join(process.cwd(), 'dist', 'route-manifest.json'), JSON.stringify({
|
|
65
|
-
|
|
66
|
-
note: 'Manifesto de rotas gerado pelo CLI do Nextify.'
|
|
67
|
-
}, null, 2));
|
|
68
|
-
console.log('✔ Build do Nextify concluído. Artefatos em dist/');
|
|
90
|
+
writeFileSync(join(process.cwd(), 'dist', 'route-manifest.json'), JSON.stringify({ generatedAt: new Date().toISOString() }, null, 2));
|
|
91
|
+
console.log('✔ Build concluído. Artefatos em dist/');
|
|
69
92
|
}
|
|
70
93
|
function showHelp() {
|
|
71
94
|
console.log(`
|
|
@@ -90,19 +113,17 @@ const command = args[0];
|
|
|
90
113
|
const portArg = Number(process.env.PORT ?? args[1] ?? 3000);
|
|
91
114
|
const port = Number.isFinite(portArg) ? portArg : 3000;
|
|
92
115
|
switch (command) {
|
|
93
|
-
case
|
|
116
|
+
case 'create':
|
|
94
117
|
createProject(args[1]);
|
|
95
118
|
break;
|
|
96
|
-
case
|
|
119
|
+
case 'dev':
|
|
97
120
|
runDevServer(port);
|
|
98
121
|
break;
|
|
99
|
-
case
|
|
122
|
+
case 'build':
|
|
100
123
|
runBuild();
|
|
101
124
|
break;
|
|
102
|
-
case
|
|
125
|
+
case 'start':
|
|
103
126
|
runProdServer(port);
|
|
104
127
|
break;
|
|
105
|
-
default:
|
|
106
|
-
// suporta: npx create-nextify minha-app
|
|
107
|
-
createProject(command);
|
|
128
|
+
default: createProject(command);
|
|
108
129
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-nextify",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "CLI para criar aplicações Nextify.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,17 +11,23 @@
|
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "
|
|
15
|
-
"typecheck": "
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
16
|
"test": "vitest run --config ./vitest.config.ts",
|
|
17
|
-
"lint": "
|
|
17
|
+
"lint": "echo 'sem lint configurado neste pacote'"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"vite": "^5.4.19",
|
|
21
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
22
|
+
"react": "^18.3.1",
|
|
23
|
+
"react-dom": "^18.3.1"
|
|
18
24
|
},
|
|
19
25
|
"keywords": [
|
|
20
26
|
"nextify",
|
|
21
27
|
"framework",
|
|
22
28
|
"cli"
|
|
23
29
|
],
|
|
24
|
-
"author": "
|
|
30
|
+
"author": "",
|
|
25
31
|
"license": "MIT",
|
|
26
32
|
"devDependencies": {
|
|
27
33
|
"typescript": "^5.9.3"
|