@infly/libs 2.0.44 → 2.0.46

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 (43) hide show
  1. package/README.md +17 -5
  2. package/adapters/vue2/module/Permission.js +1 -1
  3. package/{module → adapters/vue2/module}/REST.js +3 -3
  4. package/{module → adapters/vue2/module}/TokenService.js +1 -1
  5. package/adapters/vue2/module/file-export.js +18 -0
  6. package/adapters/vue2/project-preview.js +6 -2
  7. package/adapters/vue2/script-config.js +1 -1
  8. package/adapters/vue2/store/modules/user.js +1 -1
  9. package/adapters/vue2/vite/index.js +95 -0
  10. package/adapters/vue2/vite/jest.config.js +11 -0
  11. package/adapters/vue2/{build/webpack5 → webpack5}/webpack.base.js +0 -55
  12. package/build/build-dist/index.js +2 -2
  13. package/{script/build → build/build-utils}/command.js +13 -4
  14. package/{script/build → build/build-utils}/git.js +6 -4
  15. package/{script/build → build/build-utils}/preview.js +4 -1
  16. package/module/Uts.js +47 -443
  17. package/package.json +18 -15
  18. package/script/git-automation/git-configure.js +29 -0
  19. package/script/git-automation/index.js +0 -14
  20. package/script/git-automation/submodule-status.js +113 -0
  21. package/script/git-automation/submodule-utils.js +18 -0
  22. package/tools/concurrency.js +30 -0
  23. package/tools/progress.js +63 -0
  24. package/tools/project-command.js +5 -1
  25. package/tools/workspace-config.js +145 -0
  26. package/adapters/vue2/build/webpack5/remove-legacy-assets-plugin.js +0 -84
  27. package/build/webpack5/inline-runtime-plugin.js +0 -1
  28. package/build/webpack5/remove-legacy-assets-plugin.js +0 -1
  29. package/build/webpack5/webpack.base.js +0 -1
  30. package/module/Permission.js +0 -1
  31. package/module/cjs/deep-merge.cjs +0 -38
  32. package/store/index.js +0 -1
  33. package/store/modules/user.js +0 -1
  34. package/tools/file-export.js +0 -167
  35. /package/{module → adapters/vue2/module}/Router.js +0 -0
  36. /package/adapters/vue2/{build/webpack5 → webpack5}/inline-runtime-plugin.js +0 -0
  37. /package/{script/build → build/build-utils}/deps.js +0 -0
  38. /package/{script/build → build/build-utils}/env.js +0 -0
  39. /package/{script/webhook/webhook.js → build/build-utils/webhook-single.js} +0 -0
  40. /package/{script/build → build/build-utils}/webhook.js +0 -0
  41. /package/module/cjs/{page-config.cjs → page-config.js} +0 -0
  42. /package/module/cjs/{request-url-rules.cjs → request-url-rules.js} +0 -0
  43. /package/module/cjs/{rest-error-rules.cjs → rest-error-rules.js} +0 -0
@@ -0,0 +1,113 @@
1
+ const path = require("node:path");
2
+ const { spawnSync } = require("node:child_process");
3
+
4
+ /**
5
+ * 在指定目录执行 git 命令,自动处理 safe.directory
6
+ */
7
+ function runGit(cwd, args, { allowFailure = false } = {}) {
8
+ const absoluteCwd = path.resolve(cwd);
9
+ const safeDirectory = absoluteCwd.replaceAll("\\", "/");
10
+ const result = spawnSync(
11
+ "git",
12
+ ["-c", `safe.directory=${safeDirectory}`, "-C", absoluteCwd, ...args],
13
+ { encoding: "utf8", windowsHide: true },
14
+ );
15
+
16
+ if (result.status !== 0 && !allowFailure) {
17
+ const detail = (result.stderr || result.stdout || "Git command failed").trim();
18
+ throw new Error(detail);
19
+ }
20
+ if (result.status !== 0 && allowFailure) {
21
+ const detail = String(result.stderr || result.stdout || "").trim();
22
+ if (detail) {
23
+ process.stderr.write(`[submodule:status] git ${args.join(" ")}: ${detail}\n`);
24
+ }
25
+ }
26
+
27
+ return result.status === 0 ? result.stdout.trim() : "";
28
+ }
29
+
30
+ /**
31
+ * 从 .gitmodules 中读取所有已配置的子模块(名称、路径、配置分支)
32
+ */
33
+ function getConfiguredSubmodules(rootDir) {
34
+ const output = runGit(rootDir, [
35
+ "config",
36
+ "--file",
37
+ ".gitmodules",
38
+ "--get-regexp",
39
+ "^submodule\\..*\\.path$",
40
+ ]);
41
+
42
+ return output.split(/\r?\n/).filter(Boolean).map((line) => {
43
+ const separator = line.search(/\s/);
44
+ const key = line.slice(0, separator);
45
+ const submodulePath = line.slice(separator).trim();
46
+ const name = key.slice("submodule.".length, -".path".length);
47
+ const configuredBranch = runGit(
48
+ rootDir,
49
+ ["config", "--file", ".gitmodules", "--get", `submodule.${name}.branch`],
50
+ { allowFailure: true },
51
+ );
52
+
53
+ return { name, path: submodulePath, configuredBranch };
54
+ });
55
+ }
56
+
57
+ /**
58
+ * 获取主仓 HEAD 中记录的某个子模块指针 commit
59
+ */
60
+ function getParentPointer(rootDir, submodulePath) {
61
+ const treeEntry = runGit(rootDir, ["ls-tree", "HEAD", "--", submodulePath]);
62
+ return treeEntry ? treeEntry.split(/\s+/)[2] : "";
63
+ }
64
+
65
+ /**
66
+ * 检查单个子模块的状态:当前分支、HEAD、指针漂移、远程漂移、是否 dirty
67
+ */
68
+ function inspectSubmodule(rootDir, submodule) {
69
+ const worktree = path.join(rootDir, submodule.path);
70
+ const pointer = getParentPointer(rootDir, submodule.path);
71
+ const head = runGit(worktree, ["rev-parse", "HEAD"], { allowFailure: true });
72
+
73
+ if (!head) {
74
+ return {
75
+ path: submodule.path,
76
+ branch: "-",
77
+ head: "-",
78
+ pointer: pointer ? pointer.slice(0, 8) : "-",
79
+ originMaster: "-",
80
+ state: "not-initialized",
81
+ };
82
+ }
83
+
84
+ const branch = runGit(worktree, ["branch", "--show-current"], { allowFailure: true });
85
+ const remoteBranch = submodule.configuredBranch || branch || "master";
86
+ const originHead = runGit(
87
+ worktree,
88
+ ["rev-parse", "--verify", `origin/${remoteBranch}`],
89
+ { allowFailure: true },
90
+ );
91
+ const dirty = Boolean(runGit(worktree, ["status", "--porcelain"], { allowFailure: true }));
92
+ const states = [];
93
+
94
+ if (dirty) states.push("dirty");
95
+ if (pointer && head !== pointer) states.push("pointer-drift");
96
+ if (originHead && head !== originHead) states.push("remote-drift");
97
+
98
+ return {
99
+ path: submodule.path,
100
+ branch: branch || "DETACHED",
101
+ head: head.slice(0, 8),
102
+ pointer: pointer ? pointer.slice(0, 8) : "-",
103
+ originMaster: originHead ? originHead.slice(0, 8) : "-",
104
+ state: states.length > 0 ? states.join(",") : "ok",
105
+ };
106
+ }
107
+
108
+ module.exports = {
109
+ getConfiguredSubmodules,
110
+ getParentPointer,
111
+ inspectSubmodule,
112
+ runGit,
113
+ };
@@ -0,0 +1,18 @@
1
+ const fs = require("fs");
2
+
3
+ /**
4
+ * 从 .gitmodules 中解析所有子模块路径
5
+ */
6
+ function parseGitmodulesPaths(rootDir) {
7
+ const p = require("path").join(rootDir, ".gitmodules");
8
+ if (!fs.existsSync(p)) return [];
9
+ const content = fs.readFileSync(p, "utf8");
10
+ const paths = [];
11
+ content.split(/\[submodule/).forEach((block) => {
12
+ const m = block.match(/path\s*=\s*(.+)/);
13
+ if (m) paths.push(m[1].trim());
14
+ });
15
+ return paths;
16
+ }
17
+
18
+ module.exports = { parseGitmodulesPaths };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 异步并发限流器。
3
+ * 对 items 数组中的每一项执行 worker,同时最多运行 concurrency 个 worker。
4
+ *
5
+ * @param {Array} items - 待处理项
6
+ * @param {number} concurrency - 最大并发数
7
+ * @param {Function} worker - 处理函数 (item, index) => Promise
8
+ * @returns {Promise<Array>} 结果数组,索引与 items 一一对应
9
+ */
10
+ async function runWithConcurrency(items, concurrency, worker) {
11
+ if (!Number.isInteger(concurrency) || concurrency < 1) {
12
+ throw new TypeError("concurrency must be a positive integer");
13
+ }
14
+
15
+ const results = new Array(items.length);
16
+ let nextIndex = 0;
17
+ const runWorker = async () => {
18
+ while (nextIndex < items.length) {
19
+ const index = nextIndex;
20
+ nextIndex += 1;
21
+ results[index] = await worker(items[index], index);
22
+ }
23
+ };
24
+
25
+ const workerCount = Math.min(concurrency, items.length);
26
+ await Promise.all(Array.from({ length: workerCount }, runWorker));
27
+ return results;
28
+ }
29
+
30
+ module.exports = { runWithConcurrency };
@@ -0,0 +1,63 @@
1
+ function formatProgressLine({ label, completed, total, path = "" }) {
2
+ const safeTotal = Math.max(total, 1);
3
+ const percentage = total === 0 ? 100 : Math.round((completed / safeTotal) * 100);
4
+ const filled = Math.round(percentage / 5);
5
+ const bar = `${"█".repeat(filled)}${"░".repeat(20 - filled)}`;
6
+ return `${label} [${bar}] ${percentage}% (${completed}/${total})${path ? ` ${path}` : ""}`;
7
+ }
8
+
9
+ function createProgressReporter(options = {}) {
10
+ const stream = options.stream || process.stdout;
11
+ const quiet = Boolean(options.quiet);
12
+ const defaultLabels = {
13
+ status: "检查状态",
14
+ deinit: "停用模块",
15
+ init: "模块加载",
16
+ };
17
+ const labels = { ...defaultLabels, ...options.labels };
18
+ return (event) => {
19
+ if (quiet || !labels[event.phase] || event.total === 0) return;
20
+ const line = formatProgressLine({
21
+ ...event,
22
+ label: labels[event.phase],
23
+ });
24
+ if (stream.isTTY) {
25
+ stream.write(`\r${line}\x1b[K${event.done || event.completed >= event.total ? "\n" : ""}`);
26
+ return;
27
+ }
28
+ if (event.completed === 0 || event.done || event.completed >= event.total) stream.write(`${line}\n`);
29
+ };
30
+ }
31
+
32
+ function formatSwitchSummary(name, totalMs, dependencyResult, timings) {
33
+ const seconds = (totalMs / 1000).toFixed(1);
34
+ const dependencyLabel = dependencyResult.installed ? ",依赖已更新" : "";
35
+ const summary = `\n✅ ${name}(${seconds}s${dependencyLabel})`;
36
+ if (!timings) return summary;
37
+ const labels = {
38
+ analyze: "分析",
39
+ precheck: "预检",
40
+ status: "状态",
41
+ deinit: "停用",
42
+ init: "加载",
43
+ install: "依赖",
44
+ };
45
+ const phases = Object.entries(labels)
46
+ .filter(([phase]) => Number.isFinite(timings[phase]))
47
+ .map(([phase, label]) => `${label} ${(timings[phase] / 1000).toFixed(1)}s`);
48
+ return phases.length > 0 ? `${summary}\n 阶段: ${phases.join(" | ")}` : summary;
49
+ }
50
+
51
+ function formatSlowStatusDetails(details, thresholdMs = 2000) {
52
+ return details
53
+ .filter((detail) => detail.durationMs > thresholdMs)
54
+ .sort((left, right) => right.durationMs - left.durationMs)
55
+ .map((detail) => `${detail.path} ${detail.durationMs}ms`);
56
+ }
57
+
58
+ module.exports = {
59
+ createProgressReporter,
60
+ formatProgressLine,
61
+ formatSlowStatusDetails,
62
+ formatSwitchSummary,
63
+ };
@@ -170,9 +170,13 @@ async function runProjectCommand(options = {}, dependencies = {}) {
170
170
  for (const platform of platforms) {
171
171
  const env = { ...process.env, VUE_APP_PLATFORM: platform };
172
172
  const lifecycleArgs = environmentConfig.mode ? ["--mode", environmentConfig.mode] : [];
173
+ // staging 构建禁用 Vue CLI 5 默认的 modern mode 双构建(测试环境无需 legacy 兼容)
174
+ const buildArgs = environment === "stage"
175
+ ? [...(environmentConfig.args || []), "--no-module"]
176
+ : (environmentConfig.args || []);
173
177
  const steps = [
174
178
  { command: buildConfig.before, extraArgs: lifecycleArgs },
175
- { command: buildConfig.command, extraArgs: environmentConfig.args || [] },
179
+ { command: buildConfig.command, extraArgs: buildArgs },
176
180
  { command: buildConfig.after, extraArgs: lifecycleArgs },
177
181
  ];
178
182
 
@@ -0,0 +1,145 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const YAML = require("yaml");
4
+
5
+ function readJson(filePath) {
6
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
7
+ }
8
+
9
+ function readWorkspaceList(content, sectionName) {
10
+ let workspaceConfig;
11
+
12
+ try {
13
+ workspaceConfig = YAML.parse(content);
14
+ } catch (error) {
15
+ throw new Error(`无法解析 pnpm-workspace.yaml: ${error.message}`, { cause: error });
16
+ }
17
+
18
+ if (workspaceConfig == null) {
19
+ return [];
20
+ }
21
+
22
+ if (typeof workspaceConfig !== "object" || Array.isArray(workspaceConfig)) {
23
+ throw new TypeError("pnpm-workspace.yaml 的根节点必须是对象");
24
+ }
25
+
26
+ const patterns = workspaceConfig[sectionName];
27
+
28
+ if (patterns == null) {
29
+ return [];
30
+ }
31
+
32
+ if (
33
+ !Array.isArray(patterns)
34
+ || patterns.some((pattern) => typeof pattern !== "string" || pattern.trim().length === 0)
35
+ ) {
36
+ throw new TypeError(`pnpm-workspace.yaml 的 ${sectionName} 必须是非空字符串数组`);
37
+ }
38
+
39
+ return patterns;
40
+ }
41
+
42
+ function readWorkspaceSection(workspaceFile, sectionName) {
43
+ return readWorkspaceList(fs.readFileSync(workspaceFile, "utf8"), sectionName);
44
+ }
45
+
46
+ function readWorkspaceSectionWithFallback(workspaceFile, sectionName, fallbackSectionName) {
47
+ const patterns = readWorkspaceSection(workspaceFile, sectionName);
48
+
49
+ if (patterns.length > 0) {
50
+ return patterns;
51
+ }
52
+
53
+ return readWorkspaceSection(workspaceFile, fallbackSectionName);
54
+ }
55
+
56
+ function expandWorkspacePattern(rootDir, pattern) {
57
+ const normalizedPattern = pattern.replace(/\\/g, "/");
58
+
59
+ if (!normalizedPattern.includes("*")) {
60
+ return [path.join(rootDir, normalizedPattern)];
61
+ }
62
+
63
+ const starIndex = normalizedPattern.indexOf("*");
64
+ const basePart = normalizedPattern.slice(0, starIndex);
65
+ const suffixPart = normalizedPattern.slice(starIndex + 1);
66
+ const baseDir = path.join(rootDir, basePart);
67
+
68
+ if (!fs.existsSync(baseDir)) {
69
+ return [];
70
+ }
71
+
72
+ const suffixIsNamePattern = suffixPart && !suffixPart.startsWith("/");
73
+
74
+ return fs
75
+ .readdirSync(baseDir, { withFileTypes: true })
76
+ .filter((entry) => entry.isDirectory())
77
+ .filter((entry) => !suffixIsNamePattern || entry.name.endsWith(suffixPart))
78
+ .map((entry) => (suffixIsNamePattern
79
+ ? path.join(baseDir, entry.name)
80
+ : path.join(baseDir, entry.name, suffixPart)))
81
+ .filter((packageDir) => fs.existsSync(path.join(packageDir, "package.json")));
82
+ }
83
+
84
+ function getWorkspacePackages(rootDir, workspaceFile, sectionName, fallbackSectionName, scriptName) {
85
+ const patterns = fallbackSectionName
86
+ ? readWorkspaceSectionWithFallback(workspaceFile, sectionName, fallbackSectionName)
87
+ : readWorkspaceSection(workspaceFile, sectionName);
88
+ const included = new Set();
89
+ patterns.forEach((pattern) => {
90
+ const excluded = pattern.startsWith("!");
91
+ const resolvedPattern = excluded ? pattern.slice(1) : pattern;
92
+ expandWorkspacePattern(rootDir, resolvedPattern).forEach((packageDir) => {
93
+ const resolvedDir = path.resolve(packageDir);
94
+ if (excluded) included.delete(resolvedDir);
95
+ else included.add(resolvedDir);
96
+ });
97
+ });
98
+ const packageDirs = [...included];
99
+ const seen = new Set();
100
+
101
+ return packageDirs
102
+ .map((packageDir) => {
103
+ const packageJsonPath = path.join(packageDir, "package.json");
104
+
105
+ if (!fs.existsSync(packageJsonPath)) {
106
+ return null;
107
+ }
108
+
109
+ const packageJson = readJson(packageJsonPath);
110
+ const scripts = packageJson.scripts || {};
111
+
112
+ if (!packageJson.name || seen.has(packageJson.name) || (scriptName && !scripts[scriptName])) {
113
+ return null;
114
+ }
115
+
116
+ seen.add(packageJson.name);
117
+
118
+ return {
119
+ name: packageJson.name,
120
+ dir: path.relative(rootDir, packageDir),
121
+ packageJson,
122
+ };
123
+ })
124
+ .filter(Boolean);
125
+ }
126
+
127
+ function toTurboFilter(value) {
128
+ const normalized = value.replace(/\\/g, "/");
129
+
130
+ if (normalized.includes("/") && !normalized.startsWith("./") && !normalized.startsWith("../")) {
131
+ return `./${normalized}`;
132
+ }
133
+
134
+ return normalized;
135
+ }
136
+
137
+ module.exports = {
138
+ expandWorkspacePattern,
139
+ getWorkspacePackages,
140
+ readJson,
141
+ readWorkspaceList,
142
+ readWorkspaceSection,
143
+ readWorkspaceSectionWithFallback,
144
+ toTurboFilter,
145
+ };
@@ -1,84 +0,0 @@
1
- /**
2
- * RemoveLegacyAssetsPlugin
3
- * 用于处理 Vue CLI Modern Mode 的 legacy 文件问题
4
- *
5
- * 功能:
6
- * 1. 在构建前创建占位文件,防止 ModernModePlugin 读取时报错
7
- * 2. 在构建时删除 legacy 相关的资源
8
- * 3. 在构建完成后清理占位文件
9
- */
10
-
11
- "use strict";
12
-
13
- const fs = require('fs');
14
- const path = require('path');
15
-
16
- class RemoveLegacyAssetsPlugin {
17
- constructor(options = {}) {
18
- this.options = {
19
- legacyFiles: [
20
- 'legacy-assets-index.html.json',
21
- 'modern-assets-index.html.json'
22
- ],
23
- ...options
24
- };
25
- }
26
-
27
- apply(compiler) {
28
- const outputPath = compiler.options.output.path;
29
- const { legacyFiles } = this.options;
30
-
31
- // 🔥 Hook 1: 构建开始前创建占位文件
32
- compiler.hooks.beforeRun.tapAsync('RemoveLegacyAssetsPlugin', (compiler, callback) => {
33
- // 确保输出目录存在
34
- if (!fs.existsSync(outputPath)) {
35
- fs.mkdirSync(outputPath, { recursive: true });
36
- }
37
-
38
- // 创建空数组的 JSON 文件(Vue CLI 期望数组格式)
39
- legacyFiles.forEach(file => {
40
- const filePath = path.join(outputPath, file);
41
- try {
42
- fs.writeFileSync(filePath, '[]');
43
- } catch (err) {
44
- // 忽略创建失败的错误
45
- console.warn(`⚠ Failed to create placeholder: ${file}`, err.message);
46
- }
47
- });
48
-
49
- callback();
50
- });
51
-
52
- // 🔥 Hook 2: 构建时删除编译产物中的 legacy 资源
53
- compiler.hooks.emit.tapAsync('RemoveLegacyAssetsPlugin', (compilation, callback) => {
54
- Object.keys(compilation.assets).forEach(filename => {
55
- if (filename.includes('-legacy') || filename.includes('legacy-assets')) {
56
- delete compilation.assets[filename];
57
- }
58
- });
59
- callback();
60
- });
61
-
62
- // 🔥 Hook 3: 构建完成后清理占位文件
63
- compiler.hooks.done.tapAsync('RemoveLegacyAssetsPlugin', (stats, callback) => {
64
- // 延迟执行,确保所有读取操作完成
65
- setTimeout(() => {
66
- legacyFiles.forEach(file => {
67
- const filePath = path.join(outputPath, file);
68
- try {
69
- if (fs.existsSync(filePath)) {
70
- fs.unlinkSync(filePath);
71
- console.log(`\x1b[32m✓ Cleaned up: ${file}\x1b[0m`);
72
- }
73
- } catch (err) {
74
- // 忽略删除失败的错误
75
- console.warn(`⚠ Failed to cleanup: ${file}`, err.message);
76
- }
77
- });
78
- callback();
79
- }, 100);
80
- });
81
- }
82
- }
83
-
84
- module.exports = RemoveLegacyAssetsPlugin;
@@ -1 +0,0 @@
1
- module.exports = require("../../adapters/vue2/build/webpack5/inline-runtime-plugin.js");
@@ -1 +0,0 @@
1
- module.exports = require("../../adapters/vue2/build/webpack5/remove-legacy-assets-plugin.js");
@@ -1 +0,0 @@
1
- module.exports = require("../../adapters/vue2/build/webpack5/webpack.base.js");
@@ -1 +0,0 @@
1
- export * from "../adapters/vue2/module/Permission.js";
@@ -1,38 +0,0 @@
1
- /**
2
- * 深度合并对象
3
- * @param {Object} target - 目标对象
4
- * @param {Object} source - 源对象
5
- * @param {boolean} overwrite - 是否覆盖已存在的属性,默认 false
6
- * @returns {Object} 合并后的目标对象
7
- */
8
- function deepMerge(target, source, overwrite = false) {
9
- if (!source || typeof source !== "object") return target;
10
- if (!target || typeof target !== "object") return source;
11
-
12
- Object.keys(source).forEach((key) => {
13
- const sourceValue = source[key];
14
- const targetValue = target[key];
15
-
16
- if (Array.isArray(sourceValue)) {
17
- if (!overwrite && targetValue !== undefined) {
18
- return;
19
- }
20
- target[key] = [...sourceValue];
21
- } else if (sourceValue && typeof sourceValue === "object") {
22
- if (!target[key] || typeof target[key] !== "object") {
23
- target[key] = {};
24
- }
25
- deepMerge(target[key], sourceValue, overwrite);
26
- } else if (!overwrite && targetValue !== undefined) {
27
- return;
28
- } else {
29
- target[key] = sourceValue;
30
- }
31
- });
32
-
33
- return target;
34
- }
35
-
36
- module.exports = {
37
- deepMerge
38
- };
package/store/index.js DELETED
@@ -1 +0,0 @@
1
- module.exports = require("../adapters/vue2/store/index.js");
@@ -1 +0,0 @@
1
- export { default } from "../../adapters/vue2/store/modules/user.js";