@tanstack/cli 0.0.2 → 0.0.4

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.
@@ -5,6 +5,7 @@ import { basename, dirname, extname, join, resolve } from "node:path";
5
5
  import { z } from "zod";
6
6
  import ignore from "ignore";
7
7
  import parseGitignore from "parse-gitignore";
8
+ import { homedir } from "node:os";
8
9
 
9
10
  //#region src/engine/template.ts
10
11
  /**
@@ -1354,6 +1355,7 @@ ${packageManager}${packageManager === "npm" ? " run" : ""} dev
1354
1355
  - [TanStack Start Documentation](https://tanstack.com/start)
1355
1356
  - [TanStack Router Documentation](https://tanstack.com/router)
1356
1357
  `;
1358
+ files[".nvmrc"] = "23";
1357
1359
  return files;
1358
1360
  }
1359
1361
  /**
@@ -1461,8 +1463,8 @@ function buildPackageJson(options, packages) {
1461
1463
  "@tanstack/react-router": "^1.132.0",
1462
1464
  "@tanstack/react-router-devtools": "^1.132.0",
1463
1465
  "@tanstack/react-start": "^1.132.0",
1464
- react: "^19.0.0",
1465
- "react-dom": "^19.0.0",
1466
+ react: "^19.2.0",
1467
+ "react-dom": "^19.2.0",
1466
1468
  "vite-tsconfig-paths": "^5.1.4",
1467
1469
  ...hasHeader ? { "lucide-react": "^0.468.0" } : {},
1468
1470
  ...packages.dependencies
@@ -1471,8 +1473,8 @@ function buildPackageJson(options, packages) {
1471
1473
  "@vitejs/plugin-react": "^4.4.1",
1472
1474
  vite: "^7.0.0",
1473
1475
  ...options.typescript ? {
1474
- "@types/react": "^19.0.0",
1475
- "@types/react-dom": "^19.0.0",
1476
+ "@types/react": "^19.2.0",
1477
+ "@types/react-dom": "^19.2.0",
1476
1478
  typescript: "^5.7.0"
1477
1479
  } : {},
1478
1480
  ...options.tailwind ? {
@@ -1896,7 +1898,7 @@ const IntegrationInfoSchema = z.object({
1896
1898
  requiresTailwind: z.boolean().optional(),
1897
1899
  demoRequiresTailwind: z.boolean().optional(),
1898
1900
  dependsOn: z.array(z.string()).optional(),
1899
- conflicts: z.array(z.string()).optional(),
1901
+ exclusive: z.array(z.string()).optional(),
1900
1902
  partnerId: z.string().optional(),
1901
1903
  options: IntegrationOptionsSchema.optional(),
1902
1904
  hooks: z.array(HookSchema).optional(),
@@ -1943,7 +1945,7 @@ const ManifestIntegrationSchema = z.object({
1943
1945
  category: CategorySchema.optional(),
1944
1946
  modes: z.array(RouterModeSchema),
1945
1947
  dependsOn: z.array(z.string()).optional(),
1946
- conflicts: z.array(z.string()).optional(),
1948
+ exclusive: z.array(z.string()).optional(),
1947
1949
  partnerId: z.string().optional(),
1948
1950
  hasOptions: z.boolean().optional(),
1949
1951
  link: z.string().optional(),
@@ -1966,9 +1968,50 @@ const ManifestSchema = z.object({
1966
1968
  customTemplates: z.array(ManifestCustomTemplateSchema).optional()
1967
1969
  });
1968
1970
 
1971
+ //#endregion
1972
+ //#region src/cache/index.ts
1973
+ const CACHE_DIR = join(homedir(), ".tanstack", "cache");
1974
+ const DEFAULT_TTL_MS = 1440 * 60 * 1e3;
1975
+ function ensureCacheDir() {
1976
+ if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR, { recursive: true });
1977
+ }
1978
+ function getCachePath(key) {
1979
+ return join(CACHE_DIR, `${key.replace(/[^a-zA-Z0-9-_]/g, "_")}.json`);
1980
+ }
1981
+ function getCached(key) {
1982
+ const cachePath = getCachePath(key);
1983
+ if (!existsSync(cachePath)) return null;
1984
+ try {
1985
+ const raw = readFileSync(cachePath, "utf-8");
1986
+ const entry = JSON.parse(raw);
1987
+ if (Date.now() - entry.timestamp > entry.ttl) return null;
1988
+ return entry.data;
1989
+ } catch {
1990
+ return null;
1991
+ }
1992
+ }
1993
+ function setCache(key, data, ttlMs = DEFAULT_TTL_MS) {
1994
+ ensureCacheDir();
1995
+ const cachePath = getCachePath(key);
1996
+ const entry = {
1997
+ data,
1998
+ timestamp: Date.now(),
1999
+ ttl: ttlMs
2000
+ };
2001
+ writeFileSync(cachePath, JSON.stringify(entry, null, 2), "utf-8");
2002
+ }
2003
+ async function fetchWithCache(key, fetcher, ttlMs = DEFAULT_TTL_MS) {
2004
+ const cached = getCached(key);
2005
+ if (cached !== null) return cached;
2006
+ const data = await fetcher();
2007
+ setCache(key, data, ttlMs);
2008
+ return data;
2009
+ }
2010
+
1969
2011
  //#endregion
1970
2012
  //#region src/api/fetch.ts
1971
2013
  const GITHUB_RAW_BASE = "https://raw.githubusercontent.com/TanStack/cli/main/integrations";
2014
+ const CACHE_TTL_MS = 3600 * 1e3;
1972
2015
  /**
1973
2016
  * Check if a path is a local directory
1974
2017
  */
@@ -1976,36 +2019,40 @@ function isLocalPath(path) {
1976
2019
  return path.startsWith("/") || path.startsWith("./") || path.startsWith("..");
1977
2020
  }
1978
2021
  /**
1979
- * Fetch the integration manifest from GitHub or local path
2022
+ * Fetch the integration manifest from GitHub or local path (with caching for remote)
1980
2023
  */
1981
2024
  async function fetchManifest(baseUrl = GITHUB_RAW_BASE) {
1982
2025
  if (isLocalPath(baseUrl)) {
1983
2026
  const manifestPath = join(baseUrl, "manifest.json");
1984
2027
  if (!existsSync(manifestPath)) throw new Error(`Manifest not found at ${manifestPath}`);
1985
- const data$1 = JSON.parse(readFileSync(manifestPath, "utf-8"));
1986
- return ManifestSchema.parse(data$1);
2028
+ const data = JSON.parse(readFileSync(manifestPath, "utf-8"));
2029
+ return ManifestSchema.parse(data);
1987
2030
  }
1988
- const url = `${baseUrl}/manifest.json`;
1989
- const response = await fetch(url);
1990
- if (!response.ok) throw new Error(`Failed to fetch manifest: ${response.statusText}`);
1991
- const data = await response.json();
1992
- return ManifestSchema.parse(data);
2031
+ return fetchWithCache(`manifest_${baseUrl.replace(/[^a-zA-Z0-9]/g, "_")}`, async () => {
2032
+ const url = `${baseUrl}/manifest.json`;
2033
+ const response = await fetch(url);
2034
+ if (!response.ok) throw new Error(`Failed to fetch manifest: ${response.statusText}`);
2035
+ const data = await response.json();
2036
+ return ManifestSchema.parse(data);
2037
+ }, CACHE_TTL_MS);
1993
2038
  }
1994
2039
  /**
1995
- * Fetch integration info.json from GitHub or local path
2040
+ * Fetch integration info.json from GitHub or local path (with caching for remote)
1996
2041
  */
1997
2042
  async function fetchIntegrationInfo(integrationId, baseUrl = GITHUB_RAW_BASE) {
1998
2043
  if (isLocalPath(baseUrl)) {
1999
2044
  const infoPath = join(baseUrl, integrationId, "info.json");
2000
2045
  if (!existsSync(infoPath)) throw new Error(`Integration info not found at ${infoPath}`);
2001
- const data$1 = JSON.parse(readFileSync(infoPath, "utf-8"));
2002
- return IntegrationInfoSchema.parse(data$1);
2046
+ const data = JSON.parse(readFileSync(infoPath, "utf-8"));
2047
+ return IntegrationInfoSchema.parse(data);
2003
2048
  }
2004
- const url = `${baseUrl}/${integrationId}/info.json`;
2005
- const response = await fetch(url);
2006
- if (!response.ok) throw new Error(`Failed to fetch integration ${integrationId}: ${response.statusText}`);
2007
- const data = await response.json();
2008
- return IntegrationInfoSchema.parse(data);
2049
+ return fetchWithCache(`integration_info_${integrationId}_${baseUrl.replace(/[^a-zA-Z0-9]/g, "_")}`, async () => {
2050
+ const url = `${baseUrl}/${integrationId}/info.json`;
2051
+ const response = await fetch(url);
2052
+ if (!response.ok) throw new Error(`Failed to fetch integration ${integrationId}: ${response.statusText}`);
2053
+ const data = await response.json();
2054
+ return IntegrationInfoSchema.parse(data);
2055
+ }, CACHE_TTL_MS);
2009
2056
  }
2010
2057
  /**
2011
2058
  * Recursively read all files from a directory
@@ -2022,21 +2069,23 @@ function readDirRecursive(dir, basePath = "") {
2022
2069
  return files;
2023
2070
  }
2024
2071
  /**
2025
- * Fetch all files for an integration from GitHub or local path
2072
+ * Fetch all files for an integration from GitHub or local path (with caching for remote)
2026
2073
  */
2027
2074
  async function fetchIntegrationFiles(integrationId, baseUrl = GITHUB_RAW_BASE) {
2028
2075
  if (isLocalPath(baseUrl)) return readDirRecursive(join(baseUrl, integrationId, "assets"));
2029
- const filesUrl = `${baseUrl}/${integrationId}/files.json`;
2030
- const response = await fetch(filesUrl);
2031
- if (!response.ok) return {};
2032
- const fileList = await response.json();
2033
- const files = {};
2034
- await Promise.all(fileList.map(async (filePath) => {
2035
- const fileUrl = `${baseUrl}/${integrationId}/assets/${filePath}`;
2036
- const fileResponse = await fetch(fileUrl);
2037
- if (fileResponse.ok) files[filePath] = await fileResponse.text();
2038
- }));
2039
- return files;
2076
+ return fetchWithCache(`integration_files_${integrationId}_${baseUrl.replace(/[^a-zA-Z0-9]/g, "_")}`, async () => {
2077
+ const filesUrl = `${baseUrl}/${integrationId}/files.json`;
2078
+ const response = await fetch(filesUrl);
2079
+ if (!response.ok) return {};
2080
+ const fileList = await response.json();
2081
+ const files = {};
2082
+ await Promise.all(fileList.map(async (filePath) => {
2083
+ const fileUrl = `${baseUrl}/${integrationId}/assets/${filePath}`;
2084
+ const fileResponse = await fetch(fileUrl);
2085
+ if (fileResponse.ok) files[filePath] = await fileResponse.text();
2086
+ }));
2087
+ return files;
2088
+ }, CACHE_TTL_MS);
2040
2089
  }
2041
2090
  /**
2042
2091
  * Fetch integration package.json if it exists
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/cli",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "TanStack CLI for scaffolding and tooling",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -1,11 +1,11 @@
1
1
  import { resolve } from 'node:path'
2
2
  import { describe, expect, it } from 'vitest'
3
3
  import {
4
- fetchManifest,
5
4
  fetchIntegration,
6
- fetchIntegrations,
7
- fetchIntegrationInfo,
8
5
  fetchIntegrationFiles,
6
+ fetchIntegrationInfo,
7
+ fetchIntegrations,
8
+ fetchManifest,
9
9
  } from './fetch.js'
10
10
 
11
11
  const INTEGRATIONS_PATH = resolve(__dirname, '../../../../integrations')
package/src/api/fetch.ts CHANGED
@@ -5,11 +5,15 @@ import {
5
5
  IntegrationInfoSchema,
6
6
  ManifestSchema
7
7
  } from '../engine/types.js'
8
+ import { fetchWithCache } from '../cache/index.js'
8
9
  import type { IntegrationCompiled, IntegrationInfo, Manifest } from '../engine/types.js'
9
10
 
10
11
  const GITHUB_RAW_BASE =
11
12
  'https://raw.githubusercontent.com/TanStack/cli/main/integrations'
12
13
 
14
+ // 1 hour cache TTL for remote fetches
15
+ const CACHE_TTL_MS = 60 * 60 * 1000
16
+
13
17
  /**
14
18
  * Check if a path is a local directory
15
19
  */
@@ -18,7 +22,7 @@ function isLocalPath(path: string): boolean {
18
22
  }
19
23
 
20
24
  /**
21
- * Fetch the integration manifest from GitHub or local path
25
+ * Fetch the integration manifest from GitHub or local path (with caching for remote)
22
26
  */
23
27
  export async function fetchManifest(
24
28
  baseUrl: string = GITHUB_RAW_BASE,
@@ -32,19 +36,27 @@ export async function fetchManifest(
32
36
  return ManifestSchema.parse(data)
33
37
  }
34
38
 
35
- const url = `${baseUrl}/manifest.json`
36
- const response = await fetch(url)
39
+ const cacheKey = `manifest_${baseUrl.replace(/[^a-zA-Z0-9]/g, '_')}`
37
40
 
38
- if (!response.ok) {
39
- throw new Error(`Failed to fetch manifest: ${response.statusText}`)
40
- }
41
+ return fetchWithCache(
42
+ cacheKey,
43
+ async () => {
44
+ const url = `${baseUrl}/manifest.json`
45
+ const response = await fetch(url)
46
+
47
+ if (!response.ok) {
48
+ throw new Error(`Failed to fetch manifest: ${response.statusText}`)
49
+ }
41
50
 
42
- const data = await response.json()
43
- return ManifestSchema.parse(data)
51
+ const data = await response.json()
52
+ return ManifestSchema.parse(data)
53
+ },
54
+ CACHE_TTL_MS,
55
+ )
44
56
  }
45
57
 
46
58
  /**
47
- * Fetch integration info.json from GitHub or local path
59
+ * Fetch integration info.json from GitHub or local path (with caching for remote)
48
60
  */
49
61
  export async function fetchIntegrationInfo(
50
62
  integrationId: string,
@@ -59,15 +71,23 @@ export async function fetchIntegrationInfo(
59
71
  return IntegrationInfoSchema.parse(data)
60
72
  }
61
73
 
62
- const url = `${baseUrl}/${integrationId}/info.json`
63
- const response = await fetch(url)
74
+ const cacheKey = `integration_info_${integrationId}_${baseUrl.replace(/[^a-zA-Z0-9]/g, '_')}`
64
75
 
65
- if (!response.ok) {
66
- throw new Error(`Failed to fetch integration ${integrationId}: ${response.statusText}`)
67
- }
76
+ return fetchWithCache(
77
+ cacheKey,
78
+ async () => {
79
+ const url = `${baseUrl}/${integrationId}/info.json`
80
+ const response = await fetch(url)
81
+
82
+ if (!response.ok) {
83
+ throw new Error(`Failed to fetch integration ${integrationId}: ${response.statusText}`)
84
+ }
68
85
 
69
- const data = await response.json()
70
- return IntegrationInfoSchema.parse(data)
86
+ const data = await response.json()
87
+ return IntegrationInfoSchema.parse(data)
88
+ },
89
+ CACHE_TTL_MS,
90
+ )
71
91
  }
72
92
 
73
93
  /**
@@ -97,7 +117,7 @@ function readDirRecursive(
97
117
  }
98
118
 
99
119
  /**
100
- * Fetch all files for an integration from GitHub or local path
120
+ * Fetch all files for an integration from GitHub or local path (with caching for remote)
101
121
  */
102
122
  export async function fetchIntegrationFiles(
103
123
  integrationId: string,
@@ -108,31 +128,39 @@ export async function fetchIntegrationFiles(
108
128
  return readDirRecursive(assetsPath)
109
129
  }
110
130
 
111
- // First fetch the file list (we'll need a files.json or similar)
112
- const filesUrl = `${baseUrl}/${integrationId}/files.json`
113
- const response = await fetch(filesUrl)
131
+ const cacheKey = `integration_files_${integrationId}_${baseUrl.replace(/[^a-zA-Z0-9]/g, '_')}`
114
132
 
115
- if (!response.ok) {
116
- // No files.json, return empty
117
- return {}
118
- }
133
+ return fetchWithCache(
134
+ cacheKey,
135
+ async () => {
136
+ // First fetch the file list (we'll need a files.json or similar)
137
+ const filesUrl = `${baseUrl}/${integrationId}/files.json`
138
+ const response = await fetch(filesUrl)
119
139
 
120
- const fileList: Array<string> = await response.json()
121
- const files: Record<string, string> = {}
140
+ if (!response.ok) {
141
+ // No files.json, return empty
142
+ return {}
143
+ }
122
144
 
123
- // Fetch each file
124
- await Promise.all(
125
- fileList.map(async (filePath) => {
126
- const fileUrl = `${baseUrl}/${integrationId}/assets/${filePath}`
127
- const fileResponse = await fetch(fileUrl)
145
+ const fileList: Array<string> = await response.json()
146
+ const files: Record<string, string> = {}
128
147
 
129
- if (fileResponse.ok) {
130
- files[filePath] = await fileResponse.text()
131
- }
132
- }),
133
- )
148
+ // Fetch each file
149
+ await Promise.all(
150
+ fileList.map(async (filePath) => {
151
+ const fileUrl = `${baseUrl}/${integrationId}/assets/${filePath}`
152
+ const fileResponse = await fetch(fileUrl)
134
153
 
135
- return files
154
+ if (fileResponse.ok) {
155
+ files[filePath] = await fileResponse.text()
156
+ }
157
+ }),
158
+ )
159
+
160
+ return files
161
+ },
162
+ CACHE_TTL_MS,
163
+ )
136
164
  }
137
165
 
138
166
  /**
@@ -0,0 +1,89 @@
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import { homedir } from 'node:os'
4
+
5
+ const CACHE_DIR = join(homedir(), '.tanstack', 'cache')
6
+ const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours
7
+
8
+ interface CacheEntry<T> {
9
+ data: T
10
+ timestamp: number
11
+ ttl: number
12
+ }
13
+
14
+ function ensureCacheDir() {
15
+ if (!existsSync(CACHE_DIR)) {
16
+ mkdirSync(CACHE_DIR, { recursive: true })
17
+ }
18
+ }
19
+
20
+ function getCachePath(key: string): string {
21
+ // Sanitize key to be filesystem-safe
22
+ const safeKey = key.replace(/[^a-zA-Z0-9-_]/g, '_')
23
+ return join(CACHE_DIR, `${safeKey}.json`)
24
+ }
25
+
26
+ export function getCached<T>(key: string): T | null {
27
+ const cachePath = getCachePath(key)
28
+
29
+ if (!existsSync(cachePath)) {
30
+ return null
31
+ }
32
+
33
+ try {
34
+ const raw = readFileSync(cachePath, 'utf-8')
35
+ const entry: CacheEntry<T> = JSON.parse(raw)
36
+
37
+ const age = Date.now() - entry.timestamp
38
+ if (age > entry.ttl) {
39
+ return null // Expired
40
+ }
41
+
42
+ return entry.data
43
+ } catch {
44
+ return null
45
+ }
46
+ }
47
+
48
+ export function setCache<T>(key: string, data: T, ttlMs: number = DEFAULT_TTL_MS): void {
49
+ ensureCacheDir()
50
+ const cachePath = getCachePath(key)
51
+
52
+ const entry: CacheEntry<T> = {
53
+ data,
54
+ timestamp: Date.now(),
55
+ ttl: ttlMs,
56
+ }
57
+
58
+ writeFileSync(cachePath, JSON.stringify(entry, null, 2), 'utf-8')
59
+ }
60
+
61
+ export async function fetchWithCache<T>(
62
+ key: string,
63
+ fetcher: () => Promise<T>,
64
+ ttlMs: number = DEFAULT_TTL_MS,
65
+ ): Promise<T> {
66
+ // Try cache first
67
+ const cached = getCached<T>(key)
68
+ if (cached !== null) {
69
+ return cached
70
+ }
71
+
72
+ // Fetch fresh data
73
+ const data = await fetcher()
74
+
75
+ // Cache it
76
+ setCache(key, data, ttlMs)
77
+
78
+ return data
79
+ }
80
+
81
+ export function clearCache(): void {
82
+ if (existsSync(CACHE_DIR)) {
83
+ rmSync(CACHE_DIR, { recursive: true, force: true })
84
+ }
85
+ }
86
+
87
+ export function getCacheDir(): string {
88
+ return CACHE_DIR
89
+ }
@@ -19,8 +19,8 @@ import { compile } from '../engine/compile.js'
19
19
  import { writeConfigFile } from '../engine/config-file.js'
20
20
  import { loadTemplate } from '../engine/custom-addons/template.js'
21
21
  import type {
22
- IntegrationCompiled,
23
22
  CustomTemplateCompiled,
23
+ IntegrationCompiled,
24
24
  ManifestIntegration,
25
25
  PackageManager,
26
26
  RouterMode,
@@ -32,7 +32,7 @@ describe('MCP server tools logic', () => {
32
32
  description: integration.description,
33
33
  category: integration.category,
34
34
  dependsOn: integration.dependsOn,
35
- conflicts: integration.conflicts,
35
+ exclusive: integration.exclusive,
36
36
  hasOptions: integration.hasOptions,
37
37
  }))
38
38
 
@@ -44,7 +44,7 @@ describe('MCP server tools logic', () => {
44
44
  expect(query!.category).toBe('tanstack')
45
45
  })
46
46
 
47
- it('should include dependency and conflict info', async () => {
47
+ it('should include dependency and exclusive type info', async () => {
48
48
  const manifest = await fetchManifest(INTEGRATIONS_PATH)
49
49
  const integrations = manifest.integrations.filter((a) =>
50
50
  a.modes.includes('file-router'),
@@ -54,7 +54,7 @@ describe('MCP server tools logic', () => {
54
54
  expect(trpc?.dependsOn).toContain('tanstack-query')
55
55
 
56
56
  const clerk = integrations.find((i) => i.id === 'clerk')
57
- expect(clerk?.conflicts).toContain('better-auth')
57
+ expect(clerk?.exclusive).toContain('auth')
58
58
  })
59
59
  })
60
60
 
@@ -5,6 +5,7 @@ import express from 'express'
5
5
  import { z } from 'zod'
6
6
  import { fetchIntegrations, fetchManifest } from '../api/fetch.js'
7
7
  import { compile } from '../engine/compile.js'
8
+ import { registerDocTools } from '../mcp/tools.js'
8
9
  import type { RouterMode } from '../engine/types.js'
9
10
 
10
11
  interface McpOptions {
@@ -33,7 +34,7 @@ function createServer() {
33
34
  description: integration.description,
34
35
  category: integration.category,
35
36
  dependsOn: integration.dependsOn,
36
- conflicts: integration.conflicts,
37
+ exclusive: integration.exclusive,
37
38
  hasOptions: integration.hasOptions,
38
39
  }))
39
40
 
@@ -58,6 +59,9 @@ function createServer() {
58
59
  },
59
60
  )
60
61
 
62
+ // Register documentation/ecosystem tools
63
+ registerDocTools(server)
64
+
61
65
  server.tool(
62
66
  'createTanStackApplication',
63
67
  'Create a new TanStack Start application',
@@ -2,7 +2,7 @@ import { resolve } from 'node:path'
2
2
  import { beforeAll, describe, expect, it } from 'vitest'
3
3
  import { fetchIntegration, fetchIntegrations } from '../api/fetch.js'
4
4
  import { compile } from './compile.js'
5
- import type { IntegrationCompiled, CompileOptions } from './types.js'
5
+ import type { CompileOptions, IntegrationCompiled } from './types.js'
6
6
 
7
7
  const INTEGRATIONS_PATH = resolve(__dirname, '../../../../integrations')
8
8
 
@@ -214,16 +214,16 @@ describe('compile with real integrations', () => {
214
214
  })
215
215
  })
216
216
 
217
- describe('Deployment integration (Vercel)', () => {
218
- let vercelIntegration: IntegrationCompiled
217
+ describe('Deployment integration (Railway)', () => {
218
+ let railwayIntegration: IntegrationCompiled
219
219
 
220
220
  beforeAll(async () => {
221
- vercelIntegration = await fetchIntegration('vercel', INTEGRATIONS_PATH)
221
+ railwayIntegration = await fetchIntegration('railway', INTEGRATIONS_PATH)
222
222
  })
223
223
 
224
- it('should load vercel integration', () => {
225
- expect(vercelIntegration.id).toBe('vercel')
226
- expect(vercelIntegration.type).toBe('deployment')
224
+ it('should load railway integration', () => {
225
+ expect(railwayIntegration.id).toBe('railway')
226
+ expect(railwayIntegration.type).toBe('deployment')
227
227
  })
228
228
  })
229
229
  })
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
  import { compile, compileWithAttribution } from './compile.js'
3
- import type { IntegrationCompiled, CompileOptions, CustomTemplateCompiled } from './types.js'
3
+ import type { CompileOptions, CustomTemplateCompiled, IntegrationCompiled } from './types.js'
4
4
 
5
5
  const baseOptions: CompileOptions = {
6
6
  projectName: 'test-project',
@@ -1,12 +1,12 @@
1
1
  import { getBaseFiles, getBaseFilesWithAttribution } from '../templates/base.js'
2
2
  import { processTemplateFile } from './template.js'
3
3
  import type {
4
- IntegrationCompiled,
5
- IntegrationPhase,
6
4
  AttributedCompileOutput,
7
5
  CompileOptions,
8
6
  CompileOutput,
9
7
  EnvVar,
8
+ IntegrationCompiled,
9
+ IntegrationPhase,
10
10
  LineAttribution,
11
11
  } from './types.js'
12
12
 
@@ -130,8 +130,8 @@ function buildPackageJson(
130
130
  '@tanstack/react-router': '^1.132.0',
131
131
  '@tanstack/react-router-devtools': '^1.132.0',
132
132
  '@tanstack/react-start': '^1.132.0',
133
- react: '^19.0.0',
134
- 'react-dom': '^19.0.0',
133
+ react: '^19.2.0',
134
+ 'react-dom': '^19.2.0',
135
135
  'vite-tsconfig-paths': '^5.1.4',
136
136
  ...(hasHeader ? { 'lucide-react': '^0.468.0' } : {}),
137
137
  ...packages.dependencies,
@@ -141,8 +141,8 @@ function buildPackageJson(
141
141
  vite: '^7.0.0',
142
142
  ...(options.typescript
143
143
  ? {
144
- '@types/react': '^19.0.0',
145
- '@types/react-dom': '^19.0.0',
144
+ '@types/react': '^19.2.0',
145
+ '@types/react-dom': '^19.2.0',
146
146
  typescript: '^5.7.0',
147
147
  }
148
148
  : {}),
@@ -13,7 +13,7 @@ import { readConfigFile } from '../config-file.js'
13
13
  import { fetchIntegrations } from '../../api/fetch.js'
14
14
 
15
15
  import type { PersistedOptions } from '../config-file.js'
16
- import type { IntegrationCompiled, CompileOptions, CompileOutput } from '../types.js'
16
+ import type { CompileOptions, CompileOutput, IntegrationCompiled } from '../types.js'
17
17
 
18
18
  // Files to always ignore (from Jack's IGNORE_FILES)
19
19
  const IGNORE_FILES = [
@@ -1,8 +1,8 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
  import {
3
- relativePath,
4
3
  createTemplateContext,
5
4
  processTemplateFile,
5
+ relativePath,
6
6
  } from './template.js'
7
7
  import type { CompileOptions } from './types.js'
8
8
 
@@ -1,8 +1,8 @@
1
1
  import { render } from 'ejs'
2
2
  import type {
3
- IntegrationCompiled,
4
3
  CompileOptions,
5
4
  Hook,
5
+ IntegrationCompiled,
6
6
  Route,
7
7
  } from './types.js'
8
8