@shivaedev/quality 0.3.0 → 0.4.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +70 -3
  3. package/dist/git/base.d.ts.map +1 -1
  4. package/dist/git/base.js +29 -4
  5. package/dist/git/base.js.map +1 -1
  6. package/dist/git/command.d.ts +1 -1
  7. package/dist/git/command.d.ts.map +1 -1
  8. package/dist/git/command.js +2 -2
  9. package/dist/git/command.js.map +1 -1
  10. package/dist/rules/suppressions/biome/configs.d.ts +21 -10
  11. package/dist/rules/suppressions/biome/configs.d.ts.map +1 -1
  12. package/dist/rules/suppressions/biome/configs.js +57 -21
  13. package/dist/rules/suppressions/biome/configs.js.map +1 -1
  14. package/dist/rules/suppressions/biome/declarations.d.ts +18 -0
  15. package/dist/rules/suppressions/biome/declarations.d.ts.map +1 -0
  16. package/dist/rules/suppressions/biome/declarations.js +20 -0
  17. package/dist/rules/suppressions/biome/declarations.js.map +1 -0
  18. package/dist/rules/suppressions/biome/resolve.d.ts +3 -0
  19. package/dist/rules/suppressions/biome/resolve.d.ts.map +1 -0
  20. package/dist/rules/suppressions/biome/resolve.js +70 -0
  21. package/dist/rules/suppressions/biome/resolve.js.map +1 -0
  22. package/dist/rules/suppressions/biome/weakenings.d.ts +3 -3
  23. package/dist/rules/suppressions/biome/weakenings.d.ts.map +1 -1
  24. package/dist/rules/suppressions/biome/weakenings.js +41 -33
  25. package/dist/rules/suppressions/biome/weakenings.js.map +1 -1
  26. package/dist/rules/suppressions/biome-overrides.d.ts.map +1 -1
  27. package/dist/rules/suppressions/biome-overrides.js +31 -23
  28. package/dist/rules/suppressions/biome-overrides.js.map +1 -1
  29. package/package.json +5 -3
  30. package/src/git/base.ts +40 -5
  31. package/src/git/command.ts +2 -2
  32. package/src/rules/suppressions/biome/configs.ts +97 -28
  33. package/src/rules/suppressions/biome/declarations.ts +37 -0
  34. package/src/rules/suppressions/biome/resolve.ts +86 -0
  35. package/src/rules/suppressions/biome/weakenings.ts +54 -37
  36. package/src/rules/suppressions/biome-overrides.ts +40 -31
  37. package/tsconfig/base.json +35 -0
  38. package/tsconfig/package.json +10 -0
@@ -1 +1 @@
1
- {"version":3,"file":"weakenings.d.ts","sourceRoot":"","sources":["../../../../src/rules/suppressions/biome/weakenings.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,IAAI,EAAkB,MAAM,WAAW,CAAC;AAEnF,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAwFD,eAAO,MAAM,YAAY,GAAI,QAAQ,IAAI,EAAE,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,aAAa,CAAC,SAAS,CAM9F,CAAC"}
1
+ {"version":3,"file":"weakenings.d.ts","sourceRoot":"","sources":["../../../../src/rules/suppressions/biome/weakenings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAIjD,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACtB;AA4GD,eAAO,MAAM,YAAY,GAAI,kBAAkB,KAAK,KAAG,aAAa,CAAC,SAAS,CACU,CAAC"}
@@ -1,34 +1,38 @@
1
+ import { scoped } from "./declarations.js";
1
2
  import { entriesOf, isFalse, itemsOf, member, textOf } from "./json.js";
2
3
  const LINT_WEAK = new Set(["off", "warn", "info"]);
3
4
  const ASSIST_WEAK = new Set(["off"]);
4
5
  const PRESETS = new Set(["recommended", "preset"]);
5
6
  const levelOf = (node) => textOf(node) ?? textOf(member(node, "level"));
6
- const at = (rule, node) => (node === undefined ? [] : [{ line: node.line, rule }]);
7
- const disabled = (rule, section) => isFalse(member(section, "enabled")) ? at(rule, member(section, "enabled")) : [];
8
- const withoutDefaults = (rule, set) => [
9
- ...(isFalse(member(set, "recommended")) ? at(rule, member(set, "recommended")) : []),
10
- ...(textOf(member(set, "preset")) === "none" ? at(rule, member(set, "preset")) : []),
7
+ const at = (rule, node, weak, wholeGroup = false) => node === undefined ? [] : [{ line: node.line, rule, weak, wholeGroup }];
8
+ const enabled = (rule, section) => at(rule, member(section, "enabled"), isFalse(member(section, "enabled")));
9
+ const defaults = (rule, set) => [
10
+ ...at(rule, member(set, "recommended"), isFalse(member(set, "recommended"))),
11
+ ...at(rule, member(set, "preset"), textOf(member(set, "preset")) === "none"),
11
12
  ];
13
+ const ruleSettings = (group, weak, rules) => entriesOf(rules)
14
+ .filter(([rule]) => !PRESETS.has(rule))
15
+ .flatMap(([rule, setting]) => {
16
+ const level = levelOf(setting);
17
+ return level === undefined ? [] : at(`${group}/${rule}`, setting, weak.has(level));
18
+ });
12
19
  const groupSettings = (prefix, weak, groups) => entriesOf(groups)
13
20
  .filter(([group]) => !PRESETS.has(group))
14
- .flatMap(([group, rules]) => [
15
- ...(weak.has(textOf(rules) ?? "") ? at(`${prefix}/${group}`, rules) : []),
16
- ...withoutDefaults(`${prefix}/${group}`, rules),
17
- ...entriesOf(rules)
18
- .filter(([rule, setting]) => !PRESETS.has(rule) && weak.has(levelOf(setting) ?? ""))
19
- .flatMap(([rule, setting]) => at(`${prefix}/${group}/${rule}`, setting)),
20
- ]);
21
+ .flatMap(([group, rules]) => {
22
+ const severity = textOf(rules);
23
+ return severity === undefined
24
+ ? [...defaults(`${prefix}/${group}`, rules), ...ruleSettings(`${prefix}/${group}`, weak, rules)]
25
+ : at(`${prefix}/${group}`, rules, weak.has(severity), true);
26
+ });
21
27
  const linter = (section) => [
22
- ...disabled("lint", section),
23
- ...withoutDefaults("lint/recommended", member(section, "rules")),
28
+ ...enabled("lint", section),
29
+ ...defaults("lint/recommended", member(section, "rules")),
24
30
  ...groupSettings("lint", LINT_WEAK, member(section, "rules")),
25
- ...entriesOf(member(section, "domains"))
26
- .filter(([, value]) => textOf(value) === "none")
27
- .flatMap(([domain, value]) => at(`lint/domains/${domain}`, value)),
31
+ ...entriesOf(member(section, "domains")).flatMap(([domain, value]) => at(`lint/domains/${domain}`, value, textOf(value) === "none")),
28
32
  ];
29
33
  const assist = (section) => [
30
- ...disabled("assist", section),
31
- ...withoutDefaults("assist/recommended", member(section, "actions")),
34
+ ...enabled("assist", section),
35
+ ...defaults("assist/recommended", member(section, "actions")),
32
36
  ...groupSettings("assist", ASSIST_WEAK, member(section, "actions")),
33
37
  ];
34
38
  const LANGUAGES = ["javascript", "json", "css", "graphql", "grit", "html"];
@@ -38,31 +42,35 @@ const TOOLS = [
38
42
  ["formatter", "format"],
39
43
  ];
40
44
  const SCOPED_SECTIONS = [["files", "files"], ...TOOLS];
41
- const scopeSettings = (scope) => [
45
+ const settingsOf = (scope) => [
42
46
  ...linter(member(scope, "linter")),
43
47
  ...assist(member(scope, "assist")),
44
- ...disabled("format", member(scope, "formatter")),
45
- ...LANGUAGES.flatMap((language) => TOOLS.flatMap(([key, name]) => disabled(`${language}/${name}`, member(member(scope, language), key)))),
48
+ ...enabled("format", member(scope, "formatter")),
49
+ ...LANGUAGES.flatMap((language) => TOOLS.flatMap(([key, name]) => enabled(`${language}/${name}`, member(member(scope, language), key)))),
46
50
  ];
47
- const scoped = (base, glob) => {
48
- const [, negation = "", path = glob] = /^(!*)(.*)$/.exec(glob) ?? [];
49
- return base === "" ? glob : `${negation}${base}/${path}`;
50
- };
51
+ const replaces = (later, earlier) => later.rule === earlier.rule || (later.wholeGroup && earlier.rule.startsWith(`${later.rule}/`));
51
52
  const includesOf = (override) => {
52
53
  const includes = itemsOf(member(override, "includes")).flatMap((item) => textOf(item) ?? []);
53
54
  return includes.length === 0 ? ["**"] : includes;
54
55
  };
55
56
  const narrowsScope = (globs) => globs.length > 0 && (globs.some((glob) => glob.startsWith("!")) || !globs.includes("**"));
56
- const narrowings = (config, file, base) => SCOPED_SECTIONS.flatMap(([key, name]) => {
57
+ const narrowings = (config, base) => SCOPED_SECTIONS.flatMap(([key, name]) => {
57
58
  const list = member(member(config, key), "includes");
58
59
  const globs = itemsOf(list).flatMap((item) => textOf(item) ?? []);
59
60
  return list === undefined || !narrowsScope(globs)
60
61
  ? []
61
- : [{ file, includes: globs.map((glob) => scoped(base, glob)), line: list.line, rule: `${name}/includes` }];
62
+ : [{ includes: globs.map((glob) => scoped(base, glob)), line: list.line, rule: `${name}/includes` }];
62
63
  });
63
- export const weakeningsOf = (config, file, base) => [
64
- ...narrowings(config, file, base),
65
- ...scopeSettings(config).map((setting) => ({ ...setting, file, includes: [scoped(base, "**")] })),
66
- ...itemsOf(member(config, "overrides")).flatMap((override) => scopeSettings(override).map((setting) => ({ ...setting, file, includes: includesOf(override).map((glob) => scoped(base, glob)) }))),
67
- ];
64
+ const layerWeakenings = (layer, later, base) => {
65
+ const overriding = later.flatMap((next) => settingsOf(next.json));
66
+ const top = settingsOf(layer.json).filter((setting) => setting.weak && !overriding.some((next) => replaces(next, setting)));
67
+ return [
68
+ ...narrowings(layer.json, base),
69
+ ...top.map(({ line, rule }) => ({ includes: [scoped(base, "**")], line, rule })),
70
+ ...itemsOf(member(layer.json, "overrides")).flatMap((override) => settingsOf(override)
71
+ .filter((setting) => setting.weak)
72
+ .map(({ line, rule }) => ({ includes: includesOf(override).map((glob) => scoped(base, glob)), line, rule }))),
73
+ ].map((weakening) => ({ ...weakening, layer }));
74
+ };
75
+ export const weakeningsOf = ({ base, layers }) => layers.flatMap((layer, index) => layerWeakenings(layer, layers.slice(index + 1), base));
68
76
  //# sourceMappingURL=weakenings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"weakenings.js","sourceRoot":"","sources":["../../../../src/rules/suppressions/biome/weakenings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"weakenings.js","sourceRoot":"","sources":["../../../../src/rules/suppressions/biome/weakenings.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAa,MAAM,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAgBnF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AACnD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AACrC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC;AAEnD,MAAM,OAAO,GAAG,CAAC,IAAU,EAAsB,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AAElG,MAAM,EAAE,GAAG,CAAC,IAAY,EAAE,IAAsB,EAAE,IAAa,EAAE,UAAU,GAAG,KAAK,EAA0B,EAAE,CAC9G,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;AAEzE,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,OAAyB,EAA0B,EAAE,CACnF,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;AAE3E,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,GAAqB,EAA0B,EAAE,CAAC;IACjF,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC;IAC5E,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,KAAK,MAAM,CAAC;CAC5E,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,KAAa,EAAE,IAAyB,EAAE,KAAW,EAA0B,EAAE,CACtG,SAAS,CAAC,KAAK,CAAC;KACd,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;KACtC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AACpF,CAAC,CAAC,CAAC;AAEL,MAAM,aAAa,GAAG,CAAC,MAAc,EAAE,IAAyB,EAAE,MAAwB,EAA0B,EAAE,CACrH,SAAS,CAAC,MAAM,CAAC;KACf,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;KACxC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE;IAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,QAAQ,KAAK,SAAS;QAC5B,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,EAAE,KAAK,CAAC,EAAE,GAAG,YAAY,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAChG,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;AAC9D,CAAC,CAAC,CAAC;AAEL,MAAM,MAAM,GAAG,CAAC,OAAyB,EAA0B,EAAE,CAAC;IACrE,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;IAC3B,GAAG,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACzD,GAAG,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC7D,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,gBAAgB,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,CAAC;CACpI,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,OAAyB,EAA0B,EAAE,CAAC;IACrE,GAAG,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC7B,GAAG,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC7D,GAAG,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;CACnE,CAAC;AAEF,MAAM,SAAS,GAA0B,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAElG,MAAM,KAAK,GAA6C;IACvD,CAAC,QAAQ,EAAE,MAAM,CAAC;IAClB,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACpB,CAAC,WAAW,EAAE,QAAQ,CAAC;CACvB,CAAC;AAEF,MAAM,eAAe,GAA6C,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC;AAEjG,MAAM,UAAU,GAAG,CAAC,KAAW,EAA0B,EAAE,CAAC;IAC3D,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAClC,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAClC,GAAG,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IAChD,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,QAAQ,IAAI,IAAI,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;CACxI,CAAC;AAEF,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAE,OAAgB,EAAW,EAAE,CAC9D,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;AAEhG,MAAM,UAAU,GAAG,CAAC,QAAc,EAAyB,EAAE;IAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7F,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAClD,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,KAA4B,EAAW,EAAE,CAC9D,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAE3F,MAAM,UAAU,GAAG,CAAC,MAAY,EAAE,IAAY,EAA2C,EAAE,CAC1F,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE;IACvC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAClE,OAAO,IAAI,KAAK,SAAS,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;QAChD,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,WAAW,EAAE,CAAC,CAAC;AACvG,CAAC,CAAC,CAAC;AAEJ,MAAM,eAAe,GAAG,CAAC,KAAY,EAAE,KAA2B,EAAE,IAAY,EAA4B,EAAE;IAC7G,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAC5H,OAAO;QACN,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;QAC/B,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAChF,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAChE,UAAU,CAAC,QAAQ,CAAC;aAClB,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;aACjC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAC7G;KACD,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAS,EAA4B,EAAE,CACjF,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"biome-overrides.d.ts","sourceRoot":"","sources":["../../../src/rules/suppressions/biome-overrides.ts"],"names":[],"mappings":"AAyCA,eAAO,MAAM,cAAc;;;;;;EAmBzB,CAAC"}
1
+ {"version":3,"file":"biome-overrides.d.ts","sourceRoot":"","sources":["../../../src/rules/suppressions/biome-overrides.ts"],"names":[],"mappings":"AAmDA,eAAO,MAAM,cAAc;;;;;;EAkBzB,CAAC"}
@@ -1,46 +1,54 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import { defineRule } from "../../rule.js";
3
3
  import { biomeConfigs } from "./biome/configs.js";
4
+ import { Declaration, matches, scoped, scopeText } from "./biome/declarations.js";
4
5
  import { weakeningsOf } from "./biome/weakenings.js";
5
- const Declaration = Schema.Struct({
6
- rule: Schema.NonEmptyString,
7
- includes: Schema.NonEmptyArray(Schema.NonEmptyString),
8
- reason: Schema.String.check(Schema.isPattern(/\S/, { expected: "a reason that says why the scope needs the rule weakened" })),
9
- });
10
6
  const BiomeOverridesOptions = Schema.Struct({
11
7
  declared: Schema.Array(Declaration).pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
12
8
  });
13
- const sameScope = (left, right) => new Set(left).size === new Set(right).size && left.every((glob) => right.includes(glob));
14
- const matches = (declaration, weakening) => declaration.rule === weakening.rule && sameScope(declaration.includes, weakening.includes);
15
- const scopeText = (includes) => includes.map((glob) => `"${glob}"`).join(", ");
16
- const undeclared = (weakening) => ({
17
- file: weakening.file,
18
- line: weakening.line,
19
- message: `Weakens "${weakening.rule}" for ${scopeText(weakening.includes)} without a declaration.`,
20
- subject: weakening.rule,
21
- });
9
+ const shippedBy = ({ base, layers }) => layers.flatMap(({ preset }) => preset === undefined
10
+ ? []
11
+ : preset.declared.map(({ includes, rule }) => ({ includes: includes.map((glob) => scoped(base, glob)), rule, specifier: preset.specifier })));
12
+ const undeclared = ({ includes, layer, line, rule }) => layer.preset === undefined
13
+ ? { file: layer.path, line, message: `Weakens "${rule}" for ${scopeText(includes)} without a declaration.`, subject: rule }
14
+ : {
15
+ file: layer.preset.file,
16
+ line: layer.preset.line,
17
+ message: `Extends "${layer.preset.specifier}", which weakens "${rule}" for ${scopeText(includes)} without a declaration.`,
18
+ subject: rule,
19
+ };
22
20
  const unused = (file, declaration) => ({
23
21
  file,
24
22
  message: `Declares "${declaration.rule}" for ${scopeText(declaration.includes)}, which no Biome config weakens. Remove the declaration.`,
25
23
  subject: declaration.rule,
26
24
  });
27
- const unreadable = (config) => config._tag === "Unreadable" ? [{ file: config.path, message: `Cannot read this Biome config: ${config.reason}.` }] : [];
25
+ const repeated = (file, declaration, shipped) => ({
26
+ file,
27
+ message: `Declares "${declaration.rule}" for ${scopeText(declaration.includes)}, which "${shipped.specifier}" already declares. Remove the declaration.`,
28
+ subject: declaration.rule,
29
+ });
30
+ const staleOrRepeated = (file, weakenings, shipped) => (declaration) => {
31
+ if (!weakenings.some((weakening) => matches(declaration, weakening))) {
32
+ return [unused(file, declaration)];
33
+ }
34
+ const already = shipped.find((candidate) => matches(declaration, candidate));
35
+ return already === undefined ? [] : [repeated(file, declaration, already)];
36
+ };
28
37
  export const biomeOverrides = defineRule({
29
38
  id: "suppressions/biome-overrides",
30
39
  description: "A Biome setting that turns a check off or down, or keeps files out of it, is an exception for every file it covers. Fix the code and remove the setting, or declare its rule, includes and reason under this rule's declared option.",
31
40
  options: Schema.toStandardSchemaV1(BiomeOverridesOptions, { parseOptions: { errors: "all", onExcessProperty: "error" } }),
32
41
  registrable: false,
33
42
  check: async (inputs) => {
34
- const configs = await biomeConfigs(inputs);
43
+ const { chains, problems, root } = await biomeConfigs(inputs);
35
44
  const { declared } = inputs.options;
36
- const weakenings = configs.flatMap((config) => (config._tag === "Config" ? weakeningsOf(config.json, config.path, config.base) : []));
37
- const root = configs.find((config) => !config.path.includes("/"))?.path ?? "biome.json";
45
+ const weakenings = chains.flatMap(weakeningsOf);
46
+ const shipped = chains.flatMap(shippedBy);
47
+ const covered = (weakening) => [...declared, ...shipped].some((declaration) => matches(declaration, weakening));
38
48
  return [
39
- ...configs.flatMap(unreadable),
40
- ...weakenings.filter((weakening) => !declared.some((declaration) => matches(declaration, weakening))).map(undeclared),
41
- ...declared
42
- .filter((declaration) => !weakenings.some((weakening) => matches(declaration, weakening)))
43
- .map((declaration) => unused(root, declaration)),
49
+ ...problems,
50
+ ...weakenings.filter((weakening) => !covered(weakening)).map(undeclared),
51
+ ...declared.flatMap(staleOrRepeated(root, weakenings, shipped)),
44
52
  ];
45
53
  },
46
54
  });
@@ -1 +1 @@
1
- {"version":3,"file":"biome-overrides.js","sourceRoot":"","sources":["../../../src/rules/suppressions/biome-overrides.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"biome-overrides.js","sourceRoot":"","sources":["../../../src/rules/suppressions/biome-overrides.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/quality",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Repository quality gate with typed rules, a shrink-only baseline and a registry of reasoned exceptions",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,6 +23,7 @@
23
23
  "files": [
24
24
  "dist",
25
25
  "src",
26
+ "tsconfig",
26
27
  "CHANGELOG.md",
27
28
  "README.md"
28
29
  ],
@@ -33,6 +34,8 @@
33
34
  "import": "./dist/index.js",
34
35
  "default": "./dist/index.js"
35
36
  },
37
+ "./tsconfig/base.json": "./tsconfig/base.json",
38
+ "./tsconfig/package.json": "./tsconfig/package.json",
36
39
  "./package.json": "./package.json"
37
40
  },
38
41
  "publishConfig": {
@@ -62,7 +65,6 @@
62
65
  "check": "biome check .",
63
66
  "test": "vitest run",
64
67
  "typecheck": "tsc --noEmit",
65
- "typecheck:compat": "tsc6 --noEmit",
66
- "ready": "pnpm check && pnpm typecheck && pnpm typecheck:compat && pnpm test && pnpm build"
68
+ "ready": "pnpm check && pnpm typecheck && pnpm test && pnpm build"
67
69
  }
68
70
  }
package/src/git/base.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Effect } from "effect";
1
+ import { Console, Effect } from "effect";
2
2
  import { SetupFailure } from "../failure.ts";
3
3
  import { type Git, git } from "./command.ts";
4
4
 
@@ -30,15 +30,51 @@ const defaultBranch = (root: string): Effect.Effect<string, SetupFailure, Git> =
30
30
  );
31
31
  });
32
32
 
33
+ const isShallow = (root: string): Effect.Effect<boolean, SetupFailure, Git> =>
34
+ Effect.map(git(root, ["rev-parse", "--is-shallow-repository"]), (result) => result.stdout.trim() === "true");
35
+
36
+ const mergeBase = (root: string, ref: string): Effect.Effect<string | undefined, SetupFailure, Git> =>
37
+ Effect.map(git(root, ["merge-base", "HEAD", ref]), (result) => (result.code === 0 ? result.stdout.trim() : undefined));
38
+
39
+ const remoteOf = (root: string, ref: string): Effect.Effect<ReadonlyArray<string>, SetupFailure, Git> =>
40
+ Effect.gen(function* () {
41
+ const name = (yield* git(root, ["rev-parse", "--symbolic-full-name", ref])).stdout.trim();
42
+ const remotes = (yield* git(root, ["remote"])).stdout.split("\n");
43
+ return remotes.filter((remote) => remote !== "" && name.startsWith(`refs/remotes/${remote}/`)).slice(0, 1);
44
+ });
45
+
46
+ const unshallow = (root: string, ref: string): Effect.Effect<void, SetupFailure, Git> =>
47
+ Effect.gen(function* () {
48
+ const remote = yield* remoteOf(root, ref);
49
+ yield* Console.error(`quality: the clone is shallow; fetching its full history to find the merge base of HEAD and ${ref}.`);
50
+ yield* git(root, ["fetch", "--quiet", "--unshallow", ...remote], { GIT_TERMINAL_PROMPT: "0" });
51
+ });
52
+
33
53
  const missingMergeBase = (root: string, ref: string): Effect.Effect<never, SetupFailure, Git> =>
34
- Effect.flatMap(git(root, ["rev-parse", "--is-shallow-repository"]), (shallow) =>
54
+ Effect.flatMap(isShallow(root), (shallow) =>
35
55
  fail(
36
- shallow.stdout.trim() === "true"
56
+ shallow
37
57
  ? `the clone is shallow and does not reach the merge base of HEAD and ${ref}. Fetch the history first: \`git fetch --unshallow\`, or fetch-depth: 0 in actions/checkout.`
38
58
  : `HEAD and ${ref} share no history.`,
39
59
  ),
40
60
  );
41
61
 
62
+ const reachMergeBase = (root: string, ref: string): Effect.Effect<string, SetupFailure, Git> =>
63
+ Effect.gen(function* () {
64
+ const found = yield* mergeBase(root, ref);
65
+ if (found !== undefined) {
66
+ return found;
67
+ }
68
+ if (yield* isShallow(root)) {
69
+ yield* unshallow(root, ref);
70
+ const deepened = yield* mergeBase(root, ref);
71
+ if (deepened !== undefined) {
72
+ return deepened;
73
+ }
74
+ }
75
+ return yield* missingMergeBase(root, ref);
76
+ });
77
+
42
78
  export const resolveBase = (root: string, against: string | undefined): Effect.Effect<Base, SetupFailure, Git> =>
43
79
  Effect.gen(function* () {
44
80
  const inside = yield* git(root, ["rev-parse", "--is-inside-work-tree"]);
@@ -49,6 +85,5 @@ export const resolveBase = (root: string, against: string | undefined): Effect.E
49
85
  if (!(yield* isCommit(root, ref))) {
50
86
  return yield* fail(`${ref} names no commit in this clone. Fetch it first, or pass another --against <ref>.`);
51
87
  }
52
- const merged = yield* git(root, ["merge-base", "HEAD", ref]);
53
- return merged.code === 0 ? { commit: merged.stdout.trim(), ref } : yield* missingMergeBase(root, ref);
88
+ return { commit: yield* reachMergeBase(root, ref), ref };
54
89
  });
@@ -10,11 +10,11 @@ export interface GitResult {
10
10
 
11
11
  export type Git = ChildProcessSpawner.ChildProcessSpawner;
12
12
 
13
- export const git = (cwd: string, args: ReadonlyArray<string>): Effect.Effect<GitResult, SetupFailure, Git> =>
13
+ export const git = (cwd: string, args: ReadonlyArray<string>, env?: Readonly<Record<string, string>>): Effect.Effect<GitResult, SetupFailure, Git> =>
14
14
  Effect.scoped(
15
15
  Effect.gen(function* () {
16
16
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
17
- const child = yield* spawner.spawn(ChildProcess.make("git", [...args], { cwd, stdin: "ignore" }));
17
+ const child = yield* spawner.spawn(ChildProcess.make("git", [...args], { cwd, env, extendEnv: true, stdin: "ignore" }));
18
18
  const [stdout, stderr, code] = yield* Effect.all(
19
19
  [Stream.mkString(Stream.decodeText(child.stdout)), Stream.mkString(Stream.decodeText(child.stderr)), child.exitCode],
20
20
  { concurrency: "unbounded" },
@@ -1,50 +1,119 @@
1
1
  import { posix } from "node:path";
2
- import type { RuleInputs } from "../../../rule.ts";
3
- import { itemsOf, type Json, member, parseJsonc, textOf } from "./json.ts";
2
+ import type { Finding, RuleInputs } from "../../../rule.ts";
3
+ import { type Declaration, decodeShipped, PRESET_DECLARATIONS } from "./declarations.ts";
4
+ import { itemsOf, type Json, member, type Parsed, parseJsonc, textOf } from "./json.ts";
5
+ import { resolvePackage } from "./resolve.ts";
4
6
 
5
- export type Config =
6
- | { readonly _tag: "Config"; readonly path: string; readonly base: string; readonly json: Json }
7
- | { readonly _tag: "Unreadable"; readonly path: string; readonly reason: string };
7
+ export interface Preset {
8
+ readonly specifier: string;
9
+ readonly file: string;
10
+ readonly line: number;
11
+ readonly declared: ReadonlyArray<Declaration>;
12
+ }
13
+
14
+ export interface Layer {
15
+ readonly path: string;
16
+ readonly json: Json;
17
+ readonly preset: Preset | undefined;
18
+ }
19
+
20
+ export interface Chain {
21
+ readonly base: string;
22
+ readonly layers: ReadonlyArray<Layer>;
23
+ }
24
+
25
+ interface Loaded {
26
+ readonly layers: ReadonlyArray<Layer>;
27
+ readonly problems: ReadonlyArray<Finding>;
28
+ }
29
+
30
+ type ReadText = RuleInputs["readText"];
8
31
 
9
32
  const ROOT_CONFIGS: ReadonlyArray<string> = ["biome.json", "biome.jsonc"];
10
33
 
11
34
  const NESTED = /(?:^|\/)biome\.jsonc?$/;
12
35
 
36
+ const RELATIVE = /^\.\.?(?:\/|$)/;
37
+
13
38
  const directoryOf = (path: string): string => (posix.dirname(path) === "." ? "" : posix.dirname(path));
14
39
 
15
- const localExtends = (path: string, json: Json): ReadonlyArray<string> => {
16
- const node = member(json, "extends");
17
- const entries = node?._tag === "Array" ? itemsOf(node).flatMap((item) => textOf(item) ?? []) : [textOf(node) ?? ""];
18
- return entries.filter((entry) => entry.startsWith(".")).map((entry) => posix.join(posix.dirname(path), entry));
40
+ const read = async (readText: ReadText, path: string): Promise<Parsed> => {
41
+ const text = await readText(path);
42
+ return text === undefined ? { _tag: "Unreadable", reason: "the file does not exist" } : parseJsonc(path, text);
19
43
  };
20
44
 
21
- const load = async (readText: RuleInputs["readText"], path: string, base: string, seen: Set<string>): Promise<ReadonlyArray<Config>> => {
22
- if (seen.has(path)) {
23
- return [];
24
- }
25
- seen.add(path);
26
- const text = await readText(path);
27
- if (text === undefined) {
28
- return [{ _tag: "Unreadable", path, reason: "the file does not exist" }];
45
+ const problem = (file: string, message: string, line?: number): Loaded => ({ layers: [], problems: [{ file, line, message }] });
46
+
47
+ const local = async (readText: ReadText, path: string): Promise<Loaded> => {
48
+ const parsed = await read(readText, path);
49
+ return parsed._tag === "Parsed"
50
+ ? { layers: [{ json: parsed.json, path, preset: undefined }], problems: [] }
51
+ : problem(path, `Cannot read this Biome config: ${parsed.reason}.`);
52
+ };
53
+
54
+ const fromPackage = async (readText: ReadText, from: string, entry: Json, specifier: string): Promise<Loaded> => {
55
+ const target = await resolvePackage(readText, specifier);
56
+ if (target === undefined) {
57
+ return problem(from, `Cannot resolve the Biome config "${specifier}" from node_modules.`, entry.line);
29
58
  }
30
- const parsed = parseJsonc(path, text);
59
+ const parsed = await read(readText, target);
31
60
  if (parsed._tag === "Unreadable") {
32
- return [{ _tag: "Unreadable", path, reason: parsed.reason }];
61
+ return problem(from, `Cannot read the Biome config "${specifier}" at ${target}: ${parsed.reason}.`, entry.line);
33
62
  }
34
- const extended = await Promise.all(localExtends(path, parsed.json).map((target) => load(readText, target, base, seen)));
35
- return [{ _tag: "Config", base, json: parsed.json, path }, ...extended.flat()];
63
+ const shipped = posix.join(posix.dirname(target), PRESET_DECLARATIONS);
64
+ const declared = await decodeShipped(await readText(shipped));
65
+ if (declared._tag === "Invalid") {
66
+ return problem(from, `Cannot read the declarations shipped with "${specifier}" at ${shipped}: ${declared.issues.join("; ")}.`, entry.line);
67
+ }
68
+ const preset = { declared: declared.value, file: from, line: entry.line, specifier };
69
+ return { layers: [{ json: parsed.json, path: target, preset }], problems: [] };
70
+ };
71
+
72
+ const extended = async (readText: ReadText, from: string, entry: Json): Promise<Loaded> => {
73
+ const specifier = textOf(entry);
74
+ if (specifier === undefined || specifier === "//") {
75
+ return { layers: [], problems: [] };
76
+ }
77
+ if (RELATIVE.test(specifier)) {
78
+ return local(readText, posix.join(posix.dirname(from), specifier));
79
+ }
80
+ const relative = posix.normalize(specifier);
81
+ return (await readText(relative)) === undefined ? fromPackage(readText, from, entry, specifier) : local(readText, relative);
36
82
  };
37
83
 
38
- const rootConfig = async (readText: RuleInputs["readText"]): Promise<string | undefined> => {
84
+ const extendsOf = (json: Json): ReadonlyArray<Json> => itemsOf(member(json, "extends"));
85
+
86
+ const chainOf = async (readText: ReadText, path: string): Promise<Loaded> => {
87
+ const own = await local(readText, path);
88
+ const [layer] = own.layers;
89
+ if (layer === undefined) {
90
+ return own;
91
+ }
92
+ const parents = await Promise.all(extendsOf(layer.json).map((entry) => extended(readText, path, entry)));
93
+ return { layers: [...parents.flatMap((parent) => parent.layers), layer], problems: parents.flatMap((parent) => parent.problems) };
94
+ };
95
+
96
+ const rootConfig = async (readText: ReadText): Promise<string | undefined> => {
39
97
  const present = await Promise.all(ROOT_CONFIGS.map(async (path) => ((await readText(path)) === undefined ? [] : [path])));
40
98
  return present.flat()[0];
41
99
  };
42
100
 
43
- export const biomeConfigs = async ({ files, readText }: RuleInputs): Promise<ReadonlyArray<Config>> => {
44
- const seen = new Set<string>();
101
+ export interface BiomeConfigs {
102
+ readonly root: string;
103
+ readonly chains: ReadonlyArray<Chain>;
104
+ readonly problems: ReadonlyArray<Finding>;
105
+ }
106
+
107
+ export const biomeConfigs = async ({ files, readText }: RuleInputs): Promise<BiomeConfigs> => {
45
108
  const root = await rootConfig(readText);
46
- const nested = files.filter((path) => NESTED.test(path) && path !== root);
47
- const roots = root === undefined ? [] : await load(readText, root, "", seen);
48
- const below = await Promise.all(nested.map((path) => load(readText, path, directoryOf(path), seen)));
49
- return [...roots, ...below.flat()];
109
+ const rootChain = root === undefined ? { layers: [], problems: [] } : await chainOf(readText, root);
110
+ const loaded = new Set(rootChain.layers.map((layer) => layer.path));
111
+ const nested = files.filter((path) => NESTED.test(path) && path !== root && !loaded.has(path));
112
+ const below = await Promise.all(nested.map(async (path) => ({ base: directoryOf(path), ...(await chainOf(readText, path)) })));
113
+ const all = [{ base: "", ...rootChain }, ...below];
114
+ return {
115
+ chains: all.filter((chain) => chain.layers.length > 0).map(({ base, layers }) => ({ base, layers })),
116
+ problems: all.flatMap((chain) => chain.problems),
117
+ root: root ?? "biome.json",
118
+ };
50
119
  };
@@ -0,0 +1,37 @@
1
+ import { Schema } from "effect";
2
+ import { type Decoded, decodeWith } from "../../../decoded.ts";
3
+
4
+ export const Declaration = Schema.Struct({
5
+ rule: Schema.NonEmptyString,
6
+ includes: Schema.NonEmptyArray(Schema.NonEmptyString),
7
+ reason: Schema.String.check(Schema.isPattern(/\S/, { expected: "a reason that says why the scope needs the rule weakened" })),
8
+ });
9
+
10
+ export type Declaration = typeof Declaration.Type;
11
+
12
+ export const PRESET_DECLARATIONS = "declarations.json";
13
+
14
+ const shipped = Schema.toStandardSchemaV1(Schema.fromJsonString(Schema.Array(Declaration)), {
15
+ parseOptions: { errors: "all", onExcessProperty: "error" },
16
+ });
17
+
18
+ export const decodeShipped = (text: string | undefined): Promise<Decoded<ReadonlyArray<Declaration>>> =>
19
+ text === undefined ? Promise.resolve({ _tag: "Valid", value: [] }) : decodeWith(shipped, text);
20
+
21
+ export interface Scoped {
22
+ readonly rule: string;
23
+ readonly includes: ReadonlyArray<string>;
24
+ }
25
+
26
+ const sameScope = (left: ReadonlyArray<string>, right: ReadonlyArray<string>): boolean =>
27
+ new Set(left).size === new Set(right).size && left.every((glob) => right.includes(glob));
28
+
29
+ export const matches = (declaration: Scoped, weakening: Scoped): boolean =>
30
+ declaration.rule === weakening.rule && sameScope(declaration.includes, weakening.includes);
31
+
32
+ export const scoped = (base: string, glob: string): string => {
33
+ const [, negation = "", path = glob] = /^(!*)(.*)$/.exec(glob) ?? [];
34
+ return base === "" ? glob : `${negation}${base}/${path}`;
35
+ };
36
+
37
+ export const scopeText = (includes: ReadonlyArray<string>): string => includes.map((glob) => `"${glob}"`).join(", ");