@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,418 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { createPackageJSON, mergePackageJSON } from '../src/package-json.js'
|
|
4
|
+
import type { Options, Framework } from '../src/types.js'
|
|
5
|
+
|
|
6
|
+
describe('Conditional Package Dependencies', () => {
|
|
7
|
+
const baseFramework = {
|
|
8
|
+
basePackageJSON: {
|
|
9
|
+
version: '1.0.0',
|
|
10
|
+
type: 'module'
|
|
11
|
+
},
|
|
12
|
+
optionalPackages: {}
|
|
13
|
+
} as unknown as Framework
|
|
14
|
+
|
|
15
|
+
const baseOptions = {
|
|
16
|
+
projectName: 'test-app',
|
|
17
|
+
framework: baseFramework,
|
|
18
|
+
mode: 'file-router',
|
|
19
|
+
typescript: true,
|
|
20
|
+
tailwind: false,
|
|
21
|
+
packageManager: 'pnpm',
|
|
22
|
+
chosenAddOns: [],
|
|
23
|
+
addOnOptions: {}
|
|
24
|
+
} as unknown as Options
|
|
25
|
+
|
|
26
|
+
describe('EJS Template Processing', () => {
|
|
27
|
+
it('should process packageTemplate with conditional dependencies', () => {
|
|
28
|
+
const options = {
|
|
29
|
+
...baseOptions,
|
|
30
|
+
chosenAddOns: [
|
|
31
|
+
{
|
|
32
|
+
id: 'testAddon',
|
|
33
|
+
name: 'Drizzle ORM',
|
|
34
|
+
packageTemplate: `{
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"testAddon-orm": "^0.29.0"<% if (addOnOption.testAddon.database === 'postgres') { %>,
|
|
37
|
+
"postgres": "^3.4.0"<% } %><% if (addOnOption.testAddon.database === 'mysql') { %>,
|
|
38
|
+
"mysql2": "^3.6.0"<% } %><% if (addOnOption.testAddon.database === 'sqlite') { %>,
|
|
39
|
+
"better-sqlite3": "^8.7.0"<% } %>
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {<% if (addOnOption.testAddon.database === 'postgres') { %>
|
|
42
|
+
"@types/postgres": "^3.0.0"<% } %><% if (addOnOption.testAddon.database === 'mysql') { %>
|
|
43
|
+
"@types/mysql2": "^3.0.0"<% } %><% if (addOnOption.testAddon.database === 'sqlite') { %>
|
|
44
|
+
"@types/better-sqlite3": "^7.6.0"<% } %>
|
|
45
|
+
}
|
|
46
|
+
}`
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
addOnOptions: {
|
|
50
|
+
testAddon: {
|
|
51
|
+
database: 'postgres'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const packageJSON = createPackageJSON(options)
|
|
57
|
+
|
|
58
|
+
expect(packageJSON.dependencies).toEqual({
|
|
59
|
+
'testAddon-orm': '^0.29.0',
|
|
60
|
+
'postgres': '^3.4.0'
|
|
61
|
+
})
|
|
62
|
+
expect(packageJSON.devDependencies).toEqual({
|
|
63
|
+
'@types/postgres': '^3.0.0'
|
|
64
|
+
})
|
|
65
|
+
// MySQL and SQLite dependencies should not be included
|
|
66
|
+
expect(packageJSON.dependencies).not.toHaveProperty('mysql2')
|
|
67
|
+
expect(packageJSON.dependencies).not.toHaveProperty('better-sqlite3')
|
|
68
|
+
expect(packageJSON.devDependencies).not.toHaveProperty('@types/mysql2')
|
|
69
|
+
expect(packageJSON.devDependencies).not.toHaveProperty('@types/better-sqlite3')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should handle different database options correctly', () => {
|
|
73
|
+
const options = {
|
|
74
|
+
...baseOptions,
|
|
75
|
+
chosenAddOns: [
|
|
76
|
+
{
|
|
77
|
+
id: 'testAddon',
|
|
78
|
+
name: 'Drizzle ORM',
|
|
79
|
+
packageTemplate: `{
|
|
80
|
+
"dependencies": {
|
|
81
|
+
"testAddon-orm": "^0.29.0"<% if (addOnOption.testAddon.database === 'postgres') { %>,
|
|
82
|
+
"postgres": "^3.4.0"<% } %><% if (addOnOption.testAddon.database === 'mysql') { %>,
|
|
83
|
+
"mysql2": "^3.6.0"<% } %><% if (addOnOption.testAddon.database === 'sqlite') { %>,
|
|
84
|
+
"better-sqlite3": "^8.7.0"<% } %>
|
|
85
|
+
}
|
|
86
|
+
}`
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
addOnOptions: {
|
|
90
|
+
testAddon: {
|
|
91
|
+
database: 'mysql'
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const packageJSON = createPackageJSON(options)
|
|
97
|
+
|
|
98
|
+
expect(packageJSON.dependencies).toEqual({
|
|
99
|
+
'testAddon-orm': '^0.29.0',
|
|
100
|
+
'mysql2': '^3.6.0'
|
|
101
|
+
})
|
|
102
|
+
// PostgreSQL and SQLite dependencies should not be included
|
|
103
|
+
expect(packageJSON.dependencies).not.toHaveProperty('postgres')
|
|
104
|
+
expect(packageJSON.dependencies).not.toHaveProperty('better-sqlite3')
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('should handle SQLite option correctly', () => {
|
|
108
|
+
const options = {
|
|
109
|
+
...baseOptions,
|
|
110
|
+
chosenAddOns: [
|
|
111
|
+
{
|
|
112
|
+
id: 'testAddon',
|
|
113
|
+
name: 'Drizzle ORM',
|
|
114
|
+
packageTemplate: `{
|
|
115
|
+
"dependencies": {
|
|
116
|
+
"testAddon-orm": "^0.29.0"<% if (addOnOption.testAddon.database === 'postgres') { %>,
|
|
117
|
+
"postgres": "^3.4.0"<% } %><% if (addOnOption.testAddon.database === 'mysql') { %>,
|
|
118
|
+
"mysql2": "^3.6.0"<% } %><% if (addOnOption.testAddon.database === 'sqlite') { %>,
|
|
119
|
+
"better-sqlite3": "^8.7.0"<% } %>
|
|
120
|
+
},
|
|
121
|
+
"devDependencies": {<% if (addOnOption.testAddon.database === 'sqlite') { %>
|
|
122
|
+
"@types/better-sqlite3": "^7.6.0"<% } %>
|
|
123
|
+
}
|
|
124
|
+
}`
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
addOnOptions: {
|
|
128
|
+
testAddon: {
|
|
129
|
+
database: 'sqlite'
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const packageJSON = createPackageJSON(options)
|
|
135
|
+
|
|
136
|
+
expect(packageJSON.dependencies).toEqual({
|
|
137
|
+
'testAddon-orm': '^0.29.0',
|
|
138
|
+
'better-sqlite3': '^8.7.0'
|
|
139
|
+
})
|
|
140
|
+
expect(packageJSON.devDependencies).toEqual({
|
|
141
|
+
'@types/better-sqlite3': '^7.6.0'
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('should handle multiple add-ons with options', () => {
|
|
146
|
+
const options = {
|
|
147
|
+
...baseOptions,
|
|
148
|
+
chosenAddOns: [
|
|
149
|
+
{
|
|
150
|
+
id: 'testAddon',
|
|
151
|
+
name: 'Drizzle ORM',
|
|
152
|
+
packageTemplate: `{
|
|
153
|
+
"dependencies": {
|
|
154
|
+
"testAddon-orm": "^0.29.0"<% if (addOnOption.testAddon.database === 'postgres') { %>,
|
|
155
|
+
"postgres": "^3.4.0"<% } %>
|
|
156
|
+
}
|
|
157
|
+
}`
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: 'auth',
|
|
161
|
+
name: 'Authentication',
|
|
162
|
+
packageTemplate: `{
|
|
163
|
+
"dependencies": {<% if (addOnOption.auth.provider === 'auth0') { %>
|
|
164
|
+
"@auth0/nextjs-auth0": "^3.0.0"<% } %><% if (addOnOption.auth.provider === 'supabase') { %>
|
|
165
|
+
"@supabase/supabase-js": "^2.0.0"<% } %>
|
|
166
|
+
}
|
|
167
|
+
}`
|
|
168
|
+
}
|
|
169
|
+
],
|
|
170
|
+
addOnOptions: {
|
|
171
|
+
testAddon: {
|
|
172
|
+
database: 'postgres'
|
|
173
|
+
},
|
|
174
|
+
auth: {
|
|
175
|
+
provider: 'auth0'
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const packageJSON = createPackageJSON(options)
|
|
181
|
+
|
|
182
|
+
expect(packageJSON.dependencies).toEqual({
|
|
183
|
+
'testAddon-orm': '^0.29.0',
|
|
184
|
+
'postgres': '^3.4.0',
|
|
185
|
+
'@auth0/nextjs-auth0': '^3.0.0'
|
|
186
|
+
})
|
|
187
|
+
expect(packageJSON.dependencies).not.toHaveProperty('@supabase/supabase-js')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('should handle complex conditional logic', () => {
|
|
191
|
+
const options = {
|
|
192
|
+
...baseOptions,
|
|
193
|
+
chosenAddOns: [
|
|
194
|
+
{
|
|
195
|
+
id: 'ui',
|
|
196
|
+
name: 'UI Library',
|
|
197
|
+
packageTemplate: `{
|
|
198
|
+
"dependencies": {<% if (addOnOption.ui.library === 'chakra') { %>
|
|
199
|
+
"@chakra-ui/react": "^2.0.0",
|
|
200
|
+
"@emotion/react": "^11.0.0",
|
|
201
|
+
"@emotion/styled": "^11.0.0"<% } else if (addOnOption.ui.library === 'mui') { %>
|
|
202
|
+
"@mui/material": "^5.0.0",
|
|
203
|
+
"@emotion/react": "^11.0.0",
|
|
204
|
+
"@emotion/styled": "^11.0.0"<% } else if (addOnOption.ui.library === 'mantine') { %>
|
|
205
|
+
"@mantine/core": "^7.0.0",
|
|
206
|
+
"@mantine/hooks": "^7.0.0"<% } %>
|
|
207
|
+
}
|
|
208
|
+
}`
|
|
209
|
+
}
|
|
210
|
+
],
|
|
211
|
+
addOnOptions: {
|
|
212
|
+
ui: {
|
|
213
|
+
library: 'mantine'
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const packageJSON = createPackageJSON(options)
|
|
219
|
+
|
|
220
|
+
expect(packageJSON.dependencies).toEqual({
|
|
221
|
+
'@mantine/core': '^7.0.0',
|
|
222
|
+
'@mantine/hooks': '^7.0.0'
|
|
223
|
+
})
|
|
224
|
+
// Other UI library dependencies should not be included
|
|
225
|
+
expect(packageJSON.dependencies).not.toHaveProperty('@chakra-ui/react')
|
|
226
|
+
expect(packageJSON.dependencies).not.toHaveProperty('@mui/material')
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('should handle scripts conditionally', () => {
|
|
230
|
+
const options = {
|
|
231
|
+
...baseOptions,
|
|
232
|
+
chosenAddOns: [
|
|
233
|
+
{
|
|
234
|
+
id: 'testing',
|
|
235
|
+
name: 'Testing Setup',
|
|
236
|
+
packageTemplate: `{
|
|
237
|
+
"scripts": {<% if (addOnOption.testing.framework === 'jest') { %>
|
|
238
|
+
"test": "jest",
|
|
239
|
+
"test:watch": "jest --watch"<% } else if (addOnOption.testing.framework === 'vitest') { %>
|
|
240
|
+
"test": "vitest",
|
|
241
|
+
"test:ui": "vitest --ui"<% } %>
|
|
242
|
+
},
|
|
243
|
+
"devDependencies": {<% if (addOnOption.testing.framework === 'jest') { %>
|
|
244
|
+
"jest": "^29.0.0",
|
|
245
|
+
"@types/jest": "^29.0.0"<% } else if (addOnOption.testing.framework === 'vitest') { %>
|
|
246
|
+
"vitest": "^1.0.0",
|
|
247
|
+
"@vitest/ui": "^1.0.0"<% } %>
|
|
248
|
+
}
|
|
249
|
+
}`
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
addOnOptions: {
|
|
253
|
+
testing: {
|
|
254
|
+
framework: 'vitest'
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const packageJSON = createPackageJSON(options)
|
|
260
|
+
|
|
261
|
+
expect(packageJSON.scripts).toEqual({
|
|
262
|
+
'test': 'vitest',
|
|
263
|
+
'test:ui': 'vitest --ui'
|
|
264
|
+
})
|
|
265
|
+
expect(packageJSON.devDependencies).toEqual({
|
|
266
|
+
'vitest': '^1.0.0',
|
|
267
|
+
'@vitest/ui': '^1.0.0'
|
|
268
|
+
})
|
|
269
|
+
// Jest-specific scripts and dependencies should not be included
|
|
270
|
+
expect(packageJSON.scripts).not.toHaveProperty('test:watch')
|
|
271
|
+
expect(packageJSON.devDependencies).not.toHaveProperty('jest')
|
|
272
|
+
expect(packageJSON.devDependencies).not.toHaveProperty('@types/jest')
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
it('should fallback to packageAdditions on template error', () => {
|
|
276
|
+
const options = {
|
|
277
|
+
...baseOptions,
|
|
278
|
+
chosenAddOns: [
|
|
279
|
+
{
|
|
280
|
+
id: 'broken',
|
|
281
|
+
name: 'Broken Template',
|
|
282
|
+
packageTemplate: `{
|
|
283
|
+
"dependencies": {
|
|
284
|
+
"valid-package": "^1.0.0"
|
|
285
|
+
<% this will cause a syntax error %>
|
|
286
|
+
}
|
|
287
|
+
}`,
|
|
288
|
+
packageAdditions: {
|
|
289
|
+
dependencies: {
|
|
290
|
+
'fallback-package': '^1.0.0'
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
],
|
|
295
|
+
addOnOptions: {}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const packageJSON = createPackageJSON(options)
|
|
299
|
+
|
|
300
|
+
// Should use fallback packageAdditions
|
|
301
|
+
expect(packageJSON.dependencies).toEqual({
|
|
302
|
+
'fallback-package': '^1.0.0'
|
|
303
|
+
})
|
|
304
|
+
expect(packageJSON.dependencies).not.toHaveProperty('valid-package')
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
it('should handle empty or missing addOnOptions', () => {
|
|
308
|
+
const options = {
|
|
309
|
+
...baseOptions,
|
|
310
|
+
chosenAddOns: [
|
|
311
|
+
{
|
|
312
|
+
id: 'simple',
|
|
313
|
+
name: 'Simple Add-on',
|
|
314
|
+
packageTemplate: `{
|
|
315
|
+
"dependencies": {
|
|
316
|
+
"always-included": "^1.0.0"<% if (addOnOption.simple && addOnOption.simple.feature) { %>,
|
|
317
|
+
"conditional-package": "^1.0.0"<% } %>
|
|
318
|
+
}
|
|
319
|
+
}`
|
|
320
|
+
}
|
|
321
|
+
],
|
|
322
|
+
addOnOptions: {} // No options for this add-on
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const packageJSON = createPackageJSON(options)
|
|
326
|
+
|
|
327
|
+
expect(packageJSON.dependencies).toEqual({
|
|
328
|
+
'always-included': '^1.0.0'
|
|
329
|
+
})
|
|
330
|
+
expect(packageJSON.dependencies).not.toHaveProperty('conditional-package')
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it('should preserve dependency sorting after template processing', () => {
|
|
334
|
+
const options = {
|
|
335
|
+
...baseOptions,
|
|
336
|
+
chosenAddOns: [
|
|
337
|
+
{
|
|
338
|
+
id: 'sorting-test',
|
|
339
|
+
name: 'Sorting Test',
|
|
340
|
+
packageTemplate: `{
|
|
341
|
+
"dependencies": {
|
|
342
|
+
"z-package": "^1.0.0",
|
|
343
|
+
"a-package": "^1.0.0",
|
|
344
|
+
"m-package": "^1.0.0"
|
|
345
|
+
}
|
|
346
|
+
}`
|
|
347
|
+
}
|
|
348
|
+
],
|
|
349
|
+
addOnOptions: {}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const packageJSON = createPackageJSON(options)
|
|
353
|
+
const dependencyKeys = Object.keys(packageJSON.dependencies)
|
|
354
|
+
|
|
355
|
+
// Dependencies should be sorted alphabetically
|
|
356
|
+
expect(dependencyKeys).toEqual(['a-package', 'm-package', 'z-package'])
|
|
357
|
+
})
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
describe('mergePackageJSON', () => {
|
|
361
|
+
it('should merge dependencies correctly', () => {
|
|
362
|
+
const base = {
|
|
363
|
+
dependencies: {
|
|
364
|
+
'react': '^18.0.0',
|
|
365
|
+
'lodash': '^4.0.0'
|
|
366
|
+
},
|
|
367
|
+
devDependencies: {
|
|
368
|
+
'typescript': '^5.0.0'
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const overlay = {
|
|
373
|
+
dependencies: {
|
|
374
|
+
'axios': '^1.0.0',
|
|
375
|
+
'lodash': '^4.17.0' // Should override
|
|
376
|
+
},
|
|
377
|
+
devDependencies: {
|
|
378
|
+
'jest': '^29.0.0'
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const result = mergePackageJSON(base, overlay)
|
|
383
|
+
|
|
384
|
+
expect(result.dependencies).toEqual({
|
|
385
|
+
'react': '^18.0.0',
|
|
386
|
+
'lodash': '^4.17.0', // Overridden version
|
|
387
|
+
'axios': '^1.0.0'
|
|
388
|
+
})
|
|
389
|
+
expect(result.devDependencies).toEqual({
|
|
390
|
+
'typescript': '^5.0.0',
|
|
391
|
+
'jest': '^29.0.0'
|
|
392
|
+
})
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
it('should handle missing sections gracefully', () => {
|
|
396
|
+
const base = {
|
|
397
|
+
dependencies: {
|
|
398
|
+
'react': '^18.0.0'
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const overlay = {
|
|
403
|
+
devDependencies: {
|
|
404
|
+
'jest': '^29.0.0'
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const result = mergePackageJSON(base, overlay)
|
|
409
|
+
|
|
410
|
+
expect(result.dependencies).toEqual({
|
|
411
|
+
'react': '^18.0.0'
|
|
412
|
+
})
|
|
413
|
+
expect(result.devDependencies).toEqual({
|
|
414
|
+
'jest': '^29.0.0'
|
|
415
|
+
})
|
|
416
|
+
})
|
|
417
|
+
})
|
|
418
|
+
})
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { createMemoryEnvironment } from '../src/environment.js'
|
|
4
|
+
import { createTemplateFile } from '../src/template-file.js'
|
|
5
|
+
|
|
6
|
+
import type { Options } from '../src/types.js'
|
|
7
|
+
|
|
8
|
+
const simpleOptions = {
|
|
9
|
+
projectName: 'test',
|
|
10
|
+
targetDir: '/test',
|
|
11
|
+
framework: {
|
|
12
|
+
id: 'test',
|
|
13
|
+
name: 'Test',
|
|
14
|
+
},
|
|
15
|
+
chosenAddOns: [],
|
|
16
|
+
packageManager: 'pnpm',
|
|
17
|
+
typescript: true,
|
|
18
|
+
tailwind: true,
|
|
19
|
+
mode: 'file-router',
|
|
20
|
+
addOnOptions: {},
|
|
21
|
+
} as unknown as Options
|
|
22
|
+
|
|
23
|
+
describe('Filename Processing - Prefix Stripping', () => {
|
|
24
|
+
it('should strip single prefix from filename', async () => {
|
|
25
|
+
const { environment, output } = createMemoryEnvironment()
|
|
26
|
+
const templateFile = createTemplateFile(environment, {
|
|
27
|
+
...simpleOptions,
|
|
28
|
+
addOnOptions: {
|
|
29
|
+
testAddon: { database: 'postgres' }
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
environment.startRun()
|
|
33
|
+
await templateFile(
|
|
34
|
+
'./__postgres__testAddon.config.ts.ejs',
|
|
35
|
+
'<% if (addOnOption.testAddon.database !== "postgres") { ignoreFile() } %>\n// PostgreSQL config\nexport default { driver: "postgres" }'
|
|
36
|
+
)
|
|
37
|
+
environment.finishRun()
|
|
38
|
+
|
|
39
|
+
// File should be created with prefix stripped
|
|
40
|
+
expect(output.files['/test/testAddon.config.ts']).toBeDefined()
|
|
41
|
+
expect(output.files['/test/testAddon.config.ts'].trim()).toEqual('// PostgreSQL config\nexport default { driver: \'postgres\' }')
|
|
42
|
+
|
|
43
|
+
// Original prefixed filename should not exist
|
|
44
|
+
expect(output.files['/test/__postgres__testAddon.config.ts']).toBeUndefined()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should strip prefix from nested directory paths', async () => {
|
|
48
|
+
const { environment, output } = createMemoryEnvironment()
|
|
49
|
+
const templateFile = createTemplateFile(environment, {
|
|
50
|
+
...simpleOptions,
|
|
51
|
+
addOnOptions: {
|
|
52
|
+
testAddon: { database: 'mysql' }
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
environment.startRun()
|
|
56
|
+
await templateFile(
|
|
57
|
+
'./src/db/__mysql__connection.ts.ejs',
|
|
58
|
+
'<% if (addOnOption.testAddon.database !== "mysql") { ignoreFile() } %>\n// MySQL connection\nexport const connection = "mysql"'
|
|
59
|
+
)
|
|
60
|
+
environment.finishRun()
|
|
61
|
+
|
|
62
|
+
// File should be created with prefix stripped, preserving directory structure
|
|
63
|
+
expect(output.files['/test/src/db/connection.ts']).toBeDefined()
|
|
64
|
+
expect(output.files['/test/src/db/connection.ts'].trim()).toEqual('// MySQL connection\nexport const connection = \'mysql\'')
|
|
65
|
+
|
|
66
|
+
// Original prefixed path should not exist
|
|
67
|
+
expect(output.files['/test/src/db/__mysql__connection.ts']).toBeUndefined()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should handle multiple prefixed files in same directory', async () => {
|
|
71
|
+
const { environment, output } = createMemoryEnvironment()
|
|
72
|
+
const templateFile = createTemplateFile(environment, {
|
|
73
|
+
...simpleOptions,
|
|
74
|
+
addOnOptions: {
|
|
75
|
+
testAddon: { database: 'sqlite' }
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
environment.startRun()
|
|
79
|
+
await templateFile(
|
|
80
|
+
'./__postgres__testAddon.config.ts.ejs',
|
|
81
|
+
'<% if (addOnOption.testAddon.database !== "postgres") { ignoreFile() } %>\n// PostgreSQL config'
|
|
82
|
+
)
|
|
83
|
+
await templateFile(
|
|
84
|
+
'./__mysql__testAddon.config.ts.ejs',
|
|
85
|
+
'<% if (addOnOption.testAddon.database !== "mysql") { ignoreFile() } %>\n// MySQL config'
|
|
86
|
+
)
|
|
87
|
+
await templateFile(
|
|
88
|
+
'./__sqlite__testAddon.config.ts.ejs',
|
|
89
|
+
'<% if (addOnOption.testAddon.database !== "sqlite") { ignoreFile() } %>\n// SQLite config'
|
|
90
|
+
)
|
|
91
|
+
environment.finishRun()
|
|
92
|
+
|
|
93
|
+
// Only SQLite file should exist (others ignored via ignoreFile())
|
|
94
|
+
expect(output.files['/test/testAddon.config.ts']).toBeDefined()
|
|
95
|
+
expect(output.files['/test/testAddon.config.ts'].trim()).toEqual('// SQLite config')
|
|
96
|
+
|
|
97
|
+
// Prefixed filenames should not exist
|
|
98
|
+
expect(output.files['/test/__postgres__testAddon.config.ts']).toBeUndefined()
|
|
99
|
+
expect(output.files['/test/__mysql__testAddon.config.ts']).toBeUndefined()
|
|
100
|
+
expect(output.files['/test/__sqlite__testAddon.config.ts']).toBeUndefined()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('should handle complex filename patterns', async () => {
|
|
104
|
+
const { environment, output } = createMemoryEnvironment()
|
|
105
|
+
const templateFile = createTemplateFile(environment, {
|
|
106
|
+
...simpleOptions,
|
|
107
|
+
addOnOptions: {
|
|
108
|
+
auth: { provider: 'auth0' }
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
environment.startRun()
|
|
112
|
+
await templateFile(
|
|
113
|
+
'./__auth0__auth.config.js.ejs',
|
|
114
|
+
'<% if (addOnOption.auth.provider !== "auth0") { ignoreFile() } %>\n// Auth0 configuration\nmodule.exports = { provider: "auth0" }'
|
|
115
|
+
)
|
|
116
|
+
environment.finishRun()
|
|
117
|
+
|
|
118
|
+
// File should be created with prefix stripped
|
|
119
|
+
expect(output.files['/test/auth.config.js']).toBeDefined()
|
|
120
|
+
expect(output.files['/test/auth.config.js'].trim()).toEqual('// Auth0 configuration\nmodule.exports = { provider: "auth0" }')
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('should handle prefixed files with .append.ejs extension', async () => {
|
|
124
|
+
const { environment, output } = createMemoryEnvironment()
|
|
125
|
+
const templateFile = createTemplateFile(environment, {
|
|
126
|
+
...simpleOptions,
|
|
127
|
+
addOnOptions: {
|
|
128
|
+
testAddon: { database: 'postgres' }
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
environment.startRun()
|
|
132
|
+
// Create base file first
|
|
133
|
+
await templateFile(
|
|
134
|
+
'./.env.ejs',
|
|
135
|
+
'BASE_VAR=value\n'
|
|
136
|
+
)
|
|
137
|
+
// Then append with prefixed filename
|
|
138
|
+
await templateFile(
|
|
139
|
+
'./__postgres__.env.append.ejs',
|
|
140
|
+
'<% if (addOnOption.testAddon.database !== "postgres") { ignoreFile() } %>\nDATABASE_URL=postgresql://localhost:5432/mydb\n'
|
|
141
|
+
)
|
|
142
|
+
environment.finishRun()
|
|
143
|
+
|
|
144
|
+
// File should be created with prefix stripped and content appended
|
|
145
|
+
expect(output.files['/test/.env']).toBeDefined()
|
|
146
|
+
expect(output.files['/test/.env']).toEqual('BASE_VAR=value\n\nDATABASE_URL=postgresql://localhost:5432/mydb\n')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('should handle files without prefixes normally', async () => {
|
|
150
|
+
const { environment, output } = createMemoryEnvironment()
|
|
151
|
+
const templateFile = createTemplateFile(environment, simpleOptions)
|
|
152
|
+
environment.startRun()
|
|
153
|
+
await templateFile(
|
|
154
|
+
'./regular-file.ts.ejs',
|
|
155
|
+
'export const config = "normal"'
|
|
156
|
+
)
|
|
157
|
+
environment.finishRun()
|
|
158
|
+
|
|
159
|
+
// File should be created with normal filename processing
|
|
160
|
+
expect(output.files['/test/regular-file.ts']).toBeDefined()
|
|
161
|
+
expect(output.files['/test/regular-file.ts']).toEqual('export const config = \'normal\'\n')
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('should handle malformed prefixes gracefully', async () => {
|
|
165
|
+
const { environment, output } = createMemoryEnvironment()
|
|
166
|
+
const templateFile = createTemplateFile(environment, simpleOptions)
|
|
167
|
+
environment.startRun()
|
|
168
|
+
await templateFile(
|
|
169
|
+
'./__malformed_prefix.ts.ejs',
|
|
170
|
+
'export const config = "malformed"'
|
|
171
|
+
)
|
|
172
|
+
await templateFile(
|
|
173
|
+
'./__only_one_underscore.ts.ejs',
|
|
174
|
+
'export const config = "malformed2"'
|
|
175
|
+
)
|
|
176
|
+
await templateFile(
|
|
177
|
+
'./____.ts.ejs',
|
|
178
|
+
'export const config = "empty"'
|
|
179
|
+
)
|
|
180
|
+
environment.finishRun()
|
|
181
|
+
|
|
182
|
+
// Files with malformed prefixes should be created as-is (minus .ejs extension)
|
|
183
|
+
expect(output.files['/test/__malformed_prefix.ts']).toBeDefined()
|
|
184
|
+
expect(output.files['/test/__only_one_underscore.ts']).toBeDefined()
|
|
185
|
+
expect(output.files['/test/____.ts']).toBeDefined()
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('should handle deeply nested prefixed files', async () => {
|
|
189
|
+
const { environment, output } = createMemoryEnvironment()
|
|
190
|
+
const templateFile = createTemplateFile(environment, {
|
|
191
|
+
...simpleOptions,
|
|
192
|
+
addOnOptions: {
|
|
193
|
+
styling: { framework: 'tailwind' }
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
environment.startRun()
|
|
197
|
+
await templateFile(
|
|
198
|
+
'./src/styles/components/__tailwind__button.css.ejs',
|
|
199
|
+
'<% if (addOnOption.styling.framework !== "tailwind") { ignoreFile() } %>\n@tailwind base;\n@tailwind components;\n@tailwind utilities;'
|
|
200
|
+
)
|
|
201
|
+
environment.finishRun()
|
|
202
|
+
|
|
203
|
+
// File should be created with prefix stripped, preserving deep directory structure
|
|
204
|
+
expect(output.files['/test/src/styles/components/button.css']).toBeDefined()
|
|
205
|
+
expect(output.files['/test/src/styles/components/button.css'].trim()).toEqual('@tailwind base;\n@tailwind components;\n@tailwind utilities;')
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('should handle prefix stripping with different option values', async () => {
|
|
209
|
+
const { environment, output } = createMemoryEnvironment()
|
|
210
|
+
const templateFile = createTemplateFile(environment, {
|
|
211
|
+
...simpleOptions,
|
|
212
|
+
addOnOptions: {
|
|
213
|
+
ui: { library: 'chakra' }
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
environment.startRun()
|
|
217
|
+
await templateFile(
|
|
218
|
+
'./__chakra__theme.ts.ejs',
|
|
219
|
+
'<% if (addOnOption.ui.library !== "chakra") { ignoreFile() } %>\n// Chakra UI theme\nexport const theme = { colors: {} }'
|
|
220
|
+
)
|
|
221
|
+
await templateFile(
|
|
222
|
+
'./__mui__theme.ts.ejs',
|
|
223
|
+
'<% if (addOnOption.ui.library !== "mui") { ignoreFile() } %>\n// Material-UI theme\nexport const theme = { palette: {} }'
|
|
224
|
+
)
|
|
225
|
+
environment.finishRun()
|
|
226
|
+
|
|
227
|
+
// Only Chakra file should exist (MUI ignored via ignoreFile())
|
|
228
|
+
expect(output.files['/test/theme.ts']).toBeDefined()
|
|
229
|
+
expect(output.files['/test/theme.ts'].trim()).toEqual('// Chakra UI theme\nexport const theme = { colors: {} }')
|
|
230
|
+
|
|
231
|
+
// Prefixed filenames should not exist
|
|
232
|
+
expect(output.files['/test/__chakra__theme.ts']).toBeUndefined()
|
|
233
|
+
expect(output.files['/test/__mui__theme.ts']).toBeUndefined()
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
it('should handle complex prefix with special characters', async () => {
|
|
237
|
+
const { environment, output } = createMemoryEnvironment()
|
|
238
|
+
const templateFile = createTemplateFile(environment, {
|
|
239
|
+
...simpleOptions,
|
|
240
|
+
addOnOptions: {
|
|
241
|
+
deployment: { platform: 'vercel-edge' }
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
environment.startRun()
|
|
245
|
+
await templateFile(
|
|
246
|
+
'./__vercel-edge__api.ts.ejs',
|
|
247
|
+
'<% if (addOnOption.deployment.platform !== "vercel-edge") { ignoreFile() } %>\n// Vercel Edge API\nexport const runtime = "edge"'
|
|
248
|
+
)
|
|
249
|
+
environment.finishRun()
|
|
250
|
+
|
|
251
|
+
// File should be created with prefix stripped
|
|
252
|
+
expect(output.files['/test/api.ts']).toBeDefined()
|
|
253
|
+
expect(output.files['/test/api.ts'].trim()).toEqual('// Vercel Edge API\nexport const runtime = \'edge\'')
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('should handle multiple prefixes in same filename (edge case)', async () => {
|
|
257
|
+
const { environment, output } = createMemoryEnvironment()
|
|
258
|
+
const templateFile = createTemplateFile(environment, {
|
|
259
|
+
...simpleOptions,
|
|
260
|
+
addOnOptions: {
|
|
261
|
+
test: { value: 'postgres' }
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
environment.startRun()
|
|
265
|
+
await templateFile(
|
|
266
|
+
'./__postgres__file__with__underscores.ts.ejs',
|
|
267
|
+
'<% if (addOnOption.test.value !== "postgres") { ignoreFile() } %>\n// File with underscores\nexport const value = "test"'
|
|
268
|
+
)
|
|
269
|
+
environment.finishRun()
|
|
270
|
+
|
|
271
|
+
// Should only strip the first valid prefix pattern
|
|
272
|
+
expect(output.files['/test/file__with__underscores.ts']).toBeDefined()
|
|
273
|
+
expect(output.files['/test/file__with__underscores.ts'].trim()).toEqual('// File with underscores\nexport const value = \'test\'')
|
|
274
|
+
})
|
|
275
|
+
})
|