@amritk/lint 0.3.1 → 0.3.3

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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/dist/core/document.js +12 -10
  3. package/dist/core/formats.js +10 -13
  4. package/dist/core/glob.js +96 -118
  5. package/dist/core/index.js +31 -11
  6. package/dist/core/jsonpath.js +487 -561
  7. package/dist/core/lint.js +78 -85
  8. package/dist/core/plugin.js +21 -29
  9. package/dist/core/pointers.js +107 -151
  10. package/dist/core/ruleset.js +0 -0
  11. package/dist/core/runner.js +214 -272
  12. package/dist/core/types.js +4 -1
  13. package/dist/core/validate-ruleset.js +98 -112
  14. package/dist/fix/apply.js +58 -96
  15. package/dist/fix/index.js +7 -2
  16. package/dist/fix/plugin.js +15 -20
  17. package/dist/functions/alphabetical.js +39 -50
  18. package/dist/functions/casing.js +39 -45
  19. package/dist/functions/defined.js +7 -5
  20. package/dist/functions/enumeration.js +15 -25
  21. package/dist/functions/falsy.js +7 -5
  22. package/dist/functions/index.js +60 -44
  23. package/dist/functions/length.js +22 -32
  24. package/dist/functions/or.js +18 -24
  25. package/dist/functions/pattern.js +39 -49
  26. package/dist/functions/schema.js +93 -119
  27. package/dist/functions/truthy.js +7 -5
  28. package/dist/functions/typed-enum.js +33 -36
  29. package/dist/functions/undefined.js +7 -8
  30. package/dist/functions/unreferenced-reusable-object.js +35 -47
  31. package/dist/functions/xor.js +13 -16
  32. package/dist/index.js +114 -164
  33. package/dist/parsers/edit-model.js +316 -444
  34. package/dist/parsers/index.js +22 -19
  35. package/dist/parsers/json.js +39 -37
  36. package/dist/parsers/lines.js +23 -26
  37. package/dist/parsers/types.js +9 -7
  38. package/dist/parsers/yaml.js +137 -204
  39. package/dist/rules/openapi/fixers.js +166 -221
  40. package/dist/rules/openapi/formats.js +26 -27
  41. package/dist/rules/openapi/functions/example-validation.js +98 -138
  42. package/dist/rules/openapi/functions/helpers.js +8 -10
  43. package/dist/rules/openapi/functions/index.js +98 -72
  44. package/dist/rules/openapi/functions/oas-additional-operations.js +16 -22
  45. package/dist/rules/openapi/functions/oas-discriminator.js +24 -22
  46. package/dist/rules/openapi/functions/oas-example-external-value.js +13 -21
  47. package/dist/rules/openapi/functions/oas-example-value.js +24 -27
  48. package/dist/rules/openapi/functions/oas-mutually-exclusive.js +15 -19
  49. package/dist/rules/openapi/functions/oas-no-nullable.js +13 -21
  50. package/dist/rules/openapi/functions/oas-op-form-data-consume-check.js +21 -19
  51. package/dist/rules/openapi/functions/oas-op-id-unique.js +27 -27
  52. package/dist/rules/openapi/functions/oas-op-params.js +36 -42
  53. package/dist/rules/openapi/functions/oas-op-security-defined.js +40 -39
  54. package/dist/rules/openapi/functions/oas-op-success-response.js +11 -13
  55. package/dist/rules/openapi/functions/oas-path-param.js +75 -96
  56. package/dist/rules/openapi/functions/oas-schema-example-deprecated.js +33 -40
  57. package/dist/rules/openapi/functions/oas-schema.js +9 -14
  58. package/dist/rules/openapi/functions/oas-server-name-unique.js +21 -19
  59. package/dist/rules/openapi/functions/oas-server-variables.js +45 -49
  60. package/dist/rules/openapi/functions/oas-tag-defined.js +20 -20
  61. package/dist/rules/openapi/functions/oas-tag-kind.js +16 -16
  62. package/dist/rules/openapi/functions/oas-tag-parent-defined.js +40 -43
  63. package/dist/rules/openapi/functions/oas-tags-unique.js +18 -16
  64. package/dist/rules/openapi/functions/oas-unused-component.js +48 -58
  65. package/dist/rules/openapi/functions/ref-siblings.js +13 -11
  66. package/dist/rules/openapi/index.js +99 -117
  67. package/dist/rules/openapi/oas.js +524 -536
  68. package/dist/rules/openapi/schemas/index.js +17 -33
  69. package/dist/rules/openapi/schemas/oas20.json +1 -1592
  70. package/dist/rules/openapi/schemas/oas30.json +1 -1651
  71. package/dist/rules/openapi/schemas/oas31.json +1 -1412
  72. package/dist/rules/openapi/schemas/oas32.json +1 -1684
  73. package/package.json +5 -5
@@ -1,40 +1,37 @@
1
1
  const JS_TYPES = {
2
- string: (v) => typeof v === 'string',
3
- number: (v) => typeof v === 'number',
4
- integer: (v) => typeof v === 'number' && Number.isInteger(v),
5
- boolean: (v) => typeof v === 'boolean',
6
- null: (v) => v === null,
7
- array: (v) => Array.isArray(v),
8
- object: (v) => typeof v === 'object' && v !== null && !Array.isArray(v),
2
+ string: (v) => typeof v === "string",
3
+ number: (v) => typeof v === "number",
4
+ integer: (v) => typeof v === "number" && Number.isInteger(v),
5
+ boolean: (v) => typeof v === "boolean",
6
+ null: (v) => v === null,
7
+ array: (v) => Array.isArray(v),
8
+ object: (v) => typeof v === "object" && v !== null && !Array.isArray(v)
9
9
  };
10
- /** Validates that each `enum` entry matches the schema's declared `type`. */
11
- export const typedEnum = (input, _options, context) => {
12
- if (typeof input !== 'object' || input === null)
13
- return [];
14
- const declaredType = input['type'];
15
- const values = input['enum'];
16
- if (declaredType === undefined || !Array.isArray(values))
17
- return [];
18
- const types = Array.isArray(declaredType) ? [...declaredType] : [declaredType];
19
- // A schema marked nullable (OpenAPI 3 `nullable` or the Swagger 2 `x-nullable`
20
- // vendor extension) is allowed to hold `null` in addition to its declared
21
- // type, so `null` must not be flagged as a type mismatch.
22
- if ((input['nullable'] === true || input['x-nullable'] === true) && !types.includes('null')) {
23
- types.push('null');
10
+ const typedEnum = (input, _options, context) => {
11
+ if (typeof input !== "object" || input === null)
12
+ return [];
13
+ const declaredType = input["type"];
14
+ const values = input["enum"];
15
+ if (declaredType === void 0 || !Array.isArray(values))
16
+ return [];
17
+ const types = Array.isArray(declaredType) ? [...declaredType] : [declaredType];
18
+ if ((input["nullable"] === true || input["x-nullable"] === true) && !types.includes("null")) {
19
+ types.push("null");
20
+ }
21
+ const checkers = types.map((type) => JS_TYPES[String(type)]).filter((fn) => Boolean(fn));
22
+ if (checkers.length === 0)
23
+ return [];
24
+ const results = [];
25
+ values.forEach((value, index) => {
26
+ if (!checkers.some((check) => check(value))) {
27
+ results.push({
28
+ message: `Enum value \`${JSON.stringify(value)}\` must be of type "${types.join(" | ")}"`,
29
+ path: [...context.path, "enum", index]
30
+ });
24
31
  }
25
- const checkers = types
26
- .map((type) => JS_TYPES[String(type)])
27
- .filter((fn) => Boolean(fn));
28
- if (checkers.length === 0)
29
- return [];
30
- const results = [];
31
- values.forEach((value, index) => {
32
- if (!checkers.some((check) => check(value))) {
33
- results.push({
34
- message: `Enum value \`${JSON.stringify(value)}\` must be of type "${types.join(' | ')}"`,
35
- path: [...context.path, 'enum', index],
36
- });
37
- }
38
- });
39
- return results;
32
+ });
33
+ return results;
34
+ };
35
+ export {
36
+ typedEnum
40
37
  };
@@ -1,9 +1,8 @@
1
- /**
2
- * Flags a value that is defined. Exported as `undefinedFn` because `undefined`
3
- * is a reserved identifier; it is registered under the name `undefined`.
4
- */
5
- export const undefinedFn = (input) => {
6
- if (input !== undefined)
7
- return [{ message: 'The value must be undefined' }];
8
- return [];
1
+ const undefinedFn = (input) => {
2
+ if (input !== void 0)
3
+ return [{ message: "The value must be undefined" }];
4
+ return [];
5
+ };
6
+ export {
7
+ undefinedFn
9
8
  };
@@ -1,52 +1,40 @@
1
- /** Collects every `$ref` string anywhere in `node` into `into`. */
2
1
  const collectRefs = (node, into) => {
3
- if (Array.isArray(node)) {
4
- for (const item of node)
5
- collectRefs(item, into);
6
- return;
7
- }
8
- if (typeof node === 'object' && node !== null) {
9
- for (const [key, value] of Object.entries(node)) {
10
- if (key === '$ref' && typeof value === 'string')
11
- into.add(value);
12
- else
13
- collectRefs(value, into);
14
- }
2
+ if (Array.isArray(node)) {
3
+ for (const item of node)
4
+ collectRefs(item, into);
5
+ return;
6
+ }
7
+ if (typeof node === "object" && node !== null) {
8
+ for (const [key, value] of Object.entries(node)) {
9
+ if (key === "$ref" && typeof value === "string")
10
+ into.add(value);
11
+ else
12
+ collectRefs(value, into);
15
13
  }
14
+ }
16
15
  };
17
- /** Escapes a key for use in a JSON pointer segment (`~` -> `~0`, `/` -> `~1`). */
18
- const escapePointerSegment = (key) => key.replace(/~/g, '~0').replace(/\//g, '~1');
19
- /**
20
- * Flags entries in a reusable-object map that nothing `$ref`s.
21
- *
22
- * This must run against the *unresolved* document: once `$ref`s are inlined by a
23
- * resolver there are no references left to count, so every reusable object would
24
- * look orphaned.
25
- */
26
- export const unreferencedReusableObject = (input, options, context) => {
27
- if (typeof input !== 'object' || input === null)
28
- return [];
29
- const location = options?.reusableObjectsLocation;
30
- if (!location)
31
- return [];
32
- const refs = new Set();
33
- collectRefs(context.document.data, refs);
34
- const results = [];
35
- for (const key of Object.keys(input)) {
36
- // A key such as "a/b" appears in a pointer as "a~1b", so escape it before
37
- // building the expected reference. Without this a legitimately referenced
38
- // object with a special character in its name looks unreferenced.
39
- const base = `${location}/${escapePointerSegment(key)}`;
40
- // A reference can point straight at the object (`base`) or deeper into it
41
- // (e.g. `base/properties/x`); either counts as a use, so match the exact
42
- // pointer or any pointer nested beneath it.
43
- const referenced = refs.has(base) || [...refs].some((ref) => ref.startsWith(`${base}/`));
44
- if (!referenced) {
45
- results.push({
46
- message: 'This reusable object is never referenced',
47
- path: [...context.path, key],
48
- });
49
- }
16
+ const escapePointerSegment = (key) => key.replace(/~/g, "~0").replace(/\//g, "~1");
17
+ const unreferencedReusableObject = (input, options, context) => {
18
+ if (typeof input !== "object" || input === null)
19
+ return [];
20
+ const location = options?.reusableObjectsLocation;
21
+ if (!location)
22
+ return [];
23
+ const refs = /* @__PURE__ */ new Set();
24
+ collectRefs(context.document.data, refs);
25
+ const results = [];
26
+ for (const key of Object.keys(input)) {
27
+ const base = `${location}/${escapePointerSegment(key)}`;
28
+ const referenced = refs.has(base) || [...refs].some((ref) => ref.startsWith(`${base}/`));
29
+ if (!referenced) {
30
+ results.push({
31
+ message: "This reusable object is never referenced",
32
+ path: [...context.path, key]
33
+ });
50
34
  }
51
- return results;
35
+ }
36
+ return results;
37
+ };
38
+ export {
39
+ unreferencedReusableObject
52
40
  };
@@ -1,18 +1,15 @@
1
- /** Flags an object unless exactly one of the listed `properties` is present. */
2
- export const xor = (input, options) => {
3
- if (typeof input !== 'object' || input === null)
4
- return [];
5
- const properties = options?.properties;
6
- // Spectral validates the option schema (an array of at least two strings)
7
- // before the function runs and no-ops when it fails, so with fewer than two
8
- // properties there is nothing meaningful to check. We deliberately skip in
9
- // silence rather than push an error: an empty or single-element list would
10
- // otherwise flag every node with a message that names nothing useful.
11
- if (!Array.isArray(properties) || properties.length < 2)
12
- return [];
13
- const present = properties.filter((property) => property in input);
14
- if (present.length !== 1) {
15
- return [{ message: `Exactly one of ${properties.map((p) => `"${p}"`).join(', ')} must be defined` }];
16
- }
1
+ const xor = (input, options) => {
2
+ if (typeof input !== "object" || input === null)
17
3
  return [];
4
+ const properties = options?.properties;
5
+ if (!Array.isArray(properties) || properties.length < 2)
6
+ return [];
7
+ const present = properties.filter((property) => property in input);
8
+ if (present.length !== 1) {
9
+ return [{ message: `Exactly one of ${properties.map((p) => `"${p}"`).join(", ")} must be defined` }];
10
+ }
11
+ return [];
12
+ };
13
+ export {
14
+ xor
18
15
  };
package/dist/index.js CHANGED
@@ -1,176 +1,126 @@
1
- import { readFileSync } from 'node:fs';
2
- import { createRequire } from 'node:module';
3
- import { dirname, isAbsolute, resolve as resolvePath } from 'node:path';
4
- import { createRuleset as createCoreRuleset, lintWithResult, } from './core/index.js';
5
- import { createFixPlugin, FIX_PLUGIN_NAME } from './fix/index.js';
6
- import { builtinFunctions } from './functions/index.js';
7
- import { parseWithPointers } from './parsers/index.js';
8
- // Re-export the engine, built-in functions, and fix subsystem as the package's
9
- // public API. `export *` from `./core` also provides a low-level `createRuleset`,
10
- // but the higher-level wrapper defined below (which layers in the built-in
11
- // functions and file/package `extends` resolution) is the local export and wins.
12
- // Rendering findings is a consumer concern: `lintDocument` returns structured
13
- // `IDiagnostic[]`, and the caller decides how to display or serialize them.
14
- export * from './core/index.js';
15
- export * from './fix/index.js';
16
- export * from './functions/index.js';
17
- export { detectFormat, parseWithPointers } from './parsers/index.js';
18
- const require = createRequire(import.meta.url);
19
- /** Loads a ruleset definition from a file path by extension (YAML/JSON parsed, JS/CJS/MJS required). */
1
+ import { readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, isAbsolute, resolve as resolvePath } from "node:path";
4
+ import { createRuleset as createCoreRuleset, lintWithResult } from "./core/index.js";
5
+ import { createFixPlugin, FIX_PLUGIN_NAME } from "./fix/index.js";
6
+ import { builtinFunctions } from "./functions/index.js";
7
+ import { parseWithPointers } from "./parsers/index.js";
8
+ export * from "./core/index.js";
9
+ export * from "./fix/index.js";
10
+ export * from "./functions/index.js";
11
+ import { detectFormat, parseWithPointers as parseWithPointers2 } from "./parsers/index.js";
12
+ const require2 = createRequire(import.meta.url);
20
13
  const loadRulesetFile = (file) => {
21
- if (/\.(ya?ml|json)$/i.test(file)) {
22
- return parseWithPointers(readFileSync(file, 'utf8')).data;
23
- }
24
- const module = require(file);
25
- return (module.default ?? module);
14
+ if (/\.(ya?ml|json)$/i.test(file)) {
15
+ return parseWithPointers(readFileSync(file, "utf8")).data;
16
+ }
17
+ const module = require2(file);
18
+ return module.default ?? module;
26
19
  };
27
- /**
28
- * Resolves an `extends` reference to a ruleset definition. Supports:
29
- * - local file paths (relative to `basePath`, or absolute): `.yaml` / `.yml` / `.json` / `.js`,
30
- * - npm package specifiers (resolved from `basePath`), including subpaths.
31
- *
32
- * The engine ships no named built-in rulesets, so every string `extends` target
33
- * is a file path or an npm package.
34
- */
35
- export const resolveNamedRuleset = (name, basePath = process.cwd()) => {
36
- if (name.startsWith('.') || isAbsolute(name)) {
37
- const file = resolvePath(basePath, name);
38
- return { definition: loadRulesetFile(file), basePath: dirname(file) };
39
- }
40
- let file;
41
- try {
42
- file = require.resolve(name, { paths: [basePath] });
43
- }
44
- catch {
45
- throw new Error(`Cannot resolve extended ruleset "${name}" from ${basePath}`);
46
- }
47
- return { definition: loadRulesetFile(file), basePath: dirname(file) };
20
+ const resolveNamedRuleset = (name, basePath = process.cwd()) => {
21
+ if (name.startsWith(".") || isAbsolute(name)) {
22
+ const file2 = resolvePath(basePath, name);
23
+ return { definition: loadRulesetFile(file2), basePath: dirname(file2) };
24
+ }
25
+ let file;
26
+ try {
27
+ file = require2.resolve(name, { paths: [basePath] });
28
+ } catch {
29
+ throw new Error(`Cannot resolve extended ruleset "${name}" from ${basePath}`);
30
+ }
31
+ return { definition: loadRulesetFile(file), basePath: dirname(file) };
48
32
  };
49
- /** Loads a single custom function module (`<dir>/<name>.{js,cjs,mjs}` or a bare path). */
50
33
  const loadFunctionByName = (basePath, dir, name) => {
51
- const baseFile = resolvePath(basePath, dir, name);
52
- for (const candidate of [baseFile, `${baseFile}.js`, `${baseFile}.cjs`, `${baseFile}.mjs`]) {
53
- try {
54
- const resolvedFile = require.resolve(candidate);
55
- const module = require(resolvedFile);
56
- const fn = module.default ?? module;
57
- if (typeof fn !== 'function')
58
- throw new Error(`"${name}" did not export a function`);
59
- return fn;
60
- }
61
- catch (error) {
62
- if (error.code !== 'MODULE_NOT_FOUND')
63
- throw error;
64
- }
34
+ const baseFile = resolvePath(basePath, dir, name);
35
+ for (const candidate of [baseFile, `${baseFile}.js`, `${baseFile}.cjs`, `${baseFile}.mjs`]) {
36
+ try {
37
+ const resolvedFile = require2.resolve(candidate);
38
+ const module = require2(resolvedFile);
39
+ const fn = module.default ?? module;
40
+ if (typeof fn !== "function")
41
+ throw new Error(`"${name}" did not export a function`);
42
+ return fn;
43
+ } catch (error) {
44
+ if (error.code !== "MODULE_NOT_FOUND")
45
+ throw error;
65
46
  }
66
- throw new Error(`Cannot resolve custom function "${name}" from ${resolvePath(basePath, dir)}`);
47
+ }
48
+ throw new Error(`Cannot resolve custom function "${name}" from ${resolvePath(basePath, dir)}`);
67
49
  };
68
- /**
69
- * Walks a ruleset definition (and its string `extends`) collecting custom
70
- * functions declared via `functions` / `functionsDir`, each loaded relative to
71
- * the directory of the ruleset that declared it. YAML/JSON rulesets reference
72
- * functions by name; JS rulesets can instead pass direct references in `then`.
73
- */
74
- const collectCustomFunctions = (definition, basePath, into,
75
- // Keyed by (basePath, reference) for string extends and by object identity for
76
- // inline ones. `loadRulesetFile` returns a fresh object per read, so object
77
- // identity alone would never dedupe a file cycle — we key on the resolved edge.
78
- seen) => {
79
- if (seen.has(definition))
80
- return;
81
- seen.add(definition);
82
- if (definition.extends) {
83
- const entries = Array.isArray(definition.extends) ? definition.extends : [definition.extends];
84
- for (const entry of entries) {
85
- const target = Array.isArray(entry) ? entry[0] : entry;
86
- if (typeof target === 'string') {
87
- const key = `${basePath}\0${target}`;
88
- if (seen.has(key))
89
- continue;
90
- seen.add(key);
91
- const resolved = resolveNamedRuleset(target, basePath);
92
- collectCustomFunctions(resolved.definition, resolved.basePath, into, seen);
93
- }
94
- else {
95
- collectCustomFunctions(target, basePath, into, seen);
96
- }
97
- }
98
- }
99
- if (Array.isArray(definition.functions)) {
100
- const dir = definition.functionsDir ?? 'functions';
101
- for (const name of definition.functions)
102
- into[name] = loadFunctionByName(basePath, dir, name);
50
+ const collectCustomFunctions = (definition, basePath, into, seen) => {
51
+ if (seen.has(definition))
52
+ return;
53
+ seen.add(definition);
54
+ if (definition.extends) {
55
+ const entries = Array.isArray(definition.extends) ? definition.extends : [definition.extends];
56
+ for (const entry of entries) {
57
+ const target = Array.isArray(entry) ? entry[0] : entry;
58
+ if (typeof target === "string") {
59
+ const key = `${basePath}\0${target}`;
60
+ if (seen.has(key))
61
+ continue;
62
+ seen.add(key);
63
+ const resolved = resolveNamedRuleset(target, basePath);
64
+ collectCustomFunctions(resolved.definition, resolved.basePath, into, seen);
65
+ } else {
66
+ collectCustomFunctions(target, basePath, into, seen);
67
+ }
103
68
  }
69
+ }
70
+ if (Array.isArray(definition.functions)) {
71
+ const dir = definition.functionsDir ?? "functions";
72
+ for (const name of definition.functions)
73
+ into[name] = loadFunctionByName(basePath, dir, name);
74
+ }
104
75
  };
105
- /**
106
- * Builds a runnable {@link Ruleset} from a ruleset definition, layering the
107
- * built-in functions (plus any custom ones the definition declares via
108
- * `functions` / `functionsDir`) over the core engine and wiring up `extends`
109
- * resolution against files and npm packages. With no definition it produces an
110
- * empty ruleset (no rules run).
111
- */
112
- export const createRuleset = (definition, basePath) => {
113
- const resolved = definition ?? {};
114
- // Custom functions referenced by name (YAML/JSON rulesets) are loaded relative
115
- // to the declaring ruleset's directory and layered over the built-ins.
116
- let functions = builtinFunctions;
117
- const custom = {};
118
- collectCustomFunctions(resolved, basePath ?? process.cwd(), custom, new Set());
119
- if (Object.keys(custom).length > 0)
120
- functions = { ...builtinFunctions, ...custom };
121
- return createCoreRuleset(resolved, {
122
- functions,
123
- resolve: resolveNamedRuleset,
124
- ...(basePath !== undefined ? { basePath } : {}),
125
- });
76
+ const createRuleset = (definition, basePath) => {
77
+ const resolved = definition ?? {};
78
+ let functions = builtinFunctions;
79
+ const custom = {};
80
+ collectCustomFunctions(resolved, basePath ?? process.cwd(), custom, /* @__PURE__ */ new Set());
81
+ if (Object.keys(custom).length > 0)
82
+ functions = { ...builtinFunctions, ...custom };
83
+ return createCoreRuleset(resolved, {
84
+ functions,
85
+ resolve: resolveNamedRuleset,
86
+ ...basePath !== void 0 ? { basePath } : {}
87
+ });
126
88
  };
127
- /**
128
- * Lints a JSON/YAML `input` end to end: parses with source maps and applies the
129
- * ruleset. Returns just the findings; use {@link lintDocumentWithResult} for the
130
- * full result.
131
- */
132
- export const lintDocument = async (input, options = {}) => (await lintDocumentWithResult(input, options)).diagnostics;
133
- /**
134
- * Like {@link lintDocument}, but returns the full {@link ILintResult} — including
135
- * anything the configured `plugins` produced (e.g. the auto-fix plugin's
136
- * rewritten `output`).
137
- */
138
- export const lintDocumentWithResult = async (input, options = {}) => {
139
- const { ruleset: rulesetDefinition, rulesetBasePath, resolve, plugins, ...documentOptions } = options;
140
- const ruleset = createRuleset(rulesetDefinition, rulesetBasePath);
141
- return lintWithResult(input, {
142
- ...documentOptions,
143
- ruleset,
144
- ...(resolve ? { resolve } : {}),
145
- ...(plugins ? { plugins } : {}),
146
- });
89
+ const lintDocument = async (input, options = {}) => (await lintDocumentWithResult(input, options)).diagnostics;
90
+ const lintDocumentWithResult = async (input, options = {}) => {
91
+ const { ruleset: rulesetDefinition, rulesetBasePath, resolve, plugins, ...documentOptions } = options;
92
+ const ruleset = createRuleset(rulesetDefinition, rulesetBasePath);
93
+ return lintWithResult(input, {
94
+ ...documentOptions,
95
+ ruleset,
96
+ ...resolve ? { resolve } : {},
97
+ ...plugins ? { plugins } : {}
98
+ });
147
99
  };
148
- // One fix pass can unblock the next, so we lint-and-fix to a fixpoint. The cap is
149
- // a safety net against a fixer that oscillates rather than converging — in
150
- // practice a couple of passes is plenty.
151
100
  const MAX_FIX_PASSES = 10;
152
- /**
153
- * Lints a document and applies the supplied `fixers` repeatedly until the
154
- * document stops changing (or {@link MAX_FIX_PASSES} is reached), then re-lints
155
- * so `remaining` reflects the fixed document. A one-call convenience over
156
- * {@link lintDocumentWithResult} + `createFixPlugin`. With no `fixers` this is a
157
- * no-op that just returns the findings.
158
- */
159
- export const fixDocument = async (input, options = {}) => {
160
- const { fixers = {}, safeOnly, ...lintOptions } = options;
161
- const plugin = createFixPlugin(fixers, { safeOnly: safeOnly !== false });
162
- let current = input;
163
- const applied = [];
164
- for (let pass = 0; pass < MAX_FIX_PASSES; pass++) {
165
- const result = await lintDocumentWithResult(current, { ...lintOptions, plugins: [plugin] });
166
- // No rewrite, or a rewrite that matches what we already have, means we have converged.
167
- if (result.output === undefined || result.output === current)
168
- break;
169
- current = result.output;
170
- const data = result.pluginData[FIX_PLUGIN_NAME];
171
- if (data)
172
- applied.push(...data.applied);
173
- }
174
- const remaining = await lintDocument(current, lintOptions);
175
- return { output: current, fixed: applied.length > 0, remaining, applied };
101
+ const fixDocument = async (input, options = {}) => {
102
+ const { fixers = {}, safeOnly, ...lintOptions } = options;
103
+ const plugin = createFixPlugin(fixers, { safeOnly: safeOnly !== false });
104
+ let current = input;
105
+ const applied = [];
106
+ for (let pass = 0; pass < MAX_FIX_PASSES; pass++) {
107
+ const result = await lintDocumentWithResult(current, { ...lintOptions, plugins: [plugin] });
108
+ if (result.output === void 0 || result.output === current)
109
+ break;
110
+ current = result.output;
111
+ const data = result.pluginData[FIX_PLUGIN_NAME];
112
+ if (data)
113
+ applied.push(...data.applied);
114
+ }
115
+ const remaining = await lintDocument(current, lintOptions);
116
+ return { output: current, fixed: applied.length > 0, remaining, applied };
117
+ };
118
+ export {
119
+ createRuleset,
120
+ detectFormat,
121
+ fixDocument,
122
+ lintDocument,
123
+ lintDocumentWithResult,
124
+ parseWithPointers2 as parseWithPointers,
125
+ resolveNamedRuleset
176
126
  };