@untrustnova/nova-cli 1.0.0 → 1.1.0

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/README.md CHANGED
@@ -33,7 +33,8 @@ npm run dev
33
33
  ## Struktur Template
34
34
 
35
35
  Template menggunakan:
36
- - `nova.config.js` sebagai konfigurasi utama (tanpa `vite.config.js`)
36
+ - `nova.config.js` sebagai konfigurasi utama
37
+ - `vite.config.js` di-generate dari `nova.config.js`
37
38
  - Routing hybrid (object + file-based)
38
39
  - Folder `app/` untuk server-side
39
40
  - Folder `web/` untuk frontend React
@@ -44,7 +45,11 @@ Template menggunakan:
44
45
  (`@untrustnova/nova-framework` termasuk). Gunakan `--no-install` jika ingin skip instalasi,
45
46
  atau set `NOVA_TEMPLATE_REPO` untuk mengganti repo template.
46
47
 
47
- `nova dev` akan menjalankan `server.js` dan Vite dev server secara bersamaan.
48
+ `nova dev` menjalankan `server.js` dan Vite dev server secara bersamaan.
49
+
50
+ Dev URLs:
51
+ - Backend API: `http://localhost:3000`
52
+ - Frontend (Vite): `http://localhost:5173`
48
53
 
49
54
  Perintah `db:*` adalah pembungkus untuk `drizzle-kit`.
50
55
  Jika belum terpasang, install: `npm install -D drizzle-kit`.
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@untrustnova/nova-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Nova.js CLI",
5
5
  "type": "module",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
6
9
  "bin": {
7
10
  "nova": "./bin/nova.js"
8
11
  },
package/src/cli.js CHANGED
@@ -17,7 +17,7 @@ export async function run(args) {
17
17
  }
18
18
 
19
19
  if (command === '--version' || command === '-v') {
20
- console.log('nova-cli 0.1.0');
20
+ logInfo('Nova CLI v1.1.0');
21
21
  return;
22
22
  }
23
23
 
@@ -48,17 +48,19 @@ export async function run(args) {
48
48
  await createMigration(rest[0]);
49
49
  break;
50
50
  default:
51
- console.error(`[nova] unknown command "${command}"`);
51
+ logError(`Unknown command "${command}"`);
52
52
  printHelp();
53
53
  }
54
54
  } catch (error) {
55
- console.error(`[nova] ${error.message}`);
55
+ logError(error.message);
56
56
  process.exitCode = 1;
57
57
  }
58
58
  }
59
59
 
60
60
  function printHelp() {
61
- console.log(`Nova CLI
61
+ console.log(`[nova] Nova CLI
62
+ Version: 1.1.0
63
+ Node: ${process.version}
62
64
 
63
65
  Usage:
64
66
  nova new <name> [--no-install]
@@ -91,7 +93,7 @@ async function scaffoldProject(args) {
91
93
  }
92
94
 
93
95
  const remoteTemplate = await tryFetchTemplate(
94
- process.env.NOVA_TEMPLATE_REPO || 'https://github.com/nova-js/nova',
96
+ process.env.NOVA_TEMPLATE_REPO || 'https://github.com/untrustnova/nova',
95
97
  );
96
98
 
97
99
  const source = remoteTemplate?.path || templateRoot;
@@ -99,7 +101,9 @@ async function scaffoldProject(args) {
99
101
  '__APP_NAME__': name,
100
102
  });
101
103
 
102
- console.log(`[nova] project created at ${target}`);
104
+ await ensureViteConfig(target);
105
+
106
+ logSuccess(`Project created at ${target}`);
103
107
 
104
108
  if (remoteTemplate?.cleanup) {
105
109
  await remoteTemplate.cleanup();
@@ -107,7 +111,7 @@ async function scaffoldProject(args) {
107
111
 
108
112
  if (shouldInstall) {
109
113
  await runCommand('npm', ['install'], { cwd: target });
110
- console.log('[nova] dependencies installed');
114
+ logSuccess('Dependencies installed');
111
115
  }
112
116
  }
113
117
 
@@ -133,7 +137,7 @@ export default class ${className}Controller extends Controller {
133
137
  `;
134
138
 
135
139
  await writeFile(target, body, 'utf8');
136
- console.log(`[nova] controller created: ${relativePath(target)}`);
140
+ logSuccess(`Controller created: ${relativePath(target)}`);
137
141
  }
138
142
 
139
143
  async function createMiddleware(name) {
@@ -156,7 +160,7 @@ async function createMiddleware(name) {
156
160
  `;
157
161
 
158
162
  await writeFile(target, body, 'utf8');
159
- console.log(`[nova] middleware created: ${relativePath(target)}`);
163
+ logSuccess(`Middleware created: ${relativePath(target)}`);
160
164
  }
161
165
 
162
166
  async function createMigration(name) {
@@ -181,7 +185,7 @@ export async function down(db) {
181
185
  `;
182
186
 
183
187
  await writeFile(target, body, 'utf8');
184
- console.log(`[nova] migration created: ${relativePath(target)}`);
188
+ logSuccess(`Migration created: ${relativePath(target)}`);
185
189
  }
186
190
 
187
191
  async function runDrizzle(command) {
@@ -198,16 +202,35 @@ async function runDev() {
198
202
  shell: process.platform === 'win32',
199
203
  });
200
204
 
201
- process.on('SIGINT', () => server.kill('SIGINT'));
202
- process.on('SIGTERM', () => server.kill('SIGTERM'));
205
+ let viteServer = null;
206
+ let shuttingDown = false;
207
+
208
+ const shutdown = async (signal) => {
209
+ if (shuttingDown) return;
210
+ shuttingDown = true;
211
+ server.kill(signal);
212
+ if (viteServer) {
213
+ await viteServer.close();
214
+ }
215
+ process.exit(0);
216
+ };
217
+
218
+ process.on('SIGINT', () => shutdown('SIGINT'));
219
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
203
220
 
204
- await runViteDev(config);
221
+ try {
222
+ viteServer = await runViteDev(config);
223
+ } catch (error) {
224
+ logError(error.message);
225
+ await shutdown('SIGTERM');
226
+ }
205
227
  }
206
228
 
207
229
  async function runBuild() {
208
230
  const config = await loadNovaConfig();
209
231
  const { runBuild: runViteBuild } = await loadFrameworkModule('dev');
210
232
  await runViteBuild(config);
233
+ logSuccess('Build completed');
211
234
  }
212
235
 
213
236
  async function runCommand(command, args, options = {}) {
@@ -333,6 +356,27 @@ function relativePath(path) {
333
356
  return path.replace(process.cwd() + '/', '');
334
357
  }
335
358
 
359
+ async function ensureViteConfig(targetRoot) {
360
+ const viteConfigPath = resolve(targetRoot, 'vite.config.js');
361
+ if (await pathExists(viteConfigPath)) return;
362
+
363
+ const contents = `import { defineConfig } from 'vite';\nimport novaConfig from './nova.config.js';\nimport { resolveViteConfig } from '@untrustnova/nova-framework/config/resolveVite';\n\nexport default defineConfig(async () => resolveViteConfig(novaConfig));\n`;
364
+ await writeFile(viteConfigPath, contents, 'utf8');
365
+ logSuccess('vite.config.js generated from nova.config.js');
366
+ }
367
+
368
+ function logInfo(message) {
369
+ console.log(`[nova] ${message}`);
370
+ }
371
+
372
+ function logSuccess(message) {
373
+ console.log(`[nova] ${message}`);
374
+ }
375
+
376
+ function logError(message) {
377
+ console.error(`[nova] ${message}`);
378
+ }
379
+
336
380
  async function loadNovaConfig() {
337
381
  const configPath = resolve(process.cwd(), 'nova.config.js');
338
382
  const module = await import(pathToFileURL(configPath));
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "__APP_NAME__",
3
- "version": "0.1.0",
3
+ "version": "1.1.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -12,15 +12,19 @@
12
12
  "dependencies": {
13
13
  "dotenv": "^16.4.5",
14
14
  "drizzle-orm": "^0.40.0",
15
- "@untrustnova/nova-framework": "^0.1.0",
15
+ "@untrustnova/nova-framework": "^1.1.0",
16
16
  "react": "^19.0.0",
17
17
  "react-dom": "^19.0.0"
18
18
  },
19
19
  "devDependencies": {
20
+ "@tailwindcss/postcss": "^4.0.0",
20
21
  "@vitejs/plugin-react": "^4.3.4",
22
+ "autoprefixer": "^10.4.20",
21
23
  "eslint": "^9.18.0",
22
24
  "eslint-config-prettier": "^9.1.0",
23
25
  "eslint-plugin-react": "^7.37.3",
26
+ "postcss": "^8.4.41",
27
+ "tailwindcss": "^4.0.0",
24
28
  "vite": "^6.0.0"
25
29
  }
26
- }
30
+ }
@@ -0,0 +1,5 @@
1
+ import { defineConfig } from 'vite';
2
+ import novaConfig from './nova.config.js';
3
+ import { resolveViteConfig } from '@untrustnova/nova-framework/config/resolveVite';
4
+
5
+ export default defineConfig(async () => resolveViteConfig(novaConfig));
@@ -1,3 +1,4 @@
1
+ import React from 'react';
1
2
  import Hero from './components/Hero.jsx';
2
3
 
3
4
  export default function App() {
@@ -1,3 +1,5 @@
1
+ import React from 'react';
2
+
1
3
  export default function Hero() {
2
4
  return (
3
5
  <main className="hero">