flu-cli 2.0.5 → 2.1.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 (45) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +17 -4
  3. package/config/dev.config.js +11 -11
  4. package/config/templates.js +10 -10
  5. package/index.js +554 -102
  6. package/lib/commands/add.js +365 -266
  7. package/lib/commands/assets.js +77 -78
  8. package/lib/commands/cache.js +29 -52
  9. package/lib/commands/completion.js +13 -11
  10. package/lib/commands/config.js +150 -44
  11. package/lib/commands/init-ai-base.js +89 -0
  12. package/lib/commands/newClack.js +269 -178
  13. package/lib/commands/snippets.js +58 -43
  14. package/lib/commands/template.js +98 -58
  15. package/lib/commands/templates.js +101 -57
  16. package/lib/commands/upload.js +313 -0
  17. package/lib/commands/vnext-options.js +206 -0
  18. package/lib/generators/model_generator.js +91 -88
  19. package/lib/generators/page_generator.js +100 -93
  20. package/lib/generators/service_generator.js +44 -39
  21. package/lib/generators/viewmodel_generator.js +25 -29
  22. package/lib/generators/widget_generator.js +30 -35
  23. package/lib/templates/templateCopier.js +14 -15
  24. package/lib/templates/templateManager.js +22 -21
  25. package/lib/utils/config.js +37 -20
  26. package/lib/utils/flutterHelper.js +2 -2
  27. package/lib/utils/i18n.js +3 -3
  28. package/lib/utils/index_updater.js +22 -23
  29. package/lib/utils/json-output.js +59 -0
  30. package/lib/utils/logger.js +17 -17
  31. package/lib/utils/project_detector.js +66 -66
  32. package/lib/utils/snippet_loader.js +21 -19
  33. package/lib/utils/string_helper.js +13 -13
  34. package/lib/utils/templateSelectorEnquirer.js +94 -108
  35. package/locales/en-US.json +1 -1
  36. package/locales/zh-CN.json +2 -2
  37. package/package.json +60 -57
  38. package/scripts/smoke-vnext-generate.mjs +1934 -0
  39. package/scripts/smoke-vnext-params.mjs +92 -0
  40. package/CLI.md +0 -513
  41. package/release.sh +0 -529
  42. package/scripts/e2e-state-tests.js +0 -116
  43. package/scripts/sync-base-to-templates.js +0 -108
  44. package/scripts/workspace-clone-all.sh +0 -101
  45. package/scripts/workspace-status-all.sh +0 -112
@@ -3,67 +3,66 @@
3
3
  * 自动更新目录的 index.dart 导出文件
4
4
  */
5
5
 
6
- import { existsSync, readFileSync, writeFileSync } from 'fs';
7
- import { join } from 'path';
8
- import { logger } from './logger.js';
6
+ import { existsSync, readFileSync, writeFileSync } from 'fs'
7
+ import { join } from 'path'
8
+ import { logger } from './logger.js'
9
9
 
10
10
  /**
11
11
  * 更新 index.dart 文件
12
12
  * @param {string} dirPath - 目录路径
13
13
  * @param {string} fileName - 要添加的文件名
14
14
  */
15
- export function updateIndexFile (dirPath, fileName) {
15
+ export function updateIndexFile(dirPath, fileName) {
16
16
  try {
17
- const indexPath = join(dirPath, 'index.dart');
17
+ const indexPath = join(dirPath, 'index.dart')
18
18
 
19
19
  // 如果 index.dart 不存在,不做处理
20
20
  if (!existsSync(indexPath)) {
21
- return false;
21
+ return false
22
22
  }
23
23
 
24
24
  // 读取现有内容
25
- let content = readFileSync(indexPath, 'utf8');
25
+ let content = readFileSync(indexPath, 'utf8')
26
26
 
27
27
  // 检查是否已经导出
28
- const exportStatement = `export '${fileName}';`;
28
+ const exportStatement = `export '${fileName}';`
29
29
  if (content.includes(exportStatement)) {
30
- return false; // 已经存在,不需要更新
30
+ return false // 已经存在,不需要更新
31
31
  }
32
32
 
33
33
  // 找到注释行后添加导出语句
34
- const lines = content.split('\n');
35
- let insertIndex = -1;
34
+ const lines = content.split('\n')
35
+ let insertIndex = -1
36
36
 
37
37
  // 找到第一个注释行之后的位置
38
38
  for (let i = 0; i < lines.length; i++) {
39
39
  if (lines[i].trim().startsWith('//')) {
40
- insertIndex = i + 1;
41
- break;
40
+ insertIndex = i + 1
41
+ break
42
42
  }
43
43
  }
44
44
 
45
45
  // 如果没找到注释,添加到末尾
46
46
  if (insertIndex === -1) {
47
- insertIndex = lines.length;
47
+ insertIndex = lines.length
48
48
  }
49
49
 
50
50
  // 跳过空行
51
51
  while (insertIndex < lines.length && lines[insertIndex].trim() === '') {
52
- insertIndex++;
52
+ insertIndex++
53
53
  }
54
54
 
55
55
  // 插入导出语句
56
- lines.splice(insertIndex, 0, exportStatement);
56
+ lines.splice(insertIndex, 0, exportStatement)
57
57
 
58
58
  // 写回文件
59
- content = lines.join('\n');
60
- writeFileSync(indexPath, content, 'utf8');
61
-
62
- logger.info(`已更新 index.dart: ${exportStatement}`);
63
- return true;
59
+ content = lines.join('\n')
60
+ writeFileSync(indexPath, content, 'utf8')
64
61
 
62
+ logger.info(`已更新 index.dart: ${exportStatement}`)
63
+ return true
65
64
  } catch (error) {
66
- logger.warn(`更新 index.dart 失败: ${error.message}`);
67
- return false;
65
+ logger.warn(`更新 index.dart 失败: ${error.message}`)
66
+ return false
68
67
  }
69
68
  }
@@ -0,0 +1,59 @@
1
+ export function isJsonMode(options) {
2
+ return options?.json === true
3
+ }
4
+
5
+ export function createNullLogger() {
6
+ return {
7
+ info() {},
8
+ success() {},
9
+ warn() {},
10
+ error() {},
11
+ title() {},
12
+ newLine() {},
13
+ }
14
+ }
15
+
16
+ export function buildJsonEnvelope(command, ok, diagnostics, data) {
17
+ return {
18
+ ok: ok === true,
19
+ command,
20
+ diagnostics: Array.isArray(diagnostics) ? diagnostics : [],
21
+ data: data ?? null,
22
+ }
23
+ }
24
+
25
+ export function printJsonEnvelope(envelope) {
26
+ console.log(JSON.stringify(envelope, null, 2))
27
+ }
28
+
29
+ export async function runMuted(action) {
30
+ const prevLog = console.log
31
+ const prevInfo = console.info
32
+ const prevWarn = console.warn
33
+ const prevError = console.error
34
+ const prevStdoutWrite = process.stdout.write
35
+ const prevStderrWrite = process.stderr.write
36
+
37
+ const mute = () => true
38
+ console.log = mute
39
+ console.info = mute
40
+ console.warn = mute
41
+ console.error = mute
42
+ process.stdout.write = mute
43
+ process.stderr.write = mute
44
+
45
+ try {
46
+ const res = await action()
47
+ return { ok: true, result: res }
48
+ } catch (e) {
49
+ const message = e?.message ? String(e.message) : String(e)
50
+ return { ok: false, error: message }
51
+ } finally {
52
+ console.log = prevLog
53
+ console.info = prevInfo
54
+ console.warn = prevWarn
55
+ console.error = prevError
56
+ process.stdout.write = prevStdoutWrite
57
+ process.stderr.write = prevStderrWrite
58
+ }
59
+ }
@@ -3,55 +3,55 @@
3
3
  * 统一的日志输出格式
4
4
  */
5
5
 
6
- import chalk from 'chalk';
6
+ import chalk from 'chalk'
7
7
 
8
8
  export const logger = {
9
9
  /**
10
10
  * 成功信息
11
11
  */
12
- success (message) {
13
- console.log(chalk.green('✅ ' + message));
12
+ success(message) {
13
+ console.log(chalk.green('✅ ' + message))
14
14
  },
15
15
 
16
16
  /**
17
17
  * 错误信息
18
18
  */
19
- error (message) {
20
- console.log(chalk.red('❌ ' + message));
19
+ error(message) {
20
+ console.log(chalk.red('❌ ' + message))
21
21
  },
22
22
 
23
23
  /**
24
24
  * 警告信息
25
25
  */
26
- warn (message) {
27
- console.log(chalk.yellow('⚠️ ' + message));
26
+ warn(message) {
27
+ console.log(chalk.yellow('⚠️ ' + message))
28
28
  },
29
29
 
30
30
  /**
31
31
  * 信息
32
32
  */
33
- info (message) {
34
- console.log(chalk.blue('ℹ️ ' + message));
33
+ info(message) {
34
+ console.log(chalk.blue('ℹ️ ' + message))
35
35
  },
36
36
 
37
37
  /**
38
38
  * 标题
39
39
  */
40
- title (message) {
41
- console.log(chalk.bold.cyan('\n' + message + '\n'));
40
+ title(message) {
41
+ console.log(chalk.bold.cyan('\n' + message + '\n'))
42
42
  },
43
43
 
44
44
  /**
45
45
  * 分隔线
46
46
  */
47
- divider () {
48
- console.log(chalk.gray('─'.repeat(60)));
47
+ divider() {
48
+ console.log(chalk.gray('─'.repeat(60)))
49
49
  },
50
50
 
51
51
  /**
52
52
  * 空行
53
53
  */
54
- newLine () {
55
- console.log('');
56
- }
57
- };
54
+ newLine() {
55
+ console.log('')
56
+ },
57
+ }
@@ -3,59 +3,59 @@
3
3
  * 职责:基于目录结构识别 lite/modular/clean 模板,并提供生成路径解析
4
4
  */
5
5
 
6
- import { existsSync } from 'fs';
7
- import { join } from 'path';
6
+ import { existsSync } from 'fs'
7
+ import { join } from 'path'
8
8
 
9
9
  /**
10
10
  * 检测项目模板类型
11
11
  * @param {string} projectDir - 项目目录
12
12
  * @returns {'lite'|'modular'|'clean'|null} 模板类型
13
13
  */
14
- export function detectProjectTemplate (projectDir = process.cwd()) {
14
+ export function detectProjectTemplate(projectDir = process.cwd()) {
15
15
  try {
16
- const libDir = join(projectDir, 'lib');
16
+ const libDir = join(projectDir, 'lib')
17
17
 
18
18
  if (!existsSync(libDir)) {
19
- return null;
19
+ return null
20
20
  }
21
21
 
22
22
  // 检测 Clean 架构特征
23
23
  // Clean 架构有 features/xxx/data, features/xxx/domain, features/xxx/presentation
24
- const featuresDir = join(libDir, 'features');
24
+ const featuresDir = join(libDir, 'features')
25
25
  if (existsSync(featuresDir)) {
26
26
  // 检查是否有 data/domain/presentation 三层结构
27
- const coreDir = join(libDir, 'core');
28
- const hasUsecases = existsSync(join(coreDir, 'usecases'));
29
- const hasErrors = existsSync(join(coreDir, 'errors'));
27
+ const coreDir = join(libDir, 'core')
28
+ const hasUsecases = existsSync(join(coreDir, 'usecases'))
29
+ const hasErrors = existsSync(join(coreDir, 'errors'))
30
30
 
31
31
  if (hasUsecases && hasErrors) {
32
- return 'clean';
32
+ return 'clean'
33
33
  }
34
34
 
35
35
  // 检测 Modular 架构特征
36
36
  // Modular 架构有 core/base, core/router, shared
37
- const hasBase = existsSync(join(coreDir, 'base'));
38
- const hasRouter = existsSync(join(coreDir, 'router'));
39
- const sharedDir = existsSync(join(libDir, 'shared'));
37
+ const hasBase = existsSync(join(coreDir, 'base'))
38
+ const hasRouter = existsSync(join(coreDir, 'router'))
39
+ const sharedDir = existsSync(join(libDir, 'shared'))
40
40
 
41
41
  if (hasBase && hasRouter && sharedDir) {
42
- return 'modular';
42
+ return 'modular'
43
43
  }
44
44
  }
45
45
 
46
46
  // 检测 Lite 架构特征
47
47
  // Lite 架构有 pages, viewmodels, widgets 等扁平目录
48
- const pagesDir = join(libDir, 'pages');
49
- const viewmodelsDir = join(libDir, 'viewmodels');
50
- const widgetsDir = join(libDir, 'widgets');
48
+ const pagesDir = join(libDir, 'pages')
49
+ const viewmodelsDir = join(libDir, 'viewmodels')
50
+ const widgetsDir = join(libDir, 'widgets')
51
51
 
52
52
  if (existsSync(pagesDir) && existsSync(viewmodelsDir) && existsSync(widgetsDir)) {
53
- return 'lite';
53
+ return 'lite'
54
54
  }
55
55
 
56
- return null;
56
+ return null
57
57
  } catch (error) {
58
- return null;
58
+ return null
59
59
  }
60
60
  }
61
61
 
@@ -65,21 +65,21 @@ export function detectProjectTemplate (projectDir = process.cwd()) {
65
65
  * @param {string} moduleName - 模块名称
66
66
  * @returns {string} 页面目录路径
67
67
  */
68
- export function getPagePath (projectDir, moduleName) {
69
- const template = detectProjectTemplate(projectDir);
68
+ export function getPagePath(projectDir, moduleName) {
69
+ const template = detectProjectTemplate(projectDir)
70
70
 
71
71
  switch (template) {
72
72
  case 'lite':
73
- return join(projectDir, 'lib', 'pages');
73
+ return join(projectDir, 'lib', 'pages')
74
74
 
75
75
  case 'modular':
76
- return join(projectDir, 'lib', 'features', moduleName, 'pages');
76
+ return join(projectDir, 'lib', 'features', moduleName, 'pages')
77
77
 
78
78
  case 'clean':
79
- return join(projectDir, 'lib', 'features', moduleName, 'presentation', 'pages');
79
+ return join(projectDir, 'lib', 'features', moduleName, 'presentation', 'pages')
80
80
 
81
81
  default:
82
- return join(projectDir, 'lib', 'pages');
82
+ return join(projectDir, 'lib', 'pages')
83
83
  }
84
84
  }
85
85
 
@@ -89,21 +89,21 @@ export function getPagePath (projectDir, moduleName) {
89
89
  * @param {string} moduleName - 模块名称
90
90
  * @returns {string} ViewModel 目录路径
91
91
  */
92
- export function getViewModelPath (projectDir, moduleName) {
93
- const template = detectProjectTemplate(projectDir);
92
+ export function getViewModelPath(projectDir, moduleName) {
93
+ const template = detectProjectTemplate(projectDir)
94
94
 
95
95
  switch (template) {
96
96
  case 'lite':
97
- return join(projectDir, 'lib', 'viewmodels');
97
+ return join(projectDir, 'lib', 'viewmodels')
98
98
 
99
99
  case 'modular':
100
- return join(projectDir, 'lib', 'features', moduleName, 'viewmodels');
100
+ return join(projectDir, 'lib', 'features', moduleName, 'viewmodels')
101
101
 
102
102
  case 'clean':
103
- return join(projectDir, 'lib', 'features', moduleName, 'presentation', 'viewmodels');
103
+ return join(projectDir, 'lib', 'features', moduleName, 'presentation', 'viewmodels')
104
104
 
105
105
  default:
106
- return join(projectDir, 'lib', 'viewmodels');
106
+ return join(projectDir, 'lib', 'viewmodels')
107
107
  }
108
108
  }
109
109
 
@@ -113,27 +113,27 @@ export function getViewModelPath (projectDir, moduleName) {
113
113
  * @param {string} moduleName - 模块名称(可选)
114
114
  * @returns {string} Widget 目录路径
115
115
  */
116
- export function getWidgetPath (projectDir, moduleName = null) {
117
- const template = detectProjectTemplate(projectDir);
116
+ export function getWidgetPath(projectDir, moduleName = null) {
117
+ const template = detectProjectTemplate(projectDir)
118
118
 
119
119
  switch (template) {
120
120
  case 'lite':
121
- return join(projectDir, 'lib', 'widgets');
121
+ return join(projectDir, 'lib', 'widgets')
122
122
 
123
123
  case 'modular':
124
124
  if (moduleName) {
125
- return join(projectDir, 'lib', 'features', moduleName, 'widgets');
125
+ return join(projectDir, 'lib', 'features', moduleName, 'widgets')
126
126
  }
127
- return join(projectDir, 'lib', 'shared', 'widgets');
127
+ return join(projectDir, 'lib', 'shared', 'widgets')
128
128
 
129
129
  case 'clean':
130
130
  if (moduleName) {
131
- return join(projectDir, 'lib', 'features', moduleName, 'presentation', 'widgets');
131
+ return join(projectDir, 'lib', 'features', moduleName, 'presentation', 'widgets')
132
132
  }
133
- return join(projectDir, 'lib', 'shared', 'widgets');
133
+ return join(projectDir, 'lib', 'shared', 'widgets')
134
134
 
135
135
  default:
136
- return join(projectDir, 'lib', 'widgets');
136
+ return join(projectDir, 'lib', 'widgets')
137
137
  }
138
138
  }
139
139
 
@@ -143,27 +143,27 @@ export function getWidgetPath (projectDir, moduleName = null) {
143
143
  * @param {string} moduleName - 模块名称(可选)
144
144
  * @returns {string} Service 目录路径
145
145
  */
146
- export function getServicePath (projectDir, moduleName = null) {
147
- const template = detectProjectTemplate(projectDir);
146
+ export function getServicePath(projectDir, moduleName = null) {
147
+ const template = detectProjectTemplate(projectDir)
148
148
 
149
149
  switch (template) {
150
150
  case 'lite':
151
- return join(projectDir, 'lib', 'services');
151
+ return join(projectDir, 'lib', 'services')
152
152
 
153
153
  case 'modular':
154
154
  if (moduleName) {
155
- return join(projectDir, 'lib', 'features', moduleName, 'services');
155
+ return join(projectDir, 'lib', 'features', moduleName, 'services')
156
156
  }
157
- return join(projectDir, 'lib', 'shared', 'services');
157
+ return join(projectDir, 'lib', 'shared', 'services')
158
158
 
159
159
  case 'clean':
160
160
  if (moduleName) {
161
- return join(projectDir, 'lib', 'features', moduleName, 'data', 'datasources');
161
+ return join(projectDir, 'lib', 'features', moduleName, 'data', 'datasources')
162
162
  }
163
- return join(projectDir, 'lib', 'core', 'network');
163
+ return join(projectDir, 'lib', 'core', 'network')
164
164
 
165
165
  default:
166
- return join(projectDir, 'lib', 'services');
166
+ return join(projectDir, 'lib', 'services')
167
167
  }
168
168
  }
169
169
 
@@ -173,27 +173,27 @@ export function getServicePath (projectDir, moduleName = null) {
173
173
  * @param {string} moduleName - 模块名称(可选)
174
174
  * @returns {string} Model 目录路径
175
175
  */
176
- export function getModelPath (projectDir, moduleName = null) {
177
- const template = detectProjectTemplate(projectDir);
176
+ export function getModelPath(projectDir, moduleName = null) {
177
+ const template = detectProjectTemplate(projectDir)
178
178
 
179
179
  switch (template) {
180
180
  case 'lite':
181
- return join(projectDir, 'lib', 'models');
181
+ return join(projectDir, 'lib', 'models')
182
182
 
183
183
  case 'modular':
184
184
  if (moduleName) {
185
- return join(projectDir, 'lib', 'features', moduleName, 'models');
185
+ return join(projectDir, 'lib', 'features', moduleName, 'models')
186
186
  }
187
- return join(projectDir, 'lib', 'shared', 'models');
187
+ return join(projectDir, 'lib', 'shared', 'models')
188
188
 
189
189
  case 'clean':
190
190
  if (moduleName) {
191
- return join(projectDir, 'lib', 'features', moduleName, 'data', 'models');
191
+ return join(projectDir, 'lib', 'features', moduleName, 'data', 'models')
192
192
  }
193
- return join(projectDir, 'lib', 'shared', 'models');
193
+ return join(projectDir, 'lib', 'shared', 'models')
194
194
 
195
195
  default:
196
- return join(projectDir, 'lib', 'models');
196
+ return join(projectDir, 'lib', 'models')
197
197
  }
198
198
  }
199
199
 
@@ -203,25 +203,25 @@ export function getModelPath (projectDir, moduleName = null) {
203
203
  * @param {string} toPath - 目标文件路径
204
204
  * @returns {string} 相对路径
205
205
  */
206
- export function getRelativeImportPath (fromPath, toPath) {
207
- const fromParts = fromPath.split('/');
208
- const toParts = toPath.split('/');
206
+ export function getRelativeImportPath(fromPath, toPath) {
207
+ const fromParts = fromPath.split('/')
208
+ const toParts = toPath.split('/')
209
209
 
210
210
  // 找到共同的父目录
211
- let commonIndex = 0;
211
+ let commonIndex = 0
212
212
  while (commonIndex < fromParts.length && commonIndex < toParts.length) {
213
213
  if (fromParts[commonIndex] !== toParts[commonIndex]) {
214
- break;
214
+ break
215
215
  }
216
- commonIndex++;
216
+ commonIndex++
217
217
  }
218
218
 
219
219
  // 计算需要返回的层级
220
- const upLevels = fromParts.length - commonIndex - 1;
221
- const upPath = '../'.repeat(upLevels);
220
+ const upLevels = fromParts.length - commonIndex - 1
221
+ const upPath = '../'.repeat(upLevels)
222
222
 
223
223
  // 计算目标路径
224
- const targetPath = toParts.slice(commonIndex).join('/');
224
+ const targetPath = toParts.slice(commonIndex).join('/')
225
225
 
226
- return upPath + targetPath;
226
+ return upPath + targetPath
227
227
  }
@@ -1,32 +1,34 @@
1
- import { existsSync, readFileSync } from 'fs';
2
- import { join } from 'path';
3
- import JSON5 from 'json5';
1
+ import { existsSync, readFileSync } from 'fs'
2
+ import { join } from 'path'
3
+ import JSON5 from 'json5'
4
4
 
5
5
  // 读取项目片段文件
6
- export function loadProjectSnippets (projectDir) {
6
+ export function loadProjectSnippets(projectDir) {
7
7
  try {
8
- const snippetsPath = join(projectDir, '.vscode', 'dart.code-snippets');
9
- if (!existsSync(snippetsPath)) return {};
10
- const raw = readFileSync(snippetsPath, 'utf8');
11
- const json = JSON5.parse(raw);
12
- return json || {};
8
+ const snippetsPath = join(projectDir, '.vscode', 'dart.code-snippets')
9
+ if (!existsSync(snippetsPath)) return {}
10
+ const raw = readFileSync(snippetsPath, 'utf8')
11
+ const json = JSON5.parse(raw)
12
+ return json || {}
13
13
  } catch (_) {
14
- return {};
14
+ return {}
15
15
  }
16
16
  }
17
17
 
18
18
  // 渲染片段内容
19
- export function renderSnippet (bodyLines, variables) {
20
- const body = Array.isArray(bodyLines) ? bodyLines.join('\n') : String(bodyLines || '');
19
+ export function renderSnippet(bodyLines, variables) {
20
+ const body = Array.isArray(bodyLines) ? bodyLines.join('\n') : String(bodyLines || '')
21
21
  return body.replace(/\{\{(\w+)\}\}/g, (_, key) => {
22
- return Object.prototype.hasOwnProperty.call(variables, key) ? String(variables[key]) : `{{${key}}}`;
23
- });
22
+ return Object.prototype.hasOwnProperty.call(variables, key)
23
+ ? String(variables[key])
24
+ : `{{${key}}}`
25
+ })
24
26
  }
25
27
 
26
28
  // 简便方法:按 key 获取片段并渲染
27
- export function getSnippetContent (projectDir, key, variables) {
28
- const map = loadProjectSnippets(projectDir);
29
- const item = map[key];
30
- if (!item || !item.body) return null;
31
- return renderSnippet(item.body, variables);
29
+ export function getSnippetContent(projectDir, key, variables) {
30
+ const map = loadProjectSnippets(projectDir)
31
+ const item = map[key]
32
+ if (!item || !item.body) return null
33
+ return renderSnippet(item.body, variables)
32
34
  }
@@ -6,51 +6,51 @@
6
6
  * 转换为 PascalCase
7
7
  * home_page -> HomePage
8
8
  */
9
- export function toPascalCase (str) {
9
+ export function toPascalCase(str) {
10
10
  return str
11
11
  .split(/[-_\s]+/)
12
- .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
13
- .join('');
12
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
13
+ .join('')
14
14
  }
15
15
 
16
16
  /**
17
17
  * 转换为 snake_case
18
18
  * HomePage -> home_page
19
19
  */
20
- export function toSnakeCase (str) {
20
+ export function toSnakeCase(str) {
21
21
  return str
22
22
  .replace(/([A-Z])/g, '_$1')
23
23
  .toLowerCase()
24
- .replace(/^_/, '');
24
+ .replace(/^_/, '')
25
25
  }
26
26
 
27
27
  /**
28
28
  * 转换为 camelCase
29
29
  * home_page -> homePage
30
30
  */
31
- export function toCamelCase (str) {
32
- const pascal = toPascalCase(str);
33
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
31
+ export function toCamelCase(str) {
32
+ const pascal = toPascalCase(str)
33
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1)
34
34
  }
35
35
 
36
36
  /**
37
37
  * 转换为 Title Case
38
38
  * home_page -> Home Page
39
39
  */
40
- export function toTitleCase (str) {
40
+ export function toTitleCase(str) {
41
41
  return str
42
42
  .split(/[-_\s]+/)
43
- .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
44
- .join(' ');
43
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
44
+ .join(' ')
45
45
  }
46
46
 
47
47
  /**
48
48
  * 转换为 kebab-case
49
49
  * HomePage -> home-page
50
50
  */
51
- export function toKebabCase (str) {
51
+ export function toKebabCase(str) {
52
52
  return str
53
53
  .replace(/([A-Z])/g, '-$1')
54
54
  .toLowerCase()
55
- .replace(/^-/, '');
55
+ .replace(/^-/, '')
56
56
  }