befly-vite 1.1.10 → 1.1.12

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/README.md CHANGED
@@ -21,7 +21,7 @@ bun add -d befly-vite vite
21
21
 
22
22
  ```javascript
23
23
  // vite.config.js
24
- import { createBeflyViteConfig } from 'befly-vite';
24
+ import { createBeflyViteConfig } from "befly-vite";
25
25
 
26
26
  export default createBeflyViteConfig();
27
27
  ```
@@ -29,11 +29,11 @@ export default createBeflyViteConfig();
29
29
  ### 自定义配置
30
30
 
31
31
  ```javascript
32
- import { createBeflyViteConfig } from 'befly-vite';
33
- import { fileURLToPath } from 'node:url';
32
+ import { createBeflyViteConfig } from "befly-vite";
33
+ import { fileURLToPath } from "node:url";
34
34
 
35
35
  export default createBeflyViteConfig({
36
- root: fileURLToPath(new URL('.', import.meta.url)),
36
+ root: fileURLToPath(new URL(".", import.meta.url)),
37
37
 
38
38
  // 自定义配置
39
39
  userConfig: {
@@ -1,4 +1,4 @@
1
- import { defineConfig, presetAttributify, presetUno } from 'unocss';
1
+ import { defineConfig, presetAttributify, presetUno } from "unocss";
2
2
 
3
3
  /**
4
4
  * 创建 UnoCSS 配置
@@ -7,17 +7,17 @@ export function createUnoConfig(userConfig = {}) {
7
7
  const defaultConfig = {
8
8
  presets: [presetUno(), presetAttributify()],
9
9
  shortcuts: {
10
- 'flex-center': 'flex items-center justify-center',
11
- 'flex-between': 'flex items-center justify-between',
12
- 'flex-col-center': 'flex flex-col items-center justify-center'
10
+ "flex-center": "flex items-center justify-center",
11
+ "flex-between": "flex items-center justify-between",
12
+ "flex-col-center": "flex flex-col items-center justify-center"
13
13
  },
14
14
  theme: {
15
15
  colors: {
16
- primary: '#1890ff',
17
- success: '#52c41a',
18
- warning: '#faad14',
19
- danger: '#ff4d4f',
20
- info: '#1890ff'
16
+ primary: "#1890ff",
17
+ success: "#52c41a",
18
+ warning: "#faad14",
19
+ danger: "#ff4d4f",
20
+ info: "#1890ff"
21
21
  }
22
22
  }
23
23
  };
@@ -27,11 +27,11 @@ export function createUnoConfig(userConfig = {}) {
27
27
  ...userConfig,
28
28
  theme: {
29
29
  ...defaultConfig.theme,
30
- ...(userConfig.theme || {})
30
+ ...userConfig.theme
31
31
  },
32
32
  shortcuts: {
33
33
  ...defaultConfig.shortcuts,
34
- ...(userConfig.shortcuts || {})
34
+ ...userConfig.shortcuts
35
35
  }
36
36
  });
37
37
  }
package/index.js CHANGED
@@ -1,15 +1,17 @@
1
- import { defineConfig, mergeConfig } from 'vite';
2
- import { fileURLToPath } from 'node:url';
3
- import { createVuePlugins } from './plugins/vue.js';
4
- import { createRouterPlugin } from './plugins/router.js';
5
- import { createAutoImportPlugin } from './plugins/auto-import.js';
6
- import { createComponentsPlugin } from './plugins/components.js';
7
- import { createIconsPlugin } from './plugins/icons.js';
8
- import { createUnoCSSPlugin } from './plugins/unocss.js';
9
- import { createDevToolsPlugin } from './plugins/devtools.js';
10
- import { createAnalyzerPlugin } from './plugins/analyzer.js';
11
- import { createCompressionPlugin } from './plugins/compression.js';
12
- import { createInspectPlugin } from './plugins/inspect.js';
1
+ import { fileURLToPath } from "node:url";
2
+
3
+ import { defineConfig, mergeConfig } from "vite";
4
+
5
+ import { createAnalyzerPlugin } from "./plugins/analyzer.js";
6
+ import { createAutoImportPlugin } from "./plugins/auto-import.js";
7
+ import { createComponentsPlugin } from "./plugins/components.js";
8
+ import { createCompressionPlugin } from "./plugins/compression.js";
9
+ import { createDevToolsPlugin } from "./plugins/devtools.js";
10
+ import { createIconsPlugin } from "./plugins/icons.js";
11
+ import { createReactivityTransformPlugin } from "./plugins/reactivity-transform.js";
12
+ import { createRouterPlugin } from "./plugins/router.js";
13
+ import { createUnoCSSPlugin } from "./plugins/unocss.js";
14
+ import { createVuePlugin } from "./plugins/vue.js";
13
15
 
14
16
  /**
15
17
  * 默认分包策略
@@ -19,59 +21,59 @@ function defaultManualChunks(id) {
19
21
  // 注意:必须在 befly-addon 之前判断,因为 addon 会引用这些库
20
22
 
21
23
  // vue-router(优先级最高)
22
- if (id.match(/node_modules[\/\\]vue-router[\/\\]/)) {
23
- return 'vue-router';
24
+ if (id.match(/node_modules[/\\]vue-router[/\\]/)) {
25
+ return "vue-router";
24
26
  }
25
27
 
26
28
  // pinia
27
- if (id.match(/node_modules[\/\\]pinia[\/\\]/)) {
28
- return 'pinia';
29
+ if (id.match(/node_modules[/\\]pinia[/\\]/)) {
30
+ return "pinia";
29
31
  }
30
32
 
31
33
  // vue 核心和运行时(@vue/* 包)
32
- if (id.match(/node_modules[\/\\](vue[\/\\]|@vue[\/\\])/)) {
33
- return 'vue';
34
+ if (id.match(/node_modules[/\\](vue[/\\]|@vue[/\\])/)) {
35
+ return "vue";
34
36
  }
35
37
 
36
38
  // TDesign Vue Next
37
- if (id.includes('tdesign-vue-next')) {
38
- return 'tdesign';
39
+ if (id.includes("tdesign-vue-next")) {
40
+ return "tdesign";
39
41
  }
40
42
 
41
43
  // 工具库
42
- if (id.match(/node_modules[\/\\]axios[\/\\]/)) {
43
- return 'axios';
44
+ if (id.match(/node_modules[/\\]axios[/\\]/)) {
45
+ return "axios";
44
46
  }
45
- if (id.match(/node_modules[\/\\]lodash-es[\/\\]/)) {
46
- return 'lodash';
47
+ if (id.match(/node_modules[/\\]lodash-es[/\\]/)) {
48
+ return "lodash";
47
49
  }
48
50
 
49
51
  // echarts
50
- if (id.match(/node_modules[\/\\]echarts[\/\\]/)) {
51
- return 'echarts';
52
+ if (id.match(/node_modules[/\\]echarts[/\\]/)) {
53
+ return "echarts";
52
54
  }
53
- if (id.match(/node_modules[\/\\]zrender[\/\\]/)) {
54
- return 'zrender';
55
+ if (id.match(/node_modules[/\\]zrender[/\\]/)) {
56
+ return "zrender";
55
57
  }
56
58
 
57
59
  // 图标
58
- if (id.includes('/@iconify/') || id.includes('~icons/')) {
59
- return 'icons';
60
+ if (id.includes("/@iconify/") || id.includes("~icons/")) {
61
+ return "icons";
60
62
  }
61
63
 
62
64
  // Vue Macros
63
- if (id.match(/node_modules[\/\\](@vue-macros|vue-macros)[\/\\]/)) {
64
- return 'vue-macros';
65
+ if (id.match(/node_modules[/\\](@vue-macros|vue-macros)[/\\]/)) {
66
+ return "vue-macros";
65
67
  }
66
68
 
67
69
  // befly-addon(必须在 Vue 判断之后)
68
- if (id.includes('@befly-addon/') || id.includes('packages/addonAdmin/') || id.includes('packages\\addonAdmin\\')) {
69
- return 'befly-addon';
70
+ if (id.includes("@befly-addon/") || id.includes("packages/addonAdmin/") || id.includes("packages\\addonAdmin\\")) {
71
+ return "befly-addon";
70
72
  }
71
73
 
72
74
  // 其他 node_modules 依赖
73
- if (id.includes('node_modules/')) {
74
- return 'vendor';
75
+ if (id.includes("node_modules/")) {
76
+ return "vendor";
75
77
  }
76
78
  }
77
79
 
@@ -92,25 +94,25 @@ export function createBeflyViteConfig(options = {}) {
92
94
  const projectRoot = root || process.cwd();
93
95
 
94
96
  const baseConfig = defineConfig({
95
- base: './',
97
+ base: "./",
96
98
 
97
99
  plugins: [
98
100
  //
99
101
  createUnoCSSPlugin(),
100
- createDevToolsPlugin(),
101
102
  createRouterPlugin({ scanViews: scanViews }),
102
- ...createVuePlugins(),
103
+ createVuePlugin(),
104
+ createReactivityTransformPlugin(),
105
+ createDevToolsPlugin(),
103
106
  createAutoImportPlugin({ resolvers: resolvers }),
104
107
  createComponentsPlugin({ resolvers: resolvers }),
105
108
  createIconsPlugin(),
106
109
  createAnalyzerPlugin(),
107
- createInspectPlugin(),
108
110
  createCompressionPlugin()
109
111
  ],
110
112
 
111
113
  resolve: {
112
114
  alias: {
113
- '@': fileURLToPath(new URL('src', `file:///${projectRoot.replace(/\\/g, '/')}/`))
115
+ "@": fileURLToPath(new URL("src", `file:///${projectRoot.replace(/\\/g, "/")}/`))
114
116
  }
115
117
  },
116
118
 
@@ -120,11 +122,11 @@ export function createBeflyViteConfig(options = {}) {
120
122
  },
121
123
 
122
124
  build: {
123
- target: 'es2020',
124
- outDir: 'dist',
125
- assetsDir: 'assets',
125
+ target: "es2020",
126
+ outDir: "dist",
127
+ assetsDir: "assets",
126
128
  sourcemap: false,
127
- minify: 'esbuild',
129
+ minify: "esbuild",
128
130
  chunkSizeWarningLimit: 1000,
129
131
  commonjsOptions: {
130
132
  include: [/node_modules/],
@@ -132,53 +134,24 @@ export function createBeflyViteConfig(options = {}) {
132
134
  },
133
135
  rollupOptions: {
134
136
  output: {
135
- chunkFileNames: 'assets/js/[name]-[hash].js',
136
- entryFileNames: 'assets/js/[name]-[hash].js',
137
- assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
137
+ chunkFileNames: "assets/js/[name]-[hash].js",
138
+ entryFileNames: "assets/js/[name]-[hash].js",
139
+ assetFileNames: "assets/[ext]/[name]-[hash].[ext]",
138
140
  manualChunks(id) {
139
- // 最高优先级:框架库必须单独打包
140
- if (id.includes('node_modules')) {
141
- // vue-router
142
- if (id.includes('/vue-router/')) return 'vue-router';
143
- // pinia
144
- if (id.includes('/pinia/')) return 'pinia';
145
- // vue 核心(包括 @vue/*)
146
- if (id.includes('/vue/') || id.includes('/@vue/')) return 'vue';
147
- // tdesign
148
- if (id.includes('/tdesign-vue-next/')) return 'tdesign';
149
- // axios
150
- if (id.includes('/axios/')) return 'axios';
151
- // 其他第三方库
152
- return 'vendor';
153
- }
154
- // befly-addon(workspace 包)
155
- if (id.includes('@befly-addon/') || id.includes('/addonAdmin/')) {
156
- return 'befly-addon';
141
+ if (typeof manualChunks === "function") {
142
+ const chunkName = manualChunks(id);
143
+ if (chunkName) return chunkName;
157
144
  }
145
+ return defaultManualChunks(id);
158
146
  }
159
147
  }
160
148
  }
161
149
  },
162
150
 
163
151
  optimizeDeps: {
164
- include: ['vue', 'vue-router', 'pinia', 'axios', 'tdesign-vue-next']
152
+ include: ["vue", "vue-router", "pinia", "axios", "tdesign-vue-next"]
165
153
  }
166
154
  });
167
155
 
168
156
  return mergeConfig(baseConfig, userConfig);
169
157
  }
170
-
171
- // 导出 UnoCSS 配置创建函数
172
- export { createUnoConfig } from './configs/uno.config.js';
173
-
174
- // 导出所有插件创建函数(供高级用户自定义)
175
- export { createVuePlugins } from './plugins/vue.js';
176
- export { createRouterPlugin } from './plugins/router.js';
177
- export { createAutoImportPlugin } from './plugins/auto-import.js';
178
- export { createComponentsPlugin } from './plugins/components.js';
179
- export { createIconsPlugin } from './plugins/icons.js';
180
- export { createUnoCSSPlugin } from './plugins/unocss.js';
181
- export { createDevToolsPlugin } from './plugins/devtools.js';
182
- export { createAnalyzerPlugin } from './plugins/analyzer.js';
183
- export { createCompressionPlugin } from './plugins/compression.js';
184
- export { createInspectPlugin } from './plugins/inspect.js';
package/package.json CHANGED
@@ -1,43 +1,37 @@
1
1
  {
2
2
  "name": "befly-vite",
3
- "version": "1.1.10",
3
+ "version": "1.1.12",
4
+ "private": false,
4
5
  "description": "Befly Vite 配置预设和插件集合",
6
+ "keywords": [
7
+ "befly",
8
+ "config",
9
+ "preset",
10
+ "vite",
11
+ "vue"
12
+ ],
13
+ "homepage": "https://chensuiyi.me",
14
+ "author": "chensuiyi <bimostyle@qq.com>",
15
+ "license": "Apache-2.0",
5
16
  "type": "module",
6
- "private": false,
7
- "publishConfig": {
8
- "access": "public",
9
- "registry": "https://registry.npmjs.org"
10
- },
11
17
  "main": "index.js",
12
18
  "exports": {
13
19
  ".": "./index.js",
14
- "./configs/*": "./configs/*.js",
15
- "./plugins/*": "./plugins/*.js",
16
20
  "./utils/*": "./utils/*.js"
17
21
  },
18
22
  "files": [
23
+ "README.md",
19
24
  "configs/",
20
- "plugins/",
21
25
  "index.js",
22
26
  "package.json",
23
- "README.md"
24
- ],
25
- "keywords": [
26
- "vite",
27
- "vue",
28
- "config",
29
- "preset",
30
- "befly"
27
+ "plugins/",
28
+ "utils/"
31
29
  ],
32
- "author": "chensuiyi <bimostyle@qq.com>",
33
- "homepage": "https://chensuiyi.me",
34
- "license": "Apache-2.0",
35
30
  "dependencies": {
36
31
  "@unocss/preset-attributify": "^66.5.10",
37
32
  "@unocss/preset-uno": "^66.5.10",
38
- "@vitejs/plugin-vue": "^6.0.2",
33
+ "@vitejs/plugin-vue": "^6.0.3",
39
34
  "@vue-macros/reactivity-transform": "^3.1.1",
40
- "befly-shared": "^1.2.7",
41
35
  "sass": "^1.96.0",
42
36
  "unocss": "^66.5.10",
43
37
  "unplugin-auto-import": "^20.3.0",
@@ -46,15 +40,18 @@
46
40
  "unplugin-vue-router": "^0.19.0",
47
41
  "vite-bundle-analyzer": "^1.3.1",
48
42
  "vite-plugin-compression2": "^2.4.0",
49
- "vite-plugin-inspect": "^11.3.3",
50
43
  "vite-plugin-vue-devtools": "^8.0.5"
51
44
  },
52
45
  "peerDependencies": {
53
- "vite": "^8.0.0-beta.2",
54
- "vue": "^3.5.25"
46
+ "vite": "^8.0.0-beta.3",
47
+ "vue": "^3.5.26"
55
48
  },
56
49
  "engines": {
57
50
  "bun": ">=1.3.0"
58
51
  },
59
- "gitHead": "4b7ea2e7f6d7b5c7454f34c30794473d8bb021de"
52
+ "publishConfig": {
53
+ "access": "public",
54
+ "registry": "https://registry.npmjs.org"
55
+ },
56
+ "gitHead": "d42bf60ba6b8b11fef7e759fa008ada075829772"
60
57
  }
@@ -1,16 +1,16 @@
1
- import { analyzer } from 'vite-bundle-analyzer';
1
+ import { analyzer } from "vite-bundle-analyzer";
2
2
 
3
3
  /**
4
4
  * 创建打包分析插件配置
5
5
  */
6
6
  export function createAnalyzerPlugin(options = {}) {
7
- const { analyzerMode = 'static', fileName = 'bundle-report', reportTitle = '打包分析', openAnalyzer = false } = options;
7
+ const { analyzerMode = "static", fileName = "bundle-report", reportTitle = "打包分析", openAnalyzer = false } = options;
8
8
 
9
9
  return analyzer({
10
10
  analyzerMode: analyzerMode,
11
11
  fileName: fileName,
12
12
  reportTitle: reportTitle,
13
- defaultSizes: 'gzip',
13
+ defaultSizes: "gzip",
14
14
  gzipOptions: {},
15
15
  brotliOptions: {},
16
16
  openAnalyzer: openAnalyzer,
@@ -1,6 +1,6 @@
1
- import AutoImport from 'unplugin-auto-import/vite';
2
- import { VueRouterAutoImports } from 'unplugin-vue-router';
3
- import { TDesignResolver } from 'unplugin-vue-components/resolvers';
1
+ import AutoImport from "unplugin-auto-import/vite";
2
+ import { TDesignResolver } from "unplugin-vue-components/resolvers";
3
+ import { VueRouterAutoImports } from "unplugin-vue-router";
4
4
 
5
5
  /**
6
6
  * 创建自动导入插件配置
@@ -9,15 +9,15 @@ export function createAutoImportPlugin(options = {}) {
9
9
  const { resolvers = {} } = options;
10
10
 
11
11
  return AutoImport({
12
- imports: ['vue', 'pinia', VueRouterAutoImports],
12
+ imports: ["vue", "pinia", VueRouterAutoImports],
13
13
  resolvers: [
14
14
  TDesignResolver({
15
- library: 'vue-next'
15
+ library: "vue-next"
16
16
  }),
17
17
  ...(resolvers.auto || [])
18
18
  ],
19
- dts: 'src/types/auto-imports.d.ts',
20
- dirs: ['src/utils', 'src/plugins', 'src/config'],
19
+ dts: "src/types/auto-imports.d.ts",
20
+ dirs: ["src/utils", "src/plugins", "src/config"],
21
21
  vueTemplate: true
22
22
  });
23
23
  }
@@ -1,6 +1,6 @@
1
- import Components from 'unplugin-vue-components/vite';
2
- import { TDesignResolver } from 'unplugin-vue-components/resolvers';
3
- import IconsResolver from 'unplugin-icons/resolver';
1
+ import IconsResolver from "unplugin-icons/resolver";
2
+ import { TDesignResolver } from "unplugin-vue-components/resolvers";
3
+ import Components from "unplugin-vue-components/vite";
4
4
 
5
5
  /**
6
6
  * 创建组件自动导入插件配置
@@ -11,13 +11,13 @@ export function createComponentsPlugin(options = {}) {
11
11
  return Components({
12
12
  resolvers: [
13
13
  TDesignResolver({
14
- library: 'vue-next'
14
+ library: "vue-next"
15
15
  }),
16
16
  IconsResolver({}),
17
17
  ...(resolvers.components || [])
18
18
  ],
19
- dirs: ['src/components'],
19
+ dirs: ["src/components"],
20
20
  deep: true,
21
- dts: 'src/types/components.d.ts'
21
+ dts: "src/types/components.d.ts"
22
22
  });
23
23
  }
@@ -1,10 +1,10 @@
1
- import { compression } from 'vite-plugin-compression2';
1
+ import { compression } from "vite-plugin-compression2";
2
2
 
3
3
  /**
4
4
  * 创建文件压缩插件配置
5
5
  */
6
6
  export function createCompressionPlugin(options = {}) {
7
- const { threshold = 10240, algorithms = ['gzip', 'brotliCompress'] } = options;
7
+ const { threshold = 10240, algorithms = ["gzip", "brotliCompress"] } = options;
8
8
 
9
9
  return compression({
10
10
  include: /\.(html|xml|css|json|js|mjs|svg)$/i,
@@ -1,8 +1,14 @@
1
- import VueDevTools from 'vite-plugin-vue-devtools';
1
+ import VueDevTools from "vite-plugin-vue-devtools";
2
2
 
3
3
  /**
4
4
  * 创建 Vue DevTools 插件配置(仅开发环境)
5
5
  */
6
6
  export function createDevToolsPlugin() {
7
+ // 该插件内部会引入 inspector 能力;如果在非开发环境启用,容易引入不必要的解析开销/兼容性风险。
8
+ // Vite 的 plugins 数组允许包含 null/false,会被自动忽略。
9
+ if (process.env.NODE_ENV === "production") {
10
+ return null;
11
+ }
12
+
7
13
  return VueDevTools();
8
14
  }
package/plugins/icons.js CHANGED
@@ -1,13 +1,13 @@
1
- import Icons from 'unplugin-icons/vite';
1
+ import Icons from "unplugin-icons/vite";
2
2
 
3
3
  /**
4
4
  * 创建图标插件配置
5
5
  */
6
6
  export function createIconsPlugin() {
7
7
  return Icons({
8
- compiler: 'vue3',
8
+ compiler: "vue3",
9
9
  autoInstall: false,
10
- defaultClass: 'icon-befly',
11
- defaultStyle: 'vertical-align: middle;'
10
+ defaultClass: "icon-befly",
11
+ defaultStyle: "vertical-align: middle;"
12
12
  });
13
13
  }
@@ -0,0 +1,8 @@
1
+ import ReactivityTransform from "@vue-macros/reactivity-transform/vite";
2
+
3
+ // 创建 Vue Reactivity Transform 插件配置
4
+ export function createReactivityTransformPlugin() {
5
+ return ReactivityTransform({
6
+ exclude: []
7
+ });
8
+ }
package/plugins/router.js CHANGED
@@ -1,4 +1,4 @@
1
- import VueRouter from 'unplugin-vue-router/vite';
1
+ import VueRouter from "unplugin-vue-router/vite";
2
2
 
3
3
  /**
4
4
  * 创建路由插件配置
@@ -8,9 +8,9 @@ export function createRouterPlugin(options = {}) {
8
8
 
9
9
  return VueRouter({
10
10
  routesFolder: scanViews(),
11
- dts: './src/types/typed-router.d.ts',
12
- extensions: ['.vue'],
13
- importMode: 'async',
14
- exclude: ['**/components/**']
11
+ dts: "./src/types/typed-router.d.ts",
12
+ extensions: [".vue"],
13
+ importMode: "async",
14
+ exclude: ["**/components/**"]
15
15
  });
16
16
  }
package/plugins/unocss.js CHANGED
@@ -1,4 +1,4 @@
1
- import UnoCSS from 'unocss/vite';
1
+ import UnoCSS from "unocss/vite";
2
2
 
3
3
  /**
4
4
  * 创建 UnoCSS 插件配置
package/plugins/vue.js CHANGED
@@ -1,19 +1,13 @@
1
- import vue from '@vitejs/plugin-vue';
2
- import ReactivityTransform from '@vue-macros/reactivity-transform/vite';
1
+ import vue from "@vitejs/plugin-vue";
3
2
 
4
3
  /**
5
4
  * 创建 Vue 插件配置
6
5
  */
7
- export function createVuePlugins() {
8
- return [
9
- vue({
10
- script: {
11
- defineModel: true,
12
- propsDestructure: true
13
- }
14
- }),
15
- ReactivityTransform({
16
- exclude: []
17
- })
18
- ];
6
+ export function createVuePlugin() {
7
+ return vue({
8
+ script: {
9
+ defineModel: true,
10
+ propsDestructure: true
11
+ }
12
+ });
19
13
  }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @typedef {Object} ArrayToTreeOptions
3
+ * @property {string=} idField
4
+ * @property {string=} pidField
5
+ * @property {string=} childrenField
6
+ * @property {any=} rootPid
7
+ * @property {(node: any) => any=} mapFn
8
+ */
9
+
10
+ /**
11
+ * @template T
12
+ * @param {T[]} items
13
+ * @param {ArrayToTreeOptions=} options
14
+ * @returns {T[]}
15
+ */
16
+ export function arrayToTree(items, options = {}) {
17
+ const idField = typeof options.idField === "string" ? options.idField : "id";
18
+ const pidField = typeof options.pidField === "string" ? options.pidField : "pid";
19
+ const childrenField = typeof options.childrenField === "string" ? options.childrenField : "children";
20
+ const rootPid = "rootPid" in options ? options.rootPid : 0;
21
+ const mapFn = typeof options.mapFn === "function" ? options.mapFn : null;
22
+
23
+ /** @type {T[]} */
24
+ const tree = [];
25
+
26
+ for (const item of items) {
27
+ // @ts-ignore
28
+ const pid = item[pidField];
29
+
30
+ if (Object.is(pid, rootPid)) {
31
+ const node = Object.assign({}, item);
32
+ const mappedNode = mapFn ? mapFn(node) : node;
33
+
34
+ // 子节点 rootPid = node[id]
35
+ // @ts-ignore
36
+ const nextRootPid = mappedNode[idField];
37
+ const children = arrayToTree(items, {
38
+ idField: idField,
39
+ pidField: pidField,
40
+ childrenField: childrenField,
41
+ rootPid: nextRootPid,
42
+ mapFn: mapFn
43
+ });
44
+
45
+ if (children.length > 0) {
46
+ // @ts-ignore
47
+ mappedNode[childrenField] = children;
48
+ }
49
+
50
+ tree.push(mappedNode);
51
+ }
52
+ }
53
+
54
+ return tree;
55
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * UnoCSS 配置创建函数(对外稳定导出)
3
+ *
4
+ * 注意:befly-vite 不再从主入口导出 configs/plugins,统一通过 utils 子路径导入。
5
+ */
6
+
7
+ export { createUnoConfig } from "../configs/uno.config.js";
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @typedef {Object} FieldClearOptions
3
+ * @property {string[]=} pickKeys
4
+ * @property {string[]=} omitKeys
5
+ * @property {any[]=} keepValues
6
+ * @property {any[]=} excludeValues
7
+ * @property {Record<string, any>=} keepMap
8
+ */
9
+
10
+ function isObject(val) {
11
+ return val !== null && typeof val === "object" && !Array.isArray(val);
12
+ }
13
+
14
+ function isArray(val) {
15
+ return Array.isArray(val);
16
+ }
17
+
18
+ /**
19
+ * 清理对象/数组字段
20
+ * - 支持 pick/omit/keepValues/excludeValues
21
+ * - 支持 keepMap 强制保留
22
+ * @template T
23
+ * @param {T|T[]} data
24
+ * @param {FieldClearOptions=} options
25
+ * @returns {any}
26
+ */
27
+ export function fieldClear(data, options = {}) {
28
+ const pickKeys = options.pickKeys;
29
+ const omitKeys = options.omitKeys;
30
+ const keepValues = options.keepValues;
31
+ const excludeValues = options.excludeValues;
32
+ const keepMap = options.keepMap;
33
+
34
+ const filterObj = (obj) => {
35
+ /** @type {Record<string, any>} */
36
+ const result = {};
37
+
38
+ let keys = Object.keys(obj);
39
+ if (pickKeys && pickKeys.length) {
40
+ keys = keys.filter((k) => pickKeys.includes(k));
41
+ }
42
+ if (omitKeys && omitKeys.length) {
43
+ keys = keys.filter((k) => !omitKeys.includes(k));
44
+ }
45
+
46
+ for (const key of keys) {
47
+ const value = obj[key];
48
+
49
+ // 1. keepMap 优先
50
+ if (keepMap && Object.prototype.hasOwnProperty.call(keepMap, key)) {
51
+ if (Object.is(keepMap[key], value)) {
52
+ result[key] = value;
53
+ continue;
54
+ }
55
+ }
56
+
57
+ // 2. keepValues
58
+ if (keepValues && keepValues.length && !keepValues.includes(value)) {
59
+ continue;
60
+ }
61
+
62
+ // 3. excludeValues
63
+ if (excludeValues && excludeValues.length && excludeValues.includes(value)) {
64
+ continue;
65
+ }
66
+
67
+ result[key] = value;
68
+ }
69
+
70
+ return result;
71
+ };
72
+
73
+ if (isArray(data)) {
74
+ return data
75
+ .map((item) => {
76
+ if (isObject(item)) {
77
+ return filterObj(item);
78
+ }
79
+ return item;
80
+ })
81
+ .filter((item) => {
82
+ if (isObject(item)) {
83
+ return Object.keys(item).length > 0;
84
+ }
85
+ return true;
86
+ });
87
+ }
88
+
89
+ if (isObject(data)) {
90
+ return filterObj(data);
91
+ }
92
+
93
+ return data;
94
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * 密码哈希工具(浏览器侧)
3
+ * 使用 SHA-256 + 盐值对密码进行单向哈希
4
+ * @param {string} password
5
+ * @param {string=} salt
6
+ * @returns {Promise<string>} 十六进制哈希
7
+ */
8
+ export async function hashPassword(password, salt = "befly") {
9
+ const data = String(password) + String(salt);
10
+
11
+ const encoder = new TextEncoder();
12
+ const dataBuffer = encoder.encode(data);
13
+
14
+ // Web Crypto API
15
+ const hashBuffer = await crypto.subtle.digest("SHA-256", dataBuffer);
16
+
17
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
18
+ const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
19
+
20
+ return hashHex;
21
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * 自定义布局处理函数
3
+ * 根据文件名后缀 _数字 判断使用哪个布局
4
+ */
5
+
6
+ /**
7
+ * @typedef {Object} RouteConfig
8
+ * @property {string=} path
9
+ * @property {any=} component
10
+ * @property {RouteConfig[]=} children
11
+ * @property {Record<string, any>=} meta
12
+ */
13
+
14
+ /**
15
+ * @typedef {Object} LayoutConfig
16
+ * @property {string} path
17
+ * @property {string} layoutName
18
+ * @property {any} component
19
+ * @property {Record<string, any>=} meta
20
+ */
21
+
22
+ /**
23
+ * @param {RouteConfig[]} routes
24
+ * @param {string=} inheritLayout
25
+ * @returns {LayoutConfig[]}
26
+ */
27
+ export function Layouts(routes, inheritLayout = "") {
28
+ /** @type {LayoutConfig[]} */
29
+ const result = [];
30
+
31
+ for (const route of routes) {
32
+ const currentPath = route.path || "";
33
+
34
+ const pathMatch = currentPath.match(/_(\d+)$/);
35
+ const currentLayout = pathMatch ? pathMatch[1] : inheritLayout;
36
+
37
+ // 中间节点:递归处理子路由,不包裹布局
38
+ if (route.children && route.children.length > 0) {
39
+ const cleanPath = pathMatch ? currentPath.replace(/_\d+$/, "") : currentPath;
40
+ const childConfigs = Layouts(route.children, currentLayout);
41
+
42
+ for (const child of childConfigs) {
43
+ const mergedPath = cleanPath ? `${cleanPath}/${child.path}`.replace(/\/+/, "/") : child.path;
44
+ result.push({
45
+ path: mergedPath,
46
+ layoutName: child.layoutName,
47
+ component: child.component,
48
+ meta: child.meta
49
+ });
50
+ }
51
+ continue;
52
+ }
53
+
54
+ // 叶子节点:包裹布局
55
+ const lastPart = currentPath;
56
+ const match = lastPart.match(/_(\d+)$/);
57
+ const layoutName = match ? match[1] : currentLayout || "default";
58
+
59
+ let cleanPath = "";
60
+ if (lastPart === "index" || (lastPart.startsWith("index_") && match)) {
61
+ cleanPath = "";
62
+ } else if (match) {
63
+ cleanPath = lastPart.replace(/_\d+$/, "");
64
+ } else {
65
+ cleanPath = lastPart;
66
+ }
67
+
68
+ result.push({
69
+ path: cleanPath,
70
+ layoutName: layoutName,
71
+ component: route.component,
72
+ meta: route.meta
73
+ });
74
+ }
75
+
76
+ return result;
77
+ }
78
+
79
+ /**
80
+ * 将 Layouts 输出的扁平配置转换为 Vue Router 的 RouteRecordRaw
81
+ * 说明:resolveLayoutComponent 由业务方提供,以避免 utils 强耦合具体项目的布局路径。
82
+ *
83
+ * @param {LayoutConfig[]} configs
84
+ * @param {(layoutName: string) => any} resolveLayoutComponent
85
+ * @returns {import('vue-router').RouteRecordRaw[]}
86
+ */
87
+ export function applyLayouts(configs, resolveLayoutComponent) {
88
+ return configs.map((config) => {
89
+ const layoutComponent = resolveLayoutComponent(config.layoutName);
90
+
91
+ return {
92
+ path: config.path,
93
+ component: layoutComponent,
94
+ meta: config.meta,
95
+ children: [
96
+ {
97
+ path: "",
98
+ component: config.component
99
+ }
100
+ ]
101
+ };
102
+ });
103
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * 路由相关工具函数(守卫 / 布局装配 / resolver 等)
3
+ */
4
+
5
+ import { Layouts, applyLayouts } from "./layouts.js";
6
+
7
+ /**
8
+ * 规范化路由 path:去尾随 "/"(根路径 "/" 例外)。
9
+ *
10
+ * @param {any} path
11
+ * @returns {any}
12
+ */
13
+ export function normalizeRoutePath(path) {
14
+ if (typeof path !== "string") {
15
+ return path;
16
+ }
17
+
18
+ const normalized = path.replace(/\/+$/, "");
19
+ return normalized.length === 0 ? "/" : normalized;
20
+ }
21
+
22
+ /**
23
+ * 应用一个最小可用的 token 鉴权守卫(业务方提供 token 获取方式与路径)。
24
+ *
25
+ * 约定:当路由 meta.public === true 时认为是公开路由。
26
+ *
27
+ * @param {import('vue-router').Router} router
28
+ * @param {{
29
+ * getToken: () => any,
30
+ * loginPath: string,
31
+ * homePath: string
32
+ * }} options
33
+ */
34
+ export function applyTokenAuthGuard(router, options) {
35
+ const normalizedLoginPath = normalizeRoutePath(options.loginPath);
36
+ const normalizedHomePath = normalizeRoutePath(options.homePath);
37
+
38
+ router.beforeEach(async (to, _from, next) => {
39
+ const token = options.getToken();
40
+ const toPath = normalizeRoutePath(to.path);
41
+
42
+ // 0. 根路径重定向
43
+ if (toPath === "/") {
44
+ return next(token ? normalizedHomePath : normalizedLoginPath);
45
+ }
46
+
47
+ // 1. 未登录且访问非公开路由 → 跳转登录
48
+ if (!token && to.meta?.public !== true && toPath !== normalizedLoginPath) {
49
+ return next(normalizedLoginPath);
50
+ }
51
+
52
+ // 2. 已登录访问登录页 → 跳转首页
53
+ if (token && toPath === normalizedLoginPath) {
54
+ return next(normalizedHomePath);
55
+ }
56
+
57
+ next();
58
+ });
59
+ }
60
+
61
+ /**
62
+ * 将“组件/懒加载函数/Promise”统一转换为 Vue Router 可接受的懒加载 component 函数。
63
+ *
64
+ * - 如果已经是函数(通常是 `() => import(...)`),直接返回。
65
+ * - 否则包一层函数(使其变成 lazy component)。
66
+ *
67
+ * @param {any} value
68
+ * @returns {any}
69
+ */
70
+ function toLazyComponent(value) {
71
+ if (typeof value === "function") {
72
+ return value;
73
+ }
74
+
75
+ return () => value;
76
+ }
77
+
78
+ /**
79
+ * 创建布局组件解析器(resolver)。
80
+ *
81
+ * @param {{
82
+ * resolveDefaultLayout: () => any,
83
+ * resolveNamedLayout: (layoutName: string) => any,
84
+ * defaultLayoutName?: string
85
+ * }} options
86
+ * @returns {(layoutName: string) => any}
87
+ */
88
+ export function createLayoutComponentResolver(options) {
89
+ const defaultLayoutName = options.defaultLayoutName || "default";
90
+
91
+ return (layoutName) => {
92
+ if (layoutName === defaultLayoutName) {
93
+ return toLazyComponent(options.resolveDefaultLayout());
94
+ }
95
+
96
+ return toLazyComponent(options.resolveNamedLayout(layoutName));
97
+ };
98
+ }
99
+
100
+ /**
101
+ * 将 auto-routes 的 routes 按 `_数字` 规则套用布局组件,并输出 Vue Router 的 RouteRecordRaw[]。
102
+ *
103
+ * @param {any[]} routes
104
+ * @param {(layoutName: string) => any} resolveLayoutComponent
105
+ * @returns {import('vue-router').RouteRecordRaw[]}
106
+ */
107
+ export function buildLayoutRoutes(routes, resolveLayoutComponent) {
108
+ return applyLayouts(Layouts(routes), resolveLayoutComponent);
109
+ }
@@ -0,0 +1,55 @@
1
+ import { existsSync, readdirSync, realpathSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ /**
5
+ * 扫描项目和所有 @befly-addon 包的 views 目录
6
+ * 用于 unplugin-vue-router 的 routesFolder 配置
7
+ * 注意:此函数只能在 vite.config.js 中使用(Node.js 环境),不能在浏览器中使用
8
+ * @returns {Array<{ src: string, path: string, exclude: string[] }>} 路由文件夹配置数组
9
+ */
10
+ export function scanViews() {
11
+ const projectRoot = process.cwd();
12
+ const addonBasePath = join(projectRoot, "node_modules", "@befly-addon");
13
+
14
+ /** @type {Array<{ src: string, path: string, exclude: string[] }>} */
15
+ const routesFolders = [];
16
+
17
+ // 1. 项目自身 views
18
+ const projectViewsPath = join(projectRoot, "src", "views");
19
+ if (existsSync(projectViewsPath)) {
20
+ routesFolders.push({
21
+ src: realpathSync(projectViewsPath),
22
+ path: "",
23
+ exclude: ["**/components/**"]
24
+ });
25
+ }
26
+
27
+ // 2. 扫描 @befly-addon/*/views
28
+ if (!existsSync(addonBasePath)) {
29
+ return routesFolders;
30
+ }
31
+
32
+ try {
33
+ const addonDirs = readdirSync(addonBasePath);
34
+
35
+ for (const addonName of addonDirs) {
36
+ const addonPath = join(addonBasePath, addonName);
37
+ if (!existsSync(addonPath)) {
38
+ continue;
39
+ }
40
+
41
+ const viewsPath = join(addonPath, "views");
42
+ if (existsSync(viewsPath)) {
43
+ routesFolders.push({
44
+ src: realpathSync(viewsPath),
45
+ path: `addon/${addonName}/`,
46
+ exclude: ["**/components/**"]
47
+ });
48
+ }
49
+ }
50
+ } catch {
51
+ // 扫描失败保持静默,避免影响 Vite 启动
52
+ }
53
+
54
+ return routesFolders;
55
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * 为表格列添加默认配置
3
+ * @param {any[]} columns
4
+ * @param {Record<string, any>=} customConfig
5
+ * @returns {any[]}
6
+ */
7
+ export function withDefaultColumns(columns, customConfig = {}) {
8
+ /** @type {Record<string, any>} */
9
+ const specialColumnConfig = Object.assign(
10
+ {
11
+ operation: { width: 100, align: "center", fixed: "right" },
12
+ state: { width: 100, align: "center" },
13
+ id: { width: 200, align: "center" }
14
+ },
15
+ customConfig
16
+ );
17
+
18
+ return columns.map((col) => {
19
+ const colKey = col && col.colKey;
20
+
21
+ let specialConfig = colKey ? specialColumnConfig[colKey] : undefined;
22
+
23
+ if (!specialConfig && colKey && (colKey.endsWith("At") || colKey.endsWith("At2"))) {
24
+ specialConfig = { align: "center" };
25
+ }
26
+
27
+ const base = {
28
+ width: 200,
29
+ ellipsis: true
30
+ };
31
+
32
+ const merged = Object.assign({}, base);
33
+ if (specialConfig) {
34
+ Object.assign(merged, specialConfig);
35
+ }
36
+ Object.assign(merged, col);
37
+
38
+ return merged;
39
+ });
40
+ }
@@ -1,11 +0,0 @@
1
- import Inspect from 'vite-plugin-inspect';
2
-
3
- /**
4
- * 创建 Vite 插件转换分析工具
5
- */
6
- export function createInspectPlugin() {
7
- return Inspect({
8
- build: true,
9
- outputDir: '.vite-inspect'
10
- });
11
- }