pasika 0.3.1 → 0.3.3
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 +67 -9
- package/dist/cli/index.js +34 -40
- package/dist/cli/output.d.ts +3 -0
- package/dist/cli/output.js +11 -0
- package/dist/enforcement/coverage.d.ts +5 -1
- package/dist/enforcement/coverage.js +68 -39
- package/dist/enforcement/doctor.d.ts +16 -0
- package/dist/enforcement/doctor.js +249 -0
- package/dist/enforcement/parse-docs.js +5 -1
- package/dist/enforcement/types.d.ts +0 -3
- package/dist/enforcement/types.js +4 -13
- package/dist/eslint/pasika/ast-types.d.ts +122 -0
- package/dist/eslint/pasika/ast-types.js +1 -0
- package/dist/eslint/pasika/index.d.ts +58 -3
- package/dist/eslint/pasika/index.js +61 -1
- package/dist/eslint/pasika/rules/application-structure.d.ts +2 -0
- package/dist/eslint/pasika/rules/application-structure.js +152 -0
- package/dist/eslint/pasika/rules/component-conventions.d.ts +16 -0
- package/dist/eslint/pasika/rules/component-conventions.js +145 -0
- package/dist/eslint/pasika/rules/component-nesting.d.ts +12 -0
- package/dist/eslint/pasika/rules/component-nesting.js +69 -0
- package/dist/eslint/pasika/rules/config-extraction.d.ts +14 -0
- package/dist/eslint/pasika/rules/config-extraction.js +74 -0
- package/dist/eslint/pasika/rules/cross-feature-import.d.ts +2 -0
- package/dist/eslint/pasika/rules/cross-feature-import.js +75 -0
- package/dist/eslint/pasika/rules/css/apply-usage.d.ts +10 -0
- package/dist/eslint/pasika/rules/css/apply-usage.js +45 -0
- package/dist/eslint/pasika/rules/css/base-layer-pair.d.ts +10 -0
- package/dist/eslint/pasika/rules/css/base-layer-pair.js +49 -0
- package/dist/eslint/pasika/rules/css/css-variable-naming.d.ts +10 -0
- package/dist/eslint/pasika/rules/css/css-variable-naming.js +39 -0
- package/dist/eslint/pasika/rules/css/custom-utility-apply.d.ts +11 -0
- package/dist/eslint/pasika/rules/css/custom-utility-apply.js +52 -0
- package/dist/eslint/pasika/rules/css/global-css-location.d.ts +10 -0
- package/dist/eslint/pasika/rules/css/global-css-location.js +42 -0
- package/dist/eslint/pasika/rules/css/helpers.d.ts +29 -0
- package/dist/eslint/pasika/rules/css/helpers.js +93 -0
- package/dist/eslint/pasika/rules/css/index.d.ts +13 -0
- package/dist/eslint/pasika/rules/css/index.js +25 -0
- package/dist/eslint/pasika/rules/css/root-variables.d.ts +11 -0
- package/dist/eslint/pasika/rules/css/root-variables.js +53 -0
- package/dist/eslint/pasika/rules/css/rule-tester.d.ts +11 -0
- package/dist/eslint/pasika/rules/css/rule-tester.js +22 -0
- package/dist/eslint/pasika/rules/css/stylesheet-ordering.d.ts +11 -0
- package/dist/eslint/pasika/rules/css/stylesheet-ordering.js +76 -0
- package/dist/eslint/pasika/rules/css/surface-utility.d.ts +10 -0
- package/dist/eslint/pasika/rules/css/surface-utility.js +41 -0
- package/dist/eslint/pasika/rules/css/theme-reset.d.ts +9 -0
- package/dist/eslint/pasika/rules/css/theme-reset.js +36 -0
- package/dist/eslint/pasika/rules/css/theme-variable-namespace.d.ts +11 -0
- package/dist/eslint/pasika/rules/css/theme-variable-namespace.js +53 -0
- package/dist/eslint/pasika/rules/cva-appearance-props.d.ts +10 -0
- package/dist/eslint/pasika/rules/cva-appearance-props.js +77 -0
- package/dist/eslint/pasika/rules/cva-boolean-variants.d.ts +11 -0
- package/dist/eslint/pasika/rules/cva-boolean-variants.js +97 -0
- package/dist/eslint/pasika/rules/data-testid-case.d.ts +2 -0
- package/dist/eslint/pasika/rules/data-testid-case.js +76 -0
- package/dist/eslint/pasika/rules/enforce-cn-merge.d.ts +13 -1
- package/dist/eslint/pasika/rules/enforce-cn-merge.js +46 -44
- package/dist/eslint/pasika/rules/enforce-cva-variant-props.js +53 -48
- package/dist/eslint/pasika/rules/filename-case.js +28 -5
- package/dist/eslint/pasika/rules/hook-complexity.d.ts +12 -0
- package/dist/eslint/pasika/rules/hook-complexity.js +114 -0
- package/dist/eslint/pasika/rules/hook-extraction.d.ts +11 -0
- package/dist/eslint/pasika/rules/hook-extraction.js +55 -0
- package/dist/eslint/pasika/rules/import-through-index.d.ts +2 -0
- package/dist/eslint/pasika/rules/import-through-index.js +52 -0
- package/dist/eslint/pasika/rules/interactive-component.d.ts +10 -0
- package/dist/eslint/pasika/rules/interactive-component.js +76 -0
- package/dist/eslint/pasika/rules/json/index.d.ts +5 -0
- package/dist/eslint/pasika/rules/json/index.js +9 -0
- package/dist/eslint/pasika/rules/json/no-cache-flag.d.ts +10 -0
- package/dist/eslint/pasika/rules/json/no-cache-flag.js +45 -0
- package/dist/eslint/pasika/rules/json/no-vulyk-dependency.d.ts +10 -0
- package/dist/eslint/pasika/rules/json/no-vulyk-dependency.js +41 -0
- package/dist/eslint/pasika/rules/json/rule-tester.d.ts +11 -0
- package/dist/eslint/pasika/rules/json/rule-tester.js +21 -0
- package/dist/eslint/pasika/rules/jsx-hygiene.d.ts +9 -0
- package/dist/eslint/pasika/rules/jsx-hygiene.js +108 -0
- package/dist/eslint/pasika/rules/locale-dotted-path.d.ts +10 -0
- package/dist/eslint/pasika/rules/locale-dotted-path.js +65 -0
- package/dist/eslint/pasika/rules/locale-placement.d.ts +13 -0
- package/dist/eslint/pasika/rules/locale-placement.js +163 -0
- package/dist/eslint/pasika/rules/locales-location.d.ts +11 -0
- package/dist/eslint/pasika/rules/locales-location.js +56 -0
- package/dist/eslint/pasika/rules/md/doc-kind-suffix.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/doc-kind-suffix.js +29 -0
- package/dist/eslint/pasika/rules/md/example-heading-description.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/example-heading-description.js +30 -0
- package/dist/eslint/pasika/rules/md/glossary-term-linking.d.ts +2 -0
- package/dist/eslint/pasika/rules/md/glossary-term-linking.js +99 -0
- package/dist/eslint/pasika/rules/md/guide-folder-entry-point.d.ts +2 -0
- package/dist/eslint/pasika/rules/md/guide-folder-entry-point.js +46 -0
- package/dist/eslint/pasika/rules/md/guide-link-anchors.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/guide-link-anchors.js +50 -0
- package/dist/eslint/pasika/rules/md/guide-overview-no-links.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/guide-overview-no-links.js +51 -0
- package/dist/eslint/pasika/rules/md/guide-states-no-requirement.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/guide-states-no-requirement.js +26 -0
- package/dist/eslint/pasika/rules/md/guide-step-single-link.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/guide-step-single-link.js +48 -0
- package/dist/eslint/pasika/rules/md/guide-step-single-sentence.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/guide-step-single-sentence.js +42 -0
- package/dist/eslint/pasika/rules/md/helpers.d.ts +25 -0
- package/dist/eslint/pasika/rules/md/helpers.js +37 -0
- package/dist/eslint/pasika/rules/md/index.d.ts +27 -0
- package/dist/eslint/pasika/rules/md/index.js +53 -0
- package/dist/eslint/pasika/rules/md/no-cross-document-link.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/no-cross-document-link.js +36 -0
- package/dist/eslint/pasika/rules/md/no-nested-how-to.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/no-nested-how-to.js +39 -0
- package/dist/eslint/pasika/rules/md/no-template-prompt.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/no-template-prompt.js +22 -0
- package/dist/eslint/pasika/rules/md/overview-length.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/overview-length.js +47 -0
- package/dist/eslint/pasika/rules/md/overview-present.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/overview-present.js +41 -0
- package/dist/eslint/pasika/rules/md/policy-no-examples.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/policy-no-examples.js +28 -0
- package/dist/eslint/pasika/rules/md/policy-single-document.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/policy-single-document.js +34 -0
- package/dist/eslint/pasika/rules/md/policy-subject-headings.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/policy-subject-headings.js +58 -0
- package/dist/eslint/pasika/rules/md/project-index.d.ts +17 -0
- package/dist/eslint/pasika/rules/md/project-index.js +67 -0
- package/dist/eslint/pasika/rules/md/reference-block-headings.d.ts +6 -0
- package/dist/eslint/pasika/rules/md/reference-block-headings.js +31 -0
- package/dist/eslint/pasika/rules/md/reference-no-rfc-vocabulary.d.ts +6 -0
- package/dist/eslint/pasika/rules/md/reference-no-rfc-vocabulary.js +26 -0
- package/dist/eslint/pasika/rules/md/requirement-present.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/requirement-present.js +41 -0
- package/dist/eslint/pasika/rules/md/rfc-only-in-bullets.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/rfc-only-in-bullets.js +46 -0
- package/dist/eslint/pasika/rules/md/rule-paired-examples.d.ts +5 -0
- package/dist/eslint/pasika/rules/md/rule-paired-examples.js +36 -0
- package/dist/eslint/pasika/rules/md/rule-tester.d.ts +11 -0
- package/dist/eslint/pasika/rules/md/rule-tester.js +21 -0
- package/dist/eslint/pasika/rules/md/support-document-placement.d.ts +2 -0
- package/dist/eslint/pasika/rules/md/support-document-placement.js +36 -0
- package/dist/eslint/pasika/rules/md/title-matches-file-name.d.ts +2 -0
- package/dist/eslint/pasika/rules/md/title-matches-file-name.js +46 -0
- package/dist/eslint/pasika/rules/named-exports.d.ts +2 -0
- package/dist/eslint/pasika/rules/named-exports.js +49 -0
- package/dist/eslint/pasika/rules/no-eslint-disable.d.ts +10 -0
- package/dist/eslint/pasika/rules/no-eslint-disable.js +33 -0
- package/dist/eslint/pasika/rules/no-mixed-concerns.js +9 -22
- package/dist/eslint/pasika/rules/no-util-barrel.d.ts +2 -0
- package/dist/eslint/pasika/rules/no-util-barrel.js +48 -0
- package/dist/eslint/pasika/rules/pure-function-extract.d.ts +9 -0
- package/dist/eslint/pasika/rules/pure-function-extract.js +92 -0
- package/dist/eslint/pasika/rules/shared-style-dedup.d.ts +13 -0
- package/dist/eslint/pasika/rules/shared-style-dedup.js +91 -0
- package/dist/eslint/pasika/rules/stay-flat.d.ts +12 -0
- package/dist/eslint/pasika/rules/stay-flat.js +81 -0
- package/dist/eslint/pasika/rules/support-folder-shape.d.ts +2 -0
- package/dist/eslint/pasika/rules/support-folder-shape.js +59 -0
- package/dist/eslint/pasika/rules/type-extraction.d.ts +13 -0
- package/dist/eslint/pasika/rules/type-extraction.js +85 -0
- package/dist/eslint/pasika/rules/ui-state.d.ts +9 -0
- package/dist/eslint/pasika/rules/ui-state.js +99 -0
- package/dist/eslint/pasika/rules/util-file-name.d.ts +2 -0
- package/dist/eslint/pasika/rules/util-file-name.js +48 -0
- package/dist/eslint/pasika/rules/value-extraction.d.ts +13 -0
- package/dist/eslint/pasika/rules/value-extraction.js +48 -0
- package/package.json +14 -7
- package/dist/enforcement/docs-check.d.ts +0 -17
- package/dist/enforcement/docs-check.js +0 -162
- package/enforcement/registry.json +0 -1139
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pasika doctor` checks.
|
|
3
|
+
*
|
|
4
|
+
* Diagnoses gaps between a consumer repository and the pasika framework
|
|
5
|
+
* baseline. Scope: dependencies, configuration, managed files, and
|
|
6
|
+
* source structure. Code-level and path-level rules belong to ESLint.
|
|
7
|
+
*/
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
const packageJsonSchema = z.object({
|
|
12
|
+
dependencies: z.record(z.string(), z.string()).optional(),
|
|
13
|
+
devDependencies: z.record(z.string(), z.string()).optional(),
|
|
14
|
+
scripts: z.record(z.string(), z.string()).optional(),
|
|
15
|
+
});
|
|
16
|
+
function readPackageJson(filePath) {
|
|
17
|
+
try {
|
|
18
|
+
const result = packageJsonSchema.safeParse(JSON.parse(fs.readFileSync(filePath, "utf8")));
|
|
19
|
+
if (result.success)
|
|
20
|
+
return result.data;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// missing or unparseable
|
|
24
|
+
}
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check that pasika and zirka are installed at compatible versions.
|
|
29
|
+
*/
|
|
30
|
+
function checkFrameworkPackages(pkg) {
|
|
31
|
+
const findings = [];
|
|
32
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
33
|
+
if (!allDeps.pasika) {
|
|
34
|
+
findings.push({
|
|
35
|
+
check: "pasika-installed",
|
|
36
|
+
message: "pasika is not listed in package.json.",
|
|
37
|
+
severity: "error",
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (!allDeps.zirka) {
|
|
41
|
+
findings.push({
|
|
42
|
+
check: "zirka-installed",
|
|
43
|
+
message: "zirka is not listed in package.json.",
|
|
44
|
+
severity: "error",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return findings;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check that application source lives under src/.
|
|
51
|
+
*/
|
|
52
|
+
function checkSourceRoot(cwd) {
|
|
53
|
+
const findings = [];
|
|
54
|
+
const srcDir = path.join(cwd, "src");
|
|
55
|
+
if (!fs.existsSync(srcDir)) {
|
|
56
|
+
findings.push({
|
|
57
|
+
check: "source-under-src",
|
|
58
|
+
message: "Application source must live under src/.",
|
|
59
|
+
severity: "error",
|
|
60
|
+
});
|
|
61
|
+
return findings;
|
|
62
|
+
}
|
|
63
|
+
// Check for common non-src source directories at the project root
|
|
64
|
+
const entries = fs.readdirSync(cwd, { withFileTypes: true });
|
|
65
|
+
const suspicious = entries.filter((e) => e.isDirectory() &&
|
|
66
|
+
!e.name.startsWith(".") &&
|
|
67
|
+
e.name !== "src" &&
|
|
68
|
+
e.name !== "node_modules" &&
|
|
69
|
+
e.name !== "dist" &&
|
|
70
|
+
e.name !== "build" &&
|
|
71
|
+
e.name !== ".vulyk" &&
|
|
72
|
+
e.name !== "docs" &&
|
|
73
|
+
e.name !== "enforcement" &&
|
|
74
|
+
e.name !== "eslint" &&
|
|
75
|
+
e.name !== "cli" &&
|
|
76
|
+
e.name !== "__tests__" &&
|
|
77
|
+
e.name !== "test" &&
|
|
78
|
+
e.name !== "tests" &&
|
|
79
|
+
e.name !== "coverage" &&
|
|
80
|
+
e.name !== ".github" &&
|
|
81
|
+
e.name !== ".agents" &&
|
|
82
|
+
e.name !== ".husky" &&
|
|
83
|
+
e.name !== ".next" &&
|
|
84
|
+
e.name !== ".turbo" &&
|
|
85
|
+
e.name !== ".vercel" &&
|
|
86
|
+
e.name !== ".cache" &&
|
|
87
|
+
e.name !== ".tmp" &&
|
|
88
|
+
e.name !== "tmp" &&
|
|
89
|
+
e.name !== "public" &&
|
|
90
|
+
e.name !== "static" &&
|
|
91
|
+
e.name !== "assets" &&
|
|
92
|
+
e.name !== "bin" &&
|
|
93
|
+
e.name !== "vendor");
|
|
94
|
+
if (suspicious.length > 0) {
|
|
95
|
+
const names = suspicious.map((e) => e.name).join(", ");
|
|
96
|
+
findings.push({
|
|
97
|
+
check: "source-under-src",
|
|
98
|
+
message: `Possible source directories outside src/: ${names}.`,
|
|
99
|
+
severity: "warning",
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return findings;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Find the global CSS file that registers Tailwind.
|
|
106
|
+
*/
|
|
107
|
+
function findGlobalStylesheet(cwd) {
|
|
108
|
+
const candidates = ["src/app/globals.css", "src/styles/globals.css", "src/globals.css"];
|
|
109
|
+
for (const candidate of candidates) {
|
|
110
|
+
const full = path.join(cwd, candidate);
|
|
111
|
+
if (fs.existsSync(full))
|
|
112
|
+
return full;
|
|
113
|
+
}
|
|
114
|
+
// Search for any CSS file containing @import "tailwindcss"
|
|
115
|
+
const srcDir = path.join(cwd, "src");
|
|
116
|
+
if (!fs.existsSync(srcDir))
|
|
117
|
+
return undefined;
|
|
118
|
+
const walk = (dir) => {
|
|
119
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
120
|
+
const full = path.join(dir, entry.name);
|
|
121
|
+
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".next") {
|
|
122
|
+
const found = walk(full);
|
|
123
|
+
if (found)
|
|
124
|
+
return found;
|
|
125
|
+
}
|
|
126
|
+
if (entry.isFile() && entry.name.endsWith(".css")) {
|
|
127
|
+
const content = fs.readFileSync(full, "utf8");
|
|
128
|
+
if (content.includes('@import "tailwindcss"') || content.includes("@import 'tailwindcss'")) {
|
|
129
|
+
return full;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
};
|
|
135
|
+
return walk(srcDir);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Check that the repository has a global stylesheet entry point that
|
|
139
|
+
* registers Tailwind. The stylesheet's contents are the ESLint CSS rules'
|
|
140
|
+
* concern, so doctor only checks that it exists.
|
|
141
|
+
*/
|
|
142
|
+
function checkGlobalStylesheet(cwd) {
|
|
143
|
+
const findings = [];
|
|
144
|
+
const stylesheet = findGlobalStylesheet(cwd);
|
|
145
|
+
if (!stylesheet) {
|
|
146
|
+
findings.push({
|
|
147
|
+
check: "global-stylesheet",
|
|
148
|
+
message: 'No global stylesheet found. Create src/app/globals.css with @import "tailwindcss".',
|
|
149
|
+
severity: "error",
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return findings;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Check that configuration files follow the pasika baseline.
|
|
156
|
+
*
|
|
157
|
+
* Repositories must take their lint, format, and TypeScript configuration
|
|
158
|
+
* from pasika and zirka rather than restating it locally.
|
|
159
|
+
*/
|
|
160
|
+
function checkConfigBaseline(cwd) {
|
|
161
|
+
const findings = [];
|
|
162
|
+
// eslint.config.ts must exist and reference zirka
|
|
163
|
+
const eslintConfigs = ["eslint.config.ts", "eslint.config.mjs", "eslint.config.js"];
|
|
164
|
+
const eslintConfig = eslintConfigs.find((name) => fs.existsSync(path.join(cwd, name)));
|
|
165
|
+
if (!eslintConfig) {
|
|
166
|
+
findings.push({
|
|
167
|
+
check: "config-baseline",
|
|
168
|
+
message: "No eslint config found. Create eslint.config.ts extending the pasika/zirka baseline.",
|
|
169
|
+
severity: "error",
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
const content = fs.readFileSync(path.join(cwd, eslintConfig), "utf8");
|
|
174
|
+
if (!content.includes("zirka")) {
|
|
175
|
+
findings.push({
|
|
176
|
+
check: "config-baseline",
|
|
177
|
+
message: `eslint config does not reference zirka. Use the pasika/zirka baseline configuration.`,
|
|
178
|
+
severity: "warning",
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// tsconfig.json must exist
|
|
183
|
+
if (!fs.existsSync(path.join(cwd, "tsconfig.json"))) {
|
|
184
|
+
findings.push({
|
|
185
|
+
check: "config-baseline",
|
|
186
|
+
message: "No tsconfig.json found. Create one extending the pasika baseline.",
|
|
187
|
+
severity: "error",
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return findings;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Check that vulyk-managed files have not been hand-edited.
|
|
194
|
+
*
|
|
195
|
+
* Files listed in the .vulyk manifest are generated by vulyk commands
|
|
196
|
+
* and must not be edited directly.
|
|
197
|
+
*/
|
|
198
|
+
function checkManagedFiles(cwd) {
|
|
199
|
+
const findings = [];
|
|
200
|
+
const vulykDir = path.join(cwd, ".vulyk");
|
|
201
|
+
if (!fs.existsSync(vulykDir))
|
|
202
|
+
return findings;
|
|
203
|
+
const manifestPath = path.join(vulykDir, "manifest.json");
|
|
204
|
+
if (!fs.existsSync(manifestPath))
|
|
205
|
+
return findings;
|
|
206
|
+
try {
|
|
207
|
+
const manifestEntrySchema = z.looseObject({
|
|
208
|
+
targets: z.array(z.string()).optional(),
|
|
209
|
+
});
|
|
210
|
+
const manifestSchema = z.record(z.string(), manifestEntrySchema);
|
|
211
|
+
const result = manifestSchema.safeParse(JSON.parse(fs.readFileSync(manifestPath, "utf8")));
|
|
212
|
+
if (!result.success)
|
|
213
|
+
return findings;
|
|
214
|
+
for (const [, entry] of Object.entries(result.data)) {
|
|
215
|
+
const targets = entry.targets ?? [];
|
|
216
|
+
for (const target of targets) {
|
|
217
|
+
const targetPath = path.join(cwd, target);
|
|
218
|
+
if (fs.existsSync(targetPath)) {
|
|
219
|
+
// File exists — check it hasn't been modified since vulyk last wrote it
|
|
220
|
+
const stat = fs.statSync(targetPath);
|
|
221
|
+
const vulykStat = fs.statSync(manifestPath);
|
|
222
|
+
// If target is newer than the manifest, it was edited after vulyk wrote it
|
|
223
|
+
if (stat.mtimeMs > vulykStat.mtimeMs + 1000) {
|
|
224
|
+
findings.push({
|
|
225
|
+
check: "managed-file-edit",
|
|
226
|
+
message: `File "${target}" appears to have been edited after vulyk wrote it. Use vulyk commands to update managed files.`,
|
|
227
|
+
severity: "warning",
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
// manifest unreadable
|
|
236
|
+
}
|
|
237
|
+
return findings;
|
|
238
|
+
}
|
|
239
|
+
export function runDoctor(cwd) {
|
|
240
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
241
|
+
const pkg = readPackageJson(pkgPath);
|
|
242
|
+
return [
|
|
243
|
+
...checkFrameworkPackages(pkg),
|
|
244
|
+
...checkConfigBaseline(cwd),
|
|
245
|
+
...checkManagedFiles(cwd),
|
|
246
|
+
...checkSourceRoot(cwd),
|
|
247
|
+
...checkGlobalStylesheet(cwd),
|
|
248
|
+
];
|
|
249
|
+
}
|
|
@@ -40,6 +40,7 @@ function findDocLinks(line) {
|
|
|
40
40
|
}
|
|
41
41
|
export function parseDoc(filePath, docsRoot) {
|
|
42
42
|
const body = readFileSync(filePath, "utf8");
|
|
43
|
+
const sourceLines = body.split("\n");
|
|
43
44
|
const prose = toProse(body, "$<content>");
|
|
44
45
|
const proseWithoutCode = toProse(body, "");
|
|
45
46
|
const fileName = path.basename(filePath);
|
|
@@ -56,7 +57,10 @@ export function parseDoc(filePath, docsRoot) {
|
|
|
56
57
|
}
|
|
57
58
|
if (/^\s*[-*]\s/.test(line) && RFC_2119.test(line)) {
|
|
58
59
|
const text = normalizeRequirement(line);
|
|
59
|
-
|
|
60
|
+
// The raw bullet as written in the source file (code spans and links intact),
|
|
61
|
+
// so a registry entry's text is greppable in the doc it came from.
|
|
62
|
+
const raw = (sourceLines[index] ?? "").replace(/^\s*[-*]\s+/, "");
|
|
63
|
+
requirements.push({ text, hash: hashRequirement(text), raw, line: lineNumber });
|
|
60
64
|
}
|
|
61
65
|
if (/^\s*\d+\.\s/.test(line)) {
|
|
62
66
|
steps.push({ line: lineNumber, text: line.replace(/^\s*\d+\.\s+/, ""), links: findDocLinks(line) });
|
|
@@ -11,7 +11,6 @@ import { z } from "zod";
|
|
|
11
11
|
export declare const enforcementKindSchema: z.ZodEnum<{
|
|
12
12
|
eslint: "eslint";
|
|
13
13
|
doctor: "doctor";
|
|
14
|
-
"docs-check": "docs-check";
|
|
15
14
|
judgment: "judgment";
|
|
16
15
|
permission: "permission";
|
|
17
16
|
planned: "planned";
|
|
@@ -23,7 +22,6 @@ export declare const requirementSchema: z.ZodObject<{
|
|
|
23
22
|
kind: z.ZodEnum<{
|
|
24
23
|
eslint: "eslint";
|
|
25
24
|
doctor: "doctor";
|
|
26
|
-
"docs-check": "docs-check";
|
|
27
25
|
judgment: "judgment";
|
|
28
26
|
permission: "permission";
|
|
29
27
|
planned: "planned";
|
|
@@ -39,7 +37,6 @@ export declare const registrySchema: z.ZodObject<{
|
|
|
39
37
|
kind: z.ZodEnum<{
|
|
40
38
|
eslint: "eslint";
|
|
41
39
|
doctor: "doctor";
|
|
42
|
-
"docs-check": "docs-check";
|
|
43
40
|
judgment: "judgment";
|
|
44
41
|
permission: "permission";
|
|
45
42
|
planned: "planned";
|
|
@@ -13,8 +13,6 @@ export const enforcementKindSchema = z.enum([
|
|
|
13
13
|
"eslint",
|
|
14
14
|
/** A `pasika doctor` check reports it. */
|
|
15
15
|
"doctor",
|
|
16
|
-
/** A `pasika docs` check reports it. */
|
|
17
|
-
"docs-check",
|
|
18
16
|
/** No mechanical check can decide it; a reviewer or agent applies it. */
|
|
19
17
|
"judgment",
|
|
20
18
|
/** The requirement grants permission, so there is nothing to check. */
|
|
@@ -31,8 +29,8 @@ export const requirementSchema = z.object({
|
|
|
31
29
|
hash: z.string(),
|
|
32
30
|
kind: enforcementKindSchema,
|
|
33
31
|
/**
|
|
34
|
-
* Identifier of the check that covers this requirement: an ESLint rule id
|
|
35
|
-
* doctor check id
|
|
32
|
+
* Identifier of the check that covers this requirement: an ESLint rule id or
|
|
33
|
+
* a doctor check id. Several ids are comma-separated.
|
|
36
34
|
* Absent for judgment and permission.
|
|
37
35
|
*/
|
|
38
36
|
ref: z.string().optional(),
|
|
@@ -47,13 +45,6 @@ export const registrySchema = z.object({
|
|
|
47
45
|
requirements: z.array(requirementSchema),
|
|
48
46
|
});
|
|
49
47
|
/** Every kind, in the order the coverage summary prints them. */
|
|
50
|
-
export const ENFORCEMENT_KINDS = [
|
|
51
|
-
"eslint",
|
|
52
|
-
"doctor",
|
|
53
|
-
"docs-check",
|
|
54
|
-
"planned",
|
|
55
|
-
"judgment",
|
|
56
|
-
"permission",
|
|
57
|
-
];
|
|
48
|
+
export const ENFORCEMENT_KINDS = ["eslint", "doctor", "planned", "judgment", "permission"];
|
|
58
49
|
/** The kinds that count as mechanically enforced today. */
|
|
59
|
-
export const MECHANICAL_KINDS = ["eslint", "doctor"
|
|
50
|
+
export const MECHANICAL_KINDS = ["eslint", "doctor"];
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local AST node types for the parser-specific shapes ESLint's ESTree types
|
|
3
|
+
* do not carry: JSX nodes and TypeScript type nodes.
|
|
4
|
+
*
|
|
5
|
+
* Every extra field is optional so that any `Rule.Node` is assignable to these
|
|
6
|
+
* types — the handler parameters of the rules must accept whatever the parser
|
|
7
|
+
* hands them. Narrowing happens with `type`-discriminant checks inside each
|
|
8
|
+
* rule, exactly as in `no-arbitrary-tailwind.ts`.
|
|
9
|
+
*/
|
|
10
|
+
import type { Rule } from "eslint";
|
|
11
|
+
import type * as ESTree from "estree";
|
|
12
|
+
/** JSXIdentifier — the parser-specific name node of a JSX element or attribute. */
|
|
13
|
+
export interface JsxIdentifier {
|
|
14
|
+
type: "JSXIdentifier";
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
/** JSXMemberExpression — `<Foo.Bar>`, used by element and attribute names. */
|
|
18
|
+
export interface JsxMemberExpression {
|
|
19
|
+
type: "JSXMemberExpression";
|
|
20
|
+
object: JsxIdentifier | JsxMemberExpression;
|
|
21
|
+
property: JsxIdentifier;
|
|
22
|
+
}
|
|
23
|
+
/** JSXExpressionContainer — the `{expression}` part of a JSX attribute value. */
|
|
24
|
+
export interface JsxExpressionContainer {
|
|
25
|
+
type: "JSXExpressionContainer";
|
|
26
|
+
expression: ESTree.Expression;
|
|
27
|
+
}
|
|
28
|
+
/** JSXAttribute — `name="value"` or `name={expression}`. */
|
|
29
|
+
export type JsxAttribute = Omit<Rule.Node, "type"> & {
|
|
30
|
+
type: string;
|
|
31
|
+
name: JsxIdentifier;
|
|
32
|
+
value: ESTree.Literal | JsxExpressionContainer | null;
|
|
33
|
+
parent?: JsxOpeningElementNode;
|
|
34
|
+
};
|
|
35
|
+
/** JSXSpreadAttribute — `{...props}`. */
|
|
36
|
+
export interface JsxSpreadAttribute {
|
|
37
|
+
type: "JSXSpreadAttribute";
|
|
38
|
+
name?: never;
|
|
39
|
+
value?: never;
|
|
40
|
+
argument: ESTree.Expression;
|
|
41
|
+
}
|
|
42
|
+
/** Anything that can appear inside `attributes` of a JSX opening element. */
|
|
43
|
+
export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute;
|
|
44
|
+
/** JSXOpeningElement — the part before the closing `>` of an element. */
|
|
45
|
+
export interface JsxOpeningElement {
|
|
46
|
+
type: "JSXOpeningElement";
|
|
47
|
+
name: JsxIdentifier | JsxMemberExpression;
|
|
48
|
+
attributes: JsxAttributeLike[];
|
|
49
|
+
}
|
|
50
|
+
/** A node that may be a JSX element — used for parents and helper parameters. */
|
|
51
|
+
export type MaybeJsxElement = Rule.Node & {
|
|
52
|
+
openingElement?: JsxOpeningElement;
|
|
53
|
+
};
|
|
54
|
+
/** JSXElement as received by a `JSXElement` visitor. */
|
|
55
|
+
export type JsxElementNode = MaybeJsxElement & {
|
|
56
|
+
parent?: MaybeJsxElement;
|
|
57
|
+
};
|
|
58
|
+
/** JSXAttribute as received by a `JSXAttribute` visitor, with its parent chain. */
|
|
59
|
+
export type JsxAttributeNode = Rule.Node & {
|
|
60
|
+
name?: JsxIdentifier;
|
|
61
|
+
value?: ESTree.Literal | JsxExpressionContainer | null;
|
|
62
|
+
parent?: JsxOpeningElementNode;
|
|
63
|
+
};
|
|
64
|
+
/** JSXOpeningElement reached through a parent chain. */
|
|
65
|
+
export type JsxOpeningElementNode = Rule.Node & {
|
|
66
|
+
name?: JsxIdentifier;
|
|
67
|
+
attributes?: JsxAttributeLike[];
|
|
68
|
+
parent?: JsxElementNode;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* FunctionDeclaration as seen by a rule visitor. ESLint's ESTree typing says
|
|
72
|
+
* `id` is always present, but an anonymous `export default function () {}`
|
|
73
|
+
* reaches the visitor with `id: null`, so the field is kept nullable.
|
|
74
|
+
*/
|
|
75
|
+
export type FunctionDeclarationNode = Rule.Node & {
|
|
76
|
+
id?: {
|
|
77
|
+
name?: string;
|
|
78
|
+
} | null;
|
|
79
|
+
body?: ESTree.BlockStatement;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* TSTypeAliasDeclaration — `type X = ...`, from the typescript-eslint parser.
|
|
83
|
+
*
|
|
84
|
+
* Kept as a plain `Rule.Node` intersection so it can be passed to
|
|
85
|
+
* `context.report`, which requires an ESTree node.
|
|
86
|
+
*/
|
|
87
|
+
export type TsTypeAliasDeclarationNode = Rule.Node & {
|
|
88
|
+
id?: {
|
|
89
|
+
type?: string;
|
|
90
|
+
name?: string;
|
|
91
|
+
};
|
|
92
|
+
typeAnnotation?: TsTypeNode;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* A member of a type literal (TSPropertySignature, TSIndexSignature, ...).
|
|
96
|
+
*
|
|
97
|
+
* The `type` field is widened (via Omit) so it can be compared against the
|
|
98
|
+
* parser-specific "TSPropertySignature" string that ESTree's union lacks.
|
|
99
|
+
*/
|
|
100
|
+
export type TsTypeElementNode = Omit<Rule.Node, "type"> & {
|
|
101
|
+
type: string;
|
|
102
|
+
key?: {
|
|
103
|
+
type?: string;
|
|
104
|
+
name?: string;
|
|
105
|
+
};
|
|
106
|
+
typeAnnotation?: TsTypeAnnotationNode;
|
|
107
|
+
};
|
|
108
|
+
/** TSTypeAnnotation — the `: T` wrapper around a type node. */
|
|
109
|
+
export type TsTypeAnnotationNode = Omit<Rule.Node, "type"> & {
|
|
110
|
+
type: string;
|
|
111
|
+
typeAnnotation?: TsTypeNode;
|
|
112
|
+
};
|
|
113
|
+
/** Any TypeScript type node the rules read (TSTypeLiteral, TSUnionType, ...). */
|
|
114
|
+
export type TsTypeNode = Omit<Rule.Node, "type"> & {
|
|
115
|
+
type: string;
|
|
116
|
+
members?: TsTypeElementNode[];
|
|
117
|
+
types?: TsTypeNode[];
|
|
118
|
+
literal?: {
|
|
119
|
+
type?: string;
|
|
120
|
+
value?: unknown;
|
|
121
|
+
};
|
|
122
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -6,9 +6,64 @@
|
|
|
6
6
|
* documented requirement each rule covers is recorded in
|
|
7
7
|
* `enforcement/registry.json` and verified by `pasika coverage`.
|
|
8
8
|
*/
|
|
9
|
-
import type { Linter
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
import type { Linter } from "eslint";
|
|
10
|
+
import { mdRules } from "./rules/md/index.js";
|
|
11
|
+
import { cssRules } from "./rules/css/index.js";
|
|
12
|
+
import { jsonRules } from "./rules/json/index.js";
|
|
13
|
+
export declare const pasikaRules: {
|
|
14
|
+
"component-placement": import("eslint").Rule.RuleModule;
|
|
15
|
+
"support-file-placement": import("eslint").Rule.RuleModule;
|
|
16
|
+
"application-structure": import("eslint").Rule.RuleModule;
|
|
17
|
+
"named-exports": import("eslint").Rule.RuleModule;
|
|
18
|
+
"data-testid-case": import("eslint").Rule.RuleModule;
|
|
19
|
+
"support-folder-shape": import("eslint").Rule.RuleModule;
|
|
20
|
+
"import-through-index": import("eslint").Rule.RuleModule;
|
|
21
|
+
"util-file-name": import("eslint").Rule.RuleModule;
|
|
22
|
+
"no-util-barrel": import("eslint").Rule.RuleModule;
|
|
23
|
+
"jsx-hygiene": import("eslint").Rule.RuleModule;
|
|
24
|
+
"interactive-component": import("eslint").Rule.RuleModule;
|
|
25
|
+
"ui-state": import("eslint").Rule.RuleModule;
|
|
26
|
+
"filename-case": import("eslint").Rule.RuleModule;
|
|
27
|
+
"import-boundaries": import("eslint").Rule.RuleModule;
|
|
28
|
+
"no-mixed-concerns": import("eslint").Rule.RuleModule;
|
|
29
|
+
"no-arbitrary-tailwind": import("eslint").Rule.RuleModule;
|
|
30
|
+
"enforce-cn-merge": {
|
|
31
|
+
meta: {
|
|
32
|
+
schema: never[];
|
|
33
|
+
type: "problem";
|
|
34
|
+
docs: {
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
create(context: import("eslint").Rule.RuleContext): {
|
|
39
|
+
JSXAttribute(node: import("./ast-types.js").JsxAttributeNode): void;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
"enforce-cva-variant-props": import("eslint").Rule.RuleModule;
|
|
43
|
+
"cva-appearance-props": import("eslint").Rule.RuleModule;
|
|
44
|
+
"cva-boolean-variants": import("eslint").Rule.RuleModule;
|
|
45
|
+
"enforce-barrel-exports": import("eslint").Rule.RuleModule;
|
|
46
|
+
"cross-feature-import": import("eslint").Rule.RuleModule;
|
|
47
|
+
"pure-function-extract": import("eslint").Rule.RuleModule;
|
|
48
|
+
"hook-complexity": import("eslint").Rule.RuleModule;
|
|
49
|
+
"locale-dotted-path": import("eslint").Rule.RuleModule;
|
|
50
|
+
"locales-location": import("eslint").Rule.RuleModule;
|
|
51
|
+
"hook-extraction": import("eslint").Rule.RuleModule;
|
|
52
|
+
"value-extraction": import("eslint").Rule.RuleModule;
|
|
53
|
+
"config-extraction": import("eslint").Rule.RuleModule;
|
|
54
|
+
"component-nesting": import("eslint").Rule.RuleModule;
|
|
55
|
+
"stay-flat": import("eslint").Rule.RuleModule;
|
|
56
|
+
"type-extraction": import("eslint").Rule.RuleModule;
|
|
57
|
+
"locale-placement": import("eslint").Rule.RuleModule;
|
|
58
|
+
"shared-style-dedup": import("eslint").Rule.RuleModule;
|
|
59
|
+
"no-eslint-disable": import("eslint").Rule.RuleModule;
|
|
60
|
+
};
|
|
61
|
+
export { mdRules, cssRules, jsonRules };
|
|
12
62
|
/** Rule ids as they appear in configuration and in lint output. */
|
|
13
63
|
export declare const pasikaRuleIds: string[];
|
|
64
|
+
export declare const pasikaMdRuleIds: string[];
|
|
65
|
+
export declare const pasikaCssRuleIds: string[];
|
|
66
|
+
export declare const pasikaJsonRuleIds: string[];
|
|
67
|
+
/** Every rule id, as they appear in configuration and in lint output. */
|
|
68
|
+
export declare const allPasikaRuleIds: string[];
|
|
14
69
|
export declare const pasikaConfig: Linter.Config;
|
|
@@ -7,20 +7,80 @@ import { enforceCvaVariantPropsRule } from "./rules/enforce-cva-variant-props.js
|
|
|
7
7
|
import { enforceBarrelExportsRule } from "./rules/enforce-barrel-exports.js";
|
|
8
8
|
import { componentPlacementRule } from "./rules/component-placement.js";
|
|
9
9
|
import { supportFilePlacementRule } from "./rules/support-file-placement.js";
|
|
10
|
-
|
|
10
|
+
import { applicationStructureRule } from "./rules/application-structure.js";
|
|
11
|
+
import { namedExportsRule } from "./rules/named-exports.js";
|
|
12
|
+
import { dataTestIdCaseRule } from "./rules/data-testid-case.js";
|
|
13
|
+
import { supportFolderShapeRule } from "./rules/support-folder-shape.js";
|
|
14
|
+
import { importThroughIndexRule } from "./rules/import-through-index.js";
|
|
15
|
+
import { utilFileNameRule } from "./rules/util-file-name.js";
|
|
16
|
+
import { noUtilBarrelRule } from "./rules/no-util-barrel.js";
|
|
17
|
+
import { jsxHygieneRule } from "./rules/jsx-hygiene.js";
|
|
18
|
+
import { interactiveComponentRule } from "./rules/interactive-component.js";
|
|
19
|
+
import { uiStateRule } from "./rules/ui-state.js";
|
|
20
|
+
import { cvaAppearancePropsRule } from "./rules/cva-appearance-props.js";
|
|
21
|
+
import { cvaBooleanVariantsRule } from "./rules/cva-boolean-variants.js";
|
|
22
|
+
import { crossFeatureImportRule } from "./rules/cross-feature-import.js";
|
|
23
|
+
import { pureFunctionExtractRule } from "./rules/pure-function-extract.js";
|
|
24
|
+
import { hookComplexityRule } from "./rules/hook-complexity.js";
|
|
25
|
+
import { localeDottedPathRule } from "./rules/locale-dotted-path.js";
|
|
26
|
+
import { localesLocationRule } from "./rules/locales-location.js";
|
|
27
|
+
import { hookExtractionRule } from "./rules/hook-extraction.js";
|
|
28
|
+
import { valueExtractionRule } from "./rules/value-extraction.js";
|
|
29
|
+
import { configExtractionRule } from "./rules/config-extraction.js";
|
|
30
|
+
import { componentNestingRule } from "./rules/component-nesting.js";
|
|
31
|
+
import { stayFlatRule } from "./rules/stay-flat.js";
|
|
32
|
+
import { typeExtractionRule } from "./rules/type-extraction.js";
|
|
33
|
+
import { localePlacementRule } from "./rules/locale-placement.js";
|
|
34
|
+
import { sharedStyleDedupRule } from "./rules/shared-style-dedup.js";
|
|
35
|
+
import { noEslintDisableRule } from "./rules/no-eslint-disable.js";
|
|
36
|
+
import { mdRules } from "./rules/md/index.js";
|
|
37
|
+
import { cssRules } from "./rules/css/index.js";
|
|
38
|
+
import { jsonRules } from "./rules/json/index.js";
|
|
11
39
|
export const pasikaRules = {
|
|
12
40
|
"component-placement": componentPlacementRule,
|
|
13
41
|
"support-file-placement": supportFilePlacementRule,
|
|
42
|
+
"application-structure": applicationStructureRule,
|
|
43
|
+
"named-exports": namedExportsRule,
|
|
44
|
+
"data-testid-case": dataTestIdCaseRule,
|
|
45
|
+
"support-folder-shape": supportFolderShapeRule,
|
|
46
|
+
"import-through-index": importThroughIndexRule,
|
|
47
|
+
"util-file-name": utilFileNameRule,
|
|
48
|
+
"no-util-barrel": noUtilBarrelRule,
|
|
49
|
+
"jsx-hygiene": jsxHygieneRule,
|
|
50
|
+
"interactive-component": interactiveComponentRule,
|
|
51
|
+
"ui-state": uiStateRule,
|
|
14
52
|
"filename-case": filenameCaseRule,
|
|
15
53
|
"import-boundaries": importBoundariesRule,
|
|
16
54
|
"no-mixed-concerns": noMixedConcernsRule,
|
|
17
55
|
"no-arbitrary-tailwind": noArbitraryTailwindRule,
|
|
18
56
|
"enforce-cn-merge": enforceCnMergeRule,
|
|
19
57
|
"enforce-cva-variant-props": enforceCvaVariantPropsRule,
|
|
58
|
+
"cva-appearance-props": cvaAppearancePropsRule,
|
|
59
|
+
"cva-boolean-variants": cvaBooleanVariantsRule,
|
|
20
60
|
"enforce-barrel-exports": enforceBarrelExportsRule,
|
|
61
|
+
"cross-feature-import": crossFeatureImportRule,
|
|
62
|
+
"pure-function-extract": pureFunctionExtractRule,
|
|
63
|
+
"hook-complexity": hookComplexityRule,
|
|
64
|
+
"locale-dotted-path": localeDottedPathRule,
|
|
65
|
+
"locales-location": localesLocationRule,
|
|
66
|
+
"hook-extraction": hookExtractionRule,
|
|
67
|
+
"value-extraction": valueExtractionRule,
|
|
68
|
+
"config-extraction": configExtractionRule,
|
|
69
|
+
"component-nesting": componentNestingRule,
|
|
70
|
+
"stay-flat": stayFlatRule,
|
|
71
|
+
"type-extraction": typeExtractionRule,
|
|
72
|
+
"locale-placement": localePlacementRule,
|
|
73
|
+
"shared-style-dedup": sharedStyleDedupRule,
|
|
74
|
+
"no-eslint-disable": noEslintDisableRule,
|
|
21
75
|
};
|
|
76
|
+
export { mdRules, cssRules, jsonRules };
|
|
22
77
|
/** Rule ids as they appear in configuration and in lint output. */
|
|
23
78
|
export const pasikaRuleIds = Object.keys(pasikaRules).map((name) => `pasika/${name}`);
|
|
79
|
+
export const pasikaMdRuleIds = Object.keys(mdRules).map((name) => `pasika/${name}`);
|
|
80
|
+
export const pasikaCssRuleIds = Object.keys(cssRules).map((name) => `pasika/${name}`);
|
|
81
|
+
export const pasikaJsonRuleIds = Object.keys(jsonRules).map((name) => `pasika/${name}`);
|
|
82
|
+
/** Every rule id, as they appear in configuration and in lint output. */
|
|
83
|
+
export const allPasikaRuleIds = [...pasikaRuleIds, ...pasikaMdRuleIds, ...pasikaCssRuleIds, ...pasikaJsonRuleIds];
|
|
24
84
|
export const pasikaConfig = {
|
|
25
85
|
files: ["src/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}"],
|
|
26
86
|
plugins: {
|