@infly/libs 2.0.43 → 2.0.45
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 +104 -67
- package/{module → adapters/vue2/module}/Permission.js +2 -2
- package/{module → adapters/vue2/module}/REST.js +32 -20
- package/{module → adapters/vue2/module}/TokenService.js +10 -1
- package/adapters/vue2/module/file-export.js +18 -0
- package/adapters/vue2/postinstall.js +46 -0
- package/adapters/vue2/project-preview.js +152 -0
- package/adapters/vue2/script-config.js +26 -0
- package/adapters/vue2/store/index.js +1 -0
- package/{store → adapters/vue2/store}/modules/user.js +1 -1
- package/adapters/vue2/vite/index.js +95 -0
- package/adapters/vue2/vite/jest.config.js +11 -0
- package/{build → adapters/vue2}/webpack5/webpack.base.js +26 -79
- package/bin/cli.js +71 -71
- package/build/build-dist/index.js +863 -863
- package/build/build-dist/script-management.js +5 -5
- package/{script/build → build/build-utils}/command.js +13 -4
- package/{script/build → build/build-utils}/deps.js +1 -1
- package/{script/build → build/build-utils}/git.js +6 -4
- package/{script/build → build/build-utils}/preview.js +4 -1
- package/{script/webhook/webhook.js → build/build-utils/webhook-single.js} +7 -2
- package/{script/build → build/build-utils}/webhook.js +3 -2
- package/build/docker/compose-command.js +74 -74
- package/build/docker/compose-release.js +91 -91
- package/compat/init.js +9 -0
- package/index.js +8 -20
- package/module/Uts.js +77 -7265
- package/module/cjs/{request-url-rules.cjs → request-url-rules.js} +6 -0
- package/module/cjs/rest-error-rules.js +33 -0
- package/package.json +124 -92
- package/script/git-automation/git-clone.js +3 -4
- package/script/git-automation/git-configure.js +29 -0
- package/script/git-automation/index.js +0 -14
- package/script/git-automation/submodule-status.js +113 -0
- package/script/git-automation/submodule-utils.js +18 -0
- package/script/index.js +1 -26
- package/tools/auto-export.js +1 -2
- package/tools/concurrency.js +30 -0
- package/tools/progress.js +63 -0
- package/tools/project-command.js +198 -194
- package/tools/project-preview.js +1 -148
- package/tools/select-option.js +60 -60
- package/tools/workspace-config.js +145 -0
- package/.prettierrc +0 -5
- package/build/build-dist/script-management.test.js +0 -16
- package/build/docker/compose-command.test.js +0 -148
- package/build/docker/compose-release.test.js +0 -101
- package/build/docker/docker-build-push.test.js +0 -124
- package/build/webpack5/remove-legacy-assets-plugin.js +0 -84
- package/build/webpack5/webpack.base.test.js +0 -59
- package/lib/index.js +0 -6
- package/lib/server/connect.js +0 -3011
- package/lib/server/serve-static.js +0 -4848
- package/module/cjs/deep-merge.cjs +0 -38
- package/script/pts/cloud-scenes.mjs +0 -65
- package/script/pts/cloud.js +0 -151
- package/script/pts/generate-cloud-params.mjs +0 -210
- package/script/pts/generate-cloud-params.test.mjs +0 -67
- package/store/index.js +0 -0
- package/tools/file-export.js +0 -165
- package/tools/project-command.test.js +0 -267
- package/tools/select-option.test.js +0 -52
- package/webpack.config.js +0 -38
- /package/{module → adapters/vue2/module}/Router.js +0 -0
- /package/{build → adapters/vue2}/webpack5/inline-runtime-plugin.js +0 -0
- /package/{script/build → build/build-utils}/env.js +0 -0
- /package/module/cjs/{page-config.cjs → page-config.js} +0 -0
package/README.md
CHANGED
|
@@ -1,35 +1,60 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @infly/libs
|
|
2
2
|
|
|
3
3
|
## 介绍
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`@infly/libs` 是不受 Vue、React 等前端框架限制的独立 JavaScript 工具库。包根入口无副作用;HTTP、Token、文件处理、Docker/Compose、通用项目命令等能力可被不同技术栈使用。
|
|
6
|
+
|
|
7
|
+
Vue 2 专属的 Permission、Vuex store、实例 mixin、Vue CLI 预览和 webpack 配置集中在 `adapters/vue2/`,避免核心入口依赖 Vue 项目约定。
|
|
6
8
|
|
|
7
9
|
## 注意事项
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
新代码应显式导入 Vue 2 适配路径。Webpack 适配器仅服务脱离
|
|
12
|
+
admin-monorepo 的子模块独立构建;Monorepo 根构建不会调用该实现。
|
|
10
13
|
|
|
11
14
|
## 软件架构
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
├──
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
│
|
|
20
|
-
│
|
|
21
|
-
├──
|
|
22
|
-
├──
|
|
23
|
-
├── module
|
|
24
|
-
├──
|
|
25
|
-
│
|
|
26
|
-
│
|
|
27
|
-
├──
|
|
28
|
-
├──
|
|
29
|
-
|
|
30
|
-
├──
|
|
31
|
-
|
|
32
|
-
|
|
16
|
+
```text
|
|
17
|
+
@infly/libs
|
|
18
|
+
├── index.js # 框架无关、无副作用的包根入口
|
|
19
|
+
├── module/ # HTTP、Token、格式化等通用模块
|
|
20
|
+
├── tools/ # 文件与项目命令等通用工具
|
|
21
|
+
├── build/
|
|
22
|
+
│ ├── docker/ # Docker/Compose 工具
|
|
23
|
+
│ └── build-dist/ # 构建产物处理
|
|
24
|
+
├── adapters/vue2/
|
|
25
|
+
│ ├── module/file-export.js # 注入请求与 Element UI 能力的 Vue2 导出 mixin
|
|
26
|
+
│ ├── module/Permission.js # Vue Router/Vuex 权限适配
|
|
27
|
+
│ ├── store/ # Vuex 模块
|
|
28
|
+
│ ├── project-preview.js # Vue CLI 预览
|
|
29
|
+
│ ├── script-config.js # Vue CLI 脚本模板
|
|
30
|
+
│ ├── vite/ # Monorepo Vite 定位与兼容入口
|
|
31
|
+
│ ├── webpack5/ # 独立子仓 Webpack 兼容适配器
|
|
32
|
+
│ └── postinstall.js # 仅 Vue 2 项目执行初始化
|
|
33
|
+
├── bin/cli.js # 命令行入口
|
|
34
|
+
└── dataInit/ # 公共枚举
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Vue2 文件导出适配:
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
import { fileExportMixin } from "@infly/libs/adapters/vue2/module/file-export.js";
|
|
41
|
+
|
|
42
|
+
Vue.mixin(fileExportMixin);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
适配器只负责从 Vue 实例注入 `$axios`、`$message`、`$msgbox` 和 `$loading`;确认、请求与下载流程由 `@infly/ts-libs` 的 `exportFile` 实现。`@infly/ui` 不公开该 mixin。
|
|
46
|
+
|
|
47
|
+
根入口加载示例:
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
const inflyLibs = require('@infly/libs');
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
根入口继续保留历史 `init()` API,但只在显式调用时懒加载 Vue 2 适配器,不会在 `require('@infly/libs')` 时自动执行:
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
inflyLibs.init();
|
|
57
|
+
```
|
|
33
58
|
|
|
34
59
|
## 安装教程
|
|
35
60
|
|
|
@@ -49,57 +74,69 @@ infly-libs build:docker --local --env prod
|
|
|
49
74
|
可通过可选参数 `--cicd-url` 在构建和镜像推送全部成功后输出蓝色发布系统链接。支持等号和空格两种传参形式:
|
|
50
75
|
|
|
51
76
|
```bash
|
|
52
|
-
infly-libs build:docker --local --cicd-url="https://
|
|
77
|
+
infly-libs build:docker --local --cicd-url="https://deploy.example.com/projects/admin/"
|
|
53
78
|
```
|
|
54
79
|
|
|
55
80
|
```bash
|
|
56
|
-
infly-libs build:docker --local --cicd-url "https://
|
|
81
|
+
infly-libs build:docker --local --cicd-url "https://deploy.example.com/projects/admin/"
|
|
57
82
|
```
|
|
58
83
|
|
|
59
84
|
未传 `--cicd-url` 时保持原有输出。参数值必须是合法的 HTTP/HTTPS URL,且不能包含用户名、密码或控制字符。
|
|
60
85
|
|
|
61
|
-
##
|
|
62
|
-
|
|
63
|
-
`
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
## HTTP 环境保护配置
|
|
87
|
+
|
|
88
|
+
`module/REST.js` 不内置任何项目域名。应用如需在测试环境误连正式接口时告警,应由应用显式注入完整的正式 API 基地址:
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
const service = createService({
|
|
92
|
+
productionBaseURLs: ["https://api.example.com"]
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
未传 `productionBaseURLs` 时不启用该项目级域名判断。
|
|
97
|
+
|
|
98
|
+
## 配置驱动的项目命令
|
|
99
|
+
|
|
100
|
+
`project` 命令将多平台项目的开发和构建目标从 package scripts 移到项目配置:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
infly-libs project
|
|
104
|
+
infly-libs project dev
|
|
105
|
+
infly-libs project build
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
命令从当前目录 `package.json` 的 `infly.targets`、`infly.dev` 和 `infly.build` 读取配置。交互选择会追加“退出”选项;选择退出或按取消键时会正常结束,不执行后续命令。项目命令不支持 `--config` 文件入口。
|
|
109
|
+
|
|
110
|
+
未指定 action、target 或构建环境时,会在 TTY 中显示单选菜单。CI/CD 必须显式传参:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
infly-libs project dev --target QDXY
|
|
114
|
+
infly-libs project build --env stage --target SQ223
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`--dry-run` 只打印带 `VUE_APP_PLATFORM` 的命令计划。`infly.targets` 推荐使用平台代码数组,例如 `["DEFAULT", "MER", "JXEE"]`;同时兼容对象形式,对象值中的 `label` 只用于交互显示。普通 target 就是注入 `VUE_APP_PLATFORM` 的平台代码,批量构建通过 `build.presets` 组合多个 target。
|
|
118
|
+
|
|
119
|
+
`dev.command` 以及 `build.before/command/after` 同时支持字符串和参数数组。字符串按空白拆分为可执行文件和参数,不经过 shell;参数本身包含空格时应继续使用数组形式。
|
|
120
|
+
|
|
84
121
|
`build.environments` 推荐使用 `选择键:实际 mode` 数组,例如 `["prod:production", "stage:staging"]`;CLI 使用冒号前的值进行选择,并把冒号后的值作为 `--mode` 参数。没有冒号时选择键和 mode 同名。对象形式继续兼容,用于自定义 `label`、`mode` 或 `args`。项目如果自行管理构建入口,应设置 `infly.buildConfigs.manageScripts=false`,避免安装阶段重新写入传统 `build:prod/build:stage`。
|
|
85
122
|
|
|
86
|
-
### Docker Compose 版本发布
|
|
87
|
-
|
|
88
|
-
`docker:compose` 面向已有 Compose 文件的镜像版本递增、配置校验和构建,与上面的 Dockerfile/commit-tag `build:docker` 流程互不替代:
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
infly-libs docker:compose --target yhqy --bump patch
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
默认从当前目录 `package.json` 的 `infly.docker.targets` 读取目标,并以该 package 所在目录解析 Compose 相对路径;省略 `--target` 时在 TTY 中交互选择。`--config` 外部 JS 配置和原有 `--project-dir` + `--file` 直传方式继续兼容。
|
|
95
|
-
|
|
96
|
-
参数约束:
|
|
97
|
-
|
|
98
|
-
- `--target`:选择 `infly.docker.targets` 或外部配置中的目标;非交互环境必须显式传入。
|
|
99
|
-
- `--config`:可选的外部 JS 配置文件,使用 `targets.<name>.file` 结构。
|
|
100
|
-
- `--project-dir` + `--file`:不使用 target 配置时的兼容直传方式,Compose 文件不能越出 `project-dir`。
|
|
101
|
-
- `--bump patch`:目前只支持 patch 递增,且 Compose 中必须恰好有一个 `x.y.z` 镜像 tag。
|
|
102
|
-
- `--push`:显式推送;未传时只执行 `docker compose config` 和 `build`。
|
|
103
|
-
- `--dry-run`:只打印版本变化和计划命令,不写文件、不调用 Docker。
|
|
104
|
-
|
|
123
|
+
### Docker Compose 版本发布
|
|
124
|
+
|
|
125
|
+
`docker:compose` 面向已有 Compose 文件的镜像版本递增、配置校验和构建,与上面的 Dockerfile/commit-tag `build:docker` 流程互不替代:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
infly-libs docker:compose --target yhqy --bump patch
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
默认从当前目录 `package.json` 的 `infly.docker.targets` 读取目标,并以该 package 所在目录解析 Compose 相对路径;省略 `--target` 时在 TTY 中交互选择。`--config` 外部 JS 配置和原有 `--project-dir` + `--file` 直传方式继续兼容。
|
|
132
|
+
|
|
133
|
+
参数约束:
|
|
134
|
+
|
|
135
|
+
- `--target`:选择 `infly.docker.targets` 或外部配置中的目标;非交互环境必须显式传入。
|
|
136
|
+
- `--config`:可选的外部 JS 配置文件,使用 `targets.<name>.file` 结构。
|
|
137
|
+
- `--project-dir` + `--file`:不使用 target 配置时的兼容直传方式,Compose 文件不能越出 `project-dir`。
|
|
138
|
+
- `--bump patch`:目前只支持 patch 递增,且 Compose 中必须恰好有一个 `x.y.z` 镜像 tag。
|
|
139
|
+
- `--push`:显式推送;未传时只执行 `docker compose config` 和 `build`。
|
|
140
|
+
- `--dry-run`:只打印版本变化和计划命令,不写文件、不调用 Docker。
|
|
141
|
+
|
|
105
142
|
构建、校验或推送失败时,命令会恢复原 Compose 内容。公共包不内置 yhqy、qdxy 等业务目标映射;目标配置和单一发布 script 应归 Compose 所在项目所有。
|
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST HTTP 客户端(Vue 2 / axios 生态)
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ 本模块是 Vue 2 生态专用 axios 封装(依赖 process.env.VUE_APP_BASE_API、
|
|
5
|
+
* window.location、TokenService),与 @infly/ts-libs 的 createHttpClient
|
|
6
|
+
* (框架无关 fetch 客户端)是并行实现,非代码重复。
|
|
7
|
+
*
|
|
8
|
+
* 新项目(React/非 Vue 生态)请使用 @infly/ts-libs createHttpClient。
|
|
9
|
+
*/
|
|
1
10
|
import axios from "axios";
|
|
2
|
-
import Uts from "
|
|
11
|
+
import Uts from "../../../module/Uts.js";
|
|
3
12
|
import TokenService from "./TokenService.js";
|
|
4
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
matchesConfiguredBaseURL,
|
|
15
|
+
resolveInternalServiceURL
|
|
16
|
+
} from "../../../module/cjs/request-url-rules.js";
|
|
17
|
+
import {
|
|
18
|
+
normalizeErrorPayload,
|
|
19
|
+
resolveStatusErrorMessage
|
|
20
|
+
} from "../../../module/cjs/rest-error-rules.js";
|
|
5
21
|
|
|
6
22
|
const baseURL = process.env.VUE_APP_BASE_API;
|
|
7
23
|
const { env } = Uts.getEnv() || {};
|
|
@@ -28,7 +44,7 @@ function remapLocalhostToCurrentHost(url) {
|
|
|
28
44
|
|
|
29
45
|
parsed.hostname = window.location.hostname;
|
|
30
46
|
return parsed.toString();
|
|
31
|
-
} catch
|
|
47
|
+
} catch {
|
|
32
48
|
return normalized;
|
|
33
49
|
}
|
|
34
50
|
}
|
|
@@ -66,6 +82,7 @@ export function createNotifyDedup(fn, ttl = 500) {
|
|
|
66
82
|
* @param {Function} config.onLogout 登出回调
|
|
67
83
|
* @param {Object} config.notify Message-like 对象(如 ElementUI Message),传入后自动应用去重并构建 onNotify
|
|
68
84
|
* @param {Function} config.onNotify 自定义通知回调;若传入 Message-like 对象也可自动识别
|
|
85
|
+
* @param {String[]} config.productionBaseURLs 由应用注入的正式 API 基地址;测试环境命中时告警
|
|
69
86
|
* @returns {AxiosInstance}
|
|
70
87
|
*/
|
|
71
88
|
export function createService(config) {
|
|
@@ -78,11 +95,12 @@ export function createService(config) {
|
|
|
78
95
|
notify,
|
|
79
96
|
onNotify: rawOnNotify,
|
|
80
97
|
getTokenFunc = "getToken",
|
|
81
|
-
internalServiceURLRules = []
|
|
98
|
+
internalServiceURLRules = [],
|
|
99
|
+
productionBaseURLs = []
|
|
82
100
|
} = config || {};
|
|
83
101
|
// 统一在 REST 内部处理 baseURL,避免业务项目重复实现主机替换逻辑。
|
|
84
102
|
const resolvedBaseURL = resolveServiceBaseURL(customBaseURL);
|
|
85
|
-
const isProdAPI =
|
|
103
|
+
const isProdAPI = matchesConfiguredBaseURL(resolvedBaseURL, productionBaseURLs);
|
|
86
104
|
const service = axios.create({ baseURL: resolvedBaseURL });
|
|
87
105
|
|
|
88
106
|
// 统一处理 notify 与 onNotify:
|
|
@@ -165,27 +183,21 @@ export function createService(config) {
|
|
|
165
183
|
(error) => {
|
|
166
184
|
const { response, request } = error || {};
|
|
167
185
|
const { status, data } = response || {};
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
const errorMsg =
|
|
186
|
+
const normalizedData = Uts.transArrayBufferToObj(data);
|
|
187
|
+
const backendErrorMsg = normalizeErrorPayload(normalizedData);
|
|
188
|
+
const errorMsg = backendErrorMsg || `请求失败(${status})`;
|
|
189
|
+
|
|
190
|
+
// 让仍按 data.error 读取错误的旧调用方也能兼容 detail/message/msg 返回。
|
|
191
|
+
if (response && normalizedData !== undefined) {
|
|
192
|
+
response.data = normalizedData;
|
|
193
|
+
}
|
|
171
194
|
|
|
172
195
|
// 添加标识,表示错误已被拦截器处理
|
|
173
196
|
error.handledByInterceptor = true;
|
|
174
197
|
|
|
175
|
-
// HTTP 状态码 → 固定提示语映射(400 直接用后端返回的 errorMsg)
|
|
176
|
-
const STATUS_MESSAGES = {
|
|
177
|
-
400: errorMsg,
|
|
178
|
-
401: "登录信息已过期,请重新登录",
|
|
179
|
-
403: "您没有权限访问,请联系管理员",
|
|
180
|
-
500: errorMsg || "服务器内部错误,请联系开发人员",
|
|
181
|
-
502: "网关错误,请稍后重试或联系开发人员",
|
|
182
|
-
503: "服务暂时不可用,请稍后重试",
|
|
183
|
-
504: "网关超时,请稍后重试或联系开发人员"
|
|
184
|
-
};
|
|
185
|
-
|
|
186
198
|
// 按优先级确定最终提示语,合并为一次 handleNotify 调用
|
|
187
199
|
const notifyMsg =
|
|
188
|
-
|
|
200
|
+
resolveStatusErrorMessage(status, normalizedData) ??
|
|
189
201
|
(response ? errorMsg : null) ??
|
|
190
202
|
(request ? "网络请求失败,请检查网络连接" : null) ??
|
|
191
203
|
(error.message || "请求配置错误");
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token 服务(Vue 2 / localStorage 生态)
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ 本模块是 Vue 2 生态专用(localStorage + cookie + 多 key 支持),
|
|
5
|
+
* 与 @infly/ts-libs 的 createStorageTokenStore(框架无关 Token 抽象)
|
|
6
|
+
* 是并行实现,非代码重复。
|
|
7
|
+
*
|
|
8
|
+
* 新项目(React/非 Vue 生态)请使用 @infly/ts-libs createStorageTokenStore。
|
|
9
|
+
*/
|
|
1
10
|
// TokenService.js
|
|
2
|
-
import Uts from "
|
|
11
|
+
import Uts from "../../../module/Uts.js"; // 假设Uts工具类在同级目录或可访问路径
|
|
3
12
|
|
|
4
13
|
let TOKEN_KEY = "accessToken"; // 默认的token键名
|
|
5
14
|
let CHAT_TOKEN_KEY = "chatAccessToken"; // 默认的聊天token键名
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { exportFile } from "@infly/ts-libs";
|
|
2
|
+
|
|
3
|
+
const fileExportMixin = {
|
|
4
|
+
methods: {
|
|
5
|
+
async $exportFile(config = {}) {
|
|
6
|
+
return exportFile({
|
|
7
|
+
...config,
|
|
8
|
+
message: this.$message || config.message || config.Message || {},
|
|
9
|
+
messageBox:
|
|
10
|
+
this.$msgbox || config.messageBox || config.MessageBox || {},
|
|
11
|
+
loading: this.$loading || config.loading || config.Loading || {},
|
|
12
|
+
request: config.request || this.$axios,
|
|
13
|
+
});
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { fileExportMixin };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
|
|
6
|
+
function resolveProjectRoot({ cwd = process.cwd(), initCwd = process.env.INIT_CWD } = {}) {
|
|
7
|
+
return path.resolve(initCwd || cwd);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isVue2Project(projectRoot = resolveProjectRoot()) {
|
|
11
|
+
return fs.existsSync(path.join(projectRoot, "vue.config.js"));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function initVue2Project(projectRoot = resolveProjectRoot(), dependencies = {}) {
|
|
15
|
+
if (!isVue2Project(projectRoot)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const previousCwd = process.cwd();
|
|
20
|
+
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
if (path.resolve(previousCwd) !== resolvedProjectRoot) {
|
|
24
|
+
process.chdir(resolvedProjectRoot);
|
|
25
|
+
}
|
|
26
|
+
const projectPreview = dependencies.projectPreview || require("./project-preview");
|
|
27
|
+
const buildDist = dependencies.buildDist || require("../../build/build-dist");
|
|
28
|
+
projectPreview.scriptInit();
|
|
29
|
+
buildDist.scriptInit();
|
|
30
|
+
return true;
|
|
31
|
+
} finally {
|
|
32
|
+
if (process.cwd() !== previousCwd) {
|
|
33
|
+
process.chdir(previousCwd);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (require.main === module) {
|
|
39
|
+
initVue2Project();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
initVue2Project,
|
|
44
|
+
isVue2Project,
|
|
45
|
+
resolveProjectRoot,
|
|
46
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const net = require("net");
|
|
4
|
+
const { execSync } = require("child_process");
|
|
5
|
+
|
|
6
|
+
const { scriptWrite } = require("../../tools/file-process.js");
|
|
7
|
+
const { previewList } = require("./script-config.js");
|
|
8
|
+
|
|
9
|
+
const projectRoot = process.cwd().replace(/\\node_modules.*$/g, "");
|
|
10
|
+
const projectPkgPath = path.resolve(projectRoot, "package.json");
|
|
11
|
+
const projectVueConfigPath = path.resolve(projectRoot, "vue.config.js");
|
|
12
|
+
const projectConfig = fs.existsSync(projectVueConfigPath) ? require(projectVueConfigPath) : {};
|
|
13
|
+
const projectPackage = fs.existsSync(projectPkgPath) ? require(projectPkgPath) : {};
|
|
14
|
+
const rawArgv = process.argv.slice(2);
|
|
15
|
+
const args = rawArgv.join(" ");
|
|
16
|
+
|
|
17
|
+
function getBuildContext() {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(process.env.INFLY_BUILD_CONTEXT || "{}");
|
|
20
|
+
} catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const buildContext = getBuildContext();
|
|
26
|
+
|
|
27
|
+
global.logColor = global.logColor || {
|
|
28
|
+
success: "\n\x1b[32m%s\x1b[0m",
|
|
29
|
+
error: "\n\x1b[31m%s\x1b[0m",
|
|
30
|
+
warning: "\n\x1b[33m%s\x1b[0m",
|
|
31
|
+
link: "\x1b[34m%s\x1b[0m",
|
|
32
|
+
info: "\n\x1b[36m%s\x1b[0m"
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const DEFAULT_CONFIG = {
|
|
36
|
+
port: 9001,
|
|
37
|
+
publicPath: "/",
|
|
38
|
+
outputDir: "dist"
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function scriptInit() {
|
|
42
|
+
if (fs.existsSync(projectVueConfigPath)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
scriptWrite(projectPkgPath, previewList);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isPortAvailable(port) {
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
const server = net.createServer();
|
|
51
|
+
|
|
52
|
+
server.listen(port, () => {
|
|
53
|
+
server.once("close", () => {
|
|
54
|
+
resolve(true);
|
|
55
|
+
});
|
|
56
|
+
server.close();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
server.on("error", () => {
|
|
60
|
+
resolve(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function findAvailablePort(startPort, maxTries = 10) {
|
|
66
|
+
for (let i = 0; i < maxTries; i++) {
|
|
67
|
+
const port = startPort + i;
|
|
68
|
+
const available = await isPortAvailable(port);
|
|
69
|
+
|
|
70
|
+
if (available) {
|
|
71
|
+
if (i > 0) {
|
|
72
|
+
console.log(global.logColor.warning, `⚠️ 端口 ${startPort} 被占用,使用端口 ${port}`);
|
|
73
|
+
}
|
|
74
|
+
return port;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
throw new Error(`无法找到可用端口,已尝试从 ${startPort} 到 ${startPort + maxTries - 1}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function init(skipBuild = false) {
|
|
82
|
+
try {
|
|
83
|
+
const connect = require("connect");
|
|
84
|
+
const serveStatic = require("serve-static");
|
|
85
|
+
const app = connect();
|
|
86
|
+
const noBuild = skipBuild || rawArgv.includes("--no-build");
|
|
87
|
+
// Vite 构建:使用 workspace runner 传入的 target 输出目录
|
|
88
|
+
const useViteOutput = buildContext.outputDir && fs.existsSync(buildContext.outputDir);
|
|
89
|
+
const { devServer } = projectConfig || {};
|
|
90
|
+
const { port: devServerPort } = devServer || {};
|
|
91
|
+
projectConfig.port = devServerPort || DEFAULT_CONFIG.port;
|
|
92
|
+
const { publicPath, outputDir, port } = {
|
|
93
|
+
...DEFAULT_CONFIG,
|
|
94
|
+
...projectConfig
|
|
95
|
+
};
|
|
96
|
+
const serveDir = useViteOutput ? buildContext.outputDir : outputDir;
|
|
97
|
+
const baseListenPort = port - 1000;
|
|
98
|
+
|
|
99
|
+
if (!noBuild) {
|
|
100
|
+
buildProject();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const listenPort = await findAvailablePort(baseListenPort);
|
|
104
|
+
|
|
105
|
+
app.use(
|
|
106
|
+
publicPath,
|
|
107
|
+
serveStatic(serveDir, {
|
|
108
|
+
index: ["index.html", "/"]
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const server = app.listen(listenPort, function () {
|
|
113
|
+
const previewUrl = `http://localhost:${listenPort}${publicPath}`;
|
|
114
|
+
const targetSuffix = buildContext.targetLabel ? ` (${buildContext.targetLabel})` : '';
|
|
115
|
+
|
|
116
|
+
if (buildContext.batchPreview === true || process.env.INFLY_BATCH_PREVIEW === "1") {
|
|
117
|
+
console.log(global.logColor.link, `${projectPackage.name || path.basename(projectRoot)}${targetSuffix}: ${previewUrl}`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log(global.logColor.success, `\x1b[42m\x1b[30m DONE \x1b[0m\x1b[32m 项目构建预览,CTRL + C结束:`);
|
|
122
|
+
console.log(global.logColor.link, previewUrl);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
server.on("error", (error) => {
|
|
126
|
+
if (error.code === "EADDRINUSE") {
|
|
127
|
+
console.log(global.logColor.error, `❌ 端口 ${listenPort} 仍然被占用`);
|
|
128
|
+
} else {
|
|
129
|
+
console.log(global.logColor.error, `❌ 服务器启动失败:${error.message}`);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return server;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.log(global.logColor.error, `❌ 预览项目失败:${error.message}`);
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function buildProject() {
|
|
141
|
+
try {
|
|
142
|
+
execSync(`vue-cli-service build ${args}`, { stdio: "inherit" });
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error(global.logColor.error, "❌ 构建失败:", err.message);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = {
|
|
150
|
+
init,
|
|
151
|
+
scriptInit
|
|
152
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
previewList: [
|
|
3
|
+
{
|
|
4
|
+
key: "preview",
|
|
5
|
+
value: "infly-libs --preview"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
key: "preview:no-build",
|
|
9
|
+
value: "infly-libs --preview --no-build"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
buildList: [
|
|
13
|
+
{
|
|
14
|
+
key: "build:prod",
|
|
15
|
+
value: "infly-libs beforeBuild && vue-cli-service build && infly-libs afterBuild"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
key: "build:stage",
|
|
19
|
+
value: "infly-libs beforeBuild && vue-cli-service build --mode staging --no-module && infly-libs afterBuild"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
key: "build:test",
|
|
23
|
+
value: "infly-libs beforeBuild && infly-libs afterBuild"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as user } from "./modules/user";
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
|
|
4
|
+
function findAdminMonorepoRoot(startDir = process.cwd()) {
|
|
5
|
+
let directory = path.resolve(startDir);
|
|
6
|
+
for (;;) {
|
|
7
|
+
if (
|
|
8
|
+
fs.existsSync(path.join(directory, "pnpm-workspace.yaml"))
|
|
9
|
+
&& fs.existsSync(path.join(directory, "configs/vite-configs/registry.js"))
|
|
10
|
+
) return directory;
|
|
11
|
+
const parent = path.dirname(directory);
|
|
12
|
+
if (parent === directory) return null;
|
|
13
|
+
directory = parent;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolveAppScssImport(importPath, importer, appRoot) {
|
|
18
|
+
const cleanImporter = importer.replace(/[?#].*$/, "");
|
|
19
|
+
if (importPath.startsWith("@/")) {
|
|
20
|
+
return path.resolve(appRoot, "src", importPath.slice(2));
|
|
21
|
+
}
|
|
22
|
+
if (importPath.startsWith("./") || importPath.startsWith("../")) {
|
|
23
|
+
return path.resolve(path.dirname(cleanImporter), importPath);
|
|
24
|
+
}
|
|
25
|
+
if (path.isAbsolute(importPath)) return path.resolve(importPath);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function transformScssExportImports(code, importer, options) {
|
|
30
|
+
const { appRoot, readFile = (file) => fs.readFileSync(file, "utf8") } = options;
|
|
31
|
+
return code.replace(
|
|
32
|
+
/import\s+([\w$]+)\s+from\s+(["'])([^"'?]+\.scss)\2\s*;?/g,
|
|
33
|
+
(statement, binding, quote, importPath) => {
|
|
34
|
+
const resolved = resolveAppScssImport(importPath, importer, appRoot);
|
|
35
|
+
if (!resolved) return statement;
|
|
36
|
+
let scss;
|
|
37
|
+
try {
|
|
38
|
+
scss = readFile(resolved);
|
|
39
|
+
} catch (_) {
|
|
40
|
+
return statement;
|
|
41
|
+
}
|
|
42
|
+
if (typeof scss !== "string") return statement;
|
|
43
|
+
const exportBlock = scss.match(/:export\s*\{([\s\S]*?)\}/);
|
|
44
|
+
if (!exportBlock) return statement;
|
|
45
|
+
|
|
46
|
+
const variables = new Map();
|
|
47
|
+
for (const match of scss.matchAll(/^\s*\$([\w-]+)\s*:\s*([^;]+);/gm)) {
|
|
48
|
+
variables.set(match[1], match[2].trim());
|
|
49
|
+
}
|
|
50
|
+
const exported = {};
|
|
51
|
+
for (const match of exportBlock[1].matchAll(/([\w-]+)\s*:\s*([^;]+);/g)) {
|
|
52
|
+
const rawValue = match[2].trim();
|
|
53
|
+
const variableName = rawValue.match(/^\$([\w-]+)$/);
|
|
54
|
+
exported[match[1]] = variableName && variables.has(variableName[1])
|
|
55
|
+
? variables.get(variableName[1])
|
|
56
|
+
: rawValue;
|
|
57
|
+
}
|
|
58
|
+
return `import ${quote}${importPath}${quote};\nconst ${binding} = ${JSON.stringify(exported)};`;
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function createScssExportCompatPlugin(appRoot) {
|
|
64
|
+
return {
|
|
65
|
+
name: "infly-vue2:scss-export-compat",
|
|
66
|
+
enforce: "pre",
|
|
67
|
+
transform(code, id) {
|
|
68
|
+
if (
|
|
69
|
+
id.includes("node_modules")
|
|
70
|
+
|| (!id.includes(".vue") && !/\.[cm]?[jt]sx?(?:[?#].*)?$/.test(id))
|
|
71
|
+
|| !/import\s+[\w$]+\s+from\s+["'][^"']+\.scss["']/.test(code)
|
|
72
|
+
) return null;
|
|
73
|
+
const transformed = transformScssExportImports(code, id, { appRoot });
|
|
74
|
+
return transformed === code ? null : { code: transformed, map: null };
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function addAppRootSassLoadPath(css, appRoot) {
|
|
80
|
+
if (!css.preprocessorOptions) css.preprocessorOptions = {};
|
|
81
|
+
if (!css.preprocessorOptions.scss) css.preprocessorOptions.scss = {};
|
|
82
|
+
const loadPaths = css.preprocessorOptions.scss.loadPaths || [];
|
|
83
|
+
const resolvedAppRoot = path.resolve(appRoot);
|
|
84
|
+
css.preprocessorOptions.scss.loadPaths = loadPaths.includes(resolvedAppRoot)
|
|
85
|
+
? loadPaths
|
|
86
|
+
: [...loadPaths, resolvedAppRoot];
|
|
87
|
+
return css;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = {
|
|
91
|
+
addAppRootSassLoadPath,
|
|
92
|
+
findAdminMonorepoRoot,
|
|
93
|
+
transformScssExportImports,
|
|
94
|
+
createScssExportCompatPlugin,
|
|
95
|
+
};
|