@sarj/eslint-plugin 16.36.6 → 16.38.50

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.
@@ -35,68 +35,192 @@ __export(verify_rule_examples_exports, {
35
35
  module.exports = __toCommonJS(verify_rule_examples_exports);
36
36
  var import_promises = require("fs/promises");
37
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
38
43
  var import_node_path = require("path");
39
44
  var parser = __toESM(require("@typescript-eslint/parser"), 1);
40
- var import_eslint = require("eslint");
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
41
160
  async function verifyRuleExamples(rule, setup = {}) {
42
161
  const spec = rule.documentation;
43
162
  if (spec === void 0 || !spec.publicExamples.some((item) => item.outcome === "match") || !spec.publicExamples.some((item) => item.outcome === "no-match")) {
44
163
  throw new Error("rule requires documented public matching and non-matching examples");
45
164
  }
46
- for (const example of spec.examples) {
47
- const root = await (0, import_promises.mkdtemp)((0, import_node_path.join)((0, import_node_os.tmpdir)(), "sarj-rule-example-"));
48
- try {
49
- for (const file of example.files) {
50
- const path = fixturePath(root, file.path);
51
- validateFixtureSyntax(example.id, file, path);
52
- await (0, import_promises.mkdir)((0, import_node_path.dirname)(path), { recursive: true });
53
- await (0, import_promises.writeFile)(path, file.source);
54
- }
55
- for (const dependency of example.installedDependencies ?? []) {
56
- if (setup.installedDependencyRoot === void 0) throw new Error(`${example.id}: explicit installed dependency root is required`);
57
- const packageFile = fixturePath((0, import_node_path.resolve)(setup.installedDependencyRoot), `node_modules/${dependency.module}/package.json`);
58
- const metadata = JSON.parse(await (0, import_promises.readFile)(packageFile, "utf8"));
59
- 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}`);
60
- const destination = fixturePath(root, `node_modules/${dependency.module}`);
61
- await (0, import_promises.mkdir)((0, import_node_path.dirname)(destination), { recursive: true });
62
- await (0, import_promises.symlink)((0, import_node_path.dirname)(packageFile), destination, process.platform === "win32" ? "junction" : "dir");
63
- }
64
- const tsconfig = (0, import_node_path.join)(root, "tsconfig.json");
65
- if (!example.files.some((file) => file.path === "tsconfig.json")) {
66
- await (0, import_promises.writeFile)(tsconfig, JSON.stringify({ compilerOptions: {
67
- target: "ESNext",
68
- module: "ESNext",
69
- moduleResolution: "Bundler",
70
- strict: true,
71
- allowJs: true,
72
- jsx: "preserve",
73
- noEmit: true
74
- }, include: ["**/*"] }));
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.defaultOptions ?? []] }
203
+ };
204
+ await verifyExample(new import_eslint.Linter({ cwd: root }), config, example, root, ruleId);
205
+ } finally {
206
+ parser2.clearCaches();
207
+ await (0, import_promises.rm)(root, { recursive: true, force: true });
75
208
  }
76
- const ruleId = `@sarj/${spec.ruleId}`;
77
- const config = {
78
- files: ["**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"],
79
- languageOptions: { parser, parserOptions: { project: tsconfig, tsconfigRootDir: root, disallowAutomaticSingleRunInference: true } },
80
- plugins: { "@sarj": { rules: { [spec.ruleId]: rule } } },
81
- rules: { [ruleId]: ["error", ...example.ruleOptions ?? rule.defaultOptions ?? []] }
82
- };
83
- await verifyExample(new import_eslint.Linter({ cwd: root }), config, example, root, ruleId);
84
- } finally {
85
- parser.clearCaches();
86
- await (0, import_promises.rm)(root, { recursive: true, force: true });
87
209
  }
210
+ } finally {
211
+ exampleParser.clear();
88
212
  }
89
213
  return spec.examples.length;
90
214
  }
91
215
  function fixturePath(root, path) {
92
- const resolved = (0, import_node_path.resolve)(root, path);
93
- if (!resolved.startsWith(root + import_node_path.sep)) throw new Error(`unsafe example path: ${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}`);
94
218
  return resolved;
95
219
  }
96
220
  function validateFixtureSyntax(exampleId, file, path) {
97
221
  if (!/\.[cm]?[jt]sx?$/u.test(file.path)) return;
98
222
  try {
99
- parser.parse(file.source, { filePath: path });
223
+ parser2.parse(file.source, { filePath: path });
100
224
  } catch (error) {
101
225
  throw new Error(`${exampleId}: invalid fixture ${file.path}`, { cause: error });
102
226
  }
@@ -1,68 +1,192 @@
1
1
  // src/verify-rule-examples.ts
2
2
  import { mkdtemp, mkdir, rm, writeFile, readFile, symlink } from "fs/promises";
3
3
  import { tmpdir } from "os";
4
- import { dirname, join, resolve, sep } from "path";
5
- import * as parser from "@typescript-eslint/parser";
4
+ import { dirname as dirname2, join, resolve as resolve2, sep } from "path";
5
+ import * as parser2 from "@typescript-eslint/parser";
6
6
  import { Linter } from "eslint";
7
+
8
+ // src/create-example-parser.ts
9
+ import { basename, dirname, resolve } from "path";
10
+ import * as parser from "@typescript-eslint/parser";
11
+ import ts from "typescript";
12
+ var WATCH_DEFAULTS = {
13
+ noEmit: true,
14
+ noUnusedLocals: true,
15
+ noUnusedParameters: true,
16
+ allowJs: true,
17
+ allowNonTsExtensions: true,
18
+ checkJs: true
19
+ };
20
+ var MAX_LIBRARY_FILES = 256;
21
+ var MAX_LIBRARY_BYTES = 16 * 1024 * 1024;
22
+ function createExampleParser() {
23
+ const libraryRoot = ts.sys.realpath?.(dirname(ts.getDefaultLibFilePath({})));
24
+ const cache = /* @__PURE__ */ new Map();
25
+ const registries = /* @__PURE__ */ new Map();
26
+ const counts = { programs: 0, libraryAcquires: 0, libraryHits: 0, nativeFallbacks: 0 };
27
+ let bytes = 0;
28
+ return {
29
+ meta: parser.meta,
30
+ clear() {
31
+ cache.clear();
32
+ registries.clear();
33
+ bytes = 0;
34
+ },
35
+ statistics() {
36
+ return { ...counts, libraryFiles: cache.size, libraryBytes: bytes };
37
+ },
38
+ parseForESLint(code, options) {
39
+ const fallback = () => {
40
+ counts.nativeFallbacks += 1;
41
+ return parser.parseForESLint(code, options);
42
+ };
43
+ if (!Object.hasOwn(ts, "JSDocParsingMode")) return fallback();
44
+ if (typeof options?.project !== "string" || typeof options.filePath !== "string" || options.programs != null || options.projectService || options.extraFileExtensions?.length || options.debugLevel) {
45
+ return fallback();
46
+ }
47
+ const config = resolve(options.tsconfigRootDir ?? process.cwd(), options.project);
48
+ let unrecoverable = false;
49
+ const parsed = ts.getParsedCommandLineOfConfigFile(config, WATCH_DEFAULTS, {
50
+ ...ts.sys,
51
+ onUnRecoverableConfigFileDiagnostic() {
52
+ unrecoverable = true;
53
+ }
54
+ });
55
+ if (unrecoverable || parsed === void 0 || parsed.errors.length > 0 || parsed.projectReferences?.length) {
56
+ return fallback();
57
+ }
58
+ const filename = resolve(options.filePath);
59
+ const host = ts.createCompilerHost(parsed.options, true);
60
+ const canonical = (path) => host.getCanonicalFileName(resolve(path));
61
+ if (!parsed.fileNames.some((path) => canonical(path) === canonical(filename))) return fallback();
62
+ const mode = options.jsDocParsingMode === "none" ? ts.JSDocParsingMode.ParseNone : options.jsDocParsingMode === "type-info" ? ts.JSDocParsingMode.ParseForTypeInfo : ts.JSDocParsingMode.ParseAll;
63
+ host.jsDocParsingMode = mode;
64
+ let registry = registries.get(mode);
65
+ if (registry === void 0) {
66
+ registry = ts.createDocumentRegistry(ts.sys.useCaseSensitiveFileNames, process.cwd(), mode);
67
+ registries.set(mode, registry);
68
+ }
69
+ const settingsKey = registry.getKeyForCompilationSettings(parsed.options);
70
+ const read = host.readFile.bind(host);
71
+ host.readFile = (path) => canonical(path) === canonical(filename) ? code : read(path);
72
+ const original = host.getSourceFile.bind(host);
73
+ host.getSourceFile = (path, language, onError, fresh) => {
74
+ if (fresh || libraryRoot === void 0 || host.realpath === void 0 || canonical(path) === canonical(filename) || dirname(resolve(path)) !== libraryRoot || !/^lib(?:\..+)?\.d\.ts$/u.test(basename(path)) || !host.fileExists(path)) {
75
+ return original(path, language, onError, fresh);
76
+ }
77
+ try {
78
+ if (host.realpath(path) !== resolve(path)) return original(path, language, onError, fresh);
79
+ } catch {
80
+ return original(path, language, onError, fresh);
81
+ }
82
+ const text = read(path);
83
+ if (text === void 0) return original(path, language, onError, fresh);
84
+ const version = typeof language === "number" ? language : language.languageVersion;
85
+ const format = typeof language === "number" ? void 0 : language.impliedNodeFormat;
86
+ const key = JSON.stringify([path, settingsKey, version, format, mode]);
87
+ const previous = cache.get(key);
88
+ if (previous?.text === text) {
89
+ counts.libraryHits += 1;
90
+ return previous.source;
91
+ }
92
+ const size = Buffer.byteLength(text);
93
+ if (previous !== void 0 || cache.size >= MAX_LIBRARY_FILES || bytes + size > MAX_LIBRARY_BYTES) {
94
+ return original(path, language, onError, fresh);
95
+ }
96
+ const source = registry.acquireDocument(
97
+ path,
98
+ parsed.options,
99
+ ts.ScriptSnapshot.fromString(text),
100
+ "unchanged-native-library",
101
+ ts.ScriptKind.TS,
102
+ language
103
+ );
104
+ cache.set(key, { text, source });
105
+ bytes += size;
106
+ counts.libraryAcquires += 1;
107
+ return source;
108
+ };
109
+ const program = ts.createProgram(parsed.fileNames, parsed.options, host);
110
+ counts.programs += 1;
111
+ if (program.getSourceFile(filename)?.text !== code) {
112
+ throw new Error("The example compiler did not load the current lint source.");
113
+ }
114
+ return parser.parseForESLint(code, {
115
+ ...options,
116
+ project: false,
117
+ projectService: false,
118
+ programs: [program],
119
+ disallowAutomaticSingleRunInference: true
120
+ });
121
+ }
122
+ };
123
+ }
124
+
125
+ // src/verify-rule-examples.ts
7
126
  async function verifyRuleExamples(rule, setup = {}) {
8
127
  const spec = rule.documentation;
9
128
  if (spec === void 0 || !spec.publicExamples.some((item) => item.outcome === "match") || !spec.publicExamples.some((item) => item.outcome === "no-match")) {
10
129
  throw new Error("rule requires documented public matching and non-matching examples");
11
130
  }
12
- for (const example of spec.examples) {
13
- const root = await mkdtemp(join(tmpdir(), "sarj-rule-example-"));
14
- try {
15
- for (const file of example.files) {
16
- const path = fixturePath(root, file.path);
17
- validateFixtureSyntax(example.id, file, path);
18
- await mkdir(dirname(path), { recursive: true });
19
- await writeFile(path, file.source);
20
- }
21
- for (const dependency of example.installedDependencies ?? []) {
22
- if (setup.installedDependencyRoot === void 0) throw new Error(`${example.id}: explicit installed dependency root is required`);
23
- const packageFile = fixturePath(resolve(setup.installedDependencyRoot), `node_modules/${dependency.module}/package.json`);
24
- const metadata = JSON.parse(await readFile(packageFile, "utf8"));
25
- 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}`);
26
- const destination = fixturePath(root, `node_modules/${dependency.module}`);
27
- await mkdir(dirname(destination), { recursive: true });
28
- await symlink(dirname(packageFile), destination, process.platform === "win32" ? "junction" : "dir");
29
- }
30
- const tsconfig = join(root, "tsconfig.json");
31
- if (!example.files.some((file) => file.path === "tsconfig.json")) {
32
- await writeFile(tsconfig, JSON.stringify({ compilerOptions: {
33
- target: "ESNext",
34
- module: "ESNext",
35
- moduleResolution: "Bundler",
36
- strict: true,
37
- allowJs: true,
38
- jsx: "preserve",
39
- noEmit: true
40
- }, include: ["**/*"] }));
131
+ const exampleParser = createExampleParser();
132
+ try {
133
+ for (const example of spec.examples) {
134
+ const root = await mkdtemp(join(tmpdir(), "sarj-rule-example-"));
135
+ try {
136
+ for (const file of example.files) {
137
+ const path = fixturePath(root, file.path);
138
+ validateFixtureSyntax(example.id, file, path);
139
+ await mkdir(dirname2(path), { recursive: true });
140
+ await writeFile(path, file.source);
141
+ }
142
+ for (const dependency of example.installedDependencies ?? []) {
143
+ if (setup.installedDependencyRoot === void 0) throw new Error(`${example.id}: explicit installed dependency root is required`);
144
+ const packageFile = fixturePath(resolve2(setup.installedDependencyRoot), `node_modules/${dependency.module}/package.json`);
145
+ const metadata = JSON.parse(await readFile(packageFile, "utf8"));
146
+ 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}`);
147
+ const destination = fixturePath(root, `node_modules/${dependency.module}`);
148
+ await mkdir(dirname2(destination), { recursive: true });
149
+ await symlink(dirname2(packageFile), destination, process.platform === "win32" ? "junction" : "dir");
150
+ }
151
+ const tsconfig = join(root, "tsconfig.json");
152
+ if (!example.files.some((file) => file.path === "tsconfig.json")) {
153
+ await writeFile(tsconfig, JSON.stringify({ compilerOptions: {
154
+ target: "ESNext",
155
+ module: "ESNext",
156
+ moduleResolution: "Bundler",
157
+ strict: true,
158
+ allowJs: true,
159
+ jsx: "preserve",
160
+ noEmit: true
161
+ }, include: ["**/*"] }));
162
+ }
163
+ const ruleId = `@sarj/${spec.ruleId}`;
164
+ const config = {
165
+ files: ["**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"],
166
+ languageOptions: { parser: exampleParser, parserOptions: { project: tsconfig, tsconfigRootDir: root, disallowAutomaticSingleRunInference: true } },
167
+ plugins: { "@sarj": { rules: { [spec.ruleId]: rule } } },
168
+ rules: { [ruleId]: ["error", ...example.ruleOptions ?? rule.defaultOptions ?? []] }
169
+ };
170
+ await verifyExample(new Linter({ cwd: root }), config, example, root, ruleId);
171
+ } finally {
172
+ parser2.clearCaches();
173
+ await rm(root, { recursive: true, force: true });
41
174
  }
42
- const ruleId = `@sarj/${spec.ruleId}`;
43
- const config = {
44
- files: ["**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"],
45
- languageOptions: { parser, parserOptions: { project: tsconfig, tsconfigRootDir: root, disallowAutomaticSingleRunInference: true } },
46
- plugins: { "@sarj": { rules: { [spec.ruleId]: rule } } },
47
- rules: { [ruleId]: ["error", ...example.ruleOptions ?? rule.defaultOptions ?? []] }
48
- };
49
- await verifyExample(new Linter({ cwd: root }), config, example, root, ruleId);
50
- } finally {
51
- parser.clearCaches();
52
- await rm(root, { recursive: true, force: true });
53
175
  }
176
+ } finally {
177
+ exampleParser.clear();
54
178
  }
55
179
  return spec.examples.length;
56
180
  }
57
181
  function fixturePath(root, path) {
58
- const resolved = resolve(root, path);
182
+ const resolved = resolve2(root, path);
59
183
  if (!resolved.startsWith(root + sep)) throw new Error(`unsafe example path: ${path}`);
60
184
  return resolved;
61
185
  }
62
186
  function validateFixtureSyntax(exampleId, file, path) {
63
187
  if (!/\.[cm]?[jt]sx?$/u.test(file.path)) return;
64
188
  try {
65
- parser.parse(file.source, { filePath: path });
189
+ parser2.parse(file.source, { filePath: path });
66
190
  } catch (error) {
67
191
  throw new Error(`${exampleId}: invalid fixture ${file.path}`, { cause: error });
68
192
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarj/eslint-plugin",
3
- "version": "16.36.6",
3
+ "version": "16.38.50",
4
4
  "packageManager": "npm@12.2.1",
5
5
  "description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",