cloudcc-cli 2.1.9 → 2.2.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/README.md +10 -0
- package/bin/cc.js +9 -0
- package/package.json +3 -1
- package/src/plugin/create.js +0 -1
- package/src/plugin/create1.js +0 -1
- package/src/plugin/publish1.js +55 -14
- package/src/version/get.js +10 -0
- package/src/version/index.js +8 -0
- package/template/Appvue +14 -14
- package/template/cloudcc-cli.configjs +1 -1
- package/template/indexvue +0 -1
- package/utils/checkVersion.js +0 -2
- package/utils/config.js +1 -2
- package/utils/utils.js +404 -1
package/README.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
# ReleaseV2.2.0
|
|
2
|
+
#### Release Date: 2025-11-24
|
|
3
|
+
#### Release Scope: Full
|
|
4
|
+
#### Release Content
|
|
5
|
+
* Optimization
|
|
6
|
+
* Update the packaging and parsing of Vue files
|
|
7
|
+
* Add upload dependency tree to custom component
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
1
11
|
# ReleaseV2.1.9
|
|
2
12
|
#### Release Date: 2025-9-1
|
|
3
13
|
#### Release Scope: Full
|
package/bin/cc.js
CHANGED
|
@@ -4,6 +4,13 @@ const chalk = require("chalk")
|
|
|
4
4
|
let argvs = process.argv.splice(2);
|
|
5
5
|
|
|
6
6
|
let action = argvs[0]
|
|
7
|
+
// 处理版本查询命令
|
|
8
|
+
if (action === '-version' || action === '-v' || action === '--version' || action === '--v') {
|
|
9
|
+
const version = require("../src/version/index")
|
|
10
|
+
version('get', argvs);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
if (!action) {
|
|
8
15
|
console.log()
|
|
9
16
|
console.log(chalk.yellow("Please see the help documentation"));
|
|
@@ -43,6 +50,8 @@ cc.object = require("../src/object/index")
|
|
|
43
50
|
cc.recordType = require("../src/recordType/index")
|
|
44
51
|
|
|
45
52
|
cc.config = require("../src/config/index")
|
|
53
|
+
|
|
54
|
+
cc.version = require("../src/version/index")
|
|
46
55
|
try {
|
|
47
56
|
cc[argvs[1]](argvs[0], argvs);
|
|
48
57
|
} catch (e) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudcc-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "cloudcc-cli",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudcc",
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
"package-jar": "mvn clean && mvn package"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"@babel/parser": "^7.28.5",
|
|
24
|
+
"@babel/traverse": "^7.28.5",
|
|
23
25
|
"axios": "^0.21.4",
|
|
24
26
|
"boxen": "^4.0.0",
|
|
25
27
|
"chalk": "^2.4.2",
|
package/src/plugin/create.js
CHANGED
package/src/plugin/create1.js
CHANGED
package/src/plugin/publish1.js
CHANGED
|
@@ -7,6 +7,7 @@ const { readCache, writeCache } = require("../../utils/cache")
|
|
|
7
7
|
const { checkUpdate } = require("../../utils/checkVersion")
|
|
8
8
|
const BaseUrl = "https://developer.apis.cloudcc.cn"
|
|
9
9
|
const { getPackageJson } = require("../../utils/config.js")
|
|
10
|
+
const { parseVueDataFunction, collectDependenciesWithTree } = require("../../utils/utils.js")
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class Builder {
|
|
@@ -86,16 +87,10 @@ class Builder {
|
|
|
86
87
|
console.log(chalk.yellow("Warning: The scoped attribute is missing in style, which may cause global style pollution. It is recommended to fix it。"));
|
|
87
88
|
console.log()
|
|
88
89
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
let data =
|
|
92
|
-
|
|
93
|
-
data = eval(`(${vueData})`)()
|
|
94
|
-
}
|
|
95
|
-
catch (e) {
|
|
96
|
-
console.log()
|
|
97
|
-
console.log(chalk.red("Error: The data function cannot contain reference variables."));
|
|
98
|
-
console.log()
|
|
90
|
+
|
|
91
|
+
// 提取 data() 函数的返回对象
|
|
92
|
+
let data = parseVueDataFunction(vueContent);
|
|
93
|
+
if (!data) {
|
|
99
94
|
return null;
|
|
100
95
|
}
|
|
101
96
|
if (!data.propObj) {
|
|
@@ -189,7 +184,50 @@ class Builder {
|
|
|
189
184
|
belongOrgFlag = this.options.pluginConfig.belongOrgFlag
|
|
190
185
|
}
|
|
191
186
|
vueData = JSON.stringify(data);
|
|
192
|
-
|
|
187
|
+
|
|
188
|
+
// 收集所有依赖文件
|
|
189
|
+
const baseDir = path.join(process.cwd(), 'plugins');
|
|
190
|
+
const entryFile = path.join(baseDir, buildFileName);
|
|
191
|
+
const allDependencies = collectDependenciesWithTree(entryFile, baseDir);
|
|
192
|
+
|
|
193
|
+
// 收集根目录下的配置文件
|
|
194
|
+
const rootDir = process.cwd();
|
|
195
|
+
const configFiles = ['package.json', 'cloudcc-cli.config.js'];
|
|
196
|
+
|
|
197
|
+
for (const configFile of configFiles) {
|
|
198
|
+
const configPath = path.join(rootDir, configFile);
|
|
199
|
+
if (fs.existsSync(configPath)) {
|
|
200
|
+
try {
|
|
201
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
202
|
+
allDependencies[configFile] = content;
|
|
203
|
+
} catch (err) {
|
|
204
|
+
console.log(chalk.yellow(`Warning: Cannot read ${configFile}:`, err.message));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 输出依赖信息到文件
|
|
210
|
+
try {
|
|
211
|
+
const outputPath = path.join(process.cwd(), 'dependencies-output.json');
|
|
212
|
+
fs.writeFileSync(outputPath, JSON.stringify(allDependencies, null, 2), 'utf8');
|
|
213
|
+
console.log(chalk.green(`Dependencies collected and saved to: ${outputPath}`));
|
|
214
|
+
console.log(chalk.cyan(`Total files: ${Object.keys(allDependencies).length}`));
|
|
215
|
+
} catch (err) {
|
|
216
|
+
console.log(chalk.yellow('Warning: Failed to write dependencies output file:', err.message));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
compName,
|
|
221
|
+
component,
|
|
222
|
+
vueContent,
|
|
223
|
+
vueData,
|
|
224
|
+
bizType,
|
|
225
|
+
compDesc,
|
|
226
|
+
category,
|
|
227
|
+
loadModel,
|
|
228
|
+
belongOrgFlag,
|
|
229
|
+
dependencies: allDependencies
|
|
230
|
+
}
|
|
193
231
|
};
|
|
194
232
|
|
|
195
233
|
initPluginFile(buildFileName, component, plginTemp) {
|
|
@@ -258,13 +296,15 @@ class Builder {
|
|
|
258
296
|
"compLabel": obj.compName,
|
|
259
297
|
"compUniName": obj.component,
|
|
260
298
|
"compContentJs": jsContent,
|
|
261
|
-
"compContentVue": obj.
|
|
299
|
+
"compContentVue": JSON.stringify(obj.dependencies || {}),
|
|
300
|
+
// "compContentVue": obj.vueContent,
|
|
262
301
|
"vueData": obj.vueData,
|
|
263
302
|
"bizType": obj.bizType,
|
|
264
303
|
"compDesc": obj.compDesc,
|
|
265
304
|
"category": obj.category,
|
|
266
305
|
"loadModel": obj.loadModel,
|
|
267
|
-
"belongOrgFlag": obj.belongOrgFlag
|
|
306
|
+
"belongOrgFlag": obj.belongOrgFlag,
|
|
307
|
+
"dependencies": JSON.stringify(obj.dependencies || {})
|
|
268
308
|
}
|
|
269
309
|
|
|
270
310
|
let devSvcDispatch = this.options.devConsoleConfig.devSvcDispatch || '/devconsole'
|
|
@@ -275,7 +315,8 @@ class Builder {
|
|
|
275
315
|
console.error(chalk.green(`Success!`));
|
|
276
316
|
console.log();
|
|
277
317
|
} else {
|
|
278
|
-
console.
|
|
318
|
+
console.log("res", res);
|
|
319
|
+
console.error(chalk.red(`Fail: ${res.returnInfo}`));
|
|
279
320
|
console.log();
|
|
280
321
|
}
|
|
281
322
|
return res;
|
package/template/Appvue
CHANGED
|
@@ -5,21 +5,21 @@
|
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<script>
|
|
8
|
-
export default {
|
|
9
|
-
|
|
10
|
-
};
|
|
8
|
+
export default {
|
|
9
|
+
name: "App",
|
|
10
|
+
};
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
13
|
<style lang="scss" scoped>
|
|
14
|
-
html body {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
14
|
+
html body {
|
|
15
|
+
padding: 0;
|
|
16
|
+
margin: 0;
|
|
17
|
+
height: 100vh;
|
|
18
|
+
width: 100%;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.cc-container {
|
|
22
|
+
text-align: center;
|
|
23
|
+
padding: 8px;
|
|
24
|
+
}
|
|
25
25
|
</style>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = { "use": "dev", "dev": { "
|
|
1
|
+
module.exports = { "use": "dev", "dev": { "safetyMark": "xxx", "CloudCCDev": "xxx" } }
|
package/template/indexvue
CHANGED
package/utils/checkVersion.js
CHANGED
|
@@ -23,7 +23,6 @@ function checkNpmVersion() {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
onlineVersionNum = Number(onlineVersion.replace(/\./g, ""));
|
|
26
|
-
|
|
27
26
|
console.log('\n');
|
|
28
27
|
console.log(chalk.bold.cyan(' CloudCC CLI Version\n'));
|
|
29
28
|
console.log(' ' + chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
@@ -70,7 +69,6 @@ async function checkUpdate(path = process.cwd()) {
|
|
|
70
69
|
const cacheKey = 'version_check';
|
|
71
70
|
const now = Date.now();
|
|
72
71
|
const oneHour = 60 * 60 * 1000;
|
|
73
|
-
|
|
74
72
|
if (cache[cacheKey] &&
|
|
75
73
|
cache[cacheKey].timestamp &&
|
|
76
74
|
(now - cache[cacheKey].timestamp) < oneHour) {
|
package/utils/config.js
CHANGED
|
@@ -58,8 +58,7 @@ async function getPackageJson3(projectPath = process.cwd()) {
|
|
|
58
58
|
*/
|
|
59
59
|
async function getPackageJsonCache(projectPath = process.cwd()) {
|
|
60
60
|
let config = require(path.join(projectPath, "cloudcc-cli.config.js"));
|
|
61
|
-
const cacheData = readCache()[config[config.use].safetyMark || config[config.use].secretKey];
|
|
62
|
-
|
|
61
|
+
const cacheData = readCache(projectPath)[config[config.use].safetyMark || config[config.use].secretKey];
|
|
63
62
|
if (cacheData && cacheData.timestamp) {
|
|
64
63
|
const oneHour = 60 * 60 * 1000; // 1小时的毫秒数
|
|
65
64
|
const currentTime = Date.now();
|
package/utils/utils.js
CHANGED
|
@@ -165,5 +165,408 @@ function decryptCloudCCDevInfo(encryptedData) {
|
|
|
165
165
|
*/
|
|
166
166
|
const javaContentRegular = /\/\/\s*@SOURCE_CONTENT_START\r?\n([\s\S]*?)\r?\n\s*\/\/\s*@SOURCE_CONTENT_END/
|
|
167
167
|
|
|
168
|
+
// 用于 AST 解析
|
|
169
|
+
let babelParser, babelTraverse;
|
|
170
|
+
try {
|
|
171
|
+
babelParser = require('@babel/parser');
|
|
172
|
+
babelTraverse = require('@babel/traverse').default;
|
|
173
|
+
} catch (e) {
|
|
174
|
+
// 如果没有安装 babel 依赖,将在使用时提示安装
|
|
175
|
+
babelParser = null;
|
|
176
|
+
babelTraverse = null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 使用 AST 解析 Vue 文件中的 data() 函数
|
|
181
|
+
* @param {string} vueContent - Vue 文件内容
|
|
182
|
+
* @returns {Object|null} 解析出的 data 对象,失败返回 null
|
|
183
|
+
*/
|
|
184
|
+
function parseVueDataFunction(vueContent) {
|
|
185
|
+
// if (!babelParser || !babelTraverse) {
|
|
186
|
+
// console.log()
|
|
187
|
+
// console.log(chalk.yellow("Warning: @babel/parser and @babel/traverse are not installed. Please run:"));
|
|
188
|
+
// console.log(chalk.cyan("npm install --save @babel/parser @babel/traverse"));
|
|
189
|
+
// console.log()
|
|
190
|
+
// console.log(chalk.yellow("Falling back to regex parsing..."));
|
|
191
|
+
// return parseVueDataFunctionWithRegex(vueContent);
|
|
192
|
+
// }
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
// 提取 <script> 标签中的内容
|
|
196
|
+
const scriptMatch = vueContent.match(/<script[^>]*>([\s\S]*?)<\/script>/);
|
|
197
|
+
if (!scriptMatch) {
|
|
198
|
+
console.log()
|
|
199
|
+
console.log(chalk.red("Error: Cannot find <script> tag in the vue file."));
|
|
200
|
+
console.log()
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const scriptContent = scriptMatch[1];
|
|
205
|
+
|
|
206
|
+
// 使用 Babel 解析 JavaScript 代码
|
|
207
|
+
const ast = babelParser.parse(scriptContent, {
|
|
208
|
+
sourceType: 'module',
|
|
209
|
+
plugins: ['jsx', 'typescript']
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
let dataObject = null;
|
|
213
|
+
|
|
214
|
+
// 遍历 AST,查找 data() 方法
|
|
215
|
+
babelTraverse(ast, {
|
|
216
|
+
ObjectMethod(path) {
|
|
217
|
+
// 查找名为 data 的方法
|
|
218
|
+
if (path.node.key.name === 'data' && path.node.params.length === 0) {
|
|
219
|
+
// 查找 return 语句
|
|
220
|
+
path.traverse({
|
|
221
|
+
ReturnStatement(returnPath) {
|
|
222
|
+
if (returnPath.node.argument && returnPath.node.argument.type === 'ObjectExpression') {
|
|
223
|
+
// 将 AST 对象表达式转换为实际的 JavaScript 对象
|
|
224
|
+
dataObject = astToObject(returnPath.node.argument);
|
|
225
|
+
returnPath.stop();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
path.stop();
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
// 也处理 data: function() {} 的形式
|
|
233
|
+
ObjectProperty(path) {
|
|
234
|
+
if (path.node.key.name === 'data' &&
|
|
235
|
+
(path.node.value.type === 'FunctionExpression' || path.node.value.type === 'ArrowFunctionExpression')) {
|
|
236
|
+
// 查找 return 语句
|
|
237
|
+
path.traverse({
|
|
238
|
+
ReturnStatement(returnPath) {
|
|
239
|
+
if (returnPath.node.argument && returnPath.node.argument.type === 'ObjectExpression') {
|
|
240
|
+
dataObject = astToObject(returnPath.node.argument);
|
|
241
|
+
returnPath.stop();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
path.stop();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
if (!dataObject) {
|
|
251
|
+
console.log()
|
|
252
|
+
console.log(chalk.red("Error: Cannot find data() function or its return statement in the vue file."));
|
|
253
|
+
console.log()
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return dataObject;
|
|
258
|
+
|
|
259
|
+
} catch (e) {
|
|
260
|
+
console.log()
|
|
261
|
+
console.log(chalk.red("Error: Failed to parse vue file with AST."));
|
|
262
|
+
console.log(chalk.red("Details: " + e.message));
|
|
263
|
+
console.log()
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* 将 Babel AST 对象表达式转换为 JavaScript 对象
|
|
270
|
+
* @param {Object} node - AST 节点
|
|
271
|
+
* @returns {Object} JavaScript 对象
|
|
272
|
+
*/
|
|
273
|
+
function astToObject(node) {
|
|
274
|
+
if (node.type === 'ObjectExpression') {
|
|
275
|
+
const obj = {};
|
|
276
|
+
for (const prop of node.properties) {
|
|
277
|
+
if (prop.type === 'ObjectProperty') {
|
|
278
|
+
const key = prop.key.name || prop.key.value;
|
|
279
|
+
obj[key] = astToValue(prop.value);
|
|
280
|
+
} else if (prop.type === 'SpreadElement') {
|
|
281
|
+
// 处理展开运算符 {...obj}
|
|
282
|
+
console.log(chalk.yellow("Warning: Spread operator is not fully supported in data object."));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return obj;
|
|
286
|
+
}
|
|
287
|
+
return astToValue(node);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 将 AST 值节点转换为 JavaScript 值
|
|
292
|
+
* @param {Object} node - AST 节点
|
|
293
|
+
* @returns {*} JavaScript 值
|
|
294
|
+
*/
|
|
295
|
+
function astToValue(node) {
|
|
296
|
+
switch (node.type) {
|
|
297
|
+
case 'StringLiteral':
|
|
298
|
+
return node.value;
|
|
299
|
+
case 'NumericLiteral':
|
|
300
|
+
return node.value;
|
|
301
|
+
case 'BooleanLiteral':
|
|
302
|
+
return node.value;
|
|
303
|
+
case 'NullLiteral':
|
|
304
|
+
return null;
|
|
305
|
+
case 'ObjectExpression':
|
|
306
|
+
return astToObject(node);
|
|
307
|
+
case 'ArrayExpression':
|
|
308
|
+
return node.elements.map(el => astToValue(el));
|
|
309
|
+
case 'UnaryExpression':
|
|
310
|
+
// 处理负数等
|
|
311
|
+
if (node.operator === '-' && node.argument.type === 'NumericLiteral') {
|
|
312
|
+
return -node.argument.value;
|
|
313
|
+
}
|
|
314
|
+
return undefined;
|
|
315
|
+
default:
|
|
316
|
+
// 对于无法静态解析的值(如函数、变量引用等),返回 undefined
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* 降级方案:使用正则表达式解析 Vue data() 函数
|
|
323
|
+
* @param {string} vueContent - Vue 文件内容
|
|
324
|
+
* @returns {Object|null} 解析出的 data 对象,失败返回 null
|
|
325
|
+
*/
|
|
326
|
+
function parseVueDataFunctionWithRegex(vueContent) {
|
|
327
|
+
try {
|
|
328
|
+
// 匹配 data() 函数,找到开始位置
|
|
329
|
+
const dataStartMatch = vueContent.match(/data\s*\(\s*\)\s*\{[\s\S]*?return\s*\{/);
|
|
330
|
+
if (!dataStartMatch) {
|
|
331
|
+
console.log()
|
|
332
|
+
console.log(chalk.red("Error: Cannot find data() function or its return statement in the vue file."));
|
|
333
|
+
console.log()
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// 从 return { 后面开始提取,需要匹配嵌套的大括号
|
|
338
|
+
let startIndex = dataStartMatch.index + dataStartMatch[0].length;
|
|
339
|
+
let braceCount = 1;
|
|
340
|
+
let endIndex = startIndex;
|
|
341
|
+
|
|
342
|
+
// 逐字符扫描,匹配嵌套的大括号
|
|
343
|
+
while (endIndex < vueContent.length && braceCount > 0) {
|
|
344
|
+
const char = vueContent[endIndex];
|
|
345
|
+
if (char === '{') {
|
|
346
|
+
braceCount++;
|
|
347
|
+
} else if (char === '}') {
|
|
348
|
+
braceCount--;
|
|
349
|
+
}
|
|
350
|
+
endIndex++;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (braceCount !== 0) {
|
|
354
|
+
console.log()
|
|
355
|
+
console.log(chalk.red("Error: Cannot parse data() function, unmatched braces."));
|
|
356
|
+
console.log()
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// 提取返回对象的内容(不包括最外层的大括号)
|
|
361
|
+
const dataObjectContent = vueContent.substring(startIndex, endIndex - 1);
|
|
362
|
+
|
|
363
|
+
// 使用 Function 构造器在独立作用域中执行
|
|
364
|
+
const evalCode = `return {${dataObjectContent}};`;
|
|
365
|
+
const dataFunc = new Function(evalCode);
|
|
366
|
+
return dataFunc();
|
|
367
|
+
|
|
368
|
+
} catch (e) {
|
|
369
|
+
console.log()
|
|
370
|
+
console.log(chalk.red("Error: Failed to parse data function with regex."));
|
|
371
|
+
console.log(chalk.red("Details: " + e.message));
|
|
372
|
+
console.log()
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* 解析 JS 代码中的依赖(import/require)
|
|
380
|
+
* @param {string} jsContent - JS 代码内容
|
|
381
|
+
* @returns {Array<string>} 依赖路径数组
|
|
382
|
+
*/
|
|
383
|
+
function parseJsDependencies(jsContent) {
|
|
384
|
+
const dependencies = [];
|
|
385
|
+
|
|
386
|
+
// 匹配 ES6 import
|
|
387
|
+
const importRegex = /import\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
388
|
+
let match;
|
|
389
|
+
while ((match = importRegex.exec(jsContent)) !== null) {
|
|
390
|
+
dependencies.push(match[1]);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// 匹配 require
|
|
394
|
+
const requireRegex = /require\s*\(['"]([^'"]+)['"]\)/g;
|
|
395
|
+
while ((match = requireRegex.exec(jsContent)) !== null) {
|
|
396
|
+
dependencies.push(match[1]);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return dependencies;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* 解析 CSS 中的依赖(@import)
|
|
404
|
+
* @param {string} cssContent - CSS 代码内容
|
|
405
|
+
* @returns {Array<string>} 依赖路径数组
|
|
406
|
+
*/
|
|
407
|
+
function parseCssDependencies(cssContent) {
|
|
408
|
+
const dependencies = [];
|
|
409
|
+
|
|
410
|
+
// 匹配 @import
|
|
411
|
+
const importRegex = /@import\s+['"]([^'"]+)['"]/g;
|
|
412
|
+
let match;
|
|
413
|
+
while ((match = importRegex.exec(cssContent)) !== null) {
|
|
414
|
+
dependencies.push(match[1]);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return dependencies;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* 解析文件中的依赖(import/require)
|
|
422
|
+
* @param {string} fileContent - 文件内容
|
|
423
|
+
* @param {string} fileType - 文件类型 ('vue', 'js', 'css')
|
|
424
|
+
* @returns {Array<string>} 依赖路径数组
|
|
425
|
+
*/
|
|
426
|
+
function parseDependencies(fileContent, fileType) {
|
|
427
|
+
const dependencies = [];
|
|
428
|
+
|
|
429
|
+
if (fileType === 'vue') {
|
|
430
|
+
// 提取 <script> 部分
|
|
431
|
+
const scriptMatch = fileContent.match(/<script[^>]*>([\s\S]*?)<\/script>/);
|
|
432
|
+
if (scriptMatch) {
|
|
433
|
+
dependencies.push(...parseJsDependencies(scriptMatch[1]));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// 提取 <style> 中的 @import
|
|
437
|
+
const styleMatches = fileContent.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/g);
|
|
438
|
+
for (const match of styleMatches) {
|
|
439
|
+
dependencies.push(...parseCssDependencies(match[1]));
|
|
440
|
+
}
|
|
441
|
+
} else if (fileType === 'js') {
|
|
442
|
+
dependencies.push(...parseJsDependencies(fileContent));
|
|
443
|
+
} else if (fileType === 'css') {
|
|
444
|
+
dependencies.push(...parseCssDependencies(fileContent));
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return dependencies;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* 解析依赖路径(处理相对路径、扩展名等)
|
|
452
|
+
* @param {string} depPath - 依赖路径
|
|
453
|
+
* @param {string} currentFile - 当前文件的绝对路径
|
|
454
|
+
* @param {string} baseDir - 基础目录
|
|
455
|
+
* @returns {string|null} 解析后的绝对路径,未找到返回 null
|
|
456
|
+
*/
|
|
457
|
+
function resolveDependencyPath(depPath, currentFile, baseDir) {
|
|
458
|
+
const fs = require('fs');
|
|
459
|
+
const path = require('path');
|
|
460
|
+
|
|
461
|
+
const currentDir = path.dirname(currentFile);
|
|
462
|
+
const possibleExtensions = ['', '.vue', '.js', '.jsx', '.ts', '.tsx', '.css', '.scss', '.sass', '.less'];
|
|
463
|
+
|
|
464
|
+
// 尝试不同的扩展名
|
|
465
|
+
for (const ext of possibleExtensions) {
|
|
466
|
+
const fullPath = path.resolve(currentDir, depPath + ext);
|
|
467
|
+
if (fs.existsSync(fullPath)) {
|
|
468
|
+
const stat = fs.statSync(fullPath);
|
|
469
|
+
if (stat.isFile()) {
|
|
470
|
+
return fullPath;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// 如果是目录,尝试 index 文件
|
|
476
|
+
const dirPath = path.resolve(currentDir, depPath);
|
|
477
|
+
if (fs.existsSync(dirPath)) {
|
|
478
|
+
const stat = fs.statSync(dirPath);
|
|
479
|
+
if (stat.isDirectory()) {
|
|
480
|
+
for (const ext of ['.vue', '.js', '.jsx', '.ts', '.tsx']) {
|
|
481
|
+
const indexPath = path.join(dirPath, 'index' + ext);
|
|
482
|
+
if (fs.existsSync(indexPath)) {
|
|
483
|
+
return indexPath;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* 递归收集所有依赖文件
|
|
494
|
+
* @param {string} entryFile - 入口文件路径(绝对路径或相对于 baseDir)
|
|
495
|
+
* @param {string} baseDir - 基础目录
|
|
496
|
+
* @param {Set} visited - 已访问的文件集合(用于避免循环依赖)
|
|
497
|
+
* @returns {Object} { files: { relativePath: fileContent }, tree: dependencyTree }
|
|
498
|
+
*/
|
|
499
|
+
function collectDependencies(entryFile, baseDir, visited = new Set()) {
|
|
500
|
+
const fs = require('fs');
|
|
501
|
+
const path = require('path');
|
|
502
|
+
const dependencies = {};
|
|
503
|
+
|
|
504
|
+
// 规范化路径
|
|
505
|
+
const normalizedPath = path.isAbsolute(entryFile) ? entryFile : path.resolve(baseDir, entryFile);
|
|
506
|
+
|
|
507
|
+
// 避免循环依赖
|
|
508
|
+
if (visited.has(normalizedPath)) {
|
|
509
|
+
return dependencies;
|
|
510
|
+
}
|
|
511
|
+
visited.add(normalizedPath);
|
|
512
|
+
|
|
513
|
+
// 读取文件内容
|
|
514
|
+
let fileContent;
|
|
515
|
+
try {
|
|
516
|
+
fileContent = fs.readFileSync(normalizedPath, 'utf8');
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.log(chalk.yellow(`Warning: Cannot read file ${normalizedPath}`));
|
|
519
|
+
return dependencies;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// 保存当前文件(使用相对路径作为键)
|
|
523
|
+
const relativePath = path.relative(baseDir, normalizedPath);
|
|
524
|
+
dependencies[relativePath] = fileContent;
|
|
525
|
+
|
|
526
|
+
// 确定文件类型
|
|
527
|
+
const ext = path.extname(normalizedPath).slice(1);
|
|
528
|
+
const fileType = ext === 'vue' ? 'vue' :
|
|
529
|
+
(['css', 'scss', 'sass', 'less'].includes(ext)) ? 'css' : 'js';
|
|
530
|
+
|
|
531
|
+
// 解析依赖
|
|
532
|
+
const deps = parseDependencies(fileContent, fileType);
|
|
533
|
+
|
|
534
|
+
// 递归处理每个依赖
|
|
535
|
+
for (const dep of deps) {
|
|
536
|
+
// 只处理相对路径(跳过 node_modules 和绝对路径的包)
|
|
537
|
+
if (dep.startsWith('.') || dep.startsWith('/')) {
|
|
538
|
+
const depPath = resolveDependencyPath(dep, normalizedPath, baseDir);
|
|
539
|
+
if (depPath) {
|
|
540
|
+
const subDeps = collectDependencies(depPath, baseDir, visited);
|
|
541
|
+
Object.assign(dependencies, subDeps);
|
|
542
|
+
} else {
|
|
543
|
+
console.log(chalk.yellow(`Warning: Cannot resolve dependency "${dep}" from ${relativePath}`));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return dependencies;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* 收集依赖文件
|
|
553
|
+
* @param {string} entryFile - 入口文件路径
|
|
554
|
+
* @param {string} baseDir - 基础目录
|
|
555
|
+
* @returns {Object} { [relativePath]: fileContent }
|
|
556
|
+
*/
|
|
557
|
+
function collectDependenciesWithTree(entryFile, baseDir) {
|
|
558
|
+
return collectDependencies(entryFile, baseDir);
|
|
559
|
+
}
|
|
168
560
|
|
|
169
|
-
module.exports = {
|
|
561
|
+
module.exports = {
|
|
562
|
+
getDevConsoleConfig,
|
|
563
|
+
javaContentRegular,
|
|
564
|
+
decryptCloudCCDevInfo,
|
|
565
|
+
parseVueDataFunction,
|
|
566
|
+
parseVueDataFunctionWithRegex,
|
|
567
|
+
collectDependencies,
|
|
568
|
+
collectDependenciesWithTree,
|
|
569
|
+
parseDependencies,
|
|
570
|
+
parseJsDependencies,
|
|
571
|
+
parseCssDependencies
|
|
572
|
+
}
|