@pragmatiks/lint 0.4.1 → 0.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## js-v0.5.0 (2026-09-27)
4
+
5
+ ### Features
6
+
7
+ - **js**: add /next frontend design rules backed by Tailwind's design system
8
+
3
9
  ## js-v0.4.1 (2026-07-30)
4
10
 
5
11
  ### Bug Fixes
package/README.md CHANGED
@@ -2,8 +2,148 @@
2
2
 
3
3
  Library-only eslint flat config and bundled semgrep rules for Pragmatiks engineering principles.
4
4
 
5
+ This package has no `bin` entry. Command-line integration belongs in `pragma-cli`.
6
+
7
+ ## Peer dependencies
8
+
9
+ **Requirements:** Node `^22.13.0 || >=24`.
10
+
11
+ **Package-wide** (the root entry and `/eslint-config/next`):
12
+
13
+ | Package | Range |
14
+ | ----------------------- | --------- |
15
+ | `eslint` | `^9.38.0` |
16
+ | `typescript-eslint` | `^8.70.1` |
17
+ | `eslint-plugin-sonarjs` | `^4.0.0` |
18
+ | `eslint-plugin-unicorn` | `^64.0.0` |
19
+
20
+ **`/eslint-config/next` only** (optional peers; install them when you use `@pragmatiks/lint/eslint-config/next`):
21
+
22
+ | Package | Range | Why |
23
+ | ----------------------- | --------- | -------------------------------------------------------------------- |
24
+ | `@tailwindcss/node` | `~4.1.17` | Compiles class strings with the project's own Tailwind design system |
25
+ | `tailwindcss` | `~4.1.17` | Version check against the project |
26
+ | `@tailwindcss/postcss` | `~4.1.17` | Version check against the project |
27
+ | `@eslint/css` | `^2.0.0` | Lints `.css` files |
28
+ | `eslint-plugin-react-x` | `^5.20.8` | Leaked conditional rendering (typed) |
29
+
30
+ The project's `tailwindcss`, its `@tailwindcss/postcss` and the `@tailwindcss/node` this package imports must be the same version, or loading the config throws. `@eslint/css-tree` is a regular dependency.
31
+
32
+ ## `@pragmatiks/lint/eslint-config`
33
+
34
+ The base config for JavaScript and TypeScript, also exported from the package root:
35
+
5
36
  ```ts
6
37
  import { PRAGMATIKS_LINT_FILES, pragmatiksConfig } from "@pragmatiks/lint";
38
+
39
+ export default pragmatiksConfig();
7
40
  ```
8
41
 
9
- This package has no `bin` entry. Command-line integration belongs in `pragma-cli`.
42
+ Every object except the global ignores, the sonarjs and unicorn recommended presets included, applies only to `PRAGMATIKS_LINT_FILES` (`**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}`), so the config composes with configs for other languages such as CSS.
43
+
44
+ ## `@pragmatiks/lint/eslint-config/next`
45
+
46
+ In the app's `eslint.config.mjs`:
47
+
48
+ ```js
49
+ import nextVitals from "eslint-config-next/core-web-vitals";
50
+ import nextTs from "eslint-config-next/typescript";
51
+ import { pragmatiksNextConfig } from "@pragmatiks/lint/eslint-config/next";
52
+
53
+ export default await pragmatiksNextConfig([...nextVitals, ...nextTs], {
54
+ rootDir: import.meta.dirname,
55
+ tailwindStylesheet: "app/globals.css",
56
+ });
57
+ ```
58
+
59
+ Run it with `eslint . --max-warnings 0`; the Next presets report some rules as warnings.
60
+
61
+ Composition, in order:
62
+
63
+ 1. The Next configs. Objects without `files` get the glob of the one object that registers `jsx-a11y`; the config throws unless there is exactly one. Ignore-only objects stay global.
64
+ 2. `pragmatiksConfig()` on `**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}`. Its typescript-eslint parser replaces Next's Babel parser on JavaScript files too.
65
+ 3. The frontend rules on the `jsx-a11y` glob.
66
+ 4. `react-x/no-leaked-conditional-rendering` on `**/*.{ts,tsx,mts}` with typed linting (`projectService`, `tsconfigRootDir: rootDir`).
67
+ 5. The CSS rules on `**/*.css` (`css/css`, tolerant parsing).
68
+
69
+ Every config object you add after the result must set `files`, for example `files: PRAGMATIKS_LINT_FILES` from `@pragmatiks/lint`. An object without `files` also applies to `.css` files, and JavaScript rules crash on them.
70
+
71
+ ### Operating notes
72
+
73
+ - **Not `--cache`-safe across stylesheet edits.** Class checks depend on the stylesheet, which ESLint's cache does not track.
74
+ - **Restart after editing a stylesheet.** The design system loads when the config is built, so persistent ESLint processes and editor servers keep the old theme until restarted.
75
+ - **DEP0205 on Node 26.** `@tailwindcss/node` 4.1.17 prints a DEP0205 deprecation warning on every run under Node 26. It is harmless.
76
+ - **Tailwind bumps.** Rerun the class-token corpus (`tests/next-class-tokens.test.ts`, AC-1) at the new version before widening the `~4.1.17` peer range. The config uses Tailwind's `__unstable__loadDesignSystem`; a load canary and the version check fail fast when it changes shape.
77
+
78
+ ### Focus patterns
79
+
80
+ - **P1**, the only class pattern: `focus-visible:outline-hidden focus-visible:ring-2`. Optional extras: one project ring color (a theme color that is not from Tailwind's default palette, `@theme inline` colors included, `ring-(--x)` or `ring-[var(--x)]`, with no `/` modifier), one `ring-offset-<n>` and one project offset color; a `ring-offset-<n>` above 0 needs the offset color, and `ring-offset-0` allows it but needs none. Every in-scope token has exactly the `focus-visible` variant and no `!`.
81
+ - **C1**, the only CSS pattern: every selector in the list ends in `:focus-visible` (an `@variant focus-visible` block counts as one), and the declarations are exactly `outline: <non-zero width> solid var(--token)` plus an optional `outline-offset`, without `!important`.
82
+
83
+ ### Rules
84
+
85
+ | Principle | Rule ID | Supported syntax | Fix | Gaps |
86
+ | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
87
+ | Form controls have an accessible name | `pragmatiks/form-control-has-label` | JSX `input`, `textarea`, `select`. Passes with a non-absent `aria-label`/`aria-labelledby`, a wrapping `<label>` with a matching `htmlFor`, a wrapping `<label>` without `htmlFor` (it labels only the first control inside it, as in HTML; controls on opposite branches of a ternary each count as the first), or a `<label htmlFor>` in the same component (the nearest enclosing function declaration or variable initialiser named with a capital letter, by its own or its variable's name, else the outermost enclosing function, so callbacks such as `.map()`, named ones included, count as their component) whose id is the same static string, the same variable (a `const`, prop, parameter or `useId()` result), or the same member expression or template literal over the same variables. `undefined`, `null`, `""` and valueless attributes count as absent. Skips spread props and input types hidden, submit, reset, button, image. Known false positives: a component defined inside another component is a separate component, so it does not pair with a label the enclosing component renders; separate callbacks over the same data (a label list and an input list mapping one array) do not pair. Known false negative: E25. | Wrap in `<label>`, pair `htmlFor`/`id`, or add `aria-label` or `aria-labelledby`. A placeholder is not a label. | E9, E16, E25 |
88
+ | Focus indicators use P1 | `pragmatiks/focus-styling` | Every string and template literal, compiled by the project's Tailwind, except values of JSX attributes that are not class attributes (`class`, `className`, `*ClassName`, `*Classes`); string keys of an object passed straight to a call (`cn({ "outline-none": x })`). Flags tokens whose selector contains `:focus`, that set `outline*`, `--tw-outline-style`, `--tw-ring-*`, `--tw-inset-ring-*`, `forced-color-adjust` or `all`, or whose shadow cannot compose the ring (`[box-shadow:none]`, `shadow-[none]`, `[--tw-shadow:none]`). Inline style objects and motion props (also behind `as`, `satisfies`, `!`, ternaries and `&&`): `outline*`, `forcedColorAdjust`, `all`, `--tw-ring-*` and `--tw-inset-ring-*` keys whatever their value, `--tw-shadow`/`--tw-inset-shadow` resets, every key in `whileFocus` except `transition` (including the `variants` entries a static `whileFocus` label or label array names; not followed: `variants` held in a variable, variants written as functions, labels inherited from a parent motion element, see E8 and E26), and `boxShadow` (vendor aliases included) on focus-styled elements. `.style.<key>` writes of the same keys. `onFocus`, `onBlur`, `onFocusCapture` and `onBlurCapture` handlers writing `.style.*`, and any handler writing `.style.boxShadow` on a focus-styled element; handlers inline, by name or wrapped in `useCallback`; a handler whose body is a single call to a function of the same module also counts that function's writes, followed transitively. | Use P1. Move decorative shadows to `shadow-*` classes. | E7, E8, E11, E20, E22, E26 |
89
+ | Stylesheet focus uses C1 | `pragmatiks/css-focus-styling` | Rules whose selector contains `:focus`, and `@variant focus`, `focus-visible` and `focus-within` blocks. Outside them: `outline*`, `forced-color-adjust`, `all`, `--tw-ring-*`, `--tw-inset-ring-*`, `--tw-shadow`/`--tw-inset-shadow` resets, `!important` `box-shadow`, and `box-shadow` (vendor aliases included) not composing both `var(--tw-ring-offset-shadow)` and `var(--tw-ring-shadow)` that is unlayered, in a layer other than `theme`/`base`/`components`, or inside `@keyframes`. | Use C1. Put decorative shadows in `@layer components`, or use `filter: drop-shadow(...)` instead. | E7, E21, E24 |
90
+ | Animate paint properties only | `pragmatiks/animated-properties`, `pragmatiks/css-animated-properties` | `transition`, `transition-property`, `animation`, `animation-name` in compiled class tokens and inline styles (vendor prefixes included); theme keyframes named by `animate-*` or by a stylesheet `animation`/`animation-name` (quoted names included); motion props `initial`, `animate`, `exit`, `while*` and every state of `variants`, also behind ternaries and `&&`; stylesheet transitions and `@keyframes`. Flags `all`, a layer without a property, layout properties, and layers that cannot be verified (`var()` only). `none`, CSS-wide keywords and custom properties pass. | Name the paint properties: `transition-[opacity,transform]`, `transition-colors`. Animate `transform`, `opacity` or `filter`. | E2, E8, E10, E11, E12, E18 |
91
+ | No `vh` | `pragmatiks/viewport-units`, `pragmatiks/css-viewport-units` | Any `vh` dimension in class tokens, inline styles and stylesheets, including `var()` fallbacks, custom property values and `@property` initial values. | `dvh`, `svh` or `lvh`, such as `h-dvh` or `min-h-svh`. | E8, E11 |
92
+ | z-index at most 50 | `pragmatiks/z-index`, `pragmatiks/css-z-index` | `z-index` in class tokens, inline styles and stylesheets. Numbers ≤ 50, `auto` and `calc(a * b)` ≤ 50 pass; a `var()` passes when its theme value, or else its fallback, passes; anything else, CSS-wide keywords included, fails. | Use the scale up to 50 or a named layer such as `z-(--layer)`. | E1, E8, E11, E23 |
93
+ | Colors come from project tokens | `pragmatiks/color-tokens`, `pragmatiks/css-color-tokens` | `literal`: arbitrary values, properties and modifiers of class tokens that set a property able to hold a color (a `var()` with a fallback is judged by its fallback), with a hex color, a named color, or a color function with a literal channel or alpha, `var()` fallbacks included (relative color passes with a `var()` origin and keyword or `var()` channels; `color-mix` passes with `var()`, `transparent`, `currentcolor` operands; strings are ignored). `defaultPalette`: any Tailwind default-palette `--color-*` named by `var()`, `theme()` or `--theme()`, fallbacks included, in class tokens, inline styles and stylesheets; custom properties defined inside `@theme` may alias the palette, and a class token that names such an `@theme inline` alias, such as `bg-danger`, passes for the palette colors the alias references (other palette colors the token emits are still flagged). | A project theme color or `bg-(--token)`. | E4, E8, E11 |
94
+ | CSS lives in stylesheets | `pragmatiks/no-embedded-style` | JSX `<style>`. | Move the CSS to a project stylesheet. | — |
95
+ | No `@apply` | `pragmatiks/css-no-apply` | `@apply` at-rules. | Write the classes in markup or plain declarations. | — |
96
+ | Stylesheets parse fully | `pragmatiks/css-parsed-fully` | Blocks with unparsed (raw) children under tolerant parsing, such as an `@utility` with declarations after a nested rule, and nested rules whose selector swallowed the declarations before them. | Wrap declarations that sit before or between nested rules in `& { … }` where they stand, so the cascade order stays the same, or move `@keyframes` into an `@theme` block of their own. | — |
97
+ | Accessibility | `jsx-a11y` recommended, `jsx-a11y/control-has-associated-label` (error, preset options plus `select` in `ignoreElements`, which `pragmatiks/form-control-has-label` checks), `jsx-a11y/no-autofocus` (`ignoreNonDOM`) | JSX. | Per rule. | — |
98
+ | No leaked conditional render | `react-x/no-leaked-conditional-rendering` | `**/*.{ts,tsx,mts}`, typed. | `count > 0 && …` or a ternary. | E14 |
99
+ | Copy and platform | `no-restricted-syntax` | `...` after a letter in JSX text, strings and templates (not in `href`/`src`/`action` or import/export sources); `onPaste` calling `preventDefault`; `userScalable: false`, `maximumScale: 1` (quoted keys included); `<link href>` to Google Fonts; "lorem ipsum" and "John Doe" in JSX text. | The `…` glyph; allow paste and zoom; `next/font`; real copy. | — |
100
+ | React 19 and one icon family | `no-restricted-imports`, `no-restricted-properties` | `forwardRef` from `react`, `React.forwardRef`; imports from `lucide-react`, `react-icons`, `@heroicons/*`, `@tabler/icons-react`, `@radix-ui/react-icons`, `react-feather`, `@fortawesome/*`, `@mui/icons-material`. | Pass `ref` as a prop; use `@phosphor-icons/react`, the one icon family. | — |
101
+ | No blocking dialogs | `no-alert` | `alert`, `confirm`, `prompt`. | An in-app dialog or toast. | — |
102
+ | File names and `undefined` | `unicorn/filename-case` (kebab, Pascal, camel), `unicorn/no-useless-undefined` (`checkArguments: false`) | Files on the `jsx-a11y` glob. | Rename; `undefined` arguments are allowed. | E13 |
103
+
104
+ ### Exclusions
105
+
106
+ What the rules deliberately do not enforce. Reviewers cite these rows by number ("lint exclusion E7").
107
+
108
+ | # | Not enforced | Reason |
109
+ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
110
+ | E1 | z-index scale for values ≤ 50 | Local stacking contexts |
111
+ | E2 | Paint-property transitions | Normal state feedback |
112
+ | E3 | Size transitions (only if chosen in Q4) | Not applicable: Q4 chose to enforce |
113
+ | E4 | Color and token literals in inline styles and CSS; non-color arbitrary tokens | PRA-526 |
114
+ | E5 / E6 | Leaked render / CSS (only if Q2c / Q3c) | Not applicable: Q2a and Q3a were chosen |
115
+ | E7 | Focus-indicator contrast; per-site clipping or obscuring | Browser proof is per pattern; reviewers check sites |
116
+ | E8 | Style values held in variables or returned by calls | Needs data flow |
117
+ | E9 | Form controls with spread props | Labelling is delegated to the caller |
118
+ | E10 | Motion values not written inline | No reliable signature |
119
+ | E11 | Class tokens next to `${}` | Fragments are ambiguous |
120
+ | E12 | Custom properties in transition lists | The consumer isn't traced |
121
+ | E13 | Frontend rules on `.cjs` | The Next glob excludes it |
122
+ | E14 | Leaked render outside `ts/tsx/mts` | Web's tsconfig scope |
123
+ | E16 | Label values other than static-absent expressions | Treated as labelled |
124
+ | E18 | JS animation names defined nowhere | Checked where the keyframes are defined |
125
+ | E19 | `--cache` runs, and persistent ESLint processes or editors after a stylesheet edit | The design system loads when the config is built |
126
+ | E20 | Focus appearance driven by React state; imperative styling other than `.style.<prop>`; handlers imported from other modules; handler wrappers other than `useCallback` | Not resolvable statically; removing the native indicator is still flagged |
127
+ | E21 | Package CSS in `node_modules` | Not project source. The known @xyflow/react node and edge `outline:none` is neutralised by a C1 override in the app. |
128
+ | E22 | Class-helper object keys written as identifiers (`cn({ hidden: x })`) and class objects not passed straight to a call | An identifier key cannot be told apart from an ordinary property name |
129
+ | E23 | z-index `var()` that the theme does not define and that has no fallback | The value is set outside the theme, where the rule cannot read it |
130
+ | E24 | `@custom-variant` aliases that resolve to a focus selector | Resolving custom variants needs the design system's variant graph, not the stylesheet alone |
131
+ | E25 | Form-control components nested in a factory function and wrapped in a call such as `forwardRef` or `memo`, written as object property values, or written as classes | Only function declarations and variable initialisers are component boundaries, so these components pair with each other's labels |
132
+ | E26 | `whileFocus` labels an element inherits from a parent motion element, and `variants` entries written as functions | Label propagation and variant resolution happen at runtime |
133
+
134
+ ### Known false positives
135
+
136
+ Fix these by rewriting the code:
137
+
138
+ - In-scope styling outside P1/C1: decorative rings, `!important` shadows, split patterns, and decorative `box-shadow` in unlayered CSS or `@keyframes`.
139
+ - Class-like words in prose strings.
140
+ - `a...b` in URLs outside `href`/`src`/`action`.
141
+ - Decorative `box-shadow` in a stylesheet that its importer layers (`@import "./x.css" layer(components)`): layers are judged per file, so it counts as unlayered.
142
+ - Controls paired with a design-system label component (`<Label htmlFor>`): only a lowercase `<label>` pairs with a control. Use `<label>` or `aria-labelledby`.
143
+
144
+ ## Development
145
+
146
+ | Command | Purpose |
147
+ | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
148
+ | `task js:check` / `task js:test` / `task js:build` | Lint and type check / tests (Node `>=22.15` for `module.registerHooks`; `mise.toml` pins Node 24) / build |
149
+ | `task js:pack` | Install, build and `npm pack` the committed HEAD with CI's toolchain (Node 22, pnpm 9, latest npm) in a throwaway worktree, printing all three versions; the tarball lands in `js/` |
@@ -1,8 +1,36 @@
1
1
  import { Config } from 'eslint/config';
2
2
 
3
+ /**
4
+ * The files every Pragmatiks config object applies to: JavaScript and TypeScript, JSX included.
5
+ */
3
6
  declare const PRAGMATIKS_LINT_FILES: string[];
7
+ /**
8
+ * Finds the nearest directory at or above `start` that contains a `package.json`.
9
+ *
10
+ * @param start - The directory to start from.
11
+ * @returns The first directory, walking up, that contains a `package.json`.
12
+ * @throws Error when no directory up to the filesystem root contains one.
13
+ */
4
14
  declare function findPackageRoot(start: string): string;
15
+ /**
16
+ * Locates the Semgrep rules shipped with this package.
17
+ *
18
+ * @returns The absolute path of the package's `src/semgrep-rules` directory.
19
+ * @throws Error when the package root cannot be found above this module.
20
+ */
5
21
  declare function semgrepRulesDirectory(): string;
22
+ /**
23
+ * Builds the Pragmatiks base flat config for JavaScript and TypeScript files.
24
+ *
25
+ * Every object except the global ignores, the sonarjs and unicorn recommended presets included,
26
+ * applies only to `PRAGMATIKS_LINT_FILES`, so the config composes with configs for other
27
+ * languages such as CSS.
28
+ *
29
+ * @returns The flat config: the typescript-eslint parser, the sonarjs and unicorn recommended
30
+ * presets, the Pragmatiks size and naming rules, and global ignores for build output.
31
+ * @throws Error when the package root cannot be found, or when sonarjs or unicorn has no
32
+ * recommended flat config.
33
+ */
6
34
  declare function pragmatiksConfig(): Config[];
7
35
 
8
36
  export { PRAGMATIKS_LINT_FILES, pragmatiksConfig as default, findPackageRoot, pragmatiksConfig, semgrepRulesDirectory };
@@ -49,7 +49,7 @@ var PREVENT_ABBREVIATIONS_OPTS = {
49
49
  };
50
50
 
51
51
  // src/eslint-config/index.ts
52
- var PRAGMATIKS_LINT_FILES = ["**/*.{ts,tsx,mts,cts,js,mjs,cjs}"];
52
+ var PRAGMATIKS_LINT_FILES = ["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"];
53
53
  function findPackageRoot(start) {
54
54
  let current = start;
55
55
  for (; ; ) {
@@ -67,14 +67,14 @@ function semgrepRulesDirectory() {
67
67
  const here = path.dirname(fileURLToPath(import.meta.url));
68
68
  return path.join(findPackageRoot(here), "src", "semgrep-rules");
69
69
  }
70
- function recommendedOrThrow(plugin, name) {
70
+ function recommendedOrThrow(plugin, peer) {
71
71
  const recommended = plugin.configs?.recommended;
72
72
  if (!recommended) {
73
73
  throw new Error(
74
- `${name} has no recommended flat-config preset. Peer dependency major mismatch.`
74
+ `@pragmatiks/lint: ${peer.name} has no recommended flat-config preset (peer dependency major mismatch). To fix it, install ${peer.name} ${peer.range}.`
75
75
  );
76
76
  }
77
- return recommended;
77
+ return { ...recommended, files: PRAGMATIKS_LINT_FILES };
78
78
  }
79
79
  function pragmatiksConfig() {
80
80
  return defineConfig([
@@ -93,8 +93,14 @@ function pragmatiksConfig() {
93
93
  }
94
94
  }
95
95
  },
96
- recommendedOrThrow(sonarjs, "eslint-plugin-sonarjs"),
97
- recommendedOrThrow(unicorn, "eslint-plugin-unicorn"),
96
+ recommendedOrThrow(sonarjs, {
97
+ name: "eslint-plugin-sonarjs",
98
+ range: "^4.0.0"
99
+ }),
100
+ recommendedOrThrow(unicorn, {
101
+ name: "eslint-plugin-unicorn",
102
+ range: "^64.0.0"
103
+ }),
98
104
  {
99
105
  files: PRAGMATIKS_LINT_FILES,
100
106
  rules: {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/eslint-config/options.ts","../../src/eslint-config/index.ts"],"names":[],"mappings":";;;;;;;;;;;AAAO,IAAM,eAAA,GAAkB,EAAA;AAExB,IAAM,2BAAA,GAA8B;AAAA,EACzC,GAAA,EAAK,EAAA;AAAA,EACL,cAAA,EAAgB,IAAA;AAAA,EAChB,YAAA,EAAc;AAChB,CAAA;AAEO,IAAM,mBAAA,GAAsB,EAAA;AAE5B,IAAM,cAAA,GAAiB,CAAA;AAEvB,IAAM,eAAA,GAAkB,CAAA;AAExB,IAAM,0BAAA,GAA6B;AAAA,EACxC,SAAA,EAAW;AAAA,IACT,EAAA,EAAI,IAAA;AAAA,IACJ,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,EAAA,EAAI,IAAA;AAAA,IACJ,EAAA,EAAI,IAAA;AAAA,IACJ,EAAA,EAAI,IAAA;AAAA,IACJ,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,IAAA,EAAM,IAAA;AAAA,IACN,IAAA,EAAM,IAAA;AAAA,IACN,IAAA,EAAM,IAAA;AAAA,IACN,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,EAAA,EAAI,IAAA;AAAA,IACJ,KAAA,EAAO,IAAA;AAAA,IACP,GAAA,EAAK,IAAA;AAAA,IACL,CAAA,EAAG;AAAA;AAEP,CAAA;;;ACxBO,IAAM,qBAAA,GAAwB,CAAC,kCAAkC;AAEjE,SAAS,gBAAgB,KAAA,EAAuB;AACrD,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,WAAS;AACP,IAAA,IAAI,WAAW,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,cAAc,CAAC,CAAA,EAAG;AAClD,MAAA,OAAO,OAAA;AAAA,IACT;AACA,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,OAAO,CAAA;AACnC,IAAA,IAAI,WAAW,OAAA,EAAS;AACtB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,6BAAA,EAAgC,KAAK,CAAA,CAAE,CAAA;AAAA,IACzD;AACA,IAAA,OAAA,GAAU,MAAA;AAAA,EACZ;AACF;AAEO,SAAS,qBAAA,GAAgC;AAC9C,EAAA,MAAM,OAAO,IAAA,CAAK,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AACxD,EAAA,OAAO,KAAK,IAAA,CAAK,eAAA,CAAgB,IAAI,CAAA,EAAG,OAAO,eAAe,CAAA;AAChE;AAEA,SAAS,kBAAA,CACP,QACA,IAAA,EACQ;AACR,EAAA,MAAM,WAAA,GAAc,OAAO,OAAA,EAAS,WAAA;AACpC,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,GAAG,IAAI,CAAA,uEAAA;AAAA,KACT;AAAA,EACF;AACA,EAAA,OAAO,WAAA;AACT;AAEO,SAAS,gBAAA,GAAmB;AACjC,EAAA,OAAO,YAAA,CAAa;AAAA,IAClB;AAAA,MACE,KAAA,EAAO,qBAAA;AAAA,MACP,eAAA,EAAiB;AAAA,QACf,QAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,aAAA,EAAe;AAAA,UACb,WAAA,EAAa,QAAA;AAAA,UACb,UAAA,EAAY;AAAA;AACd,OACF;AAAA,MACA,QAAA,EAAU;AAAA,QACR,cAAA,EAAgB;AAAA,UACd,uBAAuB,qBAAA;AAAsB;AAC/C;AACF,KACF;AAAA,IACA,kBAAA,CAAmB,SAAS,uBAAuB,CAAA;AAAA,IACnD,kBAAA,CAAmB,SAAS,uBAAuB,CAAA;AAAA,IACnD;AAAA,MACE,KAAA,EAAO,qBAAA;AAAA,MACP,KAAA,EAAO;AAAA,QACL,UAAA,EAAY,CAAC,OAAA,EAAS,eAAe,CAAA;AAAA,QACrC,wBAAA,EAA0B,CAAC,OAAA,EAAS,2BAA2B,CAAA;AAAA,QAC/D,gBAAA,EAAkB,CAAC,OAAA,EAAS,mBAAmB,CAAA;AAAA,QAC/C,WAAA,EAAa,CAAC,OAAA,EAAS,cAAc,CAAA;AAAA,QACrC,YAAA,EAAc,CAAC,OAAA,EAAS,eAAe,CAAA;AAAA,QACvC,+BAAA,EAAiC,CAAC,OAAA,EAAS,0BAA0B;AAAA;AACvE,KACF;AAAA,IACA,cAAc,CAAC,SAAA,EAAW,iBAAA,EAAmB,UAAA,EAAY,WAAW,CAAC;AAAA,GACtE,CAAA;AACH;AAEA,IAAO,qBAAA,GAAQ","file":"index.js","sourcesContent":["export const COMPLEXITY_OPTS = 10;\n\nexport const MAX_LINES_PER_FUNCTION_OPTS = {\n max: 50,\n skipBlankLines: true,\n skipComments: true,\n};\n\nexport const MAX_STATEMENTS_OPTS = 15;\n\nexport const MAX_DEPTH_OPTS = 4;\n\nexport const MAX_PARAMS_OPTS = 4;\n\nexport const PREVENT_ABBREVIATIONS_OPTS = {\n allowList: {\n id: true,\n url: true,\n uri: true,\n api: true,\n cli: true,\n sdk: true,\n os: true,\n io: true,\n ip: true,\n tls: true,\n ssl: true,\n jwt: true,\n json: true,\n yaml: true,\n html: true,\n css: true,\n dom: true,\n ast: true,\n gpu: true,\n cpu: true,\n ram: true,\n vm: true,\n props: true,\n ref: true,\n e: true,\n },\n};\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { type Config, defineConfig, globalIgnores } from \"eslint/config\";\nimport sonarjs from \"eslint-plugin-sonarjs\";\nimport unicorn from \"eslint-plugin-unicorn\";\nimport tseslint from \"typescript-eslint\";\n\nimport {\n COMPLEXITY_OPTS,\n MAX_DEPTH_OPTS,\n MAX_LINES_PER_FUNCTION_OPTS,\n MAX_PARAMS_OPTS,\n MAX_STATEMENTS_OPTS,\n PREVENT_ABBREVIATIONS_OPTS,\n} from \"./options.js\";\n\nexport const PRAGMATIKS_LINT_FILES = [\"**/*.{ts,tsx,mts,cts,js,mjs,cjs}\"];\n\nexport function findPackageRoot(start: string): string {\n let current = start;\n for (;;) {\n if (existsSync(path.join(current, \"package.json\"))) {\n return current;\n }\n const parent = path.dirname(current);\n if (parent === current) {\n throw new Error(`package.json not found above ${start}`);\n }\n current = parent;\n }\n}\n\nexport function semgrepRulesDirectory(): string {\n const here = path.dirname(fileURLToPath(import.meta.url));\n return path.join(findPackageRoot(here), \"src\", \"semgrep-rules\");\n}\n\nfunction recommendedOrThrow(\n plugin: { configs?: Record<string, unknown> },\n name: string,\n): Config {\n const recommended = plugin.configs?.recommended;\n if (!recommended) {\n throw new Error(\n `${name} has no recommended flat-config preset. Peer dependency major mismatch.`,\n );\n }\n return recommended as Config;\n}\n\nexport function pragmatiksConfig() {\n return defineConfig([\n {\n files: PRAGMATIKS_LINT_FILES,\n languageOptions: {\n parser: tseslint.parser,\n parserOptions: {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n },\n },\n settings: {\n pragmatiksLint: {\n semgrepRulesDirectory: semgrepRulesDirectory(),\n },\n },\n },\n recommendedOrThrow(sonarjs, \"eslint-plugin-sonarjs\"),\n recommendedOrThrow(unicorn, \"eslint-plugin-unicorn\"),\n {\n files: PRAGMATIKS_LINT_FILES,\n rules: {\n complexity: [\"error\", COMPLEXITY_OPTS],\n \"max-lines-per-function\": [\"error\", MAX_LINES_PER_FUNCTION_OPTS],\n \"max-statements\": [\"error\", MAX_STATEMENTS_OPTS],\n \"max-depth\": [\"error\", MAX_DEPTH_OPTS],\n \"max-params\": [\"error\", MAX_PARAMS_OPTS],\n \"unicorn/prevent-abbreviations\": [\"error\", PREVENT_ABBREVIATIONS_OPTS],\n },\n },\n globalIgnores([\"dist/**\", \"node_modules/**\", \".next/**\", \".turbo/**\"]),\n ]);\n}\n\nexport default pragmatiksConfig;\n"]}
1
+ {"version":3,"sources":["../../src/eslint-config/options.ts","../../src/eslint-config/index.ts"],"names":[],"mappings":";;;;;;;;;;;AAAO,IAAM,eAAA,GAAkB,EAAA;AAExB,IAAM,2BAAA,GAA8B;AAAA,EACzC,GAAA,EAAK,EAAA;AAAA,EACL,cAAA,EAAgB,IAAA;AAAA,EAChB,YAAA,EAAc;AAChB,CAAA;AAEO,IAAM,mBAAA,GAAsB,EAAA;AAE5B,IAAM,cAAA,GAAiB,CAAA;AAEvB,IAAM,eAAA,GAAkB,CAAA;AAExB,IAAM,0BAAA,GAA6B;AAAA,EACxC,SAAA,EAAW;AAAA,IACT,EAAA,EAAI,IAAA;AAAA,IACJ,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,EAAA,EAAI,IAAA;AAAA,IACJ,EAAA,EAAI,IAAA;AAAA,IACJ,EAAA,EAAI,IAAA;AAAA,IACJ,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,IAAA,EAAM,IAAA;AAAA,IACN,IAAA,EAAM,IAAA;AAAA,IACN,IAAA,EAAM,IAAA;AAAA,IACN,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,GAAA,EAAK,IAAA;AAAA,IACL,EAAA,EAAI,IAAA;AAAA,IACJ,KAAA,EAAO,IAAA;AAAA,IACP,GAAA,EAAK,IAAA;AAAA,IACL,CAAA,EAAG;AAAA;AAEP,CAAA;;;ACrBO,IAAM,qBAAA,GAAwB,CAAC,sCAAsC;AASrE,SAAS,gBAAgB,KAAA,EAAuB;AACrD,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,WAAS;AACP,IAAA,IAAI,WAAW,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,cAAc,CAAC,CAAA,EAAG;AAClD,MAAA,OAAO,OAAA;AAAA,IACT;AACA,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,OAAO,CAAA;AACnC,IAAA,IAAI,WAAW,OAAA,EAAS;AACtB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,6BAAA,EAAgC,KAAK,CAAA,CAAE,CAAA;AAAA,IACzD;AACA,IAAA,OAAA,GAAU,MAAA;AAAA,EACZ;AACF;AAQO,SAAS,qBAAA,GAAgC;AAC9C,EAAA,MAAM,OAAO,IAAA,CAAK,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AACxD,EAAA,OAAO,KAAK,IAAA,CAAK,eAAA,CAAgB,IAAI,CAAA,EAAG,OAAO,eAAe,CAAA;AAChE;AAYA,SAAS,kBAAA,CACP,QACA,IAAA,EACQ;AACR,EAAA,MAAM,WAAA,GAAc,OAAO,OAAA,EAAS,WAAA;AACpC,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,kBAAA,EAAqB,KAAK,IAAI,CAAA,4FAAA,EAA+F,KAAK,IAAI,CAAA,CAAA,EAAI,KAAK,KAAK,CAAA,CAAA;AAAA,KACtJ;AAAA,EACF;AACA,EAAA,OAAO,EAAE,GAAI,WAAA,EAAwB,KAAA,EAAO,qBAAA,EAAsB;AACpE;AAcO,SAAS,gBAAA,GAAmB;AACjC,EAAA,OAAO,YAAA,CAAa;AAAA,IAClB;AAAA,MACE,KAAA,EAAO,qBAAA;AAAA,MACP,eAAA,EAAiB;AAAA,QACf,QAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,aAAA,EAAe;AAAA,UACb,WAAA,EAAa,QAAA;AAAA,UACb,UAAA,EAAY;AAAA;AACd,OACF;AAAA,MACA,QAAA,EAAU;AAAA,QACR,cAAA,EAAgB;AAAA,UACd,uBAAuB,qBAAA;AAAsB;AAC/C;AACF,KACF;AAAA,IACA,mBAAmB,OAAA,EAAS;AAAA,MAC1B,IAAA,EAAM,uBAAA;AAAA,MACN,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,IACD,mBAAmB,OAAA,EAAS;AAAA,MAC1B,IAAA,EAAM,uBAAA;AAAA,MACN,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,IACD;AAAA,MACE,KAAA,EAAO,qBAAA;AAAA,MACP,KAAA,EAAO;AAAA,QACL,UAAA,EAAY,CAAC,OAAA,EAAS,eAAe,CAAA;AAAA,QACrC,wBAAA,EAA0B,CAAC,OAAA,EAAS,2BAA2B,CAAA;AAAA,QAC/D,gBAAA,EAAkB,CAAC,OAAA,EAAS,mBAAmB,CAAA;AAAA,QAC/C,WAAA,EAAa,CAAC,OAAA,EAAS,cAAc,CAAA;AAAA,QACrC,YAAA,EAAc,CAAC,OAAA,EAAS,eAAe,CAAA;AAAA,QACvC,+BAAA,EAAiC,CAAC,OAAA,EAAS,0BAA0B;AAAA;AACvE,KACF;AAAA,IACA,cAAc,CAAC,SAAA,EAAW,iBAAA,EAAmB,UAAA,EAAY,WAAW,CAAC;AAAA,GACtE,CAAA;AACH;AAEA,IAAO,qBAAA,GAAQ","file":"index.js","sourcesContent":["export const COMPLEXITY_OPTS = 10;\n\nexport const MAX_LINES_PER_FUNCTION_OPTS = {\n max: 50,\n skipBlankLines: true,\n skipComments: true,\n};\n\nexport const MAX_STATEMENTS_OPTS = 15;\n\nexport const MAX_DEPTH_OPTS = 4;\n\nexport const MAX_PARAMS_OPTS = 4;\n\nexport const PREVENT_ABBREVIATIONS_OPTS = {\n allowList: {\n id: true,\n url: true,\n uri: true,\n api: true,\n cli: true,\n sdk: true,\n os: true,\n io: true,\n ip: true,\n tls: true,\n ssl: true,\n jwt: true,\n json: true,\n yaml: true,\n html: true,\n css: true,\n dom: true,\n ast: true,\n gpu: true,\n cpu: true,\n ram: true,\n vm: true,\n props: true,\n ref: true,\n e: true,\n },\n};\n\n/**\n * `unicorn/filename-case` options for Next apps: kebab-case modules, PascalCase components and\n * camelCase hooks and utilities are all allowed.\n */\nexport const FILENAME_CASE_OPTIONS = {\n cases: {\n kebabCase: true,\n pascalCase: true,\n camelCase: true,\n },\n};\n\n/**\n * `unicorn/no-useless-undefined` options for React: explicit resets such as\n * `setState(undefined)` are meaningful arguments, so arguments are not checked.\n */\nexport const NO_USELESS_UNDEFINED_OPTIONS = { checkArguments: false };\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { type Config, defineConfig, globalIgnores } from \"eslint/config\";\nimport sonarjs from \"eslint-plugin-sonarjs\";\nimport unicorn from \"eslint-plugin-unicorn\";\nimport tseslint from \"typescript-eslint\";\n\nimport {\n COMPLEXITY_OPTS,\n MAX_DEPTH_OPTS,\n MAX_LINES_PER_FUNCTION_OPTS,\n MAX_PARAMS_OPTS,\n MAX_STATEMENTS_OPTS,\n PREVENT_ABBREVIATIONS_OPTS,\n} from \"./options.js\";\n\n/**\n * The files every Pragmatiks config object applies to: JavaScript and TypeScript, JSX included.\n */\nexport const PRAGMATIKS_LINT_FILES = [\"**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}\"];\n\n/**\n * Finds the nearest directory at or above `start` that contains a `package.json`.\n *\n * @param start - The directory to start from.\n * @returns The first directory, walking up, that contains a `package.json`.\n * @throws Error when no directory up to the filesystem root contains one.\n */\nexport function findPackageRoot(start: string): string {\n let current = start;\n for (;;) {\n if (existsSync(path.join(current, \"package.json\"))) {\n return current;\n }\n const parent = path.dirname(current);\n if (parent === current) {\n throw new Error(`package.json not found above ${start}`);\n }\n current = parent;\n }\n}\n\n/**\n * Locates the Semgrep rules shipped with this package.\n *\n * @returns The absolute path of the package's `src/semgrep-rules` directory.\n * @throws Error when the package root cannot be found above this module.\n */\nexport function semgrepRulesDirectory(): string {\n const here = path.dirname(fileURLToPath(import.meta.url));\n return path.join(findPackageRoot(here), \"src\", \"semgrep-rules\");\n}\n\n/**\n * Reads a plugin's recommended flat config and scopes it to `PRAGMATIKS_LINT_FILES`.\n *\n * @param plugin - An ESLint plugin that ships flat configs.\n * @param peer - The plugin's package name and the peer range this package supports, used in the\n * error message.\n * @returns The recommended config with its `files` replaced by `PRAGMATIKS_LINT_FILES`, so it never\n * applies to CSS or other languages composed alongside it.\n * @throws Error when the plugin has no `recommended` flat config, which means an unsupported major.\n */\nfunction recommendedOrThrow(\n plugin: { configs?: Record<string, unknown> },\n peer: { name: string; range: string },\n): Config {\n const recommended = plugin.configs?.recommended;\n if (!recommended) {\n throw new Error(\n `@pragmatiks/lint: ${peer.name} has no recommended flat-config preset (peer dependency major mismatch). To fix it, install ${peer.name} ${peer.range}.`,\n );\n }\n return { ...(recommended as Config), files: PRAGMATIKS_LINT_FILES };\n}\n\n/**\n * Builds the Pragmatiks base flat config for JavaScript and TypeScript files.\n *\n * Every object except the global ignores, the sonarjs and unicorn recommended presets included,\n * applies only to `PRAGMATIKS_LINT_FILES`, so the config composes with configs for other\n * languages such as CSS.\n *\n * @returns The flat config: the typescript-eslint parser, the sonarjs and unicorn recommended\n * presets, the Pragmatiks size and naming rules, and global ignores for build output.\n * @throws Error when the package root cannot be found, or when sonarjs or unicorn has no\n * recommended flat config.\n */\nexport function pragmatiksConfig() {\n return defineConfig([\n {\n files: PRAGMATIKS_LINT_FILES,\n languageOptions: {\n parser: tseslint.parser,\n parserOptions: {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n },\n },\n settings: {\n pragmatiksLint: {\n semgrepRulesDirectory: semgrepRulesDirectory(),\n },\n },\n },\n recommendedOrThrow(sonarjs, {\n name: \"eslint-plugin-sonarjs\",\n range: \"^4.0.0\",\n }),\n recommendedOrThrow(unicorn, {\n name: \"eslint-plugin-unicorn\",\n range: \"^64.0.0\",\n }),\n {\n files: PRAGMATIKS_LINT_FILES,\n rules: {\n complexity: [\"error\", COMPLEXITY_OPTS],\n \"max-lines-per-function\": [\"error\", MAX_LINES_PER_FUNCTION_OPTS],\n \"max-statements\": [\"error\", MAX_STATEMENTS_OPTS],\n \"max-depth\": [\"error\", MAX_DEPTH_OPTS],\n \"max-params\": [\"error\", MAX_PARAMS_OPTS],\n \"unicorn/prevent-abbreviations\": [\"error\", PREVENT_ABBREVIATIONS_OPTS],\n },\n },\n globalIgnores([\"dist/**\", \"node_modules/**\", \".next/**\", \".turbo/**\"]),\n ]);\n}\n\nexport default pragmatiksConfig;\n"]}
@@ -0,0 +1,44 @@
1
+ import { Linter } from 'eslint';
2
+
3
+ /**
4
+ * Where the project's Tailwind entry stylesheet lives.
5
+ */
6
+ interface DesignSystemSource {
7
+ /** Absolute path of the project root. */
8
+ readonly rootDir: string;
9
+ /** Path of the Tailwind entry stylesheet relative to `rootDir`. */
10
+ readonly tailwindStylesheet: string;
11
+ }
12
+
13
+ /**
14
+ * Where the Next app lives and which stylesheet is its Tailwind entry point.
15
+ */
16
+ interface PragmatiksNextOptions extends DesignSystemSource {
17
+ /** Absolute path of the Next app, usually `import.meta.dirname` of its `eslint.config.mjs`. */
18
+ readonly rootDir: string;
19
+ /** Path of the Tailwind entry stylesheet relative to `rootDir`, such as `app/globals.css`. */
20
+ readonly tailwindStylesheet: string;
21
+ }
22
+ /**
23
+ * Builds the flat config for a Next app: the Next presets, the Pragmatiks base config, the
24
+ * frontend rules for JavaScript and TypeScript, a typed leaked-render check, and the CSS rules.
25
+ *
26
+ * Next config objects without `files` are scoped to the glob of the one object that registers
27
+ * `jsx-a11y`; ignore-only objects are kept as they are. The base config's typescript-eslint parser
28
+ * replaces Next's Babel parser on JavaScript files. The project's Tailwind design system is loaded
29
+ * when the config is built, so a stylesheet edit needs a restart of long-lived ESLint processes.
30
+ *
31
+ * Note: requires the optional peers `@eslint/css`, `eslint-plugin-react-x`, `tailwindcss`,
32
+ * `@tailwindcss/postcss` and `@tailwindcss/node`, with `tailwindcss` and `@tailwindcss/postcss`
33
+ * resolvable from `rootDir`.
34
+ *
35
+ * @param nextConfigs - The `eslint-config-next` presets, such as `[...nextVitals, ...nextTs]`.
36
+ * @param options - The app root and its Tailwind entry stylesheet.
37
+ * @returns The composed flat config.
38
+ * @throws Error when the presets do not register `jsx-a11y` exactly once or lack its flat
39
+ * recommended preset, when a peer or Tailwind package is missing or their versions differ, when
40
+ * the stylesheet cannot be read, or when the Tailwind API or canary check fails.
41
+ */
42
+ declare function pragmatiksNextConfig(nextConfigs: readonly Linter.Config[], options: PragmatiksNextOptions): Promise<Linter.Config[]>;
43
+
44
+ export { type PragmatiksNextOptions, pragmatiksNextConfig };