@sarj/eslint-plugin 18.3.1 → 19.0.1

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.
@@ -1,331 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/verify-rule-examples.ts
31
- var verify_rule_examples_exports = {};
32
- __export(verify_rule_examples_exports, {
33
- verifyRuleExamples: () => verifyRuleExamples
34
- });
35
- module.exports = __toCommonJS(verify_rule_examples_exports);
36
- var import_promises = require("fs/promises");
37
- var import_node_os = require("os");
38
- var import_node_path2 = require("path");
39
- var parser2 = __toESM(require("@typescript-eslint/parser"), 1);
40
- var import_eslint = require("eslint");
41
-
42
- // src/create-example-parser.ts
43
- var import_node_path = require("path");
44
- var parser = __toESM(require("@typescript-eslint/parser"), 1);
45
- var import_typescript = __toESM(require("typescript"), 1);
46
- var WATCH_DEFAULTS = {
47
- noEmit: true,
48
- noUnusedLocals: true,
49
- noUnusedParameters: true,
50
- allowJs: true,
51
- allowNonTsExtensions: true,
52
- checkJs: true
53
- };
54
- var MAX_LIBRARY_FILES = 256;
55
- var MAX_LIBRARY_BYTES = 16 * 1024 * 1024;
56
- function createExampleParser() {
57
- const libraryRoot = import_typescript.default.sys.realpath?.((0, import_node_path.dirname)(import_typescript.default.getDefaultLibFilePath({})));
58
- const cache = /* @__PURE__ */ new Map();
59
- const registries = /* @__PURE__ */ new Map();
60
- const counts = { programs: 0, libraryAcquires: 0, libraryHits: 0, nativeFallbacks: 0 };
61
- let bytes = 0;
62
- return {
63
- meta: parser.meta,
64
- clear() {
65
- cache.clear();
66
- registries.clear();
67
- bytes = 0;
68
- },
69
- statistics() {
70
- return { ...counts, libraryFiles: cache.size, libraryBytes: bytes };
71
- },
72
- parseForESLint(code, options) {
73
- const fallback = () => {
74
- counts.nativeFallbacks += 1;
75
- return parser.parseForESLint(code, options);
76
- };
77
- if (!Object.hasOwn(import_typescript.default, "JSDocParsingMode")) return fallback();
78
- if (typeof options?.project !== "string" || typeof options.filePath !== "string" || options.programs != null || options.projectService || options.extraFileExtensions?.length || options.debugLevel) {
79
- return fallback();
80
- }
81
- const config = (0, import_node_path.resolve)(options.tsconfigRootDir ?? process.cwd(), options.project);
82
- let unrecoverable = false;
83
- const parsed = import_typescript.default.getParsedCommandLineOfConfigFile(config, WATCH_DEFAULTS, {
84
- ...import_typescript.default.sys,
85
- onUnRecoverableConfigFileDiagnostic() {
86
- unrecoverable = true;
87
- }
88
- });
89
- if (unrecoverable || parsed === void 0 || parsed.errors.length > 0 || parsed.projectReferences?.length) {
90
- return fallback();
91
- }
92
- const filename = (0, import_node_path.resolve)(options.filePath);
93
- const host = import_typescript.default.createCompilerHost(parsed.options, true);
94
- const canonical = (path) => host.getCanonicalFileName((0, import_node_path.resolve)(path));
95
- if (!parsed.fileNames.some((path) => canonical(path) === canonical(filename))) return fallback();
96
- const mode = options.jsDocParsingMode === "none" ? import_typescript.default.JSDocParsingMode.ParseNone : options.jsDocParsingMode === "type-info" ? import_typescript.default.JSDocParsingMode.ParseForTypeInfo : import_typescript.default.JSDocParsingMode.ParseAll;
97
- host.jsDocParsingMode = mode;
98
- let registry = registries.get(mode);
99
- if (registry === void 0) {
100
- registry = import_typescript.default.createDocumentRegistry(import_typescript.default.sys.useCaseSensitiveFileNames, process.cwd(), mode);
101
- registries.set(mode, registry);
102
- }
103
- const settingsKey = registry.getKeyForCompilationSettings(parsed.options);
104
- const read = host.readFile.bind(host);
105
- host.readFile = (path) => canonical(path) === canonical(filename) ? code : read(path);
106
- const original = host.getSourceFile.bind(host);
107
- host.getSourceFile = (path, language, onError, fresh) => {
108
- if (fresh || libraryRoot === void 0 || host.realpath === void 0 || canonical(path) === canonical(filename) || (0, import_node_path.dirname)((0, import_node_path.resolve)(path)) !== libraryRoot || !/^lib(?:\..+)?\.d\.ts$/u.test((0, import_node_path.basename)(path)) || !host.fileExists(path)) {
109
- return original(path, language, onError, fresh);
110
- }
111
- try {
112
- if (host.realpath(path) !== (0, import_node_path.resolve)(path)) return original(path, language, onError, fresh);
113
- } catch {
114
- return original(path, language, onError, fresh);
115
- }
116
- const text = read(path);
117
- if (text === void 0) return original(path, language, onError, fresh);
118
- const version = typeof language === "number" ? language : language.languageVersion;
119
- const format = typeof language === "number" ? void 0 : language.impliedNodeFormat;
120
- const key = JSON.stringify([path, settingsKey, version, format, mode]);
121
- const previous = cache.get(key);
122
- if (previous?.text === text) {
123
- counts.libraryHits += 1;
124
- return previous.source;
125
- }
126
- const size = Buffer.byteLength(text);
127
- if (previous !== void 0 || cache.size >= MAX_LIBRARY_FILES || bytes + size > MAX_LIBRARY_BYTES) {
128
- return original(path, language, onError, fresh);
129
- }
130
- const source = registry.acquireDocument(
131
- path,
132
- parsed.options,
133
- import_typescript.default.ScriptSnapshot.fromString(text),
134
- "unchanged-native-library",
135
- import_typescript.default.ScriptKind.TS,
136
- language
137
- );
138
- cache.set(key, { text, source });
139
- bytes += size;
140
- counts.libraryAcquires += 1;
141
- return source;
142
- };
143
- const program = import_typescript.default.createProgram(parsed.fileNames, parsed.options, host);
144
- counts.programs += 1;
145
- if (program.getSourceFile(filename)?.text !== code) {
146
- throw new Error("The example compiler did not load the current lint source.");
147
- }
148
- return parser.parseForESLint(code, {
149
- ...options,
150
- project: false,
151
- projectService: false,
152
- programs: [program],
153
- disallowAutomaticSingleRunInference: true
154
- });
155
- }
156
- };
157
- }
158
-
159
- // src/verify-rule-examples.ts
160
- async function verifyRuleExamples(rule, setup = {}) {
161
- const spec = rule.documentation;
162
- if (spec === void 0 || !spec.publicExamples.some((item) => item.outcome === "match") || !spec.publicExamples.some((item) => item.outcome === "no-match")) {
163
- throw new Error("rule requires documented public matching and non-matching examples");
164
- }
165
- const exampleParser = createExampleParser();
166
- try {
167
- for (const example of spec.examples) {
168
- const root = await (0, import_promises.mkdtemp)((0, import_node_path2.join)((0, import_node_os.tmpdir)(), "sarj-rule-example-"));
169
- try {
170
- for (const file of example.files) {
171
- const path = fixturePath(root, file.path);
172
- validateFixtureSyntax(example.id, file, path);
173
- await (0, import_promises.mkdir)((0, import_node_path2.dirname)(path), { recursive: true });
174
- await (0, import_promises.writeFile)(path, file.source);
175
- }
176
- for (const dependency of example.installedDependencies ?? []) {
177
- if (setup.installedDependencyRoot === void 0) throw new Error(`${example.id}: explicit installed dependency root is required`);
178
- const packageFile = fixturePath((0, import_node_path2.resolve)(setup.installedDependencyRoot), `node_modules/${dependency.module}/package.json`);
179
- const metadata = JSON.parse(await (0, import_promises.readFile)(packageFile, "utf8"));
180
- if (typeof metadata !== "object" || metadata === null || !("name" in metadata) || !("version" in metadata) || metadata.name !== dependency.module || metadata.version !== dependency.version) throw new Error(`${example.id}: installed dependency identity/version mismatch for ${dependency.module}`);
181
- const destination = fixturePath(root, `node_modules/${dependency.module}`);
182
- await (0, import_promises.mkdir)((0, import_node_path2.dirname)(destination), { recursive: true });
183
- await (0, import_promises.symlink)((0, import_node_path2.dirname)(packageFile), destination, process.platform === "win32" ? "junction" : "dir");
184
- }
185
- const tsconfig = (0, import_node_path2.join)(root, "tsconfig.json");
186
- if (!example.files.some((file) => file.path === "tsconfig.json")) {
187
- await (0, import_promises.writeFile)(tsconfig, JSON.stringify({ compilerOptions: {
188
- target: "ESNext",
189
- module: "ESNext",
190
- moduleResolution: "Bundler",
191
- strict: true,
192
- allowJs: true,
193
- jsx: "preserve",
194
- noEmit: true
195
- }, include: ["**/*"] }));
196
- }
197
- const ruleId = `@sarj/${spec.ruleId}`;
198
- const config = {
199
- files: ["**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"],
200
- languageOptions: { parser: exampleParser, parserOptions: { project: tsconfig, tsconfigRootDir: root, disallowAutomaticSingleRunInference: true } },
201
- plugins: { "@sarj": { rules: { [spec.ruleId]: rule } } },
202
- rules: { [ruleId]: ["error", ...example.ruleOptions ?? rule.meta.defaultOptions ?? rule.defaultOptions ?? []] }
203
- };
204
- await verifyExample(new import_eslint.Linter({ cwd: root }), config, example, root, ruleId, setup.formatFixedSource);
205
- } finally {
206
- parser2.clearCaches();
207
- await (0, import_promises.rm)(root, { recursive: true, force: true });
208
- }
209
- }
210
- } finally {
211
- exampleParser.clear();
212
- }
213
- return spec.examples.length;
214
- }
215
- function fixturePath(root, path) {
216
- const resolved = (0, import_node_path2.resolve)(root, path);
217
- if (!resolved.startsWith(root + import_node_path2.sep)) throw new Error(`unsafe example path: ${path}`);
218
- return resolved;
219
- }
220
- function validateFixtureSyntax(exampleId, file, path) {
221
- if (!/\.[cm]?[jt]sx?$/u.test(file.path)) return;
222
- try {
223
- parser2.parse(file.source, { filePath: path });
224
- } catch (error) {
225
- throw new Error(`${exampleId}: invalid fixture ${file.path}`, { cause: error });
226
- }
227
- }
228
- async function verifyExample(linter, config, example, root, ruleId, formatFixedSource) {
229
- const focus = example.files.find((file) => file.path === example.focusPath);
230
- if (focus === void 0) throw new Error(`${example.id}: focus file is missing`);
231
- const filename = fixturePath(root, focus.path);
232
- const messages = linter.verify(focus.source, config, { filename });
233
- if (messages.some((message) => message.fatal || message.ruleId !== ruleId)) {
234
- throw new Error(`${example.id}: invalid fixture: ${messages.map((message) => message.message).join("; ")}`);
235
- }
236
- const lines = linter.getSourceCode().lines;
237
- if (messages.some((message) => !physicalDiagnosticLocation(message, lines))) {
238
- throw new Error(`${example.id}: invalid diagnostic location`);
239
- }
240
- const locations = messages.map((message) => JSON.stringify([
241
- message.ruleId,
242
- message.messageId ?? message.message,
243
- message.line,
244
- message.column,
245
- message.endLine,
246
- message.endColumn
247
- ]));
248
- if (new Set(locations).size !== locations.length) throw new Error(`${example.id}: duplicate diagnostic`);
249
- if (messages.length !== example.expectedCount) {
250
- throw new Error(`${example.id}: expected ${example.expectedCount} findings, received ${messages.length}`);
251
- }
252
- if (example.expectedDiagnostics !== void 0) {
253
- verifyExpectedDiagnostics(example, messages, focus.source, lines);
254
- }
255
- for (const expected of example.fixedFiles ?? []) {
256
- const input = example.files.find((file) => file.path === expected.path);
257
- if (input === void 0) throw new Error(`${example.id}: fixed file has no original`);
258
- const options = { filename: fixturePath(root, input.path) };
259
- const fixed = linter.verifyAndFix(input.source, config, options);
260
- if (formatFixedSource === void 0 && fixed.output !== expected.source) throw new Error(`${example.id}: unexpected fixed source`);
261
- await (0, import_promises.writeFile)(options.filename, fixed.output);
262
- const repeated = linter.verifyAndFix(fixed.output, config, options);
263
- if (repeated.fixed || repeated.output !== fixed.output || repeated.messages.length > 0) {
264
- throw new Error(`${example.id}: fix is not clean and idempotent`);
265
- }
266
- if (formatFixedSource !== void 0 && await formatFixedSource({ path: input.path, source: fixed.output }, example) !== expected.source) {
267
- throw new Error(`${example.id}: unexpected fixed source`);
268
- }
269
- }
270
- }
271
- function verifyExpectedDiagnostics(example, messages, source, lines) {
272
- const expectations = example.expectedDiagnostics ?? [];
273
- if (expectations.some((item) => !physicalExpectedLocation(item.location, lines))) {
274
- throw new Error(`${example.id}: invalid expected diagnostic location`);
275
- }
276
- const expected = expectations.map(expectedDiagnosticKey).sort();
277
- const actual = messages.map((message) => JSON.stringify([
278
- message.messageId,
279
- message.message,
280
- [message.line, message.column, message.endLine, message.endColumn],
281
- message.fix !== void 0,
282
- (message.suggestions ?? []).map((suggestion) => [
283
- suggestion.messageId,
284
- applySuggestion(source, suggestion.fix, example.id)
285
- ])
286
- ])).sort();
287
- if (JSON.stringify(actual) !== JSON.stringify(expected)) {
288
- throw new Error(`${example.id}: unexpected diagnostics
289
- expected: ${JSON.stringify(expected)}
290
- received: ${JSON.stringify(actual)}`);
291
- }
292
- }
293
- function expectedDiagnosticKey(expected) {
294
- return JSON.stringify([
295
- expected.messageId,
296
- expected.message,
297
- [expected.location.line, expected.location.column, expected.location.end?.line, expected.location.end?.column],
298
- expected.automaticFix,
299
- expected.suggestions.map((suggestion) => [suggestion.messageId, suggestion.output])
300
- ]);
301
- }
302
- function applySuggestion(source, fix, exampleId) {
303
- const bom = source.startsWith("\uFEFF") ? "\uFEFF" : "";
304
- const text = bom === "" ? source : source.slice(1);
305
- const [start, end] = fix.range;
306
- if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start > end || start < (bom === "" ? 0 : -1) || end < 0 || end > text.length) {
307
- throw new Error(`${exampleId}: invalid suggestion edit range`);
308
- }
309
- const prefix = start < 0 && end >= 0 || start === 0 && fix.text.startsWith("\uFEFF") ? "" : bom;
310
- return prefix + text.slice(0, Math.max(0, start)) + fix.text + text.slice(end);
311
- }
312
- function physicalExpectedLocation(location, lines) {
313
- if (!physicalPosition(lines, location.line, location.column)) return false;
314
- if (location.end === void 0) return true;
315
- return physicalPosition(lines, location.end.line, location.end.column) && (location.end.line > location.line || location.end.line === location.line && location.end.column >= location.column);
316
- }
317
- function physicalDiagnosticLocation(message, lines) {
318
- if (!physicalPosition(lines, message.line, message.column)) return false;
319
- if (message.endLine === void 0 && message.endColumn === void 0) return true;
320
- if (!physicalPosition(lines, message.endLine, message.endColumn)) return false;
321
- return message.endLine !== void 0 && message.endColumn !== void 0 && (message.endLine > message.line || message.endLine === message.line && message.endColumn >= message.column);
322
- }
323
- function physicalPosition(lines, line, column) {
324
- if (line === void 0 || column === void 0 || !Number.isInteger(line) || !Number.isInteger(column)) return false;
325
- const text = lines[line - 1];
326
- return text !== void 0 && column >= 1 && column <= text.length + 1;
327
- }
328
- // Annotate the CommonJS export names for ESM import in node:
329
- 0 && (module.exports = {
330
- verifyRuleExamples
331
- });
@@ -1,15 +0,0 @@
1
- import { E as ExampleFile, R as RuleExample } from './_docs-d31-QA2S.cjs';
2
- import { DocumentedRule } from './index.cjs';
3
- import '@typescript-eslint/utils';
4
- import 'eslint';
5
- import '@eslint/core';
6
-
7
- type FixedSourceFormatter = (file: ExampleFile, example: RuleExample) => string | Promise<string>;
8
- /** Execute all authored examples, including private regressions, in isolated projects. */
9
- declare function verifyRuleExamples<Options extends readonly unknown[], MessageIds extends string>(rule: DocumentedRule<Options, MessageIds>, setup?: {
10
- readonly installedDependencyRoot?: string;
11
- readonly formatFixedSource?: FixedSourceFormatter;
12
- }): Promise<number>;
13
-
14
- export { ExampleFile, RuleExample, verifyRuleExamples };
15
- export type { FixedSourceFormatter };
@@ -1,15 +0,0 @@
1
- import { E as ExampleFile, R as RuleExample } from './_docs-d31-QA2S.js';
2
- import { DocumentedRule } from './index.js';
3
- import '@typescript-eslint/utils';
4
- import 'eslint';
5
- import '@eslint/core';
6
-
7
- type FixedSourceFormatter = (file: ExampleFile, example: RuleExample) => string | Promise<string>;
8
- /** Execute all authored examples, including private regressions, in isolated projects. */
9
- declare function verifyRuleExamples<Options extends readonly unknown[], MessageIds extends string>(rule: DocumentedRule<Options, MessageIds>, setup?: {
10
- readonly installedDependencyRoot?: string;
11
- readonly formatFixedSource?: FixedSourceFormatter;
12
- }): Promise<number>;
13
-
14
- export { ExampleFile, RuleExample, verifyRuleExamples };
15
- export type { FixedSourceFormatter };