@sarj/eslint-plugin 15.3.0 → 15.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.
package/dist/index.cjs CHANGED
@@ -104,6 +104,7 @@ ${missing.join("\n")}`);
104
104
  function publicExample(example) {
105
105
  return {
106
106
  id: example.id,
107
+ scenarioId: example.scenarioId ?? "primary",
107
108
  title: example.title,
108
109
  outcome: example.outcome,
109
110
  files: example.files.map(publicFile),
@@ -148,9 +149,14 @@ function nativeSpec(config, documentation) {
148
149
  const examples = [...documentation.examples ?? []];
149
150
  examples.forEach(validateExample);
150
151
  assertUnique(examples.map((example) => example.id), "rule example IDs");
151
- const publicOutcomes = new Set(examples.filter((example) => example.public === true).map((example) => example.outcome));
152
- if (publicOutcomes.size > 0 && !(publicOutcomes.has("match") && publicOutcomes.has("no-match"))) {
153
- throw new TypeError("published rule examples must include matching and non-matching cases");
152
+ const publicExamples = examples.filter((example) => example.public === true);
153
+ const scenarios = new Set(publicExamples.map((example) => example.scenarioId ?? "primary"));
154
+ for (const scenario of scenarios) {
155
+ const pair = publicExamples.filter((example) => (example.scenarioId ?? "primary") === scenario);
156
+ const outcomes = new Set(pair.map((example) => example.outcome));
157
+ if (pair.length !== 2 || !outcomes.has("match") || !outcomes.has("no-match")) {
158
+ throw new TypeError(`published example scenario ${scenario} must contain both matching and non-matching cases exactly once`);
159
+ }
154
160
  }
155
161
  const messageIds = Object.keys(meta2.messages).sort();
156
162
  const schema = optionsSchema(meta2.schema);
@@ -171,7 +177,7 @@ function nativeSpec(config, documentation) {
171
177
  references,
172
178
  since: documentation.since ?? null,
173
179
  examples,
174
- publicExamples: examples.filter((example) => example.public === true),
180
+ publicExamples,
175
181
  messageIds,
176
182
  optionsSchema: schema
177
183
  };
@@ -191,6 +197,9 @@ function validateExample(example) {
191
197
  if (!KEBAB_CASE.test(example.id)) {
192
198
  throw new TypeError("example ID must be lowercase kebab-case");
193
199
  }
200
+ if (!KEBAB_CASE.test(example.scenarioId ?? "primary")) {
201
+ throw new TypeError("example scenario must be lowercase kebab-case");
202
+ }
194
203
  if (example.title.trim().length === 0) {
195
204
  throw new TypeError("example title must not be empty");
196
205
  }
@@ -14436,7 +14445,7 @@ var rules = {
14436
14445
  };
14437
14446
  var meta = {
14438
14447
  name: "@sarj/eslint-plugin",
14439
- version: "15.3.0"
14448
+ version: "15.4.0"
14440
14449
  };
14441
14450
  var applicationOnlyRules = [
14442
14451
  "no-restricted-library-load",
package/dist/index.d.cts CHANGED
@@ -25,6 +25,7 @@ interface ExampleFile {
25
25
  /** A reviewed example. It remains private unless `public: true` is explicit. */
26
26
  interface RuleExample {
27
27
  readonly id: string;
28
+ readonly scenarioId?: string;
28
29
  readonly title: string;
29
30
  readonly outcome: ExampleOutcome;
30
31
  readonly files: readonly ExampleFile[];
@@ -414,7 +415,7 @@ type FlatPreset = {
414
415
  declare const plugin: {
415
416
  readonly meta: {
416
417
  readonly name: "@sarj/eslint-plugin";
417
- readonly version: "15.3.0";
418
+ readonly version: "15.4.0";
418
419
  };
419
420
  readonly rules: {
420
421
  readonly "duplicate-test-body": DocumentedRule<readonly [], "duplicateTestBody">;
package/dist/index.d.ts CHANGED
@@ -25,6 +25,7 @@ interface ExampleFile {
25
25
  /** A reviewed example. It remains private unless `public: true` is explicit. */
26
26
  interface RuleExample {
27
27
  readonly id: string;
28
+ readonly scenarioId?: string;
28
29
  readonly title: string;
29
30
  readonly outcome: ExampleOutcome;
30
31
  readonly files: readonly ExampleFile[];
@@ -414,7 +415,7 @@ type FlatPreset = {
414
415
  declare const plugin: {
415
416
  readonly meta: {
416
417
  readonly name: "@sarj/eslint-plugin";
417
- readonly version: "15.3.0";
418
+ readonly version: "15.4.0";
418
419
  };
419
420
  readonly rules: {
420
421
  readonly "duplicate-test-body": DocumentedRule<readonly [], "duplicateTestBody">;
package/dist/index.js CHANGED
@@ -61,6 +61,7 @@ ${missing.join("\n")}`);
61
61
  function publicExample(example) {
62
62
  return {
63
63
  id: example.id,
64
+ scenarioId: example.scenarioId ?? "primary",
64
65
  title: example.title,
65
66
  outcome: example.outcome,
66
67
  files: example.files.map(publicFile),
@@ -105,9 +106,14 @@ function nativeSpec(config, documentation) {
105
106
  const examples = [...documentation.examples ?? []];
106
107
  examples.forEach(validateExample);
107
108
  assertUnique(examples.map((example) => example.id), "rule example IDs");
108
- const publicOutcomes = new Set(examples.filter((example) => example.public === true).map((example) => example.outcome));
109
- if (publicOutcomes.size > 0 && !(publicOutcomes.has("match") && publicOutcomes.has("no-match"))) {
110
- throw new TypeError("published rule examples must include matching and non-matching cases");
109
+ const publicExamples = examples.filter((example) => example.public === true);
110
+ const scenarios = new Set(publicExamples.map((example) => example.scenarioId ?? "primary"));
111
+ for (const scenario of scenarios) {
112
+ const pair = publicExamples.filter((example) => (example.scenarioId ?? "primary") === scenario);
113
+ const outcomes = new Set(pair.map((example) => example.outcome));
114
+ if (pair.length !== 2 || !outcomes.has("match") || !outcomes.has("no-match")) {
115
+ throw new TypeError(`published example scenario ${scenario} must contain both matching and non-matching cases exactly once`);
116
+ }
111
117
  }
112
118
  const messageIds = Object.keys(meta2.messages).sort();
113
119
  const schema = optionsSchema(meta2.schema);
@@ -128,7 +134,7 @@ function nativeSpec(config, documentation) {
128
134
  references,
129
135
  since: documentation.since ?? null,
130
136
  examples,
131
- publicExamples: examples.filter((example) => example.public === true),
137
+ publicExamples,
132
138
  messageIds,
133
139
  optionsSchema: schema
134
140
  };
@@ -148,6 +154,9 @@ function validateExample(example) {
148
154
  if (!KEBAB_CASE.test(example.id)) {
149
155
  throw new TypeError("example ID must be lowercase kebab-case");
150
156
  }
157
+ if (!KEBAB_CASE.test(example.scenarioId ?? "primary")) {
158
+ throw new TypeError("example scenario must be lowercase kebab-case");
159
+ }
151
160
  if (example.title.trim().length === 0) {
152
161
  throw new TypeError("example title must not be empty");
153
162
  }
@@ -14414,7 +14423,7 @@ var rules = {
14414
14423
  };
14415
14424
  var meta = {
14416
14425
  name: "@sarj/eslint-plugin",
14417
- version: "15.3.0"
14426
+ version: "15.4.0"
14418
14427
  };
14419
14428
  var applicationOnlyRules = [
14420
14429
  "no-restricted-library-load",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarj/eslint-plugin",
3
- "version": "15.3.0",
3
+ "version": "15.4.0",
4
4
  "packageManager": "npm@11.19.0",
5
5
  "description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",