@sarj/eslint-plugin 16.14.4 → 16.15.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.
@@ -48,6 +48,7 @@ async function verifyRuleExamples(rule, setup = {}) {
48
48
  try {
49
49
  for (const file of example.files) {
50
50
  const path = fixturePath(root, file.path);
51
+ validateFixtureSyntax(example.id, file, path);
51
52
  await (0, import_promises.mkdir)((0, import_node_path.dirname)(path), { recursive: true });
52
53
  await (0, import_promises.writeFile)(path, file.source);
53
54
  }
@@ -92,6 +93,14 @@ function fixturePath(root, path) {
92
93
  if (!resolved.startsWith(root + import_node_path.sep)) throw new Error(`unsafe example path: ${path}`);
93
94
  return resolved;
94
95
  }
96
+ function validateFixtureSyntax(exampleId, file, path) {
97
+ if (!/\.[cm]?[jt]sx?$/u.test(file.path)) return;
98
+ try {
99
+ parser.parse(file.source, { filePath: path });
100
+ } catch (error) {
101
+ throw new Error(`${exampleId}: invalid fixture ${file.path}`, { cause: error });
102
+ }
103
+ }
95
104
  async function verifyExample(linter, config, example, root, ruleId) {
96
105
  const focus = example.files.find((file) => file.path === example.focusPath);
97
106
  if (focus === void 0) throw new Error(`${example.id}: focus file is missing`);
@@ -100,6 +109,19 @@ async function verifyExample(linter, config, example, root, ruleId) {
100
109
  if (messages.some((message) => message.fatal || message.ruleId !== ruleId)) {
101
110
  throw new Error(`${example.id}: invalid fixture: ${messages.map((message) => message.message).join("; ")}`);
102
111
  }
112
+ const lines = linter.getSourceCode().lines;
113
+ if (messages.some((message) => !physicalDiagnosticLocation(message, lines))) {
114
+ throw new Error(`${example.id}: invalid diagnostic location`);
115
+ }
116
+ const locations = messages.map((message) => JSON.stringify([
117
+ message.ruleId,
118
+ message.messageId ?? message.message,
119
+ message.line,
120
+ message.column,
121
+ message.endLine,
122
+ message.endColumn
123
+ ]));
124
+ if (new Set(locations).size !== locations.length) throw new Error(`${example.id}: duplicate diagnostic`);
103
125
  if (messages.length !== example.expectedCount) {
104
126
  throw new Error(`${example.id}: expected ${example.expectedCount} findings, received ${messages.length}`);
105
127
  }
@@ -116,6 +138,17 @@ async function verifyExample(linter, config, example, root, ruleId) {
116
138
  }
117
139
  }
118
140
  }
141
+ function physicalDiagnosticLocation(message, lines) {
142
+ if (!physicalPosition(lines, message.line, message.column)) return false;
143
+ if (message.endLine === void 0 && message.endColumn === void 0) return true;
144
+ if (!physicalPosition(lines, message.endLine, message.endColumn)) return false;
145
+ return message.endLine !== void 0 && message.endColumn !== void 0 && (message.endLine > message.line || message.endLine === message.line && message.endColumn >= message.column);
146
+ }
147
+ function physicalPosition(lines, line, column) {
148
+ if (line === void 0 || column === void 0 || !Number.isInteger(line) || !Number.isInteger(column)) return false;
149
+ const text = lines[line - 1];
150
+ return text !== void 0 && column >= 1 && column <= text.length + 1;
151
+ }
119
152
  // Annotate the CommonJS export names for ESM import in node:
120
153
  0 && (module.exports = {
121
154
  verifyRuleExamples
@@ -14,6 +14,7 @@ async function verifyRuleExamples(rule, setup = {}) {
14
14
  try {
15
15
  for (const file of example.files) {
16
16
  const path = fixturePath(root, file.path);
17
+ validateFixtureSyntax(example.id, file, path);
17
18
  await mkdir(dirname(path), { recursive: true });
18
19
  await writeFile(path, file.source);
19
20
  }
@@ -58,6 +59,14 @@ function fixturePath(root, path) {
58
59
  if (!resolved.startsWith(root + sep)) throw new Error(`unsafe example path: ${path}`);
59
60
  return resolved;
60
61
  }
62
+ function validateFixtureSyntax(exampleId, file, path) {
63
+ if (!/\.[cm]?[jt]sx?$/u.test(file.path)) return;
64
+ try {
65
+ parser.parse(file.source, { filePath: path });
66
+ } catch (error) {
67
+ throw new Error(`${exampleId}: invalid fixture ${file.path}`, { cause: error });
68
+ }
69
+ }
61
70
  async function verifyExample(linter, config, example, root, ruleId) {
62
71
  const focus = example.files.find((file) => file.path === example.focusPath);
63
72
  if (focus === void 0) throw new Error(`${example.id}: focus file is missing`);
@@ -66,6 +75,19 @@ async function verifyExample(linter, config, example, root, ruleId) {
66
75
  if (messages.some((message) => message.fatal || message.ruleId !== ruleId)) {
67
76
  throw new Error(`${example.id}: invalid fixture: ${messages.map((message) => message.message).join("; ")}`);
68
77
  }
78
+ const lines = linter.getSourceCode().lines;
79
+ if (messages.some((message) => !physicalDiagnosticLocation(message, lines))) {
80
+ throw new Error(`${example.id}: invalid diagnostic location`);
81
+ }
82
+ const locations = messages.map((message) => JSON.stringify([
83
+ message.ruleId,
84
+ message.messageId ?? message.message,
85
+ message.line,
86
+ message.column,
87
+ message.endLine,
88
+ message.endColumn
89
+ ]));
90
+ if (new Set(locations).size !== locations.length) throw new Error(`${example.id}: duplicate diagnostic`);
69
91
  if (messages.length !== example.expectedCount) {
70
92
  throw new Error(`${example.id}: expected ${example.expectedCount} findings, received ${messages.length}`);
71
93
  }
@@ -82,6 +104,17 @@ async function verifyExample(linter, config, example, root, ruleId) {
82
104
  }
83
105
  }
84
106
  }
107
+ function physicalDiagnosticLocation(message, lines) {
108
+ if (!physicalPosition(lines, message.line, message.column)) return false;
109
+ if (message.endLine === void 0 && message.endColumn === void 0) return true;
110
+ if (!physicalPosition(lines, message.endLine, message.endColumn)) return false;
111
+ return message.endLine !== void 0 && message.endColumn !== void 0 && (message.endLine > message.line || message.endLine === message.line && message.endColumn >= message.column);
112
+ }
113
+ function physicalPosition(lines, line, column) {
114
+ if (line === void 0 || column === void 0 || !Number.isInteger(line) || !Number.isInteger(column)) return false;
115
+ const text = lines[line - 1];
116
+ return text !== void 0 && column >= 1 && column <= text.length + 1;
117
+ }
85
118
  export {
86
119
  verifyRuleExamples
87
120
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarj/eslint-plugin",
3
- "version": "16.14.4",
3
+ "version": "16.15.1",
4
4
  "packageManager": "npm@12.2.0",
5
5
  "description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",