pasika 0.10.2 → 0.10.4

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
@@ -1,238 +1,115 @@
1
- # pasika
2
-
3
- Documentation, the lint rules derived from it, and the checks that apply and diagnose both.
4
-
5
- `pasika` owns the framework's documentation and turns it into checks. Every requirement in `docs/` is recorded in an enforcement registry that says which ESLint rule or `pasika` check governs it — or, when none does, how a reviewer or agent applies it by hand. CI fails when a requirement has no recorded answer.
6
-
7
- ## Layout
8
-
9
- ```text
10
- docs/
11
- repository-policy.md # repo-wide code and documentation requirements (Policy)
12
- next-codebase-guide/ # placement, extraction, module conventions
13
- documentation-guide/ # how documents themselves are written
14
- pasika-adoption-guide/ # adopting and updating the framework
15
- next-tailwind-guide/ # Tailwind theme, composition, variants, states
16
- scripts/
17
- registry.json # requirement → enforcement, keyed by content hash
18
- coverage.ts # reconciles the docs against the registry
19
- dogfood.ts # lints sibling repos with the built presets
20
- utils/ # doc parsing, classification, registry IO
21
- types/ # registry schema
22
- constants/
23
- rfc2119.ts # single source of truth for RFC 2119 vocabulary
24
- eslint/
25
- rules/ # the lint rules, with fixture tests beside them
26
- rules/documentation/ # the documentation-guide rules, linting docs/ itself
27
- rules/tailwind/ # the Tailwind stylesheet rules
28
- rules/package-json/ # the package.json rules
29
- rules/husky/ # the husky-hook rules
30
- ```
31
-
32
- ## Documentation
33
-
34
- Documents come in four kinds — Guide, Rule, Policy, and Reference — each with a template and a creation rule under `docs/documentation-guide/`. A Rule owns requirements about one subject and demonstrates them with paired Incorrect/Correct examples; a Reference describes and defines but never constrains; a Policy document collects repo-wide requirements that span unrelated subjects; a Guide sequences the others into workflows. Each guide owns its own glossary, so a term is defined beside the workflow that uses it.
35
-
36
- ## Enforcement
37
-
38
- Requirements are identified by a hash of their canonical text, not by a hand-written id, so rewording one is visible:
39
-
40
- ```jsonc
41
- {
42
- "doc": "next-codebase-guide/rules/no-mixed-concerns-rule.md",
43
- "text": "A .tsx file that defines a component MUST contain exactly one component.",
44
- "hash": "b19fe3bd34",
45
- "ref": "pasika/no-mixed-concerns",
46
- "note": "counts exported components; a second component that is not exported is not detected",
47
- }
48
- ```
49
-
50
- The `text` field is the bullet as written in the document (markdown links and code spans intact), so it is greppable in the doc it came from; the `hash` is computed from the same text with links collapsed to their text and code spans unwrapped, so editing a URL or adding backticks does not read as a change.
1
+ # pasika 🐝
51
2
 
52
- Every requirement carries a `note` explaining how it is met: what the ref'd rule does and where it falls short, or with no `ref` — how a reviewer or agent applies it by hand. When a rule governs the requirement's subject without fully deciding it (e.g. its placement), the `ref` still names that rule and the `note` says what stays judgment, so a partial check never reads as a complete one.
3
+ > пасіка_apiary_ in Ukrainian
53
4
 
54
- `npm run coverage` fails when a requirement has no recorded answer, when its text changed, when it disappeared, when its `ref` names a rule that does not exist, or when a requirement a rule governs has no test titled with its text. Confirm a reworded requirement with `npx tsx scripts/coverage.ts --accept`.
5
+ The documentation an agent works from, the ESLint rules that documentation turns into, and the runtime helpers those rules assume.
55
6
 
56
- ## Commands
7
+ `pasika` is where a repository's conventions are written down once. Every requirement lives in `docs/`, and every one is recorded against the check that enforces it, so what the rules require and what the code does cannot drift apart. A **Rule** states requirements about one subject, a **Reference** defines the shapes and terms they are written in, a **Policy** collects the repo-wide ones, and a **Guide** sequences them into a workflow with its own glossary.
57
8
 
58
- The documentation guide itself is linted: the `pasika/*` markdown rules run over `docs/**/*.md` and report title, overview, structure, example-pairing, and RFC 2119 violations at the exact node.
9
+ ---
59
10
 
60
- The coverage checks are standalone scripts under `scripts/`, run with `tsx`; the drift check is the published `libyear` tool — there is no `pasika` binary:
11
+ ## 📦 Install
61
12
 
62
- ```bash
63
- npm run coverage # check that every requirement has recorded enforcement
64
- npm run coverage -- --accept # record reworded and removed requirements
65
- npx libyear --limit-major-individual=1 # fail when a dependency trails the latest by >1 major (drift check)
66
- ```
67
-
68
- Both acceptance and the report accept `--json` for agent use:
13
+ `zirka` brings pasika with it, as the package holding the preset's rules:
69
14
 
70
- ```bash
71
- npx tsx scripts/coverage.ts # text report
72
- npx tsx scripts/coverage.ts --json # full report as JSON
73
- npx tsx scripts/coverage.ts --accept # record accepted changes
15
+ ```sh
16
+ npm i -D zirka eslint prettier typescript
74
17
  ```
75
18
 
76
- A requirement the report shows as `new` is classified with the hash it prints:
19
+ Standalone, the presets need the language plugins as peers:
77
20
 
78
- ```bash
79
- npx tsx scripts/coverage.ts --classify d311a1457a --ref pasika/import-boundaries --note "reports imports that cross a feature boundary"
80
- npx tsx scripts/coverage.ts --classify 041b665bd7 --note "no check can compare against the previous state"
21
+ ```sh
22
+ npm i -D pasika eslint @eslint/css @eslint/json @eslint/markdown
81
23
  ```
82
24
 
83
- The script refuses a hash no requirement has, a `ref` naming a rule that does not exist, and a classification without a note — so a mismatch cannot reach the registry by hand. Re-running it on an already-recorded requirement replaces the earlier entry. All of it reads and writes `scripts/registry.json`.
25
+ The runtime helpers resolve `clsx` / `tailwind-merge`, `zod`, and `next` as optional peers, so install only the ones whose imports you use.
84
26
 
85
- ## Dogfooding
27
+ ## ⚡ Quick start
86
28
 
87
- `npm run dogfood` lints an arbitrary repository with pasika's built presets, without modifying the target. It exists to find flaws in pasika itself by running it against sibling repos — it does not fix the target. Run `npm run build` first, then point it at a repo:
29
+ Track the guides through `vulyk`, then switch them on in the ESLint config:
88
30
 
89
- ```bash
90
- npm run build
91
- npm run dogfood -- ../some/repo # pasikaNextjsApp preset (default)
92
- npm run dogfood -- ../some/repo --preset=pasikaApp
93
- npm run dogfood -- ../some/repo --pasika-only # tally only pasika/* rules
94
- npm run dogfood -- ../some/repo --rule=css-entry-point --findings
95
- npm run dogfood -- ../some/repo --json # machine-readable report
31
+ ```sh
32
+ npx vulyk add https://github.com/Bredansky/pasika/tree/<commit>/docs/next-codebase-guide \
33
+ --group managed --targets src
34
+ npx vulyk agents
96
35
  ```
97
36
 
98
- The script writes a temporary standalone `eslint.config.mjs` that imports the preset from this repo's `dist`, so it exercises exactly the config a consuming repository would write. The exit code reports whether the run succeeded; the target's problem count is informational (printed as `Exit code would be`). A missing `dist` build or an unknown flag exits non-zero.
99
-
100
- ## ESLint ruleset
101
-
102
- ### With Zirka (recommended)
103
-
104
37
  ```ts
105
- // eslint.config.ts — Next.js application
38
+ // eslint.config.ts
106
39
  import { RuleSeverity, styleguide } from "zirka";
107
40
 
108
41
  const { eslintConfig } = styleguide({
109
- next: RuleSeverity.Error,
110
42
  node: RuleSeverity.Error,
111
43
  typescript: RuleSeverity.Error,
44
+ next: RuleSeverity.Error,
112
45
  pasikaNextjsApp: RuleSeverity.Error,
113
46
  });
114
47
 
115
48
  export default eslintConfig;
116
49
  ```
117
50
 
118
- For a plain TypeScript repository, enable `pasikaApp` instead of `pasikaNextjsApp`; that preset covers the manifest, the zirka contract, and the docs only the `src/**` source rules belong to the Next.js app preset. `zirka` composes the pasika ruleset over four file scopes: TS/TSX under `src/**`, `globals.css` and other stylesheets, `package.json`, and markdown — each with its own ESLint language.
51
+ Without `zirka`, import `pasikaNextjsApp` from `pasika/eslint` and export it directly. `pasikaApp` is the plain-TypeScript preset the manifest, the zirka contract, and the docs, with no `src/**` source block.
119
52
 
120
- ### Without Zirka
53
+ ## 🛡️ The ruleset
121
54
 
122
- ```ts
123
- // eslint.config.ts — plain TypeScript repository
124
- import { pasikaApp } from "pasika/eslint";
55
+ 95 rules over five scopes:
125
56
 
126
- export default pasikaApp;
127
- ```
57
+ | Scope | Rules | Language plugin |
58
+ | ----------------- | ----- | ------------------------------------ |
59
+ | `src/**` TS / TSX | 51 | `@typescript-eslint/parser`, bundled |
60
+ | `docs/**/*.md` | 25 | `@eslint/markdown` |
61
+ | Stylesheets | 12 | `@eslint/css` |
62
+ | `package.json` | 6 | `@eslint/json` |
63
+ | `.husky/*` | 1 | — |
64
+
65
+ The requirement behind every rule id is in the guides; `npx tsx scripts/coverage.ts --json` prints the mapping. CI fails when a requirement has no recorded answer, when its text changed, when its ref names no rule, or when a rule-governed requirement has no test titled with it.
66
+
67
+ Some rules are cross-file: where a component, hook, value, type, or style belongs depends on which files use it, so those index the whole `src/` tree instead of one file. A move therefore changes which file gets reported, which is why a lint command must not pass ESLint's `--cache` — the repository policy requires this — and why the index is read from disk rather than from ESLint's file list, so a partial run like `lint-staged` still judges against the true graph.
68
+
69
+ ## 🧱 Runtime helpers
70
+
71
+ Four helpers ship with the package, so a repository imports them instead of writing its own copy to the shape the rules expect:
72
+
73
+ | Import | Exports | Resolves against (optional peer) |
74
+ | ---------------------- | -------------------------------------------------- | -------------------------------- |
75
+ | `pasika/cn` | `cn` | `clsx`, `tailwind-merge` |
76
+ | `pasika/http-error` | `HttpError` | — |
77
+ | `pasika/zod-fetch` | `zodFetch` | `zod` |
78
+ | `pasika/with-response` | `withResponse`, `HandlerResult`, `ResponseHeaders` | `next`, `zod` |
79
+
80
+ The entries share one `HttpError`, so a failure `zodFetch` throws satisfies the `instanceof` check `withResponse` makes. The TS/TSX rules call the TypeScript compiler API at lint time, so `typescript` is a pinned runtime dependency rather than a peer.
128
81
 
129
- `pasikaApp` is the plain-TypeScript-repository preset — the package.json manifest, the zirka configuration contract, and the docs. It carries no `src/**` source block: source linting is the Next.js app's job. `pasikaNextjsApp` is the full framework preset: everything in `pasikaApp` plus the Next.js-stack manifest requirement, the `src/**` app source rules, and the Tailwind stylesheet blocks. The granular rule objects (`tailwindRules`, `repoPackageJsonRules`, `documentationRules`) stay exported for manual wiring.
130
-
131
- The `src/**` blocks ship `@typescript-eslint/parser` themselves, so a standalone preset parses `.ts`/`.tsx` correctly on its own (`pasika` lists it as a runtime dependency).
132
-
133
- Because the preset blocks also wire ESLint's language plugins, using them directly (without `zirka`) requires `@eslint/css`, `@eslint/json`, and `@eslint/markdown` to be installed in the consuming project — they are `peerDependencies` of `pasika`. A `zirka`-based setup gets them automatically.
134
-
135
- ### TS/TSX rules
136
-
137
- | Rule | Enforces |
138
- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
139
- | `pasika/filename-case` | kebab-case for files that define no component |
140
- | `pasika/import-boundaries` | The shorter of the relative path and the `@/*` alias, and the layer boundaries |
141
- | `pasika/no-mixed-concerns` | One exported React component per `.tsx` file |
142
- | `pasika/no-arbitrary-tailwind` | No arbitrary `-[value]` classes, including inside `cn()` conditionals |
143
- | `pasika/unknown-utility` | Component class names are a custom `@utility`, a theme-generated utility, or a built-in the reset keeps |
144
- | `pasika/enforce-cn-merge` | `cn()` instead of `+` or template literals; at most five classes per group |
145
- | `pasika/enforce-cva-variant-props` | `VariantProps<typeof …>` instead of hand-written unions |
146
- | `pasika/cva-appearance-props` | Visual option props (`size`, `variant`, …) declared through CVA appearance props |
147
- | `pasika/cva-boolean-variants` | Boolean appearance props placed on the CVA variant, not as standalone props |
148
- | `pasika/enforce-barrel-exports` | A nested `index.ts` re-exports only its component |
149
- | `pasika/component-placement` † | The folder a component's consumers imply |
150
- | `pasika/support-file-placement` † | The folder a hook, type, schema, constant, or utility belongs in |
151
- | `pasika/application-structure` | The path-based parts of the application structure and configuration rules |
152
- | `pasika/named-exports` | Named exports for application files (framework routing files may default-export) |
153
- | `pasika/data-testid-case` | `data-testid` casing where a rendered result carries one |
154
- | `pasika/support-folder-shape` | A `constants/`, `types/`, or `schemas/` folder defines exports directly in `index.ts` or in named-re-exported files |
155
- | `pasika/import-through-index` | An extracted constant, type, or schema is imported through its folder's `index.ts` |
156
- | `pasika/util-file-name` | A single-function utility file is named in the function's kebab-case form |
157
- | `pasika/no-util-barrel` | A utility is imported directly, without a barrel |
158
- | `pasika/jsx-hygiene` | Calculations and complex conditions stay out of JSX children and attributes |
159
- | `pasika/interactive-component` | Interactive elements are component boundaries when mixed with other content |
160
- | `pasika/ui-state` | Native or ARIA state expression and Tailwind state variants |
161
- | `pasika/cross-feature-import` | A component importing from two or more feature folders lives in a shared location |
162
- | `pasika/pure-function-extract` | Pure functions extracted to `utils/`, even with one consumer |
163
- | `pasika/hook-complexity` | Hook complexity limits (imperative categories per hook) |
164
- | `pasika/locale-dotted-path` | A namespaced locale is read through its full dotted path |
165
- | `pasika/locales-location` | Locales live in the named locales object |
166
- | `pasika/hook-extraction` † | A hook with two or more consumers is extracted to its own file |
167
- | `pasika/value-extraction` † | A value with cross-folder consumers is extracted |
168
- | `pasika/config-extraction` † | A type, schema, or utility used outside its config module is moved |
169
- | `pasika/component-nesting` † | A component is not nested solely because it has support files |
170
- | `pasika/stay-flat` † | A component stays flat until it has exclusive children |
171
- | `pasika/type-extraction` † | A type or schema with cross-folder consumers is extracted |
172
- | `pasika/locale-placement` † | Shared locales at the top level, single-feature locales namespaced |
173
- | `pasika/locale-key-shape` | camelCase locale keys; keys over 30 characters end in a WAI-ARIA element role |
174
- | `pasika/shared-style-dedup` † | A className combo used by two or more components becomes a named utility |
175
- | `pasika/repeated-structure` | A block of elements repeated two or more times is extracted as a named component |
176
- | `pasika/sole-state-owner` | A contiguous JSX part that is the sole consumer of a useState hook is extracted into a named component |
177
- | `pasika/zirka-baseline` | eslint, prettier, and TypeScript configuration come from zirka instead of being restated locally |
178
- | `pasika/zod-schema-validation` | Runtime validation through Zod schemas, not hand-written type guards |
179
- | `pasika/source-under-src` | Application source lives under `src/`, not in root-level folders |
180
-
181
- ### Tailwind rules
182
-
183
- Applied to `src/**/globals.css` (and other stylesheets) through `@eslint/css` with tolerant Tailwind v4 parsing.
184
-
185
- | Rule | Enforces |
186
- | --------------------------------- | ----------------------------------------------------------------------------------------------------- |
187
- | `pasika/theme-reset` | A `--*: initial` theme reset is present |
188
- | `pasika/root-variables` | `:root` defines the CSS custom properties |
189
- | `pasika/apply-usage` | `@layer base` uses `@apply` for declarations |
190
- | `pasika/base-layer-pair` | The base layer applies `base-canvas` and `base-ink` |
191
- | `pasika/stylesheet-ordering` | Imports → `@custom-variant` → `:root` → `@theme` → `@utility` → `@layer base` |
192
- | `pasika/css-variable-naming` | Background vars named `--<role>-canvas`, text vars `--<role>-ink` |
193
- | `pasika/custom-utility-apply` | `@utility` blocks use `@apply` |
194
- | `pasika/skin-utility` | Repeated style combinations become a named skin utility |
195
- | `pasika/theme-variable-namespace` | Utility class groups share a namespace prefix |
196
- | `pasika/css-entry-point` † | One global entry, imported by one module, project CSS only in a stylesheet the entry imports directly |
197
- | `pasika/unused-utility` | A custom `@utility` no source file references is reported as dead |
198
-
199
- ### Package.json rules
200
-
201
- Applied to `package.json` through `@eslint/json`. The framework-agnostic `exact-version` rule applies to any repository, including pasika itself; `nextjs-stack` applies to a Next.js/React application, and the Vulyk rules apply to a repository adopting the framework.
202
-
203
- | Rule | Enforces |
204
- | ------------------------- | ------------------------------------------------------------------------------------------------- |
205
- | `pasika/exact-version` | Dependency and devDependency versions are pinned exactly, never ranges |
206
- | `pasika/nextjs-stack` | All Tech Stack Reference packages are listed in `package.json` (Next.js/React apps) |
207
- | `pasika/vulyk-dependency` | `vulyk` is listed in `devDependencies`, not `dependencies` |
208
- | `pasika/tracked-docs` | `vulyk.config.ts` tracks the framework's required tracked docs from pasika and `AGENTS.md` exists |
209
-
210
- ### Documentation rules
211
-
212
- The `pasika/*` markdown rules enforce the documentation guide over `docs/**/*.md` (22 rules): file-name suffixes and titles, overview shape (presence, sentence count, links), guide step structure, Incorrect/Correct pairing, policy document shape, reference block headings, RFC 2119 placement, template hygiene, link anchoring, and glossary-term linking. They run through `@eslint/markdown`; `npm run coverage` verifies each has a test and a registry entry. Pasika's own `docs/` are linted by them in CI (`npm run lint`).
213
-
214
- Run `npx tsx scripts/coverage.ts --json` for the exact requirement each rule covers.
215
-
216
- ### † Cross-file rules
217
-
218
- Where a component, hook, value, type, or style belongs depends on which files use it — and whether a stylesheet sits inside the global import graph depends on the whole `src/` tree — so the rules marked † index the whole `src/` tree instead of looking at one file. Two consequences:
219
-
220
- - **Do not pass `--cache`.** Move a file and the finding belongs to a _different_ file, whose cache entry is unchanged — so ESLint would replay a stale verdict. `repository-policy.md` requires lint commands to run without it.
221
- - The index is read from disk rather than from ESLint's file list, so a partial run such as `lint-staged` still judges against the true graph.
222
-
223
- All are inert in a repository with no `src/` tree.
224
-
225
- ## Runtime dependency on typescript
226
-
227
- Many of the TS/TSX rules call the TypeScript compiler API directly at lint time, so the published package imports `typescript` at runtime. `tsup` keeps it external (unbundled); `pasika` lists it as a pinned `dependency` so an installing consumer gets a version known to work with the rules. There is no `typescript` `peerDependency`.
228
-
229
- ## Development
230
-
231
- ```bash
232
- npm run lint
82
+ ## 📚 Documentation
83
+
84
+ The guides ship in this repository and are adopted into a project through `vulyk`. Each one owns its glossary:
85
+
86
+ | Document | Covers |
87
+ | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
88
+ | [Repository Policy](https://github.com/Bredansky/pasika/blob/main/docs/repository-policy.md) | requirements spanning every change in a repository |
89
+ | [Next Codebase Guide](https://github.com/Bredansky/pasika/blob/main/docs/next-codebase-guide/next-codebase-guide.md) | where code lives: modules, components, support files, imports |
90
+ | [Next Tailwind Guide](https://github.com/Bredansky/pasika/blob/main/docs/next-tailwind-guide/next-tailwind-guide.md) | theme tokens, class composition, variants, states |
91
+ | [Documentation Guide](https://github.com/Bredansky/pasika/blob/main/docs/documentation-guide/documentation-guide.md) | how documents themselves are written |
92
+ | [Pasika Adoption Guide](https://github.com/Bredansky/pasika/blob/main/docs/pasika-adoption-guide/pasika-adoption-guide.md) | adopting and updating the framework |
93
+
94
+ ## 🐝 Sibling packages
95
+
96
+ | Package | What it does |
97
+ | ------------------------------------------- | ------------------------------------------------------------------------------- |
98
+ | [vulyk](https://github.com/Bredansky/vulyk) | Installs skills and tracked docs from pinned sources, and generates agent files |
99
+ | **pasika** | The documentation, the rules derived from it, and the helpers — this package |
100
+ | [zirka](https://github.com/Bredansky/zirka) | Wires ESLint, Prettier, and TypeScript into one `styleguide()` config |
101
+
102
+ ## 🧪 Development
103
+
104
+ ```sh
105
+ npm run lint # eslint, including the framework's own docs rules
233
106
  npm run typecheck
234
107
  npm run test:unit
235
- npm run coverage
108
+ npm run coverage # every requirement still has recorded enforcement
236
109
  npm run build
237
- npm run dogfood -- ../some/repo # requires a build; see Dogfooding above
110
+ npm run dogfood -- ../some/repo # lint a sibling repo with the built presets
238
111
  ```
112
+
113
+ ## 📄 License
114
+
115
+ MIT
@@ -0,0 +1,14 @@
1
+ // helpers/http-error.ts
2
+ var HttpError = class extends Error {
3
+ constructor(message, status, data = null) {
4
+ super(message);
5
+ this.status = status;
6
+ this.data = data;
7
+ }
8
+ status;
9
+ data;
10
+ };
11
+
12
+ export {
13
+ HttpError
14
+ };
@@ -240,6 +240,7 @@ declare const pasikaPlugin: {
240
240
  "route-handler-shape": eslint.Rule.RuleModule;
241
241
  "http-error-usage": eslint.Rule.RuleModule;
242
242
  "with-response-helper": eslint.Rule.RuleModule;
243
+ "zod-fetch-helper": eslint.Rule.RuleModule;
243
244
  "hook-complexity": eslint.Rule.RuleModule;
244
245
  "locale-dotted-path": eslint.Rule.RuleModule;
245
246
  "locales-location": eslint.Rule.RuleModule;