@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.
Files changed (72) hide show
  1. package/dist/core/document.js +12 -10
  2. package/dist/core/formats.js +10 -13
  3. package/dist/core/glob.js +96 -118
  4. package/dist/core/index.js +31 -11
  5. package/dist/core/jsonpath.js +487 -561
  6. package/dist/core/lint.js +78 -85
  7. package/dist/core/plugin.js +21 -29
  8. package/dist/core/pointers.js +107 -151
  9. package/dist/core/ruleset.js +0 -0
  10. package/dist/core/runner.js +214 -272
  11. package/dist/core/types.js +4 -1
  12. package/dist/core/validate-ruleset.js +98 -112
  13. package/dist/fix/apply.js +58 -96
  14. package/dist/fix/index.js +7 -2
  15. package/dist/fix/plugin.js +15 -20
  16. package/dist/functions/alphabetical.js +39 -50
  17. package/dist/functions/casing.js +39 -45
  18. package/dist/functions/defined.js +7 -5
  19. package/dist/functions/enumeration.js +15 -25
  20. package/dist/functions/falsy.js +7 -5
  21. package/dist/functions/index.js +60 -44
  22. package/dist/functions/length.js +22 -32
  23. package/dist/functions/or.js +18 -24
  24. package/dist/functions/pattern.js +39 -49
  25. package/dist/functions/schema.js +93 -119
  26. package/dist/functions/truthy.js +7 -5
  27. package/dist/functions/typed-enum.js +33 -36
  28. package/dist/functions/undefined.js +7 -8
  29. package/dist/functions/unreferenced-reusable-object.js +35 -47
  30. package/dist/functions/xor.js +13 -16
  31. package/dist/index.js +114 -164
  32. package/dist/parsers/edit-model.js +316 -444
  33. package/dist/parsers/index.js +22 -19
  34. package/dist/parsers/json.js +39 -37
  35. package/dist/parsers/lines.js +23 -26
  36. package/dist/parsers/types.js +9 -7
  37. package/dist/parsers/yaml.js +137 -204
  38. package/dist/rules/openapi/fixers.js +166 -221
  39. package/dist/rules/openapi/formats.js +26 -27
  40. package/dist/rules/openapi/functions/example-validation.js +98 -138
  41. package/dist/rules/openapi/functions/helpers.js +8 -10
  42. package/dist/rules/openapi/functions/index.js +98 -72
  43. package/dist/rules/openapi/functions/oas-additional-operations.js +16 -22
  44. package/dist/rules/openapi/functions/oas-discriminator.js +24 -22
  45. package/dist/rules/openapi/functions/oas-example-external-value.js +13 -21
  46. package/dist/rules/openapi/functions/oas-example-value.js +24 -27
  47. package/dist/rules/openapi/functions/oas-mutually-exclusive.js +15 -19
  48. package/dist/rules/openapi/functions/oas-no-nullable.js +13 -21
  49. package/dist/rules/openapi/functions/oas-op-form-data-consume-check.js +21 -19
  50. package/dist/rules/openapi/functions/oas-op-id-unique.js +27 -27
  51. package/dist/rules/openapi/functions/oas-op-params.js +36 -42
  52. package/dist/rules/openapi/functions/oas-op-security-defined.js +40 -39
  53. package/dist/rules/openapi/functions/oas-op-success-response.js +11 -13
  54. package/dist/rules/openapi/functions/oas-path-param.js +75 -96
  55. package/dist/rules/openapi/functions/oas-schema-example-deprecated.js +33 -40
  56. package/dist/rules/openapi/functions/oas-schema.js +9 -14
  57. package/dist/rules/openapi/functions/oas-server-name-unique.js +21 -19
  58. package/dist/rules/openapi/functions/oas-server-variables.js +45 -49
  59. package/dist/rules/openapi/functions/oas-tag-defined.js +20 -20
  60. package/dist/rules/openapi/functions/oas-tag-kind.js +16 -16
  61. package/dist/rules/openapi/functions/oas-tag-parent-defined.js +40 -43
  62. package/dist/rules/openapi/functions/oas-tags-unique.js +18 -16
  63. package/dist/rules/openapi/functions/oas-unused-component.js +48 -58
  64. package/dist/rules/openapi/functions/ref-siblings.js +13 -11
  65. package/dist/rules/openapi/index.js +99 -117
  66. package/dist/rules/openapi/oas.js +524 -536
  67. package/dist/rules/openapi/schemas/index.js +17 -33
  68. package/dist/rules/openapi/schemas/oas20.json +1 -1592
  69. package/dist/rules/openapi/schemas/oas30.json +1 -1651
  70. package/dist/rules/openapi/schemas/oas31.json +1 -1412
  71. package/dist/rules/openapi/schemas/oas32.json +1 -1684
  72. package/package.json +5 -5
@@ -1,21 +1,24 @@
1
- export { applyEditOps, applyEditOpsWithChanges } from './edit-model.js';
2
- export { parseJson } from './json.js';
3
- export { createLineMap } from './lines.js';
4
- export * from './types.js';
5
- export { parseYaml } from './yaml.js';
6
- import { parseJson } from './json.js';
7
- import { parseYaml } from './yaml.js';
8
- /** Guesses the format from the first non-whitespace character (`{`/`[` ⇒ JSON, else YAML). */
9
- export const detectFormat = (source) => {
10
- const trimmed = source.trimStart();
11
- return trimmed.startsWith('{') || trimmed.startsWith('[') ? 'json' : 'yaml';
1
+ import { applyEditOps, applyEditOpsWithChanges } from "./edit-model.js";
2
+ import { parseJson } from "./json.js";
3
+ import { createLineMap } from "./lines.js";
4
+ export * from "./types.js";
5
+ import { parseYaml } from "./yaml.js";
6
+ import { parseJson as parseJson2 } from "./json.js";
7
+ import { parseYaml as parseYaml2 } from "./yaml.js";
8
+ const detectFormat = (source) => {
9
+ const trimmed = source.trimStart();
10
+ return trimmed.startsWith("{") || trimmed.startsWith("[") ? "json" : "yaml";
12
11
  };
13
- /**
14
- * Parses a document with source maps. YAML is a JSON superset, so the YAML
15
- * parser is the default and handles both; the JSON parser is used when a strict
16
- * JSON document is detected or requested (it reports JSON-specific errors).
17
- */
18
- export const parseWithPointers = (source, options = {}) => {
19
- const format = options.format ?? detectFormat(source);
20
- return format === 'json' ? parseJson(source) : parseYaml(source, options);
12
+ const parseWithPointers = (source, options = {}) => {
13
+ const format = options.format ?? detectFormat(source);
14
+ return format === "json" ? parseJson2(source) : parseYaml2(source, options);
15
+ };
16
+ export {
17
+ applyEditOps,
18
+ applyEditOpsWithChanges,
19
+ createLineMap,
20
+ detectFormat,
21
+ parseJson,
22
+ parseWithPointers,
23
+ parseYaml
21
24
  };
@@ -1,38 +1,40 @@
1
- import { findNodeAtLocation, getNodeValue, parseTree, printParseErrorCode } from 'jsonc-parser';
2
- import { createLineMap } from './lines.js';
3
- import { DiagnosticSeverity } from './types.js';
4
- /** Parses strict JSON into data plus a source map, reporting JSON-specific syntax errors. */
5
- export const parseJson = (source) => {
6
- const lineMap = createLineMap(source);
7
- const errors = [];
8
- const root = parseTree(source, errors, { allowTrailingComma: false, disallowComments: true });
9
- const data = (root ? getNodeValue(root) : undefined);
10
- const diagnostics = errors.map((err) => ({
11
- message: printParseErrorCode(err.error),
12
- severity: DiagnosticSeverity.Error,
13
- range: {
14
- start: lineMap.positionAt(err.offset),
15
- end: lineMap.positionAt(err.offset + err.length),
16
- },
17
- }));
18
- const getLocationForJsonPath = (path, closest = false) => {
19
- if (!root)
20
- return undefined;
21
- const p = path.slice();
22
- while (true) {
23
- const node = findNodeAtLocation(root, p);
24
- if (node) {
25
- return {
26
- range: {
27
- start: lineMap.positionAt(node.offset),
28
- end: lineMap.positionAt(node.offset + node.length),
29
- },
30
- };
31
- }
32
- if (!closest || p.length === 0)
33
- return undefined;
34
- p.pop();
35
- }
36
- };
37
- return { data, diagnostics, getLocationForJsonPath };
1
+ import { findNodeAtLocation, getNodeValue, parseTree, printParseErrorCode } from "jsonc-parser";
2
+ import { createLineMap } from "./lines.js";
3
+ import { DiagnosticSeverity } from "./types.js";
4
+ const parseJson = (source) => {
5
+ const lineMap = createLineMap(source);
6
+ const errors = [];
7
+ const root = parseTree(source, errors, { allowTrailingComma: false, disallowComments: true });
8
+ const data = root ? getNodeValue(root) : void 0;
9
+ const diagnostics = errors.map((err) => ({
10
+ message: printParseErrorCode(err.error),
11
+ severity: DiagnosticSeverity.Error,
12
+ range: {
13
+ start: lineMap.positionAt(err.offset),
14
+ end: lineMap.positionAt(err.offset + err.length)
15
+ }
16
+ }));
17
+ const getLocationForJsonPath = (path, closest = false) => {
18
+ if (!root)
19
+ return void 0;
20
+ const p = path.slice();
21
+ while (true) {
22
+ const node = findNodeAtLocation(root, p);
23
+ if (node) {
24
+ return {
25
+ range: {
26
+ start: lineMap.positionAt(node.offset),
27
+ end: lineMap.positionAt(node.offset + node.length)
28
+ }
29
+ };
30
+ }
31
+ if (!closest || p.length === 0)
32
+ return void 0;
33
+ p.pop();
34
+ }
35
+ };
36
+ return { data, diagnostics, getLocationForJsonPath };
37
+ };
38
+ export {
39
+ parseJson
38
40
  };
@@ -1,28 +1,25 @@
1
- /**
2
- * Builds a {@link LineMap} for `source`. The line-start offsets are precomputed
3
- * once so each `positionAt` lookup is a binary search rather than a re-scan.
4
- */
5
- export const createLineMap = (source) => {
6
- const length = source.length;
7
- // Offset at which each line starts. lineStarts[0] === 0.
8
- const lineStarts = [0];
9
- for (let i = 0; i < source.length; i++) {
10
- if (source.charCodeAt(i) === 10 /* \n */)
11
- lineStarts.push(i + 1);
1
+ const createLineMap = (source) => {
2
+ const length = source.length;
3
+ const lineStarts = [0];
4
+ for (let i = 0; i < source.length; i++) {
5
+ if (source.charCodeAt(i) === 10)
6
+ lineStarts.push(i + 1);
7
+ }
8
+ const positionAt = (offset) => {
9
+ const clamped = Math.max(0, Math.min(offset, length));
10
+ let low = 0;
11
+ let high = lineStarts.length - 1;
12
+ while (low < high) {
13
+ const mid = low + high + 1 >> 1;
14
+ if ((lineStarts[mid] ?? 0) <= clamped)
15
+ low = mid;
16
+ else
17
+ high = mid - 1;
12
18
  }
13
- const positionAt = (offset) => {
14
- const clamped = Math.max(0, Math.min(offset, length));
15
- // Binary search for the last line start <= clamped.
16
- let low = 0;
17
- let high = lineStarts.length - 1;
18
- while (low < high) {
19
- const mid = (low + high + 1) >> 1;
20
- if ((lineStarts[mid] ?? 0) <= clamped)
21
- low = mid;
22
- else
23
- high = mid - 1;
24
- }
25
- return { line: low, character: clamped - (lineStarts[low] ?? 0) };
26
- };
27
- return { positionAt };
19
+ return { line: low, character: clamped - (lineStarts[low] ?? 0) };
20
+ };
21
+ return { positionAt };
22
+ };
23
+ export {
24
+ createLineMap
28
25
  };
@@ -1,8 +1,10 @@
1
- /** Severity levels, ordered most-to-least severe to match LSP's numeric scale. */
2
- export var DiagnosticSeverity;
3
- (function (DiagnosticSeverity) {
4
- DiagnosticSeverity[DiagnosticSeverity["Error"] = 0] = "Error";
5
- DiagnosticSeverity[DiagnosticSeverity["Warning"] = 1] = "Warning";
6
- DiagnosticSeverity[DiagnosticSeverity["Information"] = 2] = "Information";
7
- DiagnosticSeverity[DiagnosticSeverity["Hint"] = 3] = "Hint";
1
+ var DiagnosticSeverity;
2
+ (function(DiagnosticSeverity2) {
3
+ DiagnosticSeverity2[DiagnosticSeverity2["Error"] = 0] = "Error";
4
+ DiagnosticSeverity2[DiagnosticSeverity2["Warning"] = 1] = "Warning";
5
+ DiagnosticSeverity2[DiagnosticSeverity2["Information"] = 2] = "Information";
6
+ DiagnosticSeverity2[DiagnosticSeverity2["Hint"] = 3] = "Hint";
8
7
  })(DiagnosticSeverity || (DiagnosticSeverity = {}));
8
+ export {
9
+ DiagnosticSeverity
10
+ };
@@ -1,213 +1,146 @@
1
- import { isAlias, isMap, isPair, isScalar, isSeq, parseAllDocuments, } from '@amritk/yaml';
2
- import { createLineMap } from './lines.js';
3
- import { DiagnosticSeverity, } from './types.js';
4
- /**
5
- * Encodes a path into a lookup key. Each segment is tagged by kind (`.` for a
6
- * key, `[]` for an index) so distinct paths cannot collide: a plain `join` turns
7
- * a `null` map key into `''` (colliding with the root path `[]`) and cannot tell
8
- * the numeric index `0` from the string key `"0"`. The tags keep them apart.
9
- */
10
- const pathKey = (path) => path.map((segment) => (typeof segment === 'number' ? `[${segment}]` : `.${segment}`)).join('');
11
- /**
12
- * Canonically serializes a complex (map/seq) mapping key into a stable, distinct
13
- * segment. `toJS`'s `keyText` collapses every complex key to `''`, so two
14
- * distinct complex keys (and their whole value subtrees) would share one index
15
- * slot and clobber each other. A structural serialization keeps them apart:
16
- * `[a,b]` for a sequence key, `{k:v}` for a mapping key, recursively. Strings are
17
- * quoted so a scalar member can't be confused with structure, and aliases render
18
- * as `*name` (never expanded, so this stays bounded regardless of the anchor).
19
- */
1
+ import { isAlias, isMap, isPair, isScalar, isSeq, parseAllDocuments } from "@amritk/yaml";
2
+ import { createLineMap } from "./lines.js";
3
+ import { DiagnosticSeverity } from "./types.js";
4
+ const pathKey = (path) => path.map((segment) => typeof segment === "number" ? `[${segment}]` : `.${segment}`).join("");
20
5
  const serializeComplexKey = (node) => {
21
- if (isSeq(node))
22
- return `[${node.items.map(serializeComplexKey).join(',')}]`;
23
- if (isMap(node)) {
24
- return `{${node.items
25
- .filter(isPair)
26
- .map((pair) => `${serializeComplexKey(pair.key)}:${pair.value ? serializeComplexKey(pair.value) : 'null'}`)
27
- .join(',')}}`;
28
- }
29
- if (isAlias(node))
30
- return `*${node.source}`;
31
- const v = node.value;
32
- return typeof v === 'string' ? JSON.stringify(v) : v === null ? 'null' : String(v);
6
+ if (isSeq(node))
7
+ return `[${node.items.map(serializeComplexKey).join(",")}]`;
8
+ if (isMap(node)) {
9
+ return `{${node.items.filter(isPair).map((pair) => `${serializeComplexKey(pair.key)}:${pair.value ? serializeComplexKey(pair.value) : "null"}`).join(",")}}`;
10
+ }
11
+ if (isAlias(node))
12
+ return `*${node.source}`;
13
+ const v = node.value;
14
+ return typeof v === "string" ? JSON.stringify(v) : v === null ? "null" : String(v);
33
15
  };
34
- /**
35
- * Stringifies a mapping key into an index segment. Scalar, null, and alias keys
36
- * match `toJS`'s `keyText` (`null`, the `String()` form, `*name`) so scalar-keyed
37
- * paths line up with the projected data. Complex (map/seq) keys — which `toJS`
38
- * cannot address individually — get a canonical structural serialization instead
39
- * of collapsing to `''`, so distinct complex keys occupy distinct index slots.
40
- */
41
16
  const keyToString = (key) => {
42
- if (isScalar(key)) {
43
- const v = key.value;
44
- return typeof v === 'string' ? v : v === null ? 'null' : String(v);
45
- }
46
- if (isAlias(key))
47
- return `*${key.source}`;
48
- return serializeComplexKey(key);
17
+ if (isScalar(key)) {
18
+ const v = key.value;
19
+ return typeof v === "string" ? v : v === null ? "null" : String(v);
20
+ }
21
+ if (isAlias(key))
22
+ return `*${key.source}`;
23
+ return serializeComplexKey(key);
49
24
  };
50
- /**
51
- * Parses YAML (a JSON superset, so this handles both) into data plus a source
52
- * map, surfacing duplicate-key and incompatible-value diagnostics per `options`.
53
- *
54
- * A `---`-separated stream is parsed as multiple documents (via
55
- * `parseAllDocuments`), each linted independently: `data` becomes an array of
56
- * per-document values and every position key / finding path is prefixed with the
57
- * zero-based document index, so a violation in a later document resolves to its
58
- * own range instead of being silently dropped. A single-document source keeps the
59
- * flat shape — `data` is the document value and paths are unprefixed — so existing
60
- * callers and rulesets are unaffected. Node ranges are absolute offsets into the
61
- * shared source, so diagnostics and positions in later documents are already
62
- * correct without any per-document offset arithmetic.
63
- */
64
- export const parseYaml = (source, options = {}) => {
65
- const lineMap = createLineMap(source);
66
- const duplicateKeys = options.duplicateKeys;
67
- const dedupe = duplicateKeys === 'off' || duplicateKeys === false;
68
- // A configured severity (Warning/Information/Hint) still detects duplicates; we
69
- // just re-map the reported severity below. Only `off`/`false` turns detection off.
70
- const dupSeverity = typeof duplicateKeys === 'number' ? duplicateKeys : DiagnosticSeverity.Error;
71
- // Incompatible-value detection is opt-in: it runs only when a severity is
72
- // configured. `undefined`/`off`/`false` leaves it disabled.
73
- const incompatibleValues = options.incompatibleValues;
74
- const incompatSeverity = typeof incompatibleValues === 'number' ? incompatibleValues : undefined;
75
- const docs = parseAllDocuments(source, { uniqueKeys: !dedupe });
76
- const index = new Map();
77
- const diagnostics = [];
78
- const pushError = (severity, message, start, end, code) => {
79
- diagnostics.push({
80
- ...(code !== undefined ? { code } : {}),
81
- message,
82
- severity,
83
- range: { start: lineMap.positionAt(start), end: lineMap.positionAt(end) },
84
- });
85
- };
86
- const rangeOf = (node) => ({
87
- start: lineMap.positionAt(node.start),
88
- end: lineMap.positionAt(node.end),
25
+ const parseYaml = (source, options = {}) => {
26
+ const lineMap = createLineMap(source);
27
+ const duplicateKeys = options.duplicateKeys;
28
+ const dedupe = duplicateKeys === "off" || duplicateKeys === false;
29
+ const dupSeverity = typeof duplicateKeys === "number" ? duplicateKeys : DiagnosticSeverity.Error;
30
+ const incompatibleValues = options.incompatibleValues;
31
+ const incompatSeverity = typeof incompatibleValues === "number" ? incompatibleValues : void 0;
32
+ const docs = parseAllDocuments(source, { uniqueKeys: !dedupe });
33
+ const index = /* @__PURE__ */ new Map();
34
+ const diagnostics = [];
35
+ const pushError = (severity, message, start, end, code) => {
36
+ diagnostics.push({
37
+ ...code !== void 0 ? { code } : {},
38
+ message,
39
+ severity,
40
+ range: { start: lineMap.positionAt(start), end: lineMap.positionAt(end) }
89
41
  });
90
- // Aliases are re-expanded into every path that reaches them, so nested aliases
91
- // (the "billion laughs" shape) can fan out super-linearly. Bound the total
92
- // nodes walked across the whole stream; on exhaustion we stop extending the
93
- // index rather than throw — untouched paths simply fall back to the closest
94
- // indexed ancestor.
95
- let budget = Math.max(100_000, source.length * 100);
96
- /** True when a pair is a `<<` merge key, whose value folds into the parent map. */
97
- const isMergePair = (pair) => isScalar(pair.key) && pair.key.source === '<<';
98
- /**
99
- * Indexes the keys of a merged map (or list of maps, reached through the `<<`
100
- * value) at the parent `path`. A merged key is skipped when the path is already
101
- * occupied — by an explicit key or an earlier merge — mirroring `toJS`, where
102
- * explicit keys and earlier merges win over later ones.
103
- */
104
- const walkMerge = (node, path) => {
105
- const target = node != null && isAlias(node) ? node.target : node;
106
- if (target == null)
107
- return;
108
- if (isSeq(target)) {
109
- for (const item of target.items)
110
- walkMerge(item, path);
111
- return;
112
- }
113
- if (!isMap(target))
114
- return;
115
- for (const item of target.items) {
116
- if (!isPair(item))
117
- continue;
118
- if (isMergePair(item)) {
119
- walkMerge(item.value, path);
120
- continue;
121
- }
122
- const childPath = [...path, keyToString(item.key)];
123
- if (!index.has(pathKey(childPath)))
124
- walk(item.value, childPath);
125
- }
126
- };
127
- const walk = (node, path) => {
128
- if (node == null || budget-- <= 0)
129
- return;
130
- index.set(pathKey(path), rangeOf(node));
131
- if (isScalar(node)) {
132
- // The core schema projects `.nan`/`.inf`/`-.inf` to non-finite JS numbers,
133
- // which `JSON.stringify` silently rewrites to `null`. Report them when the
134
- // caller opted in, so a value that won't survive a JSON round-trip is caught.
135
- const value = node.value;
136
- if (incompatSeverity !== undefined && typeof value === 'number' && !Number.isFinite(value)) {
137
- pushError(incompatSeverity, `Value ${String(value)} cannot be represented in JSON and will serialize to null.`, node.start, node.end, 'INCOMPATIBLE_VALUE');
138
- }
139
- return;
140
- }
141
- // Follow an alias to its anchor definition so paths reachable only through the
142
- // alias resolve to the anchored node (the alias itself keeps the range set
143
- // above); an unresolved alias has no target and simply stops here.
144
- const target = isAlias(node) ? node.target : node;
145
- if (target == null)
146
- return;
147
- if (isMap(target)) {
148
- const merges = [];
149
- for (const item of target.items) {
150
- if (!isPair(item))
151
- continue;
152
- if (isMergePair(item)) {
153
- merges.push(item.value);
154
- continue;
155
- }
156
- walk(item.value, [...path, keyToString(item.key)]);
157
- }
158
- // Merged keys fill positions the explicit keys above did not claim.
159
- for (const merge of merges)
160
- walkMerge(merge, path);
161
- }
162
- else if (isSeq(target)) {
163
- target.items.forEach((item, i) => {
164
- walk(item, [...path, i]);
165
- });
166
- }
167
- };
168
- const collectProblems = (doc) => {
169
- for (const err of doc.errors) {
170
- // Duplicate keys honor the configured severity; every other parser error is
171
- // a hard error.
172
- const severity = err.code === 'DUPLICATE_KEY' ? dupSeverity : DiagnosticSeverity.Error;
173
- pushError(severity, err.message, err.start, err.end);
174
- }
175
- for (const warn of doc.warnings) {
176
- pushError(DiagnosticSeverity.Warning, warn.message, warn.start, warn.end);
177
- }
178
- };
179
- let data;
180
- if (docs.length > 1) {
181
- // Multi-document stream: index each document under its own `[i, …]` prefix and
182
- // project to an array of per-document values.
183
- data = docs.map((doc, i) => {
184
- walk(doc.contents, [i]);
185
- collectProblems(doc);
186
- return doc.toJS();
187
- });
42
+ };
43
+ const rangeOf = (node) => ({
44
+ start: lineMap.positionAt(node.start),
45
+ end: lineMap.positionAt(node.end)
46
+ });
47
+ let budget = Math.max(1e5, source.length * 100);
48
+ const isMergePair = (pair) => isScalar(pair.key) && pair.key.source === "<<";
49
+ const walkMerge = (node, path) => {
50
+ const target = node != null && isAlias(node) ? node.target : node;
51
+ if (target == null)
52
+ return;
53
+ if (isSeq(target)) {
54
+ for (const item of target.items)
55
+ walkMerge(item, path);
56
+ return;
188
57
  }
189
- else {
190
- // Single document (or an empty stream): keep the flat, unprefixed shape.
191
- const doc = docs[0];
192
- if (doc) {
193
- walk(doc.contents, []);
194
- collectProblems(doc);
195
- data = doc.toJS();
196
- }
197
- else {
198
- data = null;
199
- }
58
+ if (!isMap(target))
59
+ return;
60
+ for (const item of target.items) {
61
+ if (!isPair(item))
62
+ continue;
63
+ if (isMergePair(item)) {
64
+ walkMerge(item.value, path);
65
+ continue;
66
+ }
67
+ const childPath = [...path, keyToString(item.key)];
68
+ if (!index.has(pathKey(childPath)))
69
+ walk(item.value, childPath);
70
+ }
71
+ };
72
+ const walk = (node, path) => {
73
+ if (node == null || budget-- <= 0)
74
+ return;
75
+ index.set(pathKey(path), rangeOf(node));
76
+ if (isScalar(node)) {
77
+ const value = node.value;
78
+ if (incompatSeverity !== void 0 && typeof value === "number" && !Number.isFinite(value)) {
79
+ pushError(incompatSeverity, `Value ${String(value)} cannot be represented in JSON and will serialize to null.`, node.start, node.end, "INCOMPATIBLE_VALUE");
80
+ }
81
+ return;
200
82
  }
201
- const getLocationForJsonPath = (path, closest = false) => {
202
- const p = path.slice();
203
- while (true) {
204
- const range = index.get(pathKey(p));
205
- if (range)
206
- return { range };
207
- if (!closest || p.length === 0)
208
- return undefined;
209
- p.pop();
83
+ const target = isAlias(node) ? node.target : node;
84
+ if (target == null)
85
+ return;
86
+ if (isMap(target)) {
87
+ const merges = [];
88
+ for (const item of target.items) {
89
+ if (!isPair(item))
90
+ continue;
91
+ if (isMergePair(item)) {
92
+ merges.push(item.value);
93
+ continue;
210
94
  }
211
- };
212
- return { data: data, diagnostics, getLocationForJsonPath };
95
+ walk(item.value, [...path, keyToString(item.key)]);
96
+ }
97
+ for (const merge of merges)
98
+ walkMerge(merge, path);
99
+ } else if (isSeq(target)) {
100
+ target.items.forEach((item, i) => {
101
+ walk(item, [...path, i]);
102
+ });
103
+ }
104
+ };
105
+ const collectProblems = (doc) => {
106
+ for (const err of doc.errors) {
107
+ const severity = err.code === "DUPLICATE_KEY" ? dupSeverity : DiagnosticSeverity.Error;
108
+ pushError(severity, err.message, err.start, err.end);
109
+ }
110
+ for (const warn of doc.warnings) {
111
+ pushError(DiagnosticSeverity.Warning, warn.message, warn.start, warn.end);
112
+ }
113
+ };
114
+ let data;
115
+ if (docs.length > 1) {
116
+ data = docs.map((doc, i) => {
117
+ walk(doc.contents, [i]);
118
+ collectProblems(doc);
119
+ return doc.toJS();
120
+ });
121
+ } else {
122
+ const doc = docs[0];
123
+ if (doc) {
124
+ walk(doc.contents, []);
125
+ collectProblems(doc);
126
+ data = doc.toJS();
127
+ } else {
128
+ data = null;
129
+ }
130
+ }
131
+ const getLocationForJsonPath = (path, closest = false) => {
132
+ const p = path.slice();
133
+ while (true) {
134
+ const range = index.get(pathKey(p));
135
+ if (range)
136
+ return { range };
137
+ if (!closest || p.length === 0)
138
+ return void 0;
139
+ p.pop();
140
+ }
141
+ };
142
+ return { data, diagnostics, getLocationForJsonPath };
143
+ };
144
+ export {
145
+ parseYaml
213
146
  };