@krizaka/config 0.3.0 → 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.
package/README.md CHANGED
@@ -48,6 +48,7 @@ the package.
48
48
  | `@krizaka/config/eslint/next` | `krizakaNext`: `eslint-config-next` (core web vitals + TypeScript, which already carry typescript-eslint, React hooks and import) + the same basics. |
49
49
  | `@krizaka/config/eslint/krizaka-ui` | `krizakaUi(options?)`: the four UI rules below. Also `krizakaUiRules` and `krizakaUiRestrictedSyntax(options?)`. |
50
50
  | `@krizaka/config/eslint/no-locale-ternary` | `noLocaleTernary(options?)`: `krizaka/no-locale-ternary` — no `locale === "fr" ? … : …` outside routing code. |
51
+ | `@krizaka/config/eslint/ui-warnings` | `krizakaUiWarnings(options?)`: the same two debts as named plugin rules, as warnings — `krizaka/no-light-variant` (no `light:`: tokens do the theming) and `krizaka/no-raw-palette` (no `bg-zinc-900`, `text-violet-400`: a role; `allow` families). `krizaka-ratchet` counts them (`light`, `palette`), so a product adopts them now and brings the counts to 0. |
51
52
  | `@krizaka/config/eslint/stack` | `krizakaStack(options?)`: the three stack rules below (warnings). Also `STACK_RULES`. |
52
53
  | `@krizaka/config/eslint/plugin` | `krizakaPlugin`: the `krizaka` plugin carrying every rule, for a hand-written config. |
53
54
  | `@krizaka/config/dependency-cruiser` | The shared dependency-cruiser config: `krizaka-no-direct-tanstack` (no `@tanstack/*` from an app). |
package/eslint/plugin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // The `krizaka` ESLint plugin: one object carrying every rule, so that configs registering it compose (flat config
2
2
  // refuses two different objects under the same plugin name).
3
- import { envName, noDirectTanstackImport, noFetchInClientEffect, noLocaleTernaryRule, noLocalTableOrForm } from "./rules.js";
3
+ import { envName, noDirectTanstackImport, noFetchInClientEffect, noLightVariant, noLocaleTernaryRule, noLocalTableOrForm, noRawPalette } from "./rules.js";
4
4
 
5
5
  /** @type {import("eslint").ESLint.Plugin} */
6
6
  export const krizakaPlugin = {
@@ -10,6 +10,8 @@ export const krizakaPlugin = {
10
10
  "no-fetch-in-client-effect": noFetchInClientEffect,
11
11
  "no-direct-tanstack-import": noDirectTanstackImport,
12
12
  "no-local-table-or-form": noLocalTableOrForm,
13
+ "no-light-variant": noLightVariant,
14
+ "no-raw-palette": noRawPalette,
13
15
  "env-name": envName,
14
16
  },
15
17
  };
package/eslint/rules.js CHANGED
@@ -3,7 +3,7 @@
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
 
6
- import { countFormFields, EFFECT_NAMES_SOURCE, FETCH_NAMES_SOURCE, TANSTACK_SCOPE } from "../patterns.js";
6
+ import { countFormFields, EFFECT_NAMES_SOURCE, FETCH_NAMES_SOURCE, LIGHT_VARIANT_SOURCE, paletteSource, TANSTACK_SCOPE } from "../patterns.js";
7
7
 
8
8
  /** @typedef {import("eslint").Rule.RuleModule} RuleModule */
9
9
  /** @typedef {import("estree").Node} Node */
@@ -264,3 +264,40 @@ export const envName = {
264
264
  };
265
265
  },
266
266
  };
267
+
268
+ /**
269
+ * A rule that reports the className strings (literals and template chunks, in `className` attributes) matching a pattern.
270
+ * @param {(options: { allow?: string[] }) => RegExp} pattern
271
+ * @param {string} message
272
+ * @returns {RuleModule}
273
+ */
274
+ function classNameRule(pattern, message) {
275
+ return {
276
+ meta: {
277
+ type: "suggestion",
278
+ docs: { description: message },
279
+ schema: [{ type: "object", properties: { allow: { type: "array", items: { type: "string" } } }, additionalProperties: false }],
280
+ messages: { found: message },
281
+ },
282
+ create(context) {
283
+ const options = /** @type {{ allow?: string[] }} */ (context.options[0] ?? {});
284
+ const regex = pattern(options);
285
+ const check = (/** @type {Node} */ node, /** @type {unknown} */ value) => {
286
+ if (typeof value === "string" && regex.test(value)) context.report({ node, messageId: "found" });
287
+ };
288
+ return {
289
+ "JSXAttribute[name.name='className'] Literal": (/** @type {any} */ node) => check(node, node.value),
290
+ "JSXAttribute[name.name='className'] TemplateElement": (/** @type {any} */ node) => check(node, node.value.cooked),
291
+ };
292
+ },
293
+ };
294
+ }
295
+
296
+ /** `krizaka/no-light-variant`: a theme is a set of tokens, never a `light:` variant (a dark island: `.theme-dark`). */
297
+ export const noLightVariant = classNameRule(() => new RegExp(LIGHT_VARIANT_SOURCE), "`light:` is forbidden in products: a theme is a set of tokens. A dark island? `.theme-dark` on the ancestor.");
298
+
299
+ /** `krizaka/no-raw-palette`: a component reads a role (`bg-surface-1`, `text-accent`), never a palette step (`bg-zinc-900`). */
300
+ export const noRawPalette = classNameRule(
301
+ ({ allow = [] }) => new RegExp(paletteSource(allow)),
302
+ "Raw palette colour: use a role (bg-surface-1, text-fg-secondary, border-border-default, text-accent, text-danger).",
303
+ );
@@ -0,0 +1,28 @@
1
+ // The two UI rules as named plugin rules, for the adoption: `krizaka/no-light-variant` (no `light:` — tokens do the
2
+ // theming) and `krizaka/no-raw-palette` (no `bg-zinc-900`, `text-violet-400`… — a role). Warnings: krizaka-ratchet counts
3
+ // the same patterns (`light`, `palette`), so a product adopts them now and brings each count to 0 by the end of its
4
+ // adoption. `krizakaUi()` stays the strict (error) set, on `no-restricted-syntax`.
5
+ import { krizakaPlugin } from "./plugin.js";
6
+
7
+ /** The rule names, as configured. */
8
+ export const UI_WARNING_RULES = Object.freeze(["krizaka/no-light-variant", "krizaka/no-raw-palette"]);
9
+
10
+ /**
11
+ * Flat config: the two named UI rules on a product's components.
12
+ * @param {{ allow?: string[], files?: string[], ignores?: string[], severity?: "warn" | "error" }} [options]
13
+ * `allow`: palette families tolerated for documented, isolated cases.
14
+ * @returns {import("eslint").Linter.Config[]}
15
+ */
16
+ export function krizakaUiWarnings({ allow = [], files = ["**/*.{jsx,tsx}"], ignores = [], severity = "warn" } = {}) {
17
+ return [
18
+ {
19
+ name: "krizaka/ui-warnings",
20
+ files,
21
+ ...(ignores.length > 0 ? { ignores } : {}),
22
+ plugins: { krizaka: krizakaPlugin },
23
+ rules: { "krizaka/no-light-variant": severity, "krizaka/no-raw-palette": [severity, { allow }] },
24
+ },
25
+ ];
26
+ }
27
+
28
+ export default krizakaUiWarnings;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krizaka/config",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "The shared configuration of every Krizaka JavaScript repository: ESLint flat configs (base, Next.js, the four UI rules, the three stack rules, no locale ternary), the dependency-cruiser boundary, tsconfig bases, Prettier, EditorConfig, and the krizaka-ratchet debt counter.",
5
5
  "keywords": [
6
6
  "krizaka",
@@ -12,7 +12,7 @@
12
12
  "dependency-cruiser",
13
13
  "tanstack"
14
14
  ],
15
- "homepage": "https://github.com/krizaka/krizaka-ui/tree/main/packages/config#readme",
15
+ "homepage": "https://www.krizaka.com/en/docs/ui",
16
16
  "bugs": "https://github.com/krizaka/krizaka-ui/issues",
17
17
  "repository": {
18
18
  "type": "git",
@@ -28,6 +28,7 @@
28
28
  "./eslint/krizaka-ui": "./eslint/krizaka-ui.js",
29
29
  "./eslint/no-locale-ternary": "./eslint/no-locale-ternary.js",
30
30
  "./eslint/stack": "./eslint/stack.js",
31
+ "./eslint/ui-warnings": "./eslint/ui-warnings.js",
31
32
  "./eslint/env-names": "./eslint/env-names.js",
32
33
  "./eslint/plugin": "./eslint/plugin.js",
33
34
  "./dependency-cruiser": "./dependency-cruiser.cjs",