@grexx/grexxlinter 0.2.2770 → 0.2.2933

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 CHANGED
@@ -15052,6 +15052,35 @@ import { createRequire } from "node:module";
15052
15052
  import { dirname, join, relative, resolve, sep } from "node:path";
15053
15053
  import { fileURLToPath } from "node:url";
15054
15054
 
15055
+ // src/deps.ts
15056
+ var DEPS_MANIFEST_PATH = "studio.deps.yaml";
15057
+ function defaultDepMount(name) {
15058
+ return `vendor/${name}`;
15059
+ }
15060
+ var TRAILING_SLASHES = /\/{1,256}$/;
15061
+ function depMountsOf(manifest) {
15062
+ const deps = manifest?.dependencies;
15063
+ if (!Array.isArray(deps)) return [];
15064
+ const out = [];
15065
+ for (const entry of deps) {
15066
+ const { name, mount } = entry ?? {};
15067
+ if (typeof name !== "string" || name === "") continue;
15068
+ const raw = typeof mount === "string" && mount !== "" ? mount : defaultDepMount(name);
15069
+ const normalized = raw.replace(TRAILING_SLASHES, "");
15070
+ if (normalized === "") continue;
15071
+ out.push({ name, mount: normalized });
15072
+ }
15073
+ return out;
15074
+ }
15075
+ function depOfPath(path, mounts) {
15076
+ let best;
15077
+ for (const m of mounts) {
15078
+ if (!path.startsWith(`${m.mount}/`)) continue;
15079
+ if (best === void 0 || m.mount.length > best.mount.length) best = m;
15080
+ }
15081
+ return best?.name;
15082
+ }
15083
+
15055
15084
  // src/coverage.ts
15056
15085
  function condKey(c) {
15057
15086
  if (!c) return null;
@@ -15342,6 +15371,14 @@ function layoutRootOf(paths) {
15342
15371
  if (paths.length !== 1) return void 0;
15343
15372
  return statSync(paths[0]).isDirectory() ? paths[0] : void 0;
15344
15373
  }
15374
+ function readDepMounts(root) {
15375
+ if (root === void 0) return [];
15376
+ try {
15377
+ return depMountsOf((0, import_yaml.parse)(readFileSync(join(root, DEPS_MANIFEST_PATH), "utf8")));
15378
+ } catch {
15379
+ return [];
15380
+ }
15381
+ }
15345
15382
  function repoPathOf(root, file) {
15346
15383
  return root === void 0 ? void 0 : relative(root, file).split(sep).join("/");
15347
15384
  }
@@ -15750,13 +15787,18 @@ function parseErrorResult(file, message, settings) {
15750
15787
  messages: [{ severity: "error", rule: "parse-error", casetype: null, path: "", field: null, message, file }]
15751
15788
  };
15752
15789
  }
15790
+ function tagDep(result, repoPath, mounts) {
15791
+ const dep = repoPath === void 0 || mounts === void 0 ? void 0 : depOfPath(repoPath, mounts);
15792
+ if (dep !== void 0) for (const m of result.messages) m.dep = dep;
15793
+ return result;
15794
+ }
15753
15795
  function lintOneDoc(doc, file, ctx, settings, caseRefs) {
15754
- if (doc.errors.length > 0) return parseErrorResult(file, doc.errors[0].message, settings);
15796
+ const repoPath = repoPathOf(ctx.root, file);
15797
+ if (doc.errors.length > 0) return tagDep(parseErrorResult(file, doc.errors[0].message, settings), repoPath, ctx.depMounts);
15755
15798
  const resolved = toJSOrError(doc);
15756
- if (resolved.error !== void 0) return parseErrorResult(file, resolved.error, settings);
15799
+ if (resolved.error !== void 0) return tagDep(parseErrorResult(file, resolved.error, settings), repoPath, ctx.depMounts);
15757
15800
  const input = { value: resolved.value, file };
15758
15801
  const result = lintDocument({ registry: ctx.registry, coverage: ctx.coverage, settings }, input);
15759
- const repoPath = repoPathOf(ctx.root, file);
15760
15802
  const extra = [
15761
15803
  ...referenceFindings(input.value, file, caseRefs, settings),
15762
15804
  ...repoPath === void 0 ? [] : layoutFindings(input.value, repoPath, settings).map((m) => ({ ...m, file }))
@@ -15765,7 +15807,7 @@ function lintOneDoc(doc, file, ctx, settings, caseRefs) {
15765
15807
  result.messages.push(...extra);
15766
15808
  if (result.ok && extra.some((m) => meetsThreshold(m.severity, settings.failOn))) result.ok = false;
15767
15809
  }
15768
- return result;
15810
+ return tagDep(result, repoPath, ctx.depMounts);
15769
15811
  }
15770
15812
  function lintOneFile(file, ctx, settings, caseRefs) {
15771
15813
  const text = readFileSync2(file, "utf8");
@@ -15786,7 +15828,7 @@ function lintFiles(files, ctx, settings) {
15786
15828
  // src/cli.ts
15787
15829
  var PARALLEL_THRESHOLD = 200;
15788
15830
  var MAX_WORKERS = 8;
15789
- var VERSION = "0.2.2770";
15831
+ var VERSION = "0.2.2933";
15790
15832
  function isNewer(latest, current) {
15791
15833
  const a = latest.split(".").map(Number);
15792
15834
  const b = current.split(".").map(Number);
@@ -15840,12 +15882,15 @@ var BOOL_FLAGS = /* @__PURE__ */ new Map([
15840
15882
  ["--errors-only", (args) => {
15841
15883
  args.errorsOnly = true;
15842
15884
  }],
15885
+ ["--own-only", (args) => {
15886
+ args.ownOnly = true;
15887
+ }],
15843
15888
  ["--no-update-check", (args) => {
15844
15889
  args.noUpdateCheck = true;
15845
15890
  }]
15846
15891
  ]);
15847
15892
  function parseArgs(argv) {
15848
- const args = { paths: [], off: [], format: "text", quiet: false, errorsOnly: false, noUpdateCheck: false };
15893
+ const args = { paths: [], off: [], format: "text", quiet: false, errorsOnly: false, ownOnly: false, noUpdateCheck: false };
15849
15894
  for (let i = 0; i < argv.length; i++) {
15850
15895
  const a = argv[i];
15851
15896
  const withValue = VALUE_FLAGS.get(a);
@@ -15902,7 +15947,9 @@ function printResult(r, quiet, errorsOnly) {
15902
15947
  if (quiet && msgs.length === 0) return;
15903
15948
  const rel = r.file ? relative2(process.cwd(), r.file) : "<input>";
15904
15949
  const casetypeTag = r.casetype ? ` (${r.casetype})` : "";
15905
- const head = `${rel}${casetypeTag}`;
15950
+ const dep = msgs.find((m) => m.dep)?.dep;
15951
+ const depTag = dep ? ` [product: ${dep}]` : "";
15952
+ const head = `${rel}${casetypeTag}${depTag}`;
15906
15953
  if (msgs.length === 0) {
15907
15954
  if (!quiet) console.log(`${paint("ok", COLORS.info)} ${head}`);
15908
15955
  return;
@@ -15913,8 +15960,18 @@ function printResult(r, quiet, errorsOnly) {
15913
15960
  console.log(` ${paint(m.severity.padEnd(7), COLORS[m.severity])} ${m.rule.padEnd(16)} ${loc} ${m.message}`);
15914
15961
  }
15915
15962
  }
15963
+ function depFindingCounts(results) {
15964
+ const by = /* @__PURE__ */ new Map();
15965
+ for (const r of results) {
15966
+ for (const m of r.messages) {
15967
+ if (m.dep) by.set(m.dep, (by.get(m.dep) ?? 0) + 1);
15968
+ }
15969
+ }
15970
+ return by;
15971
+ }
15916
15972
  function printSummary(results, settings) {
15917
- const s = summarize(results.filter((r) => !r.skipped));
15973
+ const active = results.filter((r) => !r.skipped);
15974
+ const s = summarize(active);
15918
15975
  const skipped = results.filter((r) => r.skipped).length;
15919
15976
  const skippedNote = skipped ? `, ${skipped} skipped` : "";
15920
15977
  const errorsText = paint(`${s.errors} error(s)`, COLORS.error);
@@ -15923,6 +15980,11 @@ function printSummary(results, settings) {
15923
15980
  `
15924
15981
  ${s.files} file(s)${skippedNote} \u2014 ${errorsText}, ${warningsText}, ${s.infos} info(s) [fail-on: ${settings.failOn}]`
15925
15982
  );
15983
+ const byDep = depFindingCounts(active);
15984
+ if (byDep.size > 0) {
15985
+ const total = [...byDep.values()].reduce((a, b) => a + b, 0);
15986
+ console.log(`${total} of those are in ${byDep.size} installed marketplace product(s) \u2014 run with --own-only to drop them`);
15987
+ }
15926
15988
  }
15927
15989
  function printText(results, settings, quiet, errorsOnly) {
15928
15990
  for (const r of results) printResult(r, quiet, errorsOnly);
@@ -15961,8 +16023,15 @@ function printReport(results, kind, errorsOnly) {
15961
16023
  console.log(`${paint(String(r.count).padStart(cw), COLORS[r.severity])} ${r.casetype.padEnd(tw)} ${r.severity.padEnd(8)} ${r.message}`);
15962
16024
  }
15963
16025
  }
16026
+ function withoutDepFindings(results, settings) {
16027
+ return results.map((r) => {
16028
+ const messages = r.messages.filter((m) => !m.dep);
16029
+ if (messages.length === r.messages.length) return r;
16030
+ return { ...r, messages, ok: !messages.some((m) => meetsThreshold(m.severity, settings.failOn)) };
16031
+ });
16032
+ }
15964
16033
  var WORKER_FILE = fileURLToPath2(import.meta.url);
15965
- async function runParallel(files, settings, schemasDir, root) {
16034
+ async function runParallel(files, settings, schemasDir, root, depMounts) {
15966
16035
  const n = Math.min(MAX_WORKERS, availableParallelism(), files.length);
15967
16036
  const shards = Array.from({ length: n }, () => []);
15968
16037
  files.forEach((f, i) => shards[i % n].push(f));
@@ -15971,7 +16040,7 @@ async function runParallel(files, settings, schemasDir, root) {
15971
16040
  const parts = await Promise.all(
15972
16041
  shards.map(
15973
16042
  (shard) => new Promise((resolve2, reject) => {
15974
- const w = new Worker(WORKER_FILE, { workerData: { shard, settings, schemasDir, root } });
16043
+ const w = new Worker(WORKER_FILE, { workerData: { shard, settings, schemasDir, root, depMounts } });
15975
16044
  w.once("message", (m) => {
15976
16045
  void w.terminate();
15977
16046
  resolve2(m);
@@ -15984,13 +16053,13 @@ async function runParallel(files, settings, schemasDir, root) {
15984
16053
  } catch (e) {
15985
16054
  if (process.env.GREXXLINT_DEBUG) console.error("[grexxlint] worker pool failed, inline fallback:", e);
15986
16055
  const ctx = createNodeContext({ schemasDir });
15987
- return lintFiles(files, { ...ctx, root }, settings);
16056
+ return lintFiles(files, { ...ctx, root, depMounts }, settings);
15988
16057
  }
15989
16058
  }
15990
16059
  function runWorker() {
15991
- const { shard, settings, schemasDir, root } = workerData;
16060
+ const { shard, settings, schemasDir, root, depMounts } = workerData;
15992
16061
  const ctx = createNodeContext({ schemasDir });
15993
- parentPort.postMessage(lintFiles(shard, { ...ctx, root }, settings));
16062
+ parentPort.postMessage(lintFiles(shard, { ...ctx, root, depMounts }, settings));
15994
16063
  }
15995
16064
  async function main() {
15996
16065
  const args = parseArgs(process.argv.slice(2));
@@ -16004,14 +16073,16 @@ async function main() {
16004
16073
  const settings = normalizeSettings(partial);
16005
16074
  const files = discover(args.paths, settings.exclude);
16006
16075
  const root = layoutRootOf(args.paths);
16076
+ const depMounts = readDepMounts(root);
16007
16077
  const canParallel = WORKER_FILE.endsWith(".js") && files.length > PARALLEL_THRESHOLD;
16008
16078
  let results;
16009
16079
  if (canParallel) {
16010
- results = await runParallel(files, settings, args.schemasDir, root);
16080
+ results = await runParallel(files, settings, args.schemasDir, root, depMounts);
16011
16081
  } else {
16012
16082
  const ctx = createNodeContext({ schemasDir: args.schemasDir });
16013
- results = lintFiles(files, { ...ctx, root }, settings);
16083
+ results = lintFiles(files, { ...ctx, root, depMounts }, settings);
16014
16084
  }
16085
+ if (args.ownOnly) results = withoutDepFindings(results, settings);
16015
16086
  if (args.report) {
16016
16087
  printReport(results, args.report, args.errorsOnly);
16017
16088
  } else if (args.format === "json") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grexx/grexxlinter",
3
- "version": "0.2.2770",
3
+ "version": "0.2.2933",
4
4
  "description": "Lint Grexx Studio casetype YAML against the studio JSON Schemas.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -121,6 +121,17 @@
121
121
  },
122
122
  "cookieConsentEnabled": {
123
123
  "baseline": "MUST_NOT",
124
+ "requiredWhen": [
125
+ {
126
+ "op": "cmp",
127
+ "field": "type",
128
+ "fieldName": "Type metadata",
129
+ "operator": "==",
130
+ "value": "cookieconsent",
131
+ "valueIsField": false,
132
+ "isEmpty": false
133
+ }
134
+ ],
124
135
  "enabledWhen": [
125
136
  {
126
137
  "op": "cmp",
@@ -185,6 +196,17 @@
185
196
  },
186
197
  "cookieConsentType": {
187
198
  "baseline": "MUST_NOT",
199
+ "requiredWhen": [
200
+ {
201
+ "op": "cmp",
202
+ "field": "type",
203
+ "fieldName": "Type metadata",
204
+ "operator": "==",
205
+ "value": "cookieconsent",
206
+ "valueIsField": false,
207
+ "isEmpty": false
208
+ }
209
+ ],
188
210
  "enabledWhen": [
189
211
  {
190
212
  "op": "cmp",