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,83 +1,53 @@
|
|
|
1
1
|
import { resolveAddons } from "../addons/registry.js";
|
|
2
|
-
import
|
|
2
|
+
import { FRAMEWORK_ENTRIES } from "../frameworks/index.js";
|
|
3
|
+
import type { PackageGroups, ScaffoldContext } from "../core/types.js";
|
|
4
|
+
import { resolveDeployEntry } from "./targets.js";
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"npm-run-all2",
|
|
9
|
-
"tsdown",
|
|
10
|
-
"tsx",
|
|
11
|
-
"typescript@^5.9.3",
|
|
12
|
-
"@types/node",
|
|
13
|
-
];
|
|
14
|
-
const scripts: Record<string, string> = {};
|
|
6
|
+
const BASE_DEPENDENCIES = ["@taserjs/router", "dotenv", "srvx"];
|
|
7
|
+
|
|
8
|
+
/** Runtime-agnostic build/dev tooling every scaffolded project needs. */
|
|
9
|
+
const BASE_DEV_DEPENDENCIES = ["@taserjs/router-plugin", "typescript@^5.9.3", "vite@^8.1.5"];
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
break;
|
|
21
|
-
case "fastify":
|
|
22
|
-
dependencies.push("@taserjs/adapter-fastify", "fastify");
|
|
23
|
-
break;
|
|
24
|
-
case "hono":
|
|
25
|
-
dependencies.push("@hono/node-server", "hono");
|
|
26
|
-
break;
|
|
11
|
+
function runtimeDevDeps(ctx: ScaffoldContext): string[] {
|
|
12
|
+
const { entry } = resolveDeployEntry(ctx.preset ?? "node-server");
|
|
13
|
+
const effective = ctx.runtime ?? entry.impliedRuntime;
|
|
14
|
+
switch (effective) {
|
|
27
15
|
case "bun":
|
|
28
|
-
|
|
29
|
-
devDependencies.push("@types/bun");
|
|
30
|
-
scripts["dev:server"] = "bun --watch src/index.ts";
|
|
31
|
-
scripts.start = "bun src/index.ts";
|
|
32
|
-
scripts.serve = "bun dist/index.mjs";
|
|
33
|
-
break;
|
|
16
|
+
return ["@types/bun"];
|
|
34
17
|
case "deno":
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
break;
|
|
39
|
-
case "aws-lambda":
|
|
40
|
-
dependencies.push("hono");
|
|
41
|
-
devDependencies.push("@types/aws-lambda");
|
|
42
|
-
break;
|
|
43
|
-
case "cloudflare-workers": {
|
|
44
|
-
// Cloudflare workers uses wrangler
|
|
45
|
-
const cfDevDeps = devDependencies.filter((d) => d !== "tsx");
|
|
46
|
-
cfDevDeps.push("wrangler", "@cloudflare/workers-types");
|
|
47
|
-
devDependencies.length = 0;
|
|
48
|
-
devDependencies.push(...cfDevDeps);
|
|
49
|
-
scripts["dev:server"] = "wrangler dev";
|
|
50
|
-
scripts.deploy = "wrangler deploy";
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
case "netlify":
|
|
54
|
-
dependencies.push("hono", "@netlify/functions");
|
|
55
|
-
break;
|
|
56
|
-
case "vercel":
|
|
57
|
-
dependencies.push("hono");
|
|
58
|
-
devDependencies.push("@vercel/node");
|
|
59
|
-
break;
|
|
60
|
-
case "azure-functions":
|
|
61
|
-
dependencies.push("hono", "@azure/functions", "@marplex/hono-azurefunc-adapter");
|
|
62
|
-
break;
|
|
63
|
-
case "google-cloud-run":
|
|
18
|
+
return []; // Deno ships its own tooling and types.
|
|
19
|
+
case "workerd":
|
|
20
|
+
return ["@cloudflare/workers-types"];
|
|
64
21
|
case "node":
|
|
65
22
|
default:
|
|
66
|
-
|
|
67
|
-
break;
|
|
23
|
+
return ["@types/node"];
|
|
68
24
|
}
|
|
69
|
-
|
|
70
|
-
return { dependencies, devDependencies, scripts };
|
|
71
25
|
}
|
|
72
26
|
|
|
73
27
|
export function resolvePackages(ctx: ScaffoldContext): PackageGroups {
|
|
74
|
-
const
|
|
75
|
-
const
|
|
28
|
+
const frameworkEntry = FRAMEWORK_ENTRIES[ctx.framework ?? "none"];
|
|
29
|
+
const { entry: deployEntry } = resolveDeployEntry(ctx.preset ?? "node-server");
|
|
30
|
+
|
|
31
|
+
const dependencies = [...BASE_DEPENDENCIES, ...frameworkEntry.deps];
|
|
32
|
+
const nitroDevDeps = ctx.preset === "none" ? [] : ["nitro"];
|
|
33
|
+
const devDependencies = [
|
|
34
|
+
...BASE_DEV_DEPENDENCIES,
|
|
35
|
+
...nitroDevDeps,
|
|
36
|
+
...runtimeDevDeps(ctx),
|
|
37
|
+
...deployEntry.devDeps,
|
|
38
|
+
...frameworkEntry.devDeps,
|
|
39
|
+
];
|
|
76
40
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
41
|
+
const scripts: Record<string, string> = {};
|
|
42
|
+
if (deployEntry.startScript) {
|
|
43
|
+
if (ctx.preset === "none" && ctx.runtime === "bun") {
|
|
44
|
+
scripts.start = "bun dist/serve.mjs";
|
|
45
|
+
} else {
|
|
46
|
+
scripts.start = deployEntry.startScript;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
80
49
|
|
|
50
|
+
const addons = resolveAddons(ctx);
|
|
81
51
|
for (const addon of addons) {
|
|
82
52
|
dependencies.push(...addon.dependencies(ctx));
|
|
83
53
|
devDependencies.push(...addon.devDependencies(ctx));
|
|
@@ -92,10 +62,3 @@ export function resolvePackages(ctx: ScaffoldContext): PackageGroups {
|
|
|
92
62
|
scripts,
|
|
93
63
|
};
|
|
94
64
|
}
|
|
95
|
-
|
|
96
|
-
export function getPackageGroups(
|
|
97
|
-
type: ProjectType,
|
|
98
|
-
): Omit<PackageGroups, "scripts"> & { scripts?: Record<string, string> } {
|
|
99
|
-
const groups = typePackages(type);
|
|
100
|
-
return groups;
|
|
101
|
-
}
|
|
@@ -2,11 +2,13 @@ import { copyFile, mkdir, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
4
|
import { collectBootBindings, resolveAddons } from "../addons/registry.js";
|
|
5
|
-
import {
|
|
5
|
+
import { nitroConfigTemplate, taserTsTemplate, viteConfigTemplate } from "../frameworks/index.js";
|
|
6
6
|
import { installPackages, resolveUserAgent } from "./package-manager.js";
|
|
7
7
|
import { writeProjectConfig } from "./project-config.js";
|
|
8
8
|
import { resolvePackages } from "./resolve-packages.js";
|
|
9
|
-
import
|
|
9
|
+
import { resolveDeployEntry, validateCombination } from "./targets.js";
|
|
10
|
+
import { FRAMEWORK_ENTRIES } from "../frameworks/index.js";
|
|
11
|
+
import type { ScaffoldContext, ScaffoldOptions, ScaffoldResult } from "./types.js";
|
|
10
12
|
import {
|
|
11
13
|
contextTemplate,
|
|
12
14
|
gitignoreTemplate,
|
|
@@ -14,9 +16,7 @@ import {
|
|
|
14
16
|
indexRouteTemplate,
|
|
15
17
|
packageJsonTemplate,
|
|
16
18
|
rootLayoutTemplate,
|
|
17
|
-
starterManifestTemplate,
|
|
18
19
|
tsconfigTemplate,
|
|
19
|
-
tsdownConfigTemplate,
|
|
20
20
|
} from "../templates/base.js";
|
|
21
21
|
|
|
22
22
|
async function write(filePath: string, contents: string): Promise<void> {
|
|
@@ -26,10 +26,23 @@ async function write(filePath: string, contents: string): Promise<void> {
|
|
|
26
26
|
|
|
27
27
|
export async function scaffoldProject(options: ScaffoldOptions): Promise<ScaffoldResult> {
|
|
28
28
|
const root = options.targetDir;
|
|
29
|
-
const
|
|
29
|
+
const preset = options.preset ?? "node-server";
|
|
30
|
+
const framework = options.framework ?? "none";
|
|
31
|
+
|
|
32
|
+
// Guards direct/library callers; the CLI path is validated earlier in
|
|
33
|
+
// resolveScaffoldDefaults. Throws with a precise reason on impossible
|
|
34
|
+
// runtime × framework × deploy combinations.
|
|
35
|
+
const combination = validateCombination(options.runtime, framework, preset);
|
|
36
|
+
if (!combination.ok) {
|
|
37
|
+
throw new Error(combination.reason);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ctx: ScaffoldContext = {
|
|
30
41
|
projectName: options.projectName,
|
|
31
42
|
targetDir: root,
|
|
32
|
-
|
|
43
|
+
framework,
|
|
44
|
+
preset,
|
|
45
|
+
...(options.runtime !== undefined ? { runtime: options.runtime } : {}),
|
|
33
46
|
...(options.db ? { db: options.db, driver: options.driver } : {}),
|
|
34
47
|
...(options.logger ? { logger: options.logger } : {}),
|
|
35
48
|
...(options.validator ? { validator: options.validator } : {}),
|
|
@@ -38,79 +51,42 @@ export async function scaffoldProject(options: ScaffoldOptions): Promise<Scaffol
|
|
|
38
51
|
const addons = resolveAddons(ctx);
|
|
39
52
|
const packages = resolvePackages(ctx);
|
|
40
53
|
const bootBindings = collectBootBindings(ctx);
|
|
54
|
+
const frameworkEntry = FRAMEWORK_ENTRIES[framework];
|
|
55
|
+
const { entry: deployEntry } = resolveDeployEntry(ctx.preset ?? "node-server");
|
|
41
56
|
|
|
42
57
|
await write(
|
|
43
58
|
path.join(root, "package.json"),
|
|
44
59
|
packageJsonTemplate(options.projectName, packages.scripts),
|
|
45
60
|
);
|
|
46
61
|
await write(path.join(root, "tsconfig.json"), tsconfigTemplate());
|
|
47
|
-
await write(path.join(root, "
|
|
62
|
+
await write(path.join(root, "vite.config.ts"), viteConfigTemplate(ctx.preset));
|
|
48
63
|
await write(path.join(root, ".gitignore"), gitignoreTemplate());
|
|
49
64
|
await write(path.join(root, "src/context.ts"), contextTemplate(bootBindings));
|
|
50
|
-
await write(path.join(root, "src/taser.ts"), taserTsTemplate(
|
|
51
|
-
await write(path.join(root, "src/index.ts"), indexTemplate(options.type));
|
|
65
|
+
await write(path.join(root, "src/taser.ts"), taserTsTemplate());
|
|
52
66
|
await write(path.join(root, "src/routes/$.ts"), rootLayoutTemplate());
|
|
53
67
|
await write(path.join(root, "src/routes/index.get.ts"), indexRouteTemplate());
|
|
54
68
|
await write(path.join(root, "src/routes/health.get.ts"), healthRouteTemplate(ctx));
|
|
55
|
-
await write(path.join(root, "src/routeManifest.gen.ts"), starterManifestTemplate());
|
|
56
|
-
|
|
57
|
-
if (options.type === "cloudflare-workers") {
|
|
58
|
-
await write(
|
|
59
|
-
path.join(root, "wrangler.jsonc"),
|
|
60
|
-
JSON.stringify(
|
|
61
|
-
{
|
|
62
|
-
$schema: "node_modules/wrangler/config-schema.json",
|
|
63
|
-
name: options.projectName,
|
|
64
|
-
main: "src/index.ts",
|
|
65
|
-
compatibility_date: "2024-11-01",
|
|
66
|
-
},
|
|
67
|
-
null,
|
|
68
|
-
2,
|
|
69
|
-
) + "\n",
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
{
|
|
78
|
-
rewrites: [{ source: "/(.*)", destination: "/src/index.ts" }],
|
|
79
|
-
},
|
|
80
|
-
null,
|
|
81
|
-
2,
|
|
82
|
-
) + "\n",
|
|
83
|
-
);
|
|
70
|
+
// Nitro's default preset is node-server — only emit an explicit config when
|
|
71
|
+
// the deployment target differs. Do not emit for preset "none".
|
|
72
|
+
if (ctx.preset !== "node-server" && ctx.preset !== "none") {
|
|
73
|
+
await write(path.join(root, "nitro.config.ts"), nitroConfigTemplate(deployEntry.id));
|
|
84
74
|
}
|
|
85
75
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
extensionBundle: {
|
|
101
|
-
id: "Microsoft.Azure.Functions.ExtensionBundle",
|
|
102
|
-
version: "[4.*, 5.0.0)",
|
|
103
|
-
},
|
|
104
|
-
extensions: {
|
|
105
|
-
http: {
|
|
106
|
-
routePrefix: "",
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
null,
|
|
111
|
-
2,
|
|
112
|
-
) + "\n",
|
|
113
|
-
);
|
|
76
|
+
await Promise.all(
|
|
77
|
+
deployEntry.files.map((file) =>
|
|
78
|
+
write(
|
|
79
|
+
path.join(root, file.name),
|
|
80
|
+
typeof file.content === "function"
|
|
81
|
+
? file.content({ projectName: options.projectName })
|
|
82
|
+
: file.content,
|
|
83
|
+
),
|
|
84
|
+
),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const serverEntry = frameworkEntry.serverEntry;
|
|
88
|
+
if (serverEntry) {
|
|
89
|
+
await write(path.join(root, serverEntry.fileName), serverEntry.content);
|
|
114
90
|
}
|
|
115
91
|
|
|
116
92
|
await Promise.all(
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry for the three scaffold dimensions — runtime, framework, deploy —
|
|
3
|
+
* plus the compatibility rules between them.
|
|
4
|
+
*
|
|
5
|
+
* Dependency direction: deploy implies the runtime family; the effective
|
|
6
|
+
* runtime constrains which frameworks can run. `--runtime` is an override
|
|
7
|
+
* available only where it is meaningful (self-hosted node-family targets).
|
|
8
|
+
*/
|
|
9
|
+
import { DEPLOY_TARGETS, type DeployTarget, type Framework, type Runtime } from "./types.js";
|
|
10
|
+
|
|
11
|
+
export type PlatformFile = {
|
|
12
|
+
name: string;
|
|
13
|
+
content: string | ((ctx: { projectName: string }) => string);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type DeployEntry = {
|
|
17
|
+
id: DeployTarget;
|
|
18
|
+
/** Runtime this target builds for ("workerd" = Cloudflare's V8 runtime). */
|
|
19
|
+
impliedRuntime: Runtime | "workerd";
|
|
20
|
+
/** Whether the built output is a long-running server you start locally. */
|
|
21
|
+
selfHosted: boolean;
|
|
22
|
+
/** `start` script value; null for platform-deployed targets. */
|
|
23
|
+
startScript: string | null;
|
|
24
|
+
/** Runtime/platform tooling devDependencies beyond the base set. */
|
|
25
|
+
devDeps: string[];
|
|
26
|
+
files: PlatformFile[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const NODE_START = "node .output/server/index.mjs";
|
|
30
|
+
|
|
31
|
+
export const DEPLOY_ENTRIES: Record<DeployTarget, DeployEntry> = {
|
|
32
|
+
none: {
|
|
33
|
+
id: "none",
|
|
34
|
+
impliedRuntime: "node",
|
|
35
|
+
selfHosted: true,
|
|
36
|
+
startScript: "node dist/serve.mjs",
|
|
37
|
+
devDeps: [],
|
|
38
|
+
files: [],
|
|
39
|
+
},
|
|
40
|
+
"node-server": {
|
|
41
|
+
id: "node-server",
|
|
42
|
+
impliedRuntime: "node",
|
|
43
|
+
selfHosted: true,
|
|
44
|
+
startScript: NODE_START,
|
|
45
|
+
devDeps: [],
|
|
46
|
+
files: [],
|
|
47
|
+
},
|
|
48
|
+
"node-cluster": {
|
|
49
|
+
id: "node-cluster",
|
|
50
|
+
impliedRuntime: "node",
|
|
51
|
+
selfHosted: true,
|
|
52
|
+
startScript: NODE_START,
|
|
53
|
+
devDeps: [],
|
|
54
|
+
files: [],
|
|
55
|
+
},
|
|
56
|
+
bun: {
|
|
57
|
+
id: "bun",
|
|
58
|
+
impliedRuntime: "bun",
|
|
59
|
+
selfHosted: true,
|
|
60
|
+
startScript: "bun .output/server/index.mjs",
|
|
61
|
+
devDeps: ["@types/bun"],
|
|
62
|
+
files: [],
|
|
63
|
+
},
|
|
64
|
+
"deno-server": {
|
|
65
|
+
id: "deno-server",
|
|
66
|
+
impliedRuntime: "deno",
|
|
67
|
+
selfHosted: true,
|
|
68
|
+
startScript: "deno run -A .output/server/index.mjs",
|
|
69
|
+
devDeps: [],
|
|
70
|
+
files: [],
|
|
71
|
+
},
|
|
72
|
+
"deno-deploy": {
|
|
73
|
+
id: "deno-deploy",
|
|
74
|
+
impliedRuntime: "deno",
|
|
75
|
+
selfHosted: false,
|
|
76
|
+
startScript: null,
|
|
77
|
+
devDeps: [],
|
|
78
|
+
files: [],
|
|
79
|
+
},
|
|
80
|
+
"cloudflare-module": {
|
|
81
|
+
id: "cloudflare-module",
|
|
82
|
+
impliedRuntime: "workerd",
|
|
83
|
+
selfHosted: false,
|
|
84
|
+
startScript: null,
|
|
85
|
+
devDeps: ["wrangler", "@cloudflare/workers-types"],
|
|
86
|
+
files: [
|
|
87
|
+
{
|
|
88
|
+
name: "wrangler.jsonc",
|
|
89
|
+
content: ({ projectName }) =>
|
|
90
|
+
`${JSON.stringify(
|
|
91
|
+
{
|
|
92
|
+
$schema: "node_modules/wrangler/config-schema.json",
|
|
93
|
+
name: projectName,
|
|
94
|
+
main: ".output/server/index.mjs",
|
|
95
|
+
compatibility_date: "2024-11-01",
|
|
96
|
+
},
|
|
97
|
+
null,
|
|
98
|
+
2,
|
|
99
|
+
)}\n`,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
vercel: {
|
|
104
|
+
id: "vercel",
|
|
105
|
+
impliedRuntime: "node",
|
|
106
|
+
selfHosted: false,
|
|
107
|
+
startScript: null,
|
|
108
|
+
devDeps: ["@vercel/node"],
|
|
109
|
+
files: [],
|
|
110
|
+
},
|
|
111
|
+
"aws-lambda": {
|
|
112
|
+
id: "aws-lambda",
|
|
113
|
+
impliedRuntime: "node",
|
|
114
|
+
selfHosted: false,
|
|
115
|
+
startScript: null,
|
|
116
|
+
devDeps: ["@types/aws-lambda"],
|
|
117
|
+
files: [],
|
|
118
|
+
},
|
|
119
|
+
netlify: {
|
|
120
|
+
id: "netlify",
|
|
121
|
+
impliedRuntime: "node",
|
|
122
|
+
selfHosted: false,
|
|
123
|
+
startScript: null,
|
|
124
|
+
devDeps: [],
|
|
125
|
+
files: [],
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/** Runtimes a `--runtime` override may select per self-hosted deploy target. */
|
|
130
|
+
const RUNTIME_OVERRIDES: Partial<Record<DeployTarget, readonly Runtime[]>> = {
|
|
131
|
+
none: ["node", "bun"],
|
|
132
|
+
"node-server": ["node", "bun"],
|
|
133
|
+
"node-cluster": ["node", "bun"],
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const DEFAULT_DEPLOY: DeployTarget = "node-server";
|
|
137
|
+
export const DEFAULT_FRAMEWORK: Framework = "none";
|
|
138
|
+
|
|
139
|
+
export function isCuratedDeploy(value: string): value is DeployTarget {
|
|
140
|
+
return (DEPLOY_TARGETS as readonly string[]).includes(value);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Resolves any preset id to its emission entry. Curated ids come from the
|
|
145
|
+
* registry; unknown strings pass through to Nitro verbatim as non-self-hosted
|
|
146
|
+
* node-family targets.
|
|
147
|
+
*/
|
|
148
|
+
export function resolveDeployEntry(id: string): { entry: DeployEntry; curated: boolean } {
|
|
149
|
+
if (isCuratedDeploy(id)) {
|
|
150
|
+
return { entry: DEPLOY_ENTRIES[id], curated: true };
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
entry: {
|
|
154
|
+
// Cast: passthrough ids are valid Nitro PresetNameInputs by contract.
|
|
155
|
+
id: id as DeployTarget,
|
|
156
|
+
impliedRuntime: "node",
|
|
157
|
+
selfHosted: false,
|
|
158
|
+
startScript: null,
|
|
159
|
+
devDeps: [],
|
|
160
|
+
files: [],
|
|
161
|
+
},
|
|
162
|
+
curated: false,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Runtime overrides permitted for a deploy target; empty = no override. */
|
|
167
|
+
export function allowedRuntimeOverrides(id: DeployTarget): readonly Runtime[] {
|
|
168
|
+
return RUNTIME_OVERRIDES[id] ?? [];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export type CombinationError = { ok: false; reason: string };
|
|
172
|
+
export type CombinationOk = { ok: true; runtime: Runtime | "workerd" };
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Validates a runtime × framework × deploy combination.
|
|
176
|
+
*
|
|
177
|
+
* @param runtimeOverride explicit `--runtime` selection, if any
|
|
178
|
+
* @param deploy preset id (curated or passthrough)
|
|
179
|
+
*/
|
|
180
|
+
export function validateCombination(
|
|
181
|
+
runtimeOverride: Runtime | undefined,
|
|
182
|
+
framework: Framework,
|
|
183
|
+
deploy: string,
|
|
184
|
+
): CombinationOk | CombinationError {
|
|
185
|
+
const { entry } = resolveDeployEntry(deploy);
|
|
186
|
+
|
|
187
|
+
if (runtimeOverride !== undefined) {
|
|
188
|
+
const allowed = entry.selfHosted ? allowedRuntimeOverrides(entry.id) : [];
|
|
189
|
+
if (!allowed.includes(runtimeOverride)) {
|
|
190
|
+
return {
|
|
191
|
+
ok: false,
|
|
192
|
+
reason:
|
|
193
|
+
`Runtime "${runtimeOverride}" is not valid for deploy target "${entry.id}". ` +
|
|
194
|
+
(allowed.length > 0
|
|
195
|
+
? `Allowed overrides: ${allowed.join(", ")}.`
|
|
196
|
+
: `It implies runtime "${String(entry.impliedRuntime)}".`),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const effective = runtimeOverride ?? entry.impliedRuntime;
|
|
202
|
+
|
|
203
|
+
if ((framework === "express" || framework === "fastify") && effective !== "node") {
|
|
204
|
+
return {
|
|
205
|
+
ok: false,
|
|
206
|
+
reason: `Framework "${framework}" requires the Node runtime, but deploy target "${entry.id}" implies "${String(effective)}".`,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { ok: true, runtime: effective };
|
|
211
|
+
}
|
package/src/core/types.ts
CHANGED
|
@@ -1,32 +1,41 @@
|
|
|
1
1
|
import type { Agent } from "package-manager-detector";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
/** Host framework layered on top of pure Taser pass-through dispatch. */
|
|
4
|
+
export type Framework = "none" | "hono" | "express" | "fastify";
|
|
5
|
+
|
|
6
|
+
export const FRAMEWORKS: readonly Framework[] = ["none", "hono", "express", "fastify"];
|
|
7
|
+
|
|
8
|
+
export type Runtime = "node" | "bun" | "deno";
|
|
9
|
+
|
|
10
|
+
export const RUNTIMES: readonly Runtime[] = ["node", "bun", "deno"];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Deployment target. Values are Nitro preset ids (see `nitro/config`
|
|
14
|
+
* PresetNameInput) so `--preset` passes straight through to Nitro.
|
|
15
|
+
*/
|
|
16
|
+
export type DeployTarget =
|
|
17
|
+
| "none"
|
|
18
|
+
| "node-server"
|
|
19
|
+
| "node-cluster"
|
|
8
20
|
| "bun"
|
|
9
|
-
| "deno"
|
|
10
|
-
| "
|
|
11
|
-
| "cloudflare-
|
|
12
|
-
| "netlify"
|
|
21
|
+
| "deno-server"
|
|
22
|
+
| "deno-deploy"
|
|
23
|
+
| "cloudflare-module"
|
|
13
24
|
| "vercel"
|
|
14
|
-
| "
|
|
15
|
-
| "
|
|
16
|
-
|
|
17
|
-
export const
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"hono",
|
|
25
|
+
| "aws-lambda"
|
|
26
|
+
| "netlify";
|
|
27
|
+
|
|
28
|
+
export const DEPLOY_TARGETS: readonly DeployTarget[] = [
|
|
29
|
+
"none",
|
|
30
|
+
"node-server",
|
|
31
|
+
"node-cluster",
|
|
22
32
|
"bun",
|
|
23
|
-
"deno",
|
|
33
|
+
"deno-server",
|
|
34
|
+
"deno-deploy",
|
|
35
|
+
"cloudflare-module",
|
|
36
|
+
"vercel",
|
|
24
37
|
"aws-lambda",
|
|
25
|
-
"cloudflare-workers",
|
|
26
38
|
"netlify",
|
|
27
|
-
"vercel",
|
|
28
|
-
"azure-functions",
|
|
29
|
-
"google-cloud-run",
|
|
30
39
|
];
|
|
31
40
|
|
|
32
41
|
export type DbOdm = "drizzle" | "prisma" | "kysely";
|
|
@@ -56,7 +65,12 @@ export type PackageGroups = {
|
|
|
56
65
|
export type ScaffoldContext = {
|
|
57
66
|
projectName: string;
|
|
58
67
|
targetDir: string;
|
|
59
|
-
|
|
68
|
+
/** Host framework; "none" is pure Taser pass-through dispatch. Defaults to "none". */
|
|
69
|
+
framework?: Framework;
|
|
70
|
+
/** Deployment target — a Nitro preset id. Defaults to "node-server". */
|
|
71
|
+
preset?: DeployTarget;
|
|
72
|
+
/** Set only when explicitly overriding the runtime implied by the preset. */
|
|
73
|
+
runtime?: Runtime;
|
|
60
74
|
db?: DbOdm;
|
|
61
75
|
driver?: DbDriver;
|
|
62
76
|
logger?: LoggerId;
|
|
@@ -71,7 +85,9 @@ export type ScaffoldOptions = ScaffoldContext & {
|
|
|
71
85
|
export type ScaffoldResult = ScaffoldContext;
|
|
72
86
|
|
|
73
87
|
export type CapabilitiesCatalog = {
|
|
74
|
-
|
|
88
|
+
frameworks: Framework[];
|
|
89
|
+
deployTargets: DeployTarget[];
|
|
90
|
+
runtimes: Runtime[];
|
|
75
91
|
db: {
|
|
76
92
|
odms: DbOdm[];
|
|
77
93
|
drivers: DbDriver[];
|
|
@@ -36,10 +36,6 @@ export function validateProjectName(name: string, cwd = process.cwd()): string |
|
|
|
36
36
|
return 'Project name cannot contain ".."';
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
if (trimmed.includes("/") || trimmed.includes("\\")) {
|
|
40
|
-
return "Project name cannot contain path separators";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
39
|
if (trimmed.startsWith(".") || trimmed.endsWith(".")) {
|
|
44
40
|
return "Project name cannot start or end with a dot";
|
|
45
41
|
}
|