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.
Files changed (139) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +22 -0
  3. package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
  4. package/dist/cjs/addons/drizzle/index.cjs +124 -0
  5. package/dist/cjs/addons/drizzle/index.cjs.map +1 -0
  6. package/dist/cjs/addons/drizzle/index.d.cts +2 -0
  7. package/dist/cjs/addons/kysely/index.cjs +96 -0
  8. package/dist/cjs/addons/kysely/index.cjs.map +1 -0
  9. package/dist/cjs/addons/kysely/index.d.cts +2 -0
  10. package/dist/cjs/addons/pino/index.cjs +30 -0
  11. package/dist/cjs/addons/pino/index.cjs.map +1 -0
  12. package/dist/cjs/addons/pino/index.d.cts +2 -0
  13. package/dist/cjs/addons/prisma/index.cjs +66 -0
  14. package/dist/cjs/addons/prisma/index.cjs.map +1 -0
  15. package/dist/cjs/addons/prisma/index.d.cts +2 -0
  16. package/dist/cjs/addons/registry.cjs +52 -0
  17. package/dist/cjs/addons/registry.cjs.map +1 -0
  18. package/dist/cjs/addons/registry.d.cts +5 -0
  19. package/dist/cjs/addons/types.d.cts +16 -0
  20. package/dist/cjs/addons/winston/index.cjs +34 -0
  21. package/dist/cjs/addons/winston/index.cjs.map +1 -0
  22. package/dist/cjs/addons/winston/index.d.cts +2 -0
  23. package/dist/cjs/cli.cjs +56 -0
  24. package/dist/cjs/cli.cjs.map +1 -0
  25. package/dist/cjs/cli.d.cts +2 -0
  26. package/dist/cjs/commands/create.cjs +243 -0
  27. package/dist/cjs/commands/create.cjs.map +1 -0
  28. package/dist/cjs/commands/create.d.cts +11 -0
  29. package/dist/cjs/core/json-output.cjs +16 -0
  30. package/dist/cjs/core/json-output.cjs.map +1 -0
  31. package/dist/cjs/core/json-output.d.cts +4 -0
  32. package/dist/cjs/core/package-manager.cjs +64 -0
  33. package/dist/cjs/core/package-manager.cjs.map +1 -0
  34. package/dist/cjs/core/package-manager.d.cts +7 -0
  35. package/dist/cjs/core/parse-options.cjs +59 -0
  36. package/dist/cjs/core/parse-options.cjs.map +1 -0
  37. package/dist/cjs/core/parse-options.d.cts +18 -0
  38. package/dist/cjs/core/project-config.cjs +18 -0
  39. package/dist/cjs/core/project-config.cjs.map +1 -0
  40. package/dist/cjs/core/project-config.d.cts +2 -0
  41. package/dist/cjs/core/resolve-packages.cjs +52 -0
  42. package/dist/cjs/core/resolve-packages.cjs.map +1 -0
  43. package/dist/cjs/core/resolve-packages.d.cts +5 -0
  44. package/dist/cjs/core/scaffold-engine.cjs +73 -0
  45. package/dist/cjs/core/scaffold-engine.cjs.map +1 -0
  46. package/dist/cjs/core/scaffold-engine.d.cts +2 -0
  47. package/dist/cjs/core/types.cjs +27 -0
  48. package/dist/cjs/core/types.cjs.map +1 -0
  49. package/dist/cjs/core/types.d.cts +37 -0
  50. package/dist/cjs/core/validate-project-name.cjs +45 -0
  51. package/dist/cjs/core/validate-project-name.cjs.map +1 -0
  52. package/dist/cjs/core/validate-project-name.d.cts +1 -0
  53. package/dist/cjs/frameworks/index.cjs +106 -0
  54. package/dist/cjs/frameworks/index.cjs.map +1 -0
  55. package/dist/cjs/frameworks/index.d.cts +3 -0
  56. package/dist/cjs/scaffold.d.cts +3 -0
  57. package/dist/cjs/templates/base.cjs +186 -0
  58. package/dist/cjs/templates/base.cjs.map +1 -0
  59. package/dist/cjs/templates/base.d.cts +12 -0
  60. package/dist/cjs/types.d.cts +1 -0
  61. package/dist/esm/addons/drizzle/index.d.ts +2 -0
  62. package/dist/esm/addons/drizzle/index.js +124 -0
  63. package/dist/esm/addons/drizzle/index.js.map +1 -0
  64. package/dist/esm/addons/kysely/index.d.ts +2 -0
  65. package/dist/esm/addons/kysely/index.js +96 -0
  66. package/dist/esm/addons/kysely/index.js.map +1 -0
  67. package/dist/esm/addons/pino/index.d.ts +2 -0
  68. package/dist/esm/addons/pino/index.js +30 -0
  69. package/dist/esm/addons/pino/index.js.map +1 -0
  70. package/dist/esm/addons/prisma/index.d.ts +2 -0
  71. package/dist/esm/addons/prisma/index.js +66 -0
  72. package/dist/esm/addons/prisma/index.js.map +1 -0
  73. package/dist/esm/addons/registry.d.ts +5 -0
  74. package/dist/esm/addons/registry.js +50 -0
  75. package/dist/esm/addons/registry.js.map +1 -0
  76. package/dist/esm/addons/types.d.ts +16 -0
  77. package/dist/esm/addons/winston/index.d.ts +2 -0
  78. package/dist/esm/addons/winston/index.js +34 -0
  79. package/dist/esm/addons/winston/index.js.map +1 -0
  80. package/dist/esm/cli.d.ts +2 -0
  81. package/dist/esm/cli.js +53 -0
  82. package/dist/esm/cli.js.map +1 -0
  83. package/dist/esm/commands/create.d.ts +11 -0
  84. package/dist/esm/commands/create.js +238 -0
  85. package/dist/esm/commands/create.js.map +1 -0
  86. package/dist/esm/core/json-output.d.ts +4 -0
  87. package/dist/esm/core/json-output.js +14 -0
  88. package/dist/esm/core/json-output.js.map +1 -0
  89. package/dist/esm/core/package-manager.d.ts +7 -0
  90. package/dist/esm/core/package-manager.js +62 -0
  91. package/dist/esm/core/package-manager.js.map +1 -0
  92. package/dist/esm/core/parse-options.d.ts +18 -0
  93. package/dist/esm/core/parse-options.js +56 -0
  94. package/dist/esm/core/parse-options.js.map +1 -0
  95. package/dist/esm/core/project-config.d.ts +2 -0
  96. package/dist/esm/core/project-config.js +16 -0
  97. package/dist/esm/core/project-config.js.map +1 -0
  98. package/dist/esm/core/resolve-packages.d.ts +5 -0
  99. package/dist/esm/core/resolve-packages.js +52 -0
  100. package/dist/esm/core/resolve-packages.js.map +1 -0
  101. package/dist/esm/core/scaffold-engine.d.ts +2 -0
  102. package/dist/esm/core/scaffold-engine.js +71 -0
  103. package/dist/esm/core/scaffold-engine.js.map +1 -0
  104. package/dist/esm/core/types.d.ts +37 -0
  105. package/dist/esm/core/types.js +23 -0
  106. package/dist/esm/core/types.js.map +1 -0
  107. package/dist/esm/core/validate-project-name.d.ts +1 -0
  108. package/dist/esm/core/validate-project-name.js +43 -0
  109. package/dist/esm/core/validate-project-name.js.map +1 -0
  110. package/dist/esm/frameworks/index.d.ts +3 -0
  111. package/dist/esm/frameworks/index.js +105 -0
  112. package/dist/esm/frameworks/index.js.map +1 -0
  113. package/dist/esm/scaffold.d.ts +3 -0
  114. package/dist/esm/templates/base.d.ts +12 -0
  115. package/dist/esm/templates/base.js +178 -0
  116. package/dist/esm/templates/base.js.map +1 -0
  117. package/dist/esm/types.d.ts +1 -0
  118. package/package.json +45 -0
  119. package/src/addons/drizzle/index.ts +145 -0
  120. package/src/addons/kysely/index.ts +111 -0
  121. package/src/addons/pino/index.ts +30 -0
  122. package/src/addons/prisma/index.ts +81 -0
  123. package/src/addons/registry.ts +73 -0
  124. package/src/addons/types.ts +19 -0
  125. package/src/addons/winston/index.ts +34 -0
  126. package/src/cli.ts +57 -0
  127. package/src/commands/create.ts +253 -0
  128. package/src/core/json-output.ts +13 -0
  129. package/src/core/package-manager.ts +68 -0
  130. package/src/core/parse-options.ts +97 -0
  131. package/src/core/project-config.ts +27 -0
  132. package/src/core/resolve-packages.ts +62 -0
  133. package/src/core/scaffold-engine.ts +82 -0
  134. package/src/core/types.ts +51 -0
  135. package/src/core/validate-project-name.ts +59 -0
  136. package/src/frameworks/index.ts +111 -0
  137. package/src/scaffold.ts +3 -0
  138. package/src/templates/base.ts +209 -0
  139. 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,3 @@
1
+ export { scaffoldProject } from './core/scaffold-engine.js';
2
+ export { getPackageGroups, resolvePackages } from './core/resolve-packages.js';
3
+ export type { Framework, ScaffoldOptions, ScaffoldResult, PackageGroups } from './core/types.js';
@@ -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
+ }