@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 CHANGED
@@ -164,7 +164,9 @@ nasl create-app-in-ide [entry] --verbose
164
164
 
165
165
  ## 环境变量配置
166
166
 
167
- 除了在 `nasl.config.json` 中配置外,也可以通过环境变量来配置部分选项。环境变量的优先级低于配置文件。
167
+ 除了在 `nasl.config.json` 中配置外,也可以通过环境变量来配置部分选项。
168
+
169
+ **环境变量的优先级高于配置文件**,如果同时设置了环境变量和配置文件,将使用环境变量的值。
168
170
 
169
171
  ### 支持的环境变量
170
172
 
package/dist/bin/nasl.mjs CHANGED
@@ -8342,26 +8342,29 @@ function loadConfig(configDir) {
8342
8342
  defaultLogger.error('配置文件格式不正确,缺少必需的 srcDir 或 outDir 字段');
8343
8343
  return defaultLogger.exit(1);
8344
8344
  }
8345
+ // 环境变量优先级高于配置文件
8346
+ // ideVersion: 环境变量优先,如果都没有则报错
8347
+ if (process.env.NASL_IDE_VERSION)
8348
+ config.ideVersion = process.env.NASL_IDE_VERSION;
8345
8349
  if (!config.ideVersion) {
8346
- config.ideVersion = process.env.NASL_IDE_VERSION || '';
8347
- if (!config.ideVersion) {
8348
- defaultLogger.error('缺少配置 ideVersion,请在配置文件中添加,或在环境变量中配置 NASL_IDE_VERSION');
8349
- return defaultLogger.exit(1);
8350
- }
8350
+ defaultLogger.error('缺少配置 ideVersion,请在配置文件中添加,或在环境变量中配置 NASL_IDE_VERSION');
8351
+ return defaultLogger.exit(1);
8351
8352
  }
8353
+ // serverBaseURL: 环境变量优先,如果都没有则报错
8354
+ if (process.env.NASL_SERVER_BASE_URL)
8355
+ config.serverBaseURL = process.env.NASL_SERVER_BASE_URL;
8352
8356
  if (!config.serverBaseURL) {
8353
- config.serverBaseURL = process.env.NASL_SERVER_BASE_URL || '';
8354
- if (!config.serverBaseURL) {
8355
- defaultLogger.error('缺少配置 serverBaseURL,请在配置文件中添加,或在环境变量中配置 NASL_SERVER_BASE_URL');
8356
- return defaultLogger.exit(1);
8357
- }
8357
+ defaultLogger.error('缺少配置 serverBaseURL,请在配置文件中添加,或在环境变量中配置 NASL_SERVER_BASE_URL');
8358
+ return defaultLogger.exit(1);
8358
8359
  }
8359
- if (!config.useOPENAPI)
8360
+ // 环境变量优先
8361
+ if (process.env.USE_LCAP_OPENAPI !== undefined)
8360
8362
  config.useOPENAPI = process.env.USE_LCAP_OPENAPI === 'true';
8361
- if (!config.OPENAPI_AK)
8363
+ if (process.env.LCAP_OPENAPI_AK)
8362
8364
  config.OPENAPI_AK = process.env.LCAP_OPENAPI_AK;
8363
- if (!config.OPENAPI_SK)
8365
+ if (process.env.LCAP_OPENAPI_SK)
8364
8366
  config.OPENAPI_SK = process.env.LCAP_OPENAPI_SK;
8367
+ // 如果启用了 OpenAPI,验证必需的 AK 和 SK
8365
8368
  if (config.useOPENAPI) {
8366
8369
  if (!config.OPENAPI_AK || !config.OPENAPI_SK) {
8367
8370
  defaultLogger.error(`配置了 useOPENAPI,但缺少配置 OPENAPI_AK 和 OPENAPI_SK:
@@ -8416,6 +8419,23 @@ function writeFileWithLog(filePath, content, logger) {
8416
8419
  throw error;
8417
8420
  }
8418
8421
  }
8422
+ /**
8423
+ * 判断文件路径是否为已知的 NASL 文件类型
8424
+ * 支持的类型:
8425
+ * - 枚举 (enums)
8426
+ * - 实体 (entities)
8427
+ * - 数据结构 (structures)
8428
+ * - 逻辑 (logics)
8429
+ * - 页面 (views)
8430
+ * - 前端/服务端全局变量 (variables)
8431
+ * - 依赖库 (extensions)
8432
+ */
8433
+ function isKnownFileType(filePath) {
8434
+ return (/\.(enums|entities|structures|logics|views)\.\w+\.(ts|tsx)$/.test(filePath) || // 枚举、实体、数据结构、逻辑、页面
8435
+ /^app\..*\.variables\.ts$/.test(filePath) || // 前端/服务端全局变量 (以app开头,以variables.ts结尾)
8436
+ /^extensions\.\w+\.(ts|tsx)$/.test(filePath) // 依赖库
8437
+ );
8438
+ }
8419
8439
 
8420
8440
  /**
8421
8441
  * 初始化命令 - 创建配置文件
@@ -29269,6 +29289,56 @@ function createSorter() {
29269
29289
  };
29270
29290
  }
29271
29291
  const sorter = createSorter();
29292
+ /**
29293
+ * 验证 variables 文件
29294
+ */
29295
+ function validateVariablesFile(file, errors) {
29296
+ const matchArr = Array.from(file.content.matchAll(/^(export\s+)(declare\s+)?(const|let)\s+(\w+)/gm));
29297
+ if (matchArr.length === 0) {
29298
+ errors.push(`${file.path} (variables文件) 必须包含至少一个 export const 或 export let 声明`);
29299
+ }
29300
+ for (const match of matchArr) {
29301
+ const [, isExport] = match;
29302
+ if (!isExport)
29303
+ errors.push(`${file.path} 所有声明必须使用 export,错误代码:${match[0]}`);
29304
+ }
29305
+ }
29306
+ /**
29307
+ * 验证 extensions 文件
29308
+ */
29309
+ function validateExtensionsFile(file, errors) {
29310
+ const matchArr = Array.from(file.content.matchAll(/^(export\s+)(namespace)\s+(\w+)/gm));
29311
+ if (matchArr.length === 0) {
29312
+ errors.push(`${file.path} (extensions文件) 必须包含至少一个 export namespace 声明`);
29313
+ }
29314
+ for (const match of matchArr) {
29315
+ const [, isExport] = match;
29316
+ if (!isExport)
29317
+ errors.push(`${file.path} 所有 namespace 必须使用 export,错误代码:${match[0]}`);
29318
+ }
29319
+ }
29320
+ /**
29321
+ * 验证普通文件(枚举、实体、数据结构、逻辑、页面)
29322
+ */
29323
+ function validateNormalFile(file, nameFromPath, namespace, errors) {
29324
+ const matchArr = Array.from(file.content.matchAll(/^(export\s+)?(declare\s+)?(function|class|interface)\s+(\w+)/gm));
29325
+ if (matchArr.length === 0)
29326
+ errors.push(`${file.path} 必须有一个函数或类,错误代码:${file.content}`);
29327
+ else if (matchArr.length > 1)
29328
+ errors.push(`${file.path} 只能有一个函数或类,错误代码:${matchArr.map((match) => match[0]).join('\n')}
29329
+ 你可以将该文件中所有函数合并,或者将其他函数拆到多个文件中。`);
29330
+ for (const match of matchArr) {
29331
+ const [, isExport, isDeclare, type, name] = match;
29332
+ if (!isExport)
29333
+ errors.push(`${file.path} 必须使用 export,错误代码:${match[0]}`);
29334
+ if (name !== nameFromPath)
29335
+ errors.push(`${file.path} 的函数或类名必须与文件名一致,错误代码:${match[0]}`);
29336
+ if (/\.(entities|enums|structures)/.test(namespace) && type !== 'class')
29337
+ errors.push(`${file.path} 实体、数据结构和枚举只能使用 class 定义,错误代码:${match[0]}`);
29338
+ if (/\.(logics|views)/.test(namespace) && type !== 'function')
29339
+ errors.push(`${file.path} 逻辑和页面只能使用 function 定义,错误代码:${match[0]}`);
29340
+ }
29341
+ }
29272
29342
  function composeToString(files) {
29273
29343
  files.sort((a, b) => sorter(a.path, b.path));
29274
29344
  const errors = [];
@@ -29276,28 +29346,26 @@ function composeToString(files) {
29276
29346
  const arr = file.path.split('.');
29277
29347
  const ext = arr.pop();
29278
29348
  const nameFromPath = arr.pop();
29279
- const namespace = arr.join('.');
29349
+ // 判断是否是特殊文件类型(variables extensions)
29350
+ const isVariablesFile = nameFromPath === 'variables';
29351
+ const isExtensionsFile = arr[0] === 'extensions';
29352
+ const isSpecialFile = isVariablesFile || isExtensionsFile;
29353
+ // 特殊文件的 namespace 包含文件名,普通文件不包含
29354
+ const namespace = isSpecialFile ? [...arr, nameFromPath].join('.') : arr.join('.');
29280
29355
  if (['ts', 'tsx'].includes(ext)) {
29281
- const matchArr = Array.from(file.content.matchAll(/^(export\s+)?(declare\s+)?(function|class|interface)\s+(\w+)/gm));
29282
- if (matchArr.length === 0)
29283
- errors.push(`${file.path} 必须有一个函数或类,错误代码:${file.content}`);
29284
- else if (matchArr.length > 1)
29285
- errors.push(`${file.path} 只能有一个函数或类,错误代码:${matchArr.map((match) => match[0]).join('\n')}
29286
- 你可以将该文件中所有函数合并,或者将其他函数拆到多个文件中。`);
29287
- for (const match of matchArr) {
29288
- const [, isExport, isDeclare, type, name] = match;
29289
- if (!isExport)
29290
- errors.push(`${file.path} 必须使用 export,错误代码:${match[0]}`);
29291
- if (name !== nameFromPath)
29292
- errors.push(`${file.path} 的函数或类名必须与文件名一致,错误代码:${match[0]}`);
29293
- if (/\.(entities|enums|structures)/.test(namespace) && type !== 'class')
29294
- errors.push(`${file.path} 实体、数据结构和枚举只能使用 class 定义,错误代码:${match[0]}`);
29295
- if (/\.(logics|views)/.test(namespace) && type !== 'function')
29296
- errors.push(`${file.path} 逻辑和页面只能使用 function 定义,错误代码:${match[0]}`);
29356
+ if (isVariablesFile) {
29357
+ validateVariablesFile(file, errors);
29358
+ }
29359
+ else if (isExtensionsFile) {
29360
+ validateExtensionsFile(file, errors);
29361
+ }
29362
+ else {
29363
+ validateNormalFile(file, nameFromPath, namespace, errors);
29297
29364
  }
29298
29365
  }
29299
29366
  return `namespace ${namespace} {\n${file.content}\n}\n`;
29300
- }).join('\n');
29367
+ })
29368
+ .join('\n');
29301
29369
  if (errors.length > 0) {
29302
29370
  throw new Error(errors.join('\n============\n'));
29303
29371
  }
@@ -37858,11 +37926,13 @@ async function scanEntryFiles(projectRoot, patterns, logger) {
37858
37926
  */
37859
37927
  function extractDeps(content) {
37860
37928
  const deps = new Set();
37861
- // 预处理:移除注释,避免注释影响依赖分析
37929
+ // 预处理:移除注释和字符串字面量,避免它们影响依赖分析
37862
37930
  let processedContent = content
37863
37931
  .replace(/\/\/.*$/gm, '') // 移除单行注释 // ...
37864
- .replace(/\/\*[\s\S]*?\*\//g, ''); // 移除多行注释 /* ... */
37865
- const allMatches = processedContent.matchAll(/app\.\w+\.[\w.]+/g); // 起码要2个点
37932
+ .replace(/\/\*[\s\S]*?\*\//g, '') // 移除多行注释 /* ... */
37933
+ .replace(/'(?:[^'\\]|\\.)*'/g, '') // 移除单引号字符串 'xxx'
37934
+ .replace(/"(?:[^"\\]|\\.)*"/g, ''); // 移除双引号字符串 "xxx"
37935
+ const allMatches = processedContent.matchAll(/(app|extensions)\.\w+\.[\w.]+/g); // 起码要2个点,支持 app.* 和 extensions.*
37866
37936
  for (const match of allMatches) {
37867
37937
  let dep = match[0];
37868
37938
  if (/Entity$|Entity\./.test(dep)) {
@@ -37871,6 +37941,14 @@ function extractDeps(content) {
37871
37941
  else if (/\.enums\.(\w+)\.(\w+)/.test(dep)) {
37872
37942
  dep = dep.replace(/\.enums\.(\w+).+$/, '.enums.$1');
37873
37943
  }
37944
+ else if (/\.variables\.(\w+)/.test(dep)) {
37945
+ // 处理 variables 依赖:app.backend.variables.xxx -> app.backend.variables
37946
+ dep = dep.replace(/\.variables\..+$/, '.variables');
37947
+ }
37948
+ else if (/^extensions\.\w+\.\w+/.test(dep)) {
37949
+ // 处理 extensions 依赖:extensions.lcap_auth.xxx -> extensions.lcap_auth
37950
+ dep = dep.replace(/^(extensions\.\w+)\..+$/, '$1');
37951
+ }
37874
37952
  else if (/app\.dataSources\.\w+\.entities\.\w+\.\w+$/.test(dep)) {
37875
37953
  continue; // 应该是写法有问题,跳过让后面的 checker 做检查
37876
37954
  }
@@ -37992,7 +38070,12 @@ async function collectDeps(patterns, projectRoot, srcDir, logger, verbose) {
37992
38070
  while (filesToProcess.length > 0) {
37993
38071
  const pathRelativeToSrc = filesToProcess.shift();
37994
38072
  if (processedFileMap.has(pathRelativeToSrc))
37995
- continue; // 跳过已处理的文件
38073
+ continue; // 跳过已处理的文件
38074
+ // 检查文件类型是否支持
38075
+ if (!isKnownFileType(pathRelativeToSrc)) {
38076
+ logger.warn(`跳过不支持的文件类型: ${pathRelativeToSrc}`);
38077
+ continue;
38078
+ }
37996
38079
  try {
37997
38080
  const { fileInfo, newDeps } = processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFileMap, depNotFoundList, logger, verbose);
37998
38081
  result.push(fileInfo);
@@ -38072,8 +38155,18 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
38072
38155
  }
38073
38156
  logger.info(`找到 ${collectedFiles.length} 个 NASL 文件`);
38074
38157
  }
38158
+ // 统一过滤掉不支持的文件类型
38159
+ const filteredFiles = [];
38160
+ collectedFiles.forEach((file) => {
38161
+ if (isKnownFileType(file.path)) {
38162
+ filteredFiles.push(file);
38163
+ }
38164
+ else {
38165
+ logger.warn(`跳过不支持的文件类型: ${file.path}`);
38166
+ }
38167
+ });
38075
38168
  return {
38076
- collectedFiles,
38169
+ collectedFiles: filteredFiles,
38077
38170
  config,
38078
38171
  projectRoot,
38079
38172
  srcDir,
@@ -38573,7 +38666,7 @@ async function transform(transformType, entry, options) {
38573
38666
  await transformFn(entry, options);
38574
38667
  }
38575
38668
 
38576
- var version = "0.1.18";
38669
+ var version = "0.1.19";
38577
38670
  var pkg = {
38578
38671
  version: version};
38579
38672