@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,42 +1,35 @@
1
- import { isObject } from './helpers.js';
2
- // JSON Schema keywords that mark an object as a Schema Object (vs. a Media Type
3
- // or Parameter Object, whose singular `example` is *not* deprecated in 3.1+).
4
- const SCHEMA_KEYWORDS = new Set([
5
- 'type',
6
- 'properties',
7
- 'items',
8
- 'allOf',
9
- 'anyOf',
10
- 'oneOf',
11
- 'not',
12
- 'enum',
13
- 'format',
14
- 'required',
15
- 'additionalProperties',
16
- 'patternProperties',
17
- 'prefixItems',
18
- '$ref',
1
+ import { isObject } from "./helpers.js";
2
+ const SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
3
+ "type",
4
+ "properties",
5
+ "items",
6
+ "allOf",
7
+ "anyOf",
8
+ "oneOf",
9
+ "not",
10
+ "enum",
11
+ "format",
12
+ "required",
13
+ "additionalProperties",
14
+ "patternProperties",
15
+ "prefixItems",
16
+ "$ref"
19
17
  ]);
20
- /**
21
- * Flags a Schema Object's singular `example`, deprecated in OpenAPI 3.1 (JSON
22
- * Schema 2020-12) in favor of the `examples` array. Targets the parent of an
23
- * `example` key (`$..example^`); to avoid false positives it only fires when the
24
- * parent looks like a Schema Object — it carries a JSON Schema keyword and is not
25
- * a Media Type / Parameter / Header object (those have a `schema` field and keep
26
- * a valid singular `example`).
27
- */
28
- export const oasSchemaExampleDeprecated = (input, _options, context) => {
29
- if (!isObject(input) || input['example'] === undefined)
30
- return [];
31
- if ('schema' in input)
32
- return [];
33
- const looksLikeSchema = Object.keys(input).some((key) => SCHEMA_KEYWORDS.has(key));
34
- if (!looksLikeSchema)
35
- return [];
36
- return [
37
- {
38
- message: 'Schema "example" is deprecated in OpenAPI 3.1; use "examples" instead.',
39
- path: [...context.path, 'example'],
40
- },
41
- ];
18
+ const oasSchemaExampleDeprecated = (input, _options, context) => {
19
+ if (!isObject(input) || input["example"] === void 0)
20
+ return [];
21
+ if ("schema" in input)
22
+ return [];
23
+ const looksLikeSchema = Object.keys(input).some((key) => SCHEMA_KEYWORDS.has(key));
24
+ if (!looksLikeSchema)
25
+ return [];
26
+ return [
27
+ {
28
+ message: 'Schema "example" is deprecated in OpenAPI 3.1; use "examples" instead.',
29
+ path: [...context.path, "example"]
30
+ }
31
+ ];
32
+ };
33
+ export {
34
+ oasSchemaExampleDeprecated
42
35
  };
@@ -1,15 +1,10 @@
1
- import { schema as schemaFunction } from '../../../functions/index.js';
2
- import { loadOasSchema } from '../schemas/index.js';
3
- /**
4
- * Validates a whole OpenAPI document against the official structural meta-schema
5
- * for a given `version`, loading that version's schema lazily on first use. The
6
- * `*-schema` rules are format-gated to a single version, so linting a document
7
- * only ever reads its own version's schema file — the other versions' schemas
8
- * are never loaded. Delegates the actual validation to the built-in `schema`
9
- * function (which caches the prepared validator by schema identity).
10
- */
11
- export const oasSchema = (input, options, context) => {
12
- if (!options?.version)
13
- return [];
14
- return schemaFunction(input, { schema: loadOasSchema(options.version) }, context);
1
+ import { schema as schemaFunction } from "../../../functions/index.js";
2
+ import { loadOasSchema } from "../schemas/index.js";
3
+ const oasSchema = (input, options, context) => {
4
+ if (!options?.version)
5
+ return [];
6
+ return schemaFunction(input, { schema: loadOasSchema(options.version) }, context);
7
+ };
8
+ export {
9
+ oasSchema
15
10
  };
@@ -1,20 +1,22 @@
1
- import { isObject } from './helpers.js';
2
- /** Flags duplicate OpenAPI 3.2 Server Object `name` values across the servers array. */
3
- export const oasServerNameUnique = (servers, _options, context) => {
4
- if (!Array.isArray(servers))
5
- return [];
6
- const seen = new Set();
7
- const results = [];
8
- servers.forEach((server, index) => {
9
- if (!isObject(server) || typeof server['name'] !== 'string')
10
- return;
11
- if (seen.has(server['name'])) {
12
- results.push({
13
- message: `Server name "${server['name']}" must be unique`,
14
- path: [...context.path, index, 'name'],
15
- });
16
- }
17
- seen.add(server['name']);
18
- });
19
- return results;
1
+ import { isObject } from "./helpers.js";
2
+ const oasServerNameUnique = (servers, _options, context) => {
3
+ if (!Array.isArray(servers))
4
+ return [];
5
+ const seen = /* @__PURE__ */ new Set();
6
+ const results = [];
7
+ servers.forEach((server, index) => {
8
+ if (!isObject(server) || typeof server["name"] !== "string")
9
+ return;
10
+ if (seen.has(server["name"])) {
11
+ results.push({
12
+ message: `Server name "${server["name"]}" must be unique`,
13
+ path: [...context.path, index, "name"]
14
+ });
15
+ }
16
+ seen.add(server["name"]);
17
+ });
18
+ return results;
19
+ };
20
+ export {
21
+ oasServerNameUnique
20
22
  };
@@ -1,52 +1,48 @@
1
- import { isObject } from './helpers.js';
2
- /**
3
- * Validates a Server Object's `variables`, mirroring Spectral's `serverVariables`:
4
- * every `{template}` in the URL must have a matching variable and vice versa, and
5
- * each defined variable must have a `default`, a non-empty `enum` when present,
6
- * and a `default` that is listed in that `enum`.
7
- */
8
- export const oasServerVariables = (server, _options, context) => {
9
- if (!isObject(server) || typeof server['url'] !== 'string')
10
- return [];
11
- const templates = [...server['url'].matchAll(/\{([^}]+)\}/g)].map((m) => m[1]);
12
- const variables = isObject(server['variables']) ? server['variables'] : {};
13
- const results = [];
14
- for (const template of templates) {
15
- if (!(template in variables)) {
16
- results.push({ message: `Server variable "${template}" is not defined`, path: [...context.path, 'variables'] });
17
- }
1
+ import { isObject } from "./helpers.js";
2
+ const oasServerVariables = (server, _options, context) => {
3
+ if (!isObject(server) || typeof server["url"] !== "string")
4
+ return [];
5
+ const templates = [...server["url"].matchAll(/\{([^}]+)\}/g)].map((m) => m[1]);
6
+ const variables = isObject(server["variables"]) ? server["variables"] : {};
7
+ const results = [];
8
+ for (const template of templates) {
9
+ if (!(template in variables)) {
10
+ results.push({ message: `Server variable "${template}" is not defined`, path: [...context.path, "variables"] });
18
11
  }
19
- for (const [name, variable] of Object.entries(variables)) {
20
- if (!templates.includes(name)) {
21
- results.push({
22
- message: `Server variable "${name}" is not used in the URL`,
23
- path: [...context.path, 'variables', name],
24
- });
25
- }
26
- if (!isObject(variable))
27
- continue;
28
- const hasDefault = variable['default'] !== undefined;
29
- if (!hasDefault) {
30
- results.push({
31
- message: `Server variable "${name}" has a missing default`,
32
- path: [...context.path, 'variables', name],
33
- });
34
- }
35
- if ('enum' in variable) {
36
- const enumValues = Array.isArray(variable['enum']) ? variable['enum'] : [];
37
- if (enumValues.length === 0) {
38
- results.push({
39
- message: `Server variable "${name}" has an empty enum`,
40
- path: [...context.path, 'variables', name, 'enum'],
41
- });
42
- }
43
- else if (hasDefault && !enumValues.includes(variable['default'])) {
44
- results.push({
45
- message: `Server variable "${name}" has a default not listed in the enum`,
46
- path: [...context.path, 'variables', name, 'default'],
47
- });
48
- }
49
- }
12
+ }
13
+ for (const [name, variable] of Object.entries(variables)) {
14
+ if (!templates.includes(name)) {
15
+ results.push({
16
+ message: `Server variable "${name}" is not used in the URL`,
17
+ path: [...context.path, "variables", name]
18
+ });
50
19
  }
51
- return results;
20
+ if (!isObject(variable))
21
+ continue;
22
+ const hasDefault = variable["default"] !== void 0;
23
+ if (!hasDefault) {
24
+ results.push({
25
+ message: `Server variable "${name}" has a missing default`,
26
+ path: [...context.path, "variables", name]
27
+ });
28
+ }
29
+ if ("enum" in variable) {
30
+ const enumValues = Array.isArray(variable["enum"]) ? variable["enum"] : [];
31
+ if (enumValues.length === 0) {
32
+ results.push({
33
+ message: `Server variable "${name}" has an empty enum`,
34
+ path: [...context.path, "variables", name, "enum"]
35
+ });
36
+ } else if (hasDefault && !enumValues.includes(variable["default"])) {
37
+ results.push({
38
+ message: `Server variable "${name}" has a default not listed in the enum`,
39
+ path: [...context.path, "variables", name, "default"]
40
+ });
41
+ }
42
+ }
43
+ }
44
+ return results;
45
+ };
46
+ export {
47
+ oasServerVariables
52
48
  };
@@ -1,21 +1,21 @@
1
- import { isObject } from './helpers.js';
2
- /** Ensures each operation tag is declared in the global `tags` list. */
3
- export const oasTagDefined = (input, _options, context) => {
4
- const root = context.document.data;
5
- if (!isObject(root) || !isObject(input))
6
- return [];
7
- const globalTags = new Set((Array.isArray(root['tags']) ? root['tags'] : [])
8
- .map((tag) => (isObject(tag) ? tag['name'] : undefined))
9
- .filter((name) => typeof name === 'string'));
10
- const tags = Array.isArray(input['tags']) ? input['tags'] : [];
11
- const results = [];
12
- tags.forEach((tag, index) => {
13
- if (typeof tag === 'string' && !globalTags.has(tag)) {
14
- results.push({
15
- message: `Operation tag "${tag}" is not defined in the global tags`,
16
- path: [...context.path, 'tags', index],
17
- });
18
- }
19
- });
20
- return results;
1
+ import { isObject } from "./helpers.js";
2
+ const oasTagDefined = (input, _options, context) => {
3
+ const root = context.document.data;
4
+ if (!isObject(root) || !isObject(input))
5
+ return [];
6
+ const globalTags = new Set((Array.isArray(root["tags"]) ? root["tags"] : []).map((tag) => isObject(tag) ? tag["name"] : void 0).filter((name) => typeof name === "string"));
7
+ const tags = Array.isArray(input["tags"]) ? input["tags"] : [];
8
+ const results = [];
9
+ tags.forEach((tag, index) => {
10
+ if (typeof tag === "string" && !globalTags.has(tag)) {
11
+ results.push({
12
+ message: `Operation tag "${tag}" is not defined in the global tags`,
13
+ path: [...context.path, "tags", index]
14
+ });
15
+ }
16
+ });
17
+ return results;
18
+ };
19
+ export {
20
+ oasTagDefined
21
21
  };
@@ -1,17 +1,17 @@
1
- import { isObject } from './helpers.js';
2
- // Registered OpenAPI 3.2 Tag `kind` values (the field is extensible via a
3
- // community registry, so this rule is recommended: false / opt-in).
4
- const REGISTERED_TAG_KINDS = new Set(['nav', 'badge', 'audience']);
5
- /** Flags a present-but-unregistered OpenAPI 3.2 Tag Object `kind` value. */
6
- export const oasTagKind = (tag, _options, context) => {
7
- if (!isObject(tag) || typeof tag['kind'] !== 'string')
8
- return [];
9
- if (REGISTERED_TAG_KINDS.has(tag['kind']))
10
- return [];
11
- return [
12
- {
13
- message: `Tag kind "${tag['kind']}" is not a registered value (${[...REGISTERED_TAG_KINDS].join(', ')})`,
14
- path: [...context.path, 'kind'],
15
- },
16
- ];
1
+ import { isObject } from "./helpers.js";
2
+ const REGISTERED_TAG_KINDS = /* @__PURE__ */ new Set(["nav", "badge", "audience"]);
3
+ const oasTagKind = (tag, _options, context) => {
4
+ if (!isObject(tag) || typeof tag["kind"] !== "string")
5
+ return [];
6
+ if (REGISTERED_TAG_KINDS.has(tag["kind"]))
7
+ return [];
8
+ return [
9
+ {
10
+ message: `Tag kind "${tag["kind"]}" is not a registered value (${[...REGISTERED_TAG_KINDS].join(", ")})`,
11
+ path: [...context.path, "kind"]
12
+ }
13
+ ];
14
+ };
15
+ export {
16
+ oasTagKind
17
17
  };
@@ -1,45 +1,42 @@
1
- import { isObject } from './helpers.js';
2
- /**
3
- * Validates the OpenAPI 3.2 Tag Object `parent` hierarchy: every `parent` must
4
- * name a tag that exists in the top-level `tags` list, a tag must not be its own
5
- * parent, and the parent chain must not form a cycle.
6
- */
7
- export const oasTagParentDefined = (tags, _options, context) => {
8
- if (!Array.isArray(tags))
9
- return [];
10
- const byName = new Map();
11
- for (const tag of tags) {
12
- if (isObject(tag) && typeof tag['name'] === 'string')
13
- byName.set(tag['name'], tag);
1
+ import { isObject } from "./helpers.js";
2
+ const oasTagParentDefined = (tags, _options, context) => {
3
+ if (!Array.isArray(tags))
4
+ return [];
5
+ const byName = /* @__PURE__ */ new Map();
6
+ for (const tag of tags) {
7
+ if (isObject(tag) && typeof tag["name"] === "string")
8
+ byName.set(tag["name"], tag);
9
+ }
10
+ const results = [];
11
+ tags.forEach((tag, index) => {
12
+ if (!isObject(tag) || typeof tag["parent"] !== "string")
13
+ return;
14
+ const parent = tag["parent"];
15
+ const name = typeof tag["name"] === "string" ? tag["name"] : void 0;
16
+ if (!byName.has(parent)) {
17
+ results.push({
18
+ message: `Tag parent "${parent}" is not defined in the global tags`,
19
+ path: [...context.path, index, "parent"]
20
+ });
21
+ return;
14
22
  }
15
- const results = [];
16
- tags.forEach((tag, index) => {
17
- if (!isObject(tag) || typeof tag['parent'] !== 'string')
18
- return;
19
- const parent = tag['parent'];
20
- const name = typeof tag['name'] === 'string' ? tag['name'] : undefined;
21
- if (!byName.has(parent)) {
22
- results.push({
23
- message: `Tag parent "${parent}" is not defined in the global tags`,
24
- path: [...context.path, index, 'parent'],
25
- });
26
- return;
27
- }
28
- // Walk the parent chain from this tag; a repeat means a cycle (incl. self-parent).
29
- const visited = new Set(name ? [name] : []);
30
- let current = parent;
31
- while (current !== undefined) {
32
- if (visited.has(current)) {
33
- results.push({
34
- message: `Tag "${name ?? '(unnamed)'}" has a circular parent reference via "${current}"`,
35
- path: [...context.path, index, 'parent'],
36
- });
37
- break;
38
- }
39
- visited.add(current);
40
- const next = byName.get(current)?.['parent'];
41
- current = typeof next === 'string' ? next : undefined;
42
- }
43
- });
44
- return results;
23
+ const visited = new Set(name ? [name] : []);
24
+ let current = parent;
25
+ while (current !== void 0) {
26
+ if (visited.has(current)) {
27
+ results.push({
28
+ message: `Tag "${name ?? "(unnamed)"}" has a circular parent reference via "${current}"`,
29
+ path: [...context.path, index, "parent"]
30
+ });
31
+ break;
32
+ }
33
+ visited.add(current);
34
+ const next = byName.get(current)?.["parent"];
35
+ current = typeof next === "string" ? next : void 0;
36
+ }
37
+ });
38
+ return results;
39
+ };
40
+ export {
41
+ oasTagParentDefined
45
42
  };
@@ -1,17 +1,19 @@
1
- import { isObject } from './helpers.js';
2
- /** Flags duplicate global tag names. */
3
- export const oasTagsUnique = (tags, _options, context) => {
4
- if (!Array.isArray(tags))
5
- return [];
6
- const seen = new Set();
7
- const results = [];
8
- tags.forEach((tag, index) => {
9
- if (!isObject(tag) || typeof tag['name'] !== 'string')
10
- return;
11
- if (seen.has(tag['name'])) {
12
- results.push({ message: `Duplicate tag name "${tag['name']}"`, path: [...context.path, index, 'name'] });
13
- }
14
- seen.add(tag['name']);
15
- });
16
- return results;
1
+ import { isObject } from "./helpers.js";
2
+ const oasTagsUnique = (tags, _options, context) => {
3
+ if (!Array.isArray(tags))
4
+ return [];
5
+ const seen = /* @__PURE__ */ new Set();
6
+ const results = [];
7
+ tags.forEach((tag, index) => {
8
+ if (!isObject(tag) || typeof tag["name"] !== "string")
9
+ return;
10
+ if (seen.has(tag["name"])) {
11
+ results.push({ message: `Duplicate tag name "${tag["name"]}"`, path: [...context.path, index, "name"] });
12
+ }
13
+ seen.add(tag["name"]);
14
+ });
15
+ return results;
16
+ };
17
+ export {
18
+ oasTagsUnique
17
19
  };
@@ -1,65 +1,55 @@
1
- import { isObject } from './helpers.js';
2
- // Reusable component types that are referenced via `$ref` (securitySchemes are
3
- // referenced by name in `security`, not via `$ref`, so they are excluded).
4
- // `pathItems` was added in OpenAPI 3.1 (referenced from `webhooks` / `callbacks`).
1
+ import { isObject } from "./helpers.js";
5
2
  const REUSABLE_COMPONENT_TYPES = [
6
- 'schemas',
7
- 'responses',
8
- 'parameters',
9
- 'examples',
10
- 'requestBodies',
11
- 'headers',
12
- 'links',
13
- 'callbacks',
14
- 'pathItems',
3
+ "schemas",
4
+ "responses",
5
+ "parameters",
6
+ "examples",
7
+ "requestBodies",
8
+ "headers",
9
+ "links",
10
+ "callbacks",
11
+ "pathItems"
15
12
  ];
16
- /** Collects every `$ref` string anywhere in `node` into `into`. */
17
13
  const collectRefs = (node, into) => {
18
- if (Array.isArray(node)) {
19
- for (const item of node)
20
- collectRefs(item, into);
21
- return;
22
- }
23
- if (isObject(node)) {
24
- for (const [key, value] of Object.entries(node)) {
25
- if (key === '$ref' && typeof value === 'string')
26
- into.add(value);
27
- else
28
- collectRefs(value, into);
29
- }
14
+ if (Array.isArray(node)) {
15
+ for (const item of node)
16
+ collectRefs(item, into);
17
+ return;
18
+ }
19
+ if (isObject(node)) {
20
+ for (const [key, value] of Object.entries(node)) {
21
+ if (key === "$ref" && typeof value === "string")
22
+ into.add(value);
23
+ else
24
+ collectRefs(value, into);
30
25
  }
26
+ }
31
27
  };
32
- /**
33
- * Flags reusable `components/*` entries that nothing `$ref`s. Spectral's
34
- * `oas3-unused-component` checks every reusable component type (not just
35
- * schemas), so we do too — run on the unresolved document.
36
- */
37
- export const oasUnusedComponent = (components, _options, context) => {
38
- if (!isObject(components))
39
- return [];
40
- const refs = new Set();
41
- collectRefs(context.document.data, refs);
42
- // A component counts as used when a `$ref` targets it OR points *into* it
43
- // (e.g. `#/components/schemas/Pet/properties/id` still uses `Pet`), so match by
44
- // prefix rather than exact string — an interior ref must not leave the parent
45
- // flagged as unused.
46
- const isReferenced = (base) => {
47
- for (const ref of refs) {
48
- if (ref === base || ref.startsWith(`${base}/`))
49
- return true;
50
- }
51
- return false;
52
- };
53
- const results = [];
54
- for (const type of REUSABLE_COMPONENT_TYPES) {
55
- const group = components[type];
56
- if (!isObject(group))
57
- continue;
58
- for (const key of Object.keys(group)) {
59
- if (!isReferenced(`#/components/${type}/${key}`)) {
60
- results.push({ message: 'Potentially unused component has been detected.', path: [...context.path, type, key] });
61
- }
62
- }
28
+ const oasUnusedComponent = (components, _options, context) => {
29
+ if (!isObject(components))
30
+ return [];
31
+ const refs = /* @__PURE__ */ new Set();
32
+ collectRefs(context.document.data, refs);
33
+ const isReferenced = (base) => {
34
+ for (const ref of refs) {
35
+ if (ref === base || ref.startsWith(`${base}/`))
36
+ return true;
37
+ }
38
+ return false;
39
+ };
40
+ const results = [];
41
+ for (const type of REUSABLE_COMPONENT_TYPES) {
42
+ const group = components[type];
43
+ if (!isObject(group))
44
+ continue;
45
+ for (const key of Object.keys(group)) {
46
+ if (!isReferenced(`#/components/${type}/${key}`)) {
47
+ results.push({ message: "Potentially unused component has been detected.", path: [...context.path, type, key] });
48
+ }
63
49
  }
64
- return results;
50
+ }
51
+ return results;
52
+ };
53
+ export {
54
+ oasUnusedComponent
65
55
  };
@@ -1,13 +1,15 @@
1
- import { isObject } from './helpers.js';
2
- /** Flags objects that mix `$ref` with sibling keys (which are ignored per spec). */
3
- export const refSiblings = (input, _options, context) => {
4
- if (!isObject(input) || !('$ref' in input))
5
- return [];
6
- const results = [];
7
- for (const key of Object.keys(input)) {
8
- if (key !== '$ref') {
9
- results.push({ message: `$ref must not be placed next to "${key}"`, path: [...context.path, key] });
10
- }
1
+ import { isObject } from "./helpers.js";
2
+ const refSiblings = (input, _options, context) => {
3
+ if (!isObject(input) || !("$ref" in input))
4
+ return [];
5
+ const results = [];
6
+ for (const key of Object.keys(input)) {
7
+ if (key !== "$ref") {
8
+ results.push({ message: `$ref must not be placed next to "${key}"`, path: [...context.path, key] });
11
9
  }
12
- return results;
10
+ }
11
+ return results;
12
+ };
13
+ export {
14
+ refSiblings
13
15
  };