@nexusts/cli 0.8.3 → 0.9.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/dist/index.js CHANGED
@@ -19,7 +19,7 @@ var __toESM = (mod, isNodeMode, target) => {
19
19
  var __require = import.meta.require;
20
20
 
21
21
  // packages/cli/src/commands/info.ts
22
- import { resolve as resolve4 } from "path";
22
+ import { resolve as resolve5 } from "path";
23
23
 
24
24
  // packages/cli/src/core/args.ts
25
25
  var LONG_RE = /^--([^=]+)(?:=(.*))?$/;
@@ -511,67 +511,9 @@ function loadVersion() {
511
511
  }
512
512
  }
513
513
  var VERSION = loadVersion();
514
- // packages/cli/src/commands/info.ts
515
- var infoCommand = {
516
- name: "info",
517
- aliases: ["i"],
518
- summary: "Show project configuration",
519
- description: "Prints the resolved nx.config.ts plus relevant env vars.",
520
- async run(ctx) {
521
- logger.heading("NexusTS CLI \u2014 Project Info");
522
- logger.info(colors.bold("Resolved configuration"));
523
- logger.blank();
524
- logger.table([
525
- ["routing", String(ctx.config.routing)],
526
- ["view", String(ctx.config.view)],
527
- ["orm", String(ctx.config.orm)],
528
- ["dialect", String(ctx.config.dialect ?? "(none)")],
529
- ["database.driver", String(ctx.config.database.driver)],
530
- ["database.url", String(ctx.config.database.url)],
531
- ["inertia.frontend", String(ctx.config.inertia.frontend)],
532
- ["inertia.ssr", String(ctx.config.inertia.ssr)],
533
- ["inertia.version", String(ctx.config.inertia.version)]
534
- ]);
535
- logger.blank();
536
- logger.info(colors.bold("Paths"));
537
- logger.blank();
538
- for (const [k, v] of Object.entries(ctx.config.paths)) {
539
- logger.table([[k, v]]);
540
- }
541
- logger.blank();
542
- logger.info(colors.bold("Environment"));
543
- logger.blank();
544
- const envKeys = [
545
- "NODE_ENV",
546
- "PORT",
547
- "NEXUS_DEBUG",
548
- "NO_COLOR",
549
- "FORCE_COLOR",
550
- "NX_ROUTING",
551
- "NX_VIEW",
552
- "NX_ORM",
553
- "NX_DATABASE_DRIVER",
554
- "NX_DATABASE_URL",
555
- "NX_INERTIA_FRONTEND",
556
- "NX_INERTIA_SSR"
557
- ];
558
- for (const k of envKeys) {
559
- const v = process.env[k];
560
- logger.table([[k, v === undefined ? colors.dim("(unset)") : v]]);
561
- }
562
- logger.blank();
563
- logger.info(colors.bold("Working directory"));
564
- logger.blank();
565
- logger.info(` ${resolve4(ctx.cwd)}`);
566
- logger.blank();
567
- return 0;
568
- }
569
- };
570
- var info_default = infoCommand;
571
-
572
- // packages/cli/src/commands/init.ts
573
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
574
- import { resolve as resolve5 } from "path";
514
+ // packages/cli/src/core/scaffold.ts
515
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
516
+ import { resolve as resolve4 } from "path";
575
517
 
576
518
  // packages/cli/src/templates/controller/adonis.ts
577
519
  var adonis_default = `
@@ -636,45 +578,51 @@ export const {{ camel }}Routes = {
636
578
 
637
579
  // packages/cli/src/templates/controller/nest.ts
638
580
  var nest_default = `
639
- import { Body, Controller, Delete, Get, Inject, Param, Post, Put } from '@nexusts/core';
581
+ import { Controller, Delete, Get, Inject, Post, Put, inputValue } from '@nexusts/core';
582
+ import type { Context } from 'hono';
640
583
  import { {{ service }} } from '../services/{{ kebab }}.service.js';
641
584
 
642
585
  @Controller('/{{ kebab }}s')
643
586
  export class {{ name }}Controller {
644
- constructor(@Inject({{ service }}) private readonly {{ serviceCamel }}: {{ service }}) {}
587
+ @Inject({{ service }}) declare {{ serviceCamel }}: {{ service }};
645
588
 
646
589
  @Get('/')
647
- async index() {
590
+ async index(ctx: Context) {
648
591
  return this.{{ serviceCamel }}.findAll();
649
592
  }
650
593
 
651
594
  @Get('/:id')
652
- async show(@Param('id') id: string) {
653
- return this.{{ serviceCamel }}.findOne(Number(id));
595
+ async show(ctx: Context) {
596
+ const id = inputValue(ctx.req.param('id')).number().required().value();
597
+ return this.{{ serviceCamel }}.findOne(id);
654
598
  }
655
599
 
656
600
  @Post('/')
657
- async create(@Body() body: any) {
658
- return { status: 201, body: this.{{ serviceCamel }}.create(body) };
601
+ async create(ctx: Context) {
602
+ const body = await ctx.req.json();
603
+ return { status: 201, body: await this.{{ serviceCamel }}.create(body) };
659
604
  }
660
605
 
661
606
  @Put('/:id')
662
- async update(@Param('id') id: string, @Body() body: any) {
663
- return this.{{ serviceCamel }}.update(Number(id), body);
607
+ async update(ctx: Context) {
608
+ const id = inputValue(ctx.req.param('id')).number().required().value();
609
+ const body = await ctx.req.json();
610
+ return this.{{ serviceCamel }}.update(id, body);
664
611
  }
665
612
 
666
613
  @Delete('/:id')
667
- async destroy(@Param('id') id: string) {
668
- return this.{{ serviceCamel }}.delete(Number(id));
614
+ async destroy(ctx: Context) {
615
+ const id = inputValue(ctx.req.param('id')).number().required().value();
616
+ return this.{{ serviceCamel }}.delete(id);
669
617
  }
670
618
  }
671
619
  `.trimStart();
672
620
 
673
621
  // packages/cli/src/templates/crud/controller.ts
674
622
  var controller_default = `
675
- import { Body, Controller, Delete, Get, Inject, Param, Post, Put } from '@nexusts/core';
623
+ import { Controller, Delete, Get, Inject, Post, Put, inputValue } from '@nexusts/core';
676
624
  import { z } from 'zod';
677
- import { Validate } from '@nexusts/core';
625
+ import type { Context } from 'hono';
678
626
  import { {{ service }} } from '../services/{{ kebab }}.service.js';
679
627
  {{#hasInertia}}import { Inertia } from '@nexusts/view';{{/hasInertia}}
680
628
 
@@ -685,13 +633,11 @@ const Create{{ name }}Schema = z.object({
685
633
 
686
634
  @Controller('/{{ kebab }}s')
687
635
  export class {{ controller }} {
688
- constructor(
689
- @Inject({{ service }}) private readonly {{ camel }}Service: {{ service }},
690
- {{#hasInertia}} @Inject(Inertia.TOKEN) private readonly inertia: Inertia,{{/hasInertia}}
691
- ) {}
636
+ @Inject({{ service }}) declare {{ camel }}Service: {{ service }};
637
+ {{#hasInertia}} @Inject(Inertia.TOKEN) declare inertia: Inertia;{{/hasInertia}}
692
638
 
693
639
  @Get('/')
694
- async index() {
640
+ async index(ctx: Context) {
695
641
  const items = await this.{{ camel }}Service.findAll();
696
642
  {{#hasInertia}}
697
643
  return this.inertia.render('{{ viewComponent }}', { items });
@@ -702,8 +648,9 @@ export class {{ controller }} {
702
648
  }
703
649
 
704
650
  @Get('/:id')
705
- async show(@Param('id') id: string) {
706
- const item = await this.{{ camel }}Service.findOne(Number(id));
651
+ async show(ctx: Context) {
652
+ const id = inputValue(ctx.req.param('id')).number().required().value();
653
+ const item = await this.{{ camel }}Service.findOne(id);
707
654
  {{#hasInertia}}
708
655
  return this.inertia.render('{{ viewShowComponent }}', { item });
709
656
  {{/hasInertia}}
@@ -713,23 +660,22 @@ export class {{ controller }} {
713
660
  }
714
661
 
715
662
  @Post('/')
716
- @Validate({ body: Create{{ name }}Schema })
717
- async create(@Body() body: z.infer<typeof Create{{ name }}Schema>) {
663
+ async create(ctx: Context) {
664
+ const body = Create{{ name }}Schema.parse(await ctx.req.json());
718
665
  return { status: 201, body: await this.{{ camel }}Service.create(body) };
719
666
  }
720
667
 
721
668
  @Put('/:id')
722
- @Validate({ body: Create{{ name }}Schema.partial() })
723
- async update(
724
- @Param('id') id: string,
725
- @Body() body: Partial<z.infer<typeof Create{{ name }}Schema>>,
726
- ) {
727
- return await this.{{ camel }}Service.update(Number(id), body);
669
+ async update(ctx: Context) {
670
+ const id = inputValue(ctx.req.param('id')).number().required().value();
671
+ const body = Create{{ name }}Schema.partial().parse(await ctx.req.json());
672
+ return await this.{{ camel }}Service.update(id, body);
728
673
  }
729
674
 
730
675
  @Delete('/:id')
731
- async destroy(@Param('id') id: string) {
732
- return await this.{{ camel }}Service.delete(Number(id));
676
+ async destroy(ctx: Context) {
677
+ const id = inputValue(ctx.req.param('id')).number().required().value();
678
+ return await this.{{ camel }}Service.delete(id);
733
679
  }
734
680
  }
735
681
  `.trimStart();
@@ -1066,26 +1012,21 @@ import type { {{ name }}, New{{ name }} } from '../models/{{ kebab }}.model.js';
1066
1012
 
1067
1013
  @Injectable()
1068
1014
  export class {{ repository }} extends DrizzleRepository<typeof {{ snake }}, {{ name }}> {
1069
- constructor(
1070
- @Inject(DrizzleService.TOKEN) db: DrizzleService,
1071
- ) {
1072
- super(db, {{ snake }});
1073
- }
1015
+ @Inject(DrizzleService.TOKEN) declare db: DrizzleService;
1016
+ protected readonly table = {{ snake }};
1074
1017
  }
1075
1018
  `.trimStart();
1076
1019
 
1077
1020
  // packages/cli/src/templates/service/service.ts
1078
1021
  var service_default = `
1079
- import { Inject, Injectable } from '@nexusts/core';
1022
+ import { Injectable, Inject } from '@nexusts/core';
1080
1023
  {{#hasRepo}}import { eq } from '@nexusts/drizzle';
1081
1024
  import { {{ repository }} } from '../repositories/{{ kebab }}.repository.js';
1082
1025
  import { {{ snake }} } from '../models/{{ kebab }}.model.js';{{/hasRepo}}
1083
1026
 
1084
1027
  @Injectable()
1085
1028
  export class {{ name }}Service {
1086
- constructor({{#hasRepo}}
1087
- @Inject({{ repository }}) private readonly {{ repositoryCamel }}: {{ repository }},
1088
- {{/hasRepo}}) {}
1029
+ {{#hasRepo}}@Inject({{ repository }}) declare {{ repositoryCamel }}: {{ repository }};{{/hasRepo}}
1089
1030
 
1090
1031
  async findAll() {
1091
1032
  {{#hasRepo}}return this.{{ repositoryCamel }}.findAll();{{/hasRepo}}
@@ -1164,140 +1105,506 @@ var templates = {
1164
1105
  }
1165
1106
  };
1166
1107
 
1108
+ // packages/cli/src/core/scaffold.ts
1109
+ function ensureDirectories(target, view) {
1110
+ mkdirSync2(resolve4(target, "app/controllers"), { recursive: true });
1111
+ mkdirSync2(resolve4(target, "app/modules"), { recursive: true });
1112
+ mkdirSync2(resolve4(target, "app/services"), { recursive: true });
1113
+ mkdirSync2(resolve4(target, "app/models"), { recursive: true });
1114
+ mkdirSync2(resolve4(target, "app/repositories"), { recursive: true });
1115
+ mkdirSync2(resolve4(target, "app/dto"), { recursive: true });
1116
+ mkdirSync2(resolve4(target, "public"), { recursive: true });
1117
+ if (view === "inertia") {
1118
+ mkdirSync2(resolve4(target, "resources/js/Pages"), { recursive: true });
1119
+ } else if (view !== "none") {
1120
+ mkdirSync2(resolve4(target, "resources/views"), { recursive: true });
1121
+ }
1122
+ }
1123
+ function computeDeps(view, orm, db, frontend) {
1124
+ const deps = {
1125
+ "@nexusts/core": "*",
1126
+ hono: "^4.6.0",
1127
+ zod: "^3.23.8"
1128
+ };
1129
+ const devDeps = {};
1130
+ if (orm === "drizzle") {
1131
+ deps["@nexusts/drizzle"] = "*";
1132
+ deps["drizzle-orm"] = "^0.45.0";
1133
+ if (db === "postgres")
1134
+ deps["pg"] = "^8.13.0";
1135
+ if (db === "mysql")
1136
+ deps["mysql2"] = "^3.11.0";
1137
+ if (db === "sqlite" || db === "node-sqlite" || db === "bun-sqlite")
1138
+ deps["better-sqlite3"] = "^12.0.0";
1139
+ devDeps["drizzle-kit"] = "^0.31.0";
1140
+ }
1141
+ if (view !== "none") {
1142
+ deps["@nexusts/static"] = "*";
1143
+ }
1144
+ deps["@nexusts/view"] = "*";
1145
+ if (view === "inertia") {
1146
+ if (frontend === "vue") {
1147
+ deps["@inertiajs/vue3"] = "^3.0.0";
1148
+ deps["vue"] = "^3.5.0";
1149
+ } else {
1150
+ deps["@inertiajs/react"] = "^3.0.0";
1151
+ deps["react"] = "^19.0.0";
1152
+ deps["react-dom"] = "^19.0.0";
1153
+ }
1154
+ }
1155
+ return { deps, devDeps };
1156
+ }
1157
+ function buildPackageJson(name, deps, devDeps, view, frontend) {
1158
+ const scripts = {
1159
+ dev: "bun --hot app/main.ts",
1160
+ build: "bun run build.ts",
1161
+ start: "bun app/main.ts",
1162
+ test: "vitest",
1163
+ nx: "nx"
1164
+ };
1165
+ if (view === "inertia") {
1166
+ const ext = frontend === "vue" ? "ts" : "tsx";
1167
+ scripts["build:frontend"] = `bun build ./resources/js/app.${ext} --outdir=./public --target=browser --format=esm --minify`;
1168
+ scripts["dev"] = `bun run build:frontend && bun --hot app/main.ts`;
1169
+ }
1170
+ const pkg = {
1171
+ name,
1172
+ version: "0.1.0",
1173
+ type: "module",
1174
+ private: true,
1175
+ scripts,
1176
+ dependencies: deps
1177
+ };
1178
+ if (Object.keys(devDeps).length > 0) {
1179
+ pkg.devDependencies = devDeps;
1180
+ }
1181
+ return pkg;
1182
+ }
1183
+ function generateNxConfig(target, opts) {
1184
+ const code = render(templates.project["nx.config.ts"], {
1185
+ routing: opts.routing,
1186
+ view: opts.view,
1187
+ viewPaths: opts.view === "none" ? "" : "resources/views",
1188
+ orm: opts.orm,
1189
+ dbDriver: opts.db,
1190
+ dbUrl: opts.dbUrl,
1191
+ inertiaFrontend: opts.frontend,
1192
+ inertiaSSR: opts.ssr,
1193
+ inertiaVersion: "1.0.0"
1194
+ });
1195
+ writeFileSync2(resolve4(target, "nx.config.ts"), code);
1196
+ }
1197
+ function generateDrizzleConfig(target, db, dbUrl) {
1198
+ if (db !== "bun-sqlite" && db !== "node-sqlite" && db !== "libsql" && db !== "postgres" && db !== "mysql")
1199
+ return;
1200
+ const dialect = db === "bun-sqlite" || db === "node-sqlite" || db === "libsql" ? "sqlite" : db === "postgres" ? "postgresql" : "mysql";
1201
+ const code = render(templates.project["drizzle.config.ts"], {
1202
+ dialect,
1203
+ dbUrl: dbUrl || "app.db"
1204
+ });
1205
+ writeFileSync2(resolve4(target, "drizzle.config.ts"), code);
1206
+ }
1207
+ function generateEnvFile() {
1208
+ return [
1209
+ "# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1210
+ "# NexusTS \u2014 Environment Variables (committed to git)",
1211
+ "#",
1212
+ "# Shared defaults for all environments. Override locally via",
1213
+ "# .env.local (gitignored) or by environment via .env.{NODE_ENV}",
1214
+ "# (e.g. .env.production, .env.development).",
1215
+ "# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1216
+ "",
1217
+ "# \u2500\u2500 App \u2500\u2500",
1218
+ "NODE_ENV=development",
1219
+ "PORT=3000",
1220
+ "",
1221
+ "# \u2500\u2500 Session secret (REQUIRED) \u2500\u2500",
1222
+ "# Generate with: openssl rand -base64 32",
1223
+ "SESSION_SECRET=change-me-in-production",
1224
+ "",
1225
+ "# \u2500\u2500 Database: SQLite (default, zero config) \u2500\u2500",
1226
+ "DATABASE_URL=app.db",
1227
+ "",
1228
+ "# \u2500\u2500 Database: PostgreSQL \u2500\u2500",
1229
+ "# DATABASE_URL=postgres://user:password@localhost:5432/myapp",
1230
+ "",
1231
+ "# \u2500\u2500 Database: MySQL \u2500\u2500",
1232
+ "# DATABASE_URL=mysql://user:password@localhost:3306/myapp",
1233
+ "",
1234
+ "# \u2500\u2500 Better Auth (if using nexusjs/auth) \u2500\u2500",
1235
+ "# BETTER_AUTH_SECRET=",
1236
+ "# BETTER_AUTH_URL=http://localhost:3000"
1237
+ ].join(`
1238
+ `) + `
1239
+ `;
1240
+ }
1241
+ function generateEnvLocalFile() {
1242
+ return [
1243
+ "# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1244
+ "# NexusTS \u2014 Local Overrides (DO NOT COMMIT to git)",
1245
+ "#",
1246
+ "# This file is gitignored. Use it for secrets and local",
1247
+ "# configuration that should never be checked in.",
1248
+ "# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1249
+ "",
1250
+ "# Override any value from .env here:",
1251
+ "# DATABASE_URL=postgres://user:password@localhost:5432/myapp",
1252
+ "# SESSION_SECRET=my-local-secret"
1253
+ ].join(`
1254
+ `) + `
1255
+ `;
1256
+ }
1257
+ function generateGitIgnore() {
1258
+ return `# NexusTS
1259
+ node_modules/
1260
+ app.db
1261
+ *.db
1262
+ .env.local
1263
+ dist/
1264
+ `;
1265
+ }
1266
+ function generateProjectFiles(target, opts) {
1267
+ const created = [];
1268
+ const write = (path, content) => {
1269
+ writeFileSync2(resolve4(target, path), content);
1270
+ created.push(path);
1271
+ };
1272
+ generateNxConfig(target, opts);
1273
+ write("public/.gitkeep", "");
1274
+ if (opts.view === "inertia") {
1275
+ if (opts.frontend === "vue") {
1276
+ write("resources/js/Pages/Welcome.vue", `<template>
1277
+ <main style="font-family: system-ui, sans-serif; max-width: 560px; margin: 2em auto">
1278
+ <h1>Hello, {{ name }}!</h1>
1279
+ </main>
1280
+ </template>
1281
+
1282
+ <script setup lang="ts">
1283
+ defineProps<{ name: string }>();
1284
+ </script>
1285
+ `);
1286
+ write("resources/js/app.ts", `import { createInertiaApp } from "@inertiajs/vue3";
1287
+ import { createApp, h } from "vue";
1288
+ import Welcome from "./Pages/Welcome.vue";
1289
+
1290
+ createInertiaApp({
1291
+ resolve: (name: string) => {
1292
+ if (name === "Welcome") return Welcome;
1293
+ throw new Error("Unknown page: " + name);
1294
+ },
1295
+ setup({ el, App, props }: any) {
1296
+ createApp({ render: () => h(App, props) }).mount(el);
1297
+ },
1298
+ });
1299
+ `);
1300
+ } else {
1301
+ write("resources/js/Pages/Welcome.tsx", `import { useState } from "react";
1302
+
1303
+ export default function Welcome({ name }: { name: string }) {
1304
+ const [count, setCount] = useState(0);
1305
+ return (
1306
+ <main style={{ fontFamily: "system-ui, sans-serif", maxWidth: 560, margin: "2em auto" }}>
1307
+ <h1>Hello, {name}!</h1>
1308
+ <p>Counter: <strong>{count}</strong></p>
1309
+ <button onClick={() => setCount((c) => c + 1)} style={{ padding: "0.5em 1em" }}>
1310
+ +1
1311
+ </button>
1312
+ </main>
1313
+ );
1314
+ }
1315
+ `);
1316
+ write("resources/js/app.tsx", `import { createInertiaApp } from "@inertiajs/react";
1317
+ import { createRoot } from "react-dom/client";
1318
+ import Welcome from "./Pages/Welcome.js";
1319
+
1320
+ createInertiaApp({
1321
+ resolve: (name: string) => {
1322
+ if (name === "Welcome") return Welcome;
1323
+ throw new Error("Unknown page: " + name);
1324
+ },
1325
+ setup({ el, App, props }: any) {
1326
+ createRoot(el).render(<App {...props} />);
1327
+ },
1328
+ });
1329
+ `);
1330
+ }
1331
+ } else if (opts.view !== "none") {
1332
+ write("resources/views/welcome.html", `<h1>Welcome to ${opts.name}</h1>
1333
+ <p>This is a sample Rendu template.</p>
1334
+ <p>Founded <?= year ?>.</p>
1335
+ `);
1336
+ }
1337
+ write(".env", generateEnvFile());
1338
+ write(".env.local", generateEnvLocalFile());
1339
+ write(".gitignore", generateGitIgnore());
1340
+ {
1341
+ const hasView = opts.view !== "none";
1342
+ const staticMw = hasView ? `import { StaticModule } from '@nexusts/static';
1343
+ const staticMiddleware = StaticModule.mount({ root: './public', prefix: '/static' });
1344
+ ` : "";
1345
+ const staticOpt = hasView ? `
1346
+ middleware: [staticMiddleware],` : "";
1347
+ write("app/main.ts", `import { Application } from '@nexusts/core';
1348
+ ${staticMw}import { AppModule } from './app.module.js';
1349
+
1350
+ const app = new Application(AppModule, {
1351
+ logging: true,
1352
+ port: Number(process.env['PORT'] ?? 3000),${staticOpt}
1353
+ });
1354
+
1355
+ await app.listen();
1356
+ console.log('[nexus] Listening on http://localhost:' + (process.env['PORT'] ?? 3000));
1357
+ `);
1358
+ }
1359
+ {
1360
+ const hasOrm = opts.orm === "drizzle";
1361
+ const ormImport = hasOrm ? `import { DrizzleModule } from '@nexusts/drizzle';
1362
+ ` : "";
1363
+ const forRootDialect = opts.db === "bun-sqlite" ? "bun-sqlite" : "sqlite";
1364
+ const ormBlock = hasOrm ? ` DrizzleModule.forRoot({
1365
+ dialect: '${forRootDialect}',
1366
+ connection: { filename: '${opts.dbUrl || "app.db"}' },
1367
+ logging: true,
1368
+ })` : "";
1369
+ const isInertia = opts.view === "inertia";
1370
+ const inertiaImport = isInertia ? `import { Inertia } from '@nexusts/view';
1371
+ ` : "";
1372
+ const inertiaProvider = isInertia ? ` providers: [{ provide: Inertia.TOKEN, useValue: new Inertia({ scripts: ['/static/app.js'] }) }],
1373
+ ` : "";
1374
+ write("app/app.module.ts", `${ormImport}${inertiaImport}import { Module } from '@nexusts/core';
1375
+ import { HomeController } from './controllers/home.controller.js';
1376
+
1377
+ @Module({
1378
+ imports: [${hasOrm ? `
1379
+ ${ormBlock},
1380
+ ` : ""} ],
1381
+ ${inertiaProvider} controllers: [HomeController],
1382
+ })
1383
+ export class AppModule {}
1384
+ `);
1385
+ }
1386
+ {
1387
+ if (opts.view === "inertia") {
1388
+ write("app/controllers/home.controller.ts", `import { Controller, Get, Inject } from '@nexusts/core';
1389
+ import { Inertia } from '@nexusts/view';
1390
+
1391
+ @Controller('/')
1392
+ export class HomeController {
1393
+ @Inject(Inertia.TOKEN) private inertia!: Inertia;
1394
+
1395
+ @Get('/')
1396
+ index() {
1397
+ return this.inertia.render('Welcome', { name: 'NexusTS' });
1398
+ }
1399
+ }
1400
+ `);
1401
+ } else if (opts.view !== "none") {
1402
+ write("app/controllers/home.controller.ts", `import { Controller, Get } from '@nexusts/core';
1403
+
1404
+ @Controller('/')
1405
+ export class HomeController {
1406
+ @Get('/')
1407
+ index() {
1408
+ return {
1409
+ view: 'welcome.html',
1410
+ data: { year: new Date().getFullYear() },
1411
+ };
1412
+ }
1413
+ }
1414
+ `);
1415
+ } else {
1416
+ write("app/controllers/home.controller.ts", `import { Controller, Get } from '@nexusts/core';
1417
+
1418
+ @Controller('/')
1419
+ export class HomeController {
1420
+ @Get('/')
1421
+ index() {
1422
+ return { status: 200, body: { message: 'Hello from NexusTS!' } };
1423
+ }
1424
+ }
1425
+ `);
1426
+ }
1427
+ }
1428
+ if (opts.orm === "drizzle") {
1429
+ generateDrizzleConfig(target, opts.db, opts.dbUrl);
1430
+ }
1431
+ write("README.md", `# ${opts.name}
1432
+
1433
+ A NexusTS project.
1434
+
1435
+ ## Run
1436
+
1437
+ \`\`\`bash
1438
+ bun install
1439
+ bun run dev
1440
+ \`\`\`
1441
+
1442
+ ## Scaffolding
1443
+
1444
+ \`\`\`bash
1445
+ bunx nx make:crud Post
1446
+ \`\`\`
1447
+ `);
1448
+ return created;
1449
+ }
1450
+ // packages/cli/src/commands/info.ts
1451
+ var infoCommand = {
1452
+ name: "info",
1453
+ aliases: ["i"],
1454
+ summary: "Show project configuration",
1455
+ description: "Prints the resolved nx.config.ts plus relevant env vars.",
1456
+ async run(ctx) {
1457
+ logger.heading("NexusTS CLI \u2014 Project Info");
1458
+ logger.info(colors.bold("Resolved configuration"));
1459
+ logger.blank();
1460
+ logger.table([
1461
+ ["routing", String(ctx.config.routing)],
1462
+ ["view", String(ctx.config.view)],
1463
+ ["orm", String(ctx.config.orm)],
1464
+ ["dialect", String(ctx.config.dialect ?? "(none)")],
1465
+ ["database.driver", String(ctx.config.database.driver)],
1466
+ ["database.url", String(ctx.config.database.url)],
1467
+ ["inertia.frontend", String(ctx.config.inertia.frontend)],
1468
+ ["inertia.ssr", String(ctx.config.inertia.ssr)],
1469
+ ["inertia.version", String(ctx.config.inertia.version)]
1470
+ ]);
1471
+ logger.blank();
1472
+ logger.info(colors.bold("Paths"));
1473
+ logger.blank();
1474
+ for (const [k, v] of Object.entries(ctx.config.paths)) {
1475
+ logger.table([[k, v]]);
1476
+ }
1477
+ logger.blank();
1478
+ logger.info(colors.bold("Environment"));
1479
+ logger.blank();
1480
+ const envKeys = [
1481
+ "NODE_ENV",
1482
+ "PORT",
1483
+ "NEXUS_DEBUG",
1484
+ "NO_COLOR",
1485
+ "FORCE_COLOR",
1486
+ "NX_ROUTING",
1487
+ "NX_VIEW",
1488
+ "NX_ORM",
1489
+ "NX_DATABASE_DRIVER",
1490
+ "NX_DATABASE_URL",
1491
+ "NX_INERTIA_FRONTEND",
1492
+ "NX_INERTIA_SSR"
1493
+ ];
1494
+ for (const k of envKeys) {
1495
+ const v = process.env[k];
1496
+ logger.table([[k, v === undefined ? colors.dim("(unset)") : v]]);
1497
+ }
1498
+ logger.blank();
1499
+ logger.info(colors.bold("Working directory"));
1500
+ logger.blank();
1501
+ logger.info(` ${resolve5(ctx.cwd)}`);
1502
+ logger.blank();
1503
+ return 0;
1504
+ }
1505
+ };
1506
+ var info_default = infoCommand;
1507
+
1167
1508
  // packages/cli/src/commands/init.ts
1509
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1510
+ import { resolve as resolve6 } from "path";
1511
+ var VALID_OPTIONS = {
1512
+ style: ["nest", "adonis", "functional"],
1513
+ view: ["rendu", "edge", "eta", "inertia", "none"],
1514
+ orm: ["drizzle", "prisma", "kysely", "none"],
1515
+ db: ["bun-sqlite", "node-sqlite", "libsql", "postgres", "mysql", "none"],
1516
+ frontend: ["react", "vue", "svelte", "solid"]
1517
+ };
1518
+ async function resolveOpt(flags, key, valid, defaultVal, interactive) {
1519
+ const flagVal = flags[key];
1520
+ if (flagVal) {
1521
+ if (valid.includes(flagVal))
1522
+ return flagVal;
1523
+ if (!interactive) {
1524
+ logger.error(`Invalid --${key} "${flagVal}". Valid values: ${valid.join(", ")}`);
1525
+ process.exit(1);
1526
+ }
1527
+ logger.warn(`"${flagVal}" is not valid for --${key}. Please choose from the list.`);
1528
+ }
1529
+ const label = key === "style" ? "Routing style" : key === "view" ? "View engine" : key === "orm" ? "ORM driver" : key === "db" ? "Database driver" : "Inertia frontend";
1530
+ for (;; ) {
1531
+ const answer = await select(label, [...valid], { default: defaultVal });
1532
+ if (valid.includes(answer))
1533
+ return answer;
1534
+ logger.warn(`"${answer}" is not valid. Please choose from: ${valid.join(", ")}`);
1535
+ }
1536
+ }
1168
1537
  var initCommand = {
1169
1538
  name: "init",
1170
1539
  aliases: ["i"],
1171
- summary: "Initialize nx.config.ts + app scaffold in the current directory",
1172
- description: "Non-destructive scaffold: adds nx.config.ts, app/*, and merges NexusTS into the existing package.json and tsconfig.json. Skips files that already exist (unless --force).",
1540
+ summary: "Initialize NexusTS in an existing directory",
1541
+ description: "Scaffolds a new NexusTS project in the current or target directory. Non-destructive: skips existing files, merges package.json and tsconfig.json.",
1173
1542
  examples: [
1174
1543
  "nx init",
1175
- "nx init ./my-existing-app",
1544
+ "nx init ./my-app",
1176
1545
  "nx init --style nest --view inertia --orm drizzle --db bun-sqlite",
1177
1546
  "nx init --force"
1178
1547
  ],
1179
1548
  flags: [
1180
1549
  { name: "target", description: "Target directory (default: cwd)" },
1181
- {
1182
- name: "style",
1183
- description: "Routing style (nest|adonis|functional|mixed)"
1184
- },
1550
+ { name: "style", description: "Routing style (nest|adonis|functional)" },
1185
1551
  { name: "view", description: "View engine (rendu|edge|eta|inertia|none)" },
1186
1552
  { name: "orm", description: "ORM driver (drizzle|prisma|kysely|none)" },
1187
- {
1188
- name: "db",
1189
- description: "Database driver (bun-sqlite|node-sqlite|libsql|postgres|mysql|none)"
1190
- },
1553
+ { name: "db", description: "Database driver (bun-sqlite|node-sqlite|libsql|postgres|mysql|none)" },
1191
1554
  {
1192
1555
  name: "frontend",
1193
1556
  description: "Inertia frontend (react|vue|svelte|solid)"
1194
1557
  },
1195
1558
  { name: "no-ssr", description: "Disable Inertia SSR" },
1196
- { name: "force", description: "Overwrite files that already exist" },
1197
- { name: "no-interaction", description: "Disable interactive prompts" }
1559
+ { name: "force", description: "Overwrite existing files" },
1560
+ { name: "no-interaction", description: "Skip interactive prompts" }
1198
1561
  ],
1199
1562
  async run(ctx) {
1200
- const interactive = !flagBool(ctx.flags, "no-interaction", false);
1563
+ const interactive = flagBool(ctx.flags, "interaction", true);
1201
1564
  const force = flagBool(ctx.flags, "force", false);
1202
- const target = resolve5(ctx.cwd, ctx.flags["target"] ?? ".");
1203
- if (!existsSync4(target)) {
1204
- logger.error(`Target directory does not exist: ${target}`);
1205
- logger.info(`Run \`nx new <name>\` to create a fresh project, or \`mkdir -p ${target}\` first.`);
1206
- return 1;
1207
- }
1208
- const routing = ctx.flags["style"] ?? await select("Routing style", ["nest", "adonis", "functional"], {
1209
- interactive,
1210
- default: "nest"
1211
- });
1212
- const view = ctx.flags["view"] ?? await select("View engine", ["rendu", "edge", "eta", "inertia", "none"], {
1213
- interactive,
1214
- default: "rendu"
1215
- });
1216
- const orm = ctx.flags["orm"] ?? await select("ORM driver", ["drizzle", "prisma", "kysely", "none"], {
1217
- interactive,
1218
- default: "drizzle"
1219
- });
1220
- const db = ctx.flags["db"] ?? await select("Database driver", ["bun-sqlite", "node-sqlite", "libsql", "postgres", "mysql", "none"], {
1221
- interactive,
1222
- default: "bun-sqlite"
1223
- });
1224
- const frontend = ctx.flags["frontend"] ?? await select("Inertia frontend", ["react", "vue", "svelte", "solid"], {
1225
- interactive,
1226
- default: "react"
1227
- });
1565
+ const target = resolve6(ctx.cwd, ctx.flags.target ?? ".");
1566
+ const routing = await resolveOpt(ctx.flags, "style", VALID_OPTIONS.style, "nest", interactive);
1567
+ const view = await resolveOpt(ctx.flags, "view", VALID_OPTIONS.view, "rendu", interactive);
1568
+ const orm = await resolveOpt(ctx.flags, "orm", VALID_OPTIONS.orm, "drizzle", interactive);
1569
+ const db = await resolveOpt(ctx.flags, "db", VALID_OPTIONS.db, "bun-sqlite", interactive);
1570
+ const frontend = await resolveOpt(ctx.flags, "frontend", VALID_OPTIONS.frontend, "react", interactive);
1228
1571
  const ssr = !flagBool(ctx.flags, "no-ssr", false);
1572
+ const name = target.split("/").pop() ?? "nexus-app";
1573
+ const dbUrl = db === "bun-sqlite" || db === "node-sqlite" ? "app.db" : "";
1229
1574
  const plan = [
1230
- { path: "nx.config.ts", mode: "write" },
1231
1575
  { path: "package.json", mode: "merge-pkg" },
1232
1576
  { path: "tsconfig.json", mode: "merge-tsconfig" },
1233
- { path: "public/.gitkeep", mode: "write" },
1234
- ...view !== "none" ? [{ path: "resources/views/welcome.html", mode: "write" }] : [],
1235
1577
  { path: ".env", mode: "skip" },
1236
1578
  { path: ".env.local", mode: "skip" },
1237
1579
  { path: ".gitignore", mode: "skip" },
1238
- ...orm === "drizzle" ? [{ path: "drizzle.config.ts", mode: "write" }] : [],
1580
+ { path: "nx.config.ts", mode: "write" },
1581
+ { path: "public/.gitkeep", mode: "write" },
1239
1582
  { path: "app/main.ts", mode: "write" },
1240
1583
  { path: "app/app.module.ts", mode: "write" },
1241
1584
  { path: "app/controllers/home.controller.ts", mode: "write" },
1242
- { path: "README.md", mode: "write" }
1585
+ { path: "README.md", mode: "write" },
1586
+ ...view === "inertia" ? [
1587
+ { path: `resources/js/Pages/Welcome.${frontend === "vue" ? "vue" : "tsx"}`, mode: "write" },
1588
+ { path: `resources/js/app.${frontend === "vue" ? "ts" : "tsx"}`, mode: "write" }
1589
+ ] : view !== "none" ? [{ path: "resources/views/welcome.html", mode: "write" }] : [],
1590
+ ...orm === "drizzle" ? [{ path: "drizzle.config.ts", mode: "write" }] : []
1243
1591
  ];
1244
1592
  const created = [];
1245
1593
  const skipped = [];
1246
1594
  const merged = [];
1247
- mkdirSync2(resolve5(target, "app/controllers"), { recursive: true });
1248
- mkdirSync2(resolve5(target, "public"), { recursive: true });
1249
- if (view !== "none") {
1250
- mkdirSync2(resolve5(target, "resources/views"), { recursive: true });
1251
- }
1595
+ ensureDirectories(target, view);
1252
1596
  for (const entry of plan) {
1253
- const abs = resolve5(target, entry.path);
1597
+ const abs = resolve6(target, entry.path);
1254
1598
  const exists = existsSync4(abs);
1255
1599
  if (entry.mode === "merge-pkg") {
1256
- const coreDeps = {
1257
- "@nexusts/core": "*",
1258
- "reflect-metadata": "^0.2.2",
1259
- hono: "^4.6.0",
1260
- zod: "^3.23.8"
1261
- };
1262
- const devDeps = {};
1263
- if (orm === "drizzle") {
1264
- coreDeps["@nexusts/drizzle"] = "*";
1265
- coreDeps["drizzle-orm"] = "^0.45.0";
1266
- if (db === "postgres")
1267
- coreDeps["pg"] = "^8.13.0";
1268
- if (db === "mysql")
1269
- coreDeps["mysql2"] = "^3.11.0";
1270
- if (db === "sqlite" || db === "node-sqlite" || db === "bun-sqlite")
1271
- coreDeps["better-sqlite3"] = "^12.0.0";
1272
- devDeps["drizzle-kit"] = "^0.31.0";
1273
- }
1274
- if (view !== "none") {
1275
- coreDeps["@nexusts/static"] = "*";
1276
- }
1600
+ const { deps, devDeps } = computeDeps(view, orm, db, frontend);
1277
1601
  if (exists) {
1278
- mergePackageJson(abs, coreDeps, devDeps);
1602
+ mergePackageJson(abs, deps, devDeps, view, frontend);
1279
1603
  merged.push(entry.path);
1280
1604
  } else {
1281
- const pkgJson = {
1282
- name: target.split("/").pop() ?? "nexus-app",
1283
- version: "0.1.0",
1284
- type: "module",
1285
- private: true,
1286
- scripts: {
1287
- dev: "bun --hot app/main.ts",
1288
- build: "bun run build.ts",
1289
- start: "bun app/main.ts",
1290
- test: "vitest",
1291
- nx: "nx"
1292
- },
1293
- dependencies: coreDeps
1294
- };
1295
- if (orm === "drizzle") {
1296
- pkgJson.devDependencies = {
1297
- "drizzle-kit": "^0.31.0"
1298
- };
1299
- }
1300
- writeFileSync2(abs, JSON.stringify(pkgJson, null, 2));
1605
+ const pkgJson = buildPackageJson(name, deps, devDeps, view, frontend);
1606
+ writeFileSync3(abs, `${JSON.stringify(pkgJson, null, 2)}
1607
+ `);
1301
1608
  created.push(entry.path);
1302
1609
  }
1303
1610
  continue;
@@ -1310,7 +1617,7 @@ var initCommand = {
1310
1617
  });
1311
1618
  merged.push(entry.path);
1312
1619
  } else {
1313
- writeFileSync2(abs, defaultTsconfig());
1620
+ writeFileSync3(abs, defaultTsconfig());
1314
1621
  created.push(entry.path);
1315
1622
  }
1316
1623
  continue;
@@ -1319,226 +1626,43 @@ var initCommand = {
1319
1626
  skipped.push(entry.path);
1320
1627
  continue;
1321
1628
  }
1322
- const content = renderContent(entry.path, {
1323
- routing,
1324
- view,
1325
- viewPaths: view === "none" ? "" : "resources/views",
1326
- orm,
1327
- dbDriver: db,
1328
- dbUrl: db === "bun-sqlite" || db === "node-sqlite" ? "app.db" : "",
1329
- inertiaFrontend: frontend,
1330
- inertiaSSR: ssr,
1331
- inertiaVersion: "1.0.0",
1332
- targetName: target.split("/").pop() ?? "nexus-app"
1333
- });
1334
- writeFileSync2(abs, content);
1335
1629
  created.push(entry.path);
1336
1630
  }
1631
+ const scaffoldOpts = { target, name, routing, view, orm, db, frontend, ssr, dbUrl };
1632
+ const scaffoldFiles = generateProjectFiles(target, scaffoldOpts);
1633
+ for (const f of scaffoldFiles) {
1634
+ if (!plan.some((p) => p.path === f)) {
1635
+ created.push(f);
1636
+ }
1637
+ }
1337
1638
  logger.success(`initialized NexusTS in ${target}`);
1338
1639
  logger.blank();
1339
- if (created.length) {
1640
+ if (created.length)
1340
1641
  logger.heading("Created");
1341
- for (const f of created)
1342
- logger.info(` + ${f}`);
1343
- }
1344
- if (merged.length) {
1642
+ for (const f of created)
1643
+ logger.info(` + ${f}`);
1644
+ if (merged.length)
1345
1645
  logger.heading("Merged into existing files");
1346
- for (const f of merged)
1347
- logger.info(` ~ ${f}`);
1348
- }
1349
- if (skipped.length) {
1646
+ for (const f of merged)
1647
+ logger.info(` ~ ${f}`);
1648
+ if (skipped.length)
1350
1649
  logger.heading("Skipped (already exist; use --force to overwrite)");
1351
- for (const f of skipped)
1352
- logger.info(` - ${f}`);
1353
- }
1354
- logger.blank();
1355
- logger.heading("Next steps");
1356
- logger.info(` cd ${target === ctx.cwd ? "." : target}`);
1357
- logger.info(` bun install`);
1358
- logger.info(` bun run dev`);
1650
+ for (const f of skipped)
1651
+ logger.info(` - ${f}`);
1359
1652
  logger.blank();
1360
1653
  return 0;
1361
1654
  }
1362
1655
  };
1363
- function renderContent(path, ctx) {
1364
- switch (path) {
1365
- case "nx.config.ts":
1366
- return render(templates.project["nx.config.ts"], ctx);
1367
- case "public/.gitkeep":
1368
- return "";
1369
- case "resources/views/welcome.html":
1370
- return `<h1>Welcome to ${ctx.targetName}</h1>
1371
- <p>This is a sample Rendu template.</p>
1372
- <p>Founded <?= year ?>.</p>
1373
- `;
1374
- case ".gitignore":
1375
- return `# NexusTS
1376
- node_modules/
1377
- app.db
1378
- *.db
1379
- .env.local
1380
- dist/
1381
- `;
1382
- case ".env":
1383
- return `# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1384
- # NexusTS \u2014 Environment Variables (committed to git)
1385
- #
1386
- # Shared defaults for all environments. Override locally via
1387
- # .env.local (gitignored) or by environment via .env.{NODE_ENV}
1388
- # (e.g. .env.production, .env.development).
1389
- #
1390
- # Uncomment the database config for your driver:
1391
- # \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1392
-
1393
- # \u2500\u2500 App \u2500\u2500
1394
- NODE_ENV=development
1395
- PORT=3000
1396
-
1397
- # \u2500\u2500 Session secret (REQUIRED) \u2500\u2500
1398
- # Generate with: openssl rand -base64 32
1399
- SESSION_SECRET=change-me-in-production
1400
-
1401
- # \u2500\u2500 Database: SQLite (default, zero config) \u2500\u2500
1402
- DATABASE_URL=app.db
1403
-
1404
- # \u2500\u2500 Database: PostgreSQL \u2500\u2500
1405
- # DATABASE_URL=postgres://user:password@localhost:5432/myapp
1406
-
1407
- # \u2500\u2500 Database: MySQL \u2500\u2500
1408
- # DATABASE_URL=mysql://user:password@localhost:3306/myapp
1409
-
1410
- # \u2500\u2500 Better Auth (if using nexusjs/auth) \u2500\u2500
1411
- # BETTER_AUTH_SECRET=
1412
- # BETTER_AUTH_URL=http://localhost:3000
1413
- `;
1414
- case ".env.local":
1415
- return `# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1416
- # NexusTS \u2014 Local Overrides (DO NOT COMMIT to git)
1417
- #
1418
- # This file is gitignored. Use it for secrets and local
1419
- # configuration that should never be checked in.
1420
- # \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1421
-
1422
- # Override any value from .env here:
1423
- # DATABASE_URL=postgres://user:password@localhost:5432/myapp
1424
- # SESSION_SECRET=my-local-secret
1425
- `;
1426
- case "drizzle.config.ts": {
1427
- const dialect = ctx.dbDriver === "bun-sqlite" || ctx.dbDriver === "node-sqlite" || ctx.dbDriver === "libsql" ? "sqlite" : ctx.dbDriver === "postgres" ? "postgresql" : "sqlite";
1428
- return render(templates.project["drizzle.config.ts"], {
1429
- dialect,
1430
- dbUrl: ctx.dbUrl || "app.db"
1431
- });
1432
- }
1433
- case "app/main.ts": {
1434
- const hasView = ctx.view !== "none";
1435
- const staticMw = hasView ? `import { StaticModule } from '@nexusts/static';
1436
- ` + `const staticMiddleware = StaticModule.mount({ root: './public', prefix: '/static' });
1437
- ` : "";
1438
- const staticOpt = hasView ? `
1439
- middleware: [staticMiddleware],` : "";
1440
- return `import 'reflect-metadata';
1441
- import { Application } from '@nexusts/core';
1442
- ${staticMw}import { AppModule } from './app.module.js';
1443
-
1444
- const app = new Application(AppModule, {
1445
- logging: true,
1446
- port: Number(process.env['PORT'] ?? 3000),${staticOpt}
1447
- });
1448
-
1449
- await app.listen();
1450
- console.log('[nexus] Listening on http://localhost:' + (process.env['PORT'] ?? 3000));
1451
- `;
1452
- }
1453
- case "app/app.module.ts": {
1454
- const hasOrm = ctx.orm === "drizzle";
1455
- const ormImport = hasOrm ? `import { DrizzleModule } from '@nexusts/drizzle';
1456
- ` : "";
1457
- const forRootDialect = ctx.dbDriver === "bun-sqlite" ? "bun-sqlite" : "sqlite";
1458
- const forRootFile = ctx.dbUrl || "app.db";
1459
- const ormBlock = hasOrm ? ` DrizzleModule.forRoot({
1460
- dialect: '` + forRootDialect + `',
1461
- connection: { filename: '` + forRootFile + `' },
1462
- logging: true,
1463
- })` : "";
1464
- return `${ormImport}import { Module } from '@nexusts/core';
1465
- import { HomeController } from './controllers/home.controller.js';
1466
-
1467
- @Module({
1468
- imports: [${hasOrm ? `
1469
- ${ormBlock},
1470
- ` : ""} ],
1471
- controllers: [HomeController],
1472
- })
1473
- export class AppModule {}
1474
- `;
1475
- }
1476
- case "app/controllers/home.controller.ts": {
1477
- const hasView = ctx.view !== "none";
1478
- const body = hasView ? `{
1479
- view: 'welcome.html',
1480
- data: { year: new Date().getFullYear() },
1481
- }` : `{ status: 200, body: { message: 'Hello from NexusTS!' } }`;
1482
- return `import { Controller, Get } from '@nexusts/core';
1483
-
1484
- @Controller('/')
1485
- export class HomeController {
1486
- @Get('/')
1487
- index() {
1488
- return ${body};
1489
- }
1490
- }
1491
- `;
1492
- }
1493
- case "README.md":
1494
- return `# ${ctx.targetName}
1495
-
1496
- A NexusTS project.
1497
-
1498
- ## Run
1499
-
1500
- \`\`\`bash
1501
- bun install
1502
- bun run dev
1503
- \`\`\`
1504
-
1505
- ## Scaffolding
1506
-
1507
- \`\`\`bash
1508
- bunx nx make:crud Post
1509
- \`\`\`
1510
- `;
1511
- default:
1512
- throw new Error(`No render template for: ${path}`);
1513
- }
1514
- }
1515
- function defaultTsconfig() {
1516
- return `{
1517
- "compilerOptions": {
1518
- "target": "ES2022",
1519
- "module": "ESNext",
1520
- "moduleResolution": "bundler",
1521
- "experimentalDecorators": true,
1522
- "emitDecoratorMetadata": true,
1523
- "strict": true,
1524
- "esModuleInterop": true,
1525
- "skipLibCheck": true,
1526
- "types": ["bun-types"]
1527
- },
1528
- "include": ["app/**/*.ts", "nx.config.ts"]
1529
- }
1530
- `;
1531
- }
1532
- function mergePackageJson(path, additions, devAdditions = {}) {
1656
+ function mergePackageJson(path, additions, devAdditions = {}, view, frontend) {
1533
1657
  const raw = readFileSync4(path, "utf8");
1534
1658
  const pkg = parseJsonLoose(raw);
1535
1659
  let changed = false;
1536
- if (!pkg["type"]) {
1537
- pkg["type"] = "module";
1660
+ if (!pkg.type) {
1661
+ pkg.type = "module";
1538
1662
  changed = true;
1539
1663
  }
1540
- if (!pkg["private"]) {
1541
- pkg["private"] = true;
1664
+ if (!pkg.private) {
1665
+ pkg.private = true;
1542
1666
  changed = true;
1543
1667
  }
1544
1668
  const SCRIPTS = {
@@ -1548,38 +1672,41 @@ function mergePackageJson(path, additions, devAdditions = {}) {
1548
1672
  test: "vitest",
1549
1673
  nx: "nx"
1550
1674
  };
1551
- const existingScripts = pkg["scripts"] ?? {};
1675
+ if (view === "inertia") {
1676
+ const ext = frontend === "vue" ? "ts" : "tsx";
1677
+ SCRIPTS["build:frontend"] = `bun build ./resources/js/app.${ext} --outdir=./public --target=browser --format=esm --minify`;
1678
+ SCRIPTS.dev = `bun run build:frontend && bun --hot app/main.ts`;
1679
+ }
1680
+ const existingScripts = pkg.scripts ?? {};
1552
1681
  for (const [k, v] of Object.entries(SCRIPTS)) {
1553
1682
  if (!(k in existingScripts)) {
1554
1683
  existingScripts[k] = v;
1555
1684
  changed = true;
1556
1685
  }
1557
1686
  }
1558
- if (Object.keys(existingScripts).length > 0) {
1559
- pkg["scripts"] = existingScripts;
1560
- }
1561
- const deps = pkg["dependencies"] ?? {};
1687
+ if (Object.keys(existingScripts).length > 0)
1688
+ pkg.scripts = existingScripts;
1689
+ const deps = pkg.dependencies ?? {};
1562
1690
  for (const [k, v] of Object.entries(additions)) {
1563
1691
  if (!(k in deps)) {
1564
1692
  deps[k] = v;
1565
1693
  changed = true;
1566
1694
  }
1567
1695
  }
1568
- pkg["dependencies"] = deps;
1696
+ pkg.dependencies = deps;
1569
1697
  if (Object.keys(devAdditions).length > 0) {
1570
- const devDeps = pkg["devDependencies"] ?? {};
1698
+ const devDeps = pkg.devDependencies ?? {};
1571
1699
  for (const [k, v] of Object.entries(devAdditions)) {
1572
1700
  if (!(k in devDeps)) {
1573
1701
  devDeps[k] = v;
1574
1702
  changed = true;
1575
1703
  }
1576
1704
  }
1577
- pkg["devDependencies"] = devDeps;
1705
+ pkg.devDependencies = devDeps;
1578
1706
  }
1579
- if (changed) {
1580
- writeFileSync2(path, JSON.stringify(pkg, null, 2) + `
1707
+ if (changed)
1708
+ writeFileSync3(path, `${JSON.stringify(pkg, null, 2)}
1581
1709
  `);
1582
- }
1583
1710
  }
1584
1711
  function mergeTsconfig(path, additions) {
1585
1712
  const raw = readFileSync4(path, "utf8");
@@ -1592,32 +1719,45 @@ function mergeTsconfig(path, additions) {
1592
1719
  changed = true;
1593
1720
  }
1594
1721
  }
1595
- const inc = cfg.include ?? [];
1596
- if (!inc.includes("app/**/*.ts")) {
1597
- inc.push("app/**/*.ts");
1598
- changed = true;
1599
- }
1600
- if (!inc.includes("nx.config.ts")) {
1601
- inc.push("nx.config.ts");
1602
- changed = true;
1722
+ cfg.compilerOptions = co;
1723
+ const include = cfg.include ?? [];
1724
+ for (const g of ["app/**/*.ts", "nx.config.ts"]) {
1725
+ if (!include.includes(g)) {
1726
+ include.push(g);
1727
+ changed = true;
1728
+ }
1603
1729
  }
1604
- if (changed) {
1605
- cfg.compilerOptions = co;
1606
- cfg.include = inc;
1607
- writeFileSync2(path, JSON.stringify(cfg, null, 2) + `
1730
+ cfg.include = include;
1731
+ if (changed)
1732
+ writeFileSync3(path, `${JSON.stringify(cfg, null, 2)}
1608
1733
  `);
1609
- }
1734
+ }
1735
+ function defaultTsconfig() {
1736
+ return `{
1737
+ "compilerOptions": {
1738
+ "target": "ES2022",
1739
+ "module": "ESNext",
1740
+ "moduleResolution": "bundler",
1741
+ "experimentalDecorators": true,
1742
+ "emitDecoratorMetadata": true,
1743
+ "strict": true,
1744
+ "esModuleInterop": true,
1745
+ "skipLibCheck": true,
1746
+ "types": ["@types/bun"]
1747
+ },
1748
+ "include": ["app/**/*.ts", "nx.config.ts"]
1749
+ }
1750
+ `;
1610
1751
  }
1611
1752
  var init_default = initCommand;
1612
1753
 
1613
1754
  // packages/cli/src/commands/make-auth.ts
1614
- import { resolve as resolve6 } from "path";
1755
+ import { resolve as resolve7 } from "path";
1615
1756
  var AUTH_INSTANCE_TEMPLATE = `/**
1616
1757
  * Better-auth instance \u2014 generated by \`nx make:auth\`.
1617
1758
  *
1618
1759
  * Edit \`nx.config.ts\` (\`auth\` section) instead of this file when possible.
1619
1760
  */
1620
- import 'reflect-metadata';
1621
1761
  import { createAuth } from '@nexusts/auth';
1622
1762
 
1623
1763
  export const auth = createAuth({
@@ -1726,7 +1866,7 @@ var makeAuthCommand = {
1726
1866
  passkeyRpId: rpId,
1727
1867
  passkeyOrigin: Array.isArray(origin) ? origin.join(",") : origin
1728
1868
  });
1729
- const authOut = resolve6(ctx.cwd, "app/auth/auth.ts");
1869
+ const authOut = resolve7(ctx.cwd, "app/auth/auth.ts");
1730
1870
  if (writeFile(authOut, authCode)) {
1731
1871
  logger.success(`created ${authOut}`);
1732
1872
  } else {
@@ -1736,7 +1876,7 @@ var makeAuthCommand = {
1736
1876
  providers: providers.length > 0,
1737
1877
  entries
1738
1878
  });
1739
- const envOut = resolve6(ctx.cwd, ".env.example");
1879
+ const envOut = resolve7(ctx.cwd, ".env.example");
1740
1880
  if (writeFile(envOut, envCode, { skipIfExists: true })) {
1741
1881
  logger.success(`created ${envOut}`);
1742
1882
  } else {
@@ -1762,7 +1902,7 @@ var makeAuthCommand = {
1762
1902
  var make_auth_default = makeAuthCommand;
1763
1903
 
1764
1904
  // packages/cli/src/commands/make-controller.ts
1765
- import { resolve as resolve7 } from "path";
1905
+ import { resolve as resolve8 } from "path";
1766
1906
  var makeControllerCommand = {
1767
1907
  name: "make:controller",
1768
1908
  aliases: ["mc", "make-controller"],
@@ -1808,7 +1948,7 @@ var makeControllerCommand = {
1808
1948
  service: serviceName,
1809
1949
  serviceCamel
1810
1950
  }).replace(/import .*\n/g, skipService ? (m) => m.includes("services/") ? "" : m : (m) => m);
1811
- const out = resolve7(ctx.cwd, ctx.config.paths.controllers, `${variants.kebab}.controller.ts`);
1951
+ const out = resolve8(ctx.cwd, ctx.config.paths.controllers, `${variants.kebab}.controller.ts`);
1812
1952
  const ok = writeFile(out, code, { skipIfExists: false });
1813
1953
  if (!ok) {
1814
1954
  logger.error(`Refusing to overwrite existing file: ${out}`);
@@ -1823,7 +1963,7 @@ var make_controller_default = makeControllerCommand;
1823
1963
 
1824
1964
  // packages/cli/src/commands/make-crud.ts
1825
1965
  import { mkdirSync as mkdirSync3 } from "fs";
1826
- import { dirname as dirname3, resolve as resolve8 } from "path";
1966
+ import { dirname as dirname3, resolve as resolve9 } from "path";
1827
1967
 
1828
1968
  // packages/cli/src/templates/model/drizzle-dialect.ts
1829
1969
  function renderDrizzleDialect(dialect) {
@@ -1871,10 +2011,10 @@ var DIALECT_SPECS = {
1871
2011
  tableFn: "sqliteTable",
1872
2012
  idHelper: "integer",
1873
2013
  idOpts: "{ autoIncrement: true }",
1874
- tsTimestamp: "text",
1875
- tsDateMode: "",
1876
- defaultTs: ".$defaultFn(() => new Date().toISOString())",
1877
- defaultTsUpdate: ""
2014
+ tsTimestamp: "integer",
2015
+ tsDateMode: ", { mode: 'timestamp' }",
2016
+ defaultTs: ".$defaultFn(() => Date.now())",
2017
+ defaultTsUpdate: ".$defaultFn(() => Date.now())"
1878
2018
  },
1879
2019
  sqlite: {
1880
2020
  imports: ["sqliteTable", "integer", "text", "real"],
@@ -1998,7 +2138,7 @@ var makeCrudCommand = {
1998
2138
  viewShowComponent,
1999
2139
  hasInertia
2000
2140
  });
2001
- const out = resolve8(ctx.cwd, ctx.config.paths.controllers, `${variants.kebab}.controller.ts`);
2141
+ const out = resolve9(ctx.cwd, ctx.config.paths.controllers, `${variants.kebab}.controller.ts`);
2002
2142
  if (!writeFile(out, code, { skipIfExists: true })) {
2003
2143
  logger.warn(`skipped (exists): ${out}`);
2004
2144
  } else {
@@ -2016,7 +2156,7 @@ var makeCrudCommand = {
2016
2156
  repository,
2017
2157
  repositoryCamel: variants.camel + "Repository"
2018
2158
  });
2019
- const out = resolve8(ctx.cwd, ctx.config.paths.services, `${variants.kebab}.service.ts`);
2159
+ const out = resolve9(ctx.cwd, ctx.config.paths.services, `${variants.kebab}.service.ts`);
2020
2160
  if (!writeFile(out, code, { skipIfExists: true })) {
2021
2161
  logger.warn(`skipped (exists): ${out}`);
2022
2162
  } else {
@@ -2050,7 +2190,7 @@ var makeCrudCommand = {
2050
2190
  prismaBlock: ""
2051
2191
  });
2052
2192
  }
2053
- const out = resolve8(ctx.cwd, ctx.config.paths.models, `${variants.kebab}.model.ts`);
2193
+ const out = resolve9(ctx.cwd, ctx.config.paths.models, `${variants.kebab}.model.ts`);
2054
2194
  if (!writeFile(out, code, { skipIfExists: true })) {
2055
2195
  logger.warn(`skipped (exists): ${out}`);
2056
2196
  } else {
@@ -2066,7 +2206,7 @@ var makeCrudCommand = {
2066
2206
  tableName,
2067
2207
  repository
2068
2208
  });
2069
- const repoOut = resolve8(ctx.cwd, `${ctx.config.paths.app}/repositories`, `${variants.kebab}.repository.ts`);
2209
+ const repoOut = resolve9(ctx.cwd, `${ctx.config.paths.app}/repositories`, `${variants.kebab}.repository.ts`);
2070
2210
  mkdirSync3(dirname3(repoOut), { recursive: true });
2071
2211
  if (!writeFile(repoOut, repoCode, { skipIfExists: true })) {
2072
2212
  logger.warn(`skipped (exists): ${repoOut}`);
@@ -2081,7 +2221,7 @@ var makeCrudCommand = {
2081
2221
  camel: variants.camel,
2082
2222
  kebab: variants.kebab
2083
2223
  });
2084
- const out = resolve8(ctx.cwd, ctx.config.paths.dto, `${variants.kebab}.dto.ts`);
2224
+ const out = resolve9(ctx.cwd, ctx.config.paths.dto, `${variants.kebab}.dto.ts`);
2085
2225
  if (!writeFile(out, code, { skipIfExists: true })) {
2086
2226
  logger.warn(`skipped (exists): ${out}`);
2087
2227
  } else {
@@ -2099,7 +2239,7 @@ var makeCrudCommand = {
2099
2239
  repository,
2100
2240
  hasRepo: !noRepo
2101
2241
  });
2102
- const out = resolve8(ctx.cwd, ctx.config.paths.modules, `${variants.kebab}.module.ts`);
2242
+ const out = resolve9(ctx.cwd, ctx.config.paths.modules, `${variants.kebab}.module.ts`);
2103
2243
  if (!writeFile(out, code, { skipIfExists: true })) {
2104
2244
  logger.warn(`skipped (exists): ${out}`);
2105
2245
  } else {
@@ -2115,7 +2255,7 @@ var makeCrudCommand = {
2115
2255
  controller,
2116
2256
  service
2117
2257
  });
2118
- const out = resolve8(ctx.cwd, "tests", `${variants.kebab}.test.ts`);
2258
+ const out = resolve9(ctx.cwd, "tests", `${variants.kebab}.test.ts`);
2119
2259
  if (!writeFile(out, code, { skipIfExists: true })) {
2120
2260
  logger.warn(`skipped (exists): ${out}`);
2121
2261
  } else {
@@ -2151,7 +2291,7 @@ function renderDrizzleColumns(dialect) {
2151
2291
  var make_crud_default = makeCrudCommand;
2152
2292
 
2153
2293
  // packages/cli/src/commands/make-listener.ts
2154
- import { resolve as resolve9 } from "path";
2294
+ import { resolve as resolve10 } from "path";
2155
2295
  var LISTENER_TEMPLATE = `
2156
2296
  import { Inject, Injectable } from '@nexusts/core';
2157
2297
  import { EventService, OnEvent } from '@nexusts/events';
@@ -2202,7 +2342,7 @@ var makeListenerCommand = {
2202
2342
  name: variants.pascal,
2203
2343
  kebab: variants.kebab
2204
2344
  });
2205
- const out = resolve9(ctx.cwd, "app/events/listeners", `${variants.kebab}.listener.ts`);
2345
+ const out = resolve10(ctx.cwd, "app/events/listeners", `${variants.kebab}.listener.ts`);
2206
2346
  if (writeFile(out, code, { skipIfExists: true })) {
2207
2347
  logger.success(`created ${out}`);
2208
2348
  } else {
@@ -2222,7 +2362,7 @@ var makeListenerCommand = {
2222
2362
  var make_listener_default = makeListenerCommand;
2223
2363
 
2224
2364
  // packages/cli/src/commands/make-middleware.ts
2225
- import { resolve as resolve10 } from "path";
2365
+ import { resolve as resolve11 } from "path";
2226
2366
  var makeMiddlewareCommand = {
2227
2367
  name: "make:middleware",
2228
2368
  aliases: ["mwm", "make-middleware"],
@@ -2239,7 +2379,7 @@ var makeMiddlewareCommand = {
2239
2379
  const code = render(templates.middleware, {
2240
2380
  name: variants.pascal
2241
2381
  });
2242
- const out = resolve10(ctx.cwd, ctx.config.paths.middleware, `${variants.kebab}.middleware.ts`);
2382
+ const out = resolve11(ctx.cwd, ctx.config.paths.middleware, `${variants.kebab}.middleware.ts`);
2243
2383
  writeFile(out, code);
2244
2384
  logger.success(`created ${out}`);
2245
2385
  logger.finger(`register with: app.server.app.use('*', new ${variants.pascal}Middleware().handle)`);
@@ -2249,7 +2389,7 @@ var makeMiddlewareCommand = {
2249
2389
  var make_middleware_default = makeMiddlewareCommand;
2250
2390
 
2251
2391
  // packages/cli/src/commands/make-migration.ts
2252
- import { resolve as resolve11 } from "path";
2392
+ import { resolve as resolve12 } from "path";
2253
2393
  var makeMigrationCommand = {
2254
2394
  name: "make:migration",
2255
2395
  aliases: ["mkm", "make-migration"],
@@ -2321,7 +2461,7 @@ var makeMigrationCommand = {
2321
2461
  return 1;
2322
2462
  }
2323
2463
  const filename = `${formatTimestamp(new Date)}_${variants.snake}.${extension}`;
2324
- const out = resolve11(ctx.cwd, ctx.config.paths.migrations, filename);
2464
+ const out = resolve12(ctx.cwd, ctx.config.paths.migrations, filename);
2325
2465
  writeFile(out, code);
2326
2466
  logger.success(`created ${out}`);
2327
2467
  if (isDrizzle) {
@@ -2409,7 +2549,7 @@ function formatTimestamp(d) {
2409
2549
  var make_migration_default = makeMigrationCommand;
2410
2550
 
2411
2551
  // packages/cli/src/commands/make-model.ts
2412
- import { resolve as resolve12 } from "path";
2552
+ import { resolve as resolve13 } from "path";
2413
2553
  var makeModelCommand = {
2414
2554
  name: "make:model",
2415
2555
  aliases: ["mmodel", "make-model"],
@@ -2481,7 +2621,7 @@ var makeModelCommand = {
2481
2621
  prismaBlock
2482
2622
  });
2483
2623
  }
2484
- const out = resolve12(ctx.cwd, ctx.config.paths.models, `${variants.kebab}.model.ts`);
2624
+ const out = resolve13(ctx.cwd, ctx.config.paths.models, `${variants.kebab}.model.ts`);
2485
2625
  writeFile(out, code);
2486
2626
  logger.success(`created ${out}`);
2487
2627
  logger.finger(`run \`nx make:migration create_${tableName}_table\` to scaffold a migration.`);
@@ -2540,7 +2680,7 @@ function capitalize(s) {
2540
2680
  var make_model_default = makeModelCommand;
2541
2681
 
2542
2682
  // packages/cli/src/commands/make-module.ts
2543
- import { resolve as resolve13 } from "path";
2683
+ import { resolve as resolve14 } from "path";
2544
2684
  var makeModuleCommand = {
2545
2685
  name: "make:module",
2546
2686
  aliases: ["mm", "make-module"],
@@ -2574,7 +2714,7 @@ var makeModuleCommand = {
2574
2714
  hasService,
2575
2715
  hasRepo
2576
2716
  });
2577
- const out = resolve13(ctx.cwd, ctx.config.paths.modules, `${variants.kebab}.module.ts`);
2717
+ const out = resolve14(ctx.cwd, ctx.config.paths.modules, `${variants.kebab}.module.ts`);
2578
2718
  writeFile(out, code);
2579
2719
  logger.success(`created ${out}`);
2580
2720
  logger.finger(`add ${variants.pascal}Module to AppModule.imports.`);
@@ -2584,7 +2724,7 @@ var makeModuleCommand = {
2584
2724
  var make_module_default = makeModuleCommand;
2585
2725
 
2586
2726
  // packages/cli/src/commands/make-queue.ts
2587
- import { resolve as resolve14 } from "path";
2727
+ import { resolve as resolve15 } from "path";
2588
2728
  var WORKER_TEMPLATE = `
2589
2729
  import { Inject, Injectable } from '@nexusts/core';
2590
2730
  import { QueueService, OnQueueReady } from '@nexusts/queue';
@@ -2712,7 +2852,7 @@ var makeQueueCommand = {
2712
2852
  name: variants.pascal,
2713
2853
  kebab: variants.kebab
2714
2854
  });
2715
- const out = resolve14(ctx.cwd, "app/queue/workers", `${variants.kebab}.worker.ts`);
2855
+ const out = resolve15(ctx.cwd, "app/queue/workers", `${variants.kebab}.worker.ts`);
2716
2856
  if (writeFile(out, code, { skipIfExists: true })) {
2717
2857
  logger.success(`created ${out}`);
2718
2858
  } else {
@@ -2724,7 +2864,7 @@ var makeQueueCommand = {
2724
2864
  name: variants.pascal,
2725
2865
  kebab: variants.kebab
2726
2866
  });
2727
- const out = resolve14(ctx.cwd, "app/queue/jobs", `${variants.kebab}.job.ts`);
2867
+ const out = resolve15(ctx.cwd, "app/queue/jobs", `${variants.kebab}.job.ts`);
2728
2868
  if (writeFile(out, code, { skipIfExists: true })) {
2729
2869
  logger.success(`created ${out}`);
2730
2870
  } else {
@@ -2748,7 +2888,7 @@ var make_queue_default = makeQueueCommand;
2748
2888
 
2749
2889
  // packages/cli/src/commands/make-repository.ts
2750
2890
  import { mkdirSync as mkdirSync4 } from "fs";
2751
- import { resolve as resolve15, dirname as dirname4 } from "path";
2891
+ import { resolve as resolve16, dirname as dirname4 } from "path";
2752
2892
  var makeRepositoryCommand = {
2753
2893
  name: "make:repository",
2754
2894
  aliases: ["mr", "make-repository", "make:repo"],
@@ -2773,7 +2913,7 @@ var makeRepositoryCommand = {
2773
2913
  snake: variants.snake,
2774
2914
  repository
2775
2915
  });
2776
- const out = resolve15(ctx.cwd, `${ctx.config.paths.app}/repositories`, `${variants.kebab}.repository.ts`);
2916
+ const out = resolve16(ctx.cwd, `${ctx.config.paths.app}/repositories`, `${variants.kebab}.repository.ts`);
2777
2917
  mkdirSync4(dirname4(out), { recursive: true });
2778
2918
  writeFile(out, code);
2779
2919
  logger.success(`created ${out}`);
@@ -2783,7 +2923,7 @@ var makeRepositoryCommand = {
2783
2923
  var make_repository_default = makeRepositoryCommand;
2784
2924
 
2785
2925
  // packages/cli/src/commands/make-schedule.ts
2786
- import { resolve as resolve16 } from "path";
2926
+ import { resolve as resolve17 } from "path";
2787
2927
  var TASK_TEMPLATE = `
2788
2928
  import { Injectable } from '@nexusts/core';
2789
2929
  import { Cron, Interval, Timeout } from '@nexusts/schedule';
@@ -2827,7 +2967,7 @@ var makeScheduleCommand = {
2827
2967
  name: variants.pascal,
2828
2968
  kebab: variants.kebab
2829
2969
  });
2830
- const out = resolve16(ctx.cwd, "app/schedule/tasks", `${variants.kebab}.task.ts`);
2970
+ const out = resolve17(ctx.cwd, "app/schedule/tasks", `${variants.kebab}.task.ts`);
2831
2971
  if (writeFile(out, code, { skipIfExists: true })) {
2832
2972
  logger.success(`created ${out}`);
2833
2973
  } else {
@@ -2845,7 +2985,7 @@ var makeScheduleCommand = {
2845
2985
  var make_schedule_default = makeScheduleCommand;
2846
2986
 
2847
2987
  // packages/cli/src/commands/make-service.ts
2848
- import { resolve as resolve17 } from "path";
2988
+ import { resolve as resolve18 } from "path";
2849
2989
  var makeServiceCommand = {
2850
2990
  name: "make:service",
2851
2991
  aliases: ["ms", "make-service"],
@@ -2877,7 +3017,7 @@ var makeServiceCommand = {
2877
3017
  repository,
2878
3018
  repositoryCamel
2879
3019
  });
2880
- const out = resolve17(ctx.cwd, ctx.config.paths.services, `${variants.kebab}.service.ts`);
3020
+ const out = resolve18(ctx.cwd, ctx.config.paths.services, `${variants.kebab}.service.ts`);
2881
3021
  writeFile(out, code);
2882
3022
  logger.success(`created ${out}`);
2883
3023
  return 0;
@@ -2886,7 +3026,7 @@ var makeServiceCommand = {
2886
3026
  var make_service_default = makeServiceCommand;
2887
3027
 
2888
3028
  // packages/cli/src/commands/make-session.ts
2889
- import { resolve as resolve18 } from "path";
3029
+ import { resolve as resolve19 } from "path";
2890
3030
  var SESSION_TEMPLATE = `
2891
3031
  import { Inject, Injectable } from '@nexusts/core';
2892
3032
  import { SessionService } from '@nexusts/session';
@@ -2948,7 +3088,7 @@ var makeSessionCommand = {
2948
3088
  name: variants.pascal,
2949
3089
  kebab: variants.kebab
2950
3090
  });
2951
- const out = resolve18(ctx.cwd, "app/session/services", `${variants.kebab}.session.ts`);
3091
+ const out = resolve19(ctx.cwd, "app/session/services", `${variants.kebab}.session.ts`);
2952
3092
  if (writeFile(out, code, { skipIfExists: true })) {
2953
3093
  logger.success(`created ${out}`);
2954
3094
  } else {
@@ -2966,7 +3106,7 @@ var makeSessionCommand = {
2966
3106
  var make_session_default = makeSessionCommand;
2967
3107
 
2968
3108
  // packages/cli/src/commands/make-validator.ts
2969
- import { resolve as resolve19 } from "path";
3109
+ import { resolve as resolve20 } from "path";
2970
3110
  var makeValidatorCommand = {
2971
3111
  name: "make:validator",
2972
3112
  aliases: ["mv", "make-validator"],
@@ -2983,7 +3123,7 @@ var makeValidatorCommand = {
2983
3123
  const code = render(templates.validator, {
2984
3124
  name: variants.pascal
2985
3125
  });
2986
- const out = resolve19(ctx.cwd, ctx.config.paths.dto, `${variants.kebab}.dto.ts`);
3126
+ const out = resolve20(ctx.cwd, ctx.config.paths.dto, `${variants.kebab}.dto.ts`);
2987
3127
  writeFile(out, code);
2988
3128
  logger.success(`created ${out}`);
2989
3129
  return 0;
@@ -2994,7 +3134,7 @@ var make_validator_default = makeValidatorCommand;
2994
3134
  // packages/cli/src/commands/db-migrate.ts
2995
3135
  import { spawn } from "child_process";
2996
3136
  import { existsSync as existsSync5 } from "fs";
2997
- import { resolve as resolve20 } from "path";
3137
+ import { resolve as resolve21 } from "path";
2998
3138
  var dbMigrateCommand = {
2999
3139
  name: "db:migrate",
3000
3140
  aliases: ["db:m", "migrate"],
@@ -3029,9 +3169,9 @@ var dbMigrateCommand = {
3029
3169
  }
3030
3170
  ],
3031
3171
  async run(ctx) {
3032
- const folder = ctx.flags["folder"] ?? resolve20(ctx.cwd, ctx.config.paths.migrations);
3172
+ const folder = ctx.flags["folder"] ?? resolve21(ctx.cwd, ctx.config.paths.migrations);
3033
3173
  const dialect = ctx.flags["dialect"] ?? ctx.config.dialect ?? "bun-sqlite";
3034
- const configPath = ctx.flags["config"] ?? resolve20(ctx.cwd, "drizzle.config.ts");
3174
+ const configPath = ctx.flags["config"] ?? resolve21(ctx.cwd, "drizzle.config.ts");
3035
3175
  const wantStatus = Boolean(ctx.flags["status"]);
3036
3176
  const generateName = ctx.flags["generate"];
3037
3177
  if (generateName) {
@@ -3078,7 +3218,6 @@ async function runStatus(cwd, folder, dialect, configUrl = "") {
3078
3218
  return 1;
3079
3219
  }
3080
3220
  const script = `
3081
- import 'reflect-metadata';
3082
3221
  import { DrizzleService } from '@nexusts/drizzle';
3083
3222
 
3084
3223
  const url = ${JSON.stringify(url)};
@@ -3092,7 +3231,7 @@ const applied = await svc.appliedMigrations();
3092
3231
  console.log(JSON.stringify({ total: applied.length, applied }, null, 2));
3093
3232
  await svc.close();
3094
3233
  `;
3095
- const tmpFile = resolve20(cwd, ".nx-migrate-status.mjs");
3234
+ const tmpFile = resolve21(cwd, ".nx-migrate-status.mjs");
3096
3235
  await import("fs/promises").then((m) => m.writeFile(tmpFile, script, "utf-8"));
3097
3236
  try {
3098
3237
  const code = await new Promise((resP) => {
@@ -3116,7 +3255,7 @@ function readEnvUrl(dialect) {
3116
3255
  var db_migrate_default = dbMigrateCommand;
3117
3256
 
3118
3257
  // packages/cli/src/commands/db-generate.ts
3119
- import { resolve as resolve21 } from "path";
3258
+ import { resolve as resolve22 } from "path";
3120
3259
  var dbGenerateCommand = {
3121
3260
  name: "db:generate",
3122
3261
  aliases: ["db:g", "db-generate", "generate-migration"],
@@ -3149,7 +3288,7 @@ var dbGenerateCommand = {
3149
3288
  logger.info(`Generating raw SQL migration: ${name} (dialect=${dialect})`);
3150
3289
  return runSqlTemplate(ctx.cwd, name, dialect);
3151
3290
  }
3152
- const configPath = resolve21(ctx.cwd, "drizzle.config.ts");
3291
+ const configPath = resolve22(ctx.cwd, "drizzle.config.ts");
3153
3292
  const args2 = ["generate", "--config", configPath];
3154
3293
  if (name)
3155
3294
  args2.push("--name", name);
@@ -3158,7 +3297,7 @@ var dbGenerateCommand = {
3158
3297
  }
3159
3298
  };
3160
3299
  async function runSqlTemplate(cwd, name, dialect) {
3161
- const { mkdirSync: mkdirSync5, writeFileSync: writeFileSync3 } = await import("fs");
3300
+ const { mkdirSync: mkdirSync5, writeFileSync: writeFileSync4 } = await import("fs");
3162
3301
  const { join } = await import("path");
3163
3302
  const migrationsDir = join(cwd, "app", "database", "migrations");
3164
3303
  mkdirSync5(migrationsDir, { recursive: true });
@@ -3174,7 +3313,7 @@ async function runSqlTemplate(cwd, name, dialect) {
3174
3313
  -- Generated: ${new Date().toISOString()}
3175
3314
 
3176
3315
  `;
3177
- writeFileSync3(filepath, header);
3316
+ writeFileSync4(filepath, header);
3178
3317
  logger.success(`created ${filepath}`);
3179
3318
  logger.info("Edit the SQL file, then run `nx db:migrate` to apply it.");
3180
3319
  return 0;
@@ -3185,7 +3324,7 @@ var db_generate_default = dbGenerateCommand;
3185
3324
  import { spawn as spawn2 } from "child_process";
3186
3325
  import { existsSync as existsSync6 } from "fs";
3187
3326
  import { mkdir, readdir, writeFile as writeFile2, unlink } from "fs/promises";
3188
- import { resolve as resolve22 } from "path";
3327
+ import { resolve as resolve23 } from "path";
3189
3328
  var SEED_TEMPLATE = `/**
3190
3329
  * Seed: {name}
3191
3330
  *
@@ -3255,7 +3394,7 @@ var dbSeedCommand = {
3255
3394
  }
3256
3395
  ],
3257
3396
  async run(ctx) {
3258
- const folder = resolve22(ctx.cwd, ctx.flags["folder"] ?? ctx.config.paths?.seeds ?? "db/seeds");
3397
+ const folder = resolve23(ctx.cwd, ctx.flags["folder"] ?? ctx.config.paths?.seeds ?? "db/seeds");
3259
3398
  const dialect = ctx.flags["dialect"] ?? ctx.config.dialect ?? "bun-sqlite";
3260
3399
  const createName = ctx.flags["create"];
3261
3400
  const fileName = ctx.flags["file"];
@@ -3266,7 +3405,7 @@ var dbSeedCommand = {
3266
3405
  if (!existsSync6(folder)) {
3267
3406
  logger.info(`creating empty seeds folder at ${folder}`);
3268
3407
  await mkdir(folder, { recursive: true });
3269
- await writeFile2(resolve22(folder, "_README.ts"), `// Seed files go here. Run with: nx db:seed
3408
+ await writeFile2(resolve23(folder, "_README.ts"), `// Seed files go here. Run with: nx db:seed
3270
3409
  `, "utf-8");
3271
3410
  return 0;
3272
3411
  }
@@ -3290,12 +3429,11 @@ var dbSeedCommand = {
3290
3429
  if (reset) {
3291
3430
  logger.warn("--reset is set: truncating every table in the schema before running seeds.");
3292
3431
  }
3293
- const seedImports = target.map((f, i) => `import seed_${i} from ${JSON.stringify(resolve22(folder, f))};`).join(`
3432
+ const seedImports = target.map((f, i) => `import seed_${i} from ${JSON.stringify(resolve23(folder, f))};`).join(`
3294
3433
  `);
3295
3434
  const seedCalls = target.map((_, i) => ` await seed_${i}({ db, logger, dialect, truncate: (t) => db.truncate(t) });`).join(`
3296
3435
  `);
3297
3436
  const script = `
3298
- import 'reflect-metadata';
3299
3437
  import { DrizzleService } from '@nexusts/drizzle';
3300
3438
  import { Logger } from '@nexusts/logger';
3301
3439
 
@@ -3324,7 +3462,7 @@ ${seedCalls}
3324
3462
  await db.close();
3325
3463
  logger.info(\`Seeds complete (\${${target.length}} file(s))\`);
3326
3464
  `;
3327
- const tmpFile = resolve22(ctx.cwd, ".nx-db-seed.mjs");
3465
+ const tmpFile = resolve23(ctx.cwd, ".nx-db-seed.mjs");
3328
3466
  await writeFile2(tmpFile, script, "utf-8");
3329
3467
  try {
3330
3468
  const code = await new Promise((resP) => {
@@ -3366,11 +3504,11 @@ async function createSeedFile(folder, name) {
3366
3504
  await mkdir(folder, { recursive: true });
3367
3505
  let candidate = `${name}.ts`;
3368
3506
  let i = 1;
3369
- while (existsSync6(resolve22(folder, candidate))) {
3507
+ while (existsSync6(resolve23(folder, candidate))) {
3370
3508
  candidate = `${name}_${i}.ts`;
3371
3509
  i++;
3372
3510
  }
3373
- const path = resolve22(folder, candidate);
3511
+ const path = resolve23(folder, candidate);
3374
3512
  const body = SEED_TEMPLATE.replace(/\{name\}/g, name);
3375
3513
  await writeFile2(path, body, "utf-8");
3376
3514
  logger.info(`created ${path}`);
@@ -3383,8 +3521,34 @@ function readEnvUrl2(dialect) {
3383
3521
  var db_seed_default = dbSeedCommand;
3384
3522
 
3385
3523
  // packages/cli/src/commands/new.ts
3386
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, writeFileSync as writeFileSync3 } from "fs";
3387
- import { resolve as resolve23 } from "path";
3524
+ import { existsSync as existsSync7, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
3525
+ import { resolve as resolve24 } from "path";
3526
+ var VALID_OPTIONS2 = {
3527
+ style: ["nest", "adonis", "functional"],
3528
+ view: ["rendu", "edge", "eta", "inertia", "none"],
3529
+ orm: ["drizzle", "prisma", "kysely", "none"],
3530
+ db: ["bun-sqlite", "node-sqlite", "libsql", "postgres", "mysql", "none"],
3531
+ frontend: ["react", "vue", "svelte", "solid"]
3532
+ };
3533
+ async function resolveOpt2(flags, key, valid, defaultVal, interactive) {
3534
+ const flagVal = flags[key];
3535
+ if (flagVal) {
3536
+ if (valid.includes(flagVal))
3537
+ return flagVal;
3538
+ if (!interactive) {
3539
+ logger.error(`Invalid --${key} "${flagVal}". Valid values: ${valid.join(", ")}`);
3540
+ process.exit(1);
3541
+ }
3542
+ logger.warn(`"${flagVal}" is not valid for --${key}. Please choose from the list.`);
3543
+ }
3544
+ const label = key === "style" ? "Routing style" : key === "view" ? "View engine" : key === "orm" ? "ORM driver" : key === "db" ? "Database driver" : "Inertia frontend";
3545
+ for (;; ) {
3546
+ const answer = await select(label, [...valid], { default: defaultVal });
3547
+ if (valid.includes(answer))
3548
+ return answer;
3549
+ logger.warn(`"${answer}" is not valid. Please choose from: ${valid.join(", ")}`);
3550
+ }
3551
+ }
3388
3552
  var newCommand = {
3389
3553
  name: "new",
3390
3554
  aliases: ["n"],
@@ -3392,131 +3556,38 @@ var newCommand = {
3392
3556
  description: "Generates a new project directory with nx.config.ts, tsconfig, package.json, and a starter app/main.ts.",
3393
3557
  examples: [
3394
3558
  "nx new my-app",
3395
- "nx new my-app --style nest --view rendu --orm drizzle --db bun-sqlite"
3559
+ "nx new my-app --view inertia --frontend vue"
3396
3560
  ],
3397
3561
  flags: [
3398
- {
3399
- name: "style",
3400
- description: "Routing style (nest|adonis|functional|mixed)"
3401
- },
3562
+ { name: "style", description: "Routing style (nest|adonis|functional)" },
3402
3563
  { name: "view", description: "View engine (rendu|edge|eta|inertia|none)" },
3403
3564
  { name: "orm", description: "ORM driver (drizzle|prisma|kysely|none)" },
3404
- {
3405
- name: "db",
3406
- description: "Database driver (bun-sqlite|node-sqlite|libsql|postgres|mysql|none)"
3407
- },
3408
- {
3409
- name: "frontend",
3410
- description: "Inertia frontend (react|vue|svelte|solid)"
3411
- },
3412
- { name: "no-ssr", description: "Disable Inertia SSR" },
3413
- { name: "no-interaction", description: "Disable interactive prompts" }
3565
+ { name: "db", description: "Database driver" },
3566
+ { name: "frontend", description: "Inertia frontend (react|vue|svelte|solid)" },
3567
+ { name: "no-ssr", description: "Disable SSR" }
3414
3568
  ],
3415
3569
  async run(ctx) {
3416
3570
  const name = ctx.positional[0];
3417
3571
  if (!name) {
3418
- logger.error("Usage: nx new <project-name>");
3572
+ logger.error("Usage: nx new <name>");
3419
3573
  return 1;
3420
3574
  }
3421
- const interactive = !flagBool(ctx.flags, "no-interaction", false);
3422
- const target = resolve23(ctx.cwd, name);
3575
+ const interactive = flagBool(ctx.flags, "interaction", true);
3576
+ const target = resolve24(ctx.cwd, name);
3423
3577
  if (existsSync7(target)) {
3424
- logger.error(`Directory already exists: ${target}`);
3578
+ logger.error(`Directory "${name}" already exists.`);
3425
3579
  return 1;
3426
3580
  }
3427
- const routing = ctx.flags["style"] ?? await select("Routing style", ["nest", "adonis", "functional"], {
3428
- interactive,
3429
- default: "nest"
3430
- });
3431
- const view = ctx.flags["view"] ?? await select("View engine", ["rendu", "edge", "eta", "inertia", "none"], {
3432
- interactive,
3433
- default: "rendu"
3434
- });
3435
- const orm = ctx.flags["orm"] ?? await select("ORM driver", ["drizzle", "prisma", "kysely", "none"], {
3436
- interactive,
3437
- default: "drizzle"
3438
- });
3439
- const db = ctx.flags["db"] ?? await select("Database driver", ["bun-sqlite", "node-sqlite", "libsql", "postgres", "mysql", "none"], {
3440
- interactive,
3441
- default: "bun-sqlite"
3442
- });
3443
- const frontend = ctx.flags["frontend"] ?? await select("Inertia frontend", ["react", "vue", "svelte", "solid"], {
3444
- interactive,
3445
- default: "react"
3446
- });
3581
+ const routing = await resolveOpt2(ctx.flags, "style", VALID_OPTIONS2.style, "nest", interactive);
3582
+ const view = await resolveOpt2(ctx.flags, "view", VALID_OPTIONS2.view, "rendu", interactive);
3583
+ const orm = await resolveOpt2(ctx.flags, "orm", VALID_OPTIONS2.orm, "drizzle", interactive);
3584
+ const db = await resolveOpt2(ctx.flags, "db", VALID_OPTIONS2.db, "bun-sqlite", interactive);
3585
+ const frontend = await resolveOpt2(ctx.flags, "frontend", VALID_OPTIONS2.frontend, "react", interactive);
3447
3586
  const ssr = !flagBool(ctx.flags, "no-ssr", false);
3448
- mkdirSync5(resolve23(target, "app"), { recursive: true });
3449
- if (view !== "none") {
3450
- mkdirSync5(resolve23(target, "resources/views"), { recursive: true });
3451
- }
3452
- mkdirSync5(resolve23(target, "public"), { recursive: true });
3453
- writeFileSync3(resolve23(target, "public/.gitkeep"), "");
3454
- if (view !== "none") {
3455
- writeFileSync3(resolve23(target, "resources/views/welcome.html"), `<h1>Welcome to ${name}</h1>
3456
- <p>This is a sample Rendu template.</p>
3457
- <p>Founded <?= year ?>.</p>
3458
- `);
3459
- }
3460
- writeFileSync3(resolve23(target, ".env"), generateEnvFile());
3461
- writeFileSync3(resolve23(target, ".env.local"), generateEnvLocalFile());
3462
- writeFileSync3(resolve23(target, ".gitignore"), generateGitIgnore());
3463
- const code = render(templates.project["nx.config.ts"], {
3464
- routing,
3465
- view,
3466
- viewPaths: view === "none" ? "" : "resources/views",
3467
- orm,
3468
- dbDriver: db,
3469
- dbUrl: db === "bun-sqlite" || db === "node-sqlite" ? "app.db" : "",
3470
- inertiaFrontend: frontend,
3471
- inertiaSSR: ssr,
3472
- inertiaVersion: "1.0.0"
3473
- });
3474
- writeFileSync3(resolve23(target, "nx.config.ts"), code);
3475
- if (orm === "drizzle") {
3476
- const dialect = db === "bun-sqlite" || db === "node-sqlite" || db === "libsql" ? "sqlite" : db === "postgres" ? "postgresql" : "sqlite";
3477
- const drizzleConfig = render(templates.project["drizzle.config.ts"], {
3478
- dialect,
3479
- dbUrl: db === "bun-sqlite" || db === "node-sqlite" ? "app.db" : ""
3480
- });
3481
- writeFileSync3(resolve23(target, "drizzle.config.ts"), drizzleConfig);
3482
- }
3483
- const deps = {
3484
- "@nexusts/core": "*",
3485
- "reflect-metadata": "^0.2.2",
3486
- hono: "^4.6.0",
3487
- zod: "^3.23.8"
3488
- };
3489
- if (orm === "drizzle") {
3490
- deps["@nexusts/drizzle"] = "*";
3491
- deps["drizzle-orm"] = "^0.45.0";
3492
- if (db === "postgres")
3493
- deps["pg"] = "^8.13.0";
3494
- if (db === "mysql")
3495
- deps["mysql2"] = "^3.11.0";
3496
- if (db === "sqlite" || db === "node-sqlite" || db === "bun-sqlite")
3497
- deps["better-sqlite3"] = "^12.0.0";
3498
- }
3499
- if (view !== "none") {
3500
- deps["@nexusts/static"] = "*";
3501
- }
3502
- const pkgJson = {
3503
- name,
3504
- version: "0.1.0",
3505
- type: "module",
3506
- scripts: {
3507
- dev: "bun --hot app/main.ts",
3508
- build: "bun run build.ts",
3509
- start: "bun app/main.ts",
3510
- test: "vitest",
3511
- nx: "nx"
3512
- },
3513
- dependencies: deps
3514
- };
3515
- if (orm === "drizzle") {
3516
- pkgJson.devDependencies = { "drizzle-kit": "^0.31.0" };
3517
- }
3518
- writeFileSync3(resolve23(target, "package.json"), JSON.stringify(pkgJson, null, 2));
3519
- writeFileSync3(resolve23(target, "tsconfig.json"), `{
3587
+ mkdirSync5(target, { recursive: true });
3588
+ const dbUrl = db === "bun-sqlite" || db === "node-sqlite" ? "app.db" : "";
3589
+ ensureDirectories(target, view);
3590
+ writeFileSync4(resolve24(target, "tsconfig.json"), `{
3520
3591
  "compilerOptions": {
3521
3592
  "target": "ES2022",
3522
3593
  "module": "ESNext",
@@ -3526,81 +3597,22 @@ var newCommand = {
3526
3597
  "strict": true,
3527
3598
  "esModuleInterop": true,
3528
3599
  "skipLibCheck": true,
3529
- "types": ["bun-types"]
3600
+ "types": ["@types/bun"]
3530
3601
  },
3531
3602
  "include": ["app/**/*.ts", "nx.config.ts"]
3532
3603
  }
3533
3604
  `);
3534
- const hasView = view !== "none";
3535
- const staticImport = hasView ? `import { StaticModule } from '@nexusts/static';
3536
- const staticMiddleware = StaticModule.mount({ root: './public', prefix: '/static' });
3537
- ` : "";
3538
- const staticOption = hasView ? `
3539
- middleware: [staticMiddleware],` : "";
3540
- writeFileSync3(resolve23(target, "app/main.ts"), `import 'reflect-metadata';
3541
- import { Application } from '@nexusts/core';
3542
- ${staticImport}import { AppModule } from './app.module.js';
3543
-
3544
- const app = new Application(AppModule, {
3545
- logging: true,
3546
- port: Number(process.env['PORT'] ?? 3000),${staticOption}
3547
- });
3548
-
3549
- await app.listen();
3550
- console.log('[nexus] Listening on http://localhost:' + (process.env['PORT'] ?? 3000));
3551
- `);
3552
- const ormImport = orm === "drizzle" ? `import { DrizzleModule } from '@nexusts/drizzle';
3553
- ` : "";
3554
- const forRootDialect = db === "bun-sqlite" ? "bun-sqlite" : "sqlite";
3555
- const ormBlock = orm === "drizzle" ? ` DrizzleModule.forRoot({
3556
- dialect: '${forRootDialect}',
3557
- connection: { filename: 'app.db' },
3558
- logging: true,
3559
- })` : "";
3560
- writeFileSync3(resolve23(target, "app/app.module.ts"), `${ormImport}import { Module } from '@nexusts/core';
3561
- import { HomeController } from './controllers/home.controller.js';
3562
-
3563
- @Module({
3564
- imports: [${orm === "drizzle" ? `
3565
- ${ormBlock},
3566
- ` : ""} ],
3567
- controllers: [HomeController],
3568
- })
3569
- export class AppModule {}
3570
- `);
3571
- mkdirSync5(resolve23(target, "app/controllers"), { recursive: true });
3572
- const homeViewReturn = view !== "none" ? `{
3573
- view: 'welcome.html',
3574
- data: { year: new Date().getFullYear() },
3575
- }` : `{ status: 200, body: { message: 'Hello from NexusTS!' } }`;
3576
- writeFileSync3(resolve23(target, "app/controllers/home.controller.ts"), `import { Controller, Get } from '@nexusts/core';
3577
-
3578
- @Controller('/')
3579
- export class HomeController {
3580
- @Get('/')
3581
- index() {
3582
- return ${homeViewReturn};
3583
- }
3584
- }
3585
- `);
3586
- writeFileSync3(resolve23(target, "README.md"), `# ${name}
3587
-
3588
- A new NexusTS project.
3589
-
3590
- ## Run
3591
-
3592
- \`\`\`bash
3593
- bun install
3594
- bun run dev
3595
- \`\`\`
3596
-
3597
- ## Scaffolding
3598
-
3599
- \`\`\`bash
3600
- bunx nx make:crud Post
3601
- \`\`\`
3605
+ const { deps, devDeps } = computeDeps(view, orm, db, frontend);
3606
+ const pkgJson = buildPackageJson(name, deps, devDeps, view, frontend);
3607
+ writeFileSync4(resolve24(target, "package.json"), `${JSON.stringify(pkgJson, null, 2)}
3602
3608
  `);
3603
- logger.success(`created ${target}`);
3609
+ const opts = { target, name, routing, view, orm, db, frontend, ssr, dbUrl };
3610
+ const files = generateProjectFiles(target, opts);
3611
+ logger.success(`created ${name}`);
3612
+ for (const f of files)
3613
+ logger.info(` + ${f}`);
3614
+ logger.info(` + tsconfig.json`);
3615
+ logger.info(` + package.json`);
3604
3616
  logger.blank();
3605
3617
  logger.heading("Next steps");
3606
3618
  logger.info(` cd ${name}`);
@@ -3610,62 +3622,11 @@ bunx nx make:crud Post
3610
3622
  return 0;
3611
3623
  }
3612
3624
  };
3613
- function generateEnvFile() {
3614
- return `# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3615
- # NexusTS \u2014 Environment Variables (committed to git)
3616
- #
3617
- # Shared defaults for all environments. Override locally via
3618
- # .env.local (gitignored) or by environment via .env.{NODE_ENV}
3619
- # (e.g. .env.production, .env.development).
3620
- #
3621
- # Uncomment the database config for your driver:
3622
- # \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3623
-
3624
- # \u2500\u2500 App \u2500\u2500
3625
- NODE_ENV=development
3626
- PORT=3000
3627
-
3628
- # \u2500\u2500 Session secret (REQUIRED) \u2500\u2500
3629
- # Generate with: openssl rand -base64 32
3630
- SESSION_SECRET=change-me-in-production
3631
-
3632
- # \u2500\u2500 Database: SQLite (default, zero config) \u2500\u2500
3633
- DATABASE_URL=app.db
3634
-
3635
- # \u2500\u2500 Database: PostgreSQL \u2500\u2500
3636
- # DATABASE_URL=postgres://user:password@localhost:5432/myapp
3637
-
3638
- # \u2500\u2500 Database: MySQL \u2500\u2500
3639
- # DATABASE_URL=mysql://user:password@localhost:3306/myapp
3640
- `;
3641
- }
3642
- function generateEnvLocalFile() {
3643
- return `# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3644
- # NexusTS \u2014 Local Overrides (DO NOT COMMIT to git)
3645
- #
3646
- # This file is gitignored. Use it for secrets and local
3647
- # configuration that should never be checked in.
3648
- # \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3649
-
3650
- # Override any value from .env here:
3651
- # DATABASE_URL=postgres://user:password@localhost:5432/myapp
3652
- # SESSION_SECRET=my-local-secret
3653
- `;
3654
- }
3655
- function generateGitIgnore() {
3656
- return `# NexusTS
3657
- node_modules/
3658
- app.db
3659
- *.db
3660
- .env.local
3661
- dist/
3662
- `;
3663
- }
3664
3625
  var new_default = newCommand;
3665
3626
 
3666
3627
  // packages/cli/src/commands/config.ts
3667
- import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
3668
- import { resolve as resolve24 } from "path";
3628
+ import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
3629
+ import { resolve as resolve25 } from "path";
3669
3630
  var DEFAULT_VALUES = {
3670
3631
  routing: "nest",
3671
3632
  view: "rendu",
@@ -3780,12 +3741,12 @@ var configCommand = {
3780
3741
  async run(ctx) {
3781
3742
  const interactive = !flagBool(ctx.flags, "no-interaction", false);
3782
3743
  const force = flagBool(ctx.flags, "force", false);
3783
- const target = resolve24(ctx.cwd, ctx.flags["target"] ?? ".");
3744
+ const target = resolve25(ctx.cwd, ctx.flags["target"] ?? ".");
3784
3745
  if (!existsSync8(target)) {
3785
3746
  logger.error(`Target directory does not exist: ${target}`);
3786
3747
  return 1;
3787
3748
  }
3788
- const nxConfigPath = resolve24(target, "nx.config.ts");
3749
+ const nxConfigPath = resolve25(target, "nx.config.ts");
3789
3750
  const values = parseExistingConfig(nxConfigPath);
3790
3751
  const flag = (k) => ctx.flags[k];
3791
3752
  const flagBoolStrict = (k, def) => flagBool(ctx.flags, k, def);
@@ -3840,7 +3801,7 @@ var configCommand = {
3840
3801
  writeNxConfig(target, values);
3841
3802
  logger.info(` + nx.config.ts`);
3842
3803
  }
3843
- const drizzleConfigPath = resolve24(target, "drizzle.config.ts");
3804
+ const drizzleConfigPath = resolve25(target, "drizzle.config.ts");
3844
3805
  if (values.orm === "drizzle") {
3845
3806
  const dialect = driverToDialect(values.dbDriver);
3846
3807
  const dbUrl = values.dbUrl;
@@ -3868,11 +3829,11 @@ var configCommand = {
3868
3829
  };
3869
3830
  function writeNxConfig(target, values) {
3870
3831
  const code = render(templates.project["nx.config.ts"], values);
3871
- writeFileSync4(resolve24(target, "nx.config.ts"), code);
3832
+ writeFileSync5(resolve25(target, "nx.config.ts"), code);
3872
3833
  }
3873
3834
  function writeDrizzleConfig(target, values) {
3874
3835
  const code = render(templates.project["drizzle.config.ts"], values);
3875
- writeFileSync4(resolve24(target, "drizzle.config.ts"), code);
3836
+ writeFileSync5(resolve25(target, "drizzle.config.ts"), code);
3876
3837
  }
3877
3838
  var config_default = configCommand;
3878
3839
 
@@ -3881,9 +3842,9 @@ import {
3881
3842
  existsSync as existsSync9,
3882
3843
  mkdirSync as mkdirSync6,
3883
3844
  readFileSync as readFileSync6,
3884
- writeFileSync as writeFileSync5
3845
+ writeFileSync as writeFileSync6
3885
3846
  } from "fs";
3886
- import { dirname as dirname5, resolve as resolve25 } from "path";
3847
+ import { dirname as dirname5, resolve as resolve26 } from "path";
3887
3848
  import * as readline from "readline";
3888
3849
  import * as vm from "vm";
3889
3850
  var BANNER = (() => {
@@ -3965,10 +3926,10 @@ var replCommand = {
3965
3926
  async run(ctx) {
3966
3927
  const mod = ctx.flags["module"];
3967
3928
  const noBoot = Boolean(ctx.flags["no-boot"]);
3968
- const histPath = resolve25(ctx.cwd, ctx.flags["history"] ?? ".nx-repl-history");
3929
+ const histPath = resolve26(ctx.cwd, ctx.flags["history"] ?? ".nx-repl-history");
3969
3930
  const env = { console };
3970
3931
  if (!noBoot) {
3971
- const modPath = resolve25(ctx.cwd, mod ?? "app/app.module.ts");
3932
+ const modPath = resolve26(ctx.cwd, mod ?? "app/app.module.ts");
3972
3933
  if (!existsSync9(modPath)) {
3973
3934
  logger.error(`module not found: ${modPath}`);
3974
3935
  logger.info("pass --module <path> or --no-boot to skip booting");
@@ -4286,7 +4247,7 @@ function saveHistoryFile(path, history) {
4286
4247
  const dir = dirname5(path);
4287
4248
  if (!existsSync9(dir))
4288
4249
  mkdirSync6(dir, { recursive: true });
4289
- writeFileSync5(path, history.slice(-1000).join(`
4250
+ writeFileSync6(path, history.slice(-1000).join(`
4290
4251
  `));
4291
4252
  } catch {}
4292
4253
  }
@@ -4294,7 +4255,8 @@ var repl_default = replCommand;
4294
4255
 
4295
4256
  // packages/cli/src/commands/route-list.ts
4296
4257
  import { readdirSync, statSync as statSync2 } from "fs";
4297
- import { resolve as resolve26 } from "path";
4258
+ import { resolve as resolve27 } from "path";
4259
+ import { safeGetMeta } from "@nexusts/core/di/safe-reflect";
4298
4260
  var routeListCommand = {
4299
4261
  name: "route:list",
4300
4262
  aliases: ["routes", "route-list"],
@@ -4304,7 +4266,7 @@ var routeListCommand = {
4304
4266
  { name: "format", description: "Output format: table (default) | json" }
4305
4267
  ],
4306
4268
  async run(ctx) {
4307
- const controllersDir = resolve26(ctx.cwd, ctx.config.paths.controllers);
4269
+ const controllersDir = resolve27(ctx.cwd, ctx.config.paths.controllers);
4308
4270
  try {
4309
4271
  statSync2(controllersDir);
4310
4272
  } catch {
@@ -4318,16 +4280,16 @@ var routeListCommand = {
4318
4280
  }
4319
4281
  const routes = [];
4320
4282
  for (const file of files) {
4321
- const fullPath = resolve26(controllersDir, file);
4283
+ const fullPath = resolve27(controllersDir, file);
4322
4284
  try {
4323
4285
  const mod = await import(`${fullPath}?t=${Date.now()}`);
4324
4286
  for (const exportName of Object.keys(mod)) {
4325
4287
  const cls = mod[exportName];
4326
4288
  if (typeof cls !== "function")
4327
4289
  continue;
4328
- const controllerMeta = Reflect.getMetadata("nexus:controller", cls);
4290
+ const controllerMeta = safeGetMeta("nexus:controller", cls);
4329
4291
  const prefix = controllerMeta?.prefix ?? "";
4330
- const routeList = Reflect.getMetadata("nexus:routes", cls) ?? [];
4292
+ const routeList = safeGetMeta("nexus:routes", cls) ?? [];
4331
4293
  for (const r of routeList) {
4332
4294
  routes.push({
4333
4295
  method: String(r.method).toUpperCase(),
@@ -4520,5 +4482,5 @@ main().then((code) => process.exit(code)).catch((err) => {
4520
4482
  process.exit(1);
4521
4483
  });
4522
4484
 
4523
- //# debugId=398828C274DA323A64756E2164756E21
4485
+ //# debugId=B2F4C39893BC135664756E2164756E21
4524
4486
  //# sourceMappingURL=index.js.map