@amritk/lint 0.3.2 → 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.
- package/AI.md +55 -0
- 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.d.ts +0 -1
- package/dist/core/index.js +30 -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.d.ts +1 -1
- package/dist/fix/apply.js +58 -96
- package/dist/fix/index.js +7 -2
- package/dist/fix/plugin.js +15 -20
- package/dist/fix/types.d.ts +1 -1
- package/dist/functions/alphabetical.d.ts +1 -1
- package/dist/functions/alphabetical.js +39 -50
- package/dist/functions/casing.d.ts +1 -1
- package/dist/functions/casing.js +39 -45
- package/dist/functions/defined.d.ts +1 -1
- package/dist/functions/defined.js +7 -5
- package/dist/functions/enumeration.d.ts +1 -1
- package/dist/functions/enumeration.js +15 -25
- package/dist/functions/falsy.d.ts +1 -1
- package/dist/functions/falsy.js +7 -5
- package/dist/functions/index.d.ts +1 -1
- package/dist/functions/index.js +60 -44
- package/dist/functions/length.d.ts +1 -1
- package/dist/functions/length.js +22 -32
- package/dist/functions/or.d.ts +1 -1
- package/dist/functions/or.js +18 -24
- package/dist/functions/pattern.d.ts +1 -1
- package/dist/functions/pattern.js +39 -49
- package/dist/functions/schema.d.ts +1 -1
- package/dist/functions/schema.js +93 -119
- package/dist/functions/truthy.d.ts +1 -1
- package/dist/functions/truthy.js +7 -5
- package/dist/functions/typed-enum.d.ts +1 -1
- package/dist/functions/typed-enum.js +33 -36
- package/dist/functions/undefined.d.ts +1 -1
- package/dist/functions/undefined.js +7 -8
- package/dist/functions/unreferenced-reusable-object.d.ts +1 -1
- package/dist/functions/unreferenced-reusable-object.js +35 -47
- package/dist/functions/xor.d.ts +1 -1
- package/dist/functions/xor.js +13 -16
- package/dist/index.d.ts +17 -4
- package/dist/index.js +149 -164
- package/dist/parsers/edit-model.js +316 -444
- package/dist/parsers/index.d.ts +1 -1
- package/dist/parsers/index.js +23 -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.d.ts +1 -1
- 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.d.ts +1 -1
- package/dist/rules/openapi/functions/index.js +98 -72
- package/dist/rules/openapi/functions/oas-additional-operations.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-additional-operations.js +16 -22
- package/dist/rules/openapi/functions/oas-discriminator.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-discriminator.js +24 -22
- package/dist/rules/openapi/functions/oas-example-external-value.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-example-external-value.js +13 -21
- package/dist/rules/openapi/functions/oas-example-value.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-example-value.js +24 -27
- package/dist/rules/openapi/functions/oas-mutually-exclusive.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-mutually-exclusive.js +15 -19
- package/dist/rules/openapi/functions/oas-no-nullable.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-no-nullable.js +13 -21
- package/dist/rules/openapi/functions/oas-op-form-data-consume-check.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-op-form-data-consume-check.js +21 -19
- package/dist/rules/openapi/functions/oas-op-id-unique.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-op-id-unique.js +27 -27
- package/dist/rules/openapi/functions/oas-op-params.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-op-params.js +36 -42
- package/dist/rules/openapi/functions/oas-op-security-defined.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-op-security-defined.js +40 -39
- package/dist/rules/openapi/functions/oas-op-success-response.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-op-success-response.js +11 -13
- package/dist/rules/openapi/functions/oas-path-param.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-path-param.js +75 -96
- package/dist/rules/openapi/functions/oas-schema-example-deprecated.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-schema-example-deprecated.js +33 -40
- package/dist/rules/openapi/functions/oas-schema.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-schema.js +9 -14
- package/dist/rules/openapi/functions/oas-server-name-unique.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-server-name-unique.js +21 -19
- package/dist/rules/openapi/functions/oas-server-variables.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-server-variables.js +45 -49
- package/dist/rules/openapi/functions/oas-tag-defined.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-tag-defined.js +20 -20
- package/dist/rules/openapi/functions/oas-tag-kind.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-tag-kind.js +16 -16
- package/dist/rules/openapi/functions/oas-tag-parent-defined.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-tag-parent-defined.js +40 -43
- package/dist/rules/openapi/functions/oas-tags-unique.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-tags-unique.js +18 -16
- package/dist/rules/openapi/functions/oas-unused-component.d.ts +1 -1
- package/dist/rules/openapi/functions/oas-unused-component.js +48 -58
- package/dist/rules/openapi/functions/ref-siblings.d.ts +1 -1
- package/dist/rules/openapi/functions/ref-siblings.js +13 -11
- package/dist/rules/openapi/index.d.ts +2 -1
- package/dist/rules/openapi/index.js +99 -117
- package/dist/rules/openapi/oas.d.ts +1 -1
- 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 +11 -6
package/dist/core/lint.js
CHANGED
|
@@ -1,91 +1,84 @@
|
|
|
1
|
-
import { detectFormat } from
|
|
2
|
-
import { createDocument } from
|
|
3
|
-
import { runPlugins } from
|
|
4
|
-
import { createLinter } from
|
|
5
|
-
import { DiagnosticSeverity } from
|
|
1
|
+
import { detectFormat } from "../parsers/index.js";
|
|
2
|
+
import { createDocument } from "./document.js";
|
|
3
|
+
import { runPlugins } from "./plugin.js";
|
|
4
|
+
import { createLinter } from "./runner.js";
|
|
5
|
+
import { DiagnosticSeverity } from "./types.js";
|
|
6
6
|
const toParserSeverity = (value) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
if (value === void 0)
|
|
8
|
+
return void 0;
|
|
9
|
+
if (typeof value === "number")
|
|
10
|
+
return value;
|
|
11
|
+
if (value === "off")
|
|
12
|
+
return "off";
|
|
13
|
+
const names = {
|
|
14
|
+
error: DiagnosticSeverity.Error,
|
|
15
|
+
warn: DiagnosticSeverity.Warning,
|
|
16
|
+
info: DiagnosticSeverity.Information,
|
|
17
|
+
hint: DiagnosticSeverity.Hint
|
|
18
|
+
};
|
|
19
|
+
return names[value];
|
|
20
20
|
};
|
|
21
21
|
const byPosition = (a, b) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
const sa = a.source ?? "";
|
|
23
|
+
const sb = b.source ?? "";
|
|
24
|
+
if (sa !== sb)
|
|
25
|
+
return sa < sb ? -1 : 1;
|
|
26
|
+
return a.range.start.line - b.range.start.line || a.range.start.character - b.range.start.character;
|
|
27
27
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
resolverDiagnostics = result.diagnostics;
|
|
58
|
-
}
|
|
59
|
-
const ruleResults = await createLinter(ruleset).run(document, { resolved, ...(sources ? { sources } : {}) });
|
|
60
|
-
const parserResults = document.diagnostics.map((diagnostic) => {
|
|
61
|
-
const result = {
|
|
62
|
-
code: 'parser',
|
|
63
|
-
message: diagnostic.message,
|
|
64
|
-
path: diagnostic.path ?? [],
|
|
65
|
-
severity: diagnostic.severity,
|
|
66
|
-
range: diagnostic.range,
|
|
67
|
-
};
|
|
68
|
-
if (document.source !== undefined)
|
|
69
|
-
result.source = document.source;
|
|
70
|
-
return result;
|
|
71
|
-
});
|
|
72
|
-
const diagnostics = [...parserResults, ...resolverDiagnostics, ...ruleResults].sort(byPosition);
|
|
73
|
-
if (!plugins || plugins.length === 0)
|
|
74
|
-
return { diagnostics, pluginData: {} };
|
|
75
|
-
const format = documentOptions.format ?? detectFormat(input);
|
|
76
|
-
const run = runPlugins(plugins, diagnostics, {
|
|
77
|
-
input,
|
|
78
|
-
format,
|
|
79
|
-
document,
|
|
80
|
-
resolved,
|
|
81
|
-
ruleset,
|
|
82
|
-
...(documentOptions.source !== undefined ? { source: documentOptions.source } : {}),
|
|
83
|
-
});
|
|
84
|
-
return {
|
|
85
|
-
diagnostics: run.diagnostics,
|
|
86
|
-
pluginData: run.data,
|
|
87
|
-
...(run.output !== undefined ? { output: run.output } : {}),
|
|
28
|
+
const lintWithResult = async (input, options) => {
|
|
29
|
+
const { ruleset, resolve, skip, plugins, ...documentOptions } = options;
|
|
30
|
+
const duplicateKeys = toParserSeverity(ruleset.parserOptions?.duplicateKeys);
|
|
31
|
+
const incompatibleValues = toParserSeverity(ruleset.parserOptions?.incompatibleValues);
|
|
32
|
+
const document = createDocument(input, {
|
|
33
|
+
...documentOptions,
|
|
34
|
+
...duplicateKeys !== void 0 ? { duplicateKeys } : {},
|
|
35
|
+
...incompatibleValues !== void 0 ? { incompatibleValues } : {}
|
|
36
|
+
});
|
|
37
|
+
if (skip?.(document.data))
|
|
38
|
+
return { diagnostics: [], pluginData: {} };
|
|
39
|
+
let resolved = document.data;
|
|
40
|
+
let sources;
|
|
41
|
+
let resolverDiagnostics = [];
|
|
42
|
+
if (resolve) {
|
|
43
|
+
const result = await resolve(document, { input });
|
|
44
|
+
resolved = result.resolved;
|
|
45
|
+
sources = result.sources;
|
|
46
|
+
if (result.diagnostics)
|
|
47
|
+
resolverDiagnostics = result.diagnostics;
|
|
48
|
+
}
|
|
49
|
+
const ruleResults = await createLinter(ruleset).run(document, { resolved, ...sources ? { sources } : {} });
|
|
50
|
+
const parserResults = document.diagnostics.map((diagnostic) => {
|
|
51
|
+
const result = {
|
|
52
|
+
code: "parser",
|
|
53
|
+
message: diagnostic.message,
|
|
54
|
+
path: diagnostic.path ?? [],
|
|
55
|
+
severity: diagnostic.severity,
|
|
56
|
+
range: diagnostic.range
|
|
88
57
|
};
|
|
58
|
+
if (document.source !== void 0)
|
|
59
|
+
result.source = document.source;
|
|
60
|
+
return result;
|
|
61
|
+
});
|
|
62
|
+
const diagnostics = [...parserResults, ...resolverDiagnostics, ...ruleResults].sort(byPosition);
|
|
63
|
+
if (!plugins || plugins.length === 0)
|
|
64
|
+
return { diagnostics, pluginData: {} };
|
|
65
|
+
const format = documentOptions.format ?? detectFormat(input);
|
|
66
|
+
const run = runPlugins(plugins, diagnostics, {
|
|
67
|
+
input,
|
|
68
|
+
format,
|
|
69
|
+
document,
|
|
70
|
+
resolved,
|
|
71
|
+
ruleset,
|
|
72
|
+
...documentOptions.source !== void 0 ? { source: documentOptions.source } : {}
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
diagnostics: run.diagnostics,
|
|
76
|
+
pluginData: run.data,
|
|
77
|
+
...run.output !== void 0 ? { output: run.output } : {}
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
const lint = async (input, options) => (await lintWithResult(input, options)).diagnostics;
|
|
81
|
+
export {
|
|
82
|
+
lint,
|
|
83
|
+
lintWithResult
|
|
89
84
|
};
|
|
90
|
-
/** Lints `input` against a normalized `ruleset` and returns just the findings. */
|
|
91
|
-
export const lint = async (input, options) => (await lintWithResult(input, options)).diagnostics;
|
package/dist/core/plugin.js
CHANGED
|
@@ -1,31 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let output;
|
|
16
|
-
const data = {};
|
|
17
|
-
for (const plugin of plugins) {
|
|
18
|
-
const result = plugin.afterLint?.(currentDiagnostics, { ...context, input: currentInput });
|
|
19
|
-
if (!result)
|
|
20
|
-
continue;
|
|
21
|
-
if (result.diagnostics)
|
|
22
|
-
currentDiagnostics = result.diagnostics;
|
|
23
|
-
if (result.output !== undefined) {
|
|
24
|
-
output = result.output;
|
|
25
|
-
currentInput = result.output;
|
|
26
|
-
}
|
|
27
|
-
if (result.data !== undefined)
|
|
28
|
-
data[plugin.name] = result.data;
|
|
1
|
+
const runPlugins = (plugins, diagnostics, context) => {
|
|
2
|
+
let currentDiagnostics = diagnostics;
|
|
3
|
+
let currentInput = context.input;
|
|
4
|
+
let output;
|
|
5
|
+
const data = {};
|
|
6
|
+
for (const plugin of plugins) {
|
|
7
|
+
const result = plugin.afterLint?.(currentDiagnostics, { ...context, input: currentInput });
|
|
8
|
+
if (!result)
|
|
9
|
+
continue;
|
|
10
|
+
if (result.diagnostics)
|
|
11
|
+
currentDiagnostics = result.diagnostics;
|
|
12
|
+
if (result.output !== void 0) {
|
|
13
|
+
output = result.output;
|
|
14
|
+
currentInput = result.output;
|
|
29
15
|
}
|
|
30
|
-
|
|
16
|
+
if (result.data !== void 0)
|
|
17
|
+
data[plugin.name] = result.data;
|
|
18
|
+
}
|
|
19
|
+
return { diagnostics: currentDiagnostics, data, ...output !== void 0 ? { output } : {} };
|
|
20
|
+
};
|
|
21
|
+
export {
|
|
22
|
+
runPlugins
|
|
31
23
|
};
|
package/dist/core/pointers.js
CHANGED
|
@@ -1,169 +1,125 @@
|
|
|
1
|
-
import { dirname, resolve as resolvePath } from
|
|
2
|
-
const isContainer = (value) => typeof value ===
|
|
3
|
-
/**
|
|
4
|
-
* Decodes one JSON-pointer segment the way `getByPointer` does: percent-escapes
|
|
5
|
-
* first (pointers arrive inside URI-reference `$ref`s), then the JSON-pointer
|
|
6
|
-
* escapes `~1`/`~0`, then all-digit segments become numbers. Shared by both the
|
|
7
|
-
* `#/...` and fragment parsers so they decode identically.
|
|
8
|
-
*/
|
|
1
|
+
import { dirname, resolve as resolvePath } from "node:path";
|
|
2
|
+
const isContainer = (value) => typeof value === "object" && value !== null;
|
|
9
3
|
const decodeSegment = (segment) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
decoded = decoded.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
18
|
-
return /^\d+$/.test(decoded) ? Number(decoded) : decoded;
|
|
4
|
+
let decoded = segment;
|
|
5
|
+
try {
|
|
6
|
+
decoded = decodeURIComponent(segment);
|
|
7
|
+
} catch {
|
|
8
|
+
}
|
|
9
|
+
decoded = decoded.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
10
|
+
return /^\d+$/.test(decoded) ? Number(decoded) : decoded;
|
|
19
11
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return pointer.slice(2).split('/').map(decodeSegment);
|
|
12
|
+
const pointerToPath = (pointer) => {
|
|
13
|
+
if (pointer === "#")
|
|
14
|
+
return [];
|
|
15
|
+
if (!pointer.startsWith("#/"))
|
|
16
|
+
return void 0;
|
|
17
|
+
return pointer.slice(2).split("/").map(decodeSegment);
|
|
27
18
|
};
|
|
28
19
|
const isRemote = (location) => /^https?:\/\//i.test(location);
|
|
29
|
-
/**
|
|
30
|
-
* Resolves the location of `ref` (its file/URL part) relative to `base`, exactly
|
|
31
|
-
* as `@amritk/resolve-refs` does, so the keys we look up in the source set match
|
|
32
|
-
* the documents the resolver loaded.
|
|
33
|
-
*/
|
|
34
20
|
const joinLocation = (base, ref) => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
21
|
+
if (isRemote(ref))
|
|
22
|
+
return ref;
|
|
23
|
+
if (isRemote(base))
|
|
24
|
+
return new URL(ref, base).href;
|
|
25
|
+
return resolvePath(dirname(base), ref);
|
|
40
26
|
};
|
|
41
|
-
/** Splits a `$ref` into its document part and JSON-pointer fragment (without `#`). */
|
|
42
27
|
const splitRef = (ref) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
28
|
+
const hashIndex = ref.indexOf("#");
|
|
29
|
+
return {
|
|
30
|
+
filePart: hashIndex === -1 ? ref : ref.slice(0, hashIndex),
|
|
31
|
+
fragment: hashIndex === -1 ? "" : ref.slice(hashIndex + 1)
|
|
32
|
+
};
|
|
48
33
|
};
|
|
49
|
-
/**
|
|
50
|
-
* Parses a JSON-pointer fragment (the part after `#`, e.g. `/a/b`) into a path,
|
|
51
|
-
* mirroring `getByPointer`'s decoding (`%XX` then `~1`/`~0`) so cross-file
|
|
52
|
-
* pointers resolve to the same nodes the resolver inlined.
|
|
53
|
-
*/
|
|
54
34
|
const fragmentToPath = (fragment) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
35
|
+
if (fragment === "" || fragment === "/")
|
|
36
|
+
return [];
|
|
37
|
+
return fragment.replace(/^\//, "").split("/").map(decodeSegment);
|
|
58
38
|
};
|
|
59
39
|
const getAtPath = (root, path) => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
40
|
+
let node = root;
|
|
41
|
+
for (const segment of path) {
|
|
42
|
+
if (!isContainer(node))
|
|
43
|
+
return void 0;
|
|
44
|
+
node = node[segment];
|
|
45
|
+
}
|
|
46
|
+
return node;
|
|
67
47
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
let guard = 0;
|
|
80
|
-
while (isContainer(node) && !Array.isArray(node) && typeof node['$ref'] === 'string' && guard++ < 100) {
|
|
81
|
-
const target = pointerToPath(node['$ref']);
|
|
82
|
-
if (!target)
|
|
83
|
-
return;
|
|
84
|
-
originalPath = [...target];
|
|
85
|
-
node = getAtPath(root, target);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
followRefs();
|
|
89
|
-
for (const segment of path) {
|
|
90
|
-
if (!isContainer(node))
|
|
91
|
-
break;
|
|
92
|
-
originalPath.push(segment);
|
|
93
|
-
node = node[segment];
|
|
94
|
-
followRefs();
|
|
48
|
+
const resolveSourcePath = (root, path) => {
|
|
49
|
+
let originalPath = [];
|
|
50
|
+
let node = root;
|
|
51
|
+
const followRefs = () => {
|
|
52
|
+
let guard = 0;
|
|
53
|
+
while (isContainer(node) && !Array.isArray(node) && typeof node["$ref"] === "string" && guard++ < 100) {
|
|
54
|
+
const target = pointerToPath(node["$ref"]);
|
|
55
|
+
if (!target)
|
|
56
|
+
return;
|
|
57
|
+
originalPath = [...target];
|
|
58
|
+
node = getAtPath(root, target);
|
|
95
59
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
* what lets a finding on a node inlined from `./pet.yaml` report `pet.yaml`'s own
|
|
104
|
-
* line:column instead of the `$ref` site in the root.
|
|
105
|
-
*
|
|
106
|
-
* The walk re-derives the resolver's traversal over the *unresolved* documents,
|
|
107
|
-
* so a `$ref` whose target file is not in the registry (e.g. it failed to load)
|
|
108
|
-
* stops the walk at the last known location rather than guessing. Prefer
|
|
109
|
-
* {@link resolveSourceOriginFromMap} when the resolver supplies an origin map —
|
|
110
|
-
* it avoids re-deriving this traversal.
|
|
111
|
-
*/
|
|
112
|
-
export const resolveSourceOrigin = (registry, path) => {
|
|
113
|
-
let location = registry.rootLocation;
|
|
114
|
-
let root = registry.get(location)?.data;
|
|
115
|
-
let originalPath = [];
|
|
116
|
-
let node = root;
|
|
117
|
-
const followRefs = () => {
|
|
118
|
-
let guard = 0;
|
|
119
|
-
while (isContainer(node) && !Array.isArray(node) && typeof node['$ref'] === 'string' && guard++ < 1000) {
|
|
120
|
-
const { filePart, fragment } = splitRef(node['$ref']);
|
|
121
|
-
if (filePart !== '') {
|
|
122
|
-
const target = joinLocation(location, filePart);
|
|
123
|
-
const doc = registry.get(target);
|
|
124
|
-
if (!doc)
|
|
125
|
-
return;
|
|
126
|
-
location = target;
|
|
127
|
-
root = doc.data;
|
|
128
|
-
}
|
|
129
|
-
originalPath = fragmentToPath(fragment);
|
|
130
|
-
node = getAtPath(root, originalPath);
|
|
131
|
-
}
|
|
132
|
-
};
|
|
60
|
+
};
|
|
61
|
+
followRefs();
|
|
62
|
+
for (const segment of path) {
|
|
63
|
+
if (!isContainer(node))
|
|
64
|
+
break;
|
|
65
|
+
originalPath.push(segment);
|
|
66
|
+
node = node[segment];
|
|
133
67
|
followRefs();
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
68
|
+
}
|
|
69
|
+
return originalPath;
|
|
70
|
+
};
|
|
71
|
+
const resolveSourceOrigin = (registry, path) => {
|
|
72
|
+
let location = registry.rootLocation;
|
|
73
|
+
let root = registry.get(location)?.data;
|
|
74
|
+
let originalPath = [];
|
|
75
|
+
let node = root;
|
|
76
|
+
const followRefs = () => {
|
|
77
|
+
let guard = 0;
|
|
78
|
+
while (isContainer(node) && !Array.isArray(node) && typeof node["$ref"] === "string" && guard++ < 1e3) {
|
|
79
|
+
const { filePart, fragment } = splitRef(node["$ref"]);
|
|
80
|
+
if (filePart !== "") {
|
|
81
|
+
const target = joinLocation(location, filePart);
|
|
82
|
+
const doc = registry.get(target);
|
|
83
|
+
if (!doc)
|
|
84
|
+
return;
|
|
85
|
+
location = target;
|
|
86
|
+
root = doc.data;
|
|
87
|
+
}
|
|
88
|
+
originalPath = fragmentToPath(fragment);
|
|
89
|
+
node = getAtPath(root, originalPath);
|
|
140
90
|
}
|
|
141
|
-
|
|
91
|
+
};
|
|
92
|
+
followRefs();
|
|
93
|
+
for (const segment of path) {
|
|
94
|
+
if (!isContainer(node))
|
|
95
|
+
break;
|
|
96
|
+
originalPath.push(segment);
|
|
97
|
+
node = node[segment];
|
|
98
|
+
followRefs();
|
|
99
|
+
}
|
|
100
|
+
return { location, path: originalPath };
|
|
142
101
|
};
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
break;
|
|
158
|
-
node = node[segment];
|
|
159
|
-
const stamp = isContainer(node) ? origins.get(node) : undefined;
|
|
160
|
-
if (stamp) {
|
|
161
|
-
location = stamp.location;
|
|
162
|
-
inFilePath = [...stamp.pointer];
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
inFilePath.push(segment);
|
|
166
|
-
}
|
|
102
|
+
const resolveSourceOriginFromMap = (resolved, origins, rootLocation, path) => {
|
|
103
|
+
let location = rootLocation;
|
|
104
|
+
let inFilePath = [];
|
|
105
|
+
let node = resolved;
|
|
106
|
+
for (const segment of path) {
|
|
107
|
+
if (!isContainer(node))
|
|
108
|
+
break;
|
|
109
|
+
node = node[segment];
|
|
110
|
+
const stamp = isContainer(node) ? origins.get(node) : void 0;
|
|
111
|
+
if (stamp) {
|
|
112
|
+
location = stamp.location;
|
|
113
|
+
inFilePath = [...stamp.pointer];
|
|
114
|
+
} else {
|
|
115
|
+
inFilePath.push(segment);
|
|
167
116
|
}
|
|
168
|
-
|
|
117
|
+
}
|
|
118
|
+
return { location, path: inFilePath };
|
|
119
|
+
};
|
|
120
|
+
export {
|
|
121
|
+
pointerToPath,
|
|
122
|
+
resolveSourceOrigin,
|
|
123
|
+
resolveSourceOriginFromMap,
|
|
124
|
+
resolveSourcePath
|
|
169
125
|
};
|
package/dist/core/ruleset.js
CHANGED
|
Binary file
|