cf-yoyo 1.0.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 (141) hide show
  1. package/.eslintrc.json +28 -0
  2. package/.github/workflows/ci.yml +96 -0
  3. package/.prettierrc.json +10 -0
  4. package/CHANGELOG.md +55 -0
  5. package/README.md +138 -0
  6. package/__tests__/cli-e2e.test.ts +145 -0
  7. package/__tests__/config.test.ts +268 -0
  8. package/__tests__/filesystem.test.ts +453 -0
  9. package/__tests__/logger.test.ts +274 -0
  10. package/__tests__/template-engine.test.ts +450 -0
  11. package/__tests__/types.test.ts +25 -0
  12. package/deep_todos.md +766 -0
  13. package/dist/cli/commands/create.d.ts +26 -0
  14. package/dist/cli/commands/create.d.ts.map +1 -0
  15. package/dist/cli/commands/create.js +308 -0
  16. package/dist/cli/commands/create.js.map +1 -0
  17. package/dist/cli/commands/git.d.ts +10 -0
  18. package/dist/cli/commands/git.d.ts.map +1 -0
  19. package/dist/cli/commands/git.js +887 -0
  20. package/dist/cli/commands/git.js.map +1 -0
  21. package/dist/cli/commands/list.d.ts +10 -0
  22. package/dist/cli/commands/list.d.ts.map +1 -0
  23. package/dist/cli/commands/list.js +90 -0
  24. package/dist/cli/commands/list.js.map +1 -0
  25. package/dist/cli/index.d.ts +15 -0
  26. package/dist/cli/index.d.ts.map +1 -0
  27. package/dist/cli/index.js +62 -0
  28. package/dist/cli/index.js.map +1 -0
  29. package/dist/core/config.d.ts +35 -0
  30. package/dist/core/config.d.ts.map +1 -0
  31. package/dist/core/config.js +260 -0
  32. package/dist/core/config.js.map +1 -0
  33. package/dist/core/filesystem.d.ts +84 -0
  34. package/dist/core/filesystem.d.ts.map +1 -0
  35. package/dist/core/filesystem.js +417 -0
  36. package/dist/core/filesystem.js.map +1 -0
  37. package/dist/core/git-token.d.ts +81 -0
  38. package/dist/core/git-token.d.ts.map +1 -0
  39. package/dist/core/git-token.js +244 -0
  40. package/dist/core/git-token.js.map +1 -0
  41. package/dist/core/git.d.ts +70 -0
  42. package/dist/core/git.d.ts.map +1 -0
  43. package/dist/core/git.js +367 -0
  44. package/dist/core/git.js.map +1 -0
  45. package/dist/core/prompt.d.ts +28 -0
  46. package/dist/core/prompt.d.ts.map +1 -0
  47. package/dist/core/prompt.js +253 -0
  48. package/dist/core/prompt.js.map +1 -0
  49. package/dist/core/template-engine.d.ts +52 -0
  50. package/dist/core/template-engine.d.ts.map +1 -0
  51. package/dist/core/template-engine.js +308 -0
  52. package/dist/core/template-engine.js.map +1 -0
  53. package/dist/core/template-manager.d.ts +54 -0
  54. package/dist/core/template-manager.d.ts.map +1 -0
  55. package/dist/core/template-manager.js +330 -0
  56. package/dist/core/template-manager.js.map +1 -0
  57. package/dist/index.d.ts +12 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +19 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/types/index.d.ts +244 -0
  62. package/dist/types/index.d.ts.map +1 -0
  63. package/dist/types/index.js +51 -0
  64. package/dist/types/index.js.map +1 -0
  65. package/dist/utils/logger.d.ts +68 -0
  66. package/dist/utils/logger.d.ts.map +1 -0
  67. package/dist/utils/logger.js +140 -0
  68. package/dist/utils/logger.js.map +1 -0
  69. package/memory.md +241 -0
  70. package/need-debug.md +395 -0
  71. package/package.json +42 -0
  72. package/src/cli/commands/create.ts +326 -0
  73. package/src/cli/commands/git.ts +1001 -0
  74. package/src/cli/commands/list.ts +97 -0
  75. package/src/cli/index.ts +71 -0
  76. package/src/core/config.ts +262 -0
  77. package/src/core/filesystem.ts +408 -0
  78. package/src/core/git-token.ts +248 -0
  79. package/src/core/git.ts +384 -0
  80. package/src/core/prompt.ts +345 -0
  81. package/src/core/template-engine.ts +324 -0
  82. package/src/core/template-manager.ts +338 -0
  83. package/src/index.ts +19 -0
  84. package/src/types/index.ts +259 -0
  85. package/src/utils/logger.ts +150 -0
  86. package/templates/pages/basic/README.md.mustache +63 -0
  87. package/templates/pages/basic/package.json.mustache +23 -0
  88. package/templates/pages/basic/public/css/style.css +199 -0
  89. package/templates/pages/basic/public/index.html.mustache +72 -0
  90. package/templates/pages/basic/public/js/main.js +103 -0
  91. package/templates/pages/basic/template.json +38 -0
  92. package/templates/pages/basic/tsconfig.json +21 -0
  93. package/templates/pages/basic/wrangler.toml.mustache +14 -0
  94. package/templates/pages/basic-js/README.md.mustache +62 -0
  95. package/templates/pages/basic-js/package.json.mustache +25 -0
  96. package/templates/pages/basic-js/public/css/style.css +212 -0
  97. package/templates/pages/basic-js/public/index.html.mustache +53 -0
  98. package/templates/pages/basic-js/public/js/main.js +134 -0
  99. package/templates/pages/basic-js/template.json +35 -0
  100. package/templates/pages/basic-js/wrangler.toml.mustache +14 -0
  101. package/templates/pages/react/README.md.mustache +97 -0
  102. package/templates/pages/react/index.html.mustache +14 -0
  103. package/templates/pages/react/package.json.mustache +34 -0
  104. package/templates/pages/react/src/App.css +168 -0
  105. package/templates/pages/react/src/App.tsx.mustache +62 -0
  106. package/templates/pages/react/src/index.css +53 -0
  107. package/templates/pages/react/src/main.tsx.mustache +10 -0
  108. package/templates/pages/react/src/vite-env.d.ts +1 -0
  109. package/templates/pages/react/template.json +54 -0
  110. package/templates/pages/react/tsconfig.json +21 -0
  111. package/templates/pages/react/tsconfig.node.json +10 -0
  112. package/templates/pages/react/vite.config.ts +16 -0
  113. package/templates/worker/basic/README.md.mustache +56 -0
  114. package/templates/worker/basic/package.json.mustache +29 -0
  115. package/templates/worker/basic/src/index.ts.mustache +125 -0
  116. package/templates/worker/basic/template.json +30 -0
  117. package/templates/worker/basic/tsconfig.json +24 -0
  118. package/templates/worker/basic/wrangler.toml.mustache +33 -0
  119. package/templates/worker/basic-js/README.md.mustache +55 -0
  120. package/templates/worker/basic-js/package.json.mustache +25 -0
  121. package/templates/worker/basic-js/src/index.js.mustache +146 -0
  122. package/templates/worker/basic-js/template.json +27 -0
  123. package/templates/worker/basic-js/wrangler.toml.mustache +33 -0
  124. package/templates/worker/hono/README.md.mustache +79 -0
  125. package/templates/worker/hono/package.json.mustache +33 -0
  126. package/templates/worker/hono/src/index.ts.mustache +64 -0
  127. package/templates/worker/hono/src/routes/index.ts.mustache +165 -0
  128. package/templates/worker/hono/template.json +34 -0
  129. package/templates/worker/hono/tsconfig.json +24 -0
  130. package/templates/worker/hono/wrangler.toml.mustache +36 -0
  131. package/templates/worker/hono-js/README.md.mustache +67 -0
  132. package/templates/worker/hono-js/package.json.mustache +29 -0
  133. package/templates/worker/hono-js/src/index.js.mustache +55 -0
  134. package/templates/worker/hono-js/src/routes/index.js.mustache +127 -0
  135. package/templates/worker/hono-js/template.json +31 -0
  136. package/templates/worker/hono-js/wrangler.toml.mustache +36 -0
  137. package/thoughts/ledgers/CONTINUITY_ses_287e.md +74 -0
  138. package/thoughts/ledgers/CONTINUITY_ses_28b5.md +85 -0
  139. package/tsconfig.json +30 -0
  140. package/vitest.config.ts +20 -0
  141. package//351/240/205/347/233/256/350/241/250.md +140 -0
@@ -0,0 +1,268 @@
1
+ /**
2
+ * config.ts 模組測試
3
+ * 測試配置解析、驗證與合併功能
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach } from 'vitest';
7
+ import {
8
+ parseConfig,
9
+ parseFromOptions,
10
+ getCurrentNodeVersion,
11
+ checkNodeVersion,
12
+ DEFAULT_CONFIG,
13
+ } from '../src/core/config';
14
+ import { ProjectType, TemplateType, Language } from '../src/types';
15
+
16
+ describe('Config', () => {
17
+ describe('DEFAULT_CONFIG', () => {
18
+ it('should have correct default values', () => {
19
+ expect(DEFAULT_CONFIG.language).toBe(Language.TYPESCRIPT);
20
+ expect(DEFAULT_CONFIG.projectType).toBe(ProjectType.WORKER);
21
+ expect(DEFAULT_CONFIG.template).toBe(TemplateType.BASIC);
22
+ expect(DEFAULT_CONFIG.initGit).toBe(true);
23
+ expect(DEFAULT_CONFIG.installDeps).toBe(true);
24
+ });
25
+ });
26
+
27
+ describe('parseConfig', () => {
28
+ it('should throw error when projectName is missing', () => {
29
+ expect(() => parseConfig({})).toThrow('專案名稱為必填欄位');
30
+ });
31
+
32
+ it('should parse minimal user input', () => {
33
+ const result = parseConfig({ projectName: 'test-project' });
34
+ expect(result.projectName).toBe('test-project');
35
+ expect(result.projectType).toBe(ProjectType.WORKER);
36
+ expect(result.template).toBe(TemplateType.BASIC);
37
+ });
38
+
39
+ it('should trim project name', () => {
40
+ const result = parseConfig({ projectName: ' my-project ' });
41
+ expect(result.projectName).toBe('my-project');
42
+ });
43
+
44
+ it('should use provided project type', () => {
45
+ const result = parseConfig({
46
+ projectName: 'test',
47
+ projectType: ProjectType.PAGES,
48
+ });
49
+ expect(result.projectType).toBe(ProjectType.PAGES);
50
+ expect(result.template).toBe(TemplateType.STATIC);
51
+ });
52
+
53
+ it('should use provided template', () => {
54
+ const result = parseConfig({
55
+ projectName: 'test',
56
+ projectType: ProjectType.WORKER,
57
+ template: TemplateType.HONO,
58
+ });
59
+ expect(result.template).toBe(TemplateType.HONO);
60
+ });
61
+
62
+ it('should use default template for incompatible combination', () => {
63
+ const result = parseConfig({
64
+ projectName: 'test',
65
+ projectType: ProjectType.D1,
66
+ template: TemplateType.HONO, // HONO is not compatible with D1
67
+ });
68
+ expect(result.template).toBe(TemplateType.BASIC); // Should fallback to default
69
+ });
70
+
71
+ it('should generate correct target directory', () => {
72
+ const result = parseConfig({ projectName: 'my-app' });
73
+ expect(result.targetDir).toContain('my-app');
74
+ });
75
+
76
+ it('should accept custom cwd', () => {
77
+ const result = parseConfig({ projectName: 'test' }, { cwd: '/custom/path' });
78
+ expect(result.targetDir).toContain('test');
79
+ expect(result.targetDir).toMatch(/custom.*path.*test/);
80
+ });
81
+
82
+ it('should respect initGit option', () => {
83
+ const result = parseConfig({
84
+ projectName: 'test',
85
+ initGit: false,
86
+ });
87
+ expect(result.initGit).toBe(false);
88
+ });
89
+
90
+ it('should respect installDeps option', () => {
91
+ const result = parseConfig({
92
+ projectName: 'test',
93
+ installDeps: false,
94
+ });
95
+ expect(result.installDeps).toBe(false);
96
+ });
97
+
98
+ it('should respect language option', () => {
99
+ const result = parseConfig({
100
+ projectName: 'test',
101
+ language: Language.JAVASCRIPT,
102
+ });
103
+ expect(result.language).toBe(Language.JAVASCRIPT);
104
+ });
105
+ });
106
+
107
+ describe('parseFromOptions', () => {
108
+ it('should parse project name', () => {
109
+ const result = parseFromOptions('my-project', {});
110
+ expect(result.projectName).toBe('my-project');
111
+ });
112
+
113
+ it('should return empty object when no project name', () => {
114
+ const result = parseFromOptions(undefined, {});
115
+ expect(result.projectName).toBeUndefined();
116
+ });
117
+
118
+ it('should parse language option', () => {
119
+ const result = parseFromOptions('test', { language: 'typescript' });
120
+ expect(result.language).toBe(Language.TYPESCRIPT);
121
+ });
122
+
123
+ it('should parse language shorthand "ts"', () => {
124
+ const result = parseFromOptions('test', { language: 'ts' });
125
+ expect(result.language).toBe(Language.TYPESCRIPT);
126
+ });
127
+
128
+ it('should parse language "javascript"', () => {
129
+ const result = parseFromOptions('test', { language: 'javascript' });
130
+ expect(result.language).toBe(Language.JAVASCRIPT);
131
+ });
132
+
133
+ it('should parse language shorthand "js"', () => {
134
+ const result = parseFromOptions('test', { language: 'js' });
135
+ expect(result.language).toBe(Language.JAVASCRIPT);
136
+ });
137
+
138
+ it('should parse project type', () => {
139
+ const result = parseFromOptions('test', { type: 'worker' });
140
+ expect(result.projectType).toBe(ProjectType.WORKER);
141
+ });
142
+
143
+ it('should parse project type case-insensitively', () => {
144
+ const result = parseFromOptions('test', { type: 'WORKER' });
145
+ expect(result.projectType).toBe(ProjectType.WORKER);
146
+ });
147
+
148
+ it('should parse template', () => {
149
+ const result = parseFromOptions('test', { template: 'hono' });
150
+ expect(result.template).toBe(TemplateType.HONO);
151
+ });
152
+
153
+ it('should parse git option', () => {
154
+ const result = parseFromOptions('test', { git: false });
155
+ expect(result.initGit).toBe(false);
156
+ });
157
+
158
+ it('should parse install option', () => {
159
+ const result = parseFromOptions('test', { install: false });
160
+ expect(result.installDeps).toBe(false);
161
+ });
162
+
163
+ it('should combine multiple options', () => {
164
+ const result = parseFromOptions('my-app', {
165
+ type: 'pages',
166
+ template: 'react',
167
+ git: true,
168
+ install: false,
169
+ language: 'ts',
170
+ });
171
+ expect(result.projectName).toBe('my-app');
172
+ expect(result.projectType).toBe(ProjectType.PAGES);
173
+ expect(result.template).toBe(TemplateType.REACT);
174
+ expect(result.initGit).toBe(true);
175
+ expect(result.installDeps).toBe(false);
176
+ expect(result.language).toBe(Language.TYPESCRIPT);
177
+ });
178
+ });
179
+
180
+ describe('getCurrentNodeVersion', () => {
181
+ it('should return version without "v" prefix', () => {
182
+ const version = getCurrentNodeVersion();
183
+ expect(version).not.toMatch(/^v/);
184
+ expect(version).toMatch(/^\d+\.\d+\.\d+/);
185
+ });
186
+ });
187
+
188
+ describe('checkNodeVersion', () => {
189
+ it('should return true for satisfied version', () => {
190
+ const result = checkNodeVersion('1.0.0');
191
+ expect(result).toBe(true);
192
+ });
193
+ });
194
+
195
+ describe('Project type mapping', () => {
196
+ it('should map worker correctly', () => {
197
+ const result = parseFromOptions('test', { type: 'worker' });
198
+ expect(result.projectType).toBe(ProjectType.WORKER);
199
+ });
200
+
201
+ it('should map pages correctly', () => {
202
+ const result = parseFromOptions('test', { type: 'pages' });
203
+ expect(result.projectType).toBe(ProjectType.PAGES);
204
+ });
205
+
206
+ it('should map d1 correctly', () => {
207
+ const result = parseFromOptions('test', { type: 'd1' });
208
+ expect(result.projectType).toBe(ProjectType.D1);
209
+ });
210
+
211
+ it('should map kv correctly', () => {
212
+ const result = parseFromOptions('test', { type: 'kv' });
213
+ expect(result.projectType).toBe(ProjectType.KV);
214
+ });
215
+
216
+ it('should map r2 correctly', () => {
217
+ const result = parseFromOptions('test', { type: 'r2' });
218
+ expect(result.projectType).toBe(ProjectType.R2);
219
+ });
220
+ });
221
+
222
+ describe('Template compatibility', () => {
223
+ it('should allow BASIC template for WORKER', () => {
224
+ const result = parseConfig({
225
+ projectName: 'test',
226
+ projectType: ProjectType.WORKER,
227
+ template: TemplateType.BASIC,
228
+ });
229
+ expect(result.template).toBe(TemplateType.BASIC);
230
+ });
231
+
232
+ it('should allow HONO template for WORKER', () => {
233
+ const result = parseConfig({
234
+ projectName: 'test',
235
+ projectType: ProjectType.WORKER,
236
+ template: TemplateType.HONO,
237
+ });
238
+ expect(result.template).toBe(TemplateType.HONO);
239
+ });
240
+
241
+ it('should allow STATIC template for PAGES', () => {
242
+ const result = parseConfig({
243
+ projectName: 'test',
244
+ projectType: ProjectType.PAGES,
245
+ template: TemplateType.STATIC,
246
+ });
247
+ expect(result.template).toBe(TemplateType.STATIC);
248
+ });
249
+
250
+ it('should allow REACT template for PAGES', () => {
251
+ const result = parseConfig({
252
+ projectName: 'test',
253
+ projectType: ProjectType.PAGES,
254
+ template: TemplateType.REACT,
255
+ });
256
+ expect(result.template).toBe(TemplateType.REACT);
257
+ });
258
+
259
+ it('should fallback to BASIC for D1', () => {
260
+ const result = parseConfig({
261
+ projectName: 'test',
262
+ projectType: ProjectType.D1,
263
+ template: TemplateType.HONO as unknown as TemplateType,
264
+ });
265
+ expect(result.template).toBe(TemplateType.BASIC);
266
+ });
267
+ });
268
+ });