create-taserjs 0.0.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/LICENSE +15 -0
- package/README.md +22 -0
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/cjs/addons/drizzle/index.cjs +124 -0
- package/dist/cjs/addons/drizzle/index.cjs.map +1 -0
- package/dist/cjs/addons/drizzle/index.d.cts +2 -0
- package/dist/cjs/addons/kysely/index.cjs +96 -0
- package/dist/cjs/addons/kysely/index.cjs.map +1 -0
- package/dist/cjs/addons/kysely/index.d.cts +2 -0
- package/dist/cjs/addons/pino/index.cjs +30 -0
- package/dist/cjs/addons/pino/index.cjs.map +1 -0
- package/dist/cjs/addons/pino/index.d.cts +2 -0
- package/dist/cjs/addons/prisma/index.cjs +66 -0
- package/dist/cjs/addons/prisma/index.cjs.map +1 -0
- package/dist/cjs/addons/prisma/index.d.cts +2 -0
- package/dist/cjs/addons/registry.cjs +52 -0
- package/dist/cjs/addons/registry.cjs.map +1 -0
- package/dist/cjs/addons/registry.d.cts +5 -0
- package/dist/cjs/addons/types.d.cts +16 -0
- package/dist/cjs/addons/winston/index.cjs +34 -0
- package/dist/cjs/addons/winston/index.cjs.map +1 -0
- package/dist/cjs/addons/winston/index.d.cts +2 -0
- package/dist/cjs/cli.cjs +56 -0
- package/dist/cjs/cli.cjs.map +1 -0
- package/dist/cjs/cli.d.cts +2 -0
- package/dist/cjs/commands/create.cjs +243 -0
- package/dist/cjs/commands/create.cjs.map +1 -0
- package/dist/cjs/commands/create.d.cts +11 -0
- package/dist/cjs/core/json-output.cjs +16 -0
- package/dist/cjs/core/json-output.cjs.map +1 -0
- package/dist/cjs/core/json-output.d.cts +4 -0
- package/dist/cjs/core/package-manager.cjs +64 -0
- package/dist/cjs/core/package-manager.cjs.map +1 -0
- package/dist/cjs/core/package-manager.d.cts +7 -0
- package/dist/cjs/core/parse-options.cjs +59 -0
- package/dist/cjs/core/parse-options.cjs.map +1 -0
- package/dist/cjs/core/parse-options.d.cts +18 -0
- package/dist/cjs/core/project-config.cjs +18 -0
- package/dist/cjs/core/project-config.cjs.map +1 -0
- package/dist/cjs/core/project-config.d.cts +2 -0
- package/dist/cjs/core/resolve-packages.cjs +52 -0
- package/dist/cjs/core/resolve-packages.cjs.map +1 -0
- package/dist/cjs/core/resolve-packages.d.cts +5 -0
- package/dist/cjs/core/scaffold-engine.cjs +73 -0
- package/dist/cjs/core/scaffold-engine.cjs.map +1 -0
- package/dist/cjs/core/scaffold-engine.d.cts +2 -0
- package/dist/cjs/core/types.cjs +27 -0
- package/dist/cjs/core/types.cjs.map +1 -0
- package/dist/cjs/core/types.d.cts +37 -0
- package/dist/cjs/core/validate-project-name.cjs +45 -0
- package/dist/cjs/core/validate-project-name.cjs.map +1 -0
- package/dist/cjs/core/validate-project-name.d.cts +1 -0
- package/dist/cjs/frameworks/index.cjs +106 -0
- package/dist/cjs/frameworks/index.cjs.map +1 -0
- package/dist/cjs/frameworks/index.d.cts +3 -0
- package/dist/cjs/scaffold.d.cts +3 -0
- package/dist/cjs/templates/base.cjs +186 -0
- package/dist/cjs/templates/base.cjs.map +1 -0
- package/dist/cjs/templates/base.d.cts +12 -0
- package/dist/cjs/types.d.cts +1 -0
- package/dist/esm/addons/drizzle/index.d.ts +2 -0
- package/dist/esm/addons/drizzle/index.js +124 -0
- package/dist/esm/addons/drizzle/index.js.map +1 -0
- package/dist/esm/addons/kysely/index.d.ts +2 -0
- package/dist/esm/addons/kysely/index.js +96 -0
- package/dist/esm/addons/kysely/index.js.map +1 -0
- package/dist/esm/addons/pino/index.d.ts +2 -0
- package/dist/esm/addons/pino/index.js +30 -0
- package/dist/esm/addons/pino/index.js.map +1 -0
- package/dist/esm/addons/prisma/index.d.ts +2 -0
- package/dist/esm/addons/prisma/index.js +66 -0
- package/dist/esm/addons/prisma/index.js.map +1 -0
- package/dist/esm/addons/registry.d.ts +5 -0
- package/dist/esm/addons/registry.js +50 -0
- package/dist/esm/addons/registry.js.map +1 -0
- package/dist/esm/addons/types.d.ts +16 -0
- package/dist/esm/addons/winston/index.d.ts +2 -0
- package/dist/esm/addons/winston/index.js +34 -0
- package/dist/esm/addons/winston/index.js.map +1 -0
- package/dist/esm/cli.d.ts +2 -0
- package/dist/esm/cli.js +53 -0
- package/dist/esm/cli.js.map +1 -0
- package/dist/esm/commands/create.d.ts +11 -0
- package/dist/esm/commands/create.js +238 -0
- package/dist/esm/commands/create.js.map +1 -0
- package/dist/esm/core/json-output.d.ts +4 -0
- package/dist/esm/core/json-output.js +14 -0
- package/dist/esm/core/json-output.js.map +1 -0
- package/dist/esm/core/package-manager.d.ts +7 -0
- package/dist/esm/core/package-manager.js +62 -0
- package/dist/esm/core/package-manager.js.map +1 -0
- package/dist/esm/core/parse-options.d.ts +18 -0
- package/dist/esm/core/parse-options.js +56 -0
- package/dist/esm/core/parse-options.js.map +1 -0
- package/dist/esm/core/project-config.d.ts +2 -0
- package/dist/esm/core/project-config.js +16 -0
- package/dist/esm/core/project-config.js.map +1 -0
- package/dist/esm/core/resolve-packages.d.ts +5 -0
- package/dist/esm/core/resolve-packages.js +52 -0
- package/dist/esm/core/resolve-packages.js.map +1 -0
- package/dist/esm/core/scaffold-engine.d.ts +2 -0
- package/dist/esm/core/scaffold-engine.js +71 -0
- package/dist/esm/core/scaffold-engine.js.map +1 -0
- package/dist/esm/core/types.d.ts +37 -0
- package/dist/esm/core/types.js +23 -0
- package/dist/esm/core/types.js.map +1 -0
- package/dist/esm/core/validate-project-name.d.ts +1 -0
- package/dist/esm/core/validate-project-name.js +43 -0
- package/dist/esm/core/validate-project-name.js.map +1 -0
- package/dist/esm/frameworks/index.d.ts +3 -0
- package/dist/esm/frameworks/index.js +105 -0
- package/dist/esm/frameworks/index.js.map +1 -0
- package/dist/esm/scaffold.d.ts +3 -0
- package/dist/esm/templates/base.d.ts +12 -0
- package/dist/esm/templates/base.js +178 -0
- package/dist/esm/templates/base.js.map +1 -0
- package/dist/esm/types.d.ts +1 -0
- package/package.json +45 -0
- package/src/addons/drizzle/index.ts +145 -0
- package/src/addons/kysely/index.ts +111 -0
- package/src/addons/pino/index.ts +30 -0
- package/src/addons/prisma/index.ts +81 -0
- package/src/addons/registry.ts +73 -0
- package/src/addons/types.ts +19 -0
- package/src/addons/winston/index.ts +34 -0
- package/src/cli.ts +57 -0
- package/src/commands/create.ts +253 -0
- package/src/core/json-output.ts +13 -0
- package/src/core/package-manager.ts +68 -0
- package/src/core/parse-options.ts +97 -0
- package/src/core/project-config.ts +27 -0
- package/src/core/resolve-packages.ts +62 -0
- package/src/core/scaffold-engine.ts +82 -0
- package/src/core/types.ts +51 -0
- package/src/core/validate-project-name.ts +59 -0
- package/src/frameworks/index.ts +111 -0
- package/src/scaffold.ts +3 -0
- package/src/templates/base.ts +209 -0
- package/src/types.ts +9 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/frameworks/index.ts"],"sourcesContent":["import type { Framework } from '../core/types.js'\n\nexport function indexTemplate(framework: Framework): string {\n switch (framework) {\n case 'express':\n return `import express from 'express'\nimport { createExpressHandler } from '@taserjs/adapter-express'\n\nimport { routeManifest } from '#src/routeManifest.gen.js'\nimport { t } from '#src/taser.js'\n\nconst router = t.create(routeManifest)\n\nconst taser = createExpressHandler(router)\nconst app = express()\ntaser.mount('/api{/*splat}', app)\n\nconst port = Number(process.env.PORT ?? 3000)\napp.listen(port, () => {\n console.log(\\`Express listening on http://localhost:\\${port}\\`)\n})\n`\n case 'hono':\n return `import { serve } from '@hono/node-server'\nimport { Hono } from 'hono'\n\nimport { routeManifest } from '#src/routeManifest.gen.js'\nimport { t } from '#src/taser.js'\n\nconst router = t.create(routeManifest)\n\nconst app = new Hono()\nconst api = router.base('/api')\napp.all('/api/*', c => api.native(c).fetch(c.req.raw))\n\nconst port = Number(process.env.PORT ?? 3000)\nserve({ fetch: app.fetch, port }, () => {\n console.log(\\`Hono listening on http://localhost:\\${port}\\`)\n})\n`\n case 'fastify':\n return `import Fastify from 'fastify'\nimport { createFastifyHandler } from '@taserjs/adapter-fastify'\n\nimport { routeManifest } from '#src/routeManifest.gen.js'\nimport { t } from '#src/taser.js'\n\nconst router = t.create(routeManifest)\n\nconst taser = createFastifyHandler(router)\nconst app = Fastify()\ntaser.mount('/api/*', app)\n\nconst port = Number(process.env.PORT ?? 3000)\nawait app.listen({ port })\nconsole.log(\\`Fastify listening on http://localhost:\\${port}\\`)\n`\n case 'node':\n default:\n return `import { createServer } from 'node:http'\nimport { createNodeHandler } from '@taserjs/adapter-node'\n\nimport { routeManifest } from '#src/routeManifest.gen.js'\nimport { t } from '#src/taser.js'\n\nconst router = t.create(routeManifest)\n\nconst taser = createNodeHandler(router)\nconst app = createServer()\ntaser.mount('/api/*', app)\n\nconst port = Number(process.env.PORT ?? 3000)\napp.listen(port, () => {\n console.log(\\`Node listening on http://localhost:\\${port}\\`)\n})\n`\n }\n}\n\nexport function taserTsTemplate(framework: Framework = 'node'): string {\n if (framework === 'hono') {\n return `import type { Context } from 'hono'\nimport { createTaserApp, type InferAppContext } from '@taserjs/router'\n\nimport { context } from '#src/context.js'\n\ndeclare module '@taserjs/router' {\n interface RouterRegister {\n NativeContext: Context\n }\n}\n\nexport const t = createTaserApp({\n response: { validate: true },\n}).context(context)\n\nexport type AppContext = InferAppContext<typeof context>\n`\n }\n\n return `import { createTaserApp, type InferAppContext } from '@taserjs/router'\n\nimport { context } from '#src/context.js'\n\nexport const t = createTaserApp({\n response: { validate: true },\n}).context(context)\n\nexport type AppContext = InferAppContext<typeof context>\n`\n}\n"],"mappings":";AAEA,SAAgB,cAAc,WAA8B;CAC1D,QAAQ,WAAR;EACE,KAAK,WACH,OAAO;;;;;;;;;;;;;;;;;EAiBT,KAAK,QACH,OAAO;;;;;;;;;;;;;;;;;EAiBT,KAAK,WACH,OAAO;;;;;;;;;;;;;;;;EAiBT,SACE,OAAO;;;;;;;;;;;;;;;;;CAiBX;AACF;AAEA,SAAgB,gBAAgB,YAAuB,QAAgB;CACrE,IAAI,cAAc,QAChB,OAAO;;;;;;;;;;;;;;;;;CAmBT,OAAO;;;;;;;;;;AAUT"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BootBinding } from '../addons/types.js';
|
|
2
|
+
import { ScaffoldContext } from '../core/types.js';
|
|
3
|
+
export declare function packageJsonTemplate(projectName: string, scripts?: Record<string, string>): string;
|
|
4
|
+
export declare function tsdownConfigTemplate(): string;
|
|
5
|
+
export declare function tsconfigTemplate(): string;
|
|
6
|
+
export declare function gitignoreTemplate(): string;
|
|
7
|
+
export declare function contextTemplate(bindings: BootBinding[]): string;
|
|
8
|
+
export declare function rootLayoutTemplate(): string;
|
|
9
|
+
export declare function indexRouteTemplate(): string;
|
|
10
|
+
export declare function healthRouteTemplate(ctx: ScaffoldContext): string;
|
|
11
|
+
/** Minimal placeholder until `taser generate` runs. */
|
|
12
|
+
export declare function starterManifestTemplate(): string;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
//#region src/templates/base.ts
|
|
2
|
+
function packageJsonTemplate(projectName, scripts = {}) {
|
|
3
|
+
const pkg = {
|
|
4
|
+
name: projectName,
|
|
5
|
+
version: "1.0.0",
|
|
6
|
+
private: true,
|
|
7
|
+
type: "module",
|
|
8
|
+
imports: { "#src/*": "./src/*" },
|
|
9
|
+
scripts: {
|
|
10
|
+
"dev": "run-p dev:server dev:taser",
|
|
11
|
+
"dev:server": "tsx watch src/index.ts",
|
|
12
|
+
"dev:taser": "taser watch",
|
|
13
|
+
"start": "tsx src/index.ts",
|
|
14
|
+
"generate": "taser generate",
|
|
15
|
+
"build": "taser generate && tsdown build",
|
|
16
|
+
"serve": "node dist/index.js",
|
|
17
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
18
|
+
...scripts
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
return `${JSON.stringify(pkg, null, 2)}\n`;
|
|
22
|
+
}
|
|
23
|
+
function tsdownConfigTemplate() {
|
|
24
|
+
return `import { defineConfig } from 'tsdown'
|
|
25
|
+
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
entry: ['./src/index.ts'],
|
|
28
|
+
format: 'esm',
|
|
29
|
+
platform: 'node',
|
|
30
|
+
target: 'node20',
|
|
31
|
+
outDir: 'dist',
|
|
32
|
+
clean: true,
|
|
33
|
+
sourcemap: true,
|
|
34
|
+
})
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
37
|
+
function tsconfigTemplate() {
|
|
38
|
+
return `${JSON.stringify({
|
|
39
|
+
compilerOptions: {
|
|
40
|
+
target: "ES2022",
|
|
41
|
+
lib: ["ES2022", "DOM"],
|
|
42
|
+
module: "ESNext",
|
|
43
|
+
moduleResolution: "bundler",
|
|
44
|
+
paths: { "#src/*": ["./src/*"] },
|
|
45
|
+
strict: true,
|
|
46
|
+
skipLibCheck: true,
|
|
47
|
+
verbatimModuleSyntax: true,
|
|
48
|
+
isolatedModules: true,
|
|
49
|
+
noEmit: true,
|
|
50
|
+
types: ["node"]
|
|
51
|
+
},
|
|
52
|
+
include: ["src"]
|
|
53
|
+
}, null, 2)}\n`;
|
|
54
|
+
}
|
|
55
|
+
function gitignoreTemplate() {
|
|
56
|
+
return `node_modules
|
|
57
|
+
dist
|
|
58
|
+
.DS_Store
|
|
59
|
+
*.log
|
|
60
|
+
.env
|
|
61
|
+
local.db
|
|
62
|
+
drizzle
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
function contextTemplate(bindings) {
|
|
66
|
+
const imports = bindings.map((binding) => `import { ${binding.factoryName} } from '${binding.importPath}'`);
|
|
67
|
+
const bootBody = bindings.length > 0 ? bindings.map((binding) => ` ${binding.key}: ${binding.factoryName}(),`).join("\n") : "";
|
|
68
|
+
const bootBlock = bindings.length > 0 ? ` boot: () => ({\n${bootBody}\n }),` : "";
|
|
69
|
+
return `${imports.length > 0 ? `${imports.join("\n")}\n\n` : ""}import { createContext } from '@taserjs/router'
|
|
70
|
+
|
|
71
|
+
export const context = createContext({
|
|
72
|
+
${bootBlock}
|
|
73
|
+
request: () => ({
|
|
74
|
+
requestId: crypto.randomUUID(),
|
|
75
|
+
}),
|
|
76
|
+
})
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
function rootLayoutTemplate() {
|
|
80
|
+
return `import { bodyLimit } from '@taserjs/router/body-limit'
|
|
81
|
+
import { secureHeaders } from '@taserjs/router/secure-headers'
|
|
82
|
+
|
|
83
|
+
import { t } from '#src/taser.js'
|
|
84
|
+
|
|
85
|
+
export const Middleware = t.middleware('/$')
|
|
86
|
+
.use(secureHeaders())
|
|
87
|
+
.use(bodyLimit({ maxSize: 1_000_000 }))
|
|
88
|
+
`;
|
|
89
|
+
}
|
|
90
|
+
function indexRouteTemplate() {
|
|
91
|
+
return `import { t } from '#src/taser.js'
|
|
92
|
+
|
|
93
|
+
export const Route = t.get('/').handler((ctx) => {
|
|
94
|
+
return ctx.reply.json({ message: 'Welcome to Taser' })
|
|
95
|
+
})
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
98
|
+
function healthRouteTemplate(ctx) {
|
|
99
|
+
const lines = [];
|
|
100
|
+
if (ctx.logger) lines.push(" ctx.logger.info('health check')");
|
|
101
|
+
if (ctx.db) lines.push(" // ctx.db is available from context boot");
|
|
102
|
+
return `import { t } from '#src/taser.js'
|
|
103
|
+
|
|
104
|
+
export const Route = t.get('/health').handler((ctx) => {
|
|
105
|
+
${lines.length > 0 ? `${lines.join("\n")}\n` : ""} return ctx.reply.json({ ok: true })
|
|
106
|
+
})
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
109
|
+
/** Minimal placeholder until `taser generate` runs. */
|
|
110
|
+
function starterManifestTemplate() {
|
|
111
|
+
return `/* eslint-disable */
|
|
112
|
+
// Run \`pnpm generate\` (taser generate) to replace this file.
|
|
113
|
+
import { Middleware as RootSplatLayoutImport } from './routes/$.js'
|
|
114
|
+
import { Route as RootIndexGetRouteImport } from './routes/index.get.js'
|
|
115
|
+
import { Route as HealthGetRouteImport } from './routes/health.get.js'
|
|
116
|
+
|
|
117
|
+
export const routeManifest = {
|
|
118
|
+
layouts: {
|
|
119
|
+
'/$': {
|
|
120
|
+
middlewares: RootSplatLayoutImport,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
routes: {
|
|
124
|
+
'/': {
|
|
125
|
+
GET: {
|
|
126
|
+
layoutChain: ['/$'],
|
|
127
|
+
route: RootIndexGetRouteImport,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
'/health': {
|
|
131
|
+
GET: {
|
|
132
|
+
layoutChain: ['/$'],
|
|
133
|
+
route: HealthGetRouteImport,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
} as const
|
|
138
|
+
|
|
139
|
+
export type RoutePathGen = '/' | '/health'
|
|
140
|
+
export type LayoutIdGen = '/$'
|
|
141
|
+
export type LayoutTreeGen = {
|
|
142
|
+
'/$': {
|
|
143
|
+
parent: null
|
|
144
|
+
middlewares: typeof RootSplatLayoutImport
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export type RouteByPathMethodGen = {
|
|
148
|
+
'/': {
|
|
149
|
+
GET: {
|
|
150
|
+
parent: '/$'
|
|
151
|
+
layoutChain: ['/$']
|
|
152
|
+
route: typeof RootIndexGetRouteImport
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
'/health': {
|
|
156
|
+
GET: {
|
|
157
|
+
parent: '/$'
|
|
158
|
+
layoutChain: ['/$']
|
|
159
|
+
route: typeof HealthGetRouteImport
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
export type RouteManifest = typeof routeManifest
|
|
164
|
+
|
|
165
|
+
declare module '@taserjs/router' {
|
|
166
|
+
interface RouterRegister {
|
|
167
|
+
RoutePath: RoutePathGen
|
|
168
|
+
LayoutId: LayoutIdGen
|
|
169
|
+
LayoutTree: LayoutTreeGen
|
|
170
|
+
RouteByPathMethod: RouteByPathMethodGen
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
export { contextTemplate, gitignoreTemplate, healthRouteTemplate, indexRouteTemplate, packageJsonTemplate, rootLayoutTemplate, starterManifestTemplate, tsconfigTemplate, tsdownConfigTemplate };
|
|
177
|
+
|
|
178
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +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(projectName: string, scripts: Record<string, string> = {}): string {\n const pkg = {\n name: projectName,\n version: '1.0.0',\n private: true,\n type: 'module',\n imports: {\n '#src/*': './src/*',\n },\n scripts: {\n 'dev': 'run-p dev:server dev:taser',\n 'dev:server': 'tsx watch src/index.ts',\n 'dev:taser': 'taser watch',\n 'start': 'tsx src/index.ts',\n 'generate': 'taser generate',\n 'build': 'taser generate && tsdown build',\n 'serve': 'node dist/index.js',\n 'typecheck': 'tsc --noEmit -p tsconfig.json',\n ...scripts,\n },\n }\n\n return `${JSON.stringify(pkg, null, 2)}\\n`\n}\n\nexport function tsdownConfigTemplate(): string {\n return `import { defineConfig } from 'tsdown'\n\nexport default defineConfig({\n entry: ['./src/index.ts'],\n format: 'esm',\n platform: 'node',\n target: 'node20',\n outDir: 'dist',\n clean: true,\n sourcemap: true,\n})\n`\n}\n\nexport function tsconfigTemplate(): string {\n return `${JSON.stringify({\n compilerOptions: {\n target: 'ES2022',\n lib: ['ES2022', 'DOM'],\n module: 'ESNext',\n moduleResolution: 'bundler',\n paths: {\n '#src/*': ['./src/*'],\n },\n strict: true,\n skipLibCheck: true,\n verbatimModuleSyntax: true,\n isolatedModules: true,\n noEmit: true,\n types: ['node'],\n },\n include: ['src'],\n }, null, 2)}\\n`\n}\n\nexport function gitignoreTemplate(): string {\n return `node_modules\ndist\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 = bindings.length > 0\n ? bindings.map(binding => ` ${binding.key}: ${binding.factoryName}(),`).join('\\n')\n : ''\n\n const bootBlock = bindings.length > 0\n ? ` boot: () => ({\\n${bootBody}\\n }),`\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 { bodyLimit } from '@taserjs/router/body-limit'\nimport { secureHeaders } from '@taserjs/router/secure-headers'\n\nimport { t } from '#src/taser.js'\n\nexport const Middleware = t.middleware('/$')\n .use(secureHeaders())\n .use(bodyLimit({ maxSize: 1_000_000 }))\n`\n}\n\nexport function indexRouteTemplate(): string {\n return `import { t } from '#src/taser.js'\n\nexport const Route = t.get('/').handler((ctx) => {\n return ctx.reply.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\n return `import { t } from '#src/taser.js'\n\nexport const Route = t.get('/health').handler((ctx) => {\n${body} return ctx.reply.json({ ok: true })\n})\n`\n}\n\n/** Minimal placeholder until `taser generate` runs. */\nexport function starterManifestTemplate(): string {\n return `/* eslint-disable */\n// Run \\`pnpm generate\\` (taser generate) to replace this file.\nimport { Middleware as RootSplatLayoutImport } from './routes/$.js'\nimport { Route as RootIndexGetRouteImport } from './routes/index.get.js'\nimport { Route as HealthGetRouteImport } from './routes/health.get.js'\n\nexport const routeManifest = {\n layouts: {\n '/$': {\n middlewares: RootSplatLayoutImport,\n },\n },\n routes: {\n '/': {\n GET: {\n layoutChain: ['/$'],\n route: RootIndexGetRouteImport,\n },\n },\n '/health': {\n GET: {\n layoutChain: ['/$'],\n route: HealthGetRouteImport,\n },\n },\n },\n} as const\n\nexport type RoutePathGen = '/' | '/health'\nexport type LayoutIdGen = '/$'\nexport type LayoutTreeGen = {\n '/$': {\n parent: null\n middlewares: typeof RootSplatLayoutImport\n }\n}\nexport type RouteByPathMethodGen = {\n '/': {\n GET: {\n parent: '/$'\n layoutChain: ['/$']\n route: typeof RootIndexGetRouteImport\n }\n }\n '/health': {\n GET: {\n parent: '/$'\n layoutChain: ['/$']\n route: typeof HealthGetRouteImport\n }\n }\n}\nexport type RouteManifest = typeof routeManifest\n\ndeclare module '@taserjs/router' {\n interface RouterRegister {\n RoutePath: RoutePathGen\n LayoutId: LayoutIdGen\n LayoutTree: LayoutTreeGen\n RouteByPathMethod: RouteByPathMethodGen\n }\n}\n`\n}\n"],"mappings":";AAGA,SAAgB,oBAAoB,aAAqB,UAAkC,CAAC,GAAW;CACrG,MAAM,MAAM;EACV,MAAM;EACN,SAAS;EACT,SAAS;EACT,MAAM;EACN,SAAS,EACP,UAAU,UACZ;EACA,SAAS;GACP,OAAO;GACP,cAAc;GACd,aAAa;GACb,SAAS;GACT,YAAY;GACZ,SAAS;GACT,SAAS;GACT,aAAa;GACb,GAAG;EACL;CACF;CAEA,OAAO,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE;AACzC;AAEA,SAAgB,uBAA+B;CAC7C,OAAO;;;;;;;;;;;;AAYT;AAEA,SAAgB,mBAA2B;CACzC,OAAO,GAAG,KAAK,UAAU;EACvB,iBAAiB;GACf,QAAQ;GACR,KAAK,CAAC,UAAU,KAAK;GACrB,QAAQ;GACR,kBAAkB;GAClB,OAAO,EACL,UAAU,CAAC,SAAS,EACtB;GACA,QAAQ;GACR,cAAc;GACd,sBAAsB;GACtB,iBAAiB;GACjB,QAAQ;GACR,OAAO,CAAC,MAAM;EAChB;EACA,SAAS,CAAC,KAAK;CACjB,GAAG,MAAM,CAAC,EAAE;AACd;AAEA,SAAgB,oBAA4B;CAC1C,OAAO;;;;;;;;AAQT;AAEA,SAAgB,gBAAgB,UAAiC;CAC/D,MAAM,UAAU,SAAS,KACvB,YAAW,YAAY,QAAQ,YAAY,WAAW,QAAQ,WAAW,EAC3E;CAEA,MAAM,WAAW,SAAS,SAAS,IAC/B,SAAS,KAAI,YAAW,OAAO,QAAQ,IAAI,IAAI,QAAQ,YAAY,IAAI,CAAC,CAAC,KAAK,IAAI,IAClF;CAEJ,MAAM,YAAY,SAAS,SAAS,IAChC,qBAAqB,SAAS,WAC9B;CAIJ,OAAO,GAFa,QAAQ,SAAS,IAAI,GAAG,QAAQ,KAAK,IAAI,EAAE,QAAQ,GAEjD;;;EAGtB,UAAU;;;;;;AAMZ;AAEA,SAAgB,qBAA6B;CAC3C,OAAO;;;;;;;;;AAST;AAEA,SAAgB,qBAA6B;CAC3C,OAAO;;;;;;AAMT;AAEA,SAAgB,oBAAoB,KAA8B;CAChE,MAAM,QAAkB,CAAC;CAEzB,IAAI,IAAI,QACN,MAAM,KAAK,mCAAqC;CAGlD,IAAI,IAAI,IACN,MAAM,KAAK,4CAA4C;CAKzD,OAAO;;;EAFM,MAAM,SAAS,IAAI,GAAG,MAAM,KAAK,IAAI,EAAE,MAAM,GAKrD;;;AAGP;;AAGA,SAAgB,0BAAkC;CAChD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { Framework, DbOdm, DbDriver, LoggerId, ScaffoldOptions, ScaffoldResult, PackageGroups, } from './core/types.js';
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-taserjs",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Scaffold a new Taser application",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-taserjs": "./dist/esm/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"src"
|
|
13
|
+
],
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@clack/prompts": "^0.11.0",
|
|
16
|
+
"package-manager-detector": "^1.8.0",
|
|
17
|
+
"picocolors": "^1.1.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@tanstack/vite-config": "^0.6.0",
|
|
21
|
+
"publint": "^0.3.22",
|
|
22
|
+
"typescript": "^5.9.3",
|
|
23
|
+
"vite": "^8.1.5",
|
|
24
|
+
"vitest": "^4.1.10"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20.19"
|
|
28
|
+
},
|
|
29
|
+
"license": "ISC",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/taserjs/taserjs.git",
|
|
33
|
+
"directory": "packages/create-taserjs"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public",
|
|
37
|
+
"registry": "https://registry.npmjs.org"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"clean": "rimraf dist",
|
|
41
|
+
"build": "vite build && publint --strict",
|
|
42
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
43
|
+
"test": "vitest run"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { AddonDefinition } from '../types.js'
|
|
2
|
+
import type { DbDriver } from '../../core/types.js'
|
|
3
|
+
|
|
4
|
+
function schemaSource(driver: DbDriver): string {
|
|
5
|
+
switch (driver) {
|
|
6
|
+
case 'postgres':
|
|
7
|
+
return `import { pgTable, serial, text } from 'drizzle-orm/pg-core'
|
|
8
|
+
|
|
9
|
+
export const users = pgTable('users', {
|
|
10
|
+
id: serial('id').primaryKey(),
|
|
11
|
+
email: text('email').notNull(),
|
|
12
|
+
})
|
|
13
|
+
`
|
|
14
|
+
case 'mysql':
|
|
15
|
+
return `import { mysqlTable, serial, varchar } from 'drizzle-orm/mysql-core'
|
|
16
|
+
|
|
17
|
+
export const users = mysqlTable('users', {
|
|
18
|
+
id: serial('id').primaryKey(),
|
|
19
|
+
email: varchar('email', { length: 255 }).notNull(),
|
|
20
|
+
})
|
|
21
|
+
`
|
|
22
|
+
case 'sqlite':
|
|
23
|
+
default:
|
|
24
|
+
return `import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core'
|
|
25
|
+
|
|
26
|
+
export const users = sqliteTable('users', {
|
|
27
|
+
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
28
|
+
email: text('email').notNull(),
|
|
29
|
+
})
|
|
30
|
+
`
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function dbIndexSource(driver: DbDriver): string {
|
|
35
|
+
switch (driver) {
|
|
36
|
+
case 'postgres':
|
|
37
|
+
return `import { drizzle } from 'drizzle-orm/node-postgres'
|
|
38
|
+
import pg from 'pg'
|
|
39
|
+
|
|
40
|
+
import * as schema from './schema.js'
|
|
41
|
+
|
|
42
|
+
export function createDb() {
|
|
43
|
+
const pool = new pg.Pool({
|
|
44
|
+
connectionString: process.env.DATABASE_URL,
|
|
45
|
+
})
|
|
46
|
+
return drizzle(pool, { schema })
|
|
47
|
+
}
|
|
48
|
+
`
|
|
49
|
+
case 'mysql':
|
|
50
|
+
return `import { drizzle } from 'drizzle-orm/mysql2'
|
|
51
|
+
import mysql from 'mysql2/promise'
|
|
52
|
+
|
|
53
|
+
import * as schema from './schema.js'
|
|
54
|
+
|
|
55
|
+
export function createDb() {
|
|
56
|
+
const pool = mysql.createPool(process.env.DATABASE_URL ?? '')
|
|
57
|
+
return drizzle(pool, { schema, mode: 'default' })
|
|
58
|
+
}
|
|
59
|
+
`
|
|
60
|
+
case 'sqlite':
|
|
61
|
+
default:
|
|
62
|
+
return `import Database from 'better-sqlite3'
|
|
63
|
+
import { drizzle } from 'drizzle-orm/better-sqlite3'
|
|
64
|
+
|
|
65
|
+
import * as schema from './schema.js'
|
|
66
|
+
|
|
67
|
+
export function createDb() {
|
|
68
|
+
const filePath = (process.env.DATABASE_URL ?? 'file:./local.db').replace(/^file:/, '')
|
|
69
|
+
const client = new Database(filePath)
|
|
70
|
+
return drizzle(client, { schema })
|
|
71
|
+
}
|
|
72
|
+
`
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function drizzleConfigSource(driver: DbDriver): string {
|
|
77
|
+
const dialect = driver === 'postgres' ? 'postgresql' : driver === 'mysql' ? 'mysql' : 'sqlite'
|
|
78
|
+
return `import { defineConfig } from 'drizzle-kit'
|
|
79
|
+
|
|
80
|
+
export default defineConfig({
|
|
81
|
+
schema: './src/db/schema.ts',
|
|
82
|
+
out: './drizzle',
|
|
83
|
+
dialect: '${dialect}',
|
|
84
|
+
dbCredentials: {
|
|
85
|
+
url: process.env.DATABASE_URL ?? '',
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
`
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function envExample(driver: DbDriver): string {
|
|
92
|
+
switch (driver) {
|
|
93
|
+
case 'postgres':
|
|
94
|
+
return 'DATABASE_URL=postgresql://user:password@localhost:5432/mydb\n'
|
|
95
|
+
case 'mysql':
|
|
96
|
+
return 'DATABASE_URL=mysql://user:password@localhost:3306/mydb\n'
|
|
97
|
+
case 'sqlite':
|
|
98
|
+
default:
|
|
99
|
+
return 'DATABASE_URL=file:./local.db\n'
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function driverPackages(driver: DbDriver): string[] {
|
|
104
|
+
switch (driver) {
|
|
105
|
+
case 'postgres':
|
|
106
|
+
return ['pg']
|
|
107
|
+
case 'mysql':
|
|
108
|
+
return ['mysql2']
|
|
109
|
+
case 'sqlite':
|
|
110
|
+
default:
|
|
111
|
+
return ['better-sqlite3']
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const drizzleAddon: AddonDefinition = {
|
|
116
|
+
id: 'drizzle',
|
|
117
|
+
category: 'database',
|
|
118
|
+
dependencies(ctx) {
|
|
119
|
+
const driver = ctx.driver ?? 'sqlite'
|
|
120
|
+
return ['drizzle-orm', ...driverPackages(driver)]
|
|
121
|
+
},
|
|
122
|
+
devDependencies() {
|
|
123
|
+
return ['drizzle-kit']
|
|
124
|
+
},
|
|
125
|
+
scripts() {
|
|
126
|
+
return {
|
|
127
|
+
'db:generate': 'drizzle-kit generate',
|
|
128
|
+
'db:push': 'drizzle-kit push',
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
bootBinding() {
|
|
132
|
+
return {
|
|
133
|
+
key: 'db',
|
|
134
|
+
importPath: './db/index.js',
|
|
135
|
+
factoryName: 'createDb',
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
async apply(ctx, write) {
|
|
139
|
+
const driver = ctx.driver ?? 'sqlite'
|
|
140
|
+
await write('src/db/schema.ts', schemaSource(driver))
|
|
141
|
+
await write('src/db/index.ts', dbIndexSource(driver))
|
|
142
|
+
await write('drizzle.config.ts', drizzleConfigSource(driver))
|
|
143
|
+
await write('.env.example', envExample(driver))
|
|
144
|
+
},
|
|
145
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { AddonDefinition } from '../types.js'
|
|
2
|
+
import type { DbDriver } from '../../core/types.js'
|
|
3
|
+
|
|
4
|
+
function schemaTypesSource(): string {
|
|
5
|
+
return `export interface UserTable {
|
|
6
|
+
id: number
|
|
7
|
+
email: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface DB {
|
|
11
|
+
users: UserTable
|
|
12
|
+
}
|
|
13
|
+
`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function dbIndexSource(driver: DbDriver): string {
|
|
17
|
+
switch (driver) {
|
|
18
|
+
case 'postgres':
|
|
19
|
+
return `import { Kysely, PostgresDialect } from 'kysely'
|
|
20
|
+
import pg from 'pg'
|
|
21
|
+
|
|
22
|
+
import type { DB } from './schema.js'
|
|
23
|
+
|
|
24
|
+
export function createDb() {
|
|
25
|
+
const pool = new pg.Pool({
|
|
26
|
+
connectionString: process.env.DATABASE_URL,
|
|
27
|
+
})
|
|
28
|
+
return new Kysely<DB>({
|
|
29
|
+
dialect: new PostgresDialect({ pool }),
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
`
|
|
33
|
+
case 'mysql':
|
|
34
|
+
return `import { Kysely, MysqlDialect } from 'kysely'
|
|
35
|
+
import { createPool } from 'mysql2'
|
|
36
|
+
|
|
37
|
+
import type { DB } from './schema.js'
|
|
38
|
+
|
|
39
|
+
export function createDb() {
|
|
40
|
+
const pool = createPool(process.env.DATABASE_URL ?? '')
|
|
41
|
+
return new Kysely<DB>({
|
|
42
|
+
dialect: new MysqlDialect({ pool }),
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
`
|
|
46
|
+
case 'sqlite':
|
|
47
|
+
default:
|
|
48
|
+
return `import Database from 'better-sqlite3'
|
|
49
|
+
import { Kysely, SqliteDialect } from 'kysely'
|
|
50
|
+
|
|
51
|
+
import type { DB } from './schema.js'
|
|
52
|
+
|
|
53
|
+
export function createDb() {
|
|
54
|
+
const filePath = (process.env.DATABASE_URL ?? 'file:./local.db').replace(/^file:/, '')
|
|
55
|
+
const database = new Database(filePath)
|
|
56
|
+
return new Kysely<DB>({
|
|
57
|
+
dialect: new SqliteDialect({ database }),
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
`
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function driverPackages(driver: DbDriver): string[] {
|
|
65
|
+
switch (driver) {
|
|
66
|
+
case 'postgres':
|
|
67
|
+
return ['pg']
|
|
68
|
+
case 'mysql':
|
|
69
|
+
return ['mysql2']
|
|
70
|
+
case 'sqlite':
|
|
71
|
+
default:
|
|
72
|
+
return ['better-sqlite3']
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function envExample(driver: DbDriver): string {
|
|
77
|
+
switch (driver) {
|
|
78
|
+
case 'postgres':
|
|
79
|
+
return 'DATABASE_URL=postgresql://user:password@localhost:5432/mydb\n'
|
|
80
|
+
case 'mysql':
|
|
81
|
+
return 'DATABASE_URL=mysql://user:password@localhost:3306/mydb\n'
|
|
82
|
+
case 'sqlite':
|
|
83
|
+
default:
|
|
84
|
+
return 'DATABASE_URL=file:./local.db\n'
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const kyselyAddon: AddonDefinition = {
|
|
89
|
+
id: 'kysely',
|
|
90
|
+
category: 'database',
|
|
91
|
+
dependencies(ctx) {
|
|
92
|
+
const driver = ctx.driver ?? 'sqlite'
|
|
93
|
+
return ['kysely', ...driverPackages(driver)]
|
|
94
|
+
},
|
|
95
|
+
devDependencies() {
|
|
96
|
+
return []
|
|
97
|
+
},
|
|
98
|
+
bootBinding() {
|
|
99
|
+
return {
|
|
100
|
+
key: 'db',
|
|
101
|
+
importPath: './db/index.js',
|
|
102
|
+
factoryName: 'createDb',
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
async apply(ctx, write) {
|
|
106
|
+
const driver = ctx.driver ?? 'sqlite'
|
|
107
|
+
await write('src/db/schema.ts', schemaTypesSource())
|
|
108
|
+
await write('src/db/index.ts', dbIndexSource(driver))
|
|
109
|
+
await write('.env.example', envExample(driver))
|
|
110
|
+
},
|
|
111
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AddonDefinition } from '../types.js'
|
|
2
|
+
|
|
3
|
+
export const pinoAddon: AddonDefinition = {
|
|
4
|
+
id: 'pino',
|
|
5
|
+
category: 'logger',
|
|
6
|
+
dependencies() {
|
|
7
|
+
return ['pino']
|
|
8
|
+
},
|
|
9
|
+
devDependencies() {
|
|
10
|
+
return []
|
|
11
|
+
},
|
|
12
|
+
bootBinding() {
|
|
13
|
+
return {
|
|
14
|
+
key: 'logger',
|
|
15
|
+
importPath: './logger.js',
|
|
16
|
+
factoryName: 'createLogger',
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
async apply(_ctx, write) {
|
|
20
|
+
await write(
|
|
21
|
+
'src/logger.ts',
|
|
22
|
+
`import pino from 'pino'
|
|
23
|
+
|
|
24
|
+
export function createLogger() {
|
|
25
|
+
return pino({ level: process.env.LOG_LEVEL ?? 'info' })
|
|
26
|
+
}
|
|
27
|
+
`,
|
|
28
|
+
)
|
|
29
|
+
},
|
|
30
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { AddonDefinition } from '../types.js'
|
|
2
|
+
import type { DbDriver } from '../../core/types.js'
|
|
3
|
+
|
|
4
|
+
function prismaProvider(driver: DbDriver): string {
|
|
5
|
+
switch (driver) {
|
|
6
|
+
case 'postgres':
|
|
7
|
+
return 'postgresql'
|
|
8
|
+
case 'mysql':
|
|
9
|
+
return 'mysql'
|
|
10
|
+
case 'sqlite':
|
|
11
|
+
default:
|
|
12
|
+
return 'sqlite'
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function envExample(driver: DbDriver): string {
|
|
17
|
+
switch (driver) {
|
|
18
|
+
case 'postgres':
|
|
19
|
+
return 'DATABASE_URL=postgresql://user:password@localhost:5432/mydb\n'
|
|
20
|
+
case 'mysql':
|
|
21
|
+
return 'DATABASE_URL=mysql://user:password@localhost:3306/mydb\n'
|
|
22
|
+
case 'sqlite':
|
|
23
|
+
default:
|
|
24
|
+
return 'DATABASE_URL=file:./local.db\n'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const prismaAddon: AddonDefinition = {
|
|
29
|
+
id: 'prisma',
|
|
30
|
+
category: 'database',
|
|
31
|
+
dependencies() {
|
|
32
|
+
return ['@prisma/client']
|
|
33
|
+
},
|
|
34
|
+
devDependencies() {
|
|
35
|
+
return ['prisma']
|
|
36
|
+
},
|
|
37
|
+
scripts() {
|
|
38
|
+
return {
|
|
39
|
+
'db:generate': 'prisma generate',
|
|
40
|
+
'db:push': 'prisma db push',
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
bootBinding() {
|
|
44
|
+
return {
|
|
45
|
+
key: 'db',
|
|
46
|
+
importPath: './db/index.js',
|
|
47
|
+
factoryName: 'createDb',
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
async apply(ctx, write) {
|
|
51
|
+
const driver = ctx.driver ?? 'sqlite'
|
|
52
|
+
const provider = prismaProvider(driver)
|
|
53
|
+
await write(
|
|
54
|
+
'prisma/schema.prisma',
|
|
55
|
+
`generator client {
|
|
56
|
+
provider = "prisma-client-js"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
datasource db {
|
|
60
|
+
provider = "${provider}"
|
|
61
|
+
url = env("DATABASE_URL")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
model User {
|
|
65
|
+
id Int @id @default(autoincrement())
|
|
66
|
+
email String
|
|
67
|
+
}
|
|
68
|
+
`,
|
|
69
|
+
)
|
|
70
|
+
await write(
|
|
71
|
+
'src/db/index.ts',
|
|
72
|
+
`import { PrismaClient } from '@prisma/client'
|
|
73
|
+
|
|
74
|
+
export function createDb() {
|
|
75
|
+
return new PrismaClient()
|
|
76
|
+
}
|
|
77
|
+
`,
|
|
78
|
+
)
|
|
79
|
+
await write('.env.example', envExample(driver))
|
|
80
|
+
},
|
|
81
|
+
}
|