@tanstack/create 0.59.4 → 0.61.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 (103) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/dist/create-app.js +7 -5
  3. package/dist/file-helpers.js +13 -1
  4. package/dist/frameworks/react/add-ons/ai/info.json +0 -5
  5. package/dist/frameworks/react/add-ons/ai/package.json +0 -1
  6. package/dist/frameworks/{solid/add-ons/better-auth/README.md → react/add-ons/better-auth/README.md.ejs} +2 -2
  7. package/dist/frameworks/react/add-ons/better-auth/assets/_dot_env.local.append.ejs +3 -0
  8. package/dist/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx +3 -1
  9. package/dist/frameworks/react/add-ons/better-auth/assets/src/routes/demo/better-auth.tsx +3 -1
  10. package/dist/frameworks/react/add-ons/convex/README.md.ejs +4 -0
  11. package/dist/frameworks/react/add-ons/drizzle/assets/src/routes/demo/{drizzle.tsx → drizzle.tsx.ejs} +3 -3
  12. package/dist/frameworks/react/add-ons/neon/assets/_dot_env.example.append +2 -2
  13. package/dist/frameworks/react/add-ons/neon/assets/neon-vite-plugin.ts +1 -1
  14. package/dist/frameworks/react/add-ons/neon/assets/src/db.ts +2 -2
  15. package/dist/frameworks/react/add-ons/posthog/README.md +9 -0
  16. package/dist/frameworks/react/add-ons/posthog/assets/_dot_env.local.append +4 -0
  17. package/dist/frameworks/react/add-ons/posthog/assets/src/integrations/posthog/provider.tsx +20 -0
  18. package/dist/frameworks/react/add-ons/posthog/assets/src/routes/demo/posthog.tsx +93 -0
  19. package/dist/frameworks/react/add-ons/posthog/files.json +5 -0
  20. package/dist/frameworks/react/add-ons/posthog/info.json +28 -0
  21. package/dist/frameworks/react/add-ons/posthog/package.json +6 -0
  22. package/dist/frameworks/react/add-ons/prisma/assets/src/routes/demo/{prisma.tsx → prisma.tsx.ejs} +3 -3
  23. package/dist/frameworks/react/add-ons/prisma/package.json.ejs +1 -1
  24. package/dist/frameworks/react/add-ons/sentry/package.json +1 -1
  25. package/dist/frameworks/react/add-ons/tanstack-query/info.json +1 -1
  26. package/dist/frameworks/react/hosts/nitro/info.json +1 -1
  27. package/dist/frameworks/react/hosts/railway/info.json +1 -1
  28. package/dist/frameworks/react/project/base/README.md.ejs +1 -1
  29. package/dist/frameworks/react/project/base/package.json +1 -1
  30. package/dist/frameworks/react/project/base/vite.config.ts.ejs +1 -1
  31. package/dist/frameworks/{react/add-ons/better-auth/README.md → solid/add-ons/better-auth/README.md.ejs} +2 -2
  32. package/dist/frameworks/solid/add-ons/better-auth/assets/_dot_env.local.append.ejs +3 -0
  33. package/dist/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx +3 -1
  34. package/dist/frameworks/solid/add-ons/better-auth/assets/src/routes/demo.better-auth.tsx +3 -1
  35. package/dist/frameworks/solid/add-ons/convex/README.md.ejs +4 -0
  36. package/{src/frameworks/solid/add-ons/solid-ui/README.md → dist/frameworks/solid/add-ons/solid-ui/README.md.ejs} +1 -1
  37. package/dist/frameworks/solid/add-ons/solid-ui/info.json +1 -5
  38. package/dist/frameworks/solid/project/base/README.md.ejs +1 -1
  39. package/dist/frameworks.js +6 -0
  40. package/dist/package-manager.js +29 -0
  41. package/dist/template-file.js +8 -1
  42. package/dist/types/custom-add-ons/add-on.d.ts +2 -1
  43. package/dist/types/package-manager.d.ts +7 -0
  44. package/dist/types/types.d.ts +21 -15
  45. package/dist/types/utils.d.ts +2 -0
  46. package/dist/types.js +2 -0
  47. package/dist/utils.js +6 -0
  48. package/package.json +1 -1
  49. package/src/create-app.ts +9 -6
  50. package/src/file-helpers.ts +12 -1
  51. package/src/frameworks/react/add-ons/ai/info.json +0 -5
  52. package/src/frameworks/react/add-ons/ai/package.json +0 -1
  53. package/src/frameworks/react/add-ons/better-auth/{README.md → README.md.ejs} +2 -2
  54. package/src/frameworks/react/add-ons/better-auth/assets/_dot_env.local.append.ejs +3 -0
  55. package/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx +3 -1
  56. package/src/frameworks/react/add-ons/better-auth/assets/src/routes/demo/better-auth.tsx +3 -1
  57. package/src/frameworks/react/add-ons/convex/README.md.ejs +4 -0
  58. package/src/frameworks/react/add-ons/drizzle/assets/src/routes/demo/{drizzle.tsx → drizzle.tsx.ejs} +3 -3
  59. package/src/frameworks/react/add-ons/neon/assets/_dot_env.example.append +2 -2
  60. package/src/frameworks/react/add-ons/neon/assets/neon-vite-plugin.ts +1 -1
  61. package/src/frameworks/react/add-ons/neon/assets/src/db.ts +2 -2
  62. package/src/frameworks/react/add-ons/posthog/README.md +9 -0
  63. package/src/frameworks/react/add-ons/posthog/assets/_dot_env.local.append +4 -0
  64. package/src/frameworks/react/add-ons/posthog/assets/src/integrations/posthog/provider.tsx +20 -0
  65. package/src/frameworks/react/add-ons/posthog/assets/src/routes/demo/posthog.tsx +93 -0
  66. package/src/frameworks/react/add-ons/posthog/files.json +5 -0
  67. package/src/frameworks/react/add-ons/posthog/info.json +28 -0
  68. package/src/frameworks/react/add-ons/posthog/package.json +6 -0
  69. package/src/frameworks/react/add-ons/prisma/assets/src/routes/demo/{prisma.tsx → prisma.tsx.ejs} +3 -3
  70. package/src/frameworks/react/add-ons/prisma/package.json.ejs +1 -1
  71. package/src/frameworks/react/add-ons/sentry/package.json +1 -1
  72. package/src/frameworks/react/add-ons/tanstack-query/info.json +1 -1
  73. package/src/frameworks/react/hosts/nitro/info.json +1 -1
  74. package/src/frameworks/react/hosts/railway/info.json +1 -1
  75. package/src/frameworks/react/project/base/README.md.ejs +1 -1
  76. package/src/frameworks/react/project/base/package.json +1 -1
  77. package/src/frameworks/react/project/base/vite.config.ts.ejs +1 -1
  78. package/src/frameworks/solid/add-ons/better-auth/{README.md → README.md.ejs} +2 -2
  79. package/src/frameworks/solid/add-ons/better-auth/assets/_dot_env.local.append.ejs +3 -0
  80. package/src/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx +3 -1
  81. package/src/frameworks/solid/add-ons/better-auth/assets/src/routes/demo.better-auth.tsx +3 -1
  82. package/src/frameworks/solid/add-ons/convex/README.md.ejs +4 -0
  83. package/{dist/frameworks/solid/add-ons/solid-ui/README.md → src/frameworks/solid/add-ons/solid-ui/README.md.ejs} +1 -1
  84. package/src/frameworks/solid/add-ons/solid-ui/info.json +1 -5
  85. package/src/frameworks/solid/project/base/README.md.ejs +1 -1
  86. package/src/frameworks.ts +8 -0
  87. package/src/package-manager.ts +37 -0
  88. package/src/template-file.ts +14 -0
  89. package/src/types.ts +2 -0
  90. package/src/utils.ts +8 -0
  91. package/tests/file-helper.test.ts +6 -0
  92. package/tests/package-manager.test.ts +159 -0
  93. package/tests/template-file.test.ts +13 -0
  94. package/dist/frameworks/react/add-ons/ai/assets/src/lib/ai-devtools.tsx +0 -3
  95. package/dist/frameworks/react/add-ons/better-auth/assets/_dot_env.local.append +0 -3
  96. package/dist/frameworks/react/add-ons/convex/README.md +0 -4
  97. package/dist/frameworks/solid/add-ons/better-auth/assets/_dot_env.local.append +0 -3
  98. package/dist/frameworks/solid/add-ons/convex/README.md +0 -4
  99. package/src/frameworks/react/add-ons/ai/assets/src/lib/ai-devtools.tsx +0 -3
  100. package/src/frameworks/react/add-ons/better-auth/assets/_dot_env.local.append +0 -3
  101. package/src/frameworks/react/add-ons/convex/README.md +0 -4
  102. package/src/frameworks/solid/add-ons/better-auth/assets/_dot_env.local.append +0 -3
  103. package/src/frameworks/solid/add-ons/convex/README.md +0 -4
@@ -3,11 +3,11 @@ import { neon } from '@neondatabase/serverless'
3
3
  let client: ReturnType<typeof neon>
4
4
 
5
5
  export async function getClient() {
6
- if (!process.env.VITE_DATABASE_URL) {
6
+ if (!process.env.DATABASE_URL) {
7
7
  return undefined
8
8
  }
9
9
  if (!client) {
10
- client = await neon(process.env.VITE_DATABASE_URL!)
10
+ client = await neon(process.env.DATABASE_URL!)
11
11
  }
12
12
  return client
13
13
  }
@@ -0,0 +1,9 @@
1
+ ## Setting up PostHog
2
+
3
+ 1. Create a PostHog account at [posthog.com](https://posthog.com)
4
+ 2. Get your Project API Key from [Project Settings](https://app.posthog.com/project/settings)
5
+ 3. Set `VITE_POSTHOG_KEY` in your `.env.local`
6
+
7
+ ### Optional Configuration
8
+
9
+ - `VITE_POSTHOG_HOST` - Set this if you're using PostHog Cloud EU (`https://eu.i.posthog.com`) or self-hosting
@@ -0,0 +1,4 @@
1
+ # PostHog configuration, get your key from https://app.posthog.com/project/settings
2
+ VITE_POSTHOG_KEY=phc_xxx
3
+ # Optional: PostHog API host (for self-hosted or EU cloud)
4
+ # VITE_POSTHOG_HOST=https://us.i.posthog.com
@@ -0,0 +1,20 @@
1
+ import posthog from 'posthog-js'
2
+ import { PostHogProvider as BasePostHogProvider } from '@posthog/react'
3
+ import type { ReactNode } from 'react'
4
+
5
+ if (typeof window !== 'undefined' && import.meta.env.VITE_POSTHOG_KEY) {
6
+ posthog.init(import.meta.env.VITE_POSTHOG_KEY, {
7
+ api_host: import.meta.env.VITE_POSTHOG_HOST || 'https://us.i.posthog.com',
8
+ person_profiles: 'identified_only',
9
+ capture_pageview: false,
10
+ defaults: '2025-11-30',
11
+ })
12
+ }
13
+
14
+ interface PostHogProviderProps {
15
+ children: ReactNode
16
+ }
17
+
18
+ export default function PostHogProvider({ children }: PostHogProviderProps) {
19
+ return <BasePostHogProvider client={posthog}>{children}</BasePostHogProvider>
20
+ }
@@ -0,0 +1,93 @@
1
+ import { createFileRoute, Link } from '@tanstack/react-router'
2
+ import { usePostHog } from '@posthog/react'
3
+ import { useState } from 'react'
4
+
5
+ export const Route = createFileRoute('/demo/posthog')({
6
+ component: PostHogDemo,
7
+ })
8
+
9
+ function PostHogDemo() {
10
+ const posthog = usePostHog()
11
+ const [eventCount, setEventCount] = useState(0)
12
+ const posthogKey = import.meta.env.VITE_POSTHOG_KEY
13
+ const isConfigured = Boolean(posthogKey) && posthogKey !== 'phc_xxx'
14
+
15
+ const trackEvent = (
16
+ eventName: string,
17
+ properties?: Record<string, unknown>,
18
+ ) => {
19
+ posthog.capture(eventName, properties)
20
+ setEventCount((c) => c + 1)
21
+ }
22
+
23
+ return (
24
+ <div className="min-h-screen bg-gray-900 text-white p-8">
25
+ <div className="max-w-md mx-auto">
26
+ <h1 className="text-3xl font-bold mb-6">PostHog Demo</h1>
27
+
28
+ {!isConfigured && (
29
+ <div className="mb-4 p-4 bg-yellow-900/50 border border-yellow-600 rounded-lg">
30
+ <p className="text-yellow-200 text-sm">
31
+ <strong>Warning:</strong> VITE_POSTHOG_KEY is not configured.
32
+ Events won't be sent to PostHog. Add it to your{' '}
33
+ <code className="bg-yellow-900 px-1 rounded">.env</code> file.
34
+ </p>
35
+ </div>
36
+ )}
37
+
38
+ <div className="bg-gray-800 rounded-lg p-6">
39
+ <p className="text-gray-400 mb-4">
40
+ Click the button below to send events to PostHog. Check your PostHog
41
+ dashboard to see them appear in real-time.
42
+ </p>
43
+
44
+ <button
45
+ onClick={() => trackEvent('button_clicked', { button: 'demo' })}
46
+ className="w-full bg-cyan-600 hover:bg-cyan-700 px-4 py-3 rounded font-medium"
47
+ >
48
+ Track Click
49
+ </button>
50
+
51
+ {isConfigured && (
52
+ <div className="mt-6 p-4 bg-gray-700 rounded">
53
+ <p className="text-sm text-gray-400">Events sent this session:</p>
54
+ <p className="text-4xl font-bold text-cyan-400">{eventCount}</p>
55
+ </div>
56
+ )}
57
+ </div>
58
+
59
+ <p className="mt-4 text-sm text-gray-400">
60
+ Open your{' '}
61
+ <a
62
+ href="https://app.posthog.com/events"
63
+ target="_blank"
64
+ rel="noopener noreferrer"
65
+ className="text-cyan-400 hover:text-cyan-300 underline"
66
+ >
67
+ PostHog Events
68
+ </a>{' '}
69
+ page to see these events appear.
70
+ </p>
71
+
72
+ <p className="mt-2 text-sm text-gray-400">
73
+ Learn more in the{' '}
74
+ <a
75
+ href="https://posthog.com/docs/libraries/react"
76
+ target="_blank"
77
+ rel="noopener noreferrer"
78
+ className="text-cyan-400 hover:text-cyan-300 underline"
79
+ >
80
+ PostHog React docs
81
+ </a>
82
+ .
83
+ </p>
84
+
85
+ <div className="mt-8">
86
+ <Link to="/" className="text-cyan-400 hover:text-cyan-300">
87
+ &larr; Back to Home
88
+ </Link>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ )
93
+ }
@@ -0,0 +1,5 @@
1
+ [
2
+ "src/integrations/posthog/provider.tsx",
3
+ "src/routes/demo/posthog.tsx",
4
+ "_dot_env.local.append"
5
+ ]
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "PostHog",
3
+ "description": "Product analytics, session replay, and feature flags",
4
+ "phase": "add-on",
5
+ "modes": ["file-router"],
6
+ "type": "add-on",
7
+ "category": "analytics",
8
+ "color": "#1D4AFF",
9
+ "priority": 20,
10
+ "link": "https://posthog.com",
11
+ "tailwind": true,
12
+ "routes": [
13
+ {
14
+ "icon": "BarChart",
15
+ "url": "/demo/posthog",
16
+ "name": "PostHog",
17
+ "path": "src/routes/demo/posthog.tsx",
18
+ "jsName": "PostHogDemo"
19
+ }
20
+ ],
21
+ "integrations": [
22
+ {
23
+ "type": "provider",
24
+ "jsName": "PostHogProvider",
25
+ "path": "src/integrations/posthog/provider.tsx"
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "dependencies": {
3
+ "posthog-js": "^1.335.4",
4
+ "@posthog/react": "^1.7.0"
5
+ }
6
+ }
@@ -162,19 +162,19 @@ function DemoPrisma() {
162
162
  <li>
163
163
  Run:{' '}
164
164
  <code className="px-2 py-1 rounded bg-black/30 text-purple-300">
165
- npx prisma generate
165
+ <%- getPackageManagerExecuteScript('prisma', ['generate']) %>
166
166
  </code>
167
167
  </li>
168
168
  <li>
169
169
  Run:{' '}
170
170
  <code className="px-2 py-1 rounded bg-black/30 text-purple-300">
171
- npx prisma db push
171
+ <%- getPackageManagerExecuteScript('prisma', ['db', 'push']) %>
172
172
  </code>
173
173
  </li>
174
174
  <li>
175
175
  Optional:{' '}
176
176
  <code className="px-2 py-1 rounded bg-black/30 text-purple-300">
177
- npx prisma studio
177
+ <%- getPackageManagerExecuteScript('prisma', ['studio']) %>
178
178
  </code>
179
179
  </li>
180
180
  </ol>
@@ -11,7 +11,7 @@
11
11
  "tsx": "^4.20.6"
12
12
  },
13
13
  "scripts": {<% if (addOnOption.prisma.database === 'postgres') { %>
14
- "post-cta-init": "npx create-db@latest --user-agent tanstack/tsrouter",<% } %>
14
+ "post-cta-init": "<%- getPackageManagerExecuteScript('create-db@latest', ['--user-agent', 'tanstack/tsrouter']) %>",<% } %>
15
15
  "db:generate": "dotenv -e .env.local -- prisma generate",
16
16
  "db:push": "dotenv -e .env.local -- prisma db push",
17
17
  "db:migrate": "dotenv -e .env.local -- prisma migrate dev",
@@ -5,7 +5,7 @@
5
5
  "start": "node --import ./.output/server/instrument.server.mjs .output/server/index.mjs"
6
6
  },
7
7
  "dependencies": {
8
- "@sentry/tanstackstart-react": "^10.22.0",
8
+ "@sentry/tanstackstart-react": "^10.34.0",
9
9
  "dotenv-cli": "^11.0.0"
10
10
  }
11
11
  }
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "integrations": [
21
21
  {
22
- "type": "root-provider",
22
+ "type": "provider",
23
23
  "path": "src/integrations/tanstack-query/root-provider.tsx",
24
24
  "jsName": "TanStackQueryProvider"
25
25
  },
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "type": "vite-plugin",
15
15
  "import": "import { nitro } from 'nitro/vite'",
16
- "code": "nitro()"
16
+ "code": "nitro({ rollupConfig: { external: [/^@sentry\\//] } })"
17
17
  }
18
18
  ],
19
19
  "default": true
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "type": "vite-plugin",
15
15
  "import": "import { nitro } from 'nitro/vite'",
16
- "code": "nitro()"
16
+ "code": "nitro({ rollupConfig: { external: [/^@sentry\\//] } })"
17
17
  }
18
18
  ]
19
19
  }
@@ -52,7 +52,7 @@ This project uses [eslint](https://eslint.org/) and [prettier](https://prettier.
52
52
  ```
53
53
  <% } %>
54
54
  <% for(const addon of addOns.filter(addon => addon.readme)) { %>
55
- <%- addon.readme %>
55
+ <%- addon.readmeIsEjs ? renderTemplate(addon.readme) : addon.readme %>
56
56
  <% } %>
57
57
 
58
58
  ## Routing
@@ -17,7 +17,7 @@
17
17
  "lucide-react": "^0.561.0",
18
18
  "react": "^19.2.0",
19
19
  "react-dom": "^19.2.0",
20
- "vite-tsconfig-paths": "^6.0.2"
20
+ "vite-tsconfig-paths": "^5.1.4"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@tanstack/devtools-vite": "^0.3.11",
@@ -6,7 +6,7 @@ import { paraglideVitePlugin } from "@inlang/paraglide-js"
6
6
  import { tanstackStart } from '@tanstack/react-start/plugin/vite';
7
7
  import viteReact from '@vitejs/plugin-react'
8
8
  import viteTsConfigPaths from 'vite-tsconfig-paths'
9
- import { fileURLToPath, URL } from 'url'
9
+ import { fileURLToPath, URL } from 'node:url'
10
10
  import tailwindcss from "@tailwindcss/vite"
11
11
  <% for(const integration of integrations.filter(i => i.type === 'vite-plugin')) { %><%- integrationImportContent(integration) %>
12
12
  <% } %>
@@ -3,7 +3,7 @@
3
3
  1. Generate and set the `BETTER_AUTH_SECRET` environment variable in your `.env.local`:
4
4
 
5
5
  ```bash
6
- npx @better-auth/cli secret
6
+ <%- getPackageManagerExecuteScript('@better-auth/cli', ['secret']) %>
7
7
  ```
8
8
 
9
9
  2. Visit the [Better Auth documentation](https://www.better-auth.com) to unlock the full potential of authentication in your app.
@@ -28,5 +28,5 @@ export const auth = betterAuth({
28
28
  Then run migrations:
29
29
 
30
30
  ```bash
31
- npx @better-auth/cli migrate
31
+ <%- getPackageManagerExecuteScript('@better-auth/cli', ['migrate']) %>
32
32
  ```
@@ -0,0 +1,3 @@
1
+ # Better Auth configuration
2
+ BETTER_AUTH_URL=http://localhost:3000
3
+ BETTER_AUTH_SECRET= # Generate a secret key: `<%- getPackageManagerExecuteScript('@better-auth/cli', ['secret']) %>`
@@ -38,7 +38,9 @@ export default function BetterAuthHeader() {
38
38
  {(image) => <img src={image()} alt="" class="h-8 w-8" />}
39
39
  </Show>
40
40
  <button
41
- onClick={() => authClient.signOut()}
41
+ onClick={() => {
42
+ void authClient.signOut();
43
+ }}
42
44
  class="flex-1 h-9 px-4 text-sm font-medium bg-white dark:bg-neutral-900 text-neutral-900 dark:text-neutral-50 border border-neutral-300 dark:border-neutral-700 hover:bg-neutral-50 dark:hover:bg-neutral-800 transition-colors"
43
45
  >
44
46
  Sign out
@@ -210,7 +210,9 @@ function BetterAuthDemo() {
210
210
  </div>
211
211
 
212
212
  <button
213
- onClick={() => authClient.signOut()}
213
+ onClick={() => {
214
+ void authClient.signOut();
215
+ }}
214
216
  class="w-full h-9 px-4 text-sm font-medium border border-neutral-300 dark:border-neutral-700 hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors"
215
217
  >
216
218
  Sign out
@@ -0,0 +1,4 @@
1
+ ## Setting up Convex
2
+
3
+ - Set the `VITE_CONVEX_URL` and `CONVEX_DEPLOYMENT` environment variables in your `.env.local`. (Or run `<%- getPackageManagerExecuteScript('convex', ['init']) %>` to set them automatically.)
4
+ - Run `<%- getPackageManagerExecuteScript('convex', ['dev']) %>` to start the Convex server.
@@ -5,5 +5,5 @@ This installation of Solid-UI follows the manual instructions but was modified t
5
5
  To install the components, run the following command (this install button):
6
6
 
7
7
  ```bash
8
- npx solidui-cli@latest add button
8
+ <%- getPackageManagerExecuteScript('solidui-cli@latest', ['add', 'button']) %>
9
9
  ```
@@ -6,9 +6,5 @@
6
6
  "modes": ["file-router", "code-router"],
7
7
  "type": "add-on",
8
8
  "category": "styling",
9
- "color": "#000000",
10
- "command": {
11
- "command": "npx",
12
- "args": ["solidui-cli@latest", "add", "button", "input"]
13
- }
9
+ "color": "#000000"
14
10
  }
@@ -31,7 +31,7 @@ If you prefer not to use Tailwind CSS:
31
31
  4. Uninstall the packages: `<%= getPackageManagerAddScript('@tailwindcss/vite tailwindcss', true) %>`
32
32
 
33
33
  <% for(const addon of addOns.filter(addon => addon.readme)) { %>
34
- <%- addon.readme %>
34
+ <%- addon.readmeIsEjs ? renderTemplate(addon.readme) : addon.readme %>
35
35
  <% } %>
36
36
 
37
37
  ## Routing
package/src/frameworks.ts CHANGED
@@ -72,8 +72,15 @@ export function scanAddOnDirectories(addOnsDirectories: Array<string>) {
72
72
  }
73
73
 
74
74
  let readme: string | undefined
75
+ let readmeIsEjs = false
75
76
  if (existsSync(resolve(addOnsBase, dir, 'README.md'))) {
76
77
  readme = readFileSync(resolve(addOnsBase, dir, 'README.md'), 'utf-8')
78
+ } else if (existsSync(resolve(addOnsBase, dir, 'README.md.ejs'))) {
79
+ readme = readFileSync(
80
+ resolve(addOnsBase, dir, 'README.md.ejs'),
81
+ 'utf-8',
82
+ )
83
+ readmeIsEjs = true
77
84
  }
78
85
 
79
86
  let smallLogo: string | undefined
@@ -107,6 +114,7 @@ export function scanAddOnDirectories(addOnsDirectories: Array<string>) {
107
114
  packageAdditions,
108
115
  packageTemplate,
109
116
  readme,
117
+ readmeIsEjs,
110
118
  files,
111
119
  smallLogo,
112
120
  getFiles,
@@ -101,6 +101,43 @@ export function packageManagerInstall(
101
101
  return environment.execute(command, commandArgs, cwd)
102
102
  }
103
103
 
104
+ export function translateExecuteCommand(
105
+ packageManager: PackageManager,
106
+ command: { command: string; args?: Array<string> },
107
+ ): { command: string; args: Array<string> } {
108
+ const args = command.args || []
109
+ const parsed = parseExecuteCommand(command.command, args)
110
+ if (parsed) {
111
+ return getPackageManagerExecuteCommand(packageManager, parsed.pkg, parsed.args)
112
+ }
113
+ return { command: command.command, args }
114
+ }
115
+
116
+ function parseExecuteCommand(
117
+ command: string,
118
+ args: Array<string>,
119
+ ): { pkg: string; args: Array<string> } | null {
120
+ if (command === 'npx') {
121
+ const filtered = args[0] === '-y' ? args.slice(1) : args
122
+ const [pkg, ...rest] = filtered
123
+ return pkg ? { pkg, args: rest } : null
124
+ }
125
+ if (command === 'pnpx' || command === 'bunx') {
126
+ const filtered = command === 'bunx' && args[0] === '--bun' ? args.slice(1) : args
127
+ const [pkg, ...rest] = filtered
128
+ return pkg ? { pkg, args: rest } : null
129
+ }
130
+ if ((command === 'pnpm' || command === 'yarn') && args[0] === 'dlx') {
131
+ const [, pkg, ...rest] = args
132
+ return pkg ? { pkg, args: rest } : null
133
+ }
134
+ if (command === 'deno' && args[0] === 'run' && args[1]?.startsWith('npm:')) {
135
+ const pkg = args[1].slice(4)
136
+ return pkg ? { pkg, args: args.slice(2) } : null
137
+ }
138
+ return null
139
+ }
140
+
104
141
  export function packageManagerExecute(
105
142
  environment: Environment,
106
143
  cwd: string,
@@ -4,6 +4,7 @@ import { format } from 'prettier'
4
4
 
5
5
  import { formatCommand } from './utils.js'
6
6
  import {
7
+ getPackageManagerExecuteCommand,
7
8
  getPackageManagerInstallCommand,
8
9
  getPackageManagerScriptCommand,
9
10
  } from './package-manager.js'
@@ -70,6 +71,14 @@ export function createTemplateFile(environment: Environment, options: Options) {
70
71
  ]),
71
72
  )
72
73
  }
74
+ function getPackageManagerExecuteScript(
75
+ pkg: string,
76
+ args: Array<string> = [],
77
+ ) {
78
+ return formatCommand(
79
+ getPackageManagerExecuteCommand(options.packageManager, pkg, args),
80
+ )
81
+ }
73
82
 
74
83
  class IgnoreFileError extends Error {
75
84
  constructor() {
@@ -135,6 +144,7 @@ export function createTemplateFile(environment: Environment, options: Options) {
135
144
 
136
145
  getPackageManagerAddScript,
137
146
  getPackageManagerRunScript,
147
+ getPackageManagerExecuteScript,
138
148
 
139
149
  relativePath: (path: string, stripExtension: boolean = false) =>
140
150
  relativePath(file, path, stripExtension),
@@ -142,6 +152,10 @@ export function createTemplateFile(environment: Environment, options: Options) {
142
152
  integrationImportContent,
143
153
  integrationImportCode,
144
154
 
155
+ renderTemplate: (content: string) => {
156
+ return render(content, templateValues)
157
+ },
158
+
145
159
  ignoreFile: () => {
146
160
  throw new IgnoreFileError()
147
161
  },
package/src/types.ts CHANGED
@@ -50,6 +50,7 @@ export const AddOnBaseSchema = z.object({
50
50
  'monitoring',
51
51
  'cms',
52
52
  'api',
53
+ 'analytics',
53
54
  'i18n',
54
55
  'tooling',
55
56
  'other',
@@ -119,6 +120,7 @@ export const AddOnInfoSchema = AddOnBaseSchema.extend({
119
120
  integrations: z.array(IntegrationSchema).optional(),
120
121
  phase: z.enum(['setup', 'add-on']),
121
122
  readme: z.string().optional(),
123
+ readmeIsEjs: z.boolean().optional(),
122
124
  })
123
125
 
124
126
  export const AddOnCompiledSchema = AddOnInfoSchema.extend({
package/src/utils.ts CHANGED
@@ -40,3 +40,11 @@ export function handleSpecialURL(url: string) {
40
40
  }
41
41
  return url
42
42
  }
43
+
44
+ export function hasDrive(path: string) {
45
+ return /^[A-Za-z]:/.test(path)
46
+ }
47
+
48
+ export function stripDrive(path: string) {
49
+ return path.replace(/^[A-Za-z]:/, '')
50
+ }
@@ -44,6 +44,12 @@ describe('toCleanPath', () => {
44
44
  toCleanPath('C:\\Projects\\my-app\\src\\file.ts', 'C:/Projects/my-app'),
45
45
  ).toBe('src/file.ts')
46
46
  })
47
+
48
+ it('should handle missing drive letter in path against Windows base', () => {
49
+ expect(
50
+ toCleanPath('/Users/me/my-app/src/file.ts', 'C:\\Users\\me\\my-app'),
51
+ ).toBe('src/file.ts')
52
+ })
47
53
  })
48
54
 
49
55
  describe('relativePath', () => {