@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.
@@ -8308,26 +8308,29 @@ function loadConfig(configDir) {
8308
8308
  defaultLogger.error('配置文件格式不正确,缺少必需的 srcDir 或 outDir 字段');
8309
8309
  return defaultLogger.exit(1);
8310
8310
  }
8311
+ // 环境变量优先级高于配置文件
8312
+ // ideVersion: 环境变量优先,如果都没有则报错
8313
+ if (process.env.NASL_IDE_VERSION)
8314
+ config.ideVersion = process.env.NASL_IDE_VERSION;
8311
8315
  if (!config.ideVersion) {
8312
- config.ideVersion = process.env.NASL_IDE_VERSION || '';
8313
- if (!config.ideVersion) {
8314
- defaultLogger.error('缺少配置 ideVersion,请在配置文件中添加,或在环境变量中配置 NASL_IDE_VERSION');
8315
- return defaultLogger.exit(1);
8316
- }
8316
+ defaultLogger.error('缺少配置 ideVersion,请在配置文件中添加,或在环境变量中配置 NASL_IDE_VERSION');
8317
+ return defaultLogger.exit(1);
8317
8318
  }
8319
+ // serverBaseURL: 环境变量优先,如果都没有则报错
8320
+ if (process.env.NASL_SERVER_BASE_URL)
8321
+ config.serverBaseURL = process.env.NASL_SERVER_BASE_URL;
8318
8322
  if (!config.serverBaseURL) {
8319
- config.serverBaseURL = process.env.NASL_SERVER_BASE_URL || '';
8320
- if (!config.serverBaseURL) {
8321
- defaultLogger.error('缺少配置 serverBaseURL,请在配置文件中添加,或在环境变量中配置 NASL_SERVER_BASE_URL');
8322
- return defaultLogger.exit(1);
8323
- }
8323
+ defaultLogger.error('缺少配置 serverBaseURL,请在配置文件中添加,或在环境变量中配置 NASL_SERVER_BASE_URL');
8324
+ return defaultLogger.exit(1);
8324
8325
  }
8325
- if (!config.useOPENAPI)
8326
+ // 环境变量优先
8327
+ if (process.env.USE_LCAP_OPENAPI !== undefined)
8326
8328
  config.useOPENAPI = process.env.USE_LCAP_OPENAPI === 'true';
8327
- if (!config.OPENAPI_AK)
8329
+ if (process.env.LCAP_OPENAPI_AK)
8328
8330
  config.OPENAPI_AK = process.env.LCAP_OPENAPI_AK;
8329
- if (!config.OPENAPI_SK)
8331
+ if (process.env.LCAP_OPENAPI_SK)
8330
8332
  config.OPENAPI_SK = process.env.LCAP_OPENAPI_SK;
8333
+ // 如果启用了 OpenAPI,验证必需的 AK 和 SK
8331
8334
  if (config.useOPENAPI) {
8332
8335
  if (!config.OPENAPI_AK || !config.OPENAPI_SK) {
8333
8336
  defaultLogger.error(`配置了 useOPENAPI,但缺少配置 OPENAPI_AK 和 OPENAPI_SK:
@@ -8382,6 +8385,23 @@ function writeFileWithLog(filePath, content, logger) {
8382
8385
  throw error;
8383
8386
  }
8384
8387
  }
8388
+ /**
8389
+ * 判断文件路径是否为已知的 NASL 文件类型
8390
+ * 支持的类型:
8391
+ * - 枚举 (enums)
8392
+ * - 实体 (entities)
8393
+ * - 数据结构 (structures)
8394
+ * - 逻辑 (logics)
8395
+ * - 页面 (views)
8396
+ * - 前端/服务端全局变量 (variables)
8397
+ * - 依赖库 (extensions)
8398
+ */
8399
+ function isKnownFileType(filePath) {
8400
+ return (/\.(enums|entities|structures|logics|views)\.\w+\.(ts|tsx)$/.test(filePath) || // 枚举、实体、数据结构、逻辑、页面
8401
+ /^app\..*\.variables\.ts$/.test(filePath) || // 前端/服务端全局变量 (以app开头,以variables.ts结尾)
8402
+ /^extensions\.\w+\.(ts|tsx)$/.test(filePath) // 依赖库
8403
+ );
8404
+ }
8385
8405
 
8386
8406
  /**
8387
8407
  * Create a bound version of a function with a specified `this` context
@@ -29142,6 +29162,56 @@ function createSorter() {
29142
29162
  };
29143
29163
  }
29144
29164
  const sorter = createSorter();
29165
+ /**
29166
+ * 验证 variables 文件
29167
+ */
29168
+ function validateVariablesFile(file, errors) {
29169
+ const matchArr = Array.from(file.content.matchAll(/^(export\s+)(declare\s+)?(const|let)\s+(\w+)/gm));
29170
+ if (matchArr.length === 0) {
29171
+ errors.push(`${file.path} (variables文件) 必须包含至少一个 export const 或 export let 声明`);
29172
+ }
29173
+ for (const match of matchArr) {
29174
+ const [, isExport] = match;
29175
+ if (!isExport)
29176
+ errors.push(`${file.path} 所有声明必须使用 export,错误代码:${match[0]}`);
29177
+ }
29178
+ }
29179
+ /**
29180
+ * 验证 extensions 文件
29181
+ */
29182
+ function validateExtensionsFile(file, errors) {
29183
+ const matchArr = Array.from(file.content.matchAll(/^(export\s+)(namespace)\s+(\w+)/gm));
29184
+ if (matchArr.length === 0) {
29185
+ errors.push(`${file.path} (extensions文件) 必须包含至少一个 export namespace 声明`);
29186
+ }
29187
+ for (const match of matchArr) {
29188
+ const [, isExport] = match;
29189
+ if (!isExport)
29190
+ errors.push(`${file.path} 所有 namespace 必须使用 export,错误代码:${match[0]}`);
29191
+ }
29192
+ }
29193
+ /**
29194
+ * 验证普通文件(枚举、实体、数据结构、逻辑、页面)
29195
+ */
29196
+ function validateNormalFile(file, nameFromPath, namespace, errors) {
29197
+ const matchArr = Array.from(file.content.matchAll(/^(export\s+)?(declare\s+)?(function|class|interface)\s+(\w+)/gm));
29198
+ if (matchArr.length === 0)
29199
+ errors.push(`${file.path} 必须有一个函数或类,错误代码:${file.content}`);
29200
+ else if (matchArr.length > 1)
29201
+ errors.push(`${file.path} 只能有一个函数或类,错误代码:${matchArr.map((match) => match[0]).join('\n')}
29202
+ 你可以将该文件中所有函数合并,或者将其他函数拆到多个文件中。`);
29203
+ for (const match of matchArr) {
29204
+ const [, isExport, isDeclare, type, name] = match;
29205
+ if (!isExport)
29206
+ errors.push(`${file.path} 必须使用 export,错误代码:${match[0]}`);
29207
+ if (name !== nameFromPath)
29208
+ errors.push(`${file.path} 的函数或类名必须与文件名一致,错误代码:${match[0]}`);
29209
+ if (/\.(entities|enums|structures)/.test(namespace) && type !== 'class')
29210
+ errors.push(`${file.path} 实体、数据结构和枚举只能使用 class 定义,错误代码:${match[0]}`);
29211
+ if (/\.(logics|views)/.test(namespace) && type !== 'function')
29212
+ errors.push(`${file.path} 逻辑和页面只能使用 function 定义,错误代码:${match[0]}`);
29213
+ }
29214
+ }
29145
29215
  function composeToString(files) {
29146
29216
  files.sort((a, b) => sorter(a.path, b.path));
29147
29217
  const errors = [];
@@ -29149,28 +29219,26 @@ function composeToString(files) {
29149
29219
  const arr = file.path.split('.');
29150
29220
  const ext = arr.pop();
29151
29221
  const nameFromPath = arr.pop();
29152
- const namespace = arr.join('.');
29222
+ // 判断是否是特殊文件类型(variables extensions)
29223
+ const isVariablesFile = nameFromPath === 'variables';
29224
+ const isExtensionsFile = arr[0] === 'extensions';
29225
+ const isSpecialFile = isVariablesFile || isExtensionsFile;
29226
+ // 特殊文件的 namespace 包含文件名,普通文件不包含
29227
+ const namespace = isSpecialFile ? [...arr, nameFromPath].join('.') : arr.join('.');
29153
29228
  if (['ts', 'tsx'].includes(ext)) {
29154
- const matchArr = Array.from(file.content.matchAll(/^(export\s+)?(declare\s+)?(function|class|interface)\s+(\w+)/gm));
29155
- if (matchArr.length === 0)
29156
- errors.push(`${file.path} 必须有一个函数或类,错误代码:${file.content}`);
29157
- else if (matchArr.length > 1)
29158
- errors.push(`${file.path} 只能有一个函数或类,错误代码:${matchArr.map((match) => match[0]).join('\n')}
29159
- 你可以将该文件中所有函数合并,或者将其他函数拆到多个文件中。`);
29160
- for (const match of matchArr) {
29161
- const [, isExport, isDeclare, type, name] = match;
29162
- if (!isExport)
29163
- errors.push(`${file.path} 必须使用 export,错误代码:${match[0]}`);
29164
- if (name !== nameFromPath)
29165
- errors.push(`${file.path} 的函数或类名必须与文件名一致,错误代码:${match[0]}`);
29166
- if (/\.(entities|enums|structures)/.test(namespace) && type !== 'class')
29167
- errors.push(`${file.path} 实体、数据结构和枚举只能使用 class 定义,错误代码:${match[0]}`);
29168
- if (/\.(logics|views)/.test(namespace) && type !== 'function')
29169
- errors.push(`${file.path} 逻辑和页面只能使用 function 定义,错误代码:${match[0]}`);
29229
+ if (isVariablesFile) {
29230
+ validateVariablesFile(file, errors);
29231
+ }
29232
+ else if (isExtensionsFile) {
29233
+ validateExtensionsFile(file, errors);
29234
+ }
29235
+ else {
29236
+ validateNormalFile(file, nameFromPath, namespace, errors);
29170
29237
  }
29171
29238
  }
29172
29239
  return `namespace ${namespace} {\n${file.content}\n}\n`;
29173
- }).join('\n');
29240
+ })
29241
+ .join('\n');
29174
29242
  if (errors.length > 0) {
29175
29243
  throw new Error(errors.join('\n============\n'));
29176
29244
  }
@@ -37731,11 +37799,13 @@ async function scanEntryFiles(projectRoot, patterns, logger) {
37731
37799
  */
37732
37800
  function extractDeps(content) {
37733
37801
  const deps = new Set();
37734
- // 预处理:移除注释,避免注释影响依赖分析
37802
+ // 预处理:移除注释和字符串字面量,避免它们影响依赖分析
37735
37803
  let processedContent = content
37736
37804
  .replace(/\/\/.*$/gm, '') // 移除单行注释 // ...
37737
- .replace(/\/\*[\s\S]*?\*\//g, ''); // 移除多行注释 /* ... */
37738
- const allMatches = processedContent.matchAll(/app\.\w+\.[\w.]+/g); // 起码要2个点
37805
+ .replace(/\/\*[\s\S]*?\*\//g, '') // 移除多行注释 /* ... */
37806
+ .replace(/'(?:[^'\\]|\\.)*'/g, '') // 移除单引号字符串 'xxx'
37807
+ .replace(/"(?:[^"\\]|\\.)*"/g, ''); // 移除双引号字符串 "xxx"
37808
+ const allMatches = processedContent.matchAll(/(app|extensions)\.\w+\.[\w.]+/g); // 起码要2个点,支持 app.* 和 extensions.*
37739
37809
  for (const match of allMatches) {
37740
37810
  let dep = match[0];
37741
37811
  if (/Entity$|Entity\./.test(dep)) {
@@ -37744,6 +37814,14 @@ function extractDeps(content) {
37744
37814
  else if (/\.enums\.(\w+)\.(\w+)/.test(dep)) {
37745
37815
  dep = dep.replace(/\.enums\.(\w+).+$/, '.enums.$1');
37746
37816
  }
37817
+ else if (/\.variables\.(\w+)/.test(dep)) {
37818
+ // 处理 variables 依赖:app.backend.variables.xxx -> app.backend.variables
37819
+ dep = dep.replace(/\.variables\..+$/, '.variables');
37820
+ }
37821
+ else if (/^extensions\.\w+\.\w+/.test(dep)) {
37822
+ // 处理 extensions 依赖:extensions.lcap_auth.xxx -> extensions.lcap_auth
37823
+ dep = dep.replace(/^(extensions\.\w+)\..+$/, '$1');
37824
+ }
37747
37825
  else if (/app\.dataSources\.\w+\.entities\.\w+\.\w+$/.test(dep)) {
37748
37826
  continue; // 应该是写法有问题,跳过让后面的 checker 做检查
37749
37827
  }
@@ -37865,7 +37943,12 @@ async function collectDeps(patterns, projectRoot, srcDir, logger, verbose) {
37865
37943
  while (filesToProcess.length > 0) {
37866
37944
  const pathRelativeToSrc = filesToProcess.shift();
37867
37945
  if (processedFileMap.has(pathRelativeToSrc))
37868
- continue; // 跳过已处理的文件
37946
+ continue; // 跳过已处理的文件
37947
+ // 检查文件类型是否支持
37948
+ if (!isKnownFileType(pathRelativeToSrc)) {
37949
+ logger.warn(`跳过不支持的文件类型: ${pathRelativeToSrc}`);
37950
+ continue;
37951
+ }
37869
37952
  try {
37870
37953
  const { fileInfo, newDeps } = processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFileMap, depNotFoundList, logger, verbose);
37871
37954
  result.push(fileInfo);
@@ -37943,8 +38026,18 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
37943
38026
  }
37944
38027
  logger.info(`找到 ${collectedFiles.length} 个 NASL 文件`);
37945
38028
  }
38029
+ // 统一过滤掉不支持的文件类型
38030
+ const filteredFiles = [];
38031
+ collectedFiles.forEach((file) => {
38032
+ if (isKnownFileType(file.path)) {
38033
+ filteredFiles.push(file);
38034
+ }
38035
+ else {
38036
+ logger.warn(`跳过不支持的文件类型: ${file.path}`);
38037
+ }
38038
+ });
37946
38039
  return {
37947
- collectedFiles,
38040
+ collectedFiles: filteredFiles,
37948
38041
  config,
37949
38042
  projectRoot,
37950
38043
  srcDir,
@@ -37993,7 +38086,7 @@ async function tryCompile(entry, options) {
37993
38086
  }
37994
38087
  }
37995
38088
 
37996
- var version = "0.1.18";
38089
+ var version = "0.1.19";
37997
38090
  var pkg = {
37998
38091
  version: version};
37999
38092