Skip to content

Commit 85e89d2

Browse files
feat: Prisma support (#190)
* feat: add configurable add-on options system - Add Zod schemas for add-on option definitions - Support conditional file generation with EJS templates - Enable package.json.ejs for conditional dependencies - Implement filename prefix stripping for database-specific files - Create Drizzle add-on with database provider option * no optional path to drizzle db location * feat: improve add-on options UI with settings dialog - Move add-on configuration from inline display to dedicated dialog - Add settings button (gear icon) next to info icon for configurable add-ons - Create new AddOnConfigDialog component with proper modal interface - Fix reactive state subscription for add-on options in sidebar - Clean up sidebar layout by removing inline configuration clutter - Add proper default option initialization when add-ons are toggled - Export add-on option types from engine for better type safety Improves UX by providing dedicated space for configuration with clear context while keeping the sidebar clean and compact. * cli in interactive mode asks for option values * feat: add add-on discovery and configuration CLI options - Enhanced --list-add-ons to show * for configurable add-ons - Added --addon-details command to show comprehensive add-on information - Added --add-on-config option for non-interactive configuration - Supports discovering options, dependencies, routes, and all add-on metadata * test: add comprehensive unit tests for add-on options system Implements Phase 4 testing for the add-on options system with comprehensive unit test coverage: - add-on-options.test.ts: Zod schema validation, default population, and error handling (15 tests) - template-context.test.ts: EJS template context with addOnOption variable integration (12 tests) - filename-processing.test.ts: Prefix stripping logic for __prefix__filename patterns (11 tests) - conditional-packages.test.ts: EJS conditional package.json processing (11 tests) Key test coverage areas: - Option schema validation with comprehensive positive/negative cases - Template variable integration and conditional file processing - Filename prefix stripping (__option__filename.ext.ejs → filename.ext) - Conditional package dependency generation via EJS templates - Error handling for malformed options and templates All 49 new tests pass, ensuring robust validation of the add-on options functionality. * docs: add comprehensive add-on options documentation - Document configuration format and option types in info.json - Explain template usage with addOnOption variables - Cover conditional file naming conventions with prefixes - Provide complete examples for both React CRA and Solid frameworks - Include CLI usage patterns for interactive and non-interactive modes - Add best practices for add-on developers * add addon options to the mcp * switch to prisma * feat: add post-init script support for add-ons - Add postInitScript field to add-on info schema - Implement post-init script execution in special steps - Update Prisma add-on with database initialization script - Add comprehensive tests for post-init script functionality - Handle script execution after package installation - Fix bun package manager test expectation * watch the cli's for changes with pnpm dev * split out prisma into two add-ons * feat: prisma updates and fixups * fix: small ui fix --------- Co-authored-by: timoconnellaus <[email protected]>
1 parent 276b96b commit 85e89d2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2874
-80
lines changed

.claude/settings.local.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(pnpm ls:*)",
5+
"WebFetch(domain:github.com)",
6+
"Bash(node:*)",
7+
"Bash(source:*)",
8+
"Bash(nvm use:*)",
9+
"Bash(find:*)",
10+
"Bash(grep:*)",
11+
"Bash(pnpm test:*)"
12+
],
13+
"deny": []
14+
}
15+
}

cli/create-start-app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"type": "module",
77
"scripts": {
88
"build": "tsc",
9+
"dev": "tsc --watch",
910
"start": "node dist/index.js"
1011
},
1112
"repository": {

cli/create-tanstack-app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"type": "module",
77
"scripts": {
88
"build": "tsc",
9+
"dev": "tsc --watch",
910
"start": "node dist/index.js"
1011
},
1112
"repository": {

cli/create-tanstack/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"type": "module",
77
"scripts": {
88
"build": "tsc",
9+
"dev": "tsc --watch",
910
"start": "node dist/index.js"
1011
},
1112
"repository": {

cli/create-tsrouter-app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"type": "module",
77
"scripts": {
88
"build": "tsc",
9+
"dev": "tsc --watch",
910
"start": "node dist/index.js"
1011
},
1112
"repository": {

cli/ts-create-start/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"type": "module",
77
"scripts": {
88
"build": "tsc",
9+
"dev": "tsc --watch",
910
"start": "node dist/index.js"
1011
},
1112
"repository": {

frameworks/react-cra/ADD-ON-AUTHORING.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,171 @@ If you don't want a header link you can omit the `url` and `name` properties.
193193
You **MUST** specify routes in the `info.json` file if your add-on supports the `code-router` mode. This is because the `code-routers` setup needs to import the routes in order to add them to the router.
194194

195195
By convension you should prefix demo routes with `demo` to make it clear that they are demo routes so they can be easily identified and removed.
196+
197+
# Add-on Options
198+
199+
The CTA framework supports configurable add-ons through an options system that allows users to customize add-on behavior during creation. This enables more flexible and reusable add-ons that can adapt to different use cases.
200+
201+
## Overview
202+
203+
Add-on options allow developers to create configurable add-ons where users can select from predefined choices that affect:
204+
205+
- Which files are included in the generated project
206+
- Template variable values used during file generation
207+
- Package dependencies that get installed
208+
- Configuration file contents
209+
210+
## Configuration Format
211+
212+
Options are defined in the `info.json` file using the following schema:
213+
214+
```json
215+
{
216+
"name": "My Add-on",
217+
"description": "A configurable add-on",
218+
"options": {
219+
"optionName": {
220+
"type": "select",
221+
"label": "Display Label",
222+
"description": "Optional description shown to users",
223+
"default": "defaultValue",
224+
"options": [
225+
{ "value": "option1", "label": "Option 1" },
226+
{ "value": "option2", "label": "Option 2" }
227+
]
228+
}
229+
}
230+
}
231+
```
232+
233+
### Option Types
234+
235+
#### Select Options
236+
237+
The `select` type allows users to choose from a predefined list of options:
238+
239+
```json
240+
"database": {
241+
"type": "select",
242+
"label": "Database Provider",
243+
"description": "Choose your database provider",
244+
"default": "postgres",
245+
"options": [
246+
{ "value": "postgres", "label": "PostgreSQL" },
247+
{ "value": "mysql", "label": "MySQL" },
248+
{ "value": "sqlite", "label": "SQLite" }
249+
]
250+
}
251+
```
252+
253+
**Properties:**
254+
255+
- `type`: Must be `"select"`
256+
- `label`: Display text shown to users
257+
- `description`: Optional help text
258+
- `default`: Default value that must match one of the option values
259+
- `options`: Array of value/label pairs
260+
261+
## Template Usage
262+
263+
Option values are available in EJS templates through the `addOnOption` variable:
264+
265+
```ejs
266+
<!-- Access option value -->
267+
<% if (addOnOption.myAddOnId.database === 'postgres') { %>
268+
PostgreSQL specific code
269+
<% } %>
270+
271+
<!-- Use option value in output -->
272+
const driver = '<%= addOnOption.myAddOnId.database %>'
273+
```
274+
275+
The structure is: `addOnOption.{addOnId}.{optionName}`
276+
277+
### Template Conditional Logic
278+
279+
Within template files, use `ignoreFile()` to skip file generation:
280+
281+
```ejs
282+
<% if (addOnOption.prisma.database !== 'postgres') { ignoreFile() } %>
283+
import { PrismaClient } from '@prisma/client'
284+
285+
declare global {
286+
var __prisma: PrismaClient | undefined
287+
}
288+
289+
export const prisma = globalThis.__prisma || new PrismaClient()
290+
291+
if (process.env.NODE_ENV !== 'production') {
292+
globalThis.__prisma = prisma
293+
}
294+
```
295+
296+
## Complete Example: Prisma Add-on
297+
298+
Here's how the Prisma add-on implements configurable database support:
299+
300+
### Examples
301+
302+
Configuration in `info.json`:
303+
304+
```json
305+
{
306+
"name": "Prisma ORM",
307+
"description": "Add Prisma ORM with configurable database support to your application.",
308+
"options": {
309+
"database": {
310+
"type": "select",
311+
"label": "Database Provider",
312+
"description": "Choose your database provider",
313+
"default": "postgres",
314+
"options": [
315+
{ "value": "postgres", "label": "PostgreSQL" },
316+
{ "value": "mysql", "label": "MySQL" },
317+
{ "value": "sqlite", "label": "SQLite" }
318+
]
319+
}
320+
}
321+
}
322+
```
323+
324+
Code in `package.json.ejs`:
325+
326+
```ejs
327+
{
328+
"prisma": "^6.16.3",
329+
"@prisma/client": "^6.16.3"<% if (addOnOption.prisma.database === 'postgres') { %>,
330+
"pg": "^8.11.0",
331+
"@types/pg": "^8.10.0"<% } else if (addOnOption.prisma.database === 'mysql') { %>,
332+
"mysql2": "^3.6.0"<% } else if (addOnOption.prisma.database === 'sqlite') { %><% } %>
333+
}
334+
```
335+
336+
## CLI Usage
337+
338+
### Interactive Mode
339+
340+
When using the CLI interactively, users are prompted for each option:
341+
342+
```bash
343+
create-tsrouter-app my-app
344+
# User selects Prisma add-on
345+
# CLI prompts: "Prisma ORM: Database Provider" with options
346+
```
347+
348+
### Non-Interactive Mode
349+
350+
Options can be specified via JSON configuration:
351+
352+
```bash
353+
create-tsrouter-app my-app --add-ons prisma --add-on-config '{"prisma":{"database":"mysql"}}'
354+
```
355+
356+
## Best Practices
357+
358+
1. **Use descriptive labels** - Make option purposes clear to users
359+
2. **Provide sensible defaults** - Choose the most common use case
360+
3. **Group related files** - Use consistent prefixing for option-specific files
361+
4. **Document options** - Include descriptions to help users understand choices
362+
5. **Test all combinations** - Ensure each option value generates working code
363+
6. **Use validation** - The system validates options against the schema automatically
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<% if (addOnOption.prisma.database === 'postgres') { %>
2+
# Database URL for PostgreSQL
3+
DATABASE_URL="postgresql://username:password@localhost:5432/mydb"<% } else if (addOnOption.prisma.database === 'mysql') { %>
4+
# Database URL for MySQL
5+
DATABASE_URL="mysql://username:password@localhost:3306/mydb"<% } else if (addOnOption.prisma.database === 'sqlite') { %>
6+
# Database URL for SQLite
7+
DATABASE_URL="file:./dev.db"<% } %>
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
generator client {
2+
provider = "prisma-client-js"
3+
}
4+
5+
datasource db {
6+
provider = "<%= addOnOption.prisma.database === "postgres" ? "postgresql" : addOnOption.prisma.database %>"
7+
url = env("DATABASE_URL")
8+
}
9+
10+
model Todo {
11+
id Int @id @default(autoincrement())
12+
title String
13+
createdAt DateTime @default(now())
14+
}

0 commit comments

Comments
 (0)