@amritk/lint 0.3.2 → 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.
- package/dist/core/document.js +12 -10
- package/dist/core/formats.js +10 -13
- package/dist/core/glob.js +96 -118
- package/dist/core/index.js +31 -11
- package/dist/core/jsonpath.js +487 -561
- package/dist/core/lint.js +78 -85
- package/dist/core/plugin.js +21 -29
- package/dist/core/pointers.js +107 -151
- package/dist/core/ruleset.js +0 -0
- package/dist/core/runner.js +214 -272
- package/dist/core/types.js +4 -1
- package/dist/core/validate-ruleset.js +98 -112
- package/dist/fix/apply.js +58 -96
- package/dist/fix/index.js +7 -2
- package/dist/fix/plugin.js +15 -20
- package/dist/functions/alphabetical.js +39 -50
- package/dist/functions/casing.js +39 -45
- package/dist/functions/defined.js +7 -5
- package/dist/functions/enumeration.js +15 -25
- package/dist/functions/falsy.js +7 -5
- package/dist/functions/index.js +60 -44
- package/dist/functions/length.js +22 -32
- package/dist/functions/or.js +18 -24
- package/dist/functions/pattern.js +39 -49
- package/dist/functions/schema.js +93 -119
- package/dist/functions/truthy.js +7 -5
- package/dist/functions/typed-enum.js +33 -36
- package/dist/functions/undefined.js +7 -8
- package/dist/functions/unreferenced-reusable-object.js +35 -47
- package/dist/functions/xor.js +13 -16
- package/dist/index.js +114 -164
- package/dist/parsers/edit-model.js +316 -444
- package/dist/parsers/index.js +22 -19
- package/dist/parsers/json.js +39 -37
- package/dist/parsers/lines.js +23 -26
- package/dist/parsers/types.js +9 -7
- package/dist/parsers/yaml.js +137 -204
- package/dist/rules/openapi/fixers.js +166 -221
- package/dist/rules/openapi/formats.js +26 -27
- package/dist/rules/openapi/functions/example-validation.js +98 -138
- package/dist/rules/openapi/functions/helpers.js +8 -10
- package/dist/rules/openapi/functions/index.js +98 -72
- package/dist/rules/openapi/functions/oas-additional-operations.js +16 -22
- package/dist/rules/openapi/functions/oas-discriminator.js +24 -22
- package/dist/rules/openapi/functions/oas-example-external-value.js +13 -21
- package/dist/rules/openapi/functions/oas-example-value.js +24 -27
- package/dist/rules/openapi/functions/oas-mutually-exclusive.js +15 -19
- package/dist/rules/openapi/functions/oas-no-nullable.js +13 -21
- package/dist/rules/openapi/functions/oas-op-form-data-consume-check.js +21 -19
- package/dist/rules/openapi/functions/oas-op-id-unique.js +27 -27
- package/dist/rules/openapi/functions/oas-op-params.js +36 -42
- package/dist/rules/openapi/functions/oas-op-security-defined.js +40 -39
- package/dist/rules/openapi/functions/oas-op-success-response.js +11 -13
- package/dist/rules/openapi/functions/oas-path-param.js +75 -96
- package/dist/rules/openapi/functions/oas-schema-example-deprecated.js +33 -40
- package/dist/rules/openapi/functions/oas-schema.js +9 -14
- package/dist/rules/openapi/functions/oas-server-name-unique.js +21 -19
- package/dist/rules/openapi/functions/oas-server-variables.js +45 -49
- package/dist/rules/openapi/functions/oas-tag-defined.js +20 -20
- package/dist/rules/openapi/functions/oas-tag-kind.js +16 -16
- package/dist/rules/openapi/functions/oas-tag-parent-defined.js +40 -43
- package/dist/rules/openapi/functions/oas-tags-unique.js +18 -16
- package/dist/rules/openapi/functions/oas-unused-component.js +48 -58
- package/dist/rules/openapi/functions/ref-siblings.js +13 -11
- package/dist/rules/openapi/index.js +99 -117
- package/dist/rules/openapi/oas.js +524 -536
- package/dist/rules/openapi/schemas/index.js +17 -33
- package/dist/rules/openapi/schemas/oas20.json +1 -1592
- package/dist/rules/openapi/schemas/oas30.json +1 -1651
- package/dist/rules/openapi/schemas/oas31.json +1 -1412
- package/dist/rules/openapi/schemas/oas32.json +1 -1684
- package/package.json +5 -5
|
@@ -1,40 +1,37 @@
|
|
|
1
1
|
const JS_TYPES = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
if (!
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
35
|
+
}
|
|
36
|
+
return results;
|
|
37
|
+
};
|
|
38
|
+
export {
|
|
39
|
+
unreferencedReusableObject
|
|
52
40
|
};
|
package/dist/functions/xor.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
|
2
|
-
import { createRequire } from
|
|
3
|
-
import { dirname, isAbsolute, resolve as resolvePath } from
|
|
4
|
-
import { createRuleset as createCoreRuleset, lintWithResult
|
|
5
|
-
import { createFixPlugin, FIX_PLUGIN_NAME } from
|
|
6
|
-
import { builtinFunctions } from
|
|
7
|
-
import { parseWithPointers } from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`Cannot resolve custom function "${name}" from ${resolvePath(basePath, dir)}`);
|
|
67
49
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
seen)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
};
|