birc-generator 0.5.5

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 (128) hide show
  1. package/.bircrc.example.json +14 -0
  2. package/AGENTS.md +20 -0
  3. package/PROJECT.md +83 -0
  4. package/README.md +300 -0
  5. package/bin/birc.js +645 -0
  6. package/bin/cli-util.js +428 -0
  7. package/bin/home-install.js +346 -0
  8. package/bin/update-check.js +153 -0
  9. package/implement.md +76 -0
  10. package/install.ps1 +156 -0
  11. package/install.sh +201 -0
  12. package/package.json +68 -0
  13. package/plopfile.js +1358 -0
  14. package/project-docs/AGENTS.md.hbs +12 -0
  15. package/project-docs/PROJECT.md.hbs +128 -0
  16. package/project-docs/implement.hbs +54 -0
  17. package/project-docs/test.hbs +31 -0
  18. package/scripts/install-bin.sh +72 -0
  19. package/scripts/update.sh +11 -0
  20. package/templates/aop/OperationLog.hbs +18 -0
  21. package/templates/aop/OperationLogAspect.hbs +95 -0
  22. package/templates/aop/build-gradle-dep.hbs +1 -0
  23. package/templates/base/Application.hbs +11 -0
  24. package/templates/base/ApplicationTests.java.hbs +11 -0
  25. package/templates/base/BaseService.hbs +15 -0
  26. package/templates/base/BaseServiceImpl.hbs +53 -0
  27. package/templates/base/EntityMapper.hbs +12 -0
  28. package/templates/base/README.md.hbs +56 -0
  29. package/templates/base/Result.hbs +48 -0
  30. package/templates/base/application-test.yml.hbs +27 -0
  31. package/templates/base/application.yml.hbs +18 -0
  32. package/templates/base/bircrc.hbs +14 -0
  33. package/templates/base/build.gradle.hbs +33 -0
  34. package/templates/base/gitignore.hbs +15 -0
  35. package/templates/client/ExternalApiClient.hbs +28 -0
  36. package/templates/client/ExternalApiClientConfig.hbs +26 -0
  37. package/templates/client/ExternalApiProperties.hbs +30 -0
  38. package/templates/client/application-yml-block.hbs +7 -0
  39. package/templates/controller.hbs +55 -0
  40. package/templates/dao.hbs +11 -0
  41. package/templates/docker/Dockerfile.hbs +29 -0
  42. package/templates/docker/docker-compose.prod.yml.hbs +65 -0
  43. package/templates/docker/docker-compose.yml.hbs +52 -0
  44. package/templates/docker/dockerignore.hbs +6 -0
  45. package/templates/docker/env.example.hbs +24 -0
  46. package/templates/dto-request.hbs +19 -0
  47. package/templates/dto-response.hbs +13 -0
  48. package/templates/email/EmailAsyncConfig.hbs +28 -0
  49. package/templates/email/EmailService.hbs +26 -0
  50. package/templates/email/EmailServiceImpl.hbs +49 -0
  51. package/templates/email/application-yml-block.hbs +19 -0
  52. package/templates/email/build-gradle-dep.hbs +2 -0
  53. package/templates/email/sample-template.hbs +16 -0
  54. package/templates/entity.hbs +46 -0
  55. package/templates/exception/ExceptionHandleController.hbs +16 -0
  56. package/templates/exception/NotFoundException.hbs +28 -0
  57. package/templates/exception/ProjectException.hbs +20 -0
  58. package/templates/exception.hbs +30 -0
  59. package/templates/file-upload/EmptyFileException.hbs +25 -0
  60. package/templates/file-upload/FileExtensionIllegalException.hbs +21 -0
  61. package/templates/file-upload/FileExtensionUtils.hbs +20 -0
  62. package/templates/file-upload/FileStorageProperties.hbs +37 -0
  63. package/templates/file-upload/FileStorageService.hbs +15 -0
  64. package/templates/file-upload/FileStorageServiceImpl.hbs +78 -0
  65. package/templates/file-upload/FileTooLargeException.hbs +21 -0
  66. package/templates/file-upload/FileUploadController.hbs +57 -0
  67. package/templates/file-upload/FileUtils.hbs +39 -0
  68. package/templates/file-upload/InvalidStoredFileException.hbs +21 -0
  69. package/templates/file-upload/application-yml-block.hbs +12 -0
  70. package/templates/gitlab-ci/gitlab-ci.hbs +158 -0
  71. package/templates/gradle-wrapper/gradle/wrapper/gradle-wrapper.jar +0 -0
  72. package/templates/gradle-wrapper/gradle/wrapper/gradle-wrapper.properties +8 -0
  73. package/templates/gradle-wrapper/gradlew +248 -0
  74. package/templates/gradle-wrapper/gradlew.bat +93 -0
  75. package/templates/log4j2/build-gradle-dep.hbs +6 -0
  76. package/templates/log4j2/log4j2-spring.xml.hbs +34 -0
  77. package/templates/mapper-gradle-dep.hbs +3 -0
  78. package/templates/mapper.hbs +11 -0
  79. package/templates/migration/add-column.hbs +4 -0
  80. package/templates/migration/create-table.hbs +9 -0
  81. package/templates/migration/custom.hbs +2 -0
  82. package/templates/migration/remove-column.hbs +4 -0
  83. package/templates/migration/seed.hbs +7 -0
  84. package/templates/multi-module/build.gradle.hbs +86 -0
  85. package/templates/multi-module/config/ApplicationConfig.java.hbs +33 -0
  86. package/templates/multi-module/config/Config.java.hbs +19 -0
  87. package/templates/multi-module/config/SecurityConfig.java.hbs +48 -0
  88. package/templates/multi-module/config/build.gradle.hbs +10 -0
  89. package/templates/multi-module/database-config/BaseDAO.java.hbs +31 -0
  90. package/templates/multi-module/database-config/BaseViewDAO.java.hbs +48 -0
  91. package/templates/multi-module/database-config/Config.java.hbs +14 -0
  92. package/templates/multi-module/database-config/JpaConfig.java.hbs +9 -0
  93. package/templates/multi-module/database-config/build.gradle.hbs +17 -0
  94. package/templates/multi-module/settings.gradle.hbs +9 -0
  95. package/templates/oauth/OAuth2LoginSuccessHandler.hbs +31 -0
  96. package/templates/oauth/application-yml-block.hbs +11 -0
  97. package/templates/oauth/build-gradle-dep.hbs +1 -0
  98. package/templates/openapi/OpenApiConfig.hbs +23 -0
  99. package/templates/openapi/build-gradle-dep.hbs +1 -0
  100. package/templates/pagination/GenericSpecification.hbs +76 -0
  101. package/templates/pagination/PageInfo.hbs +20 -0
  102. package/templates/pagination/PageRequest.hbs +27 -0
  103. package/templates/pagination/PageResponse.hbs +24 -0
  104. package/templates/pagination/Pager.hbs +37 -0
  105. package/templates/pagination/SearchCriteria.hbs +26 -0
  106. package/templates/pagination/SpecificationSupport.hbs +34 -0
  107. package/templates/permission/PermissionAspect.hbs +41 -0
  108. package/templates/permission/RequirePermission.hbs +18 -0
  109. package/templates/permission/build-gradle-dep.hbs +2 -0
  110. package/templates/scheduling/SampleSchedule.hbs +22 -0
  111. package/templates/scheduling/SchedulingConfig.hbs +13 -0
  112. package/templates/scheduling/application-yml-block.hbs +5 -0
  113. package/templates/sentry/SentryConfig.hbs +74 -0
  114. package/templates/sentry/application-yml-block.hbs +15 -0
  115. package/templates/sentry/build-gradle-dep.hbs +1 -0
  116. package/templates/service.hbs +9 -0
  117. package/templates/serviceImpl.hbs +23 -0
  118. package/templates/spotless/build-gradle-allprojects.hbs +16 -0
  119. package/templates/spotless/build-gradle-plugin.hbs +1 -0
  120. package/templates/spotless/spotless_formatter.xml +83 -0
  121. package/templates/sso/SsoAutoConfiguration.hbs +17 -0
  122. package/templates/sso/SsoProperties.hbs +27 -0
  123. package/templates/sso/application-yml-block.hbs +7 -0
  124. package/templates/valid-group/ValidGroup.hbs +20 -0
  125. package/test.md +56 -0
  126. package/uninstall.ps1 +60 -0
  127. package/uninstall.sh +76 -0
  128. package/versions.js +10 -0
@@ -0,0 +1,428 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { spawnSync } = require('child_process');
4
+
5
+ function pathForBash(filePath, ostype) {
6
+ const withSlashes = String(filePath || '').replace(/\\/g, '/');
7
+ const match = withSlashes.match(/^([A-Za-z]):\/(.*)$/);
8
+ if (!match) {
9
+ return withSlashes;
10
+ }
11
+ const drive = match[1].toLowerCase();
12
+ const rest = match[2];
13
+ const type = String(ostype || '').toLowerCase();
14
+ if (type.includes('msys') || type.includes('cygwin') || type.includes('mingw')) {
15
+ return `/${drive}/${rest}`;
16
+ }
17
+ return `/mnt/${drive}/${rest}`;
18
+ }
19
+
20
+ function bashOstype() {
21
+ const result = spawnSync('bash', ['-c', 'printf %s "$OSTYPE"'], {
22
+ encoding: 'utf8',
23
+ timeout: 8000
24
+ });
25
+ if (result.error) {
26
+ throw result.error;
27
+ }
28
+ if (result.status !== 0) {
29
+ throw new Error(result.stderr ? String(result.stderr).trim() : 'bash 結束狀態非 0');
30
+ }
31
+ return (result.stdout || '').trim();
32
+ }
33
+
34
+ function resolveGradleWrapper(root, platform = process.platform, exists = fs.existsSync) {
35
+ const unixWrapper = path.join(root, 'gradlew');
36
+ const windowsWrapper = path.join(root, 'gradlew.bat');
37
+ if (platform === 'win32') {
38
+ if (exists(windowsWrapper)) {
39
+ return { command: windowsWrapper, shell: true };
40
+ }
41
+ return null;
42
+ }
43
+ if (exists(unixWrapper)) {
44
+ return { command: unixWrapper, shell: false };
45
+ }
46
+ return null;
47
+ }
48
+
49
+ function gradleWrapperMissingMessage(platform = process.platform) {
50
+ return platform === 'win32'
51
+ ? '找不到 gradlew.bat,請在 birc create 產出的專案根目錄執行。'
52
+ : '找不到 ./gradlew,請在 birc create 產出的專案根目錄執行。';
53
+ }
54
+
55
+ function projectConfigMissingMessage() {
56
+ return '找不到 .bircrc.json,先在專案根目錄建立設定檔(可複製 .bircrc.example.json 改名,或用 birc create 建新專案)';
57
+ }
58
+
59
+ function findProjectRoot(startDir, exists = fs.existsSync) {
60
+ let current = path.resolve(startDir || '');
61
+ const { root } = path.parse(current);
62
+ while (true) {
63
+ if (exists(path.join(current, '.bircrc.json'))) {
64
+ return current;
65
+ }
66
+ if (current === root) {
67
+ return null;
68
+ }
69
+ const parent = path.dirname(current);
70
+ if (parent === current) {
71
+ return null;
72
+ }
73
+ current = parent;
74
+ }
75
+ }
76
+
77
+ function toKebabName(value) {
78
+ return String(value || '')
79
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
80
+ .replace(/[\s_]+/g, '-')
81
+ .replace(/^-+|-+$/g, '')
82
+ .toLowerCase();
83
+ }
84
+
85
+ function deriveBasePackage(projectName) {
86
+ const kebab = toKebabName(projectName);
87
+ if (!kebab) {
88
+ return null;
89
+ }
90
+ return `tw.edu.ntub.birc.${kebab.replace(/-/g, '.')}`;
91
+ }
92
+
93
+ function featureAliasMap(featureKeys) {
94
+ const aliases = new Map();
95
+ for (const key of featureKeys || []) {
96
+ aliases.set(key, key);
97
+ aliases.set(String(key).toLowerCase(), key);
98
+ aliases.set(toKebabName(key), key);
99
+ }
100
+ return aliases;
101
+ }
102
+
103
+ function resolveFeatureArgs(args, featureKeys) {
104
+ const names = Array.isArray(args) ? args : [];
105
+ if (names.length === 0) {
106
+ return { keys: null };
107
+ }
108
+ const aliases = featureAliasMap(featureKeys);
109
+ const keys = [];
110
+ const unknown = [];
111
+ for (const name of names) {
112
+ const key = aliases.get(String(name)) || aliases.get(String(name).toLowerCase());
113
+ if (!key) {
114
+ unknown.push(name);
115
+ } else if (!keys.includes(key)) {
116
+ keys.push(key);
117
+ }
118
+ }
119
+ if (unknown.length > 0) {
120
+ const knownNames = [];
121
+ for (const key of featureKeys || []) {
122
+ knownNames.push(key);
123
+ const kebab = toKebabName(key);
124
+ if (kebab && kebab !== key) knownNames.push(kebab);
125
+ }
126
+ const suggestions = [...new Set(
127
+ unknown.map((name) => suggestCommand(name, knownNames)).filter(Boolean)
128
+ )];
129
+ const lines = [`找不到功能: ${unknown.join(', ')}`];
130
+ if (suggestions.length > 0) {
131
+ lines.push(`你要的大概是 ${suggestions.join('、')}。`);
132
+ }
133
+ lines.push(`可用: ${(featureKeys || []).join(', ')}`);
134
+ return { error: lines.join('\n') };
135
+ }
136
+ return { keys };
137
+ }
138
+
139
+ function applyAddFeatureArgs(prompts, args, featureKeys) {
140
+ const resolved = resolveFeatureArgs(args, featureKeys);
141
+ if (resolved.error || !resolved.keys) {
142
+ return resolved;
143
+ }
144
+ for (const prompt of prompts || []) {
145
+ if (prompt.name === 'features') {
146
+ prompt.when = () => false;
147
+ }
148
+ if (prompt.name === 'gitlabProjectPath' && typeof prompt.when === 'function') {
149
+ const originalWhen = prompt.when;
150
+ prompt.when = (answers) => originalWhen({ ...answers, features: resolved.keys });
151
+ }
152
+ }
153
+ return resolved;
154
+ }
155
+
156
+ function applyCreatePromptDefaults(prompts, positionals = []) {
157
+ const [projectName, basePackage] = positionals;
158
+ const derivedPackage = projectName ? deriveBasePackage(projectName) : null;
159
+ for (const prompt of prompts) {
160
+ if (prompt.name === 'projectName' && projectName) {
161
+ prompt.default = projectName;
162
+ }
163
+ if (prompt.name === 'basePackage') {
164
+ if (basePackage) {
165
+ prompt.default = basePackage;
166
+ } else if (derivedPackage) {
167
+ prompt.default = derivedPackage;
168
+ }
169
+ }
170
+ }
171
+ return prompts;
172
+ }
173
+
174
+ function applyCreateYes(prompts, positionals = [], defaultFeatures = []) {
175
+ applyCreatePromptDefaults(prompts, positionals);
176
+ const answers = {};
177
+ for (const prompt of prompts || []) {
178
+ prompt.when = () => false;
179
+ if (prompt.name === 'projectName') {
180
+ answers.projectName = prompt.default;
181
+ }
182
+ if (prompt.name === 'basePackage') {
183
+ answers.basePackage = typeof prompt.default === 'function'
184
+ ? prompt.default(answers)
185
+ : prompt.default;
186
+ }
187
+ if (prompt.name === 'features') {
188
+ answers.features = [...defaultFeatures];
189
+ }
190
+ if (prompt.name === 'injectDocs') {
191
+ answers.injectDocs = prompt.default !== undefined ? prompt.default : true;
192
+ }
193
+ }
194
+ return answers;
195
+ }
196
+
197
+ function alreadyExistsMessage(fail = {}) {
198
+ const err = String(fail.error || fail.message || '');
199
+ if (!/already exists/i.test(err)) {
200
+ return null;
201
+ }
202
+ const fromError = err.match(/->\s*(.+)$/m);
203
+ const rawPath = fail.path || (fromError && fromError[1].trim()) || '';
204
+ const fileName = path.basename(String(rawPath).trim());
205
+ if (!fileName) {
206
+ return '檔案已在,要覆寫用 --force';
207
+ }
208
+ return `${fileName} 已在,要覆寫用 --force`;
209
+ }
210
+
211
+ function fieldsWithoutExampleMessage(flags = {}) {
212
+ const hasFields = Boolean(String(flags.fields || '').trim());
213
+ if (!hasFields || flags.example) {
214
+ return null;
215
+ }
216
+ return '--fields 要配 --example 才會寫進 Entity';
217
+ }
218
+
219
+ function levenshtein(left, right) {
220
+ const a = String(left || '');
221
+ const b = String(right || '');
222
+ const rows = a.length + 1;
223
+ const cols = b.length + 1;
224
+ const prev = new Array(cols);
225
+ const cur = new Array(cols);
226
+ for (let j = 0; j < cols; j += 1) prev[j] = j;
227
+ for (let i = 1; i < rows; i += 1) {
228
+ cur[0] = i;
229
+ for (let j = 1; j < cols; j += 1) {
230
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
231
+ cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + cost);
232
+ }
233
+ for (let j = 0; j < cols; j += 1) prev[j] = cur[j];
234
+ }
235
+ return prev[b.length];
236
+ }
237
+
238
+ function suggestCommand(name, known) {
239
+ const raw = String(name || '');
240
+ if (!raw) return null;
241
+ const commands = known || [];
242
+ const colonNormalized = raw.replace(/::/g, ':');
243
+ if (colonNormalized !== raw && commands.includes(colonNormalized)) {
244
+ return colonNormalized;
245
+ }
246
+ const dashNormalized = colonNormalized.replace(/-/g, ':');
247
+ if (dashNormalized !== raw && commands.includes(dashNormalized)) {
248
+ return dashNormalized;
249
+ }
250
+ const needle = colonNormalized.toLowerCase();
251
+ let best = null;
252
+ let bestDist = Infinity;
253
+ for (const command of commands) {
254
+ const dist = levenshtein(needle, String(command).toLowerCase());
255
+ if (dist < bestDist) {
256
+ bestDist = dist;
257
+ best = command;
258
+ }
259
+ }
260
+ const maxDist = needle.length <= 4 ? 1 : 2;
261
+ if (best && bestDist > 0 && bestDist <= maxDist) {
262
+ return best;
263
+ }
264
+ return null;
265
+ }
266
+
267
+ function shouldPrintBanner(command, flags = {}) {
268
+ if (flags.help || flags.version || !command || command === 'help') {
269
+ return true;
270
+ }
271
+ return command === 'create';
272
+ }
273
+
274
+ function formatAddNextSteps(answers = {}, { changed } = {}) {
275
+ const features = answers.features || [];
276
+ if (features.length === 0) {
277
+ return ['', '沒有加入任何功能。用 space 勾選後 enter,或 birc add email。', ''];
278
+ }
279
+ if (!changed) {
280
+ return ['', '這些功能已安裝,沒有改檔。要覆寫用 birc add --force。', ''];
281
+ }
282
+ const lines = ['', `已加入 ${features.join('、')}。`];
283
+ if (features.includes('docker')) {
284
+ lines.push('啟動資料庫:docker compose up -d db');
285
+ }
286
+ lines.push('');
287
+ return lines;
288
+ }
289
+
290
+ function formatMakeNextSteps(name, answers = {}) {
291
+ const command = String(name || '');
292
+ const wantsMigrate = command === 'make:migration'
293
+ || command === 'migration'
294
+ || Boolean(answers.migration)
295
+ || Boolean(answers.seed);
296
+ if (wantsMigrate) {
297
+ return ['', '套用遷移:', ' birc migrate', ''];
298
+ }
299
+ if (command === 'make' || command.startsWith('make:')) {
300
+ return ['', '骨架已寫好。建表可加 --migration,再 birc migrate。', ''];
301
+ }
302
+ return [];
303
+ }
304
+
305
+ function applyMakeFieldsFlag(prompts, fields) {
306
+ const value = fields == null ? '' : String(fields).trim();
307
+ if (!value) {
308
+ return prompts;
309
+ }
310
+ for (const prompt of prompts || []) {
311
+ if (prompt.name === 'fields') {
312
+ prompt.when = () => false;
313
+ prompt.default = value;
314
+ }
315
+ }
316
+ return prompts;
317
+ }
318
+
319
+ function parseArgv(argv) {
320
+ const flags = {
321
+ help: false,
322
+ version: false,
323
+ force: false,
324
+ yes: false,
325
+ example: false,
326
+ dto: false,
327
+ mapper: false,
328
+ migration: false,
329
+ seed: false,
330
+ controller: false,
331
+ softDelete: false,
332
+ fields: undefined,
333
+ positionals: []
334
+ };
335
+ const suggestable = [
336
+ '--help', '--version', '--force', '--yes', '--example', '--dto', '--mapper',
337
+ '--migration', '--seed', '--controller', '--soft-delete', '--fields', '--dest'
338
+ ];
339
+ const args = Array.isArray(argv) ? argv : [];
340
+ for (let i = 0; i < args.length; i += 1) {
341
+ const arg = args[i];
342
+ if (arg === '-h' || arg === '--help') flags.help = true;
343
+ else if (arg === '-v' || arg === '--version') flags.version = true;
344
+ else if (arg === '-f' || arg === '--force') flags.force = true;
345
+ else if (arg === '-y' || arg === '--yes') flags.yes = true;
346
+ else if (arg === '--example' || arg === '--exmaple') flags.example = true;
347
+ else if (arg === '--dto') flags.dto = true;
348
+ else if (arg === '--mapper') flags.mapper = true;
349
+ else if (arg === '--migration') flags.migration = true;
350
+ else if (arg === '--seed') flags.seed = true;
351
+ else if (arg === '--controller') flags.controller = true;
352
+ else if (arg === '--soft-delete') flags.softDelete = true;
353
+ else if (arg === '--fields') {
354
+ const next = args[i + 1];
355
+ if (next && !next.startsWith('-')) {
356
+ flags.fields = next;
357
+ i += 1;
358
+ } else {
359
+ flags.fields = '';
360
+ }
361
+ } else if (arg.startsWith('--fields=')) {
362
+ flags.fields = arg.slice('--fields='.length);
363
+ } else if (arg === '--dest') {
364
+ i += 1;
365
+ } else if (String(arg).startsWith('-')) {
366
+ const name = String(arg).includes('=') ? String(arg).slice(0, String(arg).indexOf('=')) : arg;
367
+ const suggestion = suggestCommand(name, suggestable);
368
+ const lines = [`找不到選項: ${name}`];
369
+ if (suggestion) lines.push(`你要的大概是 ${suggestion}。`);
370
+ lines.push('看全部選項:birc --help');
371
+ return { error: lines.join('\n') };
372
+ } else {
373
+ flags.positionals.push(arg);
374
+ }
375
+ }
376
+ return { flags };
377
+ }
378
+
379
+ function createDestConflict({ cwd, projectName, exists = fs.existsSync } = {}) {
380
+ const root = path.resolve(cwd || '');
381
+ const marker = path.join(root, '.bircrc.json');
382
+ if (exists(marker)) {
383
+ return `這裡已經是 BIRC 專案(${marker})。換一個空目錄再 birc create。`;
384
+ }
385
+ const dir = toKebabName(projectName);
386
+ if (!dir) return null;
387
+ const dest = path.join(root, dir);
388
+ if (exists(dest)) {
389
+ return `目標資料夾已存在:${dest}。換名稱,或刪掉後再 create。`;
390
+ }
391
+ return null;
392
+ }
393
+
394
+ function toPascalCaseName(value) {
395
+ const raw = String(value || '').trim();
396
+ if (!raw) return '';
397
+ return raw
398
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
399
+ .split(/[^A-Za-z0-9]+/)
400
+ .filter(Boolean)
401
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
402
+ .join('');
403
+ }
404
+
405
+ module.exports = {
406
+ pathForBash,
407
+ bashOstype,
408
+ resolveGradleWrapper,
409
+ gradleWrapperMissingMessage,
410
+ projectConfigMissingMessage,
411
+ findProjectRoot,
412
+ deriveBasePackage,
413
+ toKebabName,
414
+ resolveFeatureArgs,
415
+ applyAddFeatureArgs,
416
+ applyCreatePromptDefaults,
417
+ applyCreateYes,
418
+ alreadyExistsMessage,
419
+ fieldsWithoutExampleMessage,
420
+ suggestCommand,
421
+ shouldPrintBanner,
422
+ formatAddNextSteps,
423
+ formatMakeNextSteps,
424
+ applyMakeFieldsFlag,
425
+ toPascalCaseName,
426
+ parseArgv,
427
+ createDestConflict
428
+ };