pasika 0.3.9 → 0.4.1
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 +19 -16
- package/dist/eslint/pasika/index.d.ts +124 -18
- package/dist/eslint/pasika/index.js +286 -136
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -48,9 +48,9 @@ Requirements are identified by a hash of their canonical text, not by a hand-wri
|
|
|
48
48
|
|
|
49
49
|
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.
|
|
50
50
|
|
|
51
|
-
Every requirement carries a `note` explaining how it is met: what the ref'd rule
|
|
51
|
+
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.
|
|
52
52
|
|
|
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
|
|
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 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`.
|
|
54
54
|
|
|
55
55
|
## Commands
|
|
56
56
|
|
|
@@ -79,38 +79,40 @@ npx tsx scripts/coverage.ts --classify d311a1457a --ref pasika/import-boundaries
|
|
|
79
79
|
npx tsx scripts/coverage.ts --classify 041b665bd7 --note "no check can compare against the previous state"
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
The script refuses a hash no requirement has, a `ref` naming a rule
|
|
82
|
+
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`.
|
|
83
83
|
|
|
84
84
|
## ESLint ruleset
|
|
85
85
|
|
|
86
86
|
### With Zirka (recommended)
|
|
87
87
|
|
|
88
88
|
```ts
|
|
89
|
-
// eslint.config.ts
|
|
89
|
+
// eslint.config.ts — Next.js application
|
|
90
90
|
import { RuleSeverity, styleguide } from "zirka";
|
|
91
91
|
|
|
92
92
|
const { eslintConfig } = styleguide({
|
|
93
93
|
next: RuleSeverity.Error,
|
|
94
94
|
node: RuleSeverity.Error,
|
|
95
95
|
typescript: RuleSeverity.Error,
|
|
96
|
-
|
|
96
|
+
pasikaNextjsApp: RuleSeverity.Error,
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
export default eslintConfig;
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
`zirka` composes the
|
|
102
|
+
For a plain TypeScript repository, enable `pasikaTypescriptApp` instead of `pasikaNextjsApp`. `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.
|
|
103
103
|
|
|
104
104
|
### Without Zirka
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
// eslint.config.ts
|
|
108
|
-
import {
|
|
106
|
+
```ts
|
|
107
|
+
// eslint.config.ts — plain TypeScript repository
|
|
108
|
+
import { typescriptApp } from "pasika/eslint";
|
|
109
|
+
|
|
110
|
+
export default typescriptApp;
|
|
111
|
+
```
|
|
109
112
|
|
|
110
|
-
|
|
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.
|
|
113
|
+
`typescriptApp` is the plain-TypeScript-repository preset — the package.json manifest, the zirka configuration contract, the `src/**` TypeScript app source, and the docs. `nextjsApp` is the full framework preset: everything in `typescriptApp` plus the Next.js-stack manifest requirement, the Next.js app source rules, and the Tailwind stylesheet blocks. The granular rule objects (`tailwindRules`, `repoPackageJsonRules`, `documentationRules`) stay exported for manual wiring.
|
|
112
114
|
|
|
113
|
-
Because the preset blocks wire ESLint's language plugins, using
|
|
115
|
+
Because the preset blocks 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.
|
|
114
116
|
|
|
115
117
|
### TS/TSX rules
|
|
116
118
|
|
|
@@ -153,7 +155,7 @@ Because the preset blocks wire ESLint's language plugins, using `pasikaNext` dir
|
|
|
153
155
|
| `pasika/shared-style-dedup` † | A className combo used by two or more components becomes a named utility |
|
|
154
156
|
| `pasika/repeated-structure` | A block of elements repeated two or more times is extracted as a named component |
|
|
155
157
|
| `pasika/sole-state-owner` | A contiguous JSX part that is the sole consumer of a useState hook is extracted into a named component |
|
|
156
|
-
| `pasika/
|
|
158
|
+
| `pasika/zirka-baseline` | eslint, prettier, and TypeScript configuration come from zirka instead of being restated locally |
|
|
157
159
|
| `pasika/zod-schema-validation` | Runtime validation through Zod schemas, not hand-written type guards |
|
|
158
160
|
| `pasika/source-under-src` | Application source lives under `src/`, not in root-level folders |
|
|
159
161
|
|
|
@@ -176,17 +178,18 @@ Applied to `src/**/globals.css` (and other stylesheets) through `@eslint/css` wi
|
|
|
176
178
|
|
|
177
179
|
### Package.json rules
|
|
178
180
|
|
|
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.
|
|
181
|
+
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, and `vulyk-docs` to a repository adopting the framework.
|
|
180
182
|
|
|
181
183
|
| Rule | Enforces |
|
|
182
184
|
| ---------------------------- | ----------------------------------------------------------------------------------- |
|
|
183
185
|
| `pasika/no-vulyk-dependency` | `vulyk` is not in `dependencies` |
|
|
184
186
|
| `pasika/exact-version` | Dependency and devDependency versions are pinned exactly, never ranges |
|
|
185
187
|
| `pasika/nextjs-stack` | All Tech Stack Reference packages are listed in `package.json` (Next.js/React apps) |
|
|
188
|
+
| `pasika/vulyk-docs` | `vulyk.config.ts` tracks the framework's docs from pasika and `AGENTS.md` exists |
|
|
186
189
|
|
|
187
190
|
### Documentation rules
|
|
188
191
|
|
|
189
|
-
The `pasika/*` markdown rules enforce the documentation guide over `docs/**/*.md` (
|
|
192
|
+
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`).
|
|
190
193
|
|
|
191
194
|
Run `npx tsx scripts/coverage.ts --json` for the exact requirement each rule covers.
|
|
192
195
|
|
|
@@ -211,4 +214,4 @@ npm run typecheck
|
|
|
211
214
|
npm run test
|
|
212
215
|
npm run coverage
|
|
213
216
|
npm run build
|
|
214
|
-
|
|
217
|
+
```
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import * as eslint from 'eslint';
|
|
2
|
-
import { Rule, Linter } from 'eslint';
|
|
3
|
-
import * as ESTree from 'estree';
|
|
4
1
|
import * as _eslint_markdown from '@eslint/markdown';
|
|
5
2
|
import * as _eslint_css from '@eslint/css';
|
|
6
3
|
import * as _eslint_json from '@eslint/json';
|
|
4
|
+
import * as eslint from 'eslint';
|
|
5
|
+
import { Rule, Linter } from 'eslint';
|
|
6
|
+
import * as ESTree from 'estree';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Local AST node types for the parser-specific shapes ESLint's ESTree types
|
|
@@ -77,9 +77,7 @@ type JsxOpeningElementNode = Rule.Node & {
|
|
|
77
77
|
declare const documentationRules: {
|
|
78
78
|
"doc-kind-suffix": _eslint_markdown.MarkdownRuleDefinition;
|
|
79
79
|
"title-matches-file-name": _eslint_markdown.MarkdownRuleDefinition;
|
|
80
|
-
|
|
81
|
-
"overview-length": _eslint_markdown.MarkdownRuleDefinition;
|
|
82
|
-
"guide-overview-no-links": _eslint_markdown.MarkdownRuleDefinition;
|
|
80
|
+
overview: _eslint_markdown.MarkdownRuleDefinition;
|
|
83
81
|
"guide-step-single-sentence": _eslint_markdown.MarkdownRuleDefinition;
|
|
84
82
|
"guide-step-single-link": _eslint_markdown.MarkdownRuleDefinition;
|
|
85
83
|
"guide-states-no-requirement": _eslint_markdown.MarkdownRuleDefinition;
|
|
@@ -99,6 +97,7 @@ declare const documentationRules: {
|
|
|
99
97
|
"guide-link-anchors": _eslint_markdown.MarkdownRuleDefinition;
|
|
100
98
|
"no-nested-how-to": _eslint_markdown.MarkdownRuleDefinition;
|
|
101
99
|
"glossary-term-linking": _eslint_markdown.MarkdownRuleDefinition;
|
|
100
|
+
"guide-mentions-documents": _eslint_markdown.MarkdownRuleDefinition;
|
|
102
101
|
};
|
|
103
102
|
|
|
104
103
|
declare const tailwindRules: {
|
|
@@ -129,7 +128,11 @@ declare const huskyRules: {
|
|
|
129
128
|
"husky-hook": _eslint_json.JSONRuleDefinition;
|
|
130
129
|
};
|
|
131
130
|
|
|
132
|
-
|
|
131
|
+
declare const vulykRules: {
|
|
132
|
+
"vulyk-docs": _eslint_json.JSONRuleDefinition;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/** Every source rule, merged so the Next.js preset applies them all. */
|
|
133
136
|
declare const pasikaRules: {
|
|
134
137
|
"component-placement": eslint.Rule.RuleModule;
|
|
135
138
|
"application-structure": eslint.Rule.RuleModule;
|
|
@@ -182,23 +185,126 @@ declare const pasikaRules: {
|
|
|
182
185
|
"type-extraction": eslint.Rule.RuleModule;
|
|
183
186
|
"zod-schema-validation": eslint.Rule.RuleModule;
|
|
184
187
|
"source-under-src": eslint.Rule.RuleModule;
|
|
185
|
-
"
|
|
188
|
+
"zirka-baseline": eslint.Rule.RuleModule;
|
|
186
189
|
};
|
|
187
190
|
|
|
191
|
+
/**
|
|
192
|
+
* One plugin object every preset block references. ESLint only permits a
|
|
193
|
+
* plugin name to be redefined across configs when the value is the same
|
|
194
|
+
* object reference, and several blocks share file scopes (`src/**`, the
|
|
195
|
+
* manifest, `globals.css`), so a per-block plugin object would collide when
|
|
196
|
+
* the blocks merge.
|
|
197
|
+
*/
|
|
198
|
+
declare const pasikaPlugin: {
|
|
199
|
+
rules: {
|
|
200
|
+
"vulyk-docs": _eslint_json.JSONRuleDefinition;
|
|
201
|
+
"husky-hook": _eslint_json.JSONRuleDefinition;
|
|
202
|
+
"nextjs-stack": _eslint_json.JSONRuleDefinition;
|
|
203
|
+
"no-vulyk-dependency": _eslint_json.JSONRuleDefinition;
|
|
204
|
+
"exact-version": _eslint_json.JSONRuleDefinition;
|
|
205
|
+
"theme-reset": _eslint_css.CSSRuleDefinition;
|
|
206
|
+
"root-variables": _eslint_css.CSSRuleDefinition;
|
|
207
|
+
"apply-usage": _eslint_css.CSSRuleDefinition;
|
|
208
|
+
"base-layer-pair": _eslint_css.CSSRuleDefinition;
|
|
209
|
+
"stylesheet-ordering": _eslint_css.CSSRuleDefinition;
|
|
210
|
+
"css-variable-naming": _eslint_css.CSSRuleDefinition;
|
|
211
|
+
"custom-utility-apply": _eslint_css.CSSRuleDefinition;
|
|
212
|
+
"surface-utility": _eslint_css.CSSRuleDefinition;
|
|
213
|
+
"theme-variable-namespace": _eslint_css.CSSRuleDefinition;
|
|
214
|
+
"global-css-location": _eslint_css.CSSRuleDefinition;
|
|
215
|
+
"global-stylesheet": _eslint_css.CSSRuleDefinition;
|
|
216
|
+
"doc-kind-suffix": _eslint_markdown.MarkdownRuleDefinition;
|
|
217
|
+
"title-matches-file-name": _eslint_markdown.MarkdownRuleDefinition;
|
|
218
|
+
overview: _eslint_markdown.MarkdownRuleDefinition;
|
|
219
|
+
"guide-step-single-sentence": _eslint_markdown.MarkdownRuleDefinition;
|
|
220
|
+
"guide-step-single-link": _eslint_markdown.MarkdownRuleDefinition;
|
|
221
|
+
"guide-states-no-requirement": _eslint_markdown.MarkdownRuleDefinition;
|
|
222
|
+
"requirement-present": _eslint_markdown.MarkdownRuleDefinition;
|
|
223
|
+
"rule-paired-examples": _eslint_markdown.MarkdownRuleDefinition;
|
|
224
|
+
"example-heading-description": _eslint_markdown.MarkdownRuleDefinition;
|
|
225
|
+
"policy-no-examples": _eslint_markdown.MarkdownRuleDefinition;
|
|
226
|
+
"no-cross-document-link": _eslint_markdown.MarkdownRuleDefinition;
|
|
227
|
+
"reference-no-rfc-vocabulary": _eslint_markdown.MarkdownRuleDefinition;
|
|
228
|
+
"reference-block-headings": _eslint_markdown.MarkdownRuleDefinition;
|
|
229
|
+
"support-document-placement": _eslint_markdown.MarkdownRuleDefinition;
|
|
230
|
+
"no-template-prompt": _eslint_markdown.MarkdownRuleDefinition;
|
|
231
|
+
"guide-folder-entry-point": _eslint_markdown.MarkdownRuleDefinition;
|
|
232
|
+
"rfc-only-in-bullets": _eslint_markdown.MarkdownRuleDefinition;
|
|
233
|
+
"rfc-keyword-in-every-bullet": _eslint_markdown.MarkdownRuleDefinition;
|
|
234
|
+
"policy-subject-headings": _eslint_markdown.MarkdownRuleDefinition;
|
|
235
|
+
"guide-link-anchors": _eslint_markdown.MarkdownRuleDefinition;
|
|
236
|
+
"no-nested-how-to": _eslint_markdown.MarkdownRuleDefinition;
|
|
237
|
+
"glossary-term-linking": _eslint_markdown.MarkdownRuleDefinition;
|
|
238
|
+
"guide-mentions-documents": _eslint_markdown.MarkdownRuleDefinition;
|
|
239
|
+
"component-placement": eslint.Rule.RuleModule;
|
|
240
|
+
"application-structure": eslint.Rule.RuleModule;
|
|
241
|
+
"data-testid-case": eslint.Rule.RuleModule;
|
|
242
|
+
"jsx-hygiene": eslint.Rule.RuleModule;
|
|
243
|
+
"interactive-component": eslint.Rule.RuleModule;
|
|
244
|
+
"ui-state": eslint.Rule.RuleModule;
|
|
245
|
+
"no-mixed-concerns": eslint.Rule.RuleModule;
|
|
246
|
+
"no-arbitrary-tailwind": eslint.Rule.RuleModule;
|
|
247
|
+
"enforce-cn-merge": {
|
|
248
|
+
meta: {
|
|
249
|
+
schema: never[];
|
|
250
|
+
type: "problem";
|
|
251
|
+
docs: {
|
|
252
|
+
description: string;
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
create(context: eslint.Rule.RuleContext): {
|
|
256
|
+
JSXAttribute(node: JsxAttributeNode): void;
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
"cn-helper": eslint.Rule.RuleModule;
|
|
260
|
+
"enforce-cva-variant-props": eslint.Rule.RuleModule;
|
|
261
|
+
"cva-appearance-props": eslint.Rule.RuleModule;
|
|
262
|
+
"cva-boolean-variants": eslint.Rule.RuleModule;
|
|
263
|
+
"cross-feature-import": eslint.Rule.RuleModule;
|
|
264
|
+
"pure-function-extract": eslint.Rule.RuleModule;
|
|
265
|
+
"hook-complexity": eslint.Rule.RuleModule;
|
|
266
|
+
"locale-dotted-path": eslint.Rule.RuleModule;
|
|
267
|
+
"locales-location": eslint.Rule.RuleModule;
|
|
268
|
+
"hook-extraction": eslint.Rule.RuleModule;
|
|
269
|
+
"component-nesting": eslint.Rule.RuleModule;
|
|
270
|
+
"stay-flat": eslint.Rule.RuleModule;
|
|
271
|
+
"locale-placement": eslint.Rule.RuleModule;
|
|
272
|
+
"sole-state-owner": eslint.Rule.RuleModule;
|
|
273
|
+
"locale-key-shape": eslint.Rule.RuleModule;
|
|
274
|
+
"shared-style-dedup": eslint.Rule.RuleModule;
|
|
275
|
+
"repeated-structure": eslint.Rule.RuleModule;
|
|
276
|
+
"filename-case": eslint.Rule.RuleModule;
|
|
277
|
+
"import-boundaries": eslint.Rule.RuleModule;
|
|
278
|
+
"named-exports": eslint.Rule.RuleModule;
|
|
279
|
+
"support-file-placement": eslint.Rule.RuleModule;
|
|
280
|
+
"support-folder-shape": eslint.Rule.RuleModule;
|
|
281
|
+
"import-through-index": eslint.Rule.RuleModule;
|
|
282
|
+
"util-file-name": eslint.Rule.RuleModule;
|
|
283
|
+
"no-util-barrel": eslint.Rule.RuleModule;
|
|
284
|
+
"enforce-barrel-exports": eslint.Rule.RuleModule;
|
|
285
|
+
"config-extraction": eslint.Rule.RuleModule;
|
|
286
|
+
"value-extraction": eslint.Rule.RuleModule;
|
|
287
|
+
"type-extraction": eslint.Rule.RuleModule;
|
|
288
|
+
"zod-schema-validation": eslint.Rule.RuleModule;
|
|
289
|
+
"source-under-src": eslint.Rule.RuleModule;
|
|
290
|
+
"zirka-baseline": eslint.Rule.RuleModule;
|
|
291
|
+
};
|
|
292
|
+
};
|
|
188
293
|
/** Every rule id, as they appear in configuration and in lint output. */
|
|
189
294
|
declare const allPasikaRuleIds: string[];
|
|
190
295
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
296
|
+
* TypeScript app preset: the framework-agnostic baseline — the package.json
|
|
297
|
+
* manifest (incl. husky hook and vulyk requirements), the zirka configuration
|
|
298
|
+
* contract, the `src/**` TS/TSX source, and the documentation markdown rules.
|
|
299
|
+
* Use this for a plain TypeScript repository.
|
|
195
300
|
*/
|
|
196
|
-
declare const
|
|
301
|
+
declare const typescriptApp: Linter.Config[];
|
|
197
302
|
/**
|
|
198
|
-
*
|
|
199
|
-
* `
|
|
200
|
-
*
|
|
303
|
+
* Next.js app preset: the full adopted-to-the-framework stack. Anything in
|
|
304
|
+
* `typescriptApp` plus the framework-only blocks — the Next.js-stack manifest
|
|
305
|
+
* requirement, the Next.js app source rules, and the Tailwind stylesheet
|
|
306
|
+
* rules. `typescriptApp` is a strict subset of `nextjsApp`.
|
|
201
307
|
*/
|
|
202
|
-
declare const
|
|
308
|
+
declare const nextjsApp: Linter.Config[];
|
|
203
309
|
|
|
204
|
-
export { allPasikaRuleIds, documentationRules, huskyRules, nextPackageJsonRules,
|
|
310
|
+
export { allPasikaRuleIds, documentationRules, huskyRules, nextPackageJsonRules, nextjsApp, pasikaPlugin, pasikaRules, repoPackageJsonRules, tailwindRules, typescriptApp, vulykRules };
|
|
@@ -3266,43 +3266,88 @@ var sourceUnderSrcRule = {
|
|
|
3266
3266
|
}
|
|
3267
3267
|
};
|
|
3268
3268
|
|
|
3269
|
-
// eslint/rules/
|
|
3269
|
+
// eslint/rules/zirka-baseline.ts
|
|
3270
3270
|
import fs4 from "fs";
|
|
3271
3271
|
import path33 from "path";
|
|
3272
|
-
var
|
|
3272
|
+
var ESLINT_CONFIG = /^eslint\.config\.(?:ts|mts|cts|js|mjs|cjs)$/;
|
|
3273
|
+
var PRETTIER_CONFIGS = [
|
|
3274
|
+
"prettier.config.mjs",
|
|
3275
|
+
"prettier.config.cjs",
|
|
3276
|
+
"prettier.config.js",
|
|
3277
|
+
"prettier.config.ts",
|
|
3278
|
+
"prettier.config.mts",
|
|
3279
|
+
"prettier.config.cts"
|
|
3280
|
+
];
|
|
3281
|
+
var zirkaBaselineRule = {
|
|
3273
3282
|
meta: {
|
|
3274
3283
|
schema: [],
|
|
3275
3284
|
type: "problem",
|
|
3276
3285
|
docs: {
|
|
3277
|
-
description: "Require eslint
|
|
3286
|
+
description: "Require eslint, prettier, and TypeScript configuration to come from zirka."
|
|
3278
3287
|
}
|
|
3279
3288
|
},
|
|
3280
3289
|
create(context) {
|
|
3281
3290
|
const filename = path33.resolve(context.filename);
|
|
3282
3291
|
const basename = path33.basename(filename);
|
|
3283
|
-
if (
|
|
3292
|
+
if (!ESLINT_CONFIG.test(basename)) return {};
|
|
3293
|
+
const projectRoot = path33.dirname(filename);
|
|
3294
|
+
const report3 = (message) => {
|
|
3295
|
+
context.report({
|
|
3296
|
+
node: context.sourceCode.ast,
|
|
3297
|
+
loc: { line: 1, column: 0 },
|
|
3298
|
+
message
|
|
3299
|
+
});
|
|
3300
|
+
};
|
|
3284
3301
|
return {
|
|
3285
|
-
Program(
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
loc: { line: 1, column: 0 },
|
|
3291
|
-
message: "ESLint config must reference zirka. Use the pasika/zirka baseline configuration."
|
|
3292
|
-
});
|
|
3302
|
+
Program() {
|
|
3303
|
+
if (!referencesZirka(context.sourceCode)) {
|
|
3304
|
+
report3(
|
|
3305
|
+
'ESLint config must take its configuration from zirka (import { styleguide } from "zirka") instead of restating rules locally.'
|
|
3306
|
+
);
|
|
3293
3307
|
}
|
|
3294
|
-
const
|
|
3295
|
-
if (!fs4.existsSync(
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3308
|
+
const tsconfigPath = path33.join(projectRoot, "tsconfig.json");
|
|
3309
|
+
if (!fs4.existsSync(tsconfigPath)) {
|
|
3310
|
+
report3('No tsconfig.json found. Create one extending the zirka TypeScript base config ("zirka/typescript").');
|
|
3311
|
+
} else {
|
|
3312
|
+
let extendsValue;
|
|
3313
|
+
try {
|
|
3314
|
+
const parsed = JSON.parse(fs4.readFileSync(tsconfigPath, "utf8"));
|
|
3315
|
+
extendsValue = typeof parsed === "object" && parsed !== null && "extends" in parsed ? parsed.extends : void 0;
|
|
3316
|
+
} catch {
|
|
3317
|
+
report3(
|
|
3318
|
+
'tsconfig.json must be valid JSON and extend the zirka TypeScript base config ("zirka/typescript").'
|
|
3319
|
+
);
|
|
3320
|
+
return;
|
|
3321
|
+
}
|
|
3322
|
+
if (typeof extendsValue !== "string" || !extendsValue.startsWith("zirka")) {
|
|
3323
|
+
report3('tsconfig.json must extend the zirka TypeScript base config ("zirka/typescript").');
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs4.existsSync(path33.join(projectRoot, name)));
|
|
3327
|
+
if (!prettierConfigFile) {
|
|
3328
|
+
report3(
|
|
3329
|
+
"No prettier config found. Create one that takes its configuration from zirka (styleguide({ prettier: true }).prettierConfig)."
|
|
3330
|
+
);
|
|
3331
|
+
} else {
|
|
3332
|
+
const content = fs4.readFileSync(path33.join(projectRoot, prettierConfigFile), "utf8");
|
|
3333
|
+
if (!content.includes("zirka")) {
|
|
3334
|
+
report3(
|
|
3335
|
+
"The prettier config must take its configuration from zirka (styleguide({ prettier: true }).prettierConfig) instead of restating it locally."
|
|
3336
|
+
);
|
|
3337
|
+
}
|
|
3301
3338
|
}
|
|
3302
3339
|
}
|
|
3303
3340
|
};
|
|
3304
3341
|
}
|
|
3305
3342
|
};
|
|
3343
|
+
function referencesZirka(sourceCode) {
|
|
3344
|
+
for (const statement of sourceCode.ast.body) {
|
|
3345
|
+
if (statement.type === "ImportDeclaration" && typeof statement.source.value === "string" && statement.source.value.startsWith("zirka")) {
|
|
3346
|
+
return true;
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
return /require\(\s*["']zirka["']\s*\)/.test(sourceCode.getText());
|
|
3350
|
+
}
|
|
3306
3351
|
|
|
3307
3352
|
// constants/rfc2119.ts
|
|
3308
3353
|
var RFC_2119_KEYWORDS = ["MUST NOT", "MUST", "SHOULD NOT", "SHOULD", "MAY"];
|
|
@@ -3391,90 +3436,10 @@ var titleMatchesFileNameRule = {
|
|
|
3391
3436
|
}
|
|
3392
3437
|
};
|
|
3393
3438
|
|
|
3394
|
-
// eslint/rules/documentation/overview
|
|
3395
|
-
var overviewPresentRule = {
|
|
3396
|
-
meta: {
|
|
3397
|
-
type: "problem",
|
|
3398
|
-
docs: {
|
|
3399
|
-
description: "Overview must follow the title in a documentation file.",
|
|
3400
|
-
recommended: true
|
|
3401
|
-
}
|
|
3402
|
-
},
|
|
3403
|
-
create(context) {
|
|
3404
|
-
return {
|
|
3405
|
-
root(node) {
|
|
3406
|
-
const filename = getFilename(context);
|
|
3407
|
-
if (!filename.endsWith(".md")) return;
|
|
3408
|
-
let titleLine = 0;
|
|
3409
|
-
for (const child of node.children) {
|
|
3410
|
-
if (child.type === "heading" && child.depth === 1) {
|
|
3411
|
-
titleLine = getLine(child);
|
|
3412
|
-
break;
|
|
3413
|
-
}
|
|
3414
|
-
}
|
|
3415
|
-
if (!titleLine) return;
|
|
3416
|
-
let hasOverview = false;
|
|
3417
|
-
for (const child of node.children) {
|
|
3418
|
-
if (child.type === "paragraph" && getLine(child) > titleLine && getTextContent(child).trim()) {
|
|
3419
|
-
hasOverview = true;
|
|
3420
|
-
break;
|
|
3421
|
-
}
|
|
3422
|
-
}
|
|
3423
|
-
if (!hasOverview) {
|
|
3424
|
-
context.report({
|
|
3425
|
-
node,
|
|
3426
|
-
message: "no overview follows the title"
|
|
3427
|
-
});
|
|
3428
|
-
}
|
|
3429
|
-
}
|
|
3430
|
-
};
|
|
3431
|
-
}
|
|
3432
|
-
};
|
|
3433
|
-
|
|
3434
|
-
// eslint/rules/documentation/overview-length.ts
|
|
3439
|
+
// eslint/rules/documentation/overview.ts
|
|
3435
3440
|
function countSentences(text) {
|
|
3436
3441
|
return text.split(/[.!?](?:\s+|$)/).filter((part) => part.trim() !== "").length;
|
|
3437
3442
|
}
|
|
3438
|
-
var overviewLengthRule = {
|
|
3439
|
-
meta: {
|
|
3440
|
-
type: "problem",
|
|
3441
|
-
docs: {
|
|
3442
|
-
description: "Overview must contain at most two sentences.",
|
|
3443
|
-
recommended: true
|
|
3444
|
-
}
|
|
3445
|
-
},
|
|
3446
|
-
create(context) {
|
|
3447
|
-
return {
|
|
3448
|
-
root(node) {
|
|
3449
|
-
const filename = getFilename(context);
|
|
3450
|
-
if (!filename.endsWith(".md")) return;
|
|
3451
|
-
let titleLine = 0;
|
|
3452
|
-
for (const child of node.children) {
|
|
3453
|
-
if (child.type === "heading" && child.depth === 1) {
|
|
3454
|
-
titleLine = getLine(child);
|
|
3455
|
-
break;
|
|
3456
|
-
}
|
|
3457
|
-
}
|
|
3458
|
-
if (!titleLine) return;
|
|
3459
|
-
for (const child of node.children) {
|
|
3460
|
-
if (child.type !== "paragraph" || getLine(child) <= titleLine) continue;
|
|
3461
|
-
const text = getTextContent(child).trim();
|
|
3462
|
-
if (!text || text.startsWith("#")) continue;
|
|
3463
|
-
const sentenceCount = countSentences(text);
|
|
3464
|
-
if (sentenceCount > 2) {
|
|
3465
|
-
context.report({
|
|
3466
|
-
node: child,
|
|
3467
|
-
message: `overview uses ${String(sentenceCount)} sentences, at most two are allowed`
|
|
3468
|
-
});
|
|
3469
|
-
}
|
|
3470
|
-
break;
|
|
3471
|
-
}
|
|
3472
|
-
}
|
|
3473
|
-
};
|
|
3474
|
-
}
|
|
3475
|
-
};
|
|
3476
|
-
|
|
3477
|
-
// eslint/rules/documentation/guide-overview-no-links.ts
|
|
3478
3443
|
function containsLink(node) {
|
|
3479
3444
|
if (node.type === "link") return true;
|
|
3480
3445
|
if ("children" in node) {
|
|
@@ -3482,11 +3447,11 @@ function containsLink(node) {
|
|
|
3482
3447
|
}
|
|
3483
3448
|
return false;
|
|
3484
3449
|
}
|
|
3485
|
-
var
|
|
3450
|
+
var overviewRule = {
|
|
3486
3451
|
meta: {
|
|
3487
3452
|
type: "problem",
|
|
3488
3453
|
docs: {
|
|
3489
|
-
description: "
|
|
3454
|
+
description: "Overview must exist, contain at most two sentences, and not link to other documents.",
|
|
3490
3455
|
recommended: true
|
|
3491
3456
|
}
|
|
3492
3457
|
},
|
|
@@ -3494,7 +3459,7 @@ var guideOverviewNoLinksRule = {
|
|
|
3494
3459
|
return {
|
|
3495
3460
|
root(node) {
|
|
3496
3461
|
const filename = getFilename(context);
|
|
3497
|
-
if (!filename.endsWith("
|
|
3462
|
+
if (!filename.endsWith(".md")) return;
|
|
3498
3463
|
let titleLine = 0;
|
|
3499
3464
|
for (const child of node.children) {
|
|
3500
3465
|
if (child.type === "heading" && child.depth === 1) {
|
|
@@ -3503,17 +3468,31 @@ var guideOverviewNoLinksRule = {
|
|
|
3503
3468
|
}
|
|
3504
3469
|
}
|
|
3505
3470
|
if (!titleLine) return;
|
|
3471
|
+
let sentenceCount = 0;
|
|
3472
|
+
let found = false;
|
|
3506
3473
|
for (const child of node.children) {
|
|
3474
|
+
if (child.type === "heading" && getLine(child) > titleLine) break;
|
|
3507
3475
|
if (child.type !== "paragraph" || getLine(child) <= titleLine) continue;
|
|
3508
3476
|
const text = getTextContent(child).trim();
|
|
3509
3477
|
if (!text || text.startsWith("#")) continue;
|
|
3478
|
+
found = true;
|
|
3510
3479
|
if (containsLink(child)) {
|
|
3511
3480
|
context.report({
|
|
3512
3481
|
node: child,
|
|
3513
|
-
message: "
|
|
3482
|
+
message: "overview links another document"
|
|
3514
3483
|
});
|
|
3515
3484
|
}
|
|
3516
|
-
|
|
3485
|
+
sentenceCount += countSentences(text);
|
|
3486
|
+
if (sentenceCount > 2) {
|
|
3487
|
+
context.report({
|
|
3488
|
+
node: child,
|
|
3489
|
+
message: `overview uses ${String(sentenceCount)} sentences, at most two are allowed`
|
|
3490
|
+
});
|
|
3491
|
+
break;
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
if (!found) {
|
|
3495
|
+
context.report({ node, message: "no overview follows the title" });
|
|
3517
3496
|
}
|
|
3518
3497
|
}
|
|
3519
3498
|
};
|
|
@@ -4288,13 +4267,91 @@ var glossaryTermLinkingRule = {
|
|
|
4288
4267
|
}
|
|
4289
4268
|
};
|
|
4290
4269
|
|
|
4270
|
+
// eslint/rules/documentation/guide-mentions-documents.ts
|
|
4271
|
+
import { existsSync } from "fs";
|
|
4272
|
+
import path39 from "path";
|
|
4273
|
+
function visitSteps3(node, check) {
|
|
4274
|
+
if (node.type === "list" && node.ordered) {
|
|
4275
|
+
for (const child of node.children) check(child);
|
|
4276
|
+
}
|
|
4277
|
+
if ("children" in node) {
|
|
4278
|
+
for (const child of node.children) visitSteps3(child, check);
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
4281
|
+
function collectMarkdownLinks(node, out) {
|
|
4282
|
+
if (node.type === "link" && node.url.endsWith(".md")) out.push(node);
|
|
4283
|
+
if ("children" in node) {
|
|
4284
|
+
for (const child of node.children) collectMarkdownLinks(child, out);
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
function linkTarget(url) {
|
|
4288
|
+
return url.split("#")[0] ?? url;
|
|
4289
|
+
}
|
|
4290
|
+
var guideMentionsDocumentsRule = {
|
|
4291
|
+
meta: {
|
|
4292
|
+
type: "problem",
|
|
4293
|
+
docs: {
|
|
4294
|
+
description: "A Guide entry point must reference each owned Rule from a How To step, mention every Reference, and not link a missing document.",
|
|
4295
|
+
recommended: true
|
|
4296
|
+
}
|
|
4297
|
+
},
|
|
4298
|
+
create(context) {
|
|
4299
|
+
return {
|
|
4300
|
+
root(node) {
|
|
4301
|
+
const filename = getFilename(context);
|
|
4302
|
+
if (!filename.endsWith("-guide.md")) return;
|
|
4303
|
+
const docsRoot = findDocsRoot(filename);
|
|
4304
|
+
if (!docsRoot) return;
|
|
4305
|
+
const guideDir = path39.dirname(filename);
|
|
4306
|
+
if (path39.basename(filename, ".md") !== path39.basename(guideDir)) return;
|
|
4307
|
+
const docs = getProjectDocs(docsRoot);
|
|
4308
|
+
const owned = docs.filter((doc) => {
|
|
4309
|
+
const parent = path39.dirname(doc.filePath);
|
|
4310
|
+
return parent === path39.join(guideDir, "rules") || parent === path39.join(guideDir, "references");
|
|
4311
|
+
});
|
|
4312
|
+
const allLinks = [];
|
|
4313
|
+
collectMarkdownLinks(node, allLinks);
|
|
4314
|
+
const stepLinks = [];
|
|
4315
|
+
visitSteps3(node, (item) => {
|
|
4316
|
+
collectMarkdownLinks(item, stepLinks);
|
|
4317
|
+
});
|
|
4318
|
+
const mentionsOf = (links, fileName) => links.some((link) => link.url.split("/").pop() === fileName);
|
|
4319
|
+
for (const doc of owned) {
|
|
4320
|
+
if (doc.kind === "rule") {
|
|
4321
|
+
if (!mentionsOf(stepLinks, doc.fileName)) {
|
|
4322
|
+
context.report({
|
|
4323
|
+
node,
|
|
4324
|
+
message: `Guide entry point does not reference a Rule it owns from a How To step: ${doc.doc}`
|
|
4325
|
+
});
|
|
4326
|
+
}
|
|
4327
|
+
} else if (!mentionsOf(allLinks, doc.fileName)) {
|
|
4328
|
+
context.report({
|
|
4329
|
+
node,
|
|
4330
|
+
message: `Guide entry point does not mention a document it owns: ${doc.doc}`
|
|
4331
|
+
});
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
for (const link of allLinks) {
|
|
4335
|
+
const target = linkTarget(link.url);
|
|
4336
|
+
if (!target.endsWith(".md")) continue;
|
|
4337
|
+
const resolved = path39.normalize(path39.join(guideDir, target));
|
|
4338
|
+
if (!existsSync(resolved)) {
|
|
4339
|
+
context.report({
|
|
4340
|
+
node: link,
|
|
4341
|
+
message: `Guide links a document that does not exist: ${link.url}`
|
|
4342
|
+
});
|
|
4343
|
+
}
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
};
|
|
4347
|
+
}
|
|
4348
|
+
};
|
|
4349
|
+
|
|
4291
4350
|
// eslint/rules/documentation/index.ts
|
|
4292
4351
|
var documentationRules = {
|
|
4293
4352
|
"doc-kind-suffix": docKindSuffixRule,
|
|
4294
4353
|
"title-matches-file-name": titleMatchesFileNameRule,
|
|
4295
|
-
|
|
4296
|
-
"overview-length": overviewLengthRule,
|
|
4297
|
-
"guide-overview-no-links": guideOverviewNoLinksRule,
|
|
4354
|
+
overview: overviewRule,
|
|
4298
4355
|
"guide-step-single-sentence": guideStepSingleSentenceRule,
|
|
4299
4356
|
"guide-step-single-link": guideStepSingleLinkRule,
|
|
4300
4357
|
"guide-states-no-requirement": guideStatesNoRequirementRule,
|
|
@@ -4313,7 +4370,8 @@ var documentationRules = {
|
|
|
4313
4370
|
"policy-subject-headings": policySubjectHeadingsRule,
|
|
4314
4371
|
"guide-link-anchors": guideLinkAnchorsRule,
|
|
4315
4372
|
"no-nested-how-to": noNestedHowToRule,
|
|
4316
|
-
"glossary-term-linking": glossaryTermLinkingRule
|
|
4373
|
+
"glossary-term-linking": glossaryTermLinkingRule,
|
|
4374
|
+
"guide-mentions-documents": guideMentionsDocumentsRule
|
|
4317
4375
|
};
|
|
4318
4376
|
|
|
4319
4377
|
// eslint/rules/tailwind/helpers.ts
|
|
@@ -4940,8 +4998,8 @@ var nextPackageJsonRules = {
|
|
|
4940
4998
|
};
|
|
4941
4999
|
|
|
4942
5000
|
// eslint/rules/husky/husky-hook.ts
|
|
4943
|
-
import { existsSync, readFileSync as readFileSync6 } from "fs";
|
|
4944
|
-
import
|
|
5001
|
+
import { existsSync as existsSync2, readFileSync as readFileSync6 } from "fs";
|
|
5002
|
+
import path40 from "path";
|
|
4945
5003
|
function memberName4(member) {
|
|
4946
5004
|
return member.name.type === "String" ? member.name.value : member.name.name;
|
|
4947
5005
|
}
|
|
@@ -4960,8 +5018,8 @@ var huskyHookRule = {
|
|
|
4960
5018
|
if (root.type !== "Object") return;
|
|
4961
5019
|
const scriptName = context.filename;
|
|
4962
5020
|
if (!scriptName.endsWith("package.json")) return;
|
|
4963
|
-
const hookPath =
|
|
4964
|
-
if (!
|
|
5021
|
+
const hookPath = path40.join(process.cwd(), ".husky", "pre-commit");
|
|
5022
|
+
if (!existsSync2(hookPath)) {
|
|
4965
5023
|
context.report({
|
|
4966
5024
|
node,
|
|
4967
5025
|
message: "No .husky/pre-commit hook found. Configure husky to run checks before commits."
|
|
@@ -4998,8 +5056,57 @@ var huskyRules = {
|
|
|
4998
5056
|
"husky-hook": huskyHookRule
|
|
4999
5057
|
};
|
|
5000
5058
|
|
|
5059
|
+
// eslint/rules/vulyk/vulyk-docs.ts
|
|
5060
|
+
import { existsSync as existsSync3, readFileSync as readFileSync7 } from "fs";
|
|
5061
|
+
import path41 from "path";
|
|
5062
|
+
var PASIKA_REPO = "Bredansky/pasika";
|
|
5063
|
+
var vulykDocsRule = {
|
|
5064
|
+
meta: {
|
|
5065
|
+
schema: [],
|
|
5066
|
+
type: "problem",
|
|
5067
|
+
docs: {
|
|
5068
|
+
description: "Require vulyk.config.ts to track the framework's docs from pasika and the generated AGENTS.md."
|
|
5069
|
+
}
|
|
5070
|
+
},
|
|
5071
|
+
create(context) {
|
|
5072
|
+
return {
|
|
5073
|
+
Document(node) {
|
|
5074
|
+
if (!context.filename.endsWith("package.json")) return;
|
|
5075
|
+
const projectRoot = path41.dirname(path41.resolve(context.filename));
|
|
5076
|
+
const configPath = path41.join(projectRoot, "vulyk.config.ts");
|
|
5077
|
+
if (!existsSync3(configPath)) {
|
|
5078
|
+
context.report({
|
|
5079
|
+
node,
|
|
5080
|
+
message: "No vulyk.config.ts found. Run npx vulyk@latest init to create one that tracks the framework's docs."
|
|
5081
|
+
});
|
|
5082
|
+
return;
|
|
5083
|
+
}
|
|
5084
|
+
const config = readFileSync7(configPath, "utf8");
|
|
5085
|
+
if (!config.includes(PASIKA_REPO)) {
|
|
5086
|
+
context.report({
|
|
5087
|
+
node,
|
|
5088
|
+
message: "vulyk.config.ts must track the framework's docs from the pasika repository."
|
|
5089
|
+
});
|
|
5090
|
+
}
|
|
5091
|
+
const agentsPath = path41.join(projectRoot, "AGENTS.md");
|
|
5092
|
+
if (!existsSync3(agentsPath)) {
|
|
5093
|
+
context.report({
|
|
5094
|
+
node,
|
|
5095
|
+
message: "No AGENTS.md found. Run npx vulyk@latest agents to generate the agent file that routes to the tracked docs."
|
|
5096
|
+
});
|
|
5097
|
+
}
|
|
5098
|
+
}
|
|
5099
|
+
};
|
|
5100
|
+
}
|
|
5101
|
+
};
|
|
5102
|
+
|
|
5103
|
+
// eslint/rules/vulyk/index.ts
|
|
5104
|
+
var vulykRules = {
|
|
5105
|
+
"vulyk-docs": vulykDocsRule
|
|
5106
|
+
};
|
|
5107
|
+
|
|
5001
5108
|
// eslint/index.ts
|
|
5002
|
-
var
|
|
5109
|
+
var typescriptAppRules = {
|
|
5003
5110
|
"filename-case": filenameCaseRule,
|
|
5004
5111
|
"import-boundaries": importBoundariesRule,
|
|
5005
5112
|
"named-exports": namedExportsRule,
|
|
@@ -5014,9 +5121,9 @@ var repoSourceRules = {
|
|
|
5014
5121
|
"type-extraction": typeExtractionRule,
|
|
5015
5122
|
"zod-schema-validation": zodSchemaValidationRule,
|
|
5016
5123
|
"source-under-src": sourceUnderSrcRule,
|
|
5017
|
-
"
|
|
5124
|
+
"zirka-baseline": zirkaBaselineRule
|
|
5018
5125
|
};
|
|
5019
|
-
var
|
|
5126
|
+
var nextjsAppRules = {
|
|
5020
5127
|
"component-placement": componentPlacementRule,
|
|
5021
5128
|
"application-structure": applicationStructureRule,
|
|
5022
5129
|
"data-testid-case": dataTestIdCaseRule,
|
|
@@ -5045,38 +5152,61 @@ var nextSourceRules = {
|
|
|
5045
5152
|
"repeated-structure": repeatedStructureRule
|
|
5046
5153
|
};
|
|
5047
5154
|
var pasikaRules = {
|
|
5048
|
-
...
|
|
5049
|
-
...
|
|
5155
|
+
...typescriptAppRules,
|
|
5156
|
+
...nextjsAppRules
|
|
5050
5157
|
};
|
|
5158
|
+
var pasikaPlugin = {
|
|
5159
|
+
rules: {
|
|
5160
|
+
...pasikaRules,
|
|
5161
|
+
...documentationRules,
|
|
5162
|
+
...tailwindRules,
|
|
5163
|
+
...repoPackageJsonRules,
|
|
5164
|
+
...nextPackageJsonRules,
|
|
5165
|
+
...huskyRules,
|
|
5166
|
+
...vulykRules
|
|
5167
|
+
}
|
|
5168
|
+
};
|
|
5169
|
+
var jsonLanguagePlugin = { languages: { json: jsonPlugin.languages.json } };
|
|
5051
5170
|
function ruleIds(rules2) {
|
|
5052
5171
|
return Object.keys(rules2).map((name) => `pasika/${name}`);
|
|
5053
5172
|
}
|
|
5173
|
+
var typescriptAppRuleIds = ruleIds(typescriptAppRules);
|
|
5174
|
+
var nextjsAppRuleIds = ruleIds(nextjsAppRules);
|
|
5054
5175
|
var pasikaRuleIds = ruleIds(pasikaRules);
|
|
5055
5176
|
var documentationRuleIds = ruleIds(documentationRules);
|
|
5056
5177
|
var tailwindRuleIds = ruleIds(tailwindRules);
|
|
5057
5178
|
var repoPackageJsonRuleIds = ruleIds(repoPackageJsonRules);
|
|
5058
5179
|
var nextPackageJsonRuleIds = ruleIds(nextPackageJsonRules);
|
|
5059
5180
|
var huskyRuleIds = ruleIds(huskyRules);
|
|
5181
|
+
var vulykRuleIds = ruleIds(vulykRules);
|
|
5060
5182
|
var allPasikaRuleIds = [
|
|
5061
5183
|
...pasikaRuleIds,
|
|
5062
5184
|
...documentationRuleIds,
|
|
5063
5185
|
...tailwindRuleIds,
|
|
5064
5186
|
...repoPackageJsonRuleIds,
|
|
5065
5187
|
...nextPackageJsonRuleIds,
|
|
5066
|
-
...huskyRuleIds
|
|
5188
|
+
...huskyRuleIds,
|
|
5189
|
+
...vulykRuleIds
|
|
5067
5190
|
];
|
|
5068
|
-
var
|
|
5191
|
+
var typescriptAppBlock = {
|
|
5192
|
+
files: ["src/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}"],
|
|
5193
|
+
plugins: {
|
|
5194
|
+
pasika: pasikaPlugin
|
|
5195
|
+
},
|
|
5196
|
+
rules: Object.fromEntries(typescriptAppRuleIds.map((id) => [id, "error"]))
|
|
5197
|
+
};
|
|
5198
|
+
var nextjsAppBlock = {
|
|
5069
5199
|
files: ["src/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}"],
|
|
5070
5200
|
plugins: {
|
|
5071
|
-
pasika:
|
|
5201
|
+
pasika: pasikaPlugin
|
|
5072
5202
|
},
|
|
5073
|
-
rules: Object.fromEntries(
|
|
5203
|
+
rules: Object.fromEntries(nextjsAppRuleIds.map((id) => [id, "error"]))
|
|
5074
5204
|
};
|
|
5075
5205
|
var tailwindGlobalsBlock = {
|
|
5076
5206
|
files: ["src/**/globals.css"],
|
|
5077
5207
|
plugins: {
|
|
5078
5208
|
css,
|
|
5079
|
-
pasika:
|
|
5209
|
+
pasika: pasikaPlugin
|
|
5080
5210
|
},
|
|
5081
5211
|
language: "css/css",
|
|
5082
5212
|
languageOptions: { tolerant: true },
|
|
@@ -5088,43 +5218,63 @@ var tailwindAnyCssBlock = {
|
|
|
5088
5218
|
files: ["src/**/*.css"],
|
|
5089
5219
|
plugins: {
|
|
5090
5220
|
css,
|
|
5091
|
-
pasika:
|
|
5221
|
+
pasika: pasikaPlugin
|
|
5092
5222
|
},
|
|
5093
5223
|
language: "css/css",
|
|
5094
5224
|
languageOptions: { tolerant: true },
|
|
5095
5225
|
rules: { "pasika/global-css-location": "error" }
|
|
5096
5226
|
};
|
|
5097
|
-
var
|
|
5227
|
+
var repoManifestBlock = {
|
|
5098
5228
|
files: ["package.json"],
|
|
5099
5229
|
plugins: {
|
|
5100
|
-
json:
|
|
5101
|
-
pasika:
|
|
5230
|
+
json: jsonLanguagePlugin,
|
|
5231
|
+
pasika: pasikaPlugin
|
|
5102
5232
|
},
|
|
5103
5233
|
language: "json/json",
|
|
5104
|
-
rules: Object.fromEntries(
|
|
5105
|
-
|
|
5106
|
-
|
|
5234
|
+
rules: Object.fromEntries([...repoPackageJsonRuleIds, ...huskyRuleIds, ...vulykRuleIds].map((id) => [id, "error"]))
|
|
5235
|
+
};
|
|
5236
|
+
var nextManifestBlock = {
|
|
5237
|
+
files: ["package.json"],
|
|
5238
|
+
plugins: {
|
|
5239
|
+
json: jsonLanguagePlugin,
|
|
5240
|
+
pasika: pasikaPlugin
|
|
5241
|
+
},
|
|
5242
|
+
language: "json/json",
|
|
5243
|
+
rules: Object.fromEntries(nextPackageJsonRuleIds.map((id) => [id, "error"]))
|
|
5244
|
+
};
|
|
5245
|
+
var zirkaBlock = {
|
|
5246
|
+
files: ["eslint.config.{ts,mts,cts,js,mjs,cjs}"],
|
|
5247
|
+
plugins: { pasika: pasikaPlugin },
|
|
5248
|
+
rules: { "pasika/zirka-baseline": "error" }
|
|
5107
5249
|
};
|
|
5108
5250
|
var docsBlock = {
|
|
5109
5251
|
files: ["docs/**/*.md"],
|
|
5110
5252
|
ignores: ["**/_*/**"],
|
|
5111
5253
|
plugins: {
|
|
5112
5254
|
markdown,
|
|
5113
|
-
pasika:
|
|
5255
|
+
pasika: pasikaPlugin
|
|
5114
5256
|
},
|
|
5115
5257
|
language: "markdown/gfm",
|
|
5116
5258
|
rules: Object.fromEntries(documentationRuleIds.map((id) => [id, "error"]))
|
|
5117
5259
|
};
|
|
5118
|
-
var
|
|
5119
|
-
var
|
|
5260
|
+
var typescriptApp = [repoManifestBlock, zirkaBlock, typescriptAppBlock, docsBlock];
|
|
5261
|
+
var nextjsApp = [
|
|
5262
|
+
...typescriptApp,
|
|
5263
|
+
nextManifestBlock,
|
|
5264
|
+
nextjsAppBlock,
|
|
5265
|
+
tailwindGlobalsBlock,
|
|
5266
|
+
tailwindAnyCssBlock
|
|
5267
|
+
];
|
|
5120
5268
|
export {
|
|
5121
5269
|
allPasikaRuleIds,
|
|
5122
5270
|
documentationRules,
|
|
5123
5271
|
huskyRules,
|
|
5124
5272
|
nextPackageJsonRules,
|
|
5125
|
-
|
|
5126
|
-
|
|
5273
|
+
nextjsApp,
|
|
5274
|
+
pasikaPlugin,
|
|
5127
5275
|
pasikaRules,
|
|
5128
5276
|
repoPackageJsonRules,
|
|
5129
|
-
tailwindRules
|
|
5277
|
+
tailwindRules,
|
|
5278
|
+
typescriptApp,
|
|
5279
|
+
vulykRules
|
|
5130
5280
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pasika",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Reusable agent setup package",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"tsup": "8.5.1",
|
|
52
52
|
"tsx": "4.23.12",
|
|
53
53
|
"typescript": "6.0.3",
|
|
54
|
-
"zirka": "0.0.
|
|
54
|
+
"zirka": "0.0.47",
|
|
55
55
|
"zod": "4.4.3"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|