@next-core/build-next-bricks 1.25.1 → 1.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/scanBricks.js +238 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@next-core/build-next-bricks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
4
4
|
"description": "Build next bricks",
|
|
5
5
|
"homepage": "https://github.com/easyops-cn/next-core/tree/v3/packages/build-next-bricks",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"@next-shared/common-bricks": "*"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "570ac23f6fac2cfeac85b36a8d811251bc6e20df"
|
|
65
65
|
}
|
package/src/scanBricks.js
CHANGED
|
@@ -37,6 +37,117 @@ const validCustomElementName = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;
|
|
|
37
37
|
const validProcessorName = /^[a-z][a-zA-Z0-9]*\.[a-z][a-zA-Z0-9]*$/;
|
|
38
38
|
const validExposeName = /^[-\w]+$/;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* 读取外部包的构件依赖清单(支持选择性导入)
|
|
42
|
+
* @param {string} packageName - 包名,如 "@shared/common-helpers"
|
|
43
|
+
* @param {string[]} importedMembers - 导入的成员,如 ["renderButton", "renderModal"]
|
|
44
|
+
* @param {boolean} isNamespaceImport - 是否为命名空间导入(import * as)
|
|
45
|
+
* @param {string} packageDir - 当前 bricks 包的根目录
|
|
46
|
+
* @returns {string[] | null} 构件依赖列表,失败时返回 null
|
|
47
|
+
*/
|
|
48
|
+
function loadExternalDependencies(
|
|
49
|
+
packageName,
|
|
50
|
+
importedMembers,
|
|
51
|
+
isNamespaceImport,
|
|
52
|
+
packageDir
|
|
53
|
+
) {
|
|
54
|
+
try {
|
|
55
|
+
// 解析包的位置
|
|
56
|
+
const packageJsonPath = path.resolve(
|
|
57
|
+
packageDir,
|
|
58
|
+
"node_modules",
|
|
59
|
+
packageName,
|
|
60
|
+
"package.json"
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (!existsSync(packageJsonPath)) {
|
|
64
|
+
console.warn(
|
|
65
|
+
`警告: 未找到 ${packageName} 的 package.json,` + `请确保该包已安装`
|
|
66
|
+
);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const packageRoot = path.dirname(packageJsonPath);
|
|
71
|
+
|
|
72
|
+
// 读取依赖清单文件
|
|
73
|
+
const manifestPath = path.join(
|
|
74
|
+
packageRoot,
|
|
75
|
+
"dist",
|
|
76
|
+
"brick-dependencies.json"
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (!existsSync(manifestPath)) {
|
|
80
|
+
console.warn(
|
|
81
|
+
`警告: 未找到 ${packageName} 的依赖清单文件,` +
|
|
82
|
+
`请先构建该包或确保其已包含 brick-dependencies.json`
|
|
83
|
+
);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
88
|
+
|
|
89
|
+
// 验证清单格式
|
|
90
|
+
if (!Array.isArray(manifest.brickDependencies)) {
|
|
91
|
+
console.warn(`警告: ${packageName} 的依赖清单格式无效`);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 如果是命名空间导入(import * as),返回全量依赖
|
|
96
|
+
if (isNamespaceImport) {
|
|
97
|
+
console.log(
|
|
98
|
+
`✓ 加载 ${packageName} 的全量构件依赖: ${manifest.brickDependencies.length} 个(命名空间导入)`
|
|
99
|
+
);
|
|
100
|
+
return manifest.brickDependencies;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 如果支持按导出成员分组,且有导入成员信息,进行选择性加载
|
|
104
|
+
if (
|
|
105
|
+
manifest.brickDependenciesByExport &&
|
|
106
|
+
importedMembers &&
|
|
107
|
+
importedMembers.length > 0
|
|
108
|
+
) {
|
|
109
|
+
const selectedDeps = new Set();
|
|
110
|
+
let hasUnmappedMember = false;
|
|
111
|
+
|
|
112
|
+
for (const member of importedMembers) {
|
|
113
|
+
const memberDeps = manifest.brickDependenciesByExport[member];
|
|
114
|
+
if (memberDeps) {
|
|
115
|
+
memberDeps.forEach((dep) => selectedDeps.add(dep));
|
|
116
|
+
} else {
|
|
117
|
+
// 导入的成员在清单中找不到,标记为未映射
|
|
118
|
+
hasUnmappedMember = true;
|
|
119
|
+
console.warn(
|
|
120
|
+
`警告: ${packageName} 的清单中未找到导出成员 "${member}" 的依赖信息`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 如果有未映射的成员,为安全起见,包含全量依赖
|
|
126
|
+
if (hasUnmappedMember) {
|
|
127
|
+
console.log(
|
|
128
|
+
`✓ 加载 ${packageName} 的全量构件依赖: ${manifest.brickDependencies.length} 个(包含未映射成员)`
|
|
129
|
+
);
|
|
130
|
+
return manifest.brickDependencies;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const selectedDepsArray = Array.from(selectedDeps);
|
|
134
|
+
console.log(
|
|
135
|
+
`✓ 加载 ${packageName} 的构件依赖: ${selectedDepsArray.length} 个(选择性导入: ${importedMembers.join(", ")})`
|
|
136
|
+
);
|
|
137
|
+
return selectedDepsArray;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 回退:使用全量依赖
|
|
141
|
+
console.log(
|
|
142
|
+
`✓ 加载 ${packageName} 的全量构件依赖: ${manifest.brickDependencies.length} 个`
|
|
143
|
+
);
|
|
144
|
+
return manifest.brickDependencies;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.warn(`警告: 无法加载 ${packageName} 的依赖清单:`, error.message);
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
40
151
|
/**
|
|
41
152
|
* Scan defined bricks by AST.
|
|
42
153
|
*
|
|
@@ -78,6 +189,9 @@ export default async function scanBricks(packageDir) {
|
|
|
78
189
|
/** @type {Map<string, Set<string>} */
|
|
79
190
|
const importsMap = new Map();
|
|
80
191
|
|
|
192
|
+
/** @type {Map<string, Array<{packageName: string, importedMembers: string[], isNamespaceImport: boolean}>>} */
|
|
193
|
+
const externalImports = new Map();
|
|
194
|
+
|
|
81
195
|
/** @type {{ filePath: string; declaration: Declaration; usedReferences: Set<string> }[]} */
|
|
82
196
|
const typeDeclarations = [];
|
|
83
197
|
|
|
@@ -592,7 +706,7 @@ export default async function scanBricks(packageDir) {
|
|
|
592
706
|
topLevelFunctions.set(node.id.name, nodePath);
|
|
593
707
|
}
|
|
594
708
|
},
|
|
595
|
-
ImportDeclaration({ node: { source, importKind } }) {
|
|
709
|
+
ImportDeclaration({ node: { source, importKind, specifiers } }) {
|
|
596
710
|
// Match `import "..."`
|
|
597
711
|
if (
|
|
598
712
|
// Ignore import from node modules.
|
|
@@ -621,6 +735,64 @@ export default async function scanBricks(packageDir) {
|
|
|
621
735
|
// also look for "./directory/index.*"
|
|
622
736
|
addImportFile(importPath, "index");
|
|
623
737
|
}
|
|
738
|
+
} else if (
|
|
739
|
+
// 收集对 @shared/* 和 @next-shared/* 的导入
|
|
740
|
+
(source.value.startsWith("@shared/") ||
|
|
741
|
+
source.value.startsWith("@next-shared/")) &&
|
|
742
|
+
importKind === "value"
|
|
743
|
+
) {
|
|
744
|
+
const packageName = source.value.split("/").slice(0, 2).join("/");
|
|
745
|
+
|
|
746
|
+
// 收集导入的成员
|
|
747
|
+
const importedMembers = [];
|
|
748
|
+
let isNamespaceImport = false; // import * as foo
|
|
749
|
+
|
|
750
|
+
// 如果是副作用导入(import "@shared/pkg"),使用全量依赖
|
|
751
|
+
if (specifiers.length === 0) {
|
|
752
|
+
isNamespaceImport = true;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
for (const specifier of specifiers) {
|
|
756
|
+
if (specifier.type === "ImportSpecifier") {
|
|
757
|
+
// import { foo, bar } from "@shared/helpers"
|
|
758
|
+
// 处理字符串导入名:import { "some-name" as alias } from "..."
|
|
759
|
+
const importedName =
|
|
760
|
+
specifier.imported.type === "StringLiteral"
|
|
761
|
+
? specifier.imported.value
|
|
762
|
+
: specifier.imported.name;
|
|
763
|
+
importedMembers.push(importedName);
|
|
764
|
+
} else if (specifier.type === "ImportDefaultSpecifier") {
|
|
765
|
+
// import foo from "@shared/helpers"
|
|
766
|
+
importedMembers.push("default");
|
|
767
|
+
} else if (specifier.type === "ImportNamespaceSpecifier") {
|
|
768
|
+
// import * as foo from "@shared/helpers"
|
|
769
|
+
isNamespaceImport = true;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const existingImports = externalImports.get(filePath) ?? [];
|
|
774
|
+
const existing = existingImports.find(
|
|
775
|
+
(item) => item.packageName === packageName
|
|
776
|
+
);
|
|
777
|
+
|
|
778
|
+
if (existing) {
|
|
779
|
+
// 合并导入成员(去重)
|
|
780
|
+
if (isNamespaceImport) {
|
|
781
|
+
existing.isNamespaceImport = true;
|
|
782
|
+
}
|
|
783
|
+
const existingSet = new Set(existing.importedMembers);
|
|
784
|
+
importedMembers.forEach((member) => existingSet.add(member));
|
|
785
|
+
existing.importedMembers = [...existingSet];
|
|
786
|
+
} else {
|
|
787
|
+
externalImports.set(filePath, [
|
|
788
|
+
...existingImports,
|
|
789
|
+
{
|
|
790
|
+
packageName,
|
|
791
|
+
importedMembers,
|
|
792
|
+
isNamespaceImport,
|
|
793
|
+
},
|
|
794
|
+
]);
|
|
795
|
+
}
|
|
624
796
|
}
|
|
625
797
|
|
|
626
798
|
if (isDeprecatedV2Packages(source.value)) {
|
|
@@ -839,6 +1011,71 @@ export default async function scanBricks(packageDir) {
|
|
|
839
1011
|
}
|
|
840
1012
|
}
|
|
841
1013
|
|
|
1014
|
+
// 收集外部包的构件依赖(支持选择性导入)
|
|
1015
|
+
const externalDepsCache = new Map(); // 缓存外部包的依赖,键: "packageName:member1,member2,..."
|
|
1016
|
+
|
|
1017
|
+
for (const [brickName, sourcePath] of brickSourceFiles.entries()) {
|
|
1018
|
+
const analyzedFiles = new Set();
|
|
1019
|
+
const externalDeps = new Set();
|
|
1020
|
+
|
|
1021
|
+
const analyzeExternal = (filePath) => {
|
|
1022
|
+
if (analyzedFiles.has(filePath)) {
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
analyzedFiles.add(filePath);
|
|
1026
|
+
|
|
1027
|
+
// 收集该文件导入的外部包
|
|
1028
|
+
for (const externalImport of externalImports.get(filePath) ?? []) {
|
|
1029
|
+
const { packageName, importedMembers, isNamespaceImport } =
|
|
1030
|
+
externalImport;
|
|
1031
|
+
|
|
1032
|
+
// 构建缓存键
|
|
1033
|
+
const cacheKey = isNamespaceImport
|
|
1034
|
+
? `${packageName}:*` // 命名空间导入
|
|
1035
|
+
: `${packageName}:${[...new Set(importedMembers)].sort().join(",")}`; // 选择性导入
|
|
1036
|
+
|
|
1037
|
+
// 从缓存或文件中加载外部依赖
|
|
1038
|
+
let deps = externalDepsCache.get(cacheKey);
|
|
1039
|
+
if (!deps) {
|
|
1040
|
+
deps = loadExternalDependencies(
|
|
1041
|
+
packageName,
|
|
1042
|
+
importedMembers,
|
|
1043
|
+
isNamespaceImport,
|
|
1044
|
+
packageDir
|
|
1045
|
+
);
|
|
1046
|
+
if (deps) {
|
|
1047
|
+
externalDepsCache.set(cacheKey, deps);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// 添加到当前构件的依赖中
|
|
1052
|
+
if (deps) {
|
|
1053
|
+
for (const dep of deps) {
|
|
1054
|
+
if (dep !== brickName) {
|
|
1055
|
+
externalDeps.add(dep);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// 递归分析本地导入的文件
|
|
1062
|
+
for (const item of importsMap.get(filePath) ?? []) {
|
|
1063
|
+
analyzeExternal(item);
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
analyzeExternal(sourcePath);
|
|
1068
|
+
|
|
1069
|
+
// 合并本地依赖和外部依赖(去重)
|
|
1070
|
+
if (externalDeps.size > 0) {
|
|
1071
|
+
const allDeps = new Set([
|
|
1072
|
+
...(analyzedDeps[brickName] ?? []),
|
|
1073
|
+
...externalDeps,
|
|
1074
|
+
]);
|
|
1075
|
+
analyzedDeps[brickName] = [...allDeps];
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
842
1079
|
function isMatch(importPath, filePath) {
|
|
843
1080
|
return (
|
|
844
1081
|
importPath.replace(/\.[^.]+$/, "") === filePath.replace(/\.[^.]+$/, "")
|