@infly/libs 2.0.49 → 2.0.51

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 介绍
4
4
 
5
- `@infly/libs` 是不受 Vue、React 等前端框架限制的独立 JavaScript 工具库。包根入口无副作用;HTTP、Token、文件处理、Docker/Compose、通用项目命令等能力可被不同技术栈使用。
5
+ `@infly/libs` 是不受 Vue、React 等前端框架限制的独立 JavaScript 工具库。包根入口无副作用;HTTP、Token、并发、workspace、发布计划、Docker/Compose、通用项目命令等能力可被不同技术栈使用。
6
6
 
7
7
  Vue 2 专属的 Permission、Vuex store、实例 mixin、Vue CLI 预览和 webpack 配置集中在 `adapters/vue2/`,避免核心入口依赖 Vue 项目约定。
8
8
 
@@ -17,8 +17,13 @@ admin-monorepo 的子模块独立构建;Monorepo 根构建不会调用该实
17
17
  @infly/libs
18
18
  ├── index.js # 框架无关、无副作用的包根入口
19
19
  ├── module/ # HTTP、Token、格式化等通用模块
20
- ├── tools/ # 文件与项目命令等通用工具
20
+ ├── core/ # 无框架、无工程上下文的纯能力
21
+ │ └── async/concurrency.js # 并发限流
22
+ ├── cli/ # 终端进度、选择与配置驱动命令
23
+ ├── workspace/ # workspace 配置、包发现与依赖图
24
+ ├── release/ # 无副作用的 npm 版本与发布计划
21
25
  ├── build/
26
+ │ ├── project-files.js # 构建生命周期的项目文件处理
22
27
  │ ├── docker/ # Docker/Compose 工具
23
28
  │ └── build-dist/ # 构建产物处理
24
29
  ├── adapters/vue2/
@@ -30,10 +35,13 @@ admin-monorepo 的子模块独立构建;Monorepo 根构建不会调用该实
30
35
  │ ├── vite/ # Monorepo Vite 定位与兼容入口
31
36
  │ ├── webpack5/ # 独立子仓 Webpack 兼容适配器
32
37
  │ └── postinstall.js # 仅 Vue 2 项目执行初始化
33
- ├── bin/cli.js # 命令行入口
34
- └── dataInit/ # 公共枚举
38
+ ├── adapters/webpack/ # require.context 等 Webpack 专属适配
39
+ ├── compat/ # 历史能力与旧语义兼容
40
+ └── bin/cli.js # 命令行入口
35
41
  ```
36
42
 
43
+ 实现按职责进入 `core/`、`cli/`、`workspace/`、`release/`、`build/`、`adapters/` 或 `compat/`。`tools/` 混合目录及其通配导出已移除;新代码必须使用包声明的稳定子路径。`module/`、`script/` 为 ADR-002 时代历史层(薄转发与兼容面),只保留既有应用引用,新代码不向其添加实现。
44
+
37
45
  Vue2 文件导出适配:
38
46
 
39
47
  ```javascript
@@ -129,7 +137,7 @@ infly-libs release:docker --local --health-check --health-timeout=60
129
137
 
130
138
  ## HTTP 环境保护配置
131
139
 
132
- `module/REST.js` 不内置任何项目域名。应用如需在测试环境误连正式接口时告警,应由应用显式注入完整的正式 API 基地址:
140
+ `adapters/vue2/module/REST.js` 不内置任何项目域名。应用如需在测试环境误连正式接口时告警,应由应用显式注入完整的正式 API 基地址:
133
141
 
134
142
  ```javascript
135
143
  const service = createService({
@@ -141,6 +149,38 @@ const service = createService({
141
149
 
142
150
  ## 配置驱动的项目命令
143
151
 
152
+ 根仓或其他构建编排器需要使用通用构建原语时,只消费稳定入口 `@infly/libs/build`:
153
+
154
+ ```javascript
155
+ const {
156
+ createBuildContext,
157
+ run,
158
+ updateAllSources,
159
+ postBatchWebhook,
160
+ startBatchPreviews,
161
+ } = require("@infly/libs/build");
162
+ ```
163
+
164
+ `build/build-utils/*` 是包内实现目录,不作为新的跨仓调用入口。应用选择、产品线、环境和构建顺序仍由调用方负责;该入口不改变 `infly-libs beforeBuild`、`afterBuild` 的现有行为。
165
+
166
+ 通用 workspace 与 Git 原语通过稳定子路径使用:
167
+
168
+ ```javascript
169
+ const { runWithConcurrency } = require("@infly/libs/concurrency");
170
+ const { createProgressReporter } = require("@infly/libs/progress");
171
+ const { getWorkspacePackages } = require("@infly/libs/workspace-config");
172
+ const { configureGitAliases } = require("@infly/libs/git/configure");
173
+ const { inspectSubmodule } = require("@infly/libs/git/submodule-status");
174
+ const { normalizeRelativePath, parseGitmodulesPaths } = require("@infly/libs/git/submodules");
175
+ const {
176
+ discoverWorkspacePackages,
177
+ selectAffectedWorkspacePackages,
178
+ } = require("@infly/libs/workspace-packages");
179
+ const { buildReleasePlan, bumpPatch } = require("@infly/libs/npm-release");
180
+ ```
181
+
182
+ 这些入口只提供无业务含义的并发、进度、workspace 配置、包发现/依赖图、稳定版本发布计划和 Git 路径能力。`selectAffectedWorkspacePackages` 的全局变更判断由调用方注入;`buildReleasePlan` 只计算版本计划,不登录 npm、不访问 Registry、不写清单,也不提交或推送 Git。产品线解析、应用清单、切换锁、依赖安装指纹、gitlink 工作树治理和发布顺序仍由根仓负责。根仓初始化在 `node_modules` 尚未可用时需要运行,因此 `scripts/workspace` 中允许保留经过合同测试的无依赖自举实现。
183
+
144
184
  `project` 命令将多平台项目的开发和构建目标从 package scripts 移到项目配置:
145
185
 
146
186
  ```bash
@@ -1,152 +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
- };
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("../../build/project-files.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
+ };
@@ -1,55 +1,56 @@
1
- /**
2
- * 自动导出目录下所有文件
3
- * @param {Function} requireContext - require.context 返回的函数
4
- * @returns {Object} 导出对象,key 为文件名(不含扩展名),value 为模块内容
5
- *
6
- * @example
7
- * // 在 components/Dialog/index.js 中使用
8
- * import { autoExportFiles } from '@infly/libs/tools/auto-export.js';
9
- * const components = autoExportFiles(require.context('./', false, /\.vue$/));
10
- * Object.keys(components).forEach(name => {
11
- * module.exports[name] = components[name];
12
- * });
13
- *
14
- * // 或者直接一行
15
- * Object.assign(module.exports, autoExportFiles(require.context('./', false, /\.vue$/)));
16
- */
17
- function autoExportFiles(requireContext) {
18
- if (!requireContext || typeof requireContext !== 'function') {
19
- console.error('autoExportFiles: requireContext 必须是 require.context 返回的函数');
20
- return {};
21
- }
22
-
23
- const exports = {};
24
-
25
- requireContext.keys().forEach(fileName => {
26
- // 跳过 index 文件自身
27
- if (fileName.includes('index')) return;
28
-
29
- const componentConfig = requireContext(fileName);
30
- const componentName = fileName
31
- .replace(/^\.\//, '') // 移除 './'
32
- .replace(/\.\w+$/, ''); // 移除扩展名
33
-
34
- exports[componentName] = componentConfig.default || componentConfig;
35
- });
36
-
37
- return exports;
38
- }
39
-
40
- /**
41
- * 简化版:直接挂载到 module.exports
42
- * @param {Function} requireContext - require.context 返回的函数
43
- * @param {Object} targetExports - 目标导出对象(通常是 module.exports)
44
- *
45
- * @example
46
- * // 在 components/Dialog/index.js 中使用
47
- * import { autoExportToModule } from '@infly/libs/tools/auto-export.js';
48
- * autoExportToModule(require.context('./', false, /\.vue$/), module.exports);
49
- */
50
- function autoExportToModule(requireContext, targetExports) {
51
- const files = autoExportFiles(requireContext);
52
- Object.assign(targetExports, files);
53
- }
54
-
55
- export { autoExportFiles, autoExportToModule };
1
+ /**
2
+ * Webpack require.context 适配器。
3
+ * 自动导出目录下所有文件
4
+ * @param {Function} requireContext - require.context 返回的函数
5
+ * @returns {Object} 导出对象,key 为文件名(不含扩展名),value 为模块内容
6
+ *
7
+ * @example
8
+ * // components/Dialog/index.js 中使用
9
+ * import { autoExportFiles } from '@infly/libs/adapters/webpack/auto-export';
10
+ * const components = autoExportFiles(require.context('./', false, /\.vue$/));
11
+ * Object.keys(components).forEach(name => {
12
+ * module.exports[name] = components[name];
13
+ * });
14
+ *
15
+ * // 或者直接一行
16
+ * Object.assign(module.exports, autoExportFiles(require.context('./', false, /\.vue$/)));
17
+ */
18
+ function autoExportFiles(requireContext) {
19
+ if (!requireContext || typeof requireContext !== 'function') {
20
+ console.error('autoExportFiles: requireContext 必须是 require.context 返回的函数');
21
+ return {};
22
+ }
23
+
24
+ const exports = {};
25
+
26
+ requireContext.keys().forEach(fileName => {
27
+ // 跳过 index 文件自身
28
+ if (fileName.includes('index')) return;
29
+
30
+ const componentConfig = requireContext(fileName);
31
+ const componentName = fileName
32
+ .replace(/^\.\//, '') // 移除 './'
33
+ .replace(/\.\w+$/, ''); // 移除扩展名
34
+
35
+ exports[componentName] = componentConfig.default || componentConfig;
36
+ });
37
+
38
+ return exports;
39
+ }
40
+
41
+ /**
42
+ * 简化版:直接挂载到 module.exports
43
+ * @param {Function} requireContext - require.context 返回的函数
44
+ * @param {Object} targetExports - 目标导出对象(通常是 module.exports)
45
+ *
46
+ * @example
47
+ * // components/Dialog/index.js 中使用
48
+ * import { autoExportToModule } from '@infly/libs/adapters/webpack/auto-export';
49
+ * autoExportToModule(require.context('./', false, /\.vue$/), module.exports);
50
+ */
51
+ function autoExportToModule(requireContext, targetExports) {
52
+ const files = autoExportFiles(requireContext);
53
+ Object.assign(targetExports, files);
54
+ }
55
+
56
+ export { autoExportFiles, autoExportToModule };
package/bin/cli.js CHANGED
@@ -125,7 +125,7 @@ async function main(argv = process.argv.slice(2)) {
125
125
  project: () => {
126
126
  const action = argv[1] && !argv[1].startsWith("--") ? argv[1] : undefined;
127
127
  const opts = parseProjectArgs(argv.slice(action ? 2 : 1));
128
- return require("../tools/project-command").runProjectCommand({ ...opts, action });
128
+ return require("../cli/project-command").runProjectCommand({ ...opts, action });
129
129
  },
130
130
  };
131
131