create-unibest 3.0.0 → 3.0.1

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/package.json CHANGED
@@ -1,15 +1,20 @@
1
1
  {
2
2
  "name": "create-unibest",
3
3
  "type": "module",
4
- "version": "3.0.0",
4
+ "version": "3.0.1",
5
5
  "description": "快速创建unibest项目的脚手架工具",
6
6
  "author": "",
7
7
  "license": "ISC",
8
8
  "keywords": [],
9
- "main": "index.js",
9
+ "main": "dist/index.js",
10
10
  "bin": {
11
- "best": "bin/index.js"
11
+ "best": "bin/index.js",
12
+ "create-unibest": "bin/index.js"
12
13
  },
14
+ "files": [
15
+ "bin",
16
+ "dist"
17
+ ],
13
18
  "scripts": {
14
19
  "dev": "NODE_ENV=development tsup --watch",
15
20
  "build": "NODE_ENV=production tsup",
package/.prettierrc DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "printWidth": 120,
3
- "tabWidth": 2,
4
- "useTabs": false,
5
- "semi": false,
6
- "singleQuote": true,
7
- "trailingComma": "all",
8
- "bracketSpacing": true,
9
- "jsxBracketSameLine": false,
10
- "arrowParens": "avoid",
11
- "proseWrap": "preserve",
12
- "htmlWhitespaceSensitivity": "ignore",
13
- "endOfLine": "auto"
14
- }
@@ -1,15 +0,0 @@
1
- {
2
- "version": "0.2.0",
3
- "configurations": [
4
- {
5
- "type": "node",
6
- "request": "launch",
7
- "name": "Debug CLI",
8
- "program": "${workspaceFolder}/dist/index.js",
9
- "args": ["create", "my-test-project"],
10
- "preLaunchTask": "npm: build",
11
- "outFiles": ["${workspaceFolder}/dist/**/*.js"],
12
- "sourceMaps": true
13
- }
14
- ]
15
- }
@@ -1,3 +0,0 @@
1
- {
2
- "cSpell.words": ["oxclint", "skiyee", "uniapp", "unibest", "uview"]
3
- }
package/DEVELOPER.md DELETED
@@ -1,16 +0,0 @@
1
- # 开发者文档
2
-
3
- ## 调试
4
-
5
- - `npm run dev`
6
- <!-- 后面随意写 -->
7
- - `pnpm start -v`
8
- - `pnpm start new my-project`
9
-
10
- ## 设计
11
-
12
- - npm i -g create-unibest
13
- - best -v
14
- - best new my-project
15
- - best new my-project -q (快速创建,等价于 best new my-project --quick)
16
- - best new my-project --platform [h5,app,mp-weixin,mp-alipay] --double-token-strategy
@@ -1,60 +0,0 @@
1
- import process from 'node:process'
2
- import { logger } from '../../utils/logger'
3
- import type { PromptResult } from '../../types'
4
- import { cloneRepoByBranch } from '../../utils/cloneRepo'
5
- import { applyUILibraryConfig } from '../../utils/uiLibrary'
6
- import { debug } from '../../utils/debug'
7
- import path from 'node:path'
8
-
9
- const root = process.cwd()
10
- /**
11
- * 生成项目主函数
12
- * @param options - 项目配置选项
13
- */
14
- export async function generateProject(options: PromptResult) {
15
- debug('generateProject options', options)
16
- const { projectName, platforms, uiLibrary, loginStrategy, i18n } = options
17
-
18
- if (!loginStrategy && !i18n) {
19
- debug('拉取 base 分支')
20
- await cloneRepoByBranch(root, projectName, 'base')
21
- } else if (!loginStrategy && i18n) {
22
- debug('拉取 base-i18n 分支')
23
- await cloneRepoByBranch(root, projectName, 'base-i18n')
24
- } else if (loginStrategy && !i18n) {
25
- debug('拉取 base-login 分支')
26
- await cloneRepoByBranch(root, projectName, 'base-login')
27
- } else if (loginStrategy && i18n) {
28
- debug('拉取 base-login-i18n 分支')
29
- await cloneRepoByBranch(root, projectName, 'base-login-i18n')
30
- }
31
- // 平台相关代码的处理(暂不处理)
32
-
33
- // UI 库配置
34
- const projectPath = path.join(root, projectName)
35
- if (uiLibrary === 'none') {
36
- debug('不引入任何UI库')
37
- } else {
38
- debug(`配置 UI 库: ${uiLibrary}`)
39
- try {
40
- await applyUILibraryConfig(projectPath, uiLibrary)
41
- logger.success(`UI 库 ${uiLibrary} 配置完成`)
42
- } catch (error) {
43
- logger.warn(`UI 库 ${uiLibrary} 配置失败: ${(error as Error).message}`)
44
- logger.info('您可以在项目创建后手动配置 UI 库')
45
- }
46
- }
47
-
48
- try {
49
- logger.success(`项目${projectName}创建成功!`)
50
- logger.info('下一步:')
51
- logger.info(` cd ${projectName}`)
52
- logger.info(' pnpm install')
53
- logger.info(' pnpm dev')
54
- logger.info(' 运行完以上命令后,即可在对应平台上运行项目')
55
- logger.info(' 如:pnpm dev:mp, pnpm dev:app 等')
56
- } catch (error) {
57
- logger.error(`生成项目失败: ${(error as Error).message}`)
58
- throw error
59
- }
60
- }
@@ -1,163 +0,0 @@
1
- import { text, multiselect, select, confirm, cancel, isCancel } from '@clack/prompts'
2
- import { logger } from '../../utils/logger'
3
- import type { PromptResult, Platform, UILibrary, RequestLibrary, FormatPlugin, TokenStrategy } from '../../types'
4
- import { checkProjectNameExistAndValidate } from '../../utils/validate'
5
- import { green } from 'kolorist'
6
-
7
- /**
8
- * 交互式询问用户项目配置
9
- * @param projectName - 命令行提供的项目名称(可选)
10
- * @param argv - 命令行参数
11
- * @returns 用户选择的项目配置
12
- */
13
- export async function promptUser(projectName?: string, argv: Record<string, any> = {}): Promise<PromptResult> {
14
- try {
15
- // 1. 项目名称(如果未通过命令行提供)
16
- if (!projectName) {
17
- const inputProjectName = await text({
18
- message: `请输入项目名称${green('[项目名称只能包含字母、数字、下划线和短横线,千万别写中文]')}`,
19
- initialValue: '',
20
- validate: value => {
21
- if (!value.trim()) return '项目名称不能为空'
22
- const errorMessage = checkProjectNameExistAndValidate(value)
23
- if (errorMessage) return errorMessage
24
- return
25
- },
26
- })
27
-
28
- // 处理用户取消操作
29
- if (isCancel(inputProjectName)) {
30
- cancel('操作已取消')
31
- process.exit(0)
32
- }
33
-
34
- projectName = inputProjectName
35
- }
36
-
37
- // 2. 选择平台(多选)
38
- const platforms = await multiselect({
39
- message: `请选择需要支持的平台(多选)${green('[脚手架将根据所选平台生成对应的平台代码,请根据实际情况选择]')}`,
40
- options: [
41
- { value: 'h5', label: 'H5' },
42
- { value: 'mp-weixin', label: '微信小程序' },
43
- { value: 'app', label: 'APP' },
44
- { value: 'mp-alipay', label: '支付宝小程序(包含钉钉)' },
45
- { value: 'mp-toutiao', label: '抖音小程序' },
46
- ],
47
- initialValues: ['h5'], // 默认选择 H5
48
- required: true,
49
- })
50
-
51
- // 处理用户取消操作
52
- if (isCancel(platforms)) {
53
- cancel('操作已取消')
54
- process.exit(0)
55
- }
56
-
57
- // 3. 选择UI库(单选)
58
- const uiLibrary = await select({
59
- message: '请选择UI库',
60
- options: [
61
- { value: 'none', label: '无UI库' },
62
- { value: 'wot-ui', label: 'wot-ui' },
63
- { value: 'uview-pro', label: 'uview-pro' },
64
- { value: 'sard-uniapp', label: 'sard-uniapp' },
65
- { value: 'uv-ui', label: 'uv-ui' },
66
- { value: 'uview-plus', label: 'uview-plus' },
67
- ],
68
- initialValue: 'none',
69
- })
70
-
71
- // 处理用户取消操作
72
- if (isCancel(uiLibrary)) {
73
- cancel('操作已取消')
74
- process.exit(0)
75
- }
76
-
77
- // 4. 是否需要”登录策略“
78
- const loginStrategy = await confirm({
79
- message: `是否需要登录策略(黑白名单、登录拦截等)?${green('[暂不知道的,选No即可,项目生成后也可以加该策略]')}`,
80
- initialValue: false,
81
- })
82
-
83
- // 处理用户取消操作
84
- if (isCancel(loginStrategy)) {
85
- cancel('操作已取消')
86
- process.exit(0)
87
- }
88
-
89
- // 5. 是否启用多语言(确认)
90
- const i18n = await confirm({
91
- message: '是否需要多语言i18n?',
92
- initialValue: false,
93
- })
94
-
95
- // 处理用户取消操作
96
- if (isCancel(i18n)) {
97
- cancel('操作已取消')
98
- process.exit(0)
99
- }
100
-
101
- // 6. 选择请求库(单选)
102
- // const requestLibrary = await select({
103
- // message: `请选择请求库${green('[菲鸽封装的基本就够用了,除非您想用或会用 alovajs]')}`,
104
- // options: [
105
- // { value: 'request', label: '菲鸽封装' },
106
- // { value: 'alovajs', label: 'alovajs' },
107
- // // { value: 'vue-query', label: 'vue-query' },
108
- // ],
109
- // initialValue: 'request',
110
- // })
111
-
112
- // // 处理用户取消操作
113
- // if (isCancel(requestLibrary)) {
114
- // cancel('操作已取消')
115
- // process.exit(0)
116
- // }
117
-
118
- // // 7. 请选择”token策略“
119
- // const tokenStrategy = await select({
120
- // message: `请选择token策略${green('[无论选择哪种,都需要后端配合使用]')}`,
121
- // options: [
122
- // { value: 'double-token', label: '双token策略(推荐)' },
123
- // { value: 'single-token', label: '单token策略' },
124
- // ],
125
- // initialValue: 'double-token',
126
- // })
127
- // // 处理用户取消操作
128
- // if (isCancel(tokenStrategy)) {
129
- // cancel('操作已取消')
130
- // process.exit(0)
131
- // }
132
-
133
- // // 8. 格式化插件选择
134
- // const formatPlugin = await select({
135
- // message: '格式化插件选择',
136
- // options: [
137
- // { value: 'eslint', label: 'ESLint(antfu+uni-helper配置)' },
138
- // { value: 'oxclint', label: 'Oxclint + Prettier + Stylelint' },
139
- // ],
140
- // initialValue: 'oxclint',
141
- // })
142
-
143
- // // 处理用户取消操作
144
- // if (isCancel(formatPlugin)) {
145
- // cancel('操作已取消')
146
- // process.exit(0)
147
- // }
148
-
149
- return {
150
- projectName,
151
- platforms: platforms as Platform[],
152
- uiLibrary: uiLibrary as UILibrary,
153
- loginStrategy,
154
- i18n,
155
- // requestLibrary: requestLibrary as RequestLibrary,
156
- // tokenStrategy: tokenStrategy as TokenStrategy,
157
- // formatPlugin: formatPlugin as FormatPlugin,
158
- }
159
- } catch (error) {
160
- logger.error(`询问过程出错: ${(error as Error).message}`)
161
- process.exit(1)
162
- }
163
- }
@@ -1,43 +0,0 @@
1
- /* eslint-disable style/brace-style */
2
- /* eslint-disable style/operator-linebreak */
3
- import type minimist from 'minimist'
4
- import { promptUser } from './create/prompts'
5
- import { generateProject } from './create/generate'
6
- import { version } from '../../package.json'
7
- import { intro, log } from '@clack/prompts'
8
- import { bold, yellow, green } from 'kolorist'
9
- import getUnibestVersion from '../utils/unibestVersion'
10
- import { checkProjectNameExistAndValidate } from '../utils/validate'
11
-
12
- /**
13
- * 创建项目命令
14
- */
15
- export async function createCommand(args: minimist.ParsedArgs): Promise<void> {
16
- const projectName = args._[1]
17
-
18
- const versionUnibest = (await getUnibestVersion()) || '3.18.3'
19
-
20
- intro(bold(green(`create-unibest@v${version} 快速创建 ${yellow(`unibest@v${versionUnibest}`)} 项目`)))
21
-
22
- // 验证项目名称
23
- if (projectName) {
24
- const errorMessage = checkProjectNameExistAndValidate(projectName)
25
- if (errorMessage) {
26
- log.error(errorMessage)
27
- process.exit(1)
28
- }
29
- }
30
-
31
- try {
32
- // 获取项目配置(通过命令行参数或交互式询问)
33
- const projectOptions = await promptUser(projectName, args)
34
-
35
- // 生成项目
36
- await generateProject(projectOptions)
37
-
38
- log.success('项目创建成功!')
39
- } catch (error) {
40
- log.error(`创建项目失败: ${(error as Error).message}`)
41
- process.exit(1)
42
- }
43
- }
package/src/index.ts DELETED
@@ -1,64 +0,0 @@
1
- #!/usr/bin/env node
2
- import process from 'node:process'
3
- import minimist from 'minimist'
4
- import { createCommand } from './commands/create'
5
- import { printHelp } from './utils/help'
6
- import { debug } from './utils/debug' // 导入我们的debug工具函数
7
- import getUnibestVersion from './utils/unibestVersion'
8
- import { version } from '../package.json'
9
- import { color } from './utils/color'
10
- import { green, red } from 'kolorist'
11
- import { yellow } from 'kolorist'
12
- /**
13
- * unibest-cli 主入口函数
14
- */
15
- function main() {
16
- const args = minimist(process.argv.slice(2))
17
- const command = args._[0]
18
- debug('command:', command)
19
- debug('args:', args)
20
-
21
- // 首先检查版本相关的选项
22
- if (args.v || args.version) {
23
- printVersion()
24
- return
25
- }
26
-
27
- // 根据命令执行不同的功能
28
- switch (command) {
29
- case 'create':
30
- case 'new':
31
- createCommand(args)
32
- break
33
- case '-h':
34
- case '--help':
35
- printHelp()
36
- break
37
- case '-v':
38
- case '--version':
39
- printVersion()
40
- break
41
- default:
42
- if (command) {
43
- console.log(color.red(`未知命令: ${command}`))
44
- }
45
- printHelp()
46
- break
47
- }
48
- }
49
-
50
- /**
51
- * 打印版本信息
52
- */
53
- async function printVersion() {
54
- console.log(green(`create-unibest: `) + yellow(version))
55
- try {
56
- const unibestVersion = await getUnibestVersion()
57
- console.log(green(`unibest: `) + yellow(unibestVersion || '1.0.0'))
58
- } catch (error) {
59
- console.log(green(`unibest: 未能获取到版本号`))
60
- }
61
- }
62
-
63
- // 执行主函数
64
- main()
@@ -1,26 +0,0 @@
1
- /** 支持的平台类型 */
2
- export type Platform = 'mp-weixin' | 'h5' | 'app' | 'mp-alipay' | 'mp-toutiao'
3
-
4
- /** 支持的UI库类型 */
5
- export type UILibrary = 'none' | 'wot-ui' | 'sard-uniapp' | 'uview-pro' | 'uv-ui' | 'uview-plus'
6
-
7
- /** 支持的请求库类型 */
8
- export type RequestLibrary = 'request' | 'alovajs' | 'vue-query'
9
-
10
- /** 支持的格式化插件类型 */
11
- export type FormatPlugin = 'oxclint' | 'eslint'
12
-
13
- /** 支持的token策略类型 */
14
- export type TokenStrategy = 'double-token' | 'single-token'
15
-
16
- /** 交互式询问结果 */
17
- export interface PromptResult {
18
- projectName: string
19
- platforms: Platform[]
20
- uiLibrary: UILibrary
21
- requestLibrary?: RequestLibrary
22
- i18n: boolean
23
- loginStrategy: boolean
24
- tokenStrategy?: TokenStrategy
25
- formatPlugin?: FormatPlugin
26
- }
@@ -1,54 +0,0 @@
1
- import fetch from 'node-fetch'
2
- import dayjs from 'dayjs'
3
- import os from 'os'
4
- import crypto from 'crypto'
5
- import packageJSON from '../../package.json'
6
- import getUnibestVersion from './unibestVersion'
7
-
8
- /**
9
- * 发送统计数据到服务器
10
- * @param template - 使用的模板名称
11
- * @param duration - 操作持续时间
12
- */
13
- export async function beacon(template: string, duration: string) {
14
- try {
15
- const unibestVersion = await getUnibestVersion()
16
- const deviceIdentifier = generateDeviceIdentifier()
17
-
18
- await fetch('https://ukw0y1.laf.run/create-unibest/beacon', {
19
- method: 'POST',
20
- headers: {
21
- 'Content-Type': 'application/json',
22
- },
23
- body: JSON.stringify({
24
- template,
25
- unibestVersion,
26
- createUnibestVersion: packageJSON.version,
27
- duration,
28
- time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
29
- nodeVersion: process.version,
30
- osPlatform: process.platform,
31
- cpuModel: os.cpus()[0]?.model || 'unknown',
32
- osRelease: os.release(),
33
- totalMem: Math.round(os.totalmem() / (1024 * 1024 * 1024)), // 四舍五入为整数 GB
34
- cpuArch: process.arch,
35
- uuid: deviceIdentifier, // 添加设备唯一标识符
36
- }),
37
- })
38
- } catch (error) {
39
- // 不需要打印
40
- }
41
- }
42
-
43
- /**
44
- * 生成设备唯一标识符
45
- * @returns 设备唯一标识符(SHA-256哈希)
46
- */
47
- function generateDeviceIdentifier(): string {
48
- const deviceInfo = [os.cpus()[0]?.model || '', os.totalmem().toString(), os.platform(), os.userInfo().username].join(
49
- '|',
50
- )
51
-
52
- const hash = crypto.createHash('sha256').update(deviceInfo).digest('hex')
53
- return hash
54
- }
@@ -1,54 +0,0 @@
1
- import { exec } from 'node:child_process'
2
- import { promises as fs } from 'node:fs'
3
- import { join } from 'node:path'
4
- import process from 'node:process'
5
- import { bold, red } from 'kolorist'
6
- import { replacePackageJson } from './replacePackageJson'
7
-
8
- async function removeGitFolder(localPath: string): Promise<void> {
9
- const gitFolderPath = join(localPath, '.git')
10
- await fs.rm(gitFolderPath, { recursive: true, force: true })
11
- }
12
-
13
- const REPO_URL = 'https://gitee.com/feige996/unibest.git'
14
-
15
- async function cloneRepo(projectName: string, branch: string): Promise<void> {
16
- try {
17
- await new Promise<void>((resolve, reject) => {
18
- const execStr = `git clone --depth=1 -b ${branch} ${REPO_URL} "${projectName}"`
19
-
20
- exec(execStr, async error => {
21
- if (error) {
22
- console.error(`${red('exec error:')} ${error}`)
23
- reject(error)
24
- return
25
- }
26
-
27
- try {
28
- await removeGitFolder(projectName)
29
- resolve()
30
- } catch (error) {
31
- reject(error)
32
- }
33
- })
34
- })
35
- return
36
- } catch (error) {
37
- console.error(`${red('cloneRepo error:')} ${error}`)
38
- throw new Error('cloneRepo error')
39
- }
40
- }
41
-
42
- export async function cloneRepoByBranch(root: string, name: string, branch: string) {
43
- try {
44
- await cloneRepo(name, branch)
45
- } catch (error) {
46
- console.error(`${red(`模板类型${branch}下载失败!`)} ${error}`)
47
- process.exit(1)
48
- }
49
-
50
- // 替换package.json中的项目名称和version
51
- // 注意:package.json位于克隆的项目目录中,因此需要拼接正确的路径
52
- const projectPath = join(root, name)
53
- replacePackageJson(projectPath, name, '1.0.0')
54
- }
@@ -1,12 +0,0 @@
1
- import { blue, green, magenta, red, yellow } from 'kolorist'
2
-
3
- /**
4
- * 颜色工具类
5
- */
6
- export const color = {
7
- blue,
8
- green,
9
- magenta,
10
- red,
11
- yellow,
12
- }
@@ -1,17 +0,0 @@
1
- import { magenta } from 'kolorist'
2
-
3
- /**
4
- * 调试日志工具函数
5
- * 只在开发环境下输出带[debug]前缀的日志
6
- * @param args 要打印的日志内容
7
- */
8
- export function debug(...args: any[]): void {
9
- // 从process.env获取环境变量,确保在不同运行方式下都能正常工作
10
- const isDev = process.env.NODE_ENV === 'development'
11
-
12
- if (isDev) {
13
- // 使用magenta颜色为[debug]前缀添加醒目的颜色
14
- const debugPrefix = magenta('[debug]')
15
- console.log(debugPrefix, ...args)
16
- }
17
- }
package/src/utils/ejs.ts DELETED
@@ -1,22 +0,0 @@
1
- import { readFile } from 'fs/promises'
2
- import ejs from 'ejs'
3
- import { logger } from './logger'
4
-
5
- /**
6
- * 渲染EJS模板文件
7
- * @param filePath - 模板文件路径
8
- * @param data - 模板数据
9
- * @returns 渲染后的内容
10
- */
11
- export async function renderTemplate(filePath: string, data: Record<string, any>): Promise<string> {
12
- try {
13
- const templateContent = await readFile(filePath, 'utf8')
14
- return ejs.render(templateContent, data, {
15
- filename: filePath,
16
- async: true,
17
- })
18
- } catch (error) {
19
- logger.error(`渲染模板失败: ${filePath}`)
20
- throw error
21
- }
22
- }
package/src/utils/file.ts DELETED
@@ -1,48 +0,0 @@
1
- import { existsSync, readdirSync, rmdirSync, unlinkSync } from 'fs'
2
- import { join } from 'path'
3
-
4
- /**
5
- * 清空目录(保留目录本身)
6
- * @param dir - 要清空的目录路径
7
- */
8
- export function emptyDir(dir: string): void {
9
- if (!existsSync(dir)) {
10
- return
11
- }
12
-
13
- for (const file of readdirSync(dir)) {
14
- const abs = join(dir, file)
15
- // 注意: 这里使用同步方法以确保操作顺序
16
- if (existsSync(abs) && existsSync(abs)) {
17
- const isDir = existsSync(abs) && existsSync(abs)
18
- if (isDir) {
19
- emptyDir(abs)
20
- rmdirSync(abs)
21
- } else {
22
- unlinkSync(abs)
23
- }
24
- }
25
- }
26
- }
27
-
28
- /**
29
- * 检查目录是否为空
30
- * @param path - 目录路径
31
- * @returns 是否为空
32
- */
33
- export function isEmptyDir(path: string): boolean {
34
- if (!existsSync(path)) {
35
- return true
36
- }
37
-
38
- const files = readdirSync(path)
39
- return files.length === 0 || (files.length === 1 && files[0] === '.git')
40
- }
41
-
42
- /**
43
- * 获取项目根目录
44
- * @returns 根目录路径
45
- */
46
- export function getRootDir(): string {
47
- return process.cwd()
48
- }