icon-to-font 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 (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +230 -0
  3. package/bin/cli.js +11 -0
  4. package/bin/cli.ts +183 -0
  5. package/examples/uniapp-vue3/App.vue +17 -0
  6. package/examples/uniapp-vue3/index.html +20 -0
  7. package/examples/uniapp-vue3/main.js +22 -0
  8. package/examples/uniapp-vue3/manifest.json +72 -0
  9. package/examples/uniapp-vue3/pages/index/index.vue +33 -0
  10. package/examples/uniapp-vue3/pages.json +17 -0
  11. package/examples/uniapp-vue3/static/itf/test1.svg +1 -0
  12. package/examples/uniapp-vue3/static/itf/test2.svg +1 -0
  13. package/examples/uniapp-vue3/static/itf//346/265/213/350/257/225.svg +1 -0
  14. package/examples/uniapp-vue3/static/logo.png +0 -0
  15. package/examples/uniapp-vue3/uni.promisify.adaptor.js +13 -0
  16. package/examples/uniapp-vue3/uni.scss +76 -0
  17. package/examples/vite-react/README.md +73 -0
  18. package/examples/vite-react/eslint.config.js +23 -0
  19. package/examples/vite-react/index.html +13 -0
  20. package/examples/vite-react/package.json +30 -0
  21. package/examples/vite-react/pnpm-lock.yaml +2049 -0
  22. package/examples/vite-react/public/vite.svg +1 -0
  23. package/examples/vite-react/src/App.css +42 -0
  24. package/examples/vite-react/src/App.tsx +37 -0
  25. package/examples/vite-react/src/assets/itf/test1.svg +1 -0
  26. package/examples/vite-react/src/assets/itf/test2.svg +1 -0
  27. package/examples/vite-react/src/assets/itf//346/265/213/350/257/225.svg +1 -0
  28. package/examples/vite-react/src/assets/react.svg +1 -0
  29. package/examples/vite-react/src/index.css +68 -0
  30. package/examples/vite-react/src/main.tsx +10 -0
  31. package/examples/vite-react/tsconfig.app.json +28 -0
  32. package/examples/vite-react/tsconfig.json +7 -0
  33. package/examples/vite-react/tsconfig.node.json +26 -0
  34. package/examples/vite-react/vite.config.ts +7 -0
  35. package/examples/vite-vue3/.vscode/extensions.json +3 -0
  36. package/examples/vite-vue3/README.md +5 -0
  37. package/examples/vite-vue3/index.html +13 -0
  38. package/examples/vite-vue3/package.json +21 -0
  39. package/examples/vite-vue3/pnpm-lock.yaml +939 -0
  40. package/examples/vite-vue3/public/vite.svg +1 -0
  41. package/examples/vite-vue3/src/App.vue +30 -0
  42. package/examples/vite-vue3/src/assets/itf/test1.svg +1 -0
  43. package/examples/vite-vue3/src/assets/itf/test2.svg +1 -0
  44. package/examples/vite-vue3/src/assets/itf//346/265/213/350/257/225.svg +1 -0
  45. package/examples/vite-vue3/src/assets/vue.svg +1 -0
  46. package/examples/vite-vue3/src/components/HelloWorld.vue +41 -0
  47. package/examples/vite-vue3/src/main.ts +5 -0
  48. package/examples/vite-vue3/src/style.css +79 -0
  49. package/examples/vite-vue3/tsconfig.app.json +16 -0
  50. package/examples/vite-vue3/tsconfig.json +7 -0
  51. package/examples/vite-vue3/tsconfig.node.json +26 -0
  52. package/examples/vite-vue3/vite.config.ts +7 -0
  53. package/package.json +48 -0
  54. package/tsconfig.json +24 -0
  55. package/utils/command-line.ts +155 -0
  56. package/utils/component-generators/base-component-generator.abstract.ts +25 -0
  57. package/utils/component-generators/component-generator-factory.ts +32 -0
  58. package/utils/component-generators/component-generator.interface.ts +12 -0
  59. package/utils/component-generators/index.ts +11 -0
  60. package/utils/component-generators/react-component-generator.ts +69 -0
  61. package/utils/component-generators/uni-app-component-generator.ts +64 -0
  62. package/utils/component-generators/vue-component-generator.ts +65 -0
  63. package/utils/component-generators.ts +9 -0
  64. package/utils/css-generator.ts +47 -0
  65. package/utils/directory-structure.ts +139 -0
  66. package/utils/file-system.ts +83 -0
  67. package/utils/font-generator.ts +55 -0
  68. package/utils/project-detector.ts +145 -0
  69. package/utils/usage-generator.ts +77 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * React 组件生成器
3
+ */
4
+ import { BaseComponentGenerator } from './base-component-generator.abstract';
5
+
6
+ export class ReactComponentGenerator extends BaseComponentGenerator {
7
+ /**
8
+ * 生成 React 组件
9
+ * @param {string} fontName - 字体名称
10
+ * @param {string[]} iconNames - 图标名称数组
11
+ * @returns {string} React 组件内容
12
+ * @example
13
+ * ```typescript
14
+ * const generator = new ReactComponentGenerator();
15
+ * const reactComponent = generator.generate('MyFont', ['icon1', 'icon2']);
16
+ * console.log(reactComponent); // 输出 React 组件内容
17
+ * ```
18
+ */
19
+ generate(fontName: string, iconNames: string[]): string {
20
+ const iconNameUnion = this.generateIconNameUnion(iconNames);
21
+
22
+ return `
23
+ import React from 'react';
24
+ import './icon-font.css';
25
+
26
+ /**
27
+ * Icon component props
28
+ */
29
+ export interface ItfIconProps {
30
+ /** Icon name */
31
+ name: ${iconNameUnion};
32
+ /** Size in px or any valid CSS unit */
33
+ size?: number | string;
34
+ /** Color of the icon */
35
+ color?: string;
36
+ /** Additional className */
37
+ className?: string;
38
+ /** Click event handler */
39
+ onClick?: (event: React.MouseEvent<HTMLSpanElement>) => void;
40
+ }
41
+
42
+ /**
43
+ * Icon component
44
+ */
45
+ const ItfIcon: React.FC<ItfIconProps> = ({
46
+ name,
47
+ size = 14,
48
+ color,
49
+ className = '',
50
+ onClick,
51
+ ...rest
52
+ }) => {
53
+ return (
54
+ <span
55
+ className={\`itf-icon itf-icon-\${name} \${className}\`}
56
+ style={{
57
+ fontSize: typeof size === 'number' ? \`\${size}px\` : size,
58
+ color,
59
+ }}
60
+ onClick={onClick}
61
+ {...rest}
62
+ />
63
+ );
64
+ };
65
+
66
+ export default ItfIcon;
67
+ `;
68
+ }
69
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * uni-app 组件生成器
3
+ */
4
+ import { BaseComponentGenerator } from "./base-component-generator.abstract";
5
+
6
+ export class UniAppComponentGenerator extends BaseComponentGenerator {
7
+ /**
8
+ * 生成 uni-app 组件
9
+ * @param {string} fontName - 字体名称
10
+ * @param {string[]} iconNames - 图标名称数组
11
+ * @returns {string} uni-app 组件内容
12
+ * @example
13
+ * ```typescript
14
+ * const generator = new UniAppComponentGenerator();
15
+ * const uniComponent = generator.generate('MyFont', ['icon1', 'icon2']);
16
+ * console.log(uniComponent); // 输出 uni-app 组件内容
17
+ * ```
18
+ */
19
+ generate(fontName: string, iconNames: string[]): string {
20
+ const iconNameUnion = this.generateIconNameUnion(iconNames);
21
+
22
+ return `
23
+ <template>
24
+ <text
25
+ :class="['itf-icon', \`itf-icon-\${props.name}\`]"
26
+ :style="{
27
+ fontSize: typeof props.size === 'number' ? \`\${props.size}px\` : props.size,
28
+ color: props.color
29
+ }"
30
+ @click="$emit('click', $event)"
31
+ />
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ export interface ItfIconProps {
36
+ /** Icon name */
37
+ name: ${iconNameUnion};
38
+ /** Size in px or any valid CSS unit */
39
+ size?: number | string;
40
+ /** Color of the icon */
41
+ color?: string;
42
+ }
43
+
44
+ const props = withDefaults(defineProps<ItfIconProps>(), {
45
+ size: 30,
46
+ });
47
+
48
+ defineEmits<{
49
+ click: [event: MouseEvent];
50
+ }>();
51
+ </script>
52
+
53
+ <style scoped lang="scss">
54
+ @import '../../static/.itf/icon-font.css';
55
+
56
+ .itf-icon {
57
+ vertical-align: middle;
58
+ cursor: inherit;
59
+ speak: none;
60
+ }
61
+ </style>
62
+ `;
63
+ }
64
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Vue 组件生成器
3
+ */
4
+ import { BaseComponentGenerator } from './base-component-generator.abstract';
5
+
6
+ export class VueComponentGenerator extends BaseComponentGenerator {
7
+ /**
8
+ * 生成 Vue 组件
9
+ * @param {string} fontName - 字体名称
10
+ * @param {string[]} iconNames - 图标名称数组
11
+ * @returns {string} Vue 组件内容
12
+ * @example
13
+ * ```typescript
14
+ * const generator = new VueComponentGenerator();
15
+ * const vueComponent = generator.generate('MyFont', ['icon1', 'icon2']);
16
+ * console.log(vueComponent); // 输出 Vue 组件内容
17
+ * ```
18
+ */
19
+ generate(fontName: string, iconNames: string[]): string {
20
+ const iconNameUnion = this.generateIconNameUnion(iconNames);
21
+
22
+ return `
23
+ <template>
24
+ <i
25
+ :class="['itf-icon', \`itf-icon-\${props.name}\`]"
26
+ :style="{
27
+ fontSize: typeof props.size === 'number' ? \`\${props.size}px\` : props.size,
28
+ color: props.color
29
+ }"
30
+ v-bind="$attrs"
31
+ @click="$emit('click', $event)"
32
+ />
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ import './icon-font.css';
37
+
38
+ export interface ItfIconProps {
39
+ /** Icon name */
40
+ name: ${iconNameUnion};
41
+ /** Size in px or any valid CSS unit */
42
+ size?: number | string;
43
+ /** Color of the icon */
44
+ color?: string;
45
+ }
46
+
47
+ const props = withDefaults(defineProps<ItfIconProps>(), {
48
+ size: 14,
49
+ });
50
+
51
+ defineEmits<{
52
+ click: [event: MouseEvent];
53
+ }>();
54
+ </script>
55
+
56
+ <style scoped>
57
+ .itf-icon {
58
+ vertical-align: middle;
59
+ cursor: inherit;
60
+ speak: none;
61
+ }
62
+ </style>
63
+ `;
64
+ }
65
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 组件生成器模块
3
+ * @description 用于生成各种框架的图标组件
4
+ */
5
+
6
+ // 导出新目录中的所有内容
7
+ export * from './component-generators';
8
+
9
+
@@ -0,0 +1,47 @@
1
+ /**
2
+ * CSS 生成器模块
3
+ * @description 生成字体图标所需的 CSS 内容
4
+ */
5
+
6
+ /**
7
+ * 生成 CSS 文件内容
8
+ * @param {string} fontName - 字体名称
9
+ * @param {string[]} iconNames - 图标名称数组
10
+ * @returns {string} CSS 内容
11
+ * @example
12
+ * ```typescript
13
+ * const cssContent = generateCssContent('MyFont', ['icon1', 'icon2']);
14
+ * console.log(cssContent); // 输出 CSS 内容
15
+ * ```
16
+ */
17
+ export function generateCssContent(fontName: string, iconNames: string[]): string {
18
+ return `
19
+ @font-face {
20
+ font-family: '${fontName}';
21
+ src: url('./${fontName}.woff2') format('woff2');
22
+ font-weight: normal;
23
+ font-style: normal;
24
+ font-display: block;
25
+ }
26
+
27
+ .itf-icon {
28
+ font-family: '${fontName}' !important;
29
+ font-style: normal;
30
+ font-weight: normal;
31
+ font-variant: normal;
32
+ text-transform: none;
33
+ line-height: 1;
34
+ -webkit-font-smoothing: antialiased;
35
+ -moz-osx-font-smoothing: grayscale;
36
+ display: inline-block;
37
+ }
38
+
39
+ ${iconNames
40
+ .map((name, index) => {
41
+ const code = 0xf101 + index;
42
+ const codeStr = code.toString(16).padStart(4, '0');
43
+ return `.itf-icon-${name}::before { content: "\\${codeStr}"; }`;
44
+ })
45
+ .join("\n")}
46
+ `.trim();
47
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * 目录结构处理模块
3
+ * @description 处理不同项目类型的目录结构
4
+ */
5
+
6
+ import { resolve } from 'path';
7
+
8
+ /**
9
+ * 目录结构接口
10
+ */
11
+ export interface DirectoryStructure {
12
+ /** 组件目录 */
13
+ componentDir: string;
14
+ /** 资源目录(字体文件和 CSS 文件) */
15
+ assetsDir: string;
16
+ }
17
+
18
+ /**
19
+ * 项目类型枚举
20
+ */
21
+ export enum ProjectType {
22
+ /** Vue 项目 */
23
+ VUE = 'vue',
24
+ /** React 项目 */
25
+ REACT = 'react',
26
+ /** uni-app 项目 */
27
+ UNIAPP = 'uniapp',
28
+ /** 其他项目类型 */
29
+ OTHER = 'other'
30
+ }
31
+
32
+ /**
33
+ * 获取项目类型对应的目录结构
34
+ * @param {ProjectType | string} projectType - 项目类型
35
+ * @param {string} baseDistDir - 基础输出目录
36
+ * @returns {DirectoryStructure} 目录结构对象
37
+ * @example
38
+ * ```typescript
39
+ * const structure = getDirectoryStructure('vue', './output');
40
+ * console.log(structure.componentDir); // 输出:./src/components
41
+ * console.log(structure.assetsDir); // 输出:./src/assets/.itf
42
+ *
43
+ * const uniAppStructure = getDirectoryStructure('uniapp', './examples/uniapp-vue3');
44
+ * console.log(uniAppStructure.componentDir); // 输出:./examples/uniapp-vue3/components
45
+ * console.log(uniAppStructure.assetsDir); // 输出:./examples/uniapp-vue3/static/.itf
46
+ * ```
47
+ */
48
+ export function getDirectoryStructure(
49
+ projectType: ProjectType | string,
50
+ baseDistDir: string
51
+ ): DirectoryStructure {
52
+ const type = projectType as ProjectType;
53
+
54
+ switch (type) {
55
+ case ProjectType.UNIAPP:
56
+ // 对于 uni-app 项目,assetsDir 应该是在项目根目录下的 static/.itf
57
+ // 我们需要从 baseDistDir 中提取项目根目录
58
+ const uniAppRootDir = baseDistDir.includes('components') ? baseDistDir.replace('components', '') : baseDistDir;
59
+ return {
60
+ componentDir: resolve(process.cwd(), baseDistDir),
61
+ assetsDir: resolve(process.cwd(), uniAppRootDir, 'static/.itf')
62
+ };
63
+
64
+ case ProjectType.VUE:
65
+ case ProjectType.REACT:
66
+ return {
67
+ componentDir: resolve(process.cwd(), baseDistDir),
68
+ assetsDir: resolve(process.cwd(), 'src/assets/.itf')
69
+ };
70
+
71
+ default:
72
+ return {
73
+ componentDir: resolve(process.cwd(), baseDistDir),
74
+ assetsDir: resolve(process.cwd(), baseDistDir)
75
+ };
76
+ }
77
+ }
78
+
79
+ /**
80
+ * 获取组件文件路径
81
+ * @param {DirectoryStructure} structure - 目录结构
82
+ * @param {ProjectType | string} projectType - 项目类型
83
+ * @returns {string} 组件文件路径
84
+ * @example
85
+ * ```typescript
86
+ * const structure = getDirectoryStructure('vue', './output');
87
+ * const componentPath = getComponentFilePath(structure, 'vue');
88
+ * console.log(componentPath); // 输出:./src/components/ItfIcon/ItfIcon.vue
89
+ *
90
+ * const reactStructure = getDirectoryStructure('react', './output');
91
+ * const reactComponentPath = getComponentFilePath(reactStructure, 'react');
92
+ * console.log(reactComponentPath); // 输出:./src/components/ItfIcon/ItfIcon.tsx
93
+ *
94
+ * const uniAppStructure = getDirectoryStructure('uniapp', './output');
95
+ * const uniAppComponentPath = getComponentFilePath(uniAppStructure, 'uniapp');
96
+ * console.log(uniAppComponentPath); // 输出:./components/ItfIcon/ItfIcon.vue
97
+ * ```
98
+ */
99
+ export function getComponentFilePath(
100
+ structure: DirectoryStructure,
101
+ projectType: ProjectType | string
102
+ ): string {
103
+ const type = projectType as ProjectType;
104
+ const extension = type === ProjectType.REACT ? 'tsx' : 'vue';
105
+
106
+ // 所有项目类型都输出到 ItfIcon 子目录中
107
+ return resolve(structure.componentDir, 'ItfIcon', `ItfIcon.${extension}`);
108
+ }
109
+
110
+ /**
111
+ * 获取 CSS 文件路径
112
+ * @param {DirectoryStructure} structure - 目录结构
113
+ * @returns {string} CSS 文件路径
114
+ * @example
115
+ * ```typescript
116
+ * const structure = getDirectoryStructure('vue', './output');
117
+ * const cssPath = getCssFilePath(structure);
118
+ * console.log(cssPath); // 输出:./src/assets/.itf/icon-font.css
119
+ * ```
120
+ */
121
+ export function getCssFilePath(structure: DirectoryStructure): string {
122
+ return resolve(structure.assetsDir, 'icon-font.css');
123
+ }
124
+
125
+ /**
126
+ * 获取字体文件路径
127
+ * @param {DirectoryStructure} structure - 目录结构
128
+ * @param {string} fontName - 字体名称
129
+ * @returns {string} 字体文件路径
130
+ * @example
131
+ * ```typescript
132
+ * const structure = getDirectoryStructure('vue', './output');
133
+ * const fontPath = getFontFilePath(structure, 'MyIconFont');
134
+ * console.log(fontPath); // 输出:./src/assets/.itf/MyIconFont
135
+ * ```
136
+ */
137
+ export function getFontFilePath(structure: DirectoryStructure, fontName: string): string {
138
+ return resolve(structure.assetsDir, fontName);
139
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * 文件系统操作模块
3
+ * @description 封装文件系统相关操作
4
+ */
5
+
6
+ import { existsSync, mkdirSync, writeFileSync, readdirSync } from 'fs';
7
+ import { resolve } from 'path';
8
+
9
+ /**
10
+ * 检查目录是否存在
11
+ * @param {string} directory - 目录路径
12
+ * @returns {boolean} 是否存在
13
+ * @example
14
+ * ```typescript
15
+ * const exists = checkDirectoryExists('./src');
16
+ * console.log(exists); // 输出:true 或 false
17
+ * ```
18
+ */
19
+ export function checkDirectoryExists(directory: string): boolean {
20
+ return existsSync(directory);
21
+ }
22
+
23
+ /**
24
+ * 创建目录(如果不存在)
25
+ * @param {string} directory - 目录路径
26
+ * @returns {void}
27
+ * @example
28
+ * ```typescript
29
+ * createDirectory('./output');
30
+ * // 创建 output 目录(如果不存在)
31
+ * ```
32
+ */
33
+ export function createDirectory(directory: string): void {
34
+ mkdirSync(directory, { recursive: true });
35
+ }
36
+
37
+ /**
38
+ * 写入文件
39
+ * @param {string} filePath - 文件路径
40
+ * @param {string} content - 文件内容
41
+ * @returns {void}
42
+ * @example
43
+ * ```typescript
44
+ * writeFile('./output/file.txt', 'Hello World');
45
+ * // 写入文件内容
46
+ * ```
47
+ */
48
+ export function writeFile(filePath: string, content: string): void {
49
+ writeFileSync(filePath, content, 'utf8');
50
+ }
51
+
52
+ /**
53
+ * 读取目录中的文件
54
+ * @param {string} directory - 目录路径
55
+ * @param {string} extension - 文件扩展名(可选)
56
+ * @returns {string[]} 文件名称数组
57
+ * @example
58
+ * ```typescript
59
+ * const files = readDirectory('./icons', '.svg');
60
+ * console.log(files); // 输出:['icon1.svg', 'icon2.svg']
61
+ * ```
62
+ */
63
+ export function readDirectory(directory: string, extension?: string): string[] {
64
+ const files = readdirSync(directory);
65
+ if (extension) {
66
+ return files.filter(file => file.endsWith(extension));
67
+ }
68
+ return files;
69
+ }
70
+
71
+ /**
72
+ * 解析绝对路径
73
+ * @param {string} path - 相对或绝对路径
74
+ * @returns {string} 绝对路径
75
+ * @example
76
+ * ```typescript
77
+ * const absolutePath = resolvePath('./src');
78
+ * console.log(absolutePath); // 输出:/full/path/to/src
79
+ * ```
80
+ */
81
+ export function resolvePath(path: string): string {
82
+ return resolve(process.cwd(), path);
83
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * 字体生成器模块
3
+ * @description 用于生成字体文件
4
+ */
5
+
6
+ import webfontsGenerator from '@vusion/webfonts-generator';
7
+
8
+ /**
9
+ * 生成字体文件的选项接口
10
+ */
11
+ export interface GenerateFontOptions {
12
+ /** SVG 文件路径数组 */
13
+ svgFiles: string[];
14
+ /** 输出目录 */
15
+ outputDir: string;
16
+ /** 字体名称 */
17
+ fontName: string;
18
+ }
19
+
20
+ /**
21
+ * 生成字体文件
22
+ * @param {GenerateFontOptions} options - 生成选项
23
+ * @returns {Promise<void>} - 生成完成的 Promise
24
+ * @example
25
+ * ```typescript
26
+ * await generateFont({
27
+ * svgFiles: ['/path/to/icon1.svg', '/path/to/icon2.svg'],
28
+ * outputDir: '/path/to/output',
29
+ * fontName: 'MyIconFont'
30
+ * });
31
+ * ```
32
+ */
33
+ export async function generateFont(options: GenerateFontOptions): Promise<void> {
34
+ return new Promise((resolve, reject) => {
35
+ webfontsGenerator({
36
+ files: options.svgFiles,
37
+ dest: options.outputDir,
38
+ fontName: options.fontName,
39
+ types: ['woff2'],
40
+ css: false,
41
+ html: false,
42
+ order: ['woff2'],
43
+ templateOptions: {
44
+ baseClass: 'my-icon',
45
+ classPrefix: 'my-icon-'
46
+ }
47
+ }, (error) => {
48
+ if (error) {
49
+ reject(error);
50
+ } else {
51
+ resolve();
52
+ }
53
+ });
54
+ });
55
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * 项目类型检测器模块
3
+ * @description 用于检测一个文件夹下的项目类型(uniapp、vue、react)
4
+ */
5
+
6
+ import { existsSync, readFileSync } from 'fs';
7
+ import { join } from 'path';
8
+
9
+ /**
10
+ * 项目类型枚举
11
+ */
12
+ export enum ProjectType {
13
+ /** uniapp 项目 */
14
+ UNIAPP = 'uniapp',
15
+ /** Vue 项目 */
16
+ VUE = 'vue',
17
+ /** React 项目 */
18
+ REACT = 'react',
19
+ /** 未知项目类型 */
20
+ UNKNOWN = 'unknown'
21
+ }
22
+
23
+ /**
24
+ * 检测项目类型
25
+ * @param {string} directory - 要检测的目录路径
26
+ * @returns {ProjectType} 项目类型
27
+ * @example
28
+ * ```typescript
29
+ * const projectType = detectProjectType('./my-project');
30
+ * console.log(projectType); // 输出:ProjectType.VUE
31
+ * ```
32
+ */
33
+ export function detectProjectType(directory: string): ProjectType {
34
+ // 检查是否为 uniapp 项目
35
+ if (isUniappProject(directory)) {
36
+ return ProjectType.UNIAPP;
37
+ }
38
+
39
+ // 检查是否为 React 项目
40
+ if (isReactProject(directory)) {
41
+ return ProjectType.REACT;
42
+ }
43
+
44
+ // 检查是否为 Vue 项目
45
+ if (isVueProject(directory)) {
46
+ return ProjectType.VUE;
47
+ }
48
+
49
+ // 未知项目类型
50
+ return ProjectType.UNKNOWN;
51
+ }
52
+
53
+ /**
54
+ * 检查是否为 uniapp 项目
55
+ * @param {string} directory - 目录路径
56
+ * @returns {boolean} 是否为 uniapp 项目
57
+ */
58
+ function isUniappProject(directory: string): boolean {
59
+ // uniapp 项目的特征文件
60
+ const uniappFiles = [
61
+ join(directory, 'pages.json'),
62
+ join(directory, 'manifest.json'),
63
+ join(directory, 'uni.scss')
64
+ ];
65
+
66
+ // 检查是否存在 uniapp 特征文件
67
+ return uniappFiles.some(file => existsSync(file));
68
+ }
69
+
70
+ /**
71
+ * 检查是否为 Vue 项目
72
+ * @param {string} directory - 目录路径
73
+ * @returns {boolean} 是否为 Vue 项目
74
+ */
75
+ function isVueProject(directory: string): boolean {
76
+ // Vue 项目的特征文件
77
+ const vueConfigFiles = [
78
+ join(directory, 'vue.config.js'),
79
+ join(directory, 'vite.config.js'),
80
+ join(directory, 'vite.config.ts'),
81
+ join(directory, 'nuxt.config.js'),
82
+ join(directory, 'nuxt.config.ts')
83
+ ];
84
+
85
+ // 检查是否存在 Vue 配置文件
86
+ if (vueConfigFiles.some(file => existsSync(file))) {
87
+ return true;
88
+ }
89
+
90
+ // 检查 package.json 中是否有 Vue 相关依赖
91
+ return hasDependency(directory, ['vue', '@vue/cli-service', 'nuxt', 'vite']);
92
+ }
93
+
94
+ /**
95
+ * 检查是否为 React 项目
96
+ * @param {string} directory - 目录路径
97
+ * @returns {boolean} 是否为 React 项目
98
+ */
99
+ function isReactProject(directory: string): boolean {
100
+ // React 项目的特征文件
101
+ const reactConfigFiles = [
102
+ join(directory, 'webpack.config.js'),
103
+ join(directory, 'craco.config.js'),
104
+ join(directory, 'next.config.js')
105
+ ];
106
+
107
+ // 检查是否存在 React 配置文件
108
+ if (reactConfigFiles.some(file => existsSync(file))) {
109
+ return true;
110
+ }
111
+
112
+ // 检查 package.json 中是否有 React 相关依赖
113
+ return hasDependency(directory, ['react', 'react-dom', 'next', 'gatsby']);
114
+ }
115
+
116
+ /**
117
+ * 检查 package.json 中是否有指定的依赖
118
+ * @param {string} directory - 目录路径
119
+ * @param {string[]} dependencies - 要检查的依赖名称数组
120
+ * @returns {boolean} 是否有指定的依赖
121
+ */
122
+ function hasDependency(directory: string, dependencies: string[]): boolean {
123
+ const packageJsonPath = join(directory, 'package.json');
124
+
125
+ if (!existsSync(packageJsonPath)) {
126
+ return false;
127
+ }
128
+
129
+ try {
130
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
131
+
132
+ // 检查 dependencies、devDependencies 和 peerDependencies
133
+ const allDependencies = {
134
+ ...packageJson.dependencies,
135
+ ...packageJson.devDependencies,
136
+ ...packageJson.peerDependencies
137
+ };
138
+
139
+ // 检查是否有指定的依赖
140
+ return dependencies.some(dep => allDependencies[dep]);
141
+ } catch (error) {
142
+ // 解析 package.json 失败
143
+ return false;
144
+ }
145
+ }