@nocobase/client-v2 2.1.0-alpha.25 → 2.1.0-alpha.27

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.
Files changed (47) hide show
  1. package/es/BaseApplication.d.ts +1 -0
  2. package/es/flow/actions/dataScopeFilter.d.ts +9 -0
  3. package/es/flow/components/Grid/index.d.ts +5 -3
  4. package/es/flow/components/code-editor/types.d.ts +1 -0
  5. package/es/flow/internal/utils/rebuildFieldSubModel.d.ts +2 -1
  6. package/es/flow/models/base/GridModel.d.ts +19 -2
  7. package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +1 -0
  8. package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +7 -1
  9. package/es/flow/models/fields/JSFieldModel.d.ts +5 -0
  10. package/es/index.mjs +81 -81
  11. package/lib/index.js +73 -73
  12. package/package.json +5 -5
  13. package/src/BaseApplication.tsx +4 -0
  14. package/src/flow/actions/__tests__/dataScopeFilter.test.ts +158 -0
  15. package/src/flow/actions/dataScope.tsx +6 -4
  16. package/src/flow/actions/dataScopeFilter.ts +70 -0
  17. package/src/flow/actions/setTargetDataScope.tsx +6 -5
  18. package/src/flow/components/Grid/index.tsx +66 -20
  19. package/src/flow/components/code-editor/__tests__/linter.test.ts +18 -0
  20. package/src/flow/components/code-editor/__tests__/runjsDiagnostics.test.ts +23 -0
  21. package/src/flow/components/code-editor/index.tsx +18 -17
  22. package/src/flow/components/code-editor/linter.ts +222 -158
  23. package/src/flow/components/code-editor/runjsDiagnostics.ts +161 -97
  24. package/src/flow/components/code-editor/types.ts +1 -0
  25. package/src/flow/internal/utils/__tests__/rebuildFieldSubModel.test.ts +77 -2
  26. package/src/flow/internal/utils/rebuildFieldSubModel.ts +21 -5
  27. package/src/flow/models/base/BlockGridModel.tsx +2 -2
  28. package/src/flow/models/base/GridModel.tsx +428 -195
  29. package/src/flow/models/base/__tests__/BlockGridModel.dragOverlayConfig.test.ts +44 -0
  30. package/src/flow/models/base/__tests__/GridModel.computeOverlayRect.test.ts +29 -0
  31. package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +181 -2
  32. package/src/flow/models/base/__tests__/GridModel.resizeLayout.test.ts +124 -0
  33. package/src/flow/models/base/__tests__/GridModel.visibleLayout.test.ts +55 -15
  34. package/src/flow/models/blocks/details/DetailsGridModel.tsx +6 -6
  35. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +9 -5
  36. package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +54 -14
  37. package/src/flow/models/blocks/filter-form/__tests__/FilterFormBlockModel.cleanup.test.ts +138 -0
  38. package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.toggleFormFieldsCollapse.test.ts +45 -0
  39. package/src/flow/models/blocks/form/FormGridModel.tsx +6 -6
  40. package/src/flow/models/blocks/form/QuickEditFormModel.tsx +39 -16
  41. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +22 -0
  42. package/src/flow/models/blocks/table/JSColumnModel.tsx +30 -2
  43. package/src/flow/models/blocks/table/TableBlockModel.tsx +8 -1
  44. package/src/flow/models/blocks/table/TableColumnModel.tsx +1 -0
  45. package/src/flow/models/blocks/table/__tests__/JSColumnModel.test.tsx +51 -0
  46. package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +49 -0
  47. package/src/flow/models/fields/JSFieldModel.tsx +54 -14
@@ -16,6 +16,50 @@ import jsx from 'acorn-jsx';
16
16
  // @ts-ignore
17
17
  import * as acornWalk from 'acorn-walk';
18
18
 
19
+ const acornWalkBase = {
20
+ ...(acornWalk as any).base,
21
+ JSXElement(node: any, state: any, callback: any) {
22
+ callback(node.openingElement, state);
23
+ for (const child of node.children || []) callback(child, state);
24
+ if (node.closingElement) callback(node.closingElement, state);
25
+ },
26
+ JSXFragment(node: any, state: any, callback: any) {
27
+ callback(node.openingFragment, state);
28
+ for (const child of node.children || []) callback(child, state);
29
+ callback(node.closingFragment, state);
30
+ },
31
+ JSXOpeningElement(node: any, state: any, callback: any) {
32
+ callback(node.name, state);
33
+ for (const attribute of node.attributes || []) callback(attribute, state);
34
+ },
35
+ JSXClosingElement(node: any, state: any, callback: any) {
36
+ callback(node.name, state);
37
+ },
38
+ JSXAttribute(node: any, state: any, callback: any) {
39
+ callback(node.name, state);
40
+ if (node.value) callback(node.value, state);
41
+ },
42
+ JSXExpressionContainer(node: any, state: any, callback: any) {
43
+ callback(node.expression, state);
44
+ },
45
+ JSXSpreadAttribute(node: any, state: any, callback: any) {
46
+ callback(node.argument, state);
47
+ },
48
+ JSXMemberExpression(node: any, state: any, callback: any) {
49
+ callback(node.object, state);
50
+ callback(node.property, state);
51
+ },
52
+ JSXNamespacedName(node: any, state: any, callback: any) {
53
+ callback(node.namespace, state);
54
+ callback(node.name, state);
55
+ },
56
+ JSXIdentifier() {},
57
+ JSXText() {},
58
+ JSXEmptyExpression() {},
59
+ JSXOpeningFragment() {},
60
+ JSXClosingFragment() {},
61
+ };
62
+
19
63
  /**
20
64
  * 创建 JavaScript 语法检查器 - 只检查语法错误
21
65
  */
@@ -673,72 +717,80 @@ export const computeDiagnosticsFromText = (
673
717
 
674
718
  // 收集顶层声明(以及函数/参数名,粗粒度,尽量避免误报)
675
719
  // 使用 full 方式更兼容,避免对特定 walker 键的依赖(如 VariableDeclarator 在某些打包环境下不可用)
676
- acornWalk.full(ast, (node: any) => {
677
- switch (node?.type) {
678
- case 'VariableDeclarator':
679
- addPatternIds(node.id);
680
- break;
681
- case 'FunctionDeclaration':
682
- addId(node.id);
683
- (node.params || []).forEach(addPatternIds);
684
- break;
685
- case 'FunctionExpression':
686
- // 具名函数表达式也记录 id
687
- addId(node.id);
688
- (node.params || []).forEach(addPatternIds);
689
- break;
690
- case 'ArrowFunctionExpression':
691
- (node.params || []).forEach(addPatternIds);
692
- break;
693
- case 'CatchClause':
694
- addPatternIds((node as any).param);
695
- break;
696
- case 'ClassDeclaration':
697
- addId(node.id);
698
- break;
699
- default:
700
- break;
701
- }
702
- });
720
+ acornWalk.full(
721
+ ast,
722
+ (node: any) => {
723
+ switch (node?.type) {
724
+ case 'VariableDeclarator':
725
+ addPatternIds(node.id);
726
+ break;
727
+ case 'FunctionDeclaration':
728
+ addId(node.id);
729
+ (node.params || []).forEach(addPatternIds);
730
+ break;
731
+ case 'FunctionExpression':
732
+ // 具名函数表达式也记录 id
733
+ addId(node.id);
734
+ (node.params || []).forEach(addPatternIds);
735
+ break;
736
+ case 'ArrowFunctionExpression':
737
+ (node.params || []).forEach(addPatternIds);
738
+ break;
739
+ case 'CatchClause':
740
+ addPatternIds((node as any).param);
741
+ break;
742
+ case 'ClassDeclaration':
743
+ addId(node.id);
744
+ break;
745
+ default:
746
+ break;
747
+ }
748
+ },
749
+ acornWalkBase,
750
+ );
703
751
 
704
752
  // 1) 明显不可调用的调用表达式:如 123()、'x'()、(1+2)()
705
- acornWalk.full(ast, (node: any) => {
706
- if (!node || typeof node.type !== 'string') return;
707
- if (node.type === 'CallExpression') {
708
- const callee = node.callee;
709
- const isCallableLike =
710
- callee &&
711
- (callee.type === 'Identifier' ||
712
- callee.type === 'MemberExpression' ||
713
- callee.type === 'FunctionExpression' ||
714
- callee.type === 'ArrowFunctionExpression' ||
715
- callee.type === 'CallExpression' ||
716
- callee.type === 'ChainExpression');
717
- if (!isCallableLike) {
718
- const from = (callee?.loc && (callee as any).start) ?? node.start;
719
- const to = (callee?.loc && (callee as any).end) ?? node.end;
720
- if (isIgnoredPos(from)) return;
721
- diagnostics.push({
722
- from,
723
- to,
724
- severity: 'warning',
725
- source: 'no-noncallable-call',
726
- message: 'This expression is not callable.',
727
- actions: [],
728
- });
729
- }
730
- } else if (node.type === 'NewExpression') {
731
- const callee = node.callee;
732
- const isConstructorLike =
733
- callee &&
734
- (callee.type === 'Identifier' || callee.type === 'MemberExpression' || callee.type === 'CallExpression');
735
- if (!isConstructorLike) {
736
- const from = (callee?.loc && (callee as any).start) ?? node.start;
737
- const to = (callee?.loc && (callee as any).end) ?? node.end;
738
- push(from, to, 'This constructor is not a function.');
753
+ acornWalk.full(
754
+ ast,
755
+ (node: any) => {
756
+ if (!node || typeof node.type !== 'string') return;
757
+ if (node.type === 'CallExpression') {
758
+ const callee = node.callee;
759
+ const isCallableLike =
760
+ callee &&
761
+ (callee.type === 'Identifier' ||
762
+ callee.type === 'MemberExpression' ||
763
+ callee.type === 'FunctionExpression' ||
764
+ callee.type === 'ArrowFunctionExpression' ||
765
+ callee.type === 'CallExpression' ||
766
+ callee.type === 'ChainExpression');
767
+ if (!isCallableLike) {
768
+ const from = (callee?.loc && (callee as any).start) ?? node.start;
769
+ const to = (callee?.loc && (callee as any).end) ?? node.end;
770
+ if (isIgnoredPos(from)) return;
771
+ diagnostics.push({
772
+ from,
773
+ to,
774
+ severity: 'warning',
775
+ source: 'no-noncallable-call',
776
+ message: 'This expression is not callable.',
777
+ actions: [],
778
+ });
779
+ }
780
+ } else if (node.type === 'NewExpression') {
781
+ const callee = node.callee;
782
+ const isConstructorLike =
783
+ callee &&
784
+ (callee.type === 'Identifier' || callee.type === 'MemberExpression' || callee.type === 'CallExpression');
785
+ if (!isConstructorLike) {
786
+ const from = (callee?.loc && (callee as any).start) ?? node.start;
787
+ const to = (callee?.loc && (callee as any).end) ?? node.end;
788
+ push(from, to, 'This constructor is not a function.');
789
+ }
739
790
  }
740
- }
741
- });
791
+ },
792
+ acornWalkBase,
793
+ );
742
794
 
743
795
  // 1.1) 可疑的 ctx 方法调用:ctx.xxx()
744
796
  // - 当传入 knownCtxMemberRoots 时:对所有“不在已知 ctx API 列表中”的调用给出提示
@@ -751,124 +803,136 @@ export const computeDiagnosticsFromText = (
751
803
  // Always allow some well-known ctx roots to avoid noisy false positives when doc is incomplete.
752
804
  for (const k of ['t', 'logger', 'libs']) knownCtxRoots?.add(k);
753
805
  const allowedShort = new Set<string>(['t']);
754
- acornWalk.full(ast, (node: any) => {
755
- if (!node || typeof node.type !== 'string') return;
756
- if (node.type !== 'CallExpression') return;
757
- let callee = node.callee;
758
- if (callee?.type === 'ChainExpression') callee = callee.expression;
759
- if (!callee || callee.type !== 'MemberExpression') return;
760
- const obj = callee.object;
761
- if (!obj || obj.type !== 'Identifier' || obj.name !== 'ctx') return;
762
-
763
- let name: string | null = null;
764
- if (!callee.computed && callee.property?.type === 'Identifier') name = callee.property.name;
765
- else if (callee.computed && callee.property?.type === 'Literal' && typeof callee.property.value === 'string')
766
- name = callee.property.value;
767
- if (!name) return;
768
- const normalized = String(name).trim();
769
- if (!normalized || normalized.startsWith('_')) return;
770
- if (knownCtxRoots) {
771
- if (knownCtxRoots.has(normalized)) return;
772
- } else {
773
- if (normalized.length > 2) return;
774
- if (allowedShort.has(normalized)) return;
775
- }
776
-
777
- const from = (callee.property as any)?.start ?? callee.start ?? node.start;
778
- const to = (callee.property as any)?.end ?? from + 1;
779
- const key = `${normalized}@${from}`;
780
- if (reportedCtxCalls.has(key)) return;
781
- if (isIgnoredPos(from)) return;
782
- diagnostics.push({
783
- from,
784
- to,
785
- severity: 'warning',
786
- source: RULE_CTX_CALL,
787
- message: `Possible undefined ctx method call: ctx.${normalized}(). 可能是拼写错误或未在当前 ctx API 中定义。`,
788
- actions: [],
789
- });
790
- reportedCtxCalls.add(key);
791
- });
792
-
793
- // 1.2) 可疑的 ctx 成员访问:ctx.xxx(包含 ctx.xxx.yyy 的 root 访问)
794
- // 规则与方法调用保持一致,但会跳过 CallExpression.callee 的 member(避免与 1.1 重复)。
795
- const reportedCtxMembers = new Set<string>();
796
- acornWalk.ancestor(ast, {
797
- MemberExpression(node: any, ancestors: any[]) {
798
- // Only enable unknown-member detection when we have an explicit ctx API list;
799
- // otherwise the false-positive rate is too high for plain member access.
800
- if (!knownCtxRoots) return;
801
-
802
- const parent = ancestors[ancestors.length - 2];
803
- if (parent?.type === 'CallExpression') {
804
- let callee = parent.callee;
805
- if (callee?.type === 'ChainExpression') callee = callee.expression;
806
- if (callee === node) return; // handled by call rule above
807
- }
808
-
809
- const obj = node?.object;
806
+ acornWalk.full(
807
+ ast,
808
+ (node: any) => {
809
+ if (!node || typeof node.type !== 'string') return;
810
+ if (node.type !== 'CallExpression') return;
811
+ let callee = node.callee;
812
+ if (callee?.type === 'ChainExpression') callee = callee.expression;
813
+ if (!callee || callee.type !== 'MemberExpression') return;
814
+ const obj = callee.object;
810
815
  if (!obj || obj.type !== 'Identifier' || obj.name !== 'ctx') return;
811
816
 
812
817
  let name: string | null = null;
813
- if (!node.computed && node.property?.type === 'Identifier') name = node.property.name;
814
- else if (node.computed && node.property?.type === 'Literal' && typeof node.property.value === 'string')
815
- name = node.property.value;
818
+ if (!callee.computed && callee.property?.type === 'Identifier') name = callee.property.name;
819
+ else if (callee.computed && callee.property?.type === 'Literal' && typeof callee.property.value === 'string')
820
+ name = callee.property.value;
816
821
  if (!name) return;
817
822
  const normalized = String(name).trim();
818
823
  if (!normalized || normalized.startsWith('_')) return;
819
- if (knownCtxRoots.has(normalized)) return;
824
+ if (knownCtxRoots) {
825
+ if (knownCtxRoots.has(normalized)) return;
826
+ } else {
827
+ if (normalized.length > 2) return;
828
+ if (allowedShort.has(normalized)) return;
829
+ }
820
830
 
821
- const from = (node.property as any)?.start ?? node.start ?? 0;
822
- const to = (node.property as any)?.end ?? from + 1;
831
+ const from = (callee.property as any)?.start ?? callee.start ?? node.start;
832
+ const to = (callee.property as any)?.end ?? from + 1;
823
833
  const key = `${normalized}@${from}`;
824
- if (reportedCtxMembers.has(key)) return;
834
+ if (reportedCtxCalls.has(key)) return;
825
835
  if (isIgnoredPos(from)) return;
826
-
827
836
  diagnostics.push({
828
837
  from,
829
838
  to,
830
839
  severity: 'warning',
831
- source: RULE_CTX_MEMBER,
832
- message: `Possible unknown ctx member access: ctx.${normalized}. 可能是拼写错误或未在当前 ctx API 中定义。`,
840
+ source: RULE_CTX_CALL,
841
+ message: `Possible undefined ctx method call: ctx.${normalized}(). 可能是拼写错误或未在当前 ctx API 中定义。`,
833
842
  actions: [],
834
843
  });
835
- reportedCtxMembers.add(key);
844
+ reportedCtxCalls.add(key);
836
845
  },
837
- });
846
+ acornWalkBase,
847
+ );
848
+
849
+ // 1.2) 可疑的 ctx 成员访问:ctx.xxx(包含 ctx.xxx.yyy 的 root 访问)
850
+ // 规则与方法调用保持一致,但会跳过 CallExpression.callee 的 member(避免与 1.1 重复)。
851
+ const reportedCtxMembers = new Set<string>();
852
+ acornWalk.ancestor(
853
+ ast,
854
+ {
855
+ MemberExpression(node: any, ancestors: any[]) {
856
+ // Only enable unknown-member detection when we have an explicit ctx API list;
857
+ // otherwise the false-positive rate is too high for plain member access.
858
+ if (!knownCtxRoots) return;
859
+
860
+ const parent = ancestors[ancestors.length - 2];
861
+ if (parent?.type === 'CallExpression') {
862
+ let callee = parent.callee;
863
+ if (callee?.type === 'ChainExpression') callee = callee.expression;
864
+ if (callee === node) return; // handled by call rule above
865
+ }
866
+
867
+ const obj = node?.object;
868
+ if (!obj || obj.type !== 'Identifier' || obj.name !== 'ctx') return;
869
+
870
+ let name: string | null = null;
871
+ if (!node.computed && node.property?.type === 'Identifier') name = node.property.name;
872
+ else if (node.computed && node.property?.type === 'Literal' && typeof node.property.value === 'string')
873
+ name = node.property.value;
874
+ if (!name) return;
875
+ const normalized = String(name).trim();
876
+ if (!normalized || normalized.startsWith('_')) return;
877
+ if (knownCtxRoots.has(normalized)) return;
878
+
879
+ const from = (node.property as any)?.start ?? node.start ?? 0;
880
+ const to = (node.property as any)?.end ?? from + 1;
881
+ const key = `${normalized}@${from}`;
882
+ if (reportedCtxMembers.has(key)) return;
883
+ if (isIgnoredPos(from)) return;
884
+
885
+ diagnostics.push({
886
+ from,
887
+ to,
888
+ severity: 'warning',
889
+ source: RULE_CTX_MEMBER,
890
+ message: `Possible unknown ctx member access: ctx.${normalized}. 可能是拼写错误或未在当前 ctx API 中定义。`,
891
+ actions: [],
892
+ });
893
+ reportedCtxMembers.add(key);
894
+ },
895
+ },
896
+ acornWalkBase,
897
+ );
838
898
  } catch (_) {
839
899
  // ignore
840
900
  }
841
901
 
842
902
  // 2) 疑似未定义变量(尽量减少误报:排除属性名与解构/声明)
843
903
  const reported = new Set<string>();
844
- acornWalk.ancestor(ast, {
845
- Identifier(node: any, ancestors: any[]) {
846
- const name = node.name;
847
- if (!name || declared.has(name) || reported.has(name)) return;
848
- const parent = ancestors[ancestors.length - 2];
849
- if (!parent) return;
850
- // 跳过声明位置 / 属性键 / 非计算属性
851
- if (
852
- (parent.type === 'VariableDeclarator' && parent.id === node) ||
853
- (parent.type === 'FunctionDeclaration' && parent.id === node) ||
854
- (parent.type === 'FunctionExpression' && parent.id === node) ||
855
- (parent.type === 'ClassDeclaration' && parent.id === node) ||
856
- (parent.type === 'ClassExpression' && parent.id === node) ||
857
- (parent.type === 'Property' && parent.key === node && parent.computed !== true) ||
858
- (parent.type === 'MemberExpression' && parent.property === node && parent.computed !== true) ||
859
- (parent.type === 'LabeledStatement' && parent.label === node) ||
860
- (parent.type === 'BreakStatement' && parent.label === node) ||
861
- (parent.type === 'ContinueStatement' && parent.label === node)
862
- ) {
863
- return;
864
- }
865
- // 可能未定义的自由变量
866
- const from = (node as any).start ?? 0;
867
- const to = (node as any).end ?? from + 1;
868
- push(from, to, `Possible undefined variable: ${name}`, 'warning');
869
- reported.add(name);
904
+ acornWalk.ancestor(
905
+ ast,
906
+ {
907
+ Identifier(node: any, ancestors: any[]) {
908
+ const name = node.name;
909
+ if (!name || declared.has(name) || reported.has(name)) return;
910
+ const parent = ancestors[ancestors.length - 2];
911
+ if (!parent) return;
912
+ // 跳过声明位置 / 属性键 / 非计算属性
913
+ if (
914
+ (parent.type === 'VariableDeclarator' && parent.id === node) ||
915
+ (parent.type === 'FunctionDeclaration' && parent.id === node) ||
916
+ (parent.type === 'FunctionExpression' && parent.id === node) ||
917
+ (parent.type === 'ClassDeclaration' && parent.id === node) ||
918
+ (parent.type === 'ClassExpression' && parent.id === node) ||
919
+ (parent.type === 'Property' && parent.key === node && parent.computed !== true) ||
920
+ (parent.type === 'MemberExpression' && parent.property === node && parent.computed !== true) ||
921
+ (parent.type === 'LabeledStatement' && parent.label === node) ||
922
+ (parent.type === 'BreakStatement' && parent.label === node) ||
923
+ (parent.type === 'ContinueStatement' && parent.label === node)
924
+ ) {
925
+ return;
926
+ }
927
+ // 可能未定义的自由变量
928
+ const from = (node as any).start ?? 0;
929
+ const to = (node as any).end ?? from + 1;
930
+ push(from, to, `Possible undefined variable: ${name}`, 'warning');
931
+ reported.add(name);
932
+ },
870
933
  },
871
- });
934
+ acornWalkBase,
935
+ );
872
936
  } catch (e) {
873
937
  // 静态检查失败不影响编辑体验
874
938
  // console.debug('[linter] static checks failed', e);