@lcap/nasl-language-server-core 4.4.0-beta.5 → 4.4.0-beta.7

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/out/checker.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { SyntaxNode, App, BaseNode } from '@lcap/nasl-concepts';
1
+ import type { SyntaxNode, Frontend, View, App, BaseNode } from '@lcap/nasl-concepts';
2
2
  import { TypeAnnotation } from '@lcap/nasl-concepts';
3
3
  import { type SemEnv } from './typer/typer';
4
4
  import { Diagnostic as NaslTypeDiagnostic } from '@lcap/nasl-types';
@@ -49,6 +49,8 @@ export declare function createErrorDiagnoser(context: DiagnoseContext): {
49
49
  nodeConcept: string;
50
50
  }[];
51
51
  error: (node: SyntaxNode, message: string, context?: ErrorContext) => void;
52
+ checkViewIndexPageSetting: (node: View | Frontend) => void;
53
+ revalidateAncestorNode: (ancestorNode: SyntaxNode, validationFn: (ancestorNode: SyntaxNode, env: SemEnv) => void, errorFilter?: (err: Diagnostic) => boolean) => NodeDiagnosticsMap | null;
52
54
  };
53
55
  export interface DiagnosticRecord {
54
56
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EAU2G,GAAG,EAMxH,QAAQ,EAET,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACgE,cAAc,EAGpF,MAAM,qBAAqB,CAAC;AAS7B,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAK5C,OAAO,EAAE,UAAU,IAAI,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAOpE,oBAAY,QAAQ;IAClB,IAAI,YAAY;IAChB,KAAK,UAAU;CAChB;AAED,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAEnE,KAAK,YAAY,GAAG;IAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAC;AAMF,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;AAMrE,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAqBF,KAAK,eAAe,GAAG;IACrB,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,yBAAyB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC/C,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAC;AA4OF;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe;oCA47IpB,OAAO;sDAPW,gBAAgB,EAAE;;;;sBAhBjD,UAAU,GAAG,SAAS;;;8BA4Df,UAAU;2BAyBb,UAAU;;;iBAW3B,MAAM;kBACL,MAAM;kBACN,MAAM;qBACH,MAAM;;kBA/8IF,UAAU,WAAW,MAAM,YAAY,YAAY;EAk/IzE;AAGD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IAEX,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACrC,mBAAmB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACvC,qBAAqB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;CAC1C;AAMD,eAAO,MAAM,6BAA6B,aAC9B,UAAU,+DAGnB,MAAM,gBAAgB,CAyCxB,CAAC;AAMF,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
1
+ {"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EAC4C,QAAQ,EAC5B,IAAI,EAQ+E,GAAG,EAMxH,QAAQ,EAET,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACgE,cAAc,EAGpF,MAAM,qBAAqB,CAAC;AAS7B,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAK5C,OAAO,EAAE,UAAU,IAAI,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAWpE,oBAAY,QAAQ;IAClB,IAAI,YAAY;IAChB,KAAK,UAAU;CAChB;AAED,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAEnE,KAAK,YAAY,GAAG;IAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAC;AAMF,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;AAMrE,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAqBF,KAAK,eAAe,GAAG;IACrB,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,yBAAyB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC/C,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAC;AA6OF;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe;oCA2hJpB,OAAO;sDAPW,gBAAgB,EAAE;;;;sBAhBjD,UAAU,GAAG,SAAS;;;8BA4Df,UAAU;2BAyBb,UAAU;;;iBAW3B,MAAM;kBACL,MAAM;kBACN,MAAM;qBACH,MAAM;;kBA7iJF,UAAU,WAAW,MAAM,YAAY,YAAY;sCA+qD/B,IAAI,GAAG,QAAQ;2CA+5FxC,UAAU,gBACV,CAAC,YAAY,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,gBAC/C,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,KACzC,kBAAkB,GAAG,IAAI;EAgD7B;AAGD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IAEX,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACrC,mBAAmB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACvC,qBAAqB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;CAC1C;AAMD,eAAO,MAAM,6BAA6B,aAC9B,UAAU,+DAGnB,MAAM,gBAAgB,CAyCxB,CAAC;AAMF,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
package/out/checker.js CHANGED
@@ -14,6 +14,8 @@ const lodash_1 = require("lodash");
14
14
  const asserts_1 = require("@lcap/nasl-concepts/asserts");
15
15
  const helper_1 = require("./typer/helper");
16
16
  const semantic_rules_1 = require("./semantic-rules");
17
+ const regeExp = new nasl_utils_1.ExpressionLexer();
18
+ regeExp.profile = nasl_utils_1.profiles.js;
17
19
  var Severity;
18
20
  (function (Severity) {
19
21
  Severity["WARN"] = "warning";
@@ -366,7 +368,7 @@ function createErrorDiagnoser(context) {
366
368
  * @returns
367
369
  */
368
370
  function getError(node) {
369
- const curFileNode = getCurFileNode();
371
+ const curFileNode = getCurFileNode() ?? node;
370
372
  const diagnostics = diagnosticMap.get(curFileNode);
371
373
  if (diagnostics) {
372
374
  return diagnostics.get(node);
@@ -1935,9 +1937,39 @@ function createErrorDiagnoser(context) {
1935
1937
  yield* (0, nasl_utils_1.wrapForEachToGenerator)(node.views, function* (node) {
1936
1938
  yield* checkNode(node);
1937
1939
  });
1940
+ checkViewIndexPageSetting(node);
1938
1941
  }
1939
1942
  env.exitScope();
1940
1943
  }
1944
+ /**
1945
+ * 检查页面的首页/默认跳转页设置
1946
+ * 根据父级类型(Frontend 或 View)自动判断检查场景
1947
+ * @param node 当前页面节点
1948
+ */
1949
+ function checkViewIndexPageSetting(node) {
1950
+ const isNodeFrontend = (0, asserts_1.isFrontend)(node);
1951
+ const children = isNodeFrontend ? node.views : node.children;
1952
+ // 防御性检查:确保 children 存在且是数组
1953
+ if (!children || !Array.isArray(children)) {
1954
+ return;
1955
+ }
1956
+ // 检查默认跳转页数量,只允许有一个默认跳转页
1957
+ const defaultViews = children.filter((view) => view.isIndex);
1958
+ if (defaultViews.length > 1) {
1959
+ // 收集所有默认跳转页的名称
1960
+ const defaultViewNames = defaultViews.map((view) => view.name).join('、');
1961
+ const typeMap = {
1962
+ Frontend: '首页',
1963
+ View: '默认跳转页',
1964
+ };
1965
+ const pageType = typeMap[node.concept] || '默认跳转页';
1966
+ const errorMessage = `页面 ${node.name} 只能有一个 ${pageType},目前存在 ${defaultViewNames}`;
1967
+ error(node, errorMessage, {
1968
+ fileNode: node,
1969
+ isIndex: true,
1970
+ });
1971
+ }
1972
+ }
1941
1973
  /**
1942
1974
  * 检查 页面
1943
1975
  * @param node
@@ -1985,6 +2017,7 @@ function createErrorDiagnoser(context) {
1985
2017
  yield* (0, nasl_utils_1.wrapForEachToGenerator)(node.children, function* (node) {
1986
2018
  yield* checkNode(node);
1987
2019
  });
2020
+ checkViewIndexPageSetting(node);
1988
2021
  }
1989
2022
  env.exitScope();
1990
2023
  }
@@ -3362,6 +3395,7 @@ function createErrorDiagnoser(context) {
3362
3395
  function* checkSwitchCase(node) {
3363
3396
  if (!(0, nasl_concepts_1.isLastNode)(node)) {
3364
3397
  if (ensureNodeKeyExists(node, 'test')) {
3398
+ yield* rejectNullLiteral(node.test);
3365
3399
  yield* checkNode(node.test);
3366
3400
  }
3367
3401
  }
@@ -3527,7 +3561,7 @@ function createErrorDiagnoser(context) {
3527
3561
  error(node, '字符串');
3528
3562
  }
3529
3563
  const MAX_LENGTH = 2 ** 16;
3530
- if (node.value.length >= MAX_LENGTH) {
3564
+ if (node.value.length > MAX_LENGTH) {
3531
3565
  // 插值内部的字符串过长时要报错在插值节点上有红框
3532
3566
  error(node.parentNode?.concept === 'StringInterpolation' ? node.parentNode : node, `存在过长文本,当前长度:${node.value.length},最大长度:${MAX_LENGTH}。建议使用变量拆分文本后再拼接。`);
3533
3567
  }
@@ -3686,7 +3720,59 @@ function createErrorDiagnoser(context) {
3686
3720
  }
3687
3721
  return type;
3688
3722
  }
3723
+ /**
3724
+ * 获取正则表达式错误的中文消息
3725
+ */
3726
+ function getRegexErrorMessage(errorId) {
3727
+ const errors = nasl_utils_1.referenceContent?.errors;
3728
+ return errors?.[errorId] || `正则表达式错误: ${errorId}`;
3729
+ }
3730
+ /**
3731
+ * 检查正则表达式标志符
3732
+ */
3733
+ function checkRegexFlags(flags, targetNode) {
3734
+ const VALID_FLAGS = new Set(['g', 'i', 'm', 's', 'u', 'y']);
3735
+ const flagCounts = new Map();
3736
+ for (const flag of flags) {
3737
+ if (!VALID_FLAGS.has(flag)) {
3738
+ error(targetNode, `正则表达式标志符 "${flag}" 无效`);
3739
+ continue;
3740
+ }
3741
+ const count = (flagCounts.get(flag) || 0) + 1;
3742
+ flagCounts.set(flag, count);
3743
+ if (count > 1) {
3744
+ error(targetNode, `正则表达式标志符 "${flag}" 重复`);
3745
+ }
3746
+ }
3747
+ }
3748
+ /**
3749
+ * 检查正则表达式语法和标志符
3750
+ */
3689
3751
  function checkNewRegex(node) {
3752
+ // 检查正则表达式模式语法
3753
+ if (nasl_concepts_1.asserts.isStringLiteral(node.pattern)) {
3754
+ const targetNode = node.kind === 'static' ? node : node.pattern;
3755
+ try {
3756
+ regeExp.parse(`/${node.pattern.value}/`);
3757
+ const parseErrors = regeExp.errors;
3758
+ if (Array.isArray(parseErrors) && parseErrors.length > 0) {
3759
+ parseErrors.forEach((errToken) => {
3760
+ const errorId = errToken?.error?.id;
3761
+ if (errorId) {
3762
+ error(targetNode, getRegexErrorMessage(errorId));
3763
+ }
3764
+ });
3765
+ }
3766
+ }
3767
+ catch (e) {
3768
+ error(targetNode, '正则表达式解析失败');
3769
+ }
3770
+ }
3771
+ // 检查正则表达式标志符
3772
+ if (nasl_concepts_1.asserts.isStringLiteral(node.flags)) {
3773
+ const targetNode = node.kind === 'static' ? node : node.flags;
3774
+ checkRegexFlags(node.flags.value, targetNode);
3775
+ }
3690
3776
  return env.getType(node);
3691
3777
  }
3692
3778
  /**
@@ -4099,6 +4185,10 @@ function createErrorDiagnoser(context) {
4099
4185
  */
4100
4186
  function* checkUnaryExpression(node) {
4101
4187
  if (ensureNodeKeyExists(node, 'argument')) {
4188
+ const isCandidate = ['!'].includes(node.operator);
4189
+ if (isCandidate) {
4190
+ yield* rejectNullLiteral(node.argument);
4191
+ }
4102
4192
  yield* checkNode(node.argument);
4103
4193
  }
4104
4194
  return env.getType(node);
@@ -4674,6 +4764,45 @@ function createErrorDiagnoser(context) {
4674
4764
  });
4675
4765
  return flattenedDiags;
4676
4766
  }
4767
+ /**
4768
+ * 重新验证祖先节点
4769
+ * 用于增量更新时,当子节点变更需要重新验证祖先节点的场景
4770
+ * @param ancestorNode 需要重新验证的祖先节点
4771
+ * @param validationFn 验证函数
4772
+ * @param errorFilter 错误过滤函数(可选),用于保留特定的错误
4773
+ * @returns 新的诊断信息 Map,如果没有则返回 null
4774
+ */
4775
+ function revalidateAncestorNode(ancestorNode, validationFn, errorFilter) {
4776
+ // 保存并过滤相关的错误诊断
4777
+ const errs = getError(ancestorNode) || [];
4778
+ const filteredErrs = errorFilter
4779
+ ? errs.filter(errorFilter)
4780
+ : [];
4781
+ // 清除祖先节点的诊断信息
4782
+ clearDiagnostics(ancestorNode);
4783
+ // 恢复过滤后的错误诊断
4784
+ if (filteredErrs.length > 0) {
4785
+ filteredErrs.forEach((err) => {
4786
+ const { message, severity, ...context } = err;
4787
+ if (message) {
4788
+ error(ancestorNode, message, {
4789
+ severity: severity,
4790
+ ...context,
4791
+ });
4792
+ }
4793
+ });
4794
+ }
4795
+ // 设置文件节点上下文并执行验证
4796
+ const cleanup = handleFileNode(ancestorNode);
4797
+ try {
4798
+ validationFn(ancestorNode, env);
4799
+ }
4800
+ finally {
4801
+ cleanup?.();
4802
+ }
4803
+ // 返回新的诊断信息
4804
+ return getDiagnostics(ancestorNode) || null;
4805
+ }
4677
4806
  return {
4678
4807
  setMetadataTypeEnable,
4679
4808
  setLatestVersionsOfSharedApp,
@@ -4684,6 +4813,8 @@ function createErrorDiagnoser(context) {
4684
4813
  getAllDiagnostics,
4685
4814
  getDebugDiagnostics,
4686
4815
  error,
4816
+ checkViewIndexPageSetting,
4817
+ revalidateAncestorNode,
4687
4818
  };
4688
4819
  }
4689
4820
  exports.createErrorDiagnoser = createErrorDiagnoser;