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