create-unibest 3.0.0 → 3.0.4

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.
@@ -1,493 +0,0 @@
1
- import { readFileSync, writeFileSync, existsSync } from 'node:fs'
2
- import { join } from 'node:path'
3
- import type { UILibrary } from '../types'
4
-
5
- /**
6
- * UI 库配置接口
7
- */
8
- export interface UILibraryConfig {
9
- /** 包名 */
10
- packageName: string
11
- /** easycom 配置 */
12
- easycom?: {
13
- pattern: string
14
- path: string
15
- }
16
- /** TypeScript 类型配置 */
17
- types?: string[]
18
- /** 是否需要在 main.ts 中引入 */
19
- needMainImport?: boolean
20
- /** main.ts 中的引入代码 */
21
- mainImport?: string
22
- /** 是否需要在 uni.scss 中引入样式 */
23
- needUniScss?: boolean
24
- /** uni.scss 中的引入代码 */
25
- uniScssImport?: string
26
- /** 是否需要在 App.vue 中引入样式 */
27
- needAppVue?: boolean
28
- /** App.vue 中的引入代码 */
29
- appVueImport?: string
30
- }
31
-
32
- /**
33
- * UI 库配置映射
34
- */
35
- export const UI_LIBRARY_CONFIGS: Record<UILibrary, UILibraryConfig | null> = {
36
- none: null,
37
- 'wot-ui': {
38
- packageName: 'wot-design-uni',
39
- easycom: {
40
- pattern: '^wd-(.*)',
41
- path: 'wot-design-uni/components/wd-$1/wd-$1.vue',
42
- },
43
- types: ['wot-design-uni/global.d.ts'],
44
- },
45
- 'sard-uniapp': {
46
- packageName: 'sard-uniapp',
47
- easycom: {
48
- pattern: '^sar-(.*)',
49
- path: 'sard-uniapp/components/$1/$1.vue',
50
- },
51
- types: ['sard-uniapp/global'],
52
- },
53
- 'uview-pro': {
54
- packageName: 'uview-pro',
55
- easycom: {
56
- pattern: '^u-(.*)',
57
- path: 'uview-pro/components/u-$1/u-$1.vue',
58
- },
59
- needMainImport: true,
60
- mainImport: "import uViewPro from 'uview-pro';",
61
- needUniScss: true,
62
- uniScssImport: "@import 'uview-pro/theme.scss';",
63
- needAppVue: true,
64
- appVueImport: "@import 'uview-pro/index.scss';",
65
- },
66
- 'uv-ui': {
67
- packageName: '@climblee/uv-ui',
68
- easycom: {
69
- pattern: '^uv-(.*)',
70
- path: '@climblee/uv-ui/components/uv-$1/uv-$1.vue',
71
- },
72
- types: ['@ttou/uv-typings/shim', '@ttou/uv-typings/v2'],
73
- },
74
- 'uview-plus': {
75
- packageName: 'uview-plus',
76
- // uview-plus 需要多个 easycom 配置
77
- easycom: {
78
- pattern: '^u--(.*)',
79
- path: 'uview-plus/components/u-$1/u-$1.vue',
80
- },
81
- types: ['uview-plus/types'],
82
- needUniScss: true,
83
- uniScssImport: "@import 'uview-plus/theme.scss'; // /* 行为相关颜色 */",
84
- },
85
- }
86
-
87
- /**
88
- * 获取 UI 库配置
89
- */
90
- export function getUILibraryConfig(uiLibrary: UILibrary): UILibraryConfig | null {
91
- return UI_LIBRARY_CONFIGS[uiLibrary]
92
- }
93
-
94
- /**
95
- * 应用 UI 库配置到项目
96
- */
97
- export async function applyUILibraryConfig(projectPath: string, uiLibrary: UILibrary): Promise<void> {
98
- // 如果选择的是 none,直接返回
99
- if (uiLibrary === 'none') {
100
- return
101
- }
102
-
103
- const config = getUILibraryConfig(uiLibrary)
104
- if (!config) {
105
- return // 未配置的 UI 库,直接返回
106
- }
107
-
108
- // 1. 更新 package.json
109
- await updatePackageJson(projectPath, config.packageName)
110
-
111
- // 2. 更新 pages.config.ts
112
- if (config.easycom) {
113
- await updatePagesConfig(projectPath, config.easycom)
114
-
115
- // uview-plus 需要多个 easycom 配置
116
- if (uiLibrary === 'uview-plus') {
117
- await updatePagesConfig(projectPath, {
118
- pattern: '^up-(.*)',
119
- path: 'uview-plus/components/u-$1/u-$1.vue',
120
- })
121
- await updatePagesConfig(projectPath, {
122
- pattern: '^u-([^-].*)',
123
- path: 'uview-plus/components/u-$1/u-$1.vue',
124
- })
125
- }
126
- }
127
-
128
- // 3. 更新 tsconfig.json
129
- if (config.types && config.types.length > 0) {
130
- await updateTsConfig(projectPath, config.types)
131
- }
132
-
133
- // 4. 更新 main.ts
134
- if (config.needMainImport && config.mainImport) {
135
- await updateMainTs(projectPath, config.mainImport)
136
- }
137
-
138
- // 5. 更新 uni.scss
139
- if (config.needUniScss && config.uniScssImport) {
140
- await updateUniScss(projectPath, config.uniScssImport)
141
- }
142
-
143
- // 6. 更新 App.vue
144
- if (config.needAppVue && config.appVueImport) {
145
- await updateAppVue(projectPath, config.appVueImport)
146
- }
147
- }
148
-
149
- /**
150
- * 更新 package.json,添加 UI 库依赖
151
- */
152
- async function updatePackageJson(projectPath: string, packageName: string): Promise<void> {
153
- const packageJsonPath = join(projectPath, 'package.json')
154
- if (!existsSync(packageJsonPath)) {
155
- return
156
- }
157
-
158
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
159
- if (!packageJson.dependencies) {
160
- packageJson.dependencies = {}
161
- }
162
-
163
- // 如果依赖已存在,不重复添加
164
- if (!packageJson.dependencies[packageName]) {
165
- packageJson.dependencies[packageName] = 'latest'
166
- }
167
-
168
- writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n')
169
- }
170
-
171
- /**
172
- * 更新 pages.config.ts,添加 easycom 配置
173
- */
174
- async function updatePagesConfig(projectPath: string, easycom: { pattern: string; path: string }): Promise<void> {
175
- const pagesConfigPath = join(projectPath, 'pages.config.ts')
176
- if (!existsSync(pagesConfigPath)) {
177
- return
178
- }
179
-
180
- const originalContent = readFileSync(pagesConfigPath, 'utf8')
181
- let content = originalContent
182
-
183
- const patternLiteral = escapeSingleQuotes(easycom.pattern)
184
- const pathLiteral = escapeSingleQuotes(easycom.path)
185
- const existingEntryRegex = new RegExp(
186
- `["']${escapeForRegExp(patternLiteral)}["']\\s*:\\s*["']${escapeForRegExp(pathLiteral)}["']`,
187
- )
188
-
189
- if (existingEntryRegex.test(content)) {
190
- return
191
- }
192
-
193
- const entry = { pattern: patternLiteral, path: pathLiteral }
194
-
195
- content =
196
- addToExistingCustomBlock(content, entry) ??
197
- addCustomBlock(content, entry) ??
198
- addEasycomBlock(content, entry) ??
199
- appendEasycomBlock(content, entry)
200
-
201
- if (content !== originalContent) {
202
- writeFileSync(pagesConfigPath, ensureTrailingNewline(content))
203
- }
204
- }
205
-
206
- /**
207
- * 更新 tsconfig.json,添加类型配置
208
- */
209
- async function updateTsConfig(projectPath: string, types: string[]): Promise<void> {
210
- const tsConfigPath = join(projectPath, 'tsconfig.json')
211
- if (!existsSync(tsConfigPath)) {
212
- return
213
- }
214
-
215
- const tsConfig = JSON.parse(readFileSync(tsConfigPath, 'utf8'))
216
-
217
- if (!tsConfig.compilerOptions) {
218
- tsConfig.compilerOptions = {}
219
- }
220
-
221
- if (!tsConfig.compilerOptions.types) {
222
- tsConfig.compilerOptions.types = []
223
- }
224
-
225
- // 添加新的类型,避免重复
226
- const existingTypes = tsConfig.compilerOptions.types as string[]
227
- for (const type of types) {
228
- if (!existingTypes.includes(type)) {
229
- existingTypes.push(type)
230
- }
231
- }
232
-
233
- writeFileSync(tsConfigPath, JSON.stringify(tsConfig, null, 2) + '\n')
234
- }
235
-
236
- /**
237
- * 更新 main.ts,添加 UI 库引入
238
- */
239
- async function updateMainTs(projectPath: string, importCode: string): Promise<void> {
240
- const mainTsPath = join(projectPath, 'src', 'main.ts')
241
- if (!existsSync(mainTsPath)) {
242
- return
243
- }
244
-
245
- let content = readFileSync(mainTsPath, 'utf8')
246
-
247
- // 检查是否已存在该引入(通过包名检查)
248
- const firstLine = importCode.split('\n')[0]
249
- if (content.includes(firstLine)) {
250
- return
251
- }
252
-
253
- // 查找 import { createSSRApp } from "vue" 的位置
254
- const vueImportRegex = /(import\s+.*from\s+['"]vue['"];?\s*\n)/
255
- const match = content.match(vueImportRegex)
256
-
257
- if (match) {
258
- // 在 vue import 之后添加 UI 库引入
259
- content = content.replace(vueImportRegex, `$1${importCode}\n`)
260
- } else {
261
- // 如果找不到 vue import,在文件开头添加
262
- content = `${importCode}\n\n${content}`
263
- }
264
-
265
- // 对于 uview-pro,需要添加 app.use(uViewPro)
266
- if (importCode.includes('uview-pro')) {
267
- const appUseRegex = /(const\s+app\s*=\s*createSSRApp\(App\);?\s*\n)/
268
- const appUseMatch = content.match(appUseRegex)
269
-
270
- if (appUseMatch && !content.includes('app.use(uViewPro)')) {
271
- content = content.replace(appUseRegex, `$1 app.use(uViewPro);\n`)
272
- }
273
- }
274
-
275
- writeFileSync(mainTsPath, content)
276
- }
277
-
278
- /**
279
- * 更新 uni.scss,添加样式引入
280
- */
281
- async function updateUniScss(projectPath: string, importCode: string): Promise<void> {
282
- const uniScssPath = join(projectPath, 'src', 'style', 'uni.scss')
283
- if (!existsSync(uniScssPath)) {
284
- // 尝试其他可能的位置
285
- const altPath = join(projectPath, 'uni.scss')
286
- if (existsSync(altPath)) {
287
- await updateScssFile(altPath, importCode)
288
- }
289
- return
290
- }
291
-
292
- await updateScssFile(uniScssPath, importCode)
293
- }
294
-
295
- /**
296
- * 更新 SCSS 文件,在末尾添加引入
297
- */
298
- async function updateScssFile(filePath: string, importCode: string): Promise<void> {
299
- let content = readFileSync(filePath, 'utf8')
300
-
301
- // 检查是否已存在该引入
302
- if (content.includes(importCode)) {
303
- return
304
- }
305
-
306
- // 在文件末尾添加引入
307
- content = `${content.trim()}\n${importCode}\n`
308
- writeFileSync(filePath, content)
309
- }
310
-
311
- /**
312
- * 更新 App.vue,添加样式引入
313
- */
314
- async function updateAppVue(projectPath: string, importCode: string): Promise<void> {
315
- const appVuePath = join(projectPath, 'src', 'App.vue')
316
- if (!existsSync(appVuePath)) {
317
- return
318
- }
319
-
320
- let content = readFileSync(appVuePath, 'utf8')
321
-
322
- // 检查是否已存在该引入
323
- if (content.includes(importCode)) {
324
- return
325
- }
326
-
327
- // 查找 <style> 标签
328
- const styleRegex = /(<style[^>]*>)/s
329
- const match = content.match(styleRegex)
330
-
331
- if (match) {
332
- // 在 <style> 标签后添加引入
333
- content = content.replace(styleRegex, `$1\n${importCode}`)
334
- } else {
335
- // 如果没有 style 标签,在文件末尾添加
336
- content = `${content}\n<style lang="scss">\n${importCode}\n</style>`
337
- }
338
-
339
- writeFileSync(appVuePath, content)
340
- }
341
-
342
- interface EasycomEntry {
343
- pattern: string
344
- path: string
345
- }
346
-
347
- function addToExistingCustomBlock(content: string, entry: EasycomEntry): string | null {
348
- const customIndex = content.indexOf('custom:')
349
- if (customIndex === -1) {
350
- return null
351
- }
352
-
353
- const braceIndex = content.indexOf('{', customIndex)
354
- if (braceIndex === -1) {
355
- return null
356
- }
357
-
358
- const closingIndex = findMatchingBrace(content, braceIndex)
359
- if (closingIndex === -1) {
360
- return null
361
- }
362
-
363
- const inside = content.slice(braceIndex + 1, closingIndex)
364
- const hasEntries = inside.trim().length > 0
365
- const entryIndent = getLineIndent(content, braceIndex) + ' '
366
- const entryLine = `${entryIndent}'${entry.pattern}': '${entry.path}',`
367
-
368
- if (!hasEntries) {
369
- const closingIndent = getLineIndent(content, closingIndex)
370
- return (
371
- content.slice(0, braceIndex + 1) +
372
- `
373
- ${entryLine}
374
- ${closingIndent}` +
375
- content.slice(closingIndex)
376
- )
377
- }
378
-
379
- const beforeRaw = content.slice(0, closingIndex).replace(/\s*$/, '')
380
- const separator = beforeRaw.endsWith('\n') ? '' : '\n'
381
- const after = content.slice(closingIndex)
382
- return `${beforeRaw}${separator}${entryLine}
383
- ${after}`
384
- }
385
-
386
- function addCustomBlock(content: string, entry: EasycomEntry): string | null {
387
- const easycomIndex = content.indexOf('easycom:')
388
- if (easycomIndex === -1) {
389
- return null
390
- }
391
-
392
- const braceIndex = content.indexOf('{', easycomIndex)
393
- if (braceIndex === -1) {
394
- return null
395
- }
396
-
397
- const closingIndex = findMatchingBrace(content, braceIndex)
398
- if (closingIndex === -1) {
399
- return null
400
- }
401
-
402
- const easycomIndent = getLineIndent(content, braceIndex)
403
- const customIndent = easycomIndent + ' '
404
- const entryIndent = customIndent + ' '
405
- const customBlock = `${customIndent}custom: {
406
- ${entryIndent}'${entry.pattern}': '${entry.path}',
407
- ${customIndent}},`
408
-
409
- const beforeRaw = content.slice(0, closingIndex).replace(/\s*$/, '')
410
- const separator = beforeRaw.endsWith('\n') ? '' : '\n'
411
- const after = content.slice(closingIndex)
412
- return `${beforeRaw}${separator}${customBlock}
413
- ${after}`
414
- }
415
-
416
- function addEasycomBlock(content: string, entry: EasycomEntry): string | null {
417
- const exportIndex = content.indexOf('export default')
418
- if (exportIndex === -1) {
419
- return null
420
- }
421
-
422
- const braceIndex = content.indexOf('{', exportIndex)
423
- if (braceIndex === -1) {
424
- return null
425
- }
426
-
427
- const closingIndex = findMatchingBrace(content, braceIndex)
428
- if (closingIndex === -1) {
429
- return null
430
- }
431
-
432
- const blockIndent = getLineIndent(content, braceIndex) + ' '
433
- const entryIndent = blockIndent + ' '
434
- const block = `${blockIndent}easycom: {
435
- ${blockIndent} autoscan: true,
436
- ${blockIndent} custom: {
437
- ${entryIndent}'${entry.pattern}': '${entry.path}',
438
- ${blockIndent} },
439
- ${blockIndent}},`
440
-
441
- const before = content.slice(0, braceIndex + 1)
442
- const after = content.slice(braceIndex + 1)
443
- const prefix = before.endsWith('\n') ? before : `${before}\n`
444
- return `${prefix}${block}
445
- ${after}`
446
- }
447
-
448
- function appendEasycomBlock(content: string, entry: EasycomEntry): string {
449
- const block = `
450
- export const easycom = {
451
- autoscan: true,
452
- custom: {
453
- '${entry.pattern}': '${entry.path}',
454
- },
455
- }
456
- `
457
- return `${content}${block}`
458
- }
459
-
460
- function findMatchingBrace(content: string, startIndex: number): number {
461
- let depth = 0
462
- for (let i = startIndex; i < content.length; i += 1) {
463
- const char = content[i]
464
- if (char === '{') {
465
- depth += 1
466
- } else if (char === '}') {
467
- depth -= 1
468
- if (depth === 0) {
469
- return i
470
- }
471
- }
472
- }
473
- return -1
474
- }
475
-
476
- function getLineIndent(content: string, index: number): string {
477
- const lineStart = content.lastIndexOf('\n', index) + 1
478
- const line = content.slice(lineStart, index)
479
- const match = line.match(/^\s*/)
480
- return match ? match[0] : ''
481
- }
482
-
483
- function escapeSingleQuotes(value: string): string {
484
- return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")
485
- }
486
-
487
- function escapeForRegExp(value: string): string {
488
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
489
- }
490
-
491
- function ensureTrailingNewline(value: string): string {
492
- return value.endsWith('\n') ? value : `${value}\n`
493
- }
@@ -1,48 +0,0 @@
1
- import fetch from 'node-fetch'
2
-
3
- /**
4
- * Gitee API 返回的文件响应接口
5
- */
6
- interface GiteeFileResponse {
7
- content: string
8
- encoding: string
9
- }
10
-
11
- /**
12
- * 获取 unibest 仓库的最新版本号
13
- * @returns 版本号字符串或 null(如果获取失败)
14
- */
15
- async function getUnibestVersion(): Promise<string | null> {
16
- try {
17
- const apiUrl = `https://gitee.com/api/v5/repos/feige996/unibest/contents/package.json?ref=main`
18
- const response = await fetch(apiUrl, {
19
- method: 'GET',
20
- headers: {
21
- 'Content-Type': 'application/json',
22
- },
23
- })
24
-
25
- if (response.ok) {
26
- const data = (await response.json()) as GiteeFileResponse
27
- const { content, encoding } = data
28
-
29
- if (encoding === 'base64') {
30
- // 使用 Node.js 内置的 Buffer 解码 base64 内容
31
- const decodedContent = Buffer.from(content, 'base64').toString('utf8')
32
- const packageJson = JSON.parse(decodedContent)
33
- return packageJson.version || null
34
- } else {
35
- // console.error(`Unsupported encoding: ${encoding}`);
36
- return null
37
- }
38
- } else {
39
- // console.error(`Request failed with status: ${response.status}`);
40
- return null
41
- }
42
- } catch (error) {
43
- // console.error(`An error occurred: ${error}`);
44
- return null
45
- }
46
- }
47
-
48
- export default getUnibestVersion
@@ -1,59 +0,0 @@
1
- import { logger } from './logger'
2
- import { existsSync } from 'fs'
3
- import { yellow } from 'kolorist'
4
- import { join } from 'path'
5
-
6
- /**
7
- * 验证项目名称是否符合规范
8
- * npm包命名规则:只能包含小写字母、数字、连字符和下划线,且不能以连字符开头或结尾
9
- * @param name - 项目名称
10
- * @returns 是否有效
11
- */
12
- export function validateProjectName(name: string): boolean {
13
- const reg = /^[a-z0-9_-]+$/
14
- if (!reg.test(name)) {
15
- return false
16
- }
17
- if (name.startsWith('-') || name.endsWith('-')) {
18
- return false
19
- }
20
- return true
21
- }
22
-
23
- /**
24
- * 先检查目录是否已存在,再验证名称格式是否符合规范
25
- * @param projectName - 项目名称
26
- * @returns 错误信息或undefined
27
- */
28
- export function checkProjectNameExistAndValidate(_projectName: string): string {
29
- const projectName = _projectName.trim()
30
- if (existsSync(join(process.cwd(), projectName))) {
31
- return `目录 ${yellow(projectName)} 已存在,请选择其他名称`
32
- }
33
- if (!validateProjectName(projectName)) {
34
- return `项目名称 ${yellow(projectName)} 不符合规范,请使用字母、数字、连字符或下划线`
35
- }
36
- return ''
37
- }
38
-
39
- /**
40
- * 验证项目配置是否完整
41
- * @param options - 项目配置选项
42
- * @returns 是否有效
43
- */
44
- export function validateProjectOptions(options: Record<string, any>): boolean {
45
- const requiredFields = ['projectName', 'platforms', 'uiLibrary', 'requestLibrary']
46
- const missingFields = requiredFields.filter(field => !options[field])
47
-
48
- if (missingFields.length > 0) {
49
- logger.error(`项目配置不完整,缺少以下字段: ${missingFields.join(', ')}`)
50
- return false
51
- }
52
-
53
- if (!Array.isArray(options.platforms) || options.platforms.length === 0) {
54
- logger.error('至少需要选择一个平台')
55
- return false
56
- }
57
-
58
- return true
59
- }
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es2022",
4
- "module": "esnext",
5
- "moduleResolution": "node",
6
- "esModuleInterop": true,
7
- "allowSyntheticDefaultImports": true,
8
- "strict": true,
9
- "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "resolveJsonModule": true,
12
- "isolatedModules": true,
13
- "noEmit": true
14
- },
15
- "include": ["src/**/*"],
16
- "exclude": ["node_modules", "dist"]
17
- }
package/tsup.config.ts DELETED
@@ -1,14 +0,0 @@
1
- import { defineConfig } from 'tsup'
2
-
3
- // 检查是否为开发环境(通过NODE_ENV环境变量)
4
- const isProduction = process.env.NODE_ENV === 'production'
5
-
6
- export default defineConfig({
7
- entry: ['src/index.ts'],
8
- outDir: 'dist',
9
- format: ['esm'],
10
- target: 'esnext',
11
- clean: true,
12
- sourcemap: !isProduction, // 开发环境启用sourcemap,生产环境禁用
13
- dts: true,
14
- })