pasika 0.3.8 → 0.3.9

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,6 +1,6 @@
1
1
  # pasika
2
2
 
3
- Documentation, the lint rules derived from it, and the CLI that applies and diagnoses both.
3
+ Documentation, the lint rules derived from it, and the checks that apply and diagnose both.
4
4
 
5
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
6
 
@@ -8,20 +8,24 @@ Documentation, the lint rules derived from it, and the CLI that applies and diag
8
8
 
9
9
  ```text
10
10
  docs/
11
- repository-policy.md # agent-conduct requirements (Policy)
12
11
  repository-policy.md # repo-wide code and documentation requirements (Policy)
13
12
  code-organization-guide/ # placement, extraction, module conventions
14
13
  documentation-guide/ # how documents themselves are written
15
14
  framework-adoption-guide/ # adopting and updating the framework
16
15
  styling-guide/ # Tailwind theme, composition, variants, states
17
- enforcement/
16
+ scripts/
18
17
  registry.json # requirement → enforcement, keyed by content hash
19
18
  coverage.ts # reconciles the docs against the registry
19
+ utils/ # doc parsing, classification, registry IO
20
+ types/ # registry schema
21
+ constants/
22
+ rfc2119.ts # single source of truth for RFC 2119 vocabulary
20
23
  eslint/
21
- pasika/rules/ # the lint rules, with fixture tests beside them
22
- pasika/rules/md/ # the documentation-guide rules, linting docs/ itself
23
- cli/
24
- index.ts # the `pasika` command
24
+ rules/ # the lint rules, with fixture tests beside them
25
+ rules/documentation/ # the documentation-guide rules, linting docs/ itself
26
+ rules/tailwind/ # the Tailwind stylesheet rules
27
+ rules/package-json/ # the package.json rules
28
+ rules/husky/ # the husky-hook rules
25
29
  ```
26
30
 
27
31
  ## Documentation
@@ -46,27 +50,36 @@ The `text` field is the bullet as written in the document (markdown links and co
46
50
 
47
51
  Every requirement carries a `note` explaining how it is met: what the ref'd rule or doctor check 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.
48
52
 
49
- `pasika coverage` fails when a requirement has no recorded answer, when its text changed, when it disappeared, when its `ref` names a rule or doctor check that does not exist, or when a requirement a rule governs has no test titled with its text. Confirm a reworded requirement with `pasika coverage --accept`.
53
+ `npm run coverage` fails when a requirement has no recorded answer, when its text changed, when it disappeared, when its `ref` names a rule or doctor check 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`.
50
54
 
51
55
  ## Commands
52
56
 
53
57
  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.
54
58
 
59
+ The coverage checks are standalone scripts under `scripts/`, run with `tsx`; the drift check is the published `libyear` tool — there is no `pasika` binary:
60
+
55
61
  ```bash
56
- npx pasika coverage # check that every requirement has recorded enforcement
57
- npx pasika coverage --accept # record reworded and removed requirements
62
+ npm run coverage # check that every requirement has recorded enforcement
63
+ npm run coverage -- --accept # record reworded and removed requirements
64
+ npx libyear --limit-major-individual=1 # fail when a dependency trails the latest by >1 major (drift check)
58
65
  ```
59
66
 
60
- Both accept `--json` for agent use.
67
+ Both acceptance and the report accept `--json` for agent use:
61
68
 
62
- A requirement `coverage` reports as `new` is classified with the hash it prints:
69
+ ```bash
70
+ npx tsx scripts/coverage.ts # text report
71
+ npx tsx scripts/coverage.ts --json # full report as JSON
72
+ npx tsx scripts/coverage.ts --accept # record accepted changes
73
+ ```
74
+
75
+ A requirement the report shows as `new` is classified with the hash it prints:
63
76
 
64
77
  ```bash
65
- npx pasika coverage --classify d311a1457a --ref pasika/import-boundaries --note "reports imports that cross a feature boundary"
66
- npx pasika coverage --classify 041b665bd7 --note "no check can compare against the previous state"
78
+ npx tsx scripts/coverage.ts --classify d311a1457a --ref pasika/import-boundaries --note "reports imports that cross a feature boundary"
79
+ npx tsx scripts/coverage.ts --classify 041b665bd7 --note "no check can compare against the previous state"
67
80
  ```
68
81
 
69
- The command refuses a hash no requirement has, a `ref` naming a rule or doctor check that does not exist, and an entry without a note — so a mismatch cannot reach the registry by hand. Re-running it on an already-recorded requirement replaces the earlier entry.
82
+ The script refuses a hash no requirement has, a `ref` naming a rule or doctor check 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`.
70
83
 
71
84
  ## ESLint ruleset
72
85
 
@@ -90,14 +103,14 @@ export default eslintConfig;
90
103
 
91
104
  ### Without Zirka
92
105
 
93
- ```ts
106
+ ````ts
94
107
  // eslint.config.ts
95
- import { pasikaConfig } from "pasika/eslint";
108
+ import { pasikaNext } from "pasika/eslint";
96
109
 
97
- export default [pasikaConfig];
98
- ```
110
+ export default pasikaNext;
111
+ ``` `pasikaNext` is the full flat-config array: the `src/**` TS/TSX and Tailwind stylesheet blocks plus everything in `pasikaRepo`. `pasikaRepo` is the repository-level preset — every block that does not touch `src/**` (the package.json manifest, husky hooks, and docs) — and is a strict subset of `pasikaNext`. The granular rule objects (`tailwindRules`, `repoPackageJsonRules`, `documentationRules`) stay exported for manual wiring.
99
112
 
100
- `pasikaConfig` applies the TS/TSX rules to `src/**` only, so a repository without a `src/` tree passes it trivially. The CSS, JSON, and markdown language configs are composed by `zirka`'s `styleguide()`; the individual rule objects (`cssRules`, `jsonRules`, `mdRules`) are exported for manual wiring.
113
+ Because the preset blocks wire ESLint's language plugins, using `pasikaNext` 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.
101
114
 
102
115
  ### TS/TSX rules
103
116
 
@@ -140,11 +153,11 @@ export default [pasikaConfig];
140
153
  | `pasika/shared-style-dedup` † | A className combo used by two or more components becomes a named utility |
141
154
  | `pasika/repeated-structure` | A block of elements repeated two or more times is extracted as a named component |
142
155
  | `pasika/sole-state-owner` | A contiguous JSX part that is the sole consumer of a useState hook is extracted into a named component |
143
- | `pasika/no-eslint-disable` | No `eslint-disable` directives |
156
+ | `pasika/config-baseline` | `eslint.config.*` references zirka and `tsconfig.json` exists |
144
157
  | `pasika/zod-schema-validation` | Runtime validation through Zod schemas, not hand-written type guards |
145
158
  | `pasika/source-under-src` | Application source lives under `src/`, not in root-level folders |
146
159
 
147
- ### CSS rules
160
+ ### Tailwind rules
148
161
 
149
162
  Applied to `src/**/globals.css` (and other stylesheets) through `@eslint/css` with tolerant Tailwind v4 parsing.
150
163
 
@@ -161,20 +174,21 @@ Applied to `src/**/globals.css` (and other stylesheets) through `@eslint/css` wi
161
174
  | `pasika/theme-variable-namespace` | Utility class groups share a namespace prefix |
162
175
  | `pasika/global-css-location` | Global CSS lives in the correct entry point |
163
176
 
164
- ### JSON rules
177
+ ### Package.json rules
165
178
 
166
- Applied to `package.json` through `@eslint/json`.
179
+ Applied to `package.json` through `@eslint/json`. The framework-agnostic subset (`no-vulyk-dependency`, `exact-version`) applies to any repository, including pasika itself; `nextjs-stack` applies to a Next.js/React application.
167
180
 
168
- | Rule | Enforces |
169
- | ---------------------------- | --------------------------------- |
170
- | `pasika/no-cache-flag` | Lint scripts don't pass `--cache` |
171
- | `pasika/no-vulyk-dependency` | `vulyk` is not in `dependencies` |
181
+ | Rule | Enforces |
182
+ | ---------------------------- | ----------------------------------------------------------------------------------- |
183
+ | `pasika/no-vulyk-dependency` | `vulyk` is not in `dependencies` |
184
+ | `pasika/exact-version` | Dependency and devDependency versions are pinned exactly, never ranges |
185
+ | `pasika/nextjs-stack` | All Tech Stack Reference packages are listed in `package.json` (Next.js/React apps) |
172
186
 
173
187
  ### Documentation rules
174
188
 
175
- The `pasika/*` markdown rules enforce the documentation guide over `docs/**/*.md` (24 rules): file-name suffixes and titles, overview presence and length, 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`; `pasika coverage` verifies each has a test and a registry entry. Pasika's own `docs/` are linted by them in CI (`npm run lint`).
189
+ The `pasika/*` markdown rules enforce the documentation guide over `docs/**/*.md` (24 rules): file-name suffixes and titles, overview presence and length, 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`).
176
190
 
177
- Run `pasika coverage --json` for the exact requirement each rule covers.
191
+ Run `npx tsx scripts/coverage.ts --json` for the exact requirement each rule covers.
178
192
 
179
193
  ### † Cross-file rules
180
194
 
@@ -185,6 +199,10 @@ Where a component, hook, value, type, or style belongs depends on which files us
185
199
 
186
200
  All are inert in a repository with no `src/` tree.
187
201
 
202
+ ## Runtime dependency on typescript
203
+
204
+ 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`.
205
+
188
206
  ## Development
189
207
 
190
208
  ```bash
@@ -193,4 +211,4 @@ npm run typecheck
193
211
  npm run test
194
212
  npm run coverage
195
213
  npm run build
196
- ```
214
+ ````
@@ -74,7 +74,7 @@ type JsxOpeningElementNode = Rule.Node & {
74
74
  parent?: JsxElementNode;
75
75
  };
76
76
 
77
- declare const mdRules: {
77
+ declare const documentationRules: {
78
78
  "doc-kind-suffix": _eslint_markdown.MarkdownRuleDefinition;
79
79
  "title-matches-file-name": _eslint_markdown.MarkdownRuleDefinition;
80
80
  "overview-present": _eslint_markdown.MarkdownRuleDefinition;
@@ -101,7 +101,7 @@ declare const mdRules: {
101
101
  "glossary-term-linking": _eslint_markdown.MarkdownRuleDefinition;
102
102
  };
103
103
 
104
- declare const cssRules: {
104
+ declare const tailwindRules: {
105
105
  "theme-reset": _eslint_css.CSSRuleDefinition;
106
106
  "root-variables": _eslint_css.CSSRuleDefinition;
107
107
  "apply-usage": _eslint_css.CSSRuleDefinition;
@@ -115,26 +115,28 @@ declare const cssRules: {
115
115
  "global-stylesheet": _eslint_css.CSSRuleDefinition;
116
116
  };
117
117
 
118
- declare const jsonRules: {
118
+ /** Framework-agnostic package.json rules any repository should follow. */
119
+ declare const repoPackageJsonRules: {
119
120
  "no-vulyk-dependency": _eslint_json.JSONRuleDefinition;
120
- "zirka-installed": _eslint_json.JSONRuleDefinition;
121
+ "exact-version": _eslint_json.JSONRuleDefinition;
122
+ };
123
+ /** Package.json rules specific to a Next.js (or React) application. */
124
+ declare const nextPackageJsonRules: {
125
+ "nextjs-stack": _eslint_json.JSONRuleDefinition;
126
+ };
127
+
128
+ declare const huskyRules: {
129
+ "husky-hook": _eslint_json.JSONRuleDefinition;
121
130
  };
122
131
 
132
+ /** Every source rule, merged so the Next preset applies them all. */
123
133
  declare const pasikaRules: {
124
134
  "component-placement": eslint.Rule.RuleModule;
125
- "support-file-placement": eslint.Rule.RuleModule;
126
135
  "application-structure": eslint.Rule.RuleModule;
127
- "named-exports": eslint.Rule.RuleModule;
128
136
  "data-testid-case": eslint.Rule.RuleModule;
129
- "support-folder-shape": eslint.Rule.RuleModule;
130
- "import-through-index": eslint.Rule.RuleModule;
131
- "util-file-name": eslint.Rule.RuleModule;
132
- "no-util-barrel": eslint.Rule.RuleModule;
133
137
  "jsx-hygiene": eslint.Rule.RuleModule;
134
138
  "interactive-component": eslint.Rule.RuleModule;
135
139
  "ui-state": eslint.Rule.RuleModule;
136
- "filename-case": eslint.Rule.RuleModule;
137
- "import-boundaries": eslint.Rule.RuleModule;
138
140
  "no-mixed-concerns": eslint.Rule.RuleModule;
139
141
  "no-arbitrary-tailwind": eslint.Rule.RuleModule;
140
142
  "enforce-cn-merge": {
@@ -149,38 +151,54 @@ declare const pasikaRules: {
149
151
  JSXAttribute(node: JsxAttributeNode): void;
150
152
  };
151
153
  };
154
+ "cn-helper": eslint.Rule.RuleModule;
152
155
  "enforce-cva-variant-props": eslint.Rule.RuleModule;
153
156
  "cva-appearance-props": eslint.Rule.RuleModule;
154
157
  "cva-boolean-variants": eslint.Rule.RuleModule;
155
- "enforce-barrel-exports": eslint.Rule.RuleModule;
156
158
  "cross-feature-import": eslint.Rule.RuleModule;
157
159
  "pure-function-extract": eslint.Rule.RuleModule;
158
160
  "hook-complexity": eslint.Rule.RuleModule;
159
161
  "locale-dotted-path": eslint.Rule.RuleModule;
160
162
  "locales-location": eslint.Rule.RuleModule;
161
163
  "hook-extraction": eslint.Rule.RuleModule;
162
- "value-extraction": eslint.Rule.RuleModule;
163
- "config-extraction": eslint.Rule.RuleModule;
164
164
  "component-nesting": eslint.Rule.RuleModule;
165
165
  "stay-flat": eslint.Rule.RuleModule;
166
- "type-extraction": eslint.Rule.RuleModule;
167
166
  "locale-placement": eslint.Rule.RuleModule;
168
167
  "sole-state-owner": eslint.Rule.RuleModule;
169
168
  "locale-key-shape": eslint.Rule.RuleModule;
170
169
  "shared-style-dedup": eslint.Rule.RuleModule;
171
170
  "repeated-structure": eslint.Rule.RuleModule;
171
+ "filename-case": eslint.Rule.RuleModule;
172
+ "import-boundaries": eslint.Rule.RuleModule;
173
+ "named-exports": eslint.Rule.RuleModule;
174
+ "support-file-placement": eslint.Rule.RuleModule;
175
+ "support-folder-shape": eslint.Rule.RuleModule;
176
+ "import-through-index": eslint.Rule.RuleModule;
177
+ "util-file-name": eslint.Rule.RuleModule;
178
+ "no-util-barrel": eslint.Rule.RuleModule;
179
+ "enforce-barrel-exports": eslint.Rule.RuleModule;
180
+ "config-extraction": eslint.Rule.RuleModule;
181
+ "value-extraction": eslint.Rule.RuleModule;
182
+ "type-extraction": eslint.Rule.RuleModule;
172
183
  "zod-schema-validation": eslint.Rule.RuleModule;
173
184
  "source-under-src": eslint.Rule.RuleModule;
174
185
  "config-baseline": eslint.Rule.RuleModule;
175
186
  };
176
187
 
177
- /** Rule ids as they appear in configuration and in lint output. */
178
- declare const pasikaRuleIds: string[];
179
- declare const pasikaMdRuleIds: string[];
180
- declare const pasikaCssRuleIds: string[];
181
- declare const pasikaJsonRuleIds: string[];
182
188
  /** Every rule id, as they appear in configuration and in lint output. */
183
189
  declare const allPasikaRuleIds: string[];
184
- declare const pasikaConfig: Linter.Config;
190
+ /**
191
+ * Repository-level preset: every block that does NOT touch `src/**` — the
192
+ * package.json manifest (including husky hook requirements) and the
193
+ * documentation-guide markdown rules. This governs the repository itself,
194
+ * independent of any application source.
195
+ */
196
+ declare const pasikaRepo: Linter.Config[];
197
+ /**
198
+ * Framework-wide preset: the full adopted-to-the-framework stack. Anything in
199
+ * `pasikaRepo` plus the `src/**` blocks — TS/TSX source rules and the Tailwind
200
+ * stylesheet rules. `pasikaRepo` is a strict subset of `pasikaNext`.
201
+ */
202
+ declare const pasikaNext: Linter.Config[];
185
203
 
186
- export { allPasikaRuleIds, cssRules, jsonRules, mdRules, pasikaConfig, pasikaCssRuleIds, pasikaJsonRuleIds, pasikaMdRuleIds, pasikaRuleIds, pasikaRules };
204
+ export { allPasikaRuleIds, documentationRules, huskyRules, nextPackageJsonRules, pasikaNext, pasikaRepo, pasikaRules, repoPackageJsonRules, tailwindRules };
@@ -1,3 +1,8 @@
1
+ // eslint/index.ts
2
+ import css from "@eslint/css";
3
+ import jsonPlugin from "@eslint/json";
4
+ import markdown from "@eslint/markdown";
5
+
1
6
  // eslint/rules/filename-case.ts
2
7
  import path2 from "path";
3
8
 
@@ -547,6 +552,43 @@ var enforceCnMergeRule = {
547
552
  }
548
553
  };
549
554
 
555
+ // eslint/rules/cn-helper.ts
556
+ function isIdentifier(node, name) {
557
+ return node?.type === "Identifier" && node.name === name;
558
+ }
559
+ var cnHelperRule = {
560
+ meta: {
561
+ schema: [],
562
+ type: "problem",
563
+ docs: {
564
+ description: "Require the cn helper to merge clsx and tailwind-merge."
565
+ }
566
+ },
567
+ create(context) {
568
+ return {
569
+ FunctionDeclaration(node) {
570
+ if (isIdentifier(node.id, "cn")) checkBody(context, node.body);
571
+ },
572
+ VariableDeclarator(node) {
573
+ if (node.init?.type === "ArrowFunctionExpression" && isIdentifier(node.id, "cn")) {
574
+ checkBody(context, node.init.body);
575
+ }
576
+ }
577
+ };
578
+ }
579
+ };
580
+ function checkBody(context, body) {
581
+ const source = context.sourceCode.getText(body);
582
+ const usesClsx = /\bclsx\b/.test(source);
583
+ const usesTwMerge = /\btwMerge\b|\btailwind-merge\b/.test(source);
584
+ if (!usesClsx || !usesTwMerge) {
585
+ context.report({
586
+ node: body,
587
+ message: "cn must be built from clsx and tailwind-merge (e.g. cn = twMerge(clsx(inputs)))."
588
+ });
589
+ }
590
+ }
591
+
550
592
  // eslint/rules/enforce-cva-variant-props.ts
551
593
  var enforceCvaVariantPropsRule = {
552
594
  meta: {
@@ -3262,10 +3304,13 @@ var configBaselineRule = {
3262
3304
  }
3263
3305
  };
3264
3306
 
3265
- // eslint/rules/md/helpers.ts
3266
- var RFC_PATTERN = /\b(?<keyword>MUST NOT|MUST|SHALL NOT|SHALL|SHOULD NOT|SHOULD|MAY|RECOMMENDED|NOT RECOMMENDED|OPTIONAL|REQUIRED)\b/;
3307
+ // constants/rfc2119.ts
3308
+ var RFC_2119_KEYWORDS = ["MUST NOT", "MUST", "SHOULD NOT", "SHOULD", "MAY"];
3309
+ var RFC_2119_PATTERN = new RegExp(`\\b(?<keyword>${RFC_2119_KEYWORDS.join("|")})\\b`);
3310
+
3311
+ // eslint/rules/documentation/helpers.ts
3267
3312
  function containsRfcKeyword(text) {
3268
- const match = RFC_PATTERN.exec(text);
3313
+ const match = RFC_2119_PATTERN.exec(text);
3269
3314
  return match?.groups?.keyword ?? null;
3270
3315
  }
3271
3316
  function getFilename(context) {
@@ -3282,7 +3327,7 @@ function getLine(node) {
3282
3327
  return node.position?.start.line ?? 0;
3283
3328
  }
3284
3329
 
3285
- // eslint/rules/md/doc-kind-suffix.ts
3330
+ // eslint/rules/documentation/doc-kind-suffix.ts
3286
3331
  var docKindSuffixRule = {
3287
3332
  meta: {
3288
3333
  type: "problem",
@@ -3308,7 +3353,7 @@ var docKindSuffixRule = {
3308
3353
  }
3309
3354
  };
3310
3355
 
3311
- // eslint/rules/md/title-matches-file-name.ts
3356
+ // eslint/rules/documentation/title-matches-file-name.ts
3312
3357
  import path34 from "path";
3313
3358
  function toExpectedFileName(title) {
3314
3359
  return `${title.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}.md`;
@@ -3346,7 +3391,7 @@ var titleMatchesFileNameRule = {
3346
3391
  }
3347
3392
  };
3348
3393
 
3349
- // eslint/rules/md/overview-present.ts
3394
+ // eslint/rules/documentation/overview-present.ts
3350
3395
  var overviewPresentRule = {
3351
3396
  meta: {
3352
3397
  type: "problem",
@@ -3386,7 +3431,7 @@ var overviewPresentRule = {
3386
3431
  }
3387
3432
  };
3388
3433
 
3389
- // eslint/rules/md/overview-length.ts
3434
+ // eslint/rules/documentation/overview-length.ts
3390
3435
  function countSentences(text) {
3391
3436
  return text.split(/[.!?](?:\s+|$)/).filter((part) => part.trim() !== "").length;
3392
3437
  }
@@ -3429,7 +3474,7 @@ var overviewLengthRule = {
3429
3474
  }
3430
3475
  };
3431
3476
 
3432
- // eslint/rules/md/guide-overview-no-links.ts
3477
+ // eslint/rules/documentation/guide-overview-no-links.ts
3433
3478
  function containsLink(node) {
3434
3479
  if (node.type === "link") return true;
3435
3480
  if ("children" in node) {
@@ -3475,7 +3520,7 @@ var guideOverviewNoLinksRule = {
3475
3520
  }
3476
3521
  };
3477
3522
 
3478
- // eslint/rules/md/guide-step-single-sentence.ts
3523
+ // eslint/rules/documentation/guide-step-single-sentence.ts
3479
3524
  function countSentences2(text) {
3480
3525
  return text.split(/[.!?](?:\s+|$)/).filter((part) => part.trim() !== "").length;
3481
3526
  }
@@ -3513,7 +3558,7 @@ var guideStepSingleSentenceRule = {
3513
3558
  }
3514
3559
  };
3515
3560
 
3516
- // eslint/rules/md/guide-step-single-link.ts
3561
+ // eslint/rules/documentation/guide-step-single-link.ts
3517
3562
  function countDocLinks(node) {
3518
3563
  if (node.type === "link" && node.url.endsWith(".md")) return 1;
3519
3564
  if ("children" in node) {
@@ -3556,7 +3601,7 @@ var guideStepSingleLinkRule = {
3556
3601
  }
3557
3602
  };
3558
3603
 
3559
- // eslint/rules/md/guide-states-no-requirement.ts
3604
+ // eslint/rules/documentation/guide-states-no-requirement.ts
3560
3605
  var guideStatesNoRequirementRule = {
3561
3606
  meta: {
3562
3607
  type: "problem",
@@ -3582,7 +3627,7 @@ var guideStatesNoRequirementRule = {
3582
3627
  }
3583
3628
  };
3584
3629
 
3585
- // eslint/rules/md/requirement-present.ts
3630
+ // eslint/rules/documentation/requirement-present.ts
3586
3631
  function hasRequirement(node) {
3587
3632
  if (node.type === "listItem") {
3588
3633
  if (containsRfcKeyword(getTextContent(node))) return true;
@@ -3621,7 +3666,7 @@ var requirementPresentRule = {
3621
3666
  }
3622
3667
  };
3623
3668
 
3624
- // eslint/rules/md/rule-paired-examples.ts
3669
+ // eslint/rules/documentation/rule-paired-examples.ts
3625
3670
  var rulePairedExamplesRule = {
3626
3671
  meta: {
3627
3672
  type: "problem",
@@ -3657,7 +3702,7 @@ var rulePairedExamplesRule = {
3657
3702
  }
3658
3703
  };
3659
3704
 
3660
- // eslint/rules/md/example-heading-description.ts
3705
+ // eslint/rules/documentation/example-heading-description.ts
3661
3706
  var exampleHeadingDescriptionRule = {
3662
3707
  meta: {
3663
3708
  type: "problem",
@@ -3685,7 +3730,7 @@ var exampleHeadingDescriptionRule = {
3685
3730
  }
3686
3731
  };
3687
3732
 
3688
- // eslint/rules/md/policy-no-examples.ts
3733
+ // eslint/rules/documentation/policy-no-examples.ts
3689
3734
  var policyNoExamplesRule = {
3690
3735
  meta: {
3691
3736
  type: "problem",
@@ -3712,7 +3757,7 @@ var policyNoExamplesRule = {
3712
3757
  }
3713
3758
  };
3714
3759
 
3715
- // eslint/rules/md/no-cross-document-link.ts
3760
+ // eslint/rules/documentation/no-cross-document-link.ts
3716
3761
  function linkedKind(filename) {
3717
3762
  if (filename.endsWith("-rule.md")) return "rule";
3718
3763
  if (filename.endsWith("-reference.md")) return "reference";
@@ -3744,7 +3789,7 @@ var noCrossDocumentLinkRule = {
3744
3789
  }
3745
3790
  };
3746
3791
 
3747
- // eslint/rules/md/reference-no-rfc-vocabulary.ts
3792
+ // eslint/rules/documentation/reference-no-rfc-vocabulary.ts
3748
3793
  var referenceNoRfcVocabularyRule = {
3749
3794
  meta: {
3750
3795
  type: "problem",
@@ -3770,7 +3815,7 @@ var referenceNoRfcVocabularyRule = {
3770
3815
  }
3771
3816
  };
3772
3817
 
3773
- // eslint/rules/md/reference-block-headings.ts
3818
+ // eslint/rules/documentation/reference-block-headings.ts
3774
3819
  var referenceBlockHeadingsRule = {
3775
3820
  meta: {
3776
3821
  type: "problem",
@@ -3801,7 +3846,7 @@ var referenceBlockHeadingsRule = {
3801
3846
  }
3802
3847
  };
3803
3848
 
3804
- // eslint/rules/md/support-document-placement.ts
3849
+ // eslint/rules/documentation/support-document-placement.ts
3805
3850
  import path35 from "path";
3806
3851
  var supportDocumentPlacementRule = {
3807
3852
  meta: {
@@ -3834,7 +3879,7 @@ var supportDocumentPlacementRule = {
3834
3879
  }
3835
3880
  };
3836
3881
 
3837
- // eslint/rules/md/no-template-prompt.ts
3882
+ // eslint/rules/documentation/no-template-prompt.ts
3838
3883
  var TEMPLATE_PROMPT = /^\s*\[[A-Z0-9].*\]\s*$/;
3839
3884
  var noTemplatePromptRule = {
3840
3885
  meta: {
@@ -3858,10 +3903,10 @@ var noTemplatePromptRule = {
3858
3903
  }
3859
3904
  };
3860
3905
 
3861
- // eslint/rules/md/guide-folder-entry-point.ts
3906
+ // eslint/rules/documentation/guide-folder-entry-point.ts
3862
3907
  import path37 from "path";
3863
3908
 
3864
- // eslint/rules/md/project-index.ts
3909
+ // eslint/rules/documentation/project-index.ts
3865
3910
  import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
3866
3911
  import path36 from "path";
3867
3912
  var KIND_BY_SUFFIX = [
@@ -3915,7 +3960,7 @@ function findDocsRoot(filePath) {
3915
3960
  }
3916
3961
  }
3917
3962
 
3918
- // eslint/rules/md/guide-folder-entry-point.ts
3963
+ // eslint/rules/documentation/guide-folder-entry-point.ts
3919
3964
  var guideFolderEntryPointRule = {
3920
3965
  meta: {
3921
3966
  type: "problem",
@@ -3953,7 +3998,7 @@ var guideFolderEntryPointRule = {
3953
3998
  }
3954
3999
  };
3955
4000
 
3956
- // eslint/rules/md/rfc-only-in-bullets.ts
4001
+ // eslint/rules/documentation/rfc-only-in-bullets.ts
3957
4002
  function checkOutsideBullets(node, context, insideBullet) {
3958
4003
  if (node.type === "listItem") {
3959
4004
  for (const child of node.children) {
@@ -3996,7 +4041,7 @@ var rfcOnlyInBulletsRule = {
3996
4041
  }
3997
4042
  };
3998
4043
 
3999
- // eslint/rules/md/rfc-keyword-in-every-bullet.ts
4044
+ // eslint/rules/documentation/rfc-keyword-in-every-bullet.ts
4000
4045
  function checkBullets(node, context) {
4001
4046
  if (node.type === "listItem" && !containsRfcKeyword(getTextContent(node))) {
4002
4047
  context.report({
@@ -4029,7 +4074,7 @@ var rfcKeywordInEveryBulletRule = {
4029
4074
  }
4030
4075
  };
4031
4076
 
4032
- // eslint/rules/md/policy-subject-headings.ts
4077
+ // eslint/rules/documentation/policy-subject-headings.ts
4033
4078
  var policySubjectHeadingsRule = {
4034
4079
  meta: {
4035
4080
  type: "problem",
@@ -4082,7 +4127,7 @@ var policySubjectHeadingsRule = {
4082
4127
  }
4083
4128
  };
4084
4129
 
4085
- // eslint/rules/md/guide-link-anchors.ts
4130
+ // eslint/rules/documentation/guide-link-anchors.ts
4086
4131
  function visitSteps2(node, check) {
4087
4132
  if (node.type === "list" && node.ordered) {
4088
4133
  for (const child of node.children) check(child);
@@ -4126,7 +4171,7 @@ var guideLinkAnchorsRule = {
4126
4171
  }
4127
4172
  };
4128
4173
 
4129
- // eslint/rules/md/no-nested-how-to.ts
4174
+ // eslint/rules/documentation/no-nested-how-to.ts
4130
4175
  var noNestedHowToRule = {
4131
4176
  meta: {
4132
4177
  type: "problem",
@@ -4162,7 +4207,7 @@ var noNestedHowToRule = {
4162
4207
  }
4163
4208
  };
4164
4209
 
4165
- // eslint/rules/md/glossary-term-linking.ts
4210
+ // eslint/rules/documentation/glossary-term-linking.ts
4166
4211
  import { readFileSync as readFileSync5 } from "fs";
4167
4212
  import path38 from "path";
4168
4213
  function extractGlossaryTerms(filePath) {
@@ -4243,8 +4288,8 @@ var glossaryTermLinkingRule = {
4243
4288
  }
4244
4289
  };
4245
4290
 
4246
- // eslint/rules/md/index.ts
4247
- var mdRules = {
4291
+ // eslint/rules/documentation/index.ts
4292
+ var documentationRules = {
4248
4293
  "doc-kind-suffix": docKindSuffixRule,
4249
4294
  "title-matches-file-name": titleMatchesFileNameRule,
4250
4295
  "overview-present": overviewPresentRule,
@@ -4271,7 +4316,7 @@ var mdRules = {
4271
4316
  "glossary-term-linking": glossaryTermLinkingRule
4272
4317
  };
4273
4318
 
4274
- // eslint/rules/css/helpers.ts
4319
+ // eslint/rules/tailwind/helpers.ts
4275
4320
  function walkNodes(node, visit) {
4276
4321
  visit(node);
4277
4322
  if ("children" in node && node.children) {
@@ -4328,7 +4373,7 @@ function selectorNames(node) {
4328
4373
  return names;
4329
4374
  }
4330
4375
 
4331
- // eslint/rules/css/theme-reset.ts
4376
+ // eslint/rules/tailwind/theme-reset.ts
4332
4377
  var themeResetRule = {
4333
4378
  meta: {
4334
4379
  schema: [],
@@ -4358,7 +4403,7 @@ var themeResetRule = {
4358
4403
  }
4359
4404
  };
4360
4405
 
4361
- // eslint/rules/css/root-variables.ts
4406
+ // eslint/rules/tailwind/root-variables.ts
4362
4407
  var rootVariablesRule = {
4363
4408
  meta: {
4364
4409
  schema: [],
@@ -4401,7 +4446,7 @@ var rootVariablesRule = {
4401
4446
  }
4402
4447
  };
4403
4448
 
4404
- // eslint/rules/css/apply-usage.ts
4449
+ // eslint/rules/tailwind/apply-usage.ts
4405
4450
  var applyUsageRule = {
4406
4451
  meta: {
4407
4452
  schema: [],
@@ -4431,7 +4476,7 @@ var applyUsageRule = {
4431
4476
  }
4432
4477
  };
4433
4478
 
4434
- // eslint/rules/css/base-layer-pair.ts
4479
+ // eslint/rules/tailwind/base-layer-pair.ts
4435
4480
  var baseLayerPairRule = {
4436
4481
  meta: {
4437
4482
  schema: [],
@@ -4469,7 +4514,7 @@ var baseLayerPairRule = {
4469
4514
  }
4470
4515
  };
4471
4516
 
4472
- // eslint/rules/css/stylesheet-ordering.ts
4517
+ // eslint/rules/tailwind/stylesheet-ordering.ts
4473
4518
  var SECTION_ORDER = ["imports", "custom-variant", "root", "theme", "utility", "base styles", "keyframes"];
4474
4519
  var stylesheetOrderingRule = {
4475
4520
  meta: {
@@ -4526,7 +4571,7 @@ var stylesheetOrderingRule = {
4526
4571
  }
4527
4572
  };
4528
4573
 
4529
- // eslint/rules/css/css-variable-naming.ts
4574
+ // eslint/rules/tailwind/css-variable-naming.ts
4530
4575
  var cssVariableNamingRule = {
4531
4576
  meta: {
4532
4577
  schema: [],
@@ -4558,7 +4603,7 @@ var cssVariableNamingRule = {
4558
4603
  }
4559
4604
  };
4560
4605
 
4561
- // eslint/rules/css/custom-utility-apply.ts
4606
+ // eslint/rules/tailwind/custom-utility-apply.ts
4562
4607
  function rawProperty(raw) {
4563
4608
  const match = /^\s*(?<property>[a-z][a-z-]*)\s*:/.exec(raw);
4564
4609
  return match?.groups?.property;
@@ -4600,7 +4645,7 @@ var customUtilityApplyRule = {
4600
4645
  }
4601
4646
  };
4602
4647
 
4603
- // eslint/rules/css/surface-utility.ts
4648
+ // eslint/rules/tailwind/surface-utility.ts
4604
4649
  var surfaceUtilityRule = {
4605
4650
  meta: {
4606
4651
  schema: [],
@@ -4633,7 +4678,7 @@ var surfaceUtilityRule = {
4633
4678
  }
4634
4679
  };
4635
4680
 
4636
- // eslint/rules/css/theme-variable-namespace.ts
4681
+ // eslint/rules/tailwind/theme-variable-namespace.ts
4637
4682
  var themeVariableNamespaceRule = {
4638
4683
  meta: {
4639
4684
  schema: [],
@@ -4672,7 +4717,7 @@ var themeVariableNamespaceRule = {
4672
4717
  }
4673
4718
  };
4674
4719
 
4675
- // eslint/rules/css/global-css-location.ts
4720
+ // eslint/rules/tailwind/global-css-location.ts
4676
4721
  var globalCssLocationRule = {
4677
4722
  meta: {
4678
4723
  schema: [],
@@ -4703,7 +4748,7 @@ var globalCssLocationRule = {
4703
4748
  }
4704
4749
  };
4705
4750
 
4706
- // eslint/rules/css/global-stylesheet.ts
4751
+ // eslint/rules/tailwind/global-stylesheet.ts
4707
4752
  var globalStylesheetRule = {
4708
4753
  meta: {
4709
4754
  schema: [],
@@ -4736,8 +4781,8 @@ var globalStylesheetRule = {
4736
4781
  }
4737
4782
  };
4738
4783
 
4739
- // eslint/rules/css/index.ts
4740
- var cssRules = {
4784
+ // eslint/rules/tailwind/index.ts
4785
+ var tailwindRules = {
4741
4786
  "theme-reset": themeResetRule,
4742
4787
  "root-variables": rootVariablesRule,
4743
4788
  "apply-usage": applyUsageRule,
@@ -4751,10 +4796,50 @@ var cssRules = {
4751
4796
  "global-stylesheet": globalStylesheetRule
4752
4797
  };
4753
4798
 
4754
- // eslint/rules/json/no-vulyk-dependency.ts
4799
+ // eslint/rules/package-json/exact-version.ts
4755
4800
  function memberName(member) {
4756
4801
  return member.name.type === "String" ? member.name.value : member.name.name;
4757
4802
  }
4803
+ var EXACT_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
4804
+ function memberValue(member) {
4805
+ return member.value.type === "String" ? member.value.value : null;
4806
+ }
4807
+ var exactVersionRule = {
4808
+ meta: {
4809
+ schema: [],
4810
+ type: "problem",
4811
+ docs: {
4812
+ description: "Require dependency and devDependency versions to be pinned exactly."
4813
+ }
4814
+ },
4815
+ create(context) {
4816
+ return {
4817
+ Document(node) {
4818
+ const root = node.body;
4819
+ if (root.type !== "Object") return;
4820
+ for (const key of ["dependencies", "devDependencies"]) {
4821
+ const section = root.members.find((member) => memberName(member) === key);
4822
+ if (section?.value.type !== "Object") continue;
4823
+ for (const member of section.value.members) {
4824
+ const version = memberValue(member);
4825
+ if (version === null) continue;
4826
+ if (!EXACT_VERSION_PATTERN.test(version)) {
4827
+ context.report({
4828
+ node: member,
4829
+ message: `${memberName(member)} must pin an exact version (e.g. "1.2.3"), not a range or cap such as "${version}".`
4830
+ });
4831
+ }
4832
+ }
4833
+ }
4834
+ }
4835
+ };
4836
+ }
4837
+ };
4838
+
4839
+ // eslint/rules/package-json/no-vulyk-dependency.ts
4840
+ function memberName2(member) {
4841
+ return member.name.type === "String" ? member.name.value : member.name.name;
4842
+ }
4758
4843
  var noVulykDependencyRule = {
4759
4844
  meta: {
4760
4845
  schema: [],
@@ -4769,9 +4854,9 @@ var noVulykDependencyRule = {
4769
4854
  const root = node.body;
4770
4855
  if (root.type !== "Object") return;
4771
4856
  for (const key of ["dependencies", "devDependencies"]) {
4772
- const section = root.members.find((member) => memberName(member) === key);
4857
+ const section = root.members.find((member) => memberName2(member) === key);
4773
4858
  if (section?.value.type !== "Object") continue;
4774
- const vulyk = section.value.members.find((member) => memberName(member) === "vulyk");
4859
+ const vulyk = section.value.members.find((member) => memberName2(member) === "vulyk");
4775
4860
  if (vulyk) {
4776
4861
  context.report({
4777
4862
  node: vulyk,
@@ -4784,16 +4869,39 @@ var noVulykDependencyRule = {
4784
4869
  }
4785
4870
  };
4786
4871
 
4787
- // eslint/rules/json/zirka-installed.ts
4788
- function memberName2(member) {
4872
+ // eslint/rules/package-json/nextjs-stack.ts
4873
+ var NEXTJS_STACK_DEPENDENCIES = [
4874
+ "next",
4875
+ "react",
4876
+ "react-dom",
4877
+ "zod",
4878
+ "class-variance-authority",
4879
+ "clsx",
4880
+ "tailwind-merge"
4881
+ ];
4882
+ var NEXTJS_STACK_DEV_DEPENDENCIES = [
4883
+ "typescript",
4884
+ "tailwindcss",
4885
+ "eslint",
4886
+ "prettier",
4887
+ "husky",
4888
+ "lint-staged",
4889
+ "zirka"
4890
+ ];
4891
+ function memberName3(member) {
4789
4892
  return member.name.type === "String" ? member.name.value : member.name.name;
4790
4893
  }
4791
- var zirkaInstalledRule = {
4894
+ function sectionPackages(root, section) {
4895
+ const member = root.type === "Object" ? root.members.find((entry) => memberName3(entry) === section) : void 0;
4896
+ if (member?.value.type !== "Object") return /* @__PURE__ */ new Set();
4897
+ return new Set(member.value.members.map(memberName3));
4898
+ }
4899
+ var nextjsStackRule = {
4792
4900
  meta: {
4793
4901
  schema: [],
4794
4902
  type: "problem",
4795
4903
  docs: {
4796
- description: "Require zirka to be listed in package.json as a devDependency."
4904
+ description: "Require the framework's runtime packages in dependencies and toolchain packages in devDependencies."
4797
4905
  }
4798
4906
  },
4799
4907
  create(context) {
@@ -4801,18 +4909,20 @@ var zirkaInstalledRule = {
4801
4909
  Document(node) {
4802
4910
  const root = node.body;
4803
4911
  if (root.type !== "Object") return;
4804
- const allDeps = [];
4805
- for (const key of ["dependencies", "devDependencies"]) {
4806
- const section = root.members.find((member) => memberName2(member) === key);
4807
- if (section?.value.type !== "Object") continue;
4808
- for (const member of section.value.members) {
4809
- allDeps.push(memberName2(member));
4810
- }
4912
+ const dependencies = sectionPackages(root, "dependencies");
4913
+ const devDependencies = sectionPackages(root, "devDependencies");
4914
+ for (const pkg of NEXTJS_STACK_DEPENDENCIES) {
4915
+ if (dependencies.has(pkg)) continue;
4916
+ context.report({
4917
+ node,
4918
+ message: `${pkg} must be listed in package.json as a dependency.`
4919
+ });
4811
4920
  }
4812
- if (!allDeps.includes("zirka")) {
4921
+ for (const pkg of NEXTJS_STACK_DEV_DEPENDENCIES) {
4922
+ if (devDependencies.has(pkg)) continue;
4813
4923
  context.report({
4814
4924
  node,
4815
- message: "zirka must be listed in package.json as a devDependency."
4925
+ message: `${pkg} must be listed in package.json as a devDependency.`
4816
4926
  });
4817
4927
  }
4818
4928
  }
@@ -4820,76 +4930,201 @@ var zirkaInstalledRule = {
4820
4930
  }
4821
4931
  };
4822
4932
 
4823
- // eslint/rules/json/index.ts
4824
- var jsonRules = {
4933
+ // eslint/rules/package-json/index.ts
4934
+ var repoPackageJsonRules = {
4825
4935
  "no-vulyk-dependency": noVulykDependencyRule,
4826
- "zirka-installed": zirkaInstalledRule
4936
+ "exact-version": exactVersionRule
4937
+ };
4938
+ var nextPackageJsonRules = {
4939
+ "nextjs-stack": nextjsStackRule
4940
+ };
4941
+
4942
+ // eslint/rules/husky/husky-hook.ts
4943
+ import { existsSync, readFileSync as readFileSync6 } from "fs";
4944
+ import path39 from "path";
4945
+ function memberName4(member) {
4946
+ return member.name.type === "String" ? member.name.value : member.name.name;
4947
+ }
4948
+ var huskyHookRule = {
4949
+ meta: {
4950
+ schema: [],
4951
+ type: "problem",
4952
+ docs: {
4953
+ description: "Require a pre-commit hook that runs lint-staged, typecheck, and the libyear drift check."
4954
+ }
4955
+ },
4956
+ create(context) {
4957
+ return {
4958
+ Document(node) {
4959
+ const root = node.body;
4960
+ if (root.type !== "Object") return;
4961
+ const scriptName = context.filename;
4962
+ if (!scriptName.endsWith("package.json")) return;
4963
+ const hookPath = path39.join(process.cwd(), ".husky", "pre-commit");
4964
+ if (!existsSync(hookPath)) {
4965
+ context.report({
4966
+ node,
4967
+ message: "No .husky/pre-commit hook found. Configure husky to run checks before commits."
4968
+ });
4969
+ return;
4970
+ }
4971
+ const content = readFileSync6(hookPath, "utf8");
4972
+ if (!content.includes("lint-staged")) {
4973
+ context.report({ node, message: ".husky/pre-commit must run lint-staged." });
4974
+ }
4975
+ if (!content.includes("typecheck")) {
4976
+ context.report({ node, message: ".husky/pre-commit must run npm run typecheck." });
4977
+ }
4978
+ if (!content.includes("libyear --limit-major-individual=1")) {
4979
+ context.report({ node, message: ".husky/pre-commit must run npx libyear --limit-major-individual=1." });
4980
+ }
4981
+ const scripts = root.members.find((member) => memberName4(member) === "scripts");
4982
+ if (scripts?.value.type === "Object") {
4983
+ const prepare = scripts.value.members.find((member) => memberName4(member) === "prepare");
4984
+ if (prepare?.value.type === "String" && !prepare.value.value.includes("husky")) {
4985
+ context.report({
4986
+ node: prepare,
4987
+ message: 'package.json "prepare" script must run husky (e.g. "prepare": "husky").'
4988
+ });
4989
+ }
4990
+ }
4991
+ }
4992
+ };
4993
+ }
4994
+ };
4995
+
4996
+ // eslint/rules/husky/index.ts
4997
+ var huskyRules = {
4998
+ "husky-hook": huskyHookRule
4827
4999
  };
4828
5000
 
4829
5001
  // eslint/index.ts
4830
- var pasikaRules = {
4831
- "component-placement": componentPlacementRule,
4832
- "support-file-placement": supportFilePlacementRule,
4833
- "application-structure": applicationStructureRule,
5002
+ var repoSourceRules = {
5003
+ "filename-case": filenameCaseRule,
5004
+ "import-boundaries": importBoundariesRule,
4834
5005
  "named-exports": namedExportsRule,
4835
- "data-testid-case": dataTestIdCaseRule,
5006
+ "support-file-placement": supportFilePlacementRule,
4836
5007
  "support-folder-shape": supportFolderShapeRule,
4837
5008
  "import-through-index": importThroughIndexRule,
4838
5009
  "util-file-name": utilFileNameRule,
4839
5010
  "no-util-barrel": noUtilBarrelRule,
5011
+ "enforce-barrel-exports": enforceBarrelExportsRule,
5012
+ "config-extraction": configExtractionRule,
5013
+ "value-extraction": valueExtractionRule,
5014
+ "type-extraction": typeExtractionRule,
5015
+ "zod-schema-validation": zodSchemaValidationRule,
5016
+ "source-under-src": sourceUnderSrcRule,
5017
+ "config-baseline": configBaselineRule
5018
+ };
5019
+ var nextSourceRules = {
5020
+ "component-placement": componentPlacementRule,
5021
+ "application-structure": applicationStructureRule,
5022
+ "data-testid-case": dataTestIdCaseRule,
4840
5023
  "jsx-hygiene": jsxHygieneRule,
4841
5024
  "interactive-component": interactiveComponentRule,
4842
5025
  "ui-state": uiStateRule,
4843
- "filename-case": filenameCaseRule,
4844
- "import-boundaries": importBoundariesRule,
4845
5026
  "no-mixed-concerns": noMixedConcernsRule,
4846
5027
  "no-arbitrary-tailwind": noArbitraryTailwindRule,
4847
5028
  "enforce-cn-merge": enforceCnMergeRule,
5029
+ "cn-helper": cnHelperRule,
4848
5030
  "enforce-cva-variant-props": enforceCvaVariantPropsRule,
4849
5031
  "cva-appearance-props": cvaAppearancePropsRule,
4850
5032
  "cva-boolean-variants": cvaBooleanVariantsRule,
4851
- "enforce-barrel-exports": enforceBarrelExportsRule,
4852
5033
  "cross-feature-import": crossFeatureImportRule,
4853
5034
  "pure-function-extract": pureFunctionExtractRule,
4854
5035
  "hook-complexity": hookComplexityRule,
4855
5036
  "locale-dotted-path": localeDottedPathRule,
4856
5037
  "locales-location": localesLocationRule,
4857
5038
  "hook-extraction": hookExtractionRule,
4858
- "value-extraction": valueExtractionRule,
4859
- "config-extraction": configExtractionRule,
4860
5039
  "component-nesting": componentNestingRule,
4861
5040
  "stay-flat": stayFlatRule,
4862
- "type-extraction": typeExtractionRule,
4863
5041
  "locale-placement": localePlacementRule,
4864
5042
  "sole-state-owner": soleStateOwnerRule,
4865
5043
  "locale-key-shape": localeKeyShapeRule,
4866
5044
  "shared-style-dedup": sharedStyleDedupRule,
4867
- "repeated-structure": repeatedStructureRule,
4868
- "zod-schema-validation": zodSchemaValidationRule,
4869
- "source-under-src": sourceUnderSrcRule,
4870
- "config-baseline": configBaselineRule
5045
+ "repeated-structure": repeatedStructureRule
5046
+ };
5047
+ var pasikaRules = {
5048
+ ...repoSourceRules,
5049
+ ...nextSourceRules
4871
5050
  };
4872
- var pasikaRuleIds = Object.keys(pasikaRules).map((name) => `pasika/${name}`);
4873
- var pasikaMdRuleIds = Object.keys(mdRules).map((name) => `pasika/${name}`);
4874
- var pasikaCssRuleIds = Object.keys(cssRules).map((name) => `pasika/${name}`);
4875
- var pasikaJsonRuleIds = Object.keys(jsonRules).map((name) => `pasika/${name}`);
4876
- var allPasikaRuleIds = [...pasikaRuleIds, ...pasikaMdRuleIds, ...pasikaCssRuleIds, ...pasikaJsonRuleIds];
4877
- var pasikaConfig = {
5051
+ function ruleIds(rules2) {
5052
+ return Object.keys(rules2).map((name) => `pasika/${name}`);
5053
+ }
5054
+ var pasikaRuleIds = ruleIds(pasikaRules);
5055
+ var documentationRuleIds = ruleIds(documentationRules);
5056
+ var tailwindRuleIds = ruleIds(tailwindRules);
5057
+ var repoPackageJsonRuleIds = ruleIds(repoPackageJsonRules);
5058
+ var nextPackageJsonRuleIds = ruleIds(nextPackageJsonRules);
5059
+ var huskyRuleIds = ruleIds(huskyRules);
5060
+ var allPasikaRuleIds = [
5061
+ ...pasikaRuleIds,
5062
+ ...documentationRuleIds,
5063
+ ...tailwindRuleIds,
5064
+ ...repoPackageJsonRuleIds,
5065
+ ...nextPackageJsonRuleIds,
5066
+ ...huskyRuleIds
5067
+ ];
5068
+ var nextAppBlock = {
4878
5069
  files: ["src/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}"],
4879
5070
  plugins: {
4880
5071
  pasika: { rules: pasikaRules }
4881
5072
  },
4882
5073
  rules: Object.fromEntries(pasikaRuleIds.map((id) => [id, "error"]))
4883
5074
  };
5075
+ var tailwindGlobalsBlock = {
5076
+ files: ["src/**/globals.css"],
5077
+ plugins: {
5078
+ css,
5079
+ pasika: { rules: tailwindRules }
5080
+ },
5081
+ language: "css/css",
5082
+ languageOptions: { tolerant: true },
5083
+ rules: Object.fromEntries(
5084
+ tailwindRuleIds.filter((id) => id !== "pasika/global-css-location").map((id) => [id, "error"])
5085
+ )
5086
+ };
5087
+ var tailwindAnyCssBlock = {
5088
+ files: ["src/**/*.css"],
5089
+ plugins: {
5090
+ css,
5091
+ pasika: { rules: tailwindRules }
5092
+ },
5093
+ language: "css/css",
5094
+ languageOptions: { tolerant: true },
5095
+ rules: { "pasika/global-css-location": "error" }
5096
+ };
5097
+ var manifestBlock = {
5098
+ files: ["package.json"],
5099
+ plugins: {
5100
+ json: { languages: { json: jsonPlugin.languages.json } },
5101
+ pasika: { rules: { ...repoPackageJsonRules, ...nextPackageJsonRules, ...huskyRules } }
5102
+ },
5103
+ language: "json/json",
5104
+ rules: Object.fromEntries(
5105
+ [...repoPackageJsonRuleIds, ...nextPackageJsonRuleIds, ...huskyRuleIds].map((id) => [id, "error"])
5106
+ )
5107
+ };
5108
+ var docsBlock = {
5109
+ files: ["docs/**/*.md"],
5110
+ ignores: ["**/_*/**"],
5111
+ plugins: {
5112
+ markdown,
5113
+ pasika: { rules: documentationRules }
5114
+ },
5115
+ language: "markdown/gfm",
5116
+ rules: Object.fromEntries(documentationRuleIds.map((id) => [id, "error"]))
5117
+ };
5118
+ var pasikaRepo = [manifestBlock, docsBlock];
5119
+ var pasikaNext = [...pasikaRepo, nextAppBlock, tailwindGlobalsBlock, tailwindAnyCssBlock];
4884
5120
  export {
4885
5121
  allPasikaRuleIds,
4886
- cssRules,
4887
- jsonRules,
4888
- mdRules,
4889
- pasikaConfig,
4890
- pasikaCssRuleIds,
4891
- pasikaJsonRuleIds,
4892
- pasikaMdRuleIds,
4893
- pasikaRuleIds,
4894
- pasikaRules
5122
+ documentationRules,
5123
+ huskyRules,
5124
+ nextPackageJsonRules,
5125
+ pasikaNext,
5126
+ pasikaRepo,
5127
+ pasikaRules,
5128
+ repoPackageJsonRules,
5129
+ tailwindRules
4895
5130
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",
@@ -31,28 +31,33 @@
31
31
  "!(*.ts|*.mts|*.cts|*.tsx|*.js|*.cjs|*.mjs|*.jsx)": "prettier --write --ignore-unknown",
32
32
  "*.ts|*.mts|*.cts|*.tsx|*.js|*.cjs|*.mjs|*.jsx": "npx eslint --fix"
33
33
  },
34
+ "dependencies": {
35
+ "typescript": "6.0.3"
36
+ },
34
37
  "devDependencies": {
35
- "@eslint/css": "^1.4.0",
36
- "@eslint/css-tree": "^4.0.5",
37
- "@eslint/json": "^2.0.1",
38
- "@eslint/markdown": "^8.0.3",
39
- "@humanwhocodes/momoa": "^3.3.12",
40
- "@types/estree": "^1.0.9",
41
- "@types/mdast": "^4.0.4",
42
- "@types/node": "^26.4.0",
43
- "@typescript-eslint/parser": "^8.68.0",
44
- "eslint": "^10.9.1",
45
- "husky": "^9.1.7",
46
- "lint-staged": "^16.4.0",
47
- "prettier": "^3.8.1",
48
- "tsup": "^8.5.1",
49
- "tsx": "^4.23.12",
50
- "typescript": "^5.9.2",
51
- "zirka": "^0.0.42",
52
- "zod": "^4.4.3"
38
+ "@eslint/css": "1.4.0",
39
+ "@eslint/css-tree": "4.0.5",
40
+ "@eslint/json": "2.0.1",
41
+ "@eslint/markdown": "8.0.3",
42
+ "@humanwhocodes/momoa": "3.3.12",
43
+ "@types/estree": "1.0.9",
44
+ "@types/mdast": "4.0.4",
45
+ "@types/node": "26.4.0",
46
+ "@typescript-eslint/parser": "8.68.0",
47
+ "eslint": "10.9.1",
48
+ "husky": "9.1.7",
49
+ "lint-staged": "17.4.1",
50
+ "prettier": "3.8.1",
51
+ "tsup": "8.5.1",
52
+ "tsx": "4.23.12",
53
+ "typescript": "6.0.3",
54
+ "zirka": "0.0.45",
55
+ "zod": "4.4.3"
53
56
  },
54
57
  "peerDependencies": {
55
- "typescript": ">=5.0.0"
58
+ "@eslint/css": ">=1.4.0",
59
+ "@eslint/json": ">=2.0.1",
60
+ "@eslint/markdown": ">=8.0.3"
56
61
  },
57
62
  "engines": {
58
63
  "node": ">=22"