@objectstack/lint 11.9.0 → 12.0.0
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/dist/index.cjs +197 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -19
- package/dist/index.d.ts +90 -19
- package/dist/index.js +189 -23
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
20
|
// src/index.ts
|
|
@@ -34,6 +24,9 @@ __export(index_exports, {
|
|
|
34
24
|
CHART_FIELD_UNKNOWN: () => CHART_FIELD_UNKNOWN,
|
|
35
25
|
FIELD_GROUP_EMPTY: () => FIELD_GROUP_EMPTY,
|
|
36
26
|
FIELD_GROUP_UNDECLARED: () => FIELD_GROUP_UNDECLARED,
|
|
27
|
+
FORM_COLSPAN_ABSOLUTE: () => FORM_COLSPAN_ABSOLUTE,
|
|
28
|
+
FORM_FIELD_UNKNOWN: () => FORM_FIELD_UNKNOWN,
|
|
29
|
+
LIST_VIEW_FILTERS_IN_VIEWS_MODE: () => LIST_VIEW_FILTERS_IN_VIEWS_MODE,
|
|
37
30
|
MEASURE_AGGREGATE_INCOHERENT: () => MEASURE_AGGREGATE_INCOHERENT,
|
|
38
31
|
PAGE_SOURCE_CLASSNAME: () => PAGE_SOURCE_CLASSNAME,
|
|
39
32
|
SEMANTIC_ROLE_FIELD_UNKNOWN: () => SEMANTIC_ROLE_FIELD_UNKNOWN,
|
|
@@ -48,7 +41,9 @@ __export(index_exports, {
|
|
|
48
41
|
WIDGET_DATASET_UNKNOWN: () => WIDGET_DATASET_UNKNOWN,
|
|
49
42
|
WIDGET_DIMENSION_UNKNOWN: () => WIDGET_DIMENSION_UNKNOWN,
|
|
50
43
|
WIDGET_MEASURE_UNKNOWN: () => WIDGET_MEASURE_UNKNOWN,
|
|
44
|
+
validateFormLayout: () => validateFormLayout,
|
|
51
45
|
validateJsxPages: () => validateJsxPages,
|
|
46
|
+
validateListViewMode: () => validateListViewMode,
|
|
52
47
|
validatePageSourceStyling: () => validatePageSourceStyling,
|
|
53
48
|
validateReactPageProps: () => validateReactPageProps,
|
|
54
49
|
validateReactPages: () => validateReactPages,
|
|
@@ -433,6 +428,60 @@ function validateStackExpressions(stack) {
|
|
|
433
428
|
return issues;
|
|
434
429
|
}
|
|
435
430
|
|
|
431
|
+
// src/validate-list-view-mode.ts
|
|
432
|
+
var LIST_VIEW_FILTERS_IN_VIEWS_MODE = "list-view-filters-in-views-mode";
|
|
433
|
+
var FORBIDDEN_FIELDS = ["userFilters", "quickFilters"];
|
|
434
|
+
function asArray3(v) {
|
|
435
|
+
if (Array.isArray(v)) return v;
|
|
436
|
+
if (v && typeof v === "object") {
|
|
437
|
+
return Object.entries(v).map(([name, def]) => ({
|
|
438
|
+
name,
|
|
439
|
+
...def
|
|
440
|
+
}));
|
|
441
|
+
}
|
|
442
|
+
return [];
|
|
443
|
+
}
|
|
444
|
+
function scanView(view, where, path, out) {
|
|
445
|
+
if (!view || typeof view !== "object") return;
|
|
446
|
+
const rec = view;
|
|
447
|
+
for (const field of FORBIDDEN_FIELDS) {
|
|
448
|
+
if (rec[field] == null) continue;
|
|
449
|
+
out.push({
|
|
450
|
+
severity: "error",
|
|
451
|
+
rule: LIST_VIEW_FILTERS_IN_VIEWS_MODE,
|
|
452
|
+
where,
|
|
453
|
+
path: `${path}.${field}`,
|
|
454
|
+
message: `\`${field}\` is a page filters-mode control and is ignored on an object list view ("views" mode) \u2014 the ViewTabBar is the only nav control here.`,
|
|
455
|
+
hint: `Move \`${field}\` to a page list (InterfaceListPage, "filters" mode), or remove it. See ADR-0053.`
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
function scanListViews(listViews, wherePrefix, pathPrefix, out) {
|
|
460
|
+
if (!listViews || typeof listViews !== "object") return;
|
|
461
|
+
for (const [name, view] of Object.entries(listViews)) {
|
|
462
|
+
scanView(
|
|
463
|
+
view,
|
|
464
|
+
`${wherePrefix} \u203A listViews.${name}`,
|
|
465
|
+
`${pathPrefix}.listViews.${name}`,
|
|
466
|
+
out
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function validateListViewMode(stack) {
|
|
471
|
+
const out = [];
|
|
472
|
+
asArray3(stack.objects).forEach((obj, i) => {
|
|
473
|
+
const label = typeof obj.name === "string" ? `object "${obj.name}"` : `objects[${i}]`;
|
|
474
|
+
scanListViews(obj.listViews, label, `objects[${i}]`, out);
|
|
475
|
+
});
|
|
476
|
+
asArray3(stack.views).forEach((view, i) => {
|
|
477
|
+
const named = typeof view.objectName === "string" ? view.objectName : typeof view.name === "string" ? view.name : void 0;
|
|
478
|
+
const label = named ? `view "${named}"` : `views[${i}]`;
|
|
479
|
+
scanView(view.list, `${label} \u203A list`, `views[${i}].list`, out);
|
|
480
|
+
scanListViews(view.listViews, label, `views[${i}]`, out);
|
|
481
|
+
});
|
|
482
|
+
return out;
|
|
483
|
+
}
|
|
484
|
+
|
|
436
485
|
// src/validate-responsive-styles.ts
|
|
437
486
|
var STYLE_NODE_MISSING_ID = "style-node-missing-id";
|
|
438
487
|
var STYLE_CLASSNAME_TAILWIND = "style-classname-tailwind";
|
|
@@ -636,7 +685,7 @@ function looksLikeTailwind(className) {
|
|
|
636
685
|
return false;
|
|
637
686
|
});
|
|
638
687
|
}
|
|
639
|
-
function
|
|
688
|
+
function asArray4(v) {
|
|
640
689
|
if (Array.isArray(v)) return v;
|
|
641
690
|
if (v && typeof v === "object") {
|
|
642
691
|
return Object.entries(v).map(([name, def]) => ({ name, ...def }));
|
|
@@ -729,13 +778,13 @@ function checkNode(node, pageName, path, findings) {
|
|
|
729
778
|
}
|
|
730
779
|
function validateResponsiveStyles(stack) {
|
|
731
780
|
const findings = [];
|
|
732
|
-
const pages =
|
|
781
|
+
const pages = asArray4(stack.pages);
|
|
733
782
|
for (let p = 0; p < pages.length; p++) {
|
|
734
783
|
const page = pages[p];
|
|
735
784
|
const pageName = typeof page.name === "string" ? page.name : `pages[${p}]`;
|
|
736
|
-
const regions =
|
|
785
|
+
const regions = asArray4(page.regions);
|
|
737
786
|
for (let r = 0; r < regions.length; r++) {
|
|
738
|
-
const components =
|
|
787
|
+
const components = asArray4(regions[r].components);
|
|
739
788
|
for (let c = 0; c < components.length; c++) {
|
|
740
789
|
checkNode(components[c], pageName, `pages[${p}].regions[${r}].components[${c}]`, findings);
|
|
741
790
|
}
|
|
@@ -746,10 +795,10 @@ function validateResponsiveStyles(stack) {
|
|
|
746
795
|
|
|
747
796
|
// src/validate-jsx-pages.ts
|
|
748
797
|
var import_sdui_parser = require("@objectstack/sdui-parser");
|
|
749
|
-
var
|
|
798
|
+
var asArray5 = (v) => Array.isArray(v) ? v : [];
|
|
750
799
|
function validateJsxPages(stack, opts = {}) {
|
|
751
800
|
const findings = [];
|
|
752
|
-
const pages =
|
|
801
|
+
const pages = asArray5(stack.pages);
|
|
753
802
|
for (let p = 0; p < pages.length; p++) {
|
|
754
803
|
const page = pages[p];
|
|
755
804
|
if (!page || page.kind !== "html" && page.kind !== "jsx") continue;
|
|
@@ -782,11 +831,25 @@ function validateJsxPages(stack, opts = {}) {
|
|
|
782
831
|
}
|
|
783
832
|
|
|
784
833
|
// src/validate-react-pages.ts
|
|
785
|
-
var
|
|
786
|
-
var
|
|
834
|
+
var import_node_module = require("module");
|
|
835
|
+
var import_meta = {};
|
|
836
|
+
var cachedTransform = null;
|
|
837
|
+
function loadSucraseTransform() {
|
|
838
|
+
if (cachedTransform) return cachedTransform;
|
|
839
|
+
const anchor = typeof import_meta !== "undefined" && import_meta.url ? import_meta.url : typeof __filename !== "undefined" ? __filename : process.cwd() + "/";
|
|
840
|
+
try {
|
|
841
|
+
cachedTransform = (0, import_node_module.createRequire)(anchor)("sucrase").transform;
|
|
842
|
+
} catch (err) {
|
|
843
|
+
throw new Error(
|
|
844
|
+
`@objectstack/lint: validating a kind:'react' page requires the "sucrase" package, which could not be loaded (${err instanceof Error ? err.message : String(err)}). It is a declared dependency of @objectstack/lint \u2014 if this deployment prunes packages, keep "sucrase" in the image; it is only loaded when a react-source page is validated.`
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
return cachedTransform;
|
|
848
|
+
}
|
|
849
|
+
var asArray6 = (v) => Array.isArray(v) ? v : [];
|
|
787
850
|
function validateReactPages(stack) {
|
|
788
851
|
const findings = [];
|
|
789
|
-
const pages =
|
|
852
|
+
const pages = asArray6(stack.pages);
|
|
790
853
|
for (let p = 0; p < pages.length; p++) {
|
|
791
854
|
const page = pages[p];
|
|
792
855
|
if (!page || page.kind !== "react") continue;
|
|
@@ -803,8 +866,9 @@ function validateReactPages(stack) {
|
|
|
803
866
|
});
|
|
804
867
|
continue;
|
|
805
868
|
}
|
|
869
|
+
const transform = loadSucraseTransform();
|
|
806
870
|
try {
|
|
807
|
-
|
|
871
|
+
transform(source, { transforms: ["jsx", "typescript"], production: true });
|
|
808
872
|
} catch (err) {
|
|
809
873
|
const message = err instanceof Error ? err.message : String(err);
|
|
810
874
|
findings.push({
|
|
@@ -821,9 +885,23 @@ function validateReactPages(stack) {
|
|
|
821
885
|
}
|
|
822
886
|
|
|
823
887
|
// src/validate-react-page-props.ts
|
|
824
|
-
var
|
|
888
|
+
var import_node_module2 = require("module");
|
|
825
889
|
var import_ui = require("@objectstack/spec/ui");
|
|
826
|
-
var
|
|
890
|
+
var import_meta2 = {};
|
|
891
|
+
var cachedTs = null;
|
|
892
|
+
function loadTypeScript() {
|
|
893
|
+
if (cachedTs) return cachedTs;
|
|
894
|
+
const anchor = typeof import_meta2 !== "undefined" && import_meta2.url ? import_meta2.url : typeof __filename !== "undefined" ? __filename : process.cwd() + "/";
|
|
895
|
+
try {
|
|
896
|
+
cachedTs = (0, import_node_module2.createRequire)(anchor)("typescript");
|
|
897
|
+
} catch (err) {
|
|
898
|
+
throw new Error(
|
|
899
|
+
`@objectstack/lint: validating a kind:'react' page requires the "typescript" package, which could not be loaded (${err instanceof Error ? err.message : String(err)}). It is a declared dependency of @objectstack/lint \u2014 if this deployment prunes packages, keep "typescript" in the image; it is only loaded when a react-source page is validated.`
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
return cachedTs;
|
|
903
|
+
}
|
|
904
|
+
var asArray7 = (v) => Array.isArray(v) ? v : [];
|
|
827
905
|
var BLOCKS = new Map(
|
|
828
906
|
import_ui.REACT_BLOCKS.map((b) => [
|
|
829
907
|
b.tag,
|
|
@@ -862,32 +940,33 @@ function nearestKnown(prop, known) {
|
|
|
862
940
|
}
|
|
863
941
|
function validateReactPageProps(stack) {
|
|
864
942
|
const findings = [];
|
|
865
|
-
const pages =
|
|
943
|
+
const pages = asArray7(stack.pages);
|
|
866
944
|
for (let p = 0; p < pages.length; p++) {
|
|
867
945
|
const page = pages[p];
|
|
868
946
|
if (!page || page.kind !== "react") continue;
|
|
869
947
|
const source = page.source;
|
|
870
948
|
if (typeof source !== "string" || source.trim() === "") continue;
|
|
871
949
|
const name = String(page.name ?? `#${p}`);
|
|
950
|
+
const tsc = loadTypeScript();
|
|
872
951
|
let sf;
|
|
873
952
|
try {
|
|
874
|
-
sf =
|
|
953
|
+
sf = tsc.createSourceFile("page.tsx", source, tsc.ScriptTarget.Latest, true, tsc.ScriptKind.TSX);
|
|
875
954
|
} catch {
|
|
876
955
|
continue;
|
|
877
956
|
}
|
|
878
957
|
const visit = (node) => {
|
|
879
|
-
if (
|
|
958
|
+
if (tsc.isJsxOpeningElement(node) || tsc.isJsxSelfClosingElement(node)) {
|
|
880
959
|
const tag = node.tagName.getText(sf);
|
|
881
960
|
const block = BLOCKS.get(tag);
|
|
882
961
|
if (block) {
|
|
883
962
|
let hasSpread = false;
|
|
884
963
|
const used = /* @__PURE__ */ new Set();
|
|
885
964
|
for (const a of node.attributes.properties) {
|
|
886
|
-
if (
|
|
965
|
+
if (tsc.isJsxSpreadAttribute(a)) {
|
|
887
966
|
hasSpread = true;
|
|
888
967
|
continue;
|
|
889
968
|
}
|
|
890
|
-
if (
|
|
969
|
+
if (tsc.isJsxAttribute(a)) used.add(a.name.getText(sf));
|
|
891
970
|
}
|
|
892
971
|
const where = `page "${name}" \u203A <${tag}>`;
|
|
893
972
|
const path = `pages[${p}].source`;
|
|
@@ -920,7 +999,7 @@ function validateReactPageProps(stack) {
|
|
|
920
999
|
}
|
|
921
1000
|
}
|
|
922
1001
|
}
|
|
923
|
-
|
|
1002
|
+
tsc.forEachChild(node, visit);
|
|
924
1003
|
};
|
|
925
1004
|
visit(sf);
|
|
926
1005
|
}
|
|
@@ -929,11 +1008,11 @@ function validateReactPageProps(stack) {
|
|
|
929
1008
|
|
|
930
1009
|
// src/validate-page-source-styling.ts
|
|
931
1010
|
var PAGE_SOURCE_CLASSNAME = "page-source-className-tailwind";
|
|
932
|
-
var
|
|
1011
|
+
var asArray8 = (v) => Array.isArray(v) ? v : [];
|
|
933
1012
|
var CLASSNAME_ATTR = /\bclassName\s*=\s*["'{]/g;
|
|
934
1013
|
function validatePageSourceStyling(stack) {
|
|
935
1014
|
const findings = [];
|
|
936
|
-
const pages =
|
|
1015
|
+
const pages = asArray8(stack.pages);
|
|
937
1016
|
for (let p = 0; p < pages.length; p++) {
|
|
938
1017
|
const page = pages[p];
|
|
939
1018
|
if (!page) continue;
|
|
@@ -962,7 +1041,7 @@ function validatePageSourceStyling(stack) {
|
|
|
962
1041
|
var import_data2 = require("@objectstack/spec/data");
|
|
963
1042
|
var TITLE_FORMAT_RETIRED = "title-format-retired";
|
|
964
1043
|
var TITLE_UNRESOLVABLE = "title-unresolvable";
|
|
965
|
-
function
|
|
1044
|
+
function asArray9(v) {
|
|
966
1045
|
if (Array.isArray(v)) return v;
|
|
967
1046
|
if (v && typeof v === "object") {
|
|
968
1047
|
return Object.entries(v).map(([name, def]) => ({ name, ...def }));
|
|
@@ -971,7 +1050,7 @@ function asArray8(v) {
|
|
|
971
1050
|
}
|
|
972
1051
|
function validateRecordTitle(stack) {
|
|
973
1052
|
const findings = [];
|
|
974
|
-
const objects =
|
|
1053
|
+
const objects = asArray9(stack.objects);
|
|
975
1054
|
for (let i = 0; i < objects.length; i++) {
|
|
976
1055
|
const obj = objects[i];
|
|
977
1056
|
const objName = typeof obj.name === "string" ? obj.name : `(object ${i})`;
|
|
@@ -1006,7 +1085,7 @@ function validateRecordTitle(stack) {
|
|
|
1006
1085
|
var FIELD_GROUP_UNDECLARED = "field-group-undeclared";
|
|
1007
1086
|
var FIELD_GROUP_EMPTY = "field-group-empty";
|
|
1008
1087
|
var SEMANTIC_ROLE_FIELD_UNKNOWN = "semantic-role-field-unknown";
|
|
1009
|
-
function
|
|
1088
|
+
function asArray10(v) {
|
|
1010
1089
|
if (Array.isArray(v)) return v;
|
|
1011
1090
|
if (v && typeof v === "object") {
|
|
1012
1091
|
return Object.entries(v).map(([name, def]) => ({ name, ...def }));
|
|
@@ -1015,7 +1094,7 @@ function asArray9(v) {
|
|
|
1015
1094
|
}
|
|
1016
1095
|
function validateSemanticRoles(stack) {
|
|
1017
1096
|
const findings = [];
|
|
1018
|
-
const objects =
|
|
1097
|
+
const objects = asArray10(stack.objects);
|
|
1019
1098
|
for (let i = 0; i < objects.length; i++) {
|
|
1020
1099
|
const obj = objects[i];
|
|
1021
1100
|
if (!obj || typeof obj !== "object") continue;
|
|
@@ -1081,12 +1160,94 @@ function validateSemanticRoles(stack) {
|
|
|
1081
1160
|
}
|
|
1082
1161
|
return findings;
|
|
1083
1162
|
}
|
|
1163
|
+
|
|
1164
|
+
// src/validate-form-layout.ts
|
|
1165
|
+
var FORM_FIELD_UNKNOWN = "form-field-unknown";
|
|
1166
|
+
var FORM_COLSPAN_ABSOLUTE = "absolute-colspan-discouraged";
|
|
1167
|
+
function asArray11(v) {
|
|
1168
|
+
if (Array.isArray(v)) return v;
|
|
1169
|
+
if (v && typeof v === "object") {
|
|
1170
|
+
return Object.entries(v).map(([name, def]) => ({ name, ...def }));
|
|
1171
|
+
}
|
|
1172
|
+
return [];
|
|
1173
|
+
}
|
|
1174
|
+
function fieldNameOf(entry) {
|
|
1175
|
+
if (typeof entry === "string") return entry.length > 0 ? entry : null;
|
|
1176
|
+
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
1177
|
+
const f = entry.field;
|
|
1178
|
+
return typeof f === "string" && f.length > 0 ? f : null;
|
|
1179
|
+
}
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
function boundObject(view) {
|
|
1183
|
+
const data = view.data;
|
|
1184
|
+
if (data && typeof data === "object" && typeof data.object === "string") {
|
|
1185
|
+
return data.object;
|
|
1186
|
+
}
|
|
1187
|
+
return typeof view.objectName === "string" ? view.objectName : void 0;
|
|
1188
|
+
}
|
|
1189
|
+
function validateFormLayout(stack) {
|
|
1190
|
+
const findings = [];
|
|
1191
|
+
const objectFields = /* @__PURE__ */ new Map();
|
|
1192
|
+
for (const obj of asArray11(stack.objects)) {
|
|
1193
|
+
const name = typeof obj.name === "string" ? obj.name : void 0;
|
|
1194
|
+
if (!name) continue;
|
|
1195
|
+
const fields = obj.fields && typeof obj.fields === "object" && !Array.isArray(obj.fields) ? Object.keys(obj.fields) : [];
|
|
1196
|
+
objectFields.set(name, new Set(fields));
|
|
1197
|
+
}
|
|
1198
|
+
const views = asArray11(stack.views);
|
|
1199
|
+
for (let i = 0; i < views.length; i++) {
|
|
1200
|
+
const view = views[i];
|
|
1201
|
+
if (!view || typeof view !== "object") continue;
|
|
1202
|
+
const sections = Array.isArray(view.sections) ? view.sections : null;
|
|
1203
|
+
if (!sections) continue;
|
|
1204
|
+
const viewName = typeof view.name === "string" ? view.name : `(view ${i})`;
|
|
1205
|
+
const objName = boundObject(view);
|
|
1206
|
+
const known = objName ? objectFields.get(objName) : void 0;
|
|
1207
|
+
const where = `view "${viewName}"`;
|
|
1208
|
+
const base = `views[${i}]`;
|
|
1209
|
+
for (let s = 0; s < sections.length; s++) {
|
|
1210
|
+
const sec = sections[s];
|
|
1211
|
+
const secFields = sec && typeof sec === "object" && Array.isArray(sec.fields) ? sec.fields : [];
|
|
1212
|
+
for (let f = 0; f < secFields.length; f++) {
|
|
1213
|
+
const entry = secFields[f];
|
|
1214
|
+
const fname = fieldNameOf(entry);
|
|
1215
|
+
const fpath = `${base}.sections[${s}].fields[${f}]`;
|
|
1216
|
+
if (fname && known && !known.has(fname)) {
|
|
1217
|
+
findings.push({
|
|
1218
|
+
severity: "warning",
|
|
1219
|
+
rule: FORM_FIELD_UNKNOWN,
|
|
1220
|
+
where,
|
|
1221
|
+
path: fpath,
|
|
1222
|
+
message: `${viewName}: field "${fname}" is not a field on object "${objName}" \u2014 it is silently skipped and never renders on the form`,
|
|
1223
|
+
hint: `Fix the field name, or add "${fname}" to ${objName}. Section field references must match the object's field names exactly.`
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
const colSpan = entry && typeof entry === "object" && !Array.isArray(entry) ? entry.colSpan : void 0;
|
|
1227
|
+
if (colSpan != null) {
|
|
1228
|
+
findings.push({
|
|
1229
|
+
severity: "warning",
|
|
1230
|
+
rule: FORM_COLSPAN_ABSOLUTE,
|
|
1231
|
+
where,
|
|
1232
|
+
path: `${fpath}.colSpan`,
|
|
1233
|
+
message: `${viewName}: field "${fname ?? "?"}" sets absolute colSpan ${String(colSpan)} \u2014 the form's column count is derived per surface (mobile 1 / modal 2 / page 3-4), so a fixed span only aligns at one width`,
|
|
1234
|
+
hint: `Prefer span: 'full' (whole row at any column count), or omit for auto width. The renderer clamps colSpan to the current column count.`
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
return findings;
|
|
1241
|
+
}
|
|
1084
1242
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1085
1243
|
0 && (module.exports = {
|
|
1086
1244
|
CHART_CONFIG_MISSING,
|
|
1087
1245
|
CHART_FIELD_UNKNOWN,
|
|
1088
1246
|
FIELD_GROUP_EMPTY,
|
|
1089
1247
|
FIELD_GROUP_UNDECLARED,
|
|
1248
|
+
FORM_COLSPAN_ABSOLUTE,
|
|
1249
|
+
FORM_FIELD_UNKNOWN,
|
|
1250
|
+
LIST_VIEW_FILTERS_IN_VIEWS_MODE,
|
|
1090
1251
|
MEASURE_AGGREGATE_INCOHERENT,
|
|
1091
1252
|
PAGE_SOURCE_CLASSNAME,
|
|
1092
1253
|
SEMANTIC_ROLE_FIELD_UNKNOWN,
|
|
@@ -1101,7 +1262,9 @@ function validateSemanticRoles(stack) {
|
|
|
1101
1262
|
WIDGET_DATASET_UNKNOWN,
|
|
1102
1263
|
WIDGET_DIMENSION_UNKNOWN,
|
|
1103
1264
|
WIDGET_MEASURE_UNKNOWN,
|
|
1265
|
+
validateFormLayout,
|
|
1104
1266
|
validateJsxPages,
|
|
1267
|
+
validateListViewMode,
|
|
1105
1268
|
validatePageSourceStyling,
|
|
1106
1269
|
validateReactPageProps,
|
|
1107
1270
|
validateReactPages,
|