@nasl/cli 0.1.18 → 0.1.19
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 +3 -1
- package/dist/bin/nasl.mjs +130 -37
- package/dist/bin/nasl.mjs.map +1 -1
- package/dist/bin/naslc.mjs +130 -37
- package/dist/bin/naslc.mjs.map +1 -1
- package/dist/index.mjs +129 -36
- package/dist/index.mjs.map +1 -1
- package/out/services/compose.d.ts +1 -1
- package/out/services/compose.d.ts.map +1 -1
- package/out/services/compose.js +66 -18
- package/out/services/compose.js.map +1 -1
- package/out/services/resolve.d.ts.map +1 -1
- package/out/services/resolve.js +30 -5
- package/out/services/resolve.js.map +1 -1
- package/out/utils/config.d.ts.map +1 -1
- package/out/utils/config.js +16 -13
- package/out/utils/config.js.map +1 -1
- package/out/utils/file.d.ts +12 -0
- package/out/utils/file.d.ts.map +1 -1
- package/out/utils/file.js +18 -0
- package/out/utils/file.js.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -61,6 +61,56 @@ function createSorter() {
|
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
const sorter = createSorter();
|
|
64
|
+
/**
|
|
65
|
+
* 验证 variables 文件
|
|
66
|
+
*/
|
|
67
|
+
function validateVariablesFile(file, errors) {
|
|
68
|
+
const matchArr = Array.from(file.content.matchAll(/^(export\s+)(declare\s+)?(const|let)\s+(\w+)/gm));
|
|
69
|
+
if (matchArr.length === 0) {
|
|
70
|
+
errors.push(`${file.path} (variables文件) 必须包含至少一个 export const 或 export let 声明`);
|
|
71
|
+
}
|
|
72
|
+
for (const match of matchArr) {
|
|
73
|
+
const [, isExport] = match;
|
|
74
|
+
if (!isExport)
|
|
75
|
+
errors.push(`${file.path} 所有声明必须使用 export,错误代码:${match[0]}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 验证 extensions 文件
|
|
80
|
+
*/
|
|
81
|
+
function validateExtensionsFile(file, errors) {
|
|
82
|
+
const matchArr = Array.from(file.content.matchAll(/^(export\s+)(namespace)\s+(\w+)/gm));
|
|
83
|
+
if (matchArr.length === 0) {
|
|
84
|
+
errors.push(`${file.path} (extensions文件) 必须包含至少一个 export namespace 声明`);
|
|
85
|
+
}
|
|
86
|
+
for (const match of matchArr) {
|
|
87
|
+
const [, isExport] = match;
|
|
88
|
+
if (!isExport)
|
|
89
|
+
errors.push(`${file.path} 所有 namespace 必须使用 export,错误代码:${match[0]}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 验证普通文件(枚举、实体、数据结构、逻辑、页面)
|
|
94
|
+
*/
|
|
95
|
+
function validateNormalFile(file, nameFromPath, namespace, errors) {
|
|
96
|
+
const matchArr = Array.from(file.content.matchAll(/^(export\s+)?(declare\s+)?(function|class|interface)\s+(\w+)/gm));
|
|
97
|
+
if (matchArr.length === 0)
|
|
98
|
+
errors.push(`${file.path} 必须有一个函数或类,错误代码:${file.content}`);
|
|
99
|
+
else if (matchArr.length > 1)
|
|
100
|
+
errors.push(`${file.path} 只能有一个函数或类,错误代码:${matchArr.map((match) => match[0]).join('\n')}
|
|
101
|
+
你可以将该文件中所有函数合并,或者将其他函数拆到多个文件中。`);
|
|
102
|
+
for (const match of matchArr) {
|
|
103
|
+
const [, isExport, isDeclare, type, name] = match;
|
|
104
|
+
if (!isExport)
|
|
105
|
+
errors.push(`${file.path} 必须使用 export,错误代码:${match[0]}`);
|
|
106
|
+
if (name !== nameFromPath)
|
|
107
|
+
errors.push(`${file.path} 的函数或类名必须与文件名一致,错误代码:${match[0]}`);
|
|
108
|
+
if (/\.(entities|enums|structures)/.test(namespace) && type !== 'class')
|
|
109
|
+
errors.push(`${file.path} 实体、数据结构和枚举只能使用 class 定义,错误代码:${match[0]}`);
|
|
110
|
+
if (/\.(logics|views)/.test(namespace) && type !== 'function')
|
|
111
|
+
errors.push(`${file.path} 逻辑和页面只能使用 function 定义,错误代码:${match[0]}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
64
114
|
function composeToString(files) {
|
|
65
115
|
files.sort((a, b) => sorter(a.path, b.path));
|
|
66
116
|
const errors = [];
|
|
@@ -68,28 +118,26 @@ function composeToString(files) {
|
|
|
68
118
|
const arr = file.path.split('.');
|
|
69
119
|
const ext = arr.pop();
|
|
70
120
|
const nameFromPath = arr.pop();
|
|
71
|
-
|
|
121
|
+
// 判断是否是特殊文件类型(variables 或 extensions)
|
|
122
|
+
const isVariablesFile = nameFromPath === 'variables';
|
|
123
|
+
const isExtensionsFile = arr[0] === 'extensions';
|
|
124
|
+
const isSpecialFile = isVariablesFile || isExtensionsFile;
|
|
125
|
+
// 特殊文件的 namespace 包含文件名,普通文件不包含
|
|
126
|
+
const namespace = isSpecialFile ? [...arr, nameFromPath].join('.') : arr.join('.');
|
|
72
127
|
if (['ts', 'tsx'].includes(ext)) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
else if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!isExport)
|
|
82
|
-
errors.push(`${file.path} 必须使用 export,错误代码:${match[0]}`);
|
|
83
|
-
if (name !== nameFromPath)
|
|
84
|
-
errors.push(`${file.path} 的函数或类名必须与文件名一致,错误代码:${match[0]}`);
|
|
85
|
-
if (/\.(entities|enums|structures)/.test(namespace) && type !== 'class')
|
|
86
|
-
errors.push(`${file.path} 实体、数据结构和枚举只能使用 class 定义,错误代码:${match[0]}`);
|
|
87
|
-
if (/\.(logics|views)/.test(namespace) && type !== 'function')
|
|
88
|
-
errors.push(`${file.path} 逻辑和页面只能使用 function 定义,错误代码:${match[0]}`);
|
|
128
|
+
if (isVariablesFile) {
|
|
129
|
+
validateVariablesFile(file, errors);
|
|
130
|
+
}
|
|
131
|
+
else if (isExtensionsFile) {
|
|
132
|
+
validateExtensionsFile(file, errors);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
validateNormalFile(file, nameFromPath, namespace, errors);
|
|
89
136
|
}
|
|
90
137
|
}
|
|
91
138
|
return `namespace ${namespace} {\n${file.content}\n}\n`;
|
|
92
|
-
})
|
|
139
|
+
})
|
|
140
|
+
.join('\n');
|
|
93
141
|
if (errors.length > 0) {
|
|
94
142
|
throw new Error(errors.join('\n============\n'));
|
|
95
143
|
}
|
|
@@ -13586,26 +13634,29 @@ function loadConfig(configDir) {
|
|
|
13586
13634
|
defaultLogger.error('配置文件格式不正确,缺少必需的 srcDir 或 outDir 字段');
|
|
13587
13635
|
return defaultLogger.exit(1);
|
|
13588
13636
|
}
|
|
13637
|
+
// 环境变量优先级高于配置文件
|
|
13638
|
+
// ideVersion: 环境变量优先,如果都没有则报错
|
|
13639
|
+
if (process.env.NASL_IDE_VERSION)
|
|
13640
|
+
config.ideVersion = process.env.NASL_IDE_VERSION;
|
|
13589
13641
|
if (!config.ideVersion) {
|
|
13590
|
-
|
|
13591
|
-
|
|
13592
|
-
defaultLogger.error('缺少配置 ideVersion,请在配置文件中添加,或在环境变量中配置 NASL_IDE_VERSION');
|
|
13593
|
-
return defaultLogger.exit(1);
|
|
13594
|
-
}
|
|
13642
|
+
defaultLogger.error('缺少配置 ideVersion,请在配置文件中添加,或在环境变量中配置 NASL_IDE_VERSION');
|
|
13643
|
+
return defaultLogger.exit(1);
|
|
13595
13644
|
}
|
|
13645
|
+
// serverBaseURL: 环境变量优先,如果都没有则报错
|
|
13646
|
+
if (process.env.NASL_SERVER_BASE_URL)
|
|
13647
|
+
config.serverBaseURL = process.env.NASL_SERVER_BASE_URL;
|
|
13596
13648
|
if (!config.serverBaseURL) {
|
|
13597
|
-
|
|
13598
|
-
|
|
13599
|
-
defaultLogger.error('缺少配置 serverBaseURL,请在配置文件中添加,或在环境变量中配置 NASL_SERVER_BASE_URL');
|
|
13600
|
-
return defaultLogger.exit(1);
|
|
13601
|
-
}
|
|
13649
|
+
defaultLogger.error('缺少配置 serverBaseURL,请在配置文件中添加,或在环境变量中配置 NASL_SERVER_BASE_URL');
|
|
13650
|
+
return defaultLogger.exit(1);
|
|
13602
13651
|
}
|
|
13603
|
-
|
|
13652
|
+
// 环境变量优先
|
|
13653
|
+
if (process.env.USE_LCAP_OPENAPI !== undefined)
|
|
13604
13654
|
config.useOPENAPI = process.env.USE_LCAP_OPENAPI === 'true';
|
|
13605
|
-
if (
|
|
13655
|
+
if (process.env.LCAP_OPENAPI_AK)
|
|
13606
13656
|
config.OPENAPI_AK = process.env.LCAP_OPENAPI_AK;
|
|
13607
|
-
if (
|
|
13657
|
+
if (process.env.LCAP_OPENAPI_SK)
|
|
13608
13658
|
config.OPENAPI_SK = process.env.LCAP_OPENAPI_SK;
|
|
13659
|
+
// 如果启用了 OpenAPI,验证必需的 AK 和 SK
|
|
13609
13660
|
if (config.useOPENAPI) {
|
|
13610
13661
|
if (!config.OPENAPI_AK || !config.OPENAPI_SK) {
|
|
13611
13662
|
defaultLogger.error(`配置了 useOPENAPI,但缺少配置 OPENAPI_AK 和 OPENAPI_SK:
|
|
@@ -13660,6 +13711,23 @@ function writeFileWithLog(filePath, content, logger) {
|
|
|
13660
13711
|
throw error;
|
|
13661
13712
|
}
|
|
13662
13713
|
}
|
|
13714
|
+
/**
|
|
13715
|
+
* 判断文件路径是否为已知的 NASL 文件类型
|
|
13716
|
+
* 支持的类型:
|
|
13717
|
+
* - 枚举 (enums)
|
|
13718
|
+
* - 实体 (entities)
|
|
13719
|
+
* - 数据结构 (structures)
|
|
13720
|
+
* - 逻辑 (logics)
|
|
13721
|
+
* - 页面 (views)
|
|
13722
|
+
* - 前端/服务端全局变量 (variables)
|
|
13723
|
+
* - 依赖库 (extensions)
|
|
13724
|
+
*/
|
|
13725
|
+
function isKnownFileType(filePath) {
|
|
13726
|
+
return (/\.(enums|entities|structures|logics|views)\.\w+\.(ts|tsx)$/.test(filePath) || // 枚举、实体、数据结构、逻辑、页面
|
|
13727
|
+
/^app\..*\.variables\.ts$/.test(filePath) || // 前端/服务端全局变量 (以app开头,以variables.ts结尾)
|
|
13728
|
+
/^extensions\.\w+\.(ts|tsx)$/.test(filePath) // 依赖库
|
|
13729
|
+
);
|
|
13730
|
+
}
|
|
13663
13731
|
|
|
13664
13732
|
/**
|
|
13665
13733
|
* 扫描目录下的所有 NASL 文件
|
|
@@ -13701,11 +13769,13 @@ async function scanEntryFiles(projectRoot, patterns, logger) {
|
|
|
13701
13769
|
*/
|
|
13702
13770
|
function extractDeps(content) {
|
|
13703
13771
|
const deps = new Set();
|
|
13704
|
-
//
|
|
13772
|
+
// 预处理:移除注释和字符串字面量,避免它们影响依赖分析
|
|
13705
13773
|
let processedContent = content
|
|
13706
13774
|
.replace(/\/\/.*$/gm, '') // 移除单行注释 // ...
|
|
13707
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
13708
|
-
|
|
13775
|
+
.replace(/\/\*[\s\S]*?\*\//g, '') // 移除多行注释 /* ... */
|
|
13776
|
+
.replace(/'(?:[^'\\]|\\.)*'/g, '') // 移除单引号字符串 'xxx'
|
|
13777
|
+
.replace(/"(?:[^"\\]|\\.)*"/g, ''); // 移除双引号字符串 "xxx"
|
|
13778
|
+
const allMatches = processedContent.matchAll(/(app|extensions)\.\w+\.[\w.]+/g); // 起码要2个点,支持 app.* 和 extensions.*
|
|
13709
13779
|
for (const match of allMatches) {
|
|
13710
13780
|
let dep = match[0];
|
|
13711
13781
|
if (/Entity$|Entity\./.test(dep)) {
|
|
@@ -13714,6 +13784,14 @@ function extractDeps(content) {
|
|
|
13714
13784
|
else if (/\.enums\.(\w+)\.(\w+)/.test(dep)) {
|
|
13715
13785
|
dep = dep.replace(/\.enums\.(\w+).+$/, '.enums.$1');
|
|
13716
13786
|
}
|
|
13787
|
+
else if (/\.variables\.(\w+)/.test(dep)) {
|
|
13788
|
+
// 处理 variables 依赖:app.backend.variables.xxx -> app.backend.variables
|
|
13789
|
+
dep = dep.replace(/\.variables\..+$/, '.variables');
|
|
13790
|
+
}
|
|
13791
|
+
else if (/^extensions\.\w+\.\w+/.test(dep)) {
|
|
13792
|
+
// 处理 extensions 依赖:extensions.lcap_auth.xxx -> extensions.lcap_auth
|
|
13793
|
+
dep = dep.replace(/^(extensions\.\w+)\..+$/, '$1');
|
|
13794
|
+
}
|
|
13717
13795
|
else if (/app\.dataSources\.\w+\.entities\.\w+\.\w+$/.test(dep)) {
|
|
13718
13796
|
continue; // 应该是写法有问题,跳过让后面的 checker 做检查
|
|
13719
13797
|
}
|
|
@@ -13835,7 +13913,12 @@ async function collectDeps(patterns, projectRoot, srcDir, logger, verbose) {
|
|
|
13835
13913
|
while (filesToProcess.length > 0) {
|
|
13836
13914
|
const pathRelativeToSrc = filesToProcess.shift();
|
|
13837
13915
|
if (processedFileMap.has(pathRelativeToSrc))
|
|
13838
|
-
continue; // 跳过已处理的文件
|
|
13916
|
+
continue; // 跳过已处理的文件
|
|
13917
|
+
// 检查文件类型是否支持
|
|
13918
|
+
if (!isKnownFileType(pathRelativeToSrc)) {
|
|
13919
|
+
logger.warn(`跳过不支持的文件类型: ${pathRelativeToSrc}`);
|
|
13920
|
+
continue;
|
|
13921
|
+
}
|
|
13839
13922
|
try {
|
|
13840
13923
|
const { fileInfo, newDeps } = processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFileMap, depNotFoundList, logger, verbose);
|
|
13841
13924
|
result.push(fileInfo);
|
|
@@ -13915,8 +13998,18 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
|
|
|
13915
13998
|
}
|
|
13916
13999
|
logger.info(`找到 ${collectedFiles.length} 个 NASL 文件`);
|
|
13917
14000
|
}
|
|
14001
|
+
// 统一过滤掉不支持的文件类型
|
|
14002
|
+
const filteredFiles = [];
|
|
14003
|
+
collectedFiles.forEach((file) => {
|
|
14004
|
+
if (isKnownFileType(file.path)) {
|
|
14005
|
+
filteredFiles.push(file);
|
|
14006
|
+
}
|
|
14007
|
+
else {
|
|
14008
|
+
logger.warn(`跳过不支持的文件类型: ${file.path}`);
|
|
14009
|
+
}
|
|
14010
|
+
});
|
|
13918
14011
|
return {
|
|
13919
|
-
collectedFiles,
|
|
14012
|
+
collectedFiles: filteredFiles,
|
|
13920
14013
|
config,
|
|
13921
14014
|
projectRoot,
|
|
13922
14015
|
srcDir,
|