create-taserjs 0.0.6 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/addons/prisma/index.cjs +12 -5
- package/dist/cjs/addons/prisma/index.cjs.map +1 -1
- package/dist/cjs/addons/registry.cjs +3 -1
- package/dist/cjs/addons/registry.cjs.map +1 -1
- package/dist/cjs/addons/validators.cjs +10 -13
- package/dist/cjs/addons/validators.cjs.map +1 -1
- package/dist/cjs/cli.cjs +7 -5
- package/dist/cjs/cli.cjs.map +1 -1
- package/dist/cjs/commands/create.cjs +72 -49
- package/dist/cjs/commands/create.cjs.map +1 -1
- package/dist/cjs/commands/create.d.cts +2 -1
- package/dist/cjs/core/parse-options.cjs +48 -22
- package/dist/cjs/core/parse-options.cjs.map +1 -1
- package/dist/cjs/core/parse-options.d.cts +19 -4
- package/dist/cjs/core/project-config.cjs +5 -1
- package/dist/cjs/core/project-config.cjs.map +1 -1
- package/dist/cjs/core/resolve-packages.cjs +35 -65
- package/dist/cjs/core/resolve-packages.cjs.map +1 -1
- package/dist/cjs/core/resolve-packages.d.cts +1 -4
- package/dist/cjs/core/scaffold-engine.cjs +16 -27
- package/dist/cjs/core/scaffold-engine.cjs.map +1 -1
- package/dist/cjs/core/targets.cjs +171 -0
- package/dist/cjs/core/targets.cjs.map +1 -0
- package/dist/cjs/core/targets.d.cts +49 -0
- package/dist/cjs/core/types.cjs +22 -11
- package/dist/cjs/core/types.cjs.map +1 -1
- package/dist/cjs/core/types.d.cts +20 -4
- package/dist/cjs/core/validate-project-name.cjs +0 -1
- package/dist/cjs/core/validate-project-name.cjs.map +1 -1
- package/dist/cjs/frameworks/index.cjs +79 -171
- package/dist/cjs/frameworks/index.cjs.map +1 -1
- package/dist/cjs/frameworks/index.d.cts +18 -3
- package/dist/cjs/scaffold.d.cts +2 -2
- package/dist/cjs/templates/base.cjs +15 -99
- package/dist/cjs/templates/base.cjs.map +1 -1
- package/dist/cjs/templates/base.d.cts +0 -3
- package/dist/cjs/types.d.cts +1 -1
- package/dist/esm/addons/prisma/index.js +12 -5
- package/dist/esm/addons/prisma/index.js.map +1 -1
- package/dist/esm/addons/registry.js +4 -2
- package/dist/esm/addons/registry.js.map +1 -1
- package/dist/esm/addons/validators.js +10 -13
- package/dist/esm/addons/validators.js.map +1 -1
- package/dist/esm/cli.js +7 -5
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/commands/create.d.ts +2 -1
- package/dist/esm/commands/create.js +74 -51
- package/dist/esm/commands/create.js.map +1 -1
- package/dist/esm/core/parse-options.d.ts +19 -4
- package/dist/esm/core/parse-options.js +47 -23
- package/dist/esm/core/parse-options.js.map +1 -1
- package/dist/esm/core/project-config.js +5 -1
- package/dist/esm/core/project-config.js.map +1 -1
- package/dist/esm/core/resolve-packages.d.ts +1 -4
- package/dist/esm/core/resolve-packages.js +35 -65
- package/dist/esm/core/resolve-packages.js.map +1 -1
- package/dist/esm/core/scaffold-engine.js +18 -29
- package/dist/esm/core/scaffold-engine.js.map +1 -1
- package/dist/esm/core/targets.d.ts +49 -0
- package/dist/esm/core/targets.js +166 -0
- package/dist/esm/core/targets.js.map +1 -0
- package/dist/esm/core/types.d.ts +20 -4
- package/dist/esm/core/types.js +20 -11
- package/dist/esm/core/types.js.map +1 -1
- package/dist/esm/core/validate-project-name.js +0 -1
- package/dist/esm/core/validate-project-name.js.map +1 -1
- package/dist/esm/frameworks/index.d.ts +18 -3
- package/dist/esm/frameworks/index.js +76 -171
- package/dist/esm/frameworks/index.js.map +1 -1
- package/dist/esm/scaffold.d.ts +2 -2
- package/dist/esm/templates/base.d.ts +0 -3
- package/dist/esm/templates/base.js +16 -98
- package/dist/esm/templates/base.js.map +1 -1
- package/dist/esm/types.d.ts +1 -1
- package/package.json +3 -2
- package/src/addons/prisma/index.ts +12 -5
- package/src/addons/registry.ts +6 -2
- package/src/addons/validators.ts +10 -13
- package/src/cli.ts +6 -4
- package/src/commands/create.ts +92 -26
- package/src/core/parse-options.ts +84 -35
- package/src/core/project-config.ts +6 -1
- package/src/core/resolve-packages.ts +37 -74
- package/src/core/scaffold-engine.ts +41 -65
- package/src/core/targets.ts +211 -0
- package/src/core/types.ts +40 -24
- package/src/core/validate-project-name.ts +0 -4
- package/src/frameworks/index.ts +80 -178
- package/src/scaffold.ts +2 -2
- package/src/templates/base.ts +14 -98
- package/src/types.ts +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../../src/templates/base.ts"],"sourcesContent":["import type { BootBinding } from \"../addons/types.js\";\nimport type { ScaffoldContext } from \"../core/types.js\";\n\nexport function packageJsonTemplate(\n projectName: string,\n scripts: Record<string, string> = {},\n): string {\n const pkg = {\n name: projectName,\n version: \"1.0.0\",\n private: true,\n type: \"module\",\n imports: {\n \"#
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../../src/templates/base.ts"],"sourcesContent":["import type { BootBinding } from \"../addons/types.js\";\nimport type { ScaffoldContext } from \"../core/types.js\";\n\nexport function packageJsonTemplate(\n projectName: string,\n scripts: Record<string, string> = {},\n): string {\n const pkg = {\n name: projectName,\n version: \"1.0.0\",\n private: true,\n type: \"module\",\n imports: {\n \"#taserjs/router\": \"./src/taser.ts\",\n },\n scripts: {\n dev: \"vite\",\n build: \"vite build\",\n typecheck: \"tsc --noEmit -p tsconfig.json\",\n ...scripts,\n },\n };\n\n return `${JSON.stringify(pkg, null, 2)}\\n`;\n}\n\nexport function tsconfigTemplate(): string {\n return `${JSON.stringify(\n {\n compilerOptions: {\n target: \"ES2022\",\n module: \"NodeNext\",\n paths: {\n \"#taserjs/router\": [\"./src/taser.ts\"],\n },\n strict: true,\n skipLibCheck: true,\n verbatimModuleSyntax: true,\n isolatedModules: true,\n noEmit: true,\n },\n include: [\"src\", \".taser/types/**/*.d.ts\"],\n },\n null,\n 2,\n )}\\n`;\n}\n\nexport function gitignoreTemplate(): string {\n return `node_modules\ndist\n.output\n.taser\n.DS_Store\n*.log\n.env\nlocal.db\ndrizzle\n`;\n}\n\nexport function contextTemplate(bindings: BootBinding[]): string {\n const imports = bindings.map(\n (binding) => `import { ${binding.factoryName} } from '${binding.importPath}'`,\n );\n\n const bootBody =\n bindings.length > 0\n ? bindings.map((binding) => ` ${binding.key}: ${binding.factoryName}(),`).join(\"\\n\")\n : \"\";\n\n const bootBlock = bindings.length > 0 ? ` boot: () => ({\\n${bootBody}\\n }),` : \"\";\n\n const importBlock = imports.length > 0 ? `${imports.join(\"\\n\")}\\n\\n` : \"\";\n\n return `${importBlock}import { createContext } from '@taserjs/router'\n\nexport const context = createContext({\n${bootBlock}\n request: () => ({\n requestId: crypto.randomUUID(),\n }),\n})\n`;\n}\n\nexport function rootLayoutTemplate(): string {\n return `import { cors } from '@taserjs/router/cors'\n\nimport { t } from '#taserjs/router'\n\nexport const Middleware = t.middleware('/$')\n .use(cors())\n`;\n}\n\nexport function indexRouteTemplate(): string {\n return `import { json } from '@taserjs/router/reply'\nimport { t } from '#taserjs/router'\n\nconst GET = t.get('/')\n\nexport type RouteContext = typeof GET.$Infer.Context\nexport const Route = GET.handler((_ctx) => {\n return json({ message: 'Welcome to Taser' })\n})\n`;\n}\n\nexport function healthRouteTemplate(ctx: ScaffoldContext): string {\n const lines: string[] = [];\n\n if (ctx.logger) {\n lines.push(\" ctx.logger.info('health check')\");\n }\n\n if (ctx.db) {\n lines.push(\" // ctx.db is available from context boot\");\n }\n\n const body = lines.length > 0 ? `${lines.join(\"\\n\")}\\n` : \"\";\n const ctxArg = lines.length > 0 ? \"(ctx)\" : \"(_ctx)\";\n\n return `import { json } from '@taserjs/router/reply'\nimport { t } from '#taserjs/router'\n\nconst GET = t.get('/health')\n\nexport type RouteContext = typeof GET.$Infer.Context\nexport const Route = GET.handler(${ctxArg} => {\n${body} return json({ ok: true })\n})\n`;\n}\n"],"mappings":";AAGA,SAAgB,oBACd,aACA,UAAkC,CAAC,GAC3B;CACR,MAAM,MAAM;EACV,MAAM;EACN,SAAS;EACT,SAAS;EACT,MAAM;EACN,SAAS,EACP,mBAAmB,iBACrB;EACA,SAAS;GACP,KAAK;GACL,OAAO;GACP,WAAW;GACX,GAAG;EACL;CACF;CAEA,OAAO,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE;AACzC;AAEA,SAAgB,mBAA2B;CACzC,OAAO,GAAG,KAAK,UACb;EACE,iBAAiB;GACf,QAAQ;GACR,QAAQ;GACR,OAAO,EACL,mBAAmB,CAAC,gBAAgB,EACtC;GACA,QAAQ;GACR,cAAc;GACd,sBAAsB;GACtB,iBAAiB;GACjB,QAAQ;EACV;EACA,SAAS,CAAC,OAAO,wBAAwB;CAC3C,GACA,MACA,CACF,EAAE;AACJ;AAEA,SAAgB,oBAA4B;CAC1C,OAAO;;;;;;;;;;AAUT;AAEA,SAAgB,gBAAgB,UAAiC;CAC/D,MAAM,UAAU,SAAS,KACtB,YAAY,YAAY,QAAQ,YAAY,WAAW,QAAQ,WAAW,EAC7E;CAEA,MAAM,WACJ,SAAS,SAAS,IACd,SAAS,KAAK,YAAY,OAAO,QAAQ,IAAI,IAAI,QAAQ,YAAY,IAAI,CAAC,CAAC,KAAK,IAAI,IACpF;CAEN,MAAM,YAAY,SAAS,SAAS,IAAI,qBAAqB,SAAS,WAAW;CAIjF,OAAO,GAFa,QAAQ,SAAS,IAAI,GAAG,QAAQ,KAAK,IAAI,EAAE,QAAQ,GAEjD;;;EAGtB,UAAU;;;;;;AAMZ;AAEA,SAAgB,qBAA6B;CAC3C,OAAO;;;;;;;AAOT;AAEA,SAAgB,qBAA6B;CAC3C,OAAO;;;;;;;;;;AAUT;AAEA,SAAgB,oBAAoB,KAA8B;CAChE,MAAM,QAAkB,CAAC;CAEzB,IAAI,IAAI,QACN,MAAM,KAAK,mCAAmC;CAGhD,IAAI,IAAI,IACN,MAAM,KAAK,4CAA4C;CAGzD,MAAM,OAAO,MAAM,SAAS,IAAI,GAAG,MAAM,KAAK,IAAI,EAAE,MAAM;CAG1D,OAAO;;;;;;mCAFQ,MAAM,SAAS,IAAI,UAAU,SAQJ;EACxC,KAAK;;;AAGP"}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { DbDriver, DbOdm, LoggerId, PackageGroups,
|
|
1
|
+
export type { DbDriver, DbOdm, DeployTarget, Framework, LoggerId, PackageGroups, Runtime, ScaffoldOptions, ScaffoldResult, ValidatorId, } from './core/types.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-taserjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Scaffold a new Taser application",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"publint": "^0.3.22",
|
|
32
32
|
"typescript": "^5.9.3",
|
|
33
33
|
"vite": "^8.1.5",
|
|
34
|
-
"vitest": "^4.1.10"
|
|
34
|
+
"vitest": "^4.1.10",
|
|
35
|
+
"@taserjs/router-generator": "0.1.1"
|
|
35
36
|
},
|
|
36
37
|
"engines": {
|
|
37
38
|
"node": ">=20.19"
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { AddonDefinition } from "../types.js";
|
|
2
2
|
import type { DbDriver } from "../../core/types.js";
|
|
3
3
|
|
|
4
|
+
/** Latest Prisma ORM v7 release line — keep all @prisma/* packages on this range. */
|
|
5
|
+
const PRISMA_VERSION = "^7.10.0";
|
|
6
|
+
|
|
7
|
+
function prismaPackage(name: string): string {
|
|
8
|
+
return `${name}@${PRISMA_VERSION}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
4
11
|
function prismaProvider(driver: DbDriver): string {
|
|
5
12
|
switch (driver) {
|
|
6
13
|
case "postgres":
|
|
@@ -28,12 +35,12 @@ function envExample(driver: DbDriver): string {
|
|
|
28
35
|
function driverPackages(driver: DbDriver): string[] {
|
|
29
36
|
switch (driver) {
|
|
30
37
|
case "postgres":
|
|
31
|
-
return ["@prisma/adapter-pg", "pg"];
|
|
38
|
+
return [prismaPackage("@prisma/adapter-pg"), "pg"];
|
|
32
39
|
case "mysql":
|
|
33
|
-
return ["@prisma/adapter-mariadb", "mariadb"];
|
|
40
|
+
return [prismaPackage("@prisma/adapter-mariadb"), "mariadb"];
|
|
34
41
|
case "sqlite":
|
|
35
42
|
default:
|
|
36
|
-
return ["@prisma/adapter-better-sqlite3", "better-sqlite3"];
|
|
43
|
+
return [prismaPackage("@prisma/adapter-better-sqlite3"), "better-sqlite3"];
|
|
37
44
|
}
|
|
38
45
|
}
|
|
39
46
|
|
|
@@ -125,11 +132,11 @@ export const prismaAddon: AddonDefinition = {
|
|
|
125
132
|
category: "database",
|
|
126
133
|
dependencies(ctx) {
|
|
127
134
|
const driver = ctx.driver ?? "sqlite";
|
|
128
|
-
return ["@prisma/client", ...driverPackages(driver)];
|
|
135
|
+
return [prismaPackage("@prisma/client"), ...driverPackages(driver)];
|
|
129
136
|
},
|
|
130
137
|
devDependencies(ctx) {
|
|
131
138
|
const driver = ctx.driver ?? "sqlite";
|
|
132
|
-
return ["prisma", ...driverDevPackages(driver)];
|
|
139
|
+
return [prismaPackage("prisma"), ...driverDevPackages(driver)];
|
|
133
140
|
},
|
|
134
141
|
scripts() {
|
|
135
142
|
return {
|
package/src/addons/registry.ts
CHANGED
|
@@ -12,8 +12,10 @@ import {
|
|
|
12
12
|
DB_DRIVERS,
|
|
13
13
|
DB_ODMS,
|
|
14
14
|
DEFAULT_DB_DRIVER,
|
|
15
|
+
DEPLOY_TARGETS,
|
|
16
|
+
FRAMEWORKS,
|
|
15
17
|
LOGGERS,
|
|
16
|
-
|
|
18
|
+
RUNTIMES,
|
|
17
19
|
VALIDATORS,
|
|
18
20
|
} from "../core/types.js";
|
|
19
21
|
|
|
@@ -34,7 +36,9 @@ const VALIDATOR_ADDONS = ALL_ADDONS.filter((addon) => addon.category === "valida
|
|
|
34
36
|
|
|
35
37
|
export function getCapabilitiesCatalog(): CapabilitiesCatalog {
|
|
36
38
|
return {
|
|
37
|
-
|
|
39
|
+
frameworks: [...FRAMEWORKS],
|
|
40
|
+
deployTargets: [...DEPLOY_TARGETS],
|
|
41
|
+
runtimes: [...RUNTIMES],
|
|
38
42
|
db: {
|
|
39
43
|
odms: [...DB_ODMS],
|
|
40
44
|
drivers: [...DB_DRIVERS],
|
package/src/addons/validators.ts
CHANGED
|
@@ -8,26 +8,23 @@ export const IMPORT_LINES: Record<ValidatorId, string> = {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
export const VALIDATION_BLOCK_TEMPLATE: Record<ValidatorId, string> = {
|
|
11
|
-
zod:
|
|
12
|
-
query
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}`,
|
|
17
|
-
valibot: `, {
|
|
18
|
-
query: v.object({ name: v.optional(v.string(), 'Taser') }),
|
|
19
|
-
}`,
|
|
11
|
+
zod: `
|
|
12
|
+
.query(z.object({ name: z.string().default('Taser') }))`,
|
|
13
|
+
arktype: `
|
|
14
|
+
.query(type({ 'name?': 'string = "Taser"' }))`,
|
|
15
|
+
valibot: `
|
|
16
|
+
.query(v.object({ name: v.optional(v.string(), 'Taser') }))`,
|
|
20
17
|
};
|
|
21
18
|
|
|
22
|
-
const ROUTE_TEMPLATE = (validator: ValidatorId) => `import {
|
|
23
|
-
import { t } from '#
|
|
19
|
+
const ROUTE_TEMPLATE = (validator: ValidatorId) => `import { json } from '@taserjs/router/reply'
|
|
20
|
+
import { t } from '#taserjs/router'
|
|
24
21
|
${IMPORT_LINES[validator]}
|
|
25
22
|
|
|
26
|
-
const GET = t.get('/'${VALIDATION_BLOCK_TEMPLATE[validator]}
|
|
23
|
+
const GET = t.get('/')${VALIDATION_BLOCK_TEMPLATE[validator]}
|
|
27
24
|
|
|
28
25
|
export type RouteContext = typeof GET.$Infer.Context
|
|
29
26
|
export const Route = GET.handler((ctx) => {
|
|
30
|
-
return
|
|
27
|
+
return json({ message: \`Hello, \${ctx.query.name}!\` })
|
|
31
28
|
})
|
|
32
29
|
`;
|
|
33
30
|
|
package/src/cli.ts
CHANGED
|
@@ -11,11 +11,12 @@ async function main(): Promise<void> {
|
|
|
11
11
|
const { values, positionals } = parseArgs({
|
|
12
12
|
args: process.argv.slice(2),
|
|
13
13
|
options: {
|
|
14
|
-
|
|
15
|
-
framework: { type: "string" },
|
|
14
|
+
preset: { type: "string", short: "p" },
|
|
16
15
|
db: { type: "string" },
|
|
17
16
|
logger: { type: "string" },
|
|
18
17
|
validator: { type: "string" },
|
|
18
|
+
framework: { type: "string" },
|
|
19
|
+
runtime: { type: "string" },
|
|
19
20
|
y: { type: "boolean", short: "y" },
|
|
20
21
|
noInstall: { type: "boolean" },
|
|
21
22
|
json: { type: "boolean" },
|
|
@@ -25,11 +26,12 @@ async function main(): Promise<void> {
|
|
|
25
26
|
|
|
26
27
|
const parsed = buildParsedArgsFromCli(
|
|
27
28
|
{
|
|
28
|
-
...(values.
|
|
29
|
-
...(values.framework !== undefined ? { framework: values.framework } : {}),
|
|
29
|
+
...(values.preset !== undefined ? { preset: values.preset } : {}),
|
|
30
30
|
...(values.db !== undefined ? { db: values.db } : {}),
|
|
31
31
|
...(values.logger !== undefined ? { logger: values.logger } : {}),
|
|
32
32
|
...(values.validator !== undefined ? { validator: values.validator } : {}),
|
|
33
|
+
...(values.framework !== undefined ? { framework: values.framework } : {}),
|
|
34
|
+
...(values.runtime !== undefined ? { runtime: values.runtime } : {}),
|
|
33
35
|
...(values.y !== undefined ? { y: values.y } : {}),
|
|
34
36
|
...(values.noInstall !== undefined ? { noInstall: values.noInstall } : {}),
|
|
35
37
|
...(values.json !== undefined ? { json: values.json } : {}),
|
package/src/commands/create.ts
CHANGED
|
@@ -11,27 +11,30 @@ import {
|
|
|
11
11
|
} from "../core/json-output.js";
|
|
12
12
|
import {
|
|
13
13
|
parseDbFlag,
|
|
14
|
+
parseFrameworkFlag,
|
|
14
15
|
parseLoggerFlag,
|
|
15
|
-
|
|
16
|
+
parsePresetFlag,
|
|
17
|
+
parseRuntimeFlag,
|
|
16
18
|
parseValidatorFlag,
|
|
17
19
|
type ParsedCreateArgs,
|
|
18
20
|
resolveScaffoldDefaults,
|
|
19
21
|
} from "../core/parse-options.js";
|
|
20
22
|
import { scaffoldProject } from "../core/scaffold-engine.js";
|
|
23
|
+
import { allowedRuntimeOverrides } from "../core/targets.js";
|
|
21
24
|
import type {
|
|
22
25
|
DbDriver,
|
|
23
26
|
DbOdm,
|
|
27
|
+
DeployTarget,
|
|
24
28
|
LoggerId,
|
|
25
|
-
ProjectType,
|
|
26
29
|
ScaffoldResult,
|
|
27
30
|
ValidatorId,
|
|
28
31
|
} from "../core/types.js";
|
|
29
|
-
import { DB_DRIVERS, DB_ODMS,
|
|
32
|
+
import { DB_DRIVERS, DB_ODMS, DEPLOY_TARGETS, LOGGERS, VALIDATORS } from "../core/types.js";
|
|
30
33
|
import { validateProjectName } from "../core/validate-project-name.js";
|
|
31
34
|
import { resolveUserAgent, runScript } from "../core/package-manager.js";
|
|
32
35
|
|
|
33
|
-
function
|
|
34
|
-
return typeof value === "string" && (
|
|
36
|
+
function isDeployTarget(value: unknown): value is DeployTarget {
|
|
37
|
+
return typeof value === "string" && (DEPLOY_TARGETS as readonly string[]).includes(value);
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
function isDbOdm(value: unknown): value is DbOdm {
|
|
@@ -67,32 +70,81 @@ async function promptInteractiveOptions(args: ParsedCreateArgs): Promise<ParsedC
|
|
|
67
70
|
process.exit(0);
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
const
|
|
71
|
-
args.
|
|
73
|
+
const framework =
|
|
74
|
+
args.framework ??
|
|
72
75
|
(await p.select({
|
|
73
|
-
message: "
|
|
76
|
+
message: "Host framework",
|
|
74
77
|
options: [
|
|
75
|
-
{ value: "
|
|
78
|
+
{ value: "none", label: "None", hint: "Taser Fetch (default)" },
|
|
79
|
+
{ value: "hono", label: "Hono" },
|
|
76
80
|
{ value: "express", label: "Express" },
|
|
77
81
|
{ value: "fastify", label: "Fastify" },
|
|
78
|
-
{ value: "hono", label: "Hono" },
|
|
79
|
-
{ value: "bun", label: "Bun" },
|
|
80
|
-
{ value: "deno", label: "Deno" },
|
|
81
|
-
{ value: "aws-lambda", label: "AWS Lambda" },
|
|
82
|
-
{ value: "cloudflare-workers", label: "Cloudflare Workers" },
|
|
83
|
-
{ value: "netlify", label: "Netlify" },
|
|
84
|
-
{ value: "vercel", label: "Vercel" },
|
|
85
|
-
{ value: "azure-functions", label: "Azure Functions" },
|
|
86
|
-
{ value: "google-cloud-run", label: "Google Cloud Run" },
|
|
87
82
|
],
|
|
88
|
-
initialValue: "
|
|
83
|
+
initialValue: "none",
|
|
89
84
|
}));
|
|
90
85
|
|
|
91
|
-
if (p.isCancel(
|
|
86
|
+
if (p.isCancel(framework)) {
|
|
92
87
|
p.cancel("Scaffold cancelled.");
|
|
93
88
|
process.exit(0);
|
|
94
89
|
}
|
|
95
90
|
|
|
91
|
+
const DEPLOY_LABELS: Record<DeployTarget, string> = {
|
|
92
|
+
none: "None (Standalone Vite)",
|
|
93
|
+
"node-server": "Node.js server",
|
|
94
|
+
"node-cluster": "Node.js cluster",
|
|
95
|
+
bun: "Bun",
|
|
96
|
+
"deno-server": "Deno",
|
|
97
|
+
"deno-deploy": "Deno Deploy",
|
|
98
|
+
"cloudflare-module": "Cloudflare Workers",
|
|
99
|
+
vercel: "Vercel",
|
|
100
|
+
"aws-lambda": "AWS Lambda",
|
|
101
|
+
netlify: "Netlify",
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const DEPLOY_HINTS: Partial<Record<DeployTarget, string>> = {
|
|
105
|
+
none: "standalone Vite, no Nitro",
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const preset =
|
|
109
|
+
args.preset ??
|
|
110
|
+
(await p.select({
|
|
111
|
+
message: "Deployment target",
|
|
112
|
+
// oxlint-disable-next-line oxc/no-map-spread
|
|
113
|
+
options: DEPLOY_TARGETS.map((id) => ({
|
|
114
|
+
value: id,
|
|
115
|
+
label: DEPLOY_LABELS[id],
|
|
116
|
+
...(DEPLOY_HINTS[id] ? { hint: DEPLOY_HINTS[id] } : {}),
|
|
117
|
+
})),
|
|
118
|
+
initialValue: "node-server",
|
|
119
|
+
}));
|
|
120
|
+
|
|
121
|
+
if (p.isCancel(preset) || !isDeployTarget(preset)) {
|
|
122
|
+
p.cancel("Scaffold cancelled.");
|
|
123
|
+
process.exit(0);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let runtime: import("../core/types.js").Runtime | undefined;
|
|
127
|
+
const runtimeOverrides = allowedRuntimeOverrides(preset);
|
|
128
|
+
if (runtimeOverrides.length > 0) {
|
|
129
|
+
const runtimeChoice =
|
|
130
|
+
args.runtime ??
|
|
131
|
+
(await p.select({
|
|
132
|
+
message: "Runtime",
|
|
133
|
+
options: [
|
|
134
|
+
...runtimeOverrides.map((rt) => ({ value: rt, label: rt })),
|
|
135
|
+
{ value: "default", label: `Preset default`, hint: "no override" },
|
|
136
|
+
],
|
|
137
|
+
initialValue: "default",
|
|
138
|
+
}));
|
|
139
|
+
|
|
140
|
+
if (p.isCancel(runtimeChoice)) {
|
|
141
|
+
p.cancel("Scaffold cancelled.");
|
|
142
|
+
process.exit(0);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
runtime = runtimeChoice === "default" ? undefined : runtimeChoice;
|
|
146
|
+
}
|
|
147
|
+
|
|
96
148
|
const dbChoice =
|
|
97
149
|
args.db ??
|
|
98
150
|
(await p.select({
|
|
@@ -182,7 +234,9 @@ async function promptInteractiveOptions(args: ParsedCreateArgs): Promise<ParsedC
|
|
|
182
234
|
|
|
183
235
|
return {
|
|
184
236
|
projectName: String(projectName).trim(),
|
|
185
|
-
|
|
237
|
+
framework,
|
|
238
|
+
preset,
|
|
239
|
+
...(runtime !== undefined ? { runtime } : {}),
|
|
186
240
|
yes: args.yes,
|
|
187
241
|
noInstall: args.noInstall,
|
|
188
242
|
json: args.json,
|
|
@@ -207,7 +261,7 @@ export async function runCreateCommand(
|
|
|
207
261
|
: {
|
|
208
262
|
...args,
|
|
209
263
|
projectName: args.projectName?.trim(),
|
|
210
|
-
|
|
264
|
+
preset: args.preset ?? "node-server",
|
|
211
265
|
};
|
|
212
266
|
|
|
213
267
|
if (!resolved.projectName) {
|
|
@@ -268,8 +322,9 @@ export async function runCreateCommand(
|
|
|
268
322
|
|
|
269
323
|
export function buildParsedArgsFromCli(
|
|
270
324
|
values: {
|
|
271
|
-
|
|
325
|
+
preset?: string;
|
|
272
326
|
framework?: string;
|
|
327
|
+
runtime?: string;
|
|
273
328
|
db?: string;
|
|
274
329
|
logger?: string;
|
|
275
330
|
validator?: string;
|
|
@@ -289,9 +344,20 @@ export function buildParsedArgsFromCli(
|
|
|
289
344
|
args.projectName = positionals[0];
|
|
290
345
|
}
|
|
291
346
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
args.
|
|
347
|
+
if (values.preset) {
|
|
348
|
+
const parsed = parsePresetFlag(values.preset);
|
|
349
|
+
args.preset = parsed.preset;
|
|
350
|
+
if (parsed.warning && !args.json) {
|
|
351
|
+
console.warn(`warning: ${parsed.warning}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (values.framework) {
|
|
356
|
+
args.framework = parseFrameworkFlag(values.framework);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (values.runtime) {
|
|
360
|
+
args.runtime = parseRuntimeFlag(values.runtime);
|
|
295
361
|
}
|
|
296
362
|
|
|
297
363
|
if (values.db) {
|
|
@@ -2,20 +2,32 @@ import {
|
|
|
2
2
|
DB_DRIVERS,
|
|
3
3
|
DB_ODMS,
|
|
4
4
|
DEFAULT_DB_DRIVER,
|
|
5
|
+
FRAMEWORKS,
|
|
5
6
|
LOGGERS,
|
|
6
|
-
|
|
7
|
+
RUNTIMES,
|
|
7
8
|
VALIDATORS,
|
|
8
9
|
type DbDriver,
|
|
9
10
|
type DbOdm,
|
|
11
|
+
type DeployTarget,
|
|
12
|
+
type Framework,
|
|
10
13
|
type LoggerId,
|
|
11
|
-
type
|
|
14
|
+
type Runtime,
|
|
12
15
|
type ScaffoldContext,
|
|
13
16
|
type ValidatorId,
|
|
14
17
|
} from "./types.js";
|
|
18
|
+
import {
|
|
19
|
+
DEFAULT_DEPLOY,
|
|
20
|
+
isCuratedDeploy,
|
|
21
|
+
resolveDeployEntry,
|
|
22
|
+
validateCombination,
|
|
23
|
+
} from "./targets.js";
|
|
15
24
|
|
|
16
25
|
export type ParsedCreateArgs = {
|
|
17
26
|
projectName?: string;
|
|
18
|
-
|
|
27
|
+
framework?: Framework;
|
|
28
|
+
/** Deployment target — a Nitro preset id. */
|
|
29
|
+
preset?: string;
|
|
30
|
+
runtime?: Runtime;
|
|
19
31
|
db?: DbOdm;
|
|
20
32
|
driver?: DbDriver;
|
|
21
33
|
logger?: LoggerId;
|
|
@@ -25,8 +37,12 @@ export type ParsedCreateArgs = {
|
|
|
25
37
|
json: boolean;
|
|
26
38
|
};
|
|
27
39
|
|
|
28
|
-
function
|
|
29
|
-
return (
|
|
40
|
+
function isFramework(value: string): value is Framework {
|
|
41
|
+
return (FRAMEWORKS as readonly string[]).includes(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isRuntime(value: string): value is Runtime {
|
|
45
|
+
return (RUNTIMES as readonly string[]).includes(value);
|
|
30
46
|
}
|
|
31
47
|
|
|
32
48
|
function isDbOdm(value: string): value is DbOdm {
|
|
@@ -64,41 +80,33 @@ export function parseDbFlag(value: string): { db: DbOdm; driver: DbDriver } {
|
|
|
64
80
|
return { db: odm, driver };
|
|
65
81
|
}
|
|
66
82
|
|
|
67
|
-
export function
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const logger = args.logger;
|
|
71
|
-
const validator = args.validator;
|
|
72
|
-
|
|
73
|
-
if (!args.projectName) {
|
|
74
|
-
throw new Error("Project name is required");
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const result: ScaffoldContext = {
|
|
78
|
-
projectName: args.projectName.trim(),
|
|
79
|
-
targetDir: "",
|
|
80
|
-
type,
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
if (db) {
|
|
84
|
-
result.db = db;
|
|
85
|
-
result.driver = args.driver ?? DEFAULT_DB_DRIVER;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (logger) {
|
|
89
|
-
result.logger = logger;
|
|
83
|
+
export function parseFrameworkFlag(value: string): Framework {
|
|
84
|
+
if (!isFramework(value)) {
|
|
85
|
+
throw new Error(`Invalid --framework "${value}". Use ${FRAMEWORKS.join(", ")}.`);
|
|
90
86
|
}
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
91
89
|
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Accepts curated deploy targets verbatim; unknown ids pass through to Nitro.
|
|
92
|
+
* Returns a warning for passthrough ids so callers can surface it.
|
|
93
|
+
*/
|
|
94
|
+
export function parsePresetFlag(value: string): { preset: string; warning?: string } {
|
|
95
|
+
if (!isCuratedDeploy(value)) {
|
|
96
|
+
if (!/^[a-z0-9][a-z0-9-_]*$/i.test(value)) {
|
|
97
|
+
throw new Error(`Invalid --preset "${value}".`);
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
preset: value,
|
|
101
|
+
warning: `"${value}" is not a curated Taser deploy target; passing it through to Nitro as-is.`,
|
|
102
|
+
};
|
|
94
103
|
}
|
|
95
|
-
|
|
96
|
-
return result;
|
|
104
|
+
return { preset: value };
|
|
97
105
|
}
|
|
98
106
|
|
|
99
|
-
export function
|
|
100
|
-
if (!
|
|
101
|
-
throw new Error(`Invalid --
|
|
107
|
+
export function parseRuntimeFlag(value: string): Runtime {
|
|
108
|
+
if (!isRuntime(value)) {
|
|
109
|
+
throw new Error(`Invalid --runtime "${value}". Use node, bun, or deno.`);
|
|
102
110
|
}
|
|
103
111
|
return value;
|
|
104
112
|
}
|
|
@@ -116,3 +124,44 @@ export function parseValidatorFlag(value: string): ValidatorId {
|
|
|
116
124
|
}
|
|
117
125
|
return value;
|
|
118
126
|
}
|
|
127
|
+
|
|
128
|
+
export function resolveScaffoldDefaults(args: ParsedCreateArgs): ScaffoldContext {
|
|
129
|
+
const framework: Framework = args.framework ?? "none";
|
|
130
|
+
const preset = args.preset ?? DEFAULT_DEPLOY;
|
|
131
|
+
|
|
132
|
+
if (!args.projectName) {
|
|
133
|
+
throw new Error("Project name is required");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Throws with a precise reason when runtime × framework × deploy clash.
|
|
137
|
+
const combination = validateCombination(args.runtime, framework, preset);
|
|
138
|
+
if (!combination.ok) {
|
|
139
|
+
throw new Error(combination.reason);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const result: ScaffoldContext = {
|
|
143
|
+
projectName: args.projectName.trim(),
|
|
144
|
+
targetDir: "",
|
|
145
|
+
framework,
|
|
146
|
+
preset: preset as DeployTarget,
|
|
147
|
+
...(args.runtime !== undefined ? { runtime: args.runtime } : {}),
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
if (args.db) {
|
|
151
|
+
result.db = args.db;
|
|
152
|
+
result.driver = args.driver ?? DEFAULT_DB_DRIVER;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (args.logger) {
|
|
156
|
+
result.logger = args.logger;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (args.validator) {
|
|
160
|
+
result.validator = args.validator;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Re-exported for CLI layers that need to warn about passthrough presets. */
|
|
167
|
+
export { resolveDeployEntry };
|
|
@@ -5,9 +5,14 @@ import type { ScaffoldContext } from "./types.js";
|
|
|
5
5
|
|
|
6
6
|
export async function writeProjectConfig(root: string, ctx: ScaffoldContext): Promise<void> {
|
|
7
7
|
const config: Record<string, string> = {
|
|
8
|
-
|
|
8
|
+
framework: ctx.framework ?? "none",
|
|
9
|
+
preset: ctx.preset ?? "node-server",
|
|
9
10
|
};
|
|
10
11
|
|
|
12
|
+
if (ctx.runtime) {
|
|
13
|
+
config.runtime = ctx.runtime;
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
if (ctx.db) {
|
|
12
17
|
config.db = ctx.db;
|
|
13
18
|
if (ctx.driver) {
|