pasika 0.3.5 → 0.3.7
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 +61 -61
- package/dist/eslint/pasika/index.d.ts +3 -2
- package/dist/eslint/pasika/index.js +120 -72
- package/package.json +11 -12
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +0 -5444
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Documentation, the lint rules derived from it, and the CLI that applies and diag
|
|
|
8
8
|
|
|
9
9
|
```text
|
|
10
10
|
docs/
|
|
11
|
-
|
|
11
|
+
repository-policy.md # agent-conduct requirements (Policy)
|
|
12
12
|
repository-policy.md # repo-wide code and documentation requirements (Policy)
|
|
13
13
|
code-organization-guide/ # placement, extraction, module conventions
|
|
14
14
|
documentation-guide/ # how documents themselves are written
|
|
@@ -38,7 +38,7 @@ Requirements are identified by a hash of their canonical text, not by a hand-wri
|
|
|
38
38
|
"text": "A .tsx file that defines a component MUST contain exactly one component.",
|
|
39
39
|
"hash": "b19fe3bd34",
|
|
40
40
|
"ref": "pasika/no-mixed-concerns",
|
|
41
|
-
"note": "counts exported components; a second component that is not exported is not detected"
|
|
41
|
+
"note": "counts exported components; a second component that is not exported is not detected",
|
|
42
42
|
}
|
|
43
43
|
```
|
|
44
44
|
|
|
@@ -101,74 +101,74 @@ export default [pasikaConfig];
|
|
|
101
101
|
|
|
102
102
|
### TS/TSX rules
|
|
103
103
|
|
|
104
|
-
| Rule
|
|
105
|
-
|
|
|
106
|
-
| `pasika/filename-case`
|
|
107
|
-
| `pasika/import-boundaries`
|
|
108
|
-
| `pasika/no-mixed-concerns`
|
|
109
|
-
| `pasika/no-arbitrary-tailwind`
|
|
110
|
-
| `pasika/enforce-cn-merge`
|
|
111
|
-
| `pasika/enforce-cva-variant-props` | `VariantProps<typeof …>` instead of hand-written unions
|
|
112
|
-
| `pasika/cva-appearance-props`
|
|
113
|
-
| `pasika/cva-boolean-variants`
|
|
114
|
-
| `pasika/enforce-barrel-exports`
|
|
115
|
-
| `pasika/component-placement` †
|
|
116
|
-
| `pasika/support-file-placement` †
|
|
117
|
-
| `pasika/application-structure`
|
|
118
|
-
| `pasika/named-exports`
|
|
119
|
-
| `pasika/data-testid-case`
|
|
120
|
-
| `pasika/support-folder-shape`
|
|
121
|
-
| `pasika/import-through-index`
|
|
122
|
-
| `pasika/util-file-name`
|
|
123
|
-
| `pasika/no-util-barrel`
|
|
124
|
-
| `pasika/jsx-hygiene`
|
|
125
|
-
| `pasika/interactive-component`
|
|
126
|
-
| `pasika/ui-state`
|
|
127
|
-
| `pasika/cross-feature-import`
|
|
128
|
-
| `pasika/pure-function-extract`
|
|
129
|
-
| `pasika/hook-complexity`
|
|
130
|
-
| `pasika/locale-dotted-path`
|
|
131
|
-
| `pasika/locales-location`
|
|
132
|
-
| `pasika/hook-extraction` †
|
|
133
|
-
| `pasika/value-extraction` †
|
|
134
|
-
| `pasika/config-extraction` †
|
|
135
|
-
| `pasika/component-nesting` †
|
|
136
|
-
| `pasika/stay-flat` †
|
|
137
|
-
| `pasika/type-extraction` †
|
|
138
|
-
| `pasika/locale-placement` †
|
|
139
|
-
| `pasika/locale-key-shape`
|
|
140
|
-
| `pasika/shared-style-dedup` †
|
|
141
|
-
| `pasika/repeated-structure`
|
|
142
|
-
| `pasika/sole-state-owner`
|
|
143
|
-
| `pasika/no-eslint-disable`
|
|
144
|
-
| `pasika/zod-schema-validation`
|
|
145
|
-
| `pasika/source-under-src`
|
|
104
|
+
| Rule | Enforces |
|
|
105
|
+
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
106
|
+
| `pasika/filename-case` | kebab-case for files that define no component |
|
|
107
|
+
| `pasika/import-boundaries` | The shorter of the relative path and the `@/*` alias, and the layer boundaries |
|
|
108
|
+
| `pasika/no-mixed-concerns` | One exported React component per `.tsx` file |
|
|
109
|
+
| `pasika/no-arbitrary-tailwind` | No arbitrary `-[value]` classes, including inside `cn()` conditionals |
|
|
110
|
+
| `pasika/enforce-cn-merge` | `cn()` instead of `+` or template literals; at most five classes per group |
|
|
111
|
+
| `pasika/enforce-cva-variant-props` | `VariantProps<typeof …>` instead of hand-written unions |
|
|
112
|
+
| `pasika/cva-appearance-props` | Visual option props (`size`, `variant`, …) declared through CVA appearance props |
|
|
113
|
+
| `pasika/cva-boolean-variants` | Boolean appearance props placed on the CVA variant, not as standalone props |
|
|
114
|
+
| `pasika/enforce-barrel-exports` | A nested `index.ts` re-exports only its component |
|
|
115
|
+
| `pasika/component-placement` † | The folder a component's consumers imply |
|
|
116
|
+
| `pasika/support-file-placement` † | The folder a hook, type, schema, constant, or utility belongs in |
|
|
117
|
+
| `pasika/application-structure` | The path-based parts of the application structure and configuration rules |
|
|
118
|
+
| `pasika/named-exports` | Named exports for application files (framework routing files may default-export) |
|
|
119
|
+
| `pasika/data-testid-case` | `data-testid` casing where a rendered result carries one |
|
|
120
|
+
| `pasika/support-folder-shape` | A `constants/`, `types/`, or `schemas/` folder defines exports directly in `index.ts` or in named-re-exported files |
|
|
121
|
+
| `pasika/import-through-index` | An extracted constant, type, or schema is imported through its folder's `index.ts` |
|
|
122
|
+
| `pasika/util-file-name` | A single-function utility file is named in the function's kebab-case form |
|
|
123
|
+
| `pasika/no-util-barrel` | A utility is imported directly, without a barrel |
|
|
124
|
+
| `pasika/jsx-hygiene` | Calculations and complex conditions stay out of JSX children and attributes |
|
|
125
|
+
| `pasika/interactive-component` | Interactive elements are component boundaries when mixed with other content |
|
|
126
|
+
| `pasika/ui-state` | Native or ARIA state expression and Tailwind state variants |
|
|
127
|
+
| `pasika/cross-feature-import` | A component importing from two or more feature folders lives in a shared location |
|
|
128
|
+
| `pasika/pure-function-extract` | Pure functions extracted to `utils/`, even with one consumer |
|
|
129
|
+
| `pasika/hook-complexity` | Hook complexity limits (imperative categories per hook) |
|
|
130
|
+
| `pasika/locale-dotted-path` | A namespaced locale is read through its full dotted path |
|
|
131
|
+
| `pasika/locales-location` | Locales live in the named locales object |
|
|
132
|
+
| `pasika/hook-extraction` † | A hook with two or more consumers is extracted to its own file |
|
|
133
|
+
| `pasika/value-extraction` † | A value with cross-folder consumers is extracted |
|
|
134
|
+
| `pasika/config-extraction` † | A type, schema, or utility used outside its config module is moved |
|
|
135
|
+
| `pasika/component-nesting` † | A component is not nested solely because it has support files |
|
|
136
|
+
| `pasika/stay-flat` † | A component stays flat until it has exclusive children |
|
|
137
|
+
| `pasika/type-extraction` † | A type or schema with cross-folder consumers is extracted |
|
|
138
|
+
| `pasika/locale-placement` † | Shared locales at the top level, single-feature locales namespaced |
|
|
139
|
+
| `pasika/locale-key-shape` | camelCase locale keys; keys over 30 characters end in a WAI-ARIA element role |
|
|
140
|
+
| `pasika/shared-style-dedup` † | A className combo used by two or more components becomes a named utility |
|
|
141
|
+
| `pasika/repeated-structure` | A block of elements repeated two or more times is extracted as a named component |
|
|
142
|
+
| `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 |
|
|
144
|
+
| `pasika/zod-schema-validation` | Runtime validation through Zod schemas, not hand-written type guards |
|
|
145
|
+
| `pasika/source-under-src` | Application source lives under `src/`, not in root-level folders |
|
|
146
146
|
|
|
147
147
|
### CSS rules
|
|
148
148
|
|
|
149
149
|
Applied to `src/**/globals.css` (and other stylesheets) through `@eslint/css` with tolerant Tailwind v4 parsing.
|
|
150
150
|
|
|
151
|
-
| Rule
|
|
152
|
-
|
|
|
153
|
-
| `pasika/theme-reset`
|
|
154
|
-
| `pasika/root-variables`
|
|
155
|
-
| `pasika/apply-usage`
|
|
156
|
-
| `pasika/base-layer-pair`
|
|
157
|
-
| `pasika/stylesheet-ordering`
|
|
158
|
-
| `pasika/css-variable-naming`
|
|
159
|
-
| `pasika/custom-utility-apply`
|
|
160
|
-
| `pasika/surface-utility`
|
|
161
|
-
| `pasika/theme-variable-namespace` | Utility class groups share a namespace prefix
|
|
162
|
-
| `pasika/global-css-location`
|
|
151
|
+
| Rule | Enforces |
|
|
152
|
+
| --------------------------------- | ----------------------------------------------------------------------------- |
|
|
153
|
+
| `pasika/theme-reset` | A `--*: initial` theme reset is present |
|
|
154
|
+
| `pasika/root-variables` | `:root` defines the CSS custom properties |
|
|
155
|
+
| `pasika/apply-usage` | `@layer base` uses `@apply` for declarations |
|
|
156
|
+
| `pasika/base-layer-pair` | The base layer applies `base-canvas` and `base-ink` |
|
|
157
|
+
| `pasika/stylesheet-ordering` | Imports → `@custom-variant` → `:root` → `@theme` → `@utility` → `@layer base` |
|
|
158
|
+
| `pasika/css-variable-naming` | Background vars named `--<role>-canvas`, text vars `--<role>-ink` |
|
|
159
|
+
| `pasika/custom-utility-apply` | `@utility` blocks use `@apply` |
|
|
160
|
+
| `pasika/surface-utility` | Repeated canvas+ink combos become a named surface utility |
|
|
161
|
+
| `pasika/theme-variable-namespace` | Utility class groups share a namespace prefix |
|
|
162
|
+
| `pasika/global-css-location` | Global CSS lives in the correct entry point |
|
|
163
163
|
|
|
164
164
|
### JSON rules
|
|
165
165
|
|
|
166
166
|
Applied to `package.json` through `@eslint/json`.
|
|
167
167
|
|
|
168
|
-
| Rule
|
|
169
|
-
|
|
|
170
|
-
| `pasika/no-cache-flag`
|
|
171
|
-
| `pasika/no-vulyk-dependency` | `vulyk` is not in `dependencies`
|
|
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` |
|
|
172
172
|
|
|
173
173
|
### Documentation rules
|
|
174
174
|
|
|
@@ -180,7 +180,7 @@ Run `pasika coverage --json` for the exact requirement each rule covers.
|
|
|
180
180
|
|
|
181
181
|
Where a component, hook, value, type, or style belongs depends on which files use it, so the rules marked † index the whole `src/` tree instead of looking at one file. Two consequences:
|
|
182
182
|
|
|
183
|
-
- **Do not pass `--cache`.** Move a file and the finding belongs to a
|
|
183
|
+
- **Do not pass `--cache`.** Move a file and the finding belongs to a _different_ file, whose cache entry is unchanged — so ESLint would replay a stale verdict. `repository-policy.md` requires lint commands to run without it.
|
|
184
184
|
- The index is read from disk rather than from ESLint's file list, so a partial run such as `lint-staged` still judges against the true graph.
|
|
185
185
|
|
|
186
186
|
All are inert in a repository with no `src/` tree.
|
|
@@ -112,11 +112,12 @@ declare const cssRules: {
|
|
|
112
112
|
"surface-utility": _eslint_css.CSSRuleDefinition;
|
|
113
113
|
"theme-variable-namespace": _eslint_css.CSSRuleDefinition;
|
|
114
114
|
"global-css-location": _eslint_css.CSSRuleDefinition;
|
|
115
|
+
"global-stylesheet": _eslint_css.CSSRuleDefinition;
|
|
115
116
|
};
|
|
116
117
|
|
|
117
118
|
declare const jsonRules: {
|
|
118
|
-
"no-cache-flag": _eslint_json.JSONRuleDefinition;
|
|
119
119
|
"no-vulyk-dependency": _eslint_json.JSONRuleDefinition;
|
|
120
|
+
"zirka-installed": _eslint_json.JSONRuleDefinition;
|
|
120
121
|
};
|
|
121
122
|
|
|
122
123
|
declare const pasikaRules: {
|
|
@@ -168,9 +169,9 @@ declare const pasikaRules: {
|
|
|
168
169
|
"locale-key-shape": eslint.Rule.RuleModule;
|
|
169
170
|
"shared-style-dedup": eslint.Rule.RuleModule;
|
|
170
171
|
"repeated-structure": eslint.Rule.RuleModule;
|
|
171
|
-
"no-eslint-disable": eslint.Rule.RuleModule;
|
|
172
172
|
"zod-schema-validation": eslint.Rule.RuleModule;
|
|
173
173
|
"source-under-src": eslint.Rule.RuleModule;
|
|
174
|
+
"config-baseline": eslint.Rule.RuleModule;
|
|
174
175
|
};
|
|
175
176
|
|
|
176
177
|
/** Rule ids as they appear in configuration and in lint output. */
|
|
@@ -3087,31 +3087,6 @@ var repeatedStructureRule = {
|
|
|
3087
3087
|
}
|
|
3088
3088
|
};
|
|
3089
3089
|
|
|
3090
|
-
// eslint/pasika/rules/no-eslint-disable.ts
|
|
3091
|
-
var noEslintDisableRule = {
|
|
3092
|
-
meta: {
|
|
3093
|
-
schema: [],
|
|
3094
|
-
type: "problem",
|
|
3095
|
-
docs: {
|
|
3096
|
-
description: "Forbid eslint-disable directives."
|
|
3097
|
-
}
|
|
3098
|
-
},
|
|
3099
|
-
create(context) {
|
|
3100
|
-
return {
|
|
3101
|
-
Program() {
|
|
3102
|
-
for (const comment of context.sourceCode.getAllComments()) {
|
|
3103
|
-
if (!comment.value.includes("eslint-disable")) continue;
|
|
3104
|
-
if (!comment.loc) continue;
|
|
3105
|
-
context.report({
|
|
3106
|
-
loc: comment.loc,
|
|
3107
|
-
message: "Do not use eslint-disable directives; fix the reported violation instead."
|
|
3108
|
-
});
|
|
3109
|
-
}
|
|
3110
|
-
}
|
|
3111
|
-
};
|
|
3112
|
-
}
|
|
3113
|
-
};
|
|
3114
|
-
|
|
3115
3090
|
// eslint/pasika/rules/zod-schema-validation.ts
|
|
3116
3091
|
var NON_SCHEMA_OBJECTS = /* @__PURE__ */ new Set(["JSON", "Date", "Number"]);
|
|
3117
3092
|
var SCHEMA_METHODS = /* @__PURE__ */ new Set(["safeParse", "parse", "assert"]);
|
|
@@ -3168,7 +3143,7 @@ var zodSchemaValidationRule = {
|
|
|
3168
3143
|
if (delegatesToSchema(node.body)) return;
|
|
3169
3144
|
context.report({
|
|
3170
3145
|
loc: node.loc ?? { line: 1, column: 0 },
|
|
3171
|
-
message: "This hand-written type guard validates an unknown value ad hoc. Use a Zod schema instead: schema.safeParse(value) returns a discriminated result whose .success narrows the type. See docs/
|
|
3146
|
+
message: "This hand-written type guard validates an unknown value ad hoc. Use a Zod schema instead: schema.safeParse(value) returns a discriminated result whose .success narrows the type. See docs/repository-policy.md"
|
|
3172
3147
|
});
|
|
3173
3148
|
}
|
|
3174
3149
|
return {
|
|
@@ -3205,8 +3180,8 @@ var NON_SOURCE_ROOT_DIRS = /* @__PURE__ */ new Set([
|
|
|
3205
3180
|
"coverage",
|
|
3206
3181
|
"dist",
|
|
3207
3182
|
"docs",
|
|
3208
|
-
"enforcement",
|
|
3209
3183
|
"eslint",
|
|
3184
|
+
"scripts",
|
|
3210
3185
|
"node_modules",
|
|
3211
3186
|
"public",
|
|
3212
3187
|
"static",
|
|
@@ -3249,6 +3224,44 @@ var sourceUnderSrcRule = {
|
|
|
3249
3224
|
}
|
|
3250
3225
|
};
|
|
3251
3226
|
|
|
3227
|
+
// eslint/pasika/rules/config-baseline.ts
|
|
3228
|
+
import fs4 from "fs";
|
|
3229
|
+
import path33 from "path";
|
|
3230
|
+
var configBaselineRule = {
|
|
3231
|
+
meta: {
|
|
3232
|
+
schema: [],
|
|
3233
|
+
type: "problem",
|
|
3234
|
+
docs: {
|
|
3235
|
+
description: "Require eslint config to reference zirka and tsconfig.json to exist."
|
|
3236
|
+
}
|
|
3237
|
+
},
|
|
3238
|
+
create(context) {
|
|
3239
|
+
const filename = path33.resolve(context.filename);
|
|
3240
|
+
const basename = path33.basename(filename);
|
|
3241
|
+
if (!/^eslint\.config\.(?:ts|mts|cts|js|mjs|cjs)$/.test(basename)) return {};
|
|
3242
|
+
return {
|
|
3243
|
+
Program(node) {
|
|
3244
|
+
const content = context.sourceCode.getText();
|
|
3245
|
+
if (!content.includes("zirka")) {
|
|
3246
|
+
context.report({
|
|
3247
|
+
node,
|
|
3248
|
+
loc: { line: 1, column: 0 },
|
|
3249
|
+
message: "ESLint config must reference zirka. Use the pasika/zirka baseline configuration."
|
|
3250
|
+
});
|
|
3251
|
+
}
|
|
3252
|
+
const projectRoot = path33.dirname(filename);
|
|
3253
|
+
if (!fs4.existsSync(path33.join(projectRoot, "tsconfig.json"))) {
|
|
3254
|
+
context.report({
|
|
3255
|
+
node,
|
|
3256
|
+
loc: { line: 1, column: 0 },
|
|
3257
|
+
message: "No tsconfig.json found. Create one extending the pasika baseline."
|
|
3258
|
+
});
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
};
|
|
3262
|
+
}
|
|
3263
|
+
};
|
|
3264
|
+
|
|
3252
3265
|
// eslint/pasika/rules/md/helpers.ts
|
|
3253
3266
|
var RFC_PATTERN = /\b(?<keyword>MUST NOT|MUST|SHALL NOT|SHALL|SHOULD NOT|SHOULD|MAY|RECOMMENDED|NOT RECOMMENDED|OPTIONAL|REQUIRED)\b/;
|
|
3254
3267
|
function containsRfcKeyword(text) {
|
|
@@ -3296,7 +3309,7 @@ var docKindSuffixRule = {
|
|
|
3296
3309
|
};
|
|
3297
3310
|
|
|
3298
3311
|
// eslint/pasika/rules/md/title-matches-file-name.ts
|
|
3299
|
-
import
|
|
3312
|
+
import path34 from "path";
|
|
3300
3313
|
function toExpectedFileName(title) {
|
|
3301
3314
|
return `${title.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}.md`;
|
|
3302
3315
|
}
|
|
@@ -3316,7 +3329,7 @@ var titleMatchesFileNameRule = {
|
|
|
3316
3329
|
if (!filename.endsWith(".md")) return;
|
|
3317
3330
|
const title = getTextContent(node).trim();
|
|
3318
3331
|
const expectedFileName = toExpectedFileName(title);
|
|
3319
|
-
const actualFileName =
|
|
3332
|
+
const actualFileName = path34.basename(filename);
|
|
3320
3333
|
if (!title) {
|
|
3321
3334
|
context.report({
|
|
3322
3335
|
node,
|
|
@@ -3789,7 +3802,7 @@ var referenceBlockHeadingsRule = {
|
|
|
3789
3802
|
};
|
|
3790
3803
|
|
|
3791
3804
|
// eslint/pasika/rules/md/support-document-placement.ts
|
|
3792
|
-
import
|
|
3805
|
+
import path35 from "path";
|
|
3793
3806
|
var supportDocumentPlacementRule = {
|
|
3794
3807
|
meta: {
|
|
3795
3808
|
type: "problem",
|
|
@@ -3803,7 +3816,7 @@ var supportDocumentPlacementRule = {
|
|
|
3803
3816
|
root(node) {
|
|
3804
3817
|
const filename = getFilename(context);
|
|
3805
3818
|
if (!filename.endsWith(".md")) return;
|
|
3806
|
-
const parentFolder =
|
|
3819
|
+
const parentFolder = path35.basename(path35.dirname(filename));
|
|
3807
3820
|
if (filename.endsWith("-rule.md") && parentFolder !== "rules") {
|
|
3808
3821
|
context.report({
|
|
3809
3822
|
node,
|
|
@@ -3846,11 +3859,11 @@ var noTemplatePromptRule = {
|
|
|
3846
3859
|
};
|
|
3847
3860
|
|
|
3848
3861
|
// eslint/pasika/rules/md/guide-folder-entry-point.ts
|
|
3849
|
-
import
|
|
3862
|
+
import path37 from "path";
|
|
3850
3863
|
|
|
3851
3864
|
// eslint/pasika/rules/md/project-index.ts
|
|
3852
3865
|
import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
|
|
3853
|
-
import
|
|
3866
|
+
import path36 from "path";
|
|
3854
3867
|
var KIND_BY_SUFFIX = [
|
|
3855
3868
|
["-rule.md", "rule"],
|
|
3856
3869
|
["-guide.md", "guide"],
|
|
@@ -3859,7 +3872,7 @@ var KIND_BY_SUFFIX = [
|
|
|
3859
3872
|
];
|
|
3860
3873
|
function listMarkdownFiles(dir) {
|
|
3861
3874
|
return readdirSync2(dir).flatMap((entry) => {
|
|
3862
|
-
const entryPath =
|
|
3875
|
+
const entryPath = path36.join(dir, entry);
|
|
3863
3876
|
if (statSync3(entryPath).isDirectory()) {
|
|
3864
3877
|
return entry.startsWith("_") ? [] : listMarkdownFiles(entryPath);
|
|
3865
3878
|
}
|
|
@@ -3877,11 +3890,11 @@ function getProjectDocs(docsRoot) {
|
|
|
3877
3890
|
if (cached) return cached;
|
|
3878
3891
|
const files = listMarkdownFiles(docsRoot);
|
|
3879
3892
|
const docs = files.sort((a, b) => a.localeCompare(b)).map((filePath) => {
|
|
3880
|
-
const fileName =
|
|
3893
|
+
const fileName = path36.basename(filePath);
|
|
3881
3894
|
const kind = KIND_BY_SUFFIX.find(([suffix]) => fileName.endsWith(suffix))?.[1];
|
|
3882
3895
|
return {
|
|
3883
3896
|
filePath,
|
|
3884
|
-
doc:
|
|
3897
|
+
doc: path36.relative(docsRoot, filePath).split(path36.sep).join("/"),
|
|
3885
3898
|
fileName,
|
|
3886
3899
|
kind,
|
|
3887
3900
|
title: extractTitle(filePath)
|
|
@@ -3891,12 +3904,12 @@ function getProjectDocs(docsRoot) {
|
|
|
3891
3904
|
return docs;
|
|
3892
3905
|
}
|
|
3893
3906
|
function findDocsRoot(filePath) {
|
|
3894
|
-
let dir =
|
|
3907
|
+
let dir = path36.dirname(filePath);
|
|
3895
3908
|
for (; ; ) {
|
|
3896
|
-
if (
|
|
3909
|
+
if (path36.basename(dir) === "docs" && statSync3(dir).isDirectory()) {
|
|
3897
3910
|
return dir;
|
|
3898
3911
|
}
|
|
3899
|
-
const parent =
|
|
3912
|
+
const parent = path36.dirname(dir);
|
|
3900
3913
|
if (parent === dir) return void 0;
|
|
3901
3914
|
dir = parent;
|
|
3902
3915
|
}
|
|
@@ -3920,13 +3933,13 @@ var guideFolderEntryPointRule = {
|
|
|
3920
3933
|
if (!docsRoot) return;
|
|
3921
3934
|
const docs = getProjectDocs(docsRoot);
|
|
3922
3935
|
const guideFolders = new Set(
|
|
3923
|
-
docs.filter((doc) => ["rules", "references"].includes(
|
|
3936
|
+
docs.filter((doc) => ["rules", "references"].includes(path37.basename(path37.dirname(doc.filePath)))).map((doc) => path37.dirname(path37.dirname(doc.filePath))).filter((folder) => path37.resolve(folder) !== path37.resolve(docsRoot))
|
|
3924
3937
|
);
|
|
3925
|
-
const currentDir =
|
|
3938
|
+
const currentDir = path37.dirname(filename);
|
|
3926
3939
|
if (guideFolders.has(currentDir)) {
|
|
3927
|
-
const expectedEntryPoint = `${
|
|
3940
|
+
const expectedEntryPoint = `${path37.basename(currentDir)}.md`;
|
|
3928
3941
|
const hasEntryPoint = docs.some(
|
|
3929
|
-
(doc) => doc.kind === "guide" &&
|
|
3942
|
+
(doc) => doc.kind === "guide" && path37.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
|
|
3930
3943
|
);
|
|
3931
3944
|
if (!hasEntryPoint) {
|
|
3932
3945
|
context.report({
|
|
@@ -4151,7 +4164,7 @@ var noNestedHowToRule = {
|
|
|
4151
4164
|
|
|
4152
4165
|
// eslint/pasika/rules/md/glossary-term-linking.ts
|
|
4153
4166
|
import { readFileSync as readFileSync5 } from "fs";
|
|
4154
|
-
import
|
|
4167
|
+
import path38 from "path";
|
|
4155
4168
|
function extractGlossaryTerms(filePath) {
|
|
4156
4169
|
const content = readFileSync5(filePath, "utf8");
|
|
4157
4170
|
const terms = [];
|
|
@@ -4205,9 +4218,9 @@ var glossaryTermLinkingRule = {
|
|
|
4205
4218
|
const docsRoot = findDocsRoot(filename);
|
|
4206
4219
|
if (!docsRoot) return;
|
|
4207
4220
|
const docs = getProjectDocs(docsRoot);
|
|
4208
|
-
const guideDir =
|
|
4221
|
+
const guideDir = path38.dirname(filename);
|
|
4209
4222
|
const guideReferences = docs.filter(
|
|
4210
|
-
(doc) => doc.kind === "reference" &&
|
|
4223
|
+
(doc) => doc.kind === "reference" && path38.dirname(doc.filePath) === guideDir
|
|
4211
4224
|
);
|
|
4212
4225
|
if (guideReferences.length === 0) return;
|
|
4213
4226
|
const glossaryTerms = [];
|
|
@@ -4690,6 +4703,39 @@ var globalCssLocationRule = {
|
|
|
4690
4703
|
}
|
|
4691
4704
|
};
|
|
4692
4705
|
|
|
4706
|
+
// eslint/pasika/rules/css/global-stylesheet.ts
|
|
4707
|
+
var globalStylesheetRule = {
|
|
4708
|
+
meta: {
|
|
4709
|
+
schema: [],
|
|
4710
|
+
type: "problem",
|
|
4711
|
+
docs: {
|
|
4712
|
+
description: "Require a global stylesheet entry point that registers Tailwind."
|
|
4713
|
+
}
|
|
4714
|
+
},
|
|
4715
|
+
create(context) {
|
|
4716
|
+
return {
|
|
4717
|
+
"StyleSheet:exit"(node) {
|
|
4718
|
+
const registersTailwind = node.children.some(
|
|
4719
|
+
(child) => child.type === "Atrule" && child.name === "import" && JSON.stringify(child.prelude).includes("tailwindcss")
|
|
4720
|
+
);
|
|
4721
|
+
if (!registersTailwind) {
|
|
4722
|
+
const hasProjectCss = node.children.some((child) => {
|
|
4723
|
+
if (child.type === "Atrule" && child.name === "import") return false;
|
|
4724
|
+
if (child.type === "Comment") return false;
|
|
4725
|
+
return true;
|
|
4726
|
+
});
|
|
4727
|
+
if (hasProjectCss) {
|
|
4728
|
+
context.report({
|
|
4729
|
+
node,
|
|
4730
|
+
message: 'Global stylesheet must register Tailwind with @import "tailwindcss".'
|
|
4731
|
+
});
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
};
|
|
4738
|
+
|
|
4693
4739
|
// eslint/pasika/rules/css/index.ts
|
|
4694
4740
|
var cssRules = {
|
|
4695
4741
|
"theme-reset": themeResetRule,
|
|
@@ -4701,19 +4747,20 @@ var cssRules = {
|
|
|
4701
4747
|
"custom-utility-apply": customUtilityApplyRule,
|
|
4702
4748
|
"surface-utility": surfaceUtilityRule,
|
|
4703
4749
|
"theme-variable-namespace": themeVariableNamespaceRule,
|
|
4704
|
-
"global-css-location": globalCssLocationRule
|
|
4750
|
+
"global-css-location": globalCssLocationRule,
|
|
4751
|
+
"global-stylesheet": globalStylesheetRule
|
|
4705
4752
|
};
|
|
4706
4753
|
|
|
4707
|
-
// eslint/pasika/rules/json/no-
|
|
4754
|
+
// eslint/pasika/rules/json/no-vulyk-dependency.ts
|
|
4708
4755
|
function memberName(member) {
|
|
4709
4756
|
return member.name.type === "String" ? member.name.value : member.name.name;
|
|
4710
4757
|
}
|
|
4711
|
-
var
|
|
4758
|
+
var noVulykDependencyRule = {
|
|
4712
4759
|
meta: {
|
|
4713
4760
|
schema: [],
|
|
4714
4761
|
type: "problem",
|
|
4715
4762
|
docs: {
|
|
4716
|
-
description: "Require
|
|
4763
|
+
description: "Require vulyk to run as an ephemeral command, not a package.json dependency."
|
|
4717
4764
|
}
|
|
4718
4765
|
},
|
|
4719
4766
|
create(context) {
|
|
@@ -4721,16 +4768,14 @@ var noCacheFlagRule = {
|
|
|
4721
4768
|
Document(node) {
|
|
4722
4769
|
const root = node.body;
|
|
4723
4770
|
if (root.type !== "Object") return;
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
const
|
|
4728
|
-
if (
|
|
4729
|
-
if (member.value.type !== "String") continue;
|
|
4730
|
-
if (member.value.value.includes("--cache")) {
|
|
4771
|
+
for (const key of ["dependencies", "devDependencies"]) {
|
|
4772
|
+
const section = root.members.find((member) => memberName(member) === key);
|
|
4773
|
+
if (section?.value.type !== "Object") continue;
|
|
4774
|
+
const vulyk = section.value.members.find((member) => memberName(member) === "vulyk");
|
|
4775
|
+
if (vulyk) {
|
|
4731
4776
|
context.report({
|
|
4732
|
-
node:
|
|
4733
|
-
message:
|
|
4777
|
+
node: vulyk,
|
|
4778
|
+
message: 'Vulyk must not be a package.json dependency. Run it as "npx vulyk@latest" instead.'
|
|
4734
4779
|
});
|
|
4735
4780
|
}
|
|
4736
4781
|
}
|
|
@@ -4739,16 +4784,16 @@ var noCacheFlagRule = {
|
|
|
4739
4784
|
}
|
|
4740
4785
|
};
|
|
4741
4786
|
|
|
4742
|
-
// eslint/pasika/rules/json/
|
|
4787
|
+
// eslint/pasika/rules/json/zirka-installed.ts
|
|
4743
4788
|
function memberName2(member) {
|
|
4744
4789
|
return member.name.type === "String" ? member.name.value : member.name.name;
|
|
4745
4790
|
}
|
|
4746
|
-
var
|
|
4791
|
+
var zirkaInstalledRule = {
|
|
4747
4792
|
meta: {
|
|
4748
4793
|
schema: [],
|
|
4749
4794
|
type: "problem",
|
|
4750
4795
|
docs: {
|
|
4751
|
-
description: "Require
|
|
4796
|
+
description: "Require zirka to be listed in package.json as a devDependency."
|
|
4752
4797
|
}
|
|
4753
4798
|
},
|
|
4754
4799
|
create(context) {
|
|
@@ -4756,17 +4801,20 @@ var noVulykDependencyRule = {
|
|
|
4756
4801
|
Document(node) {
|
|
4757
4802
|
const root = node.body;
|
|
4758
4803
|
if (root.type !== "Object") return;
|
|
4804
|
+
const allDeps = [];
|
|
4759
4805
|
for (const key of ["dependencies", "devDependencies"]) {
|
|
4760
4806
|
const section = root.members.find((member) => memberName2(member) === key);
|
|
4761
4807
|
if (section?.value.type !== "Object") continue;
|
|
4762
|
-
const
|
|
4763
|
-
|
|
4764
|
-
context.report({
|
|
4765
|
-
node: vulyk,
|
|
4766
|
-
message: 'Vulyk must not be a package.json dependency. Run it as "npx vulyk@latest" instead.'
|
|
4767
|
-
});
|
|
4808
|
+
for (const member of section.value.members) {
|
|
4809
|
+
allDeps.push(memberName2(member));
|
|
4768
4810
|
}
|
|
4769
4811
|
}
|
|
4812
|
+
if (!allDeps.includes("zirka")) {
|
|
4813
|
+
context.report({
|
|
4814
|
+
node,
|
|
4815
|
+
message: "zirka must be listed in package.json as a devDependency."
|
|
4816
|
+
});
|
|
4817
|
+
}
|
|
4770
4818
|
}
|
|
4771
4819
|
};
|
|
4772
4820
|
}
|
|
@@ -4774,8 +4822,8 @@ var noVulykDependencyRule = {
|
|
|
4774
4822
|
|
|
4775
4823
|
// eslint/pasika/rules/json/index.ts
|
|
4776
4824
|
var jsonRules = {
|
|
4777
|
-
"no-
|
|
4778
|
-
"
|
|
4825
|
+
"no-vulyk-dependency": noVulykDependencyRule,
|
|
4826
|
+
"zirka-installed": zirkaInstalledRule
|
|
4779
4827
|
};
|
|
4780
4828
|
|
|
4781
4829
|
// eslint/pasika/index.ts
|
|
@@ -4817,9 +4865,9 @@ var pasikaRules = {
|
|
|
4817
4865
|
"locale-key-shape": localeKeyShapeRule,
|
|
4818
4866
|
"shared-style-dedup": sharedStyleDedupRule,
|
|
4819
4867
|
"repeated-structure": repeatedStructureRule,
|
|
4820
|
-
"no-eslint-disable": noEslintDisableRule,
|
|
4821
4868
|
"zod-schema-validation": zodSchemaValidationRule,
|
|
4822
|
-
"source-under-src": sourceUnderSrcRule
|
|
4869
|
+
"source-under-src": sourceUnderSrcRule,
|
|
4870
|
+
"config-baseline": configBaselineRule
|
|
4823
4871
|
};
|
|
4824
4872
|
var pasikaRuleIds = Object.keys(pasikaRules).map((name) => `pasika/${name}`);
|
|
4825
4873
|
var pasikaMdRuleIds = Object.keys(mdRules).map((name) => `pasika/${name}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pasika",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "Reusable agent setup package",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,24 +15,22 @@
|
|
|
15
15
|
},
|
|
16
16
|
"./package.json": "./package.json"
|
|
17
17
|
},
|
|
18
|
-
"bin": {
|
|
19
|
-
"pasika": "dist/cli/index.js"
|
|
20
|
-
},
|
|
21
18
|
"files": [
|
|
22
19
|
"dist"
|
|
23
20
|
],
|
|
24
21
|
"scripts": {
|
|
25
22
|
"build": "tsup",
|
|
26
|
-
"coverage": "tsx
|
|
23
|
+
"coverage": "tsx scripts/coverage.ts",
|
|
27
24
|
"fix": "eslint . --fix",
|
|
28
25
|
"lint": "eslint .",
|
|
29
26
|
"prepack": "npm run build",
|
|
30
|
-
"
|
|
27
|
+
"prepare": "husky",
|
|
28
|
+
"test": "node --import tsx --test 'eslint/**/*.test.ts' 'scripts/**/*.test.ts' 'utils/**/*.test.ts'",
|
|
31
29
|
"typecheck": "tsc --noEmit"
|
|
32
30
|
},
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
31
|
+
"lint-staged": {
|
|
32
|
+
"!(*.ts|*.mts|*.cts|*.tsx|*.js|*.cjs|*.mjs|*.jsx)": "prettier --write --ignore-unknown",
|
|
33
|
+
"*.ts|*.mts|*.cts|*.tsx|*.js|*.cjs|*.mjs|*.jsx": "npx eslint --fix"
|
|
36
34
|
},
|
|
37
35
|
"devDependencies": {
|
|
38
36
|
"@eslint/css": "^1.4.0",
|
|
@@ -44,14 +42,15 @@
|
|
|
44
42
|
"@types/mdast": "^4.0.4",
|
|
45
43
|
"@types/node": "^26.4.0",
|
|
46
44
|
"@typescript-eslint/parser": "^8.68.0",
|
|
47
|
-
"@typescript-eslint/utils": "^8.68.0",
|
|
48
45
|
"eslint": "^10.9.1",
|
|
49
|
-
"
|
|
46
|
+
"husky": "^9.1.7",
|
|
47
|
+
"lint-staged": "^16.4.0",
|
|
50
48
|
"prettier": "^3.8.1",
|
|
51
49
|
"tsup": "^8.5.1",
|
|
52
50
|
"tsx": "^4.23.12",
|
|
53
51
|
"typescript": "^5.9.2",
|
|
54
|
-
"zirka": "^0.0.42"
|
|
52
|
+
"zirka": "^0.0.42",
|
|
53
|
+
"zod": "^4.4.3"
|
|
55
54
|
},
|
|
56
55
|
"peerDependencies": {
|
|
57
56
|
"typescript": ">=5.0.0"
|
package/dist/cli/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|