@grexx/grexxlinter 0.2.343 → 0.2.350
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/cli.js +321 -45
- package/lsp.js +13502 -13383
- package/package.json +1 -1
- package/schemas/.caserefs.json +174 -0
- package/schemas/.coverage.json +75 -567
- package/schemas/activity.schema.json +0 -18
- package/schemas/activityAttribute.schema.json +0 -17
- package/schemas/attribute.schema.json +0 -25
- package/schemas/catchBlock.schema.json +11 -3
- package/schemas/container.schema.json +57 -68
- package/schemas/customThrow.schema.json +11 -3
- package/schemas/dataset.schema.json +0 -52
- package/schemas/datasetColumn.schema.json +3 -107
- package/schemas/datasetCondition.schema.json +0 -77
- package/schemas/folderInfo.schema.json +45 -0
- package/schemas/forEach.schema.json +42 -22
- package/schemas/formAction.schema.json +0 -10
- package/schemas/formfield.schema.json +0 -35
- package/schemas/mapping.schema.json +32 -31
- package/schemas/menuItem.schema.json +43 -51
- package/schemas/metadata.schema.json +0 -17
- package/schemas/platformAttribute.schema.json +0 -22
- package/schemas/platformRole.schema.json +0 -5
- package/schemas/simpleCondition.schema.json +0 -5
- package/schemas/trigger.schema.json +131 -63
- package/schemas/tryBlock.schema.json +11 -3
- package/schemas/view.schema.json +0 -17
- package/schemas/while.schema.json +23 -43
- package/schemas/widget.schema.json +0 -463
package/cli.js
CHANGED
|
@@ -14456,7 +14456,7 @@ var require__ = __commonJS({
|
|
|
14456
14456
|
});
|
|
14457
14457
|
|
|
14458
14458
|
// src/cli.ts
|
|
14459
|
-
var
|
|
14459
|
+
var import_yaml3 = __toESM(require_dist(), 1);
|
|
14460
14460
|
import { readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
|
|
14461
14461
|
import { availableParallelism } from "node:os";
|
|
14462
14462
|
import { extname, join as join2, relative } from "node:path";
|
|
@@ -14464,6 +14464,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
|
14464
14464
|
import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";
|
|
14465
14465
|
|
|
14466
14466
|
// src/node.ts
|
|
14467
|
+
var import_yaml = __toESM(require_dist(), 1);
|
|
14467
14468
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
14468
14469
|
import { createRequire } from "node:module";
|
|
14469
14470
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -14518,9 +14519,11 @@ function createSchemaRegistry(schemas) {
|
|
|
14518
14519
|
validateSchema: false
|
|
14519
14520
|
});
|
|
14520
14521
|
const casetypes = [];
|
|
14522
|
+
const byId = /* @__PURE__ */ new Map();
|
|
14521
14523
|
for (const schema of schemas) {
|
|
14522
14524
|
const id = typeof schema.$id === "string" ? schema.$id : void 0;
|
|
14523
14525
|
if (!id) continue;
|
|
14526
|
+
byId.set(id, schema);
|
|
14524
14527
|
if (!ajv.getSchema(id)) ajv.addSchema(schema, id);
|
|
14525
14528
|
const m = /^https:\/\/grexx\.net\/studio-next\/schemas\/([^/]+)\.schema\.json$/.exec(id);
|
|
14526
14529
|
if (m) casetypes.push(m[1]);
|
|
@@ -14538,12 +14541,81 @@ function createSchemaRegistry(schemas) {
|
|
|
14538
14541
|
cache.set(casetype, fn);
|
|
14539
14542
|
return fn;
|
|
14540
14543
|
}
|
|
14544
|
+
function resolveRef(ref, base) {
|
|
14545
|
+
const hash = ref.indexOf("#");
|
|
14546
|
+
const path = hash === -1 ? ref : ref.slice(0, hash);
|
|
14547
|
+
const frag = hash === -1 ? "" : ref.slice(hash + 1);
|
|
14548
|
+
let schema;
|
|
14549
|
+
if (path === "") {
|
|
14550
|
+
schema = base;
|
|
14551
|
+
} else {
|
|
14552
|
+
const file = path.replace(/^\.\//, "");
|
|
14553
|
+
schema = byId.get(file.startsWith("http") ? file : `${SCHEMA_ID_BASE}${file}`);
|
|
14554
|
+
}
|
|
14555
|
+
if (!schema) return null;
|
|
14556
|
+
if (!frag) return schema;
|
|
14557
|
+
let cur = schema;
|
|
14558
|
+
for (const raw of frag.split("/").filter(Boolean)) {
|
|
14559
|
+
const key2 = raw.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
14560
|
+
if (cur == null || typeof cur !== "object") return null;
|
|
14561
|
+
cur = cur[key2];
|
|
14562
|
+
}
|
|
14563
|
+
return cur ?? null;
|
|
14564
|
+
}
|
|
14541
14565
|
return {
|
|
14542
14566
|
casetypes,
|
|
14543
14567
|
has: (casetype) => validatorFor(casetype) != null,
|
|
14544
|
-
validatorFor
|
|
14568
|
+
validatorFor,
|
|
14569
|
+
resolveRef
|
|
14570
|
+
};
|
|
14571
|
+
}
|
|
14572
|
+
|
|
14573
|
+
// src/types.ts
|
|
14574
|
+
var DEFAULT_RULE_SEVERITY = {
|
|
14575
|
+
"required-field": "error",
|
|
14576
|
+
"forbidden-field": "error",
|
|
14577
|
+
"additional-field": "warning",
|
|
14578
|
+
"invalid-value": "error",
|
|
14579
|
+
"value-case": "warning",
|
|
14580
|
+
"invalid-type": "error",
|
|
14581
|
+
"invalid-format": "error",
|
|
14582
|
+
"invalid-reference": "error",
|
|
14583
|
+
"unknown-casetype": "warning",
|
|
14584
|
+
"parse-error": "error",
|
|
14585
|
+
schema: "error"
|
|
14586
|
+
};
|
|
14587
|
+
var DEFAULT_LINTER_SETTINGS = {
|
|
14588
|
+
rules: {},
|
|
14589
|
+
failOn: "error",
|
|
14590
|
+
ignoreCasetypes: [],
|
|
14591
|
+
includeCasetypes: [],
|
|
14592
|
+
reportUncovered: false,
|
|
14593
|
+
include: ["**/*.yaml", "**/*.yml"],
|
|
14594
|
+
exclude: ["node_modules", "dist", ".git"]
|
|
14595
|
+
};
|
|
14596
|
+
var LINT_RULE_DESCRIPTORS = [
|
|
14597
|
+
{ id: "required-field", label: "Required field", description: "A field the casetype state requires (MUST) is missing.", defaultSeverity: "error" },
|
|
14598
|
+
{ id: "forbidden-field", label: "Forbidden field", description: "A field present that the casetype state forbids (MUST_NOT).", defaultSeverity: "error" },
|
|
14599
|
+
{ id: "additional-field", label: "Additional field", description: "A field not defined by the schema. Allowed in principle, but flagged for review.", defaultSeverity: "warning" },
|
|
14600
|
+
{ id: "invalid-value", label: "Invalid value", description: "Value not allowed by an enum / const.", defaultSeverity: "error" },
|
|
14601
|
+
{ id: "value-case", label: "Value case mismatch", description: "Value matches an allowed option except for case / camelCase / separators (e.g. FORM vs form).", defaultSeverity: "warning" },
|
|
14602
|
+
{ id: "invalid-type", label: "Invalid type", description: "Value has the wrong JSON type.", defaultSeverity: "error" },
|
|
14603
|
+
{ id: "invalid-format", label: "Invalid format", description: "Value fails a pattern / format constraint.", defaultSeverity: "error" },
|
|
14604
|
+
{ id: "invalid-reference", label: "Invalid reference", description: "A case-reference field points at a file whose `_type` is not in the field's allowedCasetypes.", defaultSeverity: "error" },
|
|
14605
|
+
{ id: "unknown-casetype", label: "Unknown casetype", description: "No schema found for the document `_type`.", defaultSeverity: "warning" },
|
|
14606
|
+
{ id: "parse-error", label: "Parse error", description: "The YAML document could not be parsed.", defaultSeverity: "error" },
|
|
14607
|
+
{ id: "schema", label: "Other schema error", description: "Any other constraint reported by the validator.", defaultSeverity: "error" }
|
|
14608
|
+
];
|
|
14609
|
+
function normalizeSettings(partial) {
|
|
14610
|
+
return {
|
|
14611
|
+
...DEFAULT_LINTER_SETTINGS,
|
|
14612
|
+
...partial,
|
|
14613
|
+
rules: { ...DEFAULT_LINTER_SETTINGS.rules, ...partial?.rules ?? {} }
|
|
14545
14614
|
};
|
|
14546
14615
|
}
|
|
14616
|
+
function severityFor(rule, settings) {
|
|
14617
|
+
return settings.rules[rule] ?? DEFAULT_RULE_SEVERITY[rule];
|
|
14618
|
+
}
|
|
14547
14619
|
|
|
14548
14620
|
// src/node.ts
|
|
14549
14621
|
var require2 = createRequire(import.meta.url);
|
|
@@ -14576,6 +14648,95 @@ function loadCoverage(dir) {
|
|
|
14576
14648
|
return null;
|
|
14577
14649
|
}
|
|
14578
14650
|
}
|
|
14651
|
+
function loadCaseRefs(dir) {
|
|
14652
|
+
const file = join(dir, ".caserefs.json");
|
|
14653
|
+
if (!existsSync(file) || !statSync(file).isFile()) return {};
|
|
14654
|
+
try {
|
|
14655
|
+
return JSON.parse(readFileSync(file, "utf8"));
|
|
14656
|
+
} catch {
|
|
14657
|
+
return {};
|
|
14658
|
+
}
|
|
14659
|
+
}
|
|
14660
|
+
var docCache = /* @__PURE__ */ new Map();
|
|
14661
|
+
function targetDoc(absPath) {
|
|
14662
|
+
if (docCache.has(absPath)) return docCache.get(absPath);
|
|
14663
|
+
let doc = null;
|
|
14664
|
+
if (existsSync(absPath) && statSync(absPath).isFile()) {
|
|
14665
|
+
try {
|
|
14666
|
+
doc = (0, import_yaml.parse)(readFileSync(absPath, "utf8"));
|
|
14667
|
+
} catch {
|
|
14668
|
+
doc = null;
|
|
14669
|
+
}
|
|
14670
|
+
}
|
|
14671
|
+
docCache.set(absPath, doc);
|
|
14672
|
+
return doc;
|
|
14673
|
+
}
|
|
14674
|
+
function targetType(absPath, fragment) {
|
|
14675
|
+
const doc = targetDoc(absPath);
|
|
14676
|
+
if (doc == null || typeof doc !== "object") return "not-found";
|
|
14677
|
+
if (!fragment) {
|
|
14678
|
+
const t2 = doc._type;
|
|
14679
|
+
return typeof t2 === "string" ? t2 : null;
|
|
14680
|
+
}
|
|
14681
|
+
let found = null;
|
|
14682
|
+
const walk = (n) => {
|
|
14683
|
+
if (found || !n || typeof n !== "object") return;
|
|
14684
|
+
if (Array.isArray(n)) {
|
|
14685
|
+
n.forEach(walk);
|
|
14686
|
+
return;
|
|
14687
|
+
}
|
|
14688
|
+
const o = n;
|
|
14689
|
+
if (o._uid === fragment) {
|
|
14690
|
+
found = o;
|
|
14691
|
+
return;
|
|
14692
|
+
}
|
|
14693
|
+
for (const v of Object.values(o)) walk(v);
|
|
14694
|
+
};
|
|
14695
|
+
walk(doc);
|
|
14696
|
+
if (!found) return "not-found";
|
|
14697
|
+
const t = found._type;
|
|
14698
|
+
return typeof t === "string" ? t : null;
|
|
14699
|
+
}
|
|
14700
|
+
var looksLikePath = (s) => s.includes("/") || s.endsWith(".yaml") || s.endsWith(".yml");
|
|
14701
|
+
function referenceFindings(value, file, caseRefs, settings) {
|
|
14702
|
+
const sev = severityFor("invalid-reference", settings);
|
|
14703
|
+
if (sev === "off" || Object.keys(caseRefs).length === 0) return [];
|
|
14704
|
+
const baseDir = dirname(file);
|
|
14705
|
+
const out = [];
|
|
14706
|
+
const checkRef = (ref, allowed, casetype, field, path) => {
|
|
14707
|
+
if (!looksLikePath(ref)) return;
|
|
14708
|
+
const frag = /^(.*\.ya?ml)#(.+)$/.exec(ref);
|
|
14709
|
+
const pathPart = frag ? frag[1] : ref;
|
|
14710
|
+
const fragment = frag ? frag[2] : "";
|
|
14711
|
+
const t = targetType(resolve(baseDir, pathPart), fragment);
|
|
14712
|
+
if (t === "not-found") {
|
|
14713
|
+
out.push({ severity: sev, rule: "invalid-reference", casetype, path, field, message: `reference target not found: "${ref}"`, file });
|
|
14714
|
+
} else if (t !== null && !allowed.includes(t)) {
|
|
14715
|
+
out.push({ severity: sev, rule: "invalid-reference", casetype, path, field, message: `field "${field}" references a "${t}" but must reference one of: ${allowed.join(", ")}`, file });
|
|
14716
|
+
}
|
|
14717
|
+
};
|
|
14718
|
+
const walk = (node, path) => {
|
|
14719
|
+
if (Array.isArray(node)) {
|
|
14720
|
+
node.forEach((n, i) => walk(n, `${path}/${i}`));
|
|
14721
|
+
return;
|
|
14722
|
+
}
|
|
14723
|
+
if (!node || typeof node !== "object") return;
|
|
14724
|
+
const o = node;
|
|
14725
|
+
const refs = typeof o._type === "string" ? caseRefs[o._type] : void 0;
|
|
14726
|
+
if (refs) {
|
|
14727
|
+
for (const [field, allowed] of Object.entries(refs)) {
|
|
14728
|
+
const v = o[field];
|
|
14729
|
+
if (typeof v === "string") checkRef(v, allowed, o._type, field, `${path}/${field}`);
|
|
14730
|
+
else if (Array.isArray(v)) v.forEach((item, i) => {
|
|
14731
|
+
if (typeof item === "string") checkRef(item, allowed, o._type, field, `${path}/${field}/${i}`);
|
|
14732
|
+
});
|
|
14733
|
+
}
|
|
14734
|
+
}
|
|
14735
|
+
for (const [k, v] of Object.entries(o)) walk(v, `${path}/${k}`);
|
|
14736
|
+
};
|
|
14737
|
+
walk(value, "");
|
|
14738
|
+
return out;
|
|
14739
|
+
}
|
|
14579
14740
|
function createNodeContext(opts = {}) {
|
|
14580
14741
|
const schemasDir = resolveSchemasDir(opts.schemasDir);
|
|
14581
14742
|
const registry = createSchemaRegistry(loadSchemasFromDir(schemasDir));
|
|
@@ -14584,40 +14745,7 @@ function createNodeContext(opts = {}) {
|
|
|
14584
14745
|
}
|
|
14585
14746
|
|
|
14586
14747
|
// src/yaml.ts
|
|
14587
|
-
var
|
|
14588
|
-
|
|
14589
|
-
// src/types.ts
|
|
14590
|
-
var DEFAULT_RULE_SEVERITY = {
|
|
14591
|
-
"required-field": "error",
|
|
14592
|
-
"forbidden-field": "error",
|
|
14593
|
-
"unknown-field": "warning",
|
|
14594
|
-
"invalid-value": "error",
|
|
14595
|
-
"value-case": "warning",
|
|
14596
|
-
"invalid-type": "error",
|
|
14597
|
-
"invalid-format": "error",
|
|
14598
|
-
"unknown-casetype": "warning",
|
|
14599
|
-
"parse-error": "error",
|
|
14600
|
-
schema: "error"
|
|
14601
|
-
};
|
|
14602
|
-
var DEFAULT_LINTER_SETTINGS = {
|
|
14603
|
-
rules: {},
|
|
14604
|
-
failOn: "error",
|
|
14605
|
-
ignoreCasetypes: [],
|
|
14606
|
-
includeCasetypes: [],
|
|
14607
|
-
reportUncovered: false,
|
|
14608
|
-
include: ["**/*.yaml", "**/*.yml"],
|
|
14609
|
-
exclude: ["node_modules", "dist", ".git"]
|
|
14610
|
-
};
|
|
14611
|
-
function normalizeSettings(partial) {
|
|
14612
|
-
return {
|
|
14613
|
-
...DEFAULT_LINTER_SETTINGS,
|
|
14614
|
-
...partial,
|
|
14615
|
-
rules: { ...DEFAULT_LINTER_SETTINGS.rules, ...partial?.rules ?? {} }
|
|
14616
|
-
};
|
|
14617
|
-
}
|
|
14618
|
-
function severityFor(rule, settings) {
|
|
14619
|
-
return settings.rules[rule] ?? DEFAULT_RULE_SEVERITY[rule];
|
|
14620
|
-
}
|
|
14748
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
14621
14749
|
|
|
14622
14750
|
// src/lint.ts
|
|
14623
14751
|
function ruleForKeyword(keyword) {
|
|
@@ -14628,7 +14756,7 @@ function ruleForKeyword(keyword) {
|
|
|
14628
14756
|
return "forbidden-field";
|
|
14629
14757
|
case "additionalProperties":
|
|
14630
14758
|
case "unevaluatedProperties":
|
|
14631
|
-
return "
|
|
14759
|
+
return "additional-field";
|
|
14632
14760
|
case "const":
|
|
14633
14761
|
case "enum":
|
|
14634
14762
|
return "invalid-value";
|
|
@@ -14656,7 +14784,7 @@ function humanize(err) {
|
|
|
14656
14784
|
return `field "${lastSegment(err.instancePath) ?? ""}" is not allowed for this casetype state`;
|
|
14657
14785
|
case "additionalProperties":
|
|
14658
14786
|
case "unevaluatedProperties":
|
|
14659
|
-
return `
|
|
14787
|
+
return `additional field "${String(p.additionalProperty)}" not defined by the schema`;
|
|
14660
14788
|
case "const":
|
|
14661
14789
|
return `must equal ${JSON.stringify(p.allowedValue)}`;
|
|
14662
14790
|
case "enum":
|
|
@@ -14683,6 +14811,92 @@ function locate(err) {
|
|
|
14683
14811
|
}
|
|
14684
14812
|
return { field: lastSegment(err.instancePath), path: err.instancePath };
|
|
14685
14813
|
}
|
|
14814
|
+
function collectConsts(cond, out = /* @__PURE__ */ new Map()) {
|
|
14815
|
+
if (!cond || typeof cond !== "object") return out;
|
|
14816
|
+
const c = cond;
|
|
14817
|
+
const props = c.properties;
|
|
14818
|
+
if (props) {
|
|
14819
|
+
for (const [k, sub] of Object.entries(props)) {
|
|
14820
|
+
if (sub && typeof sub === "object" && "const" in sub) {
|
|
14821
|
+
(out.get(k) ?? out.set(k, /* @__PURE__ */ new Set()).get(k)).add(sub.const);
|
|
14822
|
+
}
|
|
14823
|
+
}
|
|
14824
|
+
}
|
|
14825
|
+
for (const key2 of ["allOf", "anyOf", "oneOf"]) {
|
|
14826
|
+
if (Array.isArray(c[key2])) c[key2].forEach((s) => collectConsts(s, out));
|
|
14827
|
+
}
|
|
14828
|
+
return out;
|
|
14829
|
+
}
|
|
14830
|
+
function parentPointer(pointer) {
|
|
14831
|
+
const i = pointer.lastIndexOf("/");
|
|
14832
|
+
return i <= 0 ? "" : pointer.slice(0, i);
|
|
14833
|
+
}
|
|
14834
|
+
function fmtStateValue(v) {
|
|
14835
|
+
if (v === void 0 || v === null || v === "") return "null";
|
|
14836
|
+
if (typeof v === "string") return v;
|
|
14837
|
+
if (typeof v === "object") return Array.isArray(v) ? `[${v.length}]` : "{\u2026}";
|
|
14838
|
+
return String(v);
|
|
14839
|
+
}
|
|
14840
|
+
function deref(schema, resolve2, docRoot) {
|
|
14841
|
+
let cur = schema;
|
|
14842
|
+
for (let i = 0; cur && typeof cur === "object" && typeof cur.$ref === "string" && i < 16; i++) {
|
|
14843
|
+
const ref = cur.$ref;
|
|
14844
|
+
const next = resolve2(ref, docRoot.v ?? void 0);
|
|
14845
|
+
if (!next) break;
|
|
14846
|
+
if (!ref.startsWith("#")) docRoot.v = next;
|
|
14847
|
+
cur = next;
|
|
14848
|
+
}
|
|
14849
|
+
return cur ?? null;
|
|
14850
|
+
}
|
|
14851
|
+
function propSchema(schema, key2) {
|
|
14852
|
+
const direct = schema.properties;
|
|
14853
|
+
if (direct && key2 in direct) return direct[key2];
|
|
14854
|
+
for (const branch of schema.allOf ?? []) {
|
|
14855
|
+
const t = branch.then ?? branch;
|
|
14856
|
+
const props = t.properties;
|
|
14857
|
+
if (props && key2 in props) return props[key2];
|
|
14858
|
+
}
|
|
14859
|
+
return null;
|
|
14860
|
+
}
|
|
14861
|
+
function schemaAtPointer(rootSchema, pointer, resolve2) {
|
|
14862
|
+
const docRoot = { v: rootSchema };
|
|
14863
|
+
let schema = deref(rootSchema, resolve2, docRoot);
|
|
14864
|
+
for (const raw of pointer.split("/").filter(Boolean)) {
|
|
14865
|
+
if (!schema) return null;
|
|
14866
|
+
if (/^\d+$/.test(raw)) {
|
|
14867
|
+
let items = schema.items;
|
|
14868
|
+
if (Array.isArray(items)) items = items[Number(raw)] ?? items[0];
|
|
14869
|
+
schema = deref(items, resolve2, docRoot);
|
|
14870
|
+
} else {
|
|
14871
|
+
const key2 = raw.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
14872
|
+
schema = deref(propSchema(schema, key2), resolve2, docRoot);
|
|
14873
|
+
}
|
|
14874
|
+
}
|
|
14875
|
+
return schema;
|
|
14876
|
+
}
|
|
14877
|
+
function describeState(rootSchema, err, root, resolve2) {
|
|
14878
|
+
const m = /^#\/allOf\/(\d+)\/(then|else)\b/.exec(err.schemaPath);
|
|
14879
|
+
if (!m || !rootSchema || typeof rootSchema !== "object") return null;
|
|
14880
|
+
const branch = m[2];
|
|
14881
|
+
const objPath = err.keyword === "false schema" ? parentPointer(err.instancePath) : err.instancePath;
|
|
14882
|
+
const govSchema = schemaAtPointer(rootSchema, objPath, resolve2);
|
|
14883
|
+
const allOf = govSchema?.allOf;
|
|
14884
|
+
const clause = Array.isArray(allOf) ? allOf[Number(m[1])] : void 0;
|
|
14885
|
+
if (!clause?.if) return null;
|
|
14886
|
+
const consts = collectConsts(clause.if);
|
|
14887
|
+
if (consts.size === 0) return null;
|
|
14888
|
+
const obj = valueAtPointer(root, objPath);
|
|
14889
|
+
if (!obj || typeof obj !== "object") return null;
|
|
14890
|
+
const rec = obj;
|
|
14891
|
+
if (branch === "then") {
|
|
14892
|
+
for (const [field, values] of consts) if (!values.has(rec[field])) return null;
|
|
14893
|
+
const parts2 = [...consts].map(([f, vs]) => `${f}=${fmtStateValue([...vs][0])}`);
|
|
14894
|
+
return `(${parts2.join(", ")})`;
|
|
14895
|
+
}
|
|
14896
|
+
for (const field of consts.keys()) if (!(field in rec)) return null;
|
|
14897
|
+
const parts = [...consts.keys()].map((f) => `${f}=${fmtStateValue(rec[f])}`);
|
|
14898
|
+
return `(${parts.join(", ")})`;
|
|
14899
|
+
}
|
|
14686
14900
|
function valueAtPointer(root, pointer) {
|
|
14687
14901
|
if (!pointer) return root;
|
|
14688
14902
|
let cur = root;
|
|
@@ -14734,6 +14948,7 @@ function lintDocument(ctx, input) {
|
|
|
14734
14948
|
push("unknown-casetype", "", casetype, casetype ? `no schema for casetype "${casetype}"` : "could not determine casetype (no `_type`)");
|
|
14735
14949
|
} else if (!validate(input.value)) {
|
|
14736
14950
|
const errors = validate.errors ?? [];
|
|
14951
|
+
const schema = validate.schema;
|
|
14737
14952
|
const forbidden = /* @__PURE__ */ new Set();
|
|
14738
14953
|
for (const e of errors) if (e.keyword === "false schema") forbidden.add(e.instancePath);
|
|
14739
14954
|
for (const e of errors) {
|
|
@@ -14752,7 +14967,13 @@ function lintDocument(ctx, input) {
|
|
|
14752
14967
|
continue;
|
|
14753
14968
|
}
|
|
14754
14969
|
}
|
|
14755
|
-
|
|
14970
|
+
const rule = ruleForKeyword(e.keyword);
|
|
14971
|
+
let message = humanize(e);
|
|
14972
|
+
if (rule === "forbidden-field" || rule === "required-field") {
|
|
14973
|
+
const state = describeState(schema, e, input.value, ctx.registry.resolveRef);
|
|
14974
|
+
if (state) message += ` ${state}`;
|
|
14975
|
+
}
|
|
14976
|
+
push(rule, path, field, message);
|
|
14756
14977
|
}
|
|
14757
14978
|
}
|
|
14758
14979
|
if (settings.reportUncovered && ctx.coverage && casetype) {
|
|
@@ -14793,7 +15014,7 @@ function isLintableYaml(text) {
|
|
|
14793
15014
|
// src/cli.ts
|
|
14794
15015
|
var PARALLEL_THRESHOLD = 200;
|
|
14795
15016
|
var MAX_WORKERS = 8;
|
|
14796
|
-
var VERSION = true ? "0.2.
|
|
15017
|
+
var VERSION = true ? "0.2.350" : "0.0.0-dev";
|
|
14797
15018
|
function isNewer(latest, current) {
|
|
14798
15019
|
const a = latest.split(".").map(Number);
|
|
14799
15020
|
const b = current.split(".").map(Number);
|
|
@@ -14812,19 +15033,30 @@ function checkForUpdate(disabled) {
|
|
|
14812
15033
|
const timer = setTimeout(() => ctrl.abort(), 1500);
|
|
14813
15034
|
return fetch("https://registry.npmjs.org/@grexx%2Fgrexxlinter/latest", { signal: ctrl.signal }).then((r) => r.ok ? r.json() : null).then((j) => j?.version && isNewer(j.version, VERSION) ? j.version : null).catch(() => null).finally(() => clearTimeout(timer));
|
|
14814
15035
|
}
|
|
15036
|
+
var VALID_RULES = new Set(LINT_RULE_DESCRIPTORS.map((d) => d.id));
|
|
14815
15037
|
function parseArgs(argv) {
|
|
14816
|
-
const args = { paths: [], format: "text", quiet: false, noUpdateCheck: false };
|
|
15038
|
+
const args = { paths: [], off: [], format: "text", quiet: false, noUpdateCheck: false };
|
|
14817
15039
|
for (let i = 0; i < argv.length; i++) {
|
|
14818
15040
|
const a = argv[i];
|
|
14819
15041
|
if (a === "--settings") args.settingsFile = argv[++i];
|
|
14820
|
-
else if (a === "--
|
|
15042
|
+
else if (a === "--off") {
|
|
15043
|
+
const list = (argv[++i] ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
15044
|
+
for (const r of list) {
|
|
15045
|
+
if (!VALID_RULES.has(r)) throw new Error(`--off: unknown rule "${r}" (valid: ${[...VALID_RULES].join(", ")})`);
|
|
15046
|
+
args.off.push(r);
|
|
15047
|
+
}
|
|
15048
|
+
} else if (a === "--schemas-dir") args.schemasDir = argv[++i];
|
|
14821
15049
|
else if (a === "--fail-on") args.failOn = argv[++i];
|
|
14822
15050
|
else if (a === "--format") args.format = argv[++i];
|
|
15051
|
+
else if (a === "--report") args.report = argv[++i];
|
|
14823
15052
|
else if (a === "--quiet") args.quiet = true;
|
|
14824
15053
|
else if (a === "--no-update-check") args.noUpdateCheck = true;
|
|
14825
15054
|
else if (a.startsWith("--")) throw new Error(`unknown option: ${a}`);
|
|
14826
15055
|
else args.paths.push(a);
|
|
14827
15056
|
}
|
|
15057
|
+
if (args.report && args.report !== "rules" && args.report !== "fields") {
|
|
15058
|
+
throw new Error(`--report must be "rules" or "fields" (got "${args.report}")`);
|
|
15059
|
+
}
|
|
14828
15060
|
if (args.paths.length === 0) args.paths.push(".");
|
|
14829
15061
|
return args;
|
|
14830
15062
|
}
|
|
@@ -14879,9 +15111,42 @@ function printText(results, settings, quiet) {
|
|
|
14879
15111
|
${s.files} file(s)${skipped ? `, ${skipped} skipped` : ""} \u2014 ${paint(`${s.errors} error(s)`, COLORS.error)}, ${paint(`${s.warnings} warning(s)`, COLORS.warning)}, ${s.infos} info(s) [fail-on: ${settings.failOn}]`
|
|
14880
15112
|
);
|
|
14881
15113
|
}
|
|
15114
|
+
function printReport(results, kind) {
|
|
15115
|
+
const msgs = results.filter((r) => !r.skipped).flatMap((r) => r.messages);
|
|
15116
|
+
if (kind === "rules") {
|
|
15117
|
+
const by2 = /* @__PURE__ */ new Map();
|
|
15118
|
+
for (const m of msgs) {
|
|
15119
|
+
const e = by2.get(m.rule) ?? { count: 0, severity: m.severity };
|
|
15120
|
+
e.count++;
|
|
15121
|
+
by2.set(m.rule, e);
|
|
15122
|
+
}
|
|
15123
|
+
const rows2 = [...by2.entries()].map(([rule, e]) => ({ count: e.count, rule, severity: e.severity })).sort((a, b) => b.count - a.count);
|
|
15124
|
+
const w = Math.max(5, ...rows2.map((r) => String(r.count).length));
|
|
15125
|
+
console.log(`${"count".padStart(w)} rule severity`);
|
|
15126
|
+
for (const r of rows2) {
|
|
15127
|
+
console.log(`${paint(String(r.count).padStart(w), COLORS[r.severity])} ${r.rule.padEnd(16)} ${r.severity}`);
|
|
15128
|
+
}
|
|
15129
|
+
return;
|
|
15130
|
+
}
|
|
15131
|
+
const by = /* @__PURE__ */ new Map();
|
|
15132
|
+
for (const m of msgs) {
|
|
15133
|
+
const key2 = `${m.casetype ?? "?"}\0${m.rule}\0${m.severity}\0${m.message}`;
|
|
15134
|
+
const e = by.get(key2) ?? { count: 0, casetype: m.casetype ?? "?", severity: m.severity, message: m.message };
|
|
15135
|
+
e.count++;
|
|
15136
|
+
by.set(key2, e);
|
|
15137
|
+
}
|
|
15138
|
+
const rows = [...by.values()].sort((a, b) => b.count - a.count || a.casetype.localeCompare(b.casetype));
|
|
15139
|
+
const cw = Math.max(5, ...rows.map((r) => String(r.count).length));
|
|
15140
|
+
const tw = Math.max(8, ...rows.map((r) => r.casetype.length));
|
|
15141
|
+
console.log(`${"count".padStart(cw)} ${"casetype".padEnd(tw)} severity message`);
|
|
15142
|
+
for (const r of rows) {
|
|
15143
|
+
console.log(`${paint(String(r.count).padStart(cw), COLORS[r.severity])} ${r.casetype.padEnd(tw)} ${r.severity.padEnd(8)} ${r.message}`);
|
|
15144
|
+
}
|
|
15145
|
+
}
|
|
14882
15146
|
var WORKER_FILE = fileURLToPath2(import.meta.url);
|
|
14883
15147
|
function lintFiles(files, ctx, settings) {
|
|
14884
15148
|
const out = [];
|
|
15149
|
+
const caseRefs = loadCaseRefs(ctx.schemasDir);
|
|
14885
15150
|
const parseErr = (file, message) => ({
|
|
14886
15151
|
file,
|
|
14887
15152
|
casetype: null,
|
|
@@ -14894,7 +15159,7 @@ function lintFiles(files, ctx, settings) {
|
|
|
14894
15159
|
if (!isLintableYaml(text)) continue;
|
|
14895
15160
|
let docs;
|
|
14896
15161
|
try {
|
|
14897
|
-
docs = (0,
|
|
15162
|
+
docs = (0, import_yaml3.parseAllDocuments)(text);
|
|
14898
15163
|
} catch (e) {
|
|
14899
15164
|
out.push(parseErr(file, e.message));
|
|
14900
15165
|
continue;
|
|
@@ -14905,7 +15170,13 @@ function lintFiles(files, ctx, settings) {
|
|
|
14905
15170
|
continue;
|
|
14906
15171
|
}
|
|
14907
15172
|
const input = { value: doc.toJS(), file };
|
|
14908
|
-
|
|
15173
|
+
const result = lintDocument({ registry: ctx.registry, coverage: ctx.coverage, settings }, input);
|
|
15174
|
+
const refs = referenceFindings(input.value, file, caseRefs, settings);
|
|
15175
|
+
if (refs.length) {
|
|
15176
|
+
result.messages.push(...refs);
|
|
15177
|
+
if (result.ok && refs.some((m) => meetsThreshold(m.severity, settings.failOn))) result.ok = false;
|
|
15178
|
+
}
|
|
15179
|
+
out.push(result);
|
|
14909
15180
|
}
|
|
14910
15181
|
}
|
|
14911
15182
|
return out;
|
|
@@ -14946,6 +15217,9 @@ async function main() {
|
|
|
14946
15217
|
let partial = {};
|
|
14947
15218
|
if (args.settingsFile) partial = JSON.parse(readFileSync2(args.settingsFile, "utf8"));
|
|
14948
15219
|
if (args.failOn) partial = { ...partial, failOn: args.failOn };
|
|
15220
|
+
if (args.off.length > 0) {
|
|
15221
|
+
partial = { ...partial, rules: { ...partial.rules, ...Object.fromEntries(args.off.map((r) => [r, "off"])) } };
|
|
15222
|
+
}
|
|
14949
15223
|
const settings = normalizeSettings(partial);
|
|
14950
15224
|
const files = discover(args.paths, settings.exclude);
|
|
14951
15225
|
const canParallel = WORKER_FILE.endsWith(".js") && files.length > PARALLEL_THRESHOLD;
|
|
@@ -14956,7 +15230,9 @@ async function main() {
|
|
|
14956
15230
|
const ctx = createNodeContext({ schemasDir: args.schemasDir });
|
|
14957
15231
|
results = lintFiles(files, ctx, settings);
|
|
14958
15232
|
}
|
|
14959
|
-
if (args.
|
|
15233
|
+
if (args.report) {
|
|
15234
|
+
printReport(results, args.report);
|
|
15235
|
+
} else if (args.format === "json") {
|
|
14960
15236
|
const active = results.filter((r) => !r.skipped);
|
|
14961
15237
|
console.log(JSON.stringify({ summary: summarize(active), results }, null, 2));
|
|
14962
15238
|
} else {
|