@tanstack/cta-engine 0.27.1 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/add-ons.js +13 -0
- package/dist/add-to-app.js +15 -1
- package/dist/create-app.js +13 -3
- package/dist/custom-add-ons/shared.js +7 -4
- package/dist/environment.js +16 -5
- package/dist/frameworks.js +5 -0
- package/dist/index.js +1 -1
- package/dist/package-json.js +32 -2
- package/dist/special-steps/index.js +3 -1
- package/dist/special-steps/post-init-script.js +31 -0
- package/dist/template-file.js +7 -0
- package/dist/types/add-ons.d.ts +1 -0
- package/dist/types/custom-add-ons/add-on.d.ts +14 -3
- package/dist/types/index.d.ts +2 -2
- package/dist/types/registry.d.ts +8 -8
- package/dist/types/special-steps/post-init-script.d.ts +2 -0
- package/dist/types/types.d.ts +433 -32
- package/dist/types.js +17 -0
- package/package.json +1 -1
- package/src/add-ons.ts +18 -0
- package/src/add-to-app.ts +20 -1
- package/src/create-app.ts +18 -1
- package/src/custom-add-ons/shared.ts +7 -4
- package/src/environment.ts +15 -5
- package/src/frameworks.ts +6 -1
- package/src/index.ts +5 -1
- package/src/package-json.ts +40 -4
- package/src/special-steps/index.ts +3 -1
- package/src/special-steps/post-init-script.ts +52 -0
- package/src/template-file.ts +8 -0
- package/src/types.ts +37 -1
- package/tests/add-on-options.test.ts +332 -0
- package/tests/conditional-packages.test.ts +418 -0
- package/tests/filename-processing.test.ts +275 -0
- package/tests/package-json.test.ts +3 -3
- package/tests/special-steps.test.ts +165 -0
- package/tests/template-context.test.ts +314 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { populateAddOnOptionsDefaults } from '../src/add-ons.js'
|
|
3
|
+
import { AddOnOptionSchema, AddOnOptionsSchema, AddOnSelectOptionSchema } from '../src/types.js'
|
|
4
|
+
import type { AddOn } from '../src/types.js'
|
|
5
|
+
|
|
6
|
+
// Helper function to create test AddOn objects
|
|
7
|
+
function createTestAddOn(overrides: Partial<AddOn>): AddOn {
|
|
8
|
+
return {
|
|
9
|
+
id: 'test-addon',
|
|
10
|
+
name: 'Test Addon',
|
|
11
|
+
description: 'Test addon description',
|
|
12
|
+
type: 'add-on',
|
|
13
|
+
modes: ['file-router'],
|
|
14
|
+
phase: 'add-on',
|
|
15
|
+
files: {},
|
|
16
|
+
deletedFiles: [],
|
|
17
|
+
getFiles: () => Promise.resolve([]),
|
|
18
|
+
getFileContents: () => Promise.resolve(''),
|
|
19
|
+
getDeletedFiles: () => Promise.resolve([]),
|
|
20
|
+
...overrides
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('Add-on Options', () => {
|
|
25
|
+
describe('Option Schema Validation', () => {
|
|
26
|
+
it('should validate a valid select option', () => {
|
|
27
|
+
const validSelectOption = {
|
|
28
|
+
type: 'select',
|
|
29
|
+
label: 'Database Provider',
|
|
30
|
+
description: 'Choose your database provider',
|
|
31
|
+
default: 'postgres',
|
|
32
|
+
options: [
|
|
33
|
+
{ value: 'postgres', label: 'PostgreSQL' },
|
|
34
|
+
{ value: 'mysql', label: 'MySQL' },
|
|
35
|
+
{ value: 'sqlite', label: 'SQLite' }
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
expect(() => AddOnSelectOptionSchema.parse(validSelectOption)).not.toThrow()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should reject select option without required fields', () => {
|
|
43
|
+
const invalidSelectOption = {
|
|
44
|
+
type: 'select',
|
|
45
|
+
// Missing required 'label' field
|
|
46
|
+
options: [{ value: 'test', label: 'Test' }]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
expect(() => AddOnSelectOptionSchema.parse(invalidSelectOption)).toThrow()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('should reject select option with invalid option format', () => {
|
|
53
|
+
const invalidSelectOption = {
|
|
54
|
+
type: 'select',
|
|
55
|
+
label: 'Test',
|
|
56
|
+
options: [
|
|
57
|
+
{ value: 'test' } // Missing 'label' field
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
expect(() => AddOnSelectOptionSchema.parse(invalidSelectOption)).toThrow()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should reject select option with empty options array', () => {
|
|
65
|
+
const invalidSelectOption = {
|
|
66
|
+
type: 'select',
|
|
67
|
+
label: 'Test',
|
|
68
|
+
options: []
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
expect(() => AddOnSelectOptionSchema.parse(invalidSelectOption)).toThrow()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('should validate AddOnOption discriminated union', () => {
|
|
75
|
+
const validOption = {
|
|
76
|
+
type: 'select',
|
|
77
|
+
label: 'Theme',
|
|
78
|
+
default: 'dark',
|
|
79
|
+
options: [
|
|
80
|
+
{ value: 'dark', label: 'Dark' },
|
|
81
|
+
{ value: 'light', label: 'Light' }
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
expect(() => AddOnOptionSchema.parse(validOption)).not.toThrow()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('should validate AddOnOptions record', () => {
|
|
89
|
+
const validOptions = {
|
|
90
|
+
database: {
|
|
91
|
+
type: 'select',
|
|
92
|
+
label: 'Database Provider',
|
|
93
|
+
default: 'postgres',
|
|
94
|
+
options: [
|
|
95
|
+
{ value: 'postgres', label: 'PostgreSQL' },
|
|
96
|
+
{ value: 'mysql', label: 'MySQL' }
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
theme: {
|
|
100
|
+
type: 'select',
|
|
101
|
+
label: 'Theme',
|
|
102
|
+
default: 'dark',
|
|
103
|
+
options: [
|
|
104
|
+
{ value: 'dark', label: 'Dark' },
|
|
105
|
+
{ value: 'light', label: 'Light' }
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
expect(() => AddOnOptionsSchema.parse(validOptions)).not.toThrow()
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe('populateAddOnOptionsDefaults', () => {
|
|
115
|
+
it('should populate defaults for add-ons with options', () => {
|
|
116
|
+
const addOns = [
|
|
117
|
+
createTestAddOn({
|
|
118
|
+
id: 'testAddon',
|
|
119
|
+
name: 'Test Addon',
|
|
120
|
+
options: {
|
|
121
|
+
database: {
|
|
122
|
+
type: 'select' as const,
|
|
123
|
+
label: 'Database Provider',
|
|
124
|
+
default: 'postgres',
|
|
125
|
+
options: [
|
|
126
|
+
{ value: 'postgres', label: 'PostgreSQL' },
|
|
127
|
+
{ value: 'mysql', label: 'MySQL' },
|
|
128
|
+
{ value: 'sqlite', label: 'SQLite' }
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}),
|
|
133
|
+
createTestAddOn({
|
|
134
|
+
id: 'shadcn',
|
|
135
|
+
name: 'shadcn/ui',
|
|
136
|
+
options: {
|
|
137
|
+
theme: {
|
|
138
|
+
type: 'select' as const,
|
|
139
|
+
label: 'Theme',
|
|
140
|
+
default: 'neutral',
|
|
141
|
+
options: [
|
|
142
|
+
{ value: 'neutral', label: 'Neutral' },
|
|
143
|
+
{ value: 'slate', label: 'Slate' }
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
const result = populateAddOnOptionsDefaults(addOns)
|
|
151
|
+
|
|
152
|
+
expect(result).toEqual({
|
|
153
|
+
testAddon: {
|
|
154
|
+
database: 'postgres'
|
|
155
|
+
},
|
|
156
|
+
shadcn: {
|
|
157
|
+
theme: 'neutral'
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('should handle add-ons without options', () => {
|
|
163
|
+
const addOns = [
|
|
164
|
+
createTestAddOn({
|
|
165
|
+
id: 'simple-addon',
|
|
166
|
+
name: 'Simple Add-on'
|
|
167
|
+
// No options property
|
|
168
|
+
})
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
const result = populateAddOnOptionsDefaults(addOns)
|
|
172
|
+
|
|
173
|
+
expect(result).toEqual({})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('should only populate defaults for enabled add-ons', () => {
|
|
177
|
+
const addOns = [
|
|
178
|
+
createTestAddOn({
|
|
179
|
+
id: 'testAddon',
|
|
180
|
+
name: 'Test Addon',
|
|
181
|
+
options: {
|
|
182
|
+
database: {
|
|
183
|
+
type: 'select' as const,
|
|
184
|
+
label: 'Database Provider',
|
|
185
|
+
default: 'postgres',
|
|
186
|
+
options: [
|
|
187
|
+
{ value: 'postgres', label: 'PostgreSQL' },
|
|
188
|
+
{ value: 'mysql', label: 'MySQL' }
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}),
|
|
193
|
+
createTestAddOn({
|
|
194
|
+
id: 'shadcn',
|
|
195
|
+
name: 'shadcn/ui',
|
|
196
|
+
options: {
|
|
197
|
+
theme: {
|
|
198
|
+
type: 'select' as const,
|
|
199
|
+
label: 'Theme',
|
|
200
|
+
default: 'neutral',
|
|
201
|
+
options: [
|
|
202
|
+
{ value: 'neutral', label: 'Neutral' },
|
|
203
|
+
{ value: 'slate', label: 'Slate' }
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
]
|
|
209
|
+
|
|
210
|
+
const enabledAddOns = [addOns[0]] // Only testAddon
|
|
211
|
+
const result = populateAddOnOptionsDefaults(enabledAddOns)
|
|
212
|
+
|
|
213
|
+
expect(result).toEqual({
|
|
214
|
+
testAddon: {
|
|
215
|
+
database: 'postgres'
|
|
216
|
+
}
|
|
217
|
+
// shadcn should not be included
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('should handle empty enabled add-ons array', () => {
|
|
222
|
+
const enabledAddOns: Array<any> = []
|
|
223
|
+
const result = populateAddOnOptionsDefaults(enabledAddOns)
|
|
224
|
+
|
|
225
|
+
expect(result).toEqual({})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('should handle add-ons with multiple options', () => {
|
|
229
|
+
const addOns = [
|
|
230
|
+
createTestAddOn({
|
|
231
|
+
id: 'complex-addon',
|
|
232
|
+
name: 'Complex Add-on',
|
|
233
|
+
options: {
|
|
234
|
+
database: {
|
|
235
|
+
type: 'select' as const,
|
|
236
|
+
label: 'Database',
|
|
237
|
+
default: 'postgres',
|
|
238
|
+
options: [
|
|
239
|
+
{ value: 'postgres', label: 'PostgreSQL' },
|
|
240
|
+
{ value: 'mysql', label: 'MySQL' }
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
theme: {
|
|
244
|
+
type: 'select' as const,
|
|
245
|
+
label: 'Theme',
|
|
246
|
+
default: 'dark',
|
|
247
|
+
options: [
|
|
248
|
+
{ value: 'dark', label: 'Dark' },
|
|
249
|
+
{ value: 'light', label: 'Light' }
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
const result = populateAddOnOptionsDefaults(addOns)
|
|
257
|
+
|
|
258
|
+
expect(result).toEqual({
|
|
259
|
+
'complex-addon': {
|
|
260
|
+
database: 'postgres',
|
|
261
|
+
theme: 'dark'
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('should handle options without default values', () => {
|
|
267
|
+
const addOns = [
|
|
268
|
+
createTestAddOn({
|
|
269
|
+
id: 'no-default',
|
|
270
|
+
name: 'No Default Add-on',
|
|
271
|
+
options: {
|
|
272
|
+
database: {
|
|
273
|
+
type: 'select' as const,
|
|
274
|
+
label: 'Database',
|
|
275
|
+
default: 'postgres', // We need a default for valid schema
|
|
276
|
+
options: [
|
|
277
|
+
{ value: 'postgres', label: 'PostgreSQL' },
|
|
278
|
+
{ value: 'mysql', label: 'MySQL' }
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
]
|
|
284
|
+
|
|
285
|
+
// Test the case where an addon has no default by manually modifying the option
|
|
286
|
+
if (addOns[0].options?.database) {
|
|
287
|
+
delete (addOns[0].options.database as any).default
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const result = populateAddOnOptionsDefaults(addOns)
|
|
291
|
+
|
|
292
|
+
expect(result).toEqual({
|
|
293
|
+
'no-default': {
|
|
294
|
+
database: undefined
|
|
295
|
+
}
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
describe('Error Handling', () => {
|
|
301
|
+
it('should handle malformed option definitions gracefully', () => {
|
|
302
|
+
const malformedOptions = {
|
|
303
|
+
invalid: {
|
|
304
|
+
type: 'unknown-type', // Invalid type
|
|
305
|
+
label: 'Test'
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
expect(() => AddOnOptionsSchema.parse(malformedOptions)).toThrow()
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
it('should validate option value types', () => {
|
|
313
|
+
const invalidOption = {
|
|
314
|
+
type: 'select',
|
|
315
|
+
label: 123, // Should be string
|
|
316
|
+
options: [{ value: 'test', label: 'Test' }]
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
expect(() => AddOnSelectOptionSchema.parse(invalidOption)).toThrow()
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('should require non-empty option arrays', () => {
|
|
323
|
+
const emptyOptionsArray = {
|
|
324
|
+
type: 'select',
|
|
325
|
+
label: 'Test',
|
|
326
|
+
options: []
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
expect(() => AddOnSelectOptionSchema.parse(emptyOptionsArray)).toThrow()
|
|
330
|
+
})
|
|
331
|
+
})
|
|
332
|
+
})
|