pasika 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +72 -20
  2. package/dist/cli/index.d.ts +0 -1
  3. package/dist/cli/index.js +4999 -103
  4. package/dist/eslint/pasika/index.d.ts +165 -53
  5. package/dist/eslint/pasika/index.js +4424 -89
  6. package/package.json +5 -6
  7. package/dist/cli/output.d.ts +0 -3
  8. package/dist/cli/output.js +0 -11
  9. package/dist/enforcement/coverage.d.ts +0 -51
  10. package/dist/enforcement/coverage.js +0 -206
  11. package/dist/enforcement/doctor.d.ts +0 -16
  12. package/dist/enforcement/doctor.js +0 -249
  13. package/dist/enforcement/normalize.d.ts +0 -11
  14. package/dist/enforcement/normalize.js +0 -21
  15. package/dist/enforcement/parse-docs.d.ts +0 -58
  16. package/dist/enforcement/parse-docs.js +0 -94
  17. package/dist/enforcement/types.d.ts +0 -54
  18. package/dist/enforcement/types.js +0 -50
  19. package/dist/eslint/pasika/ast-types.d.ts +0 -122
  20. package/dist/eslint/pasika/ast-types.js +0 -1
  21. package/dist/eslint/pasika/project/ccf.d.ts +0 -48
  22. package/dist/eslint/pasika/project/ccf.js +0 -119
  23. package/dist/eslint/pasika/project/index.d.ts +0 -21
  24. package/dist/eslint/pasika/project/index.js +0 -139
  25. package/dist/eslint/pasika/project/parse-module.d.ts +0 -27
  26. package/dist/eslint/pasika/project/parse-module.js +0 -128
  27. package/dist/eslint/pasika/rules/application-structure.d.ts +0 -2
  28. package/dist/eslint/pasika/rules/application-structure.js +0 -152
  29. package/dist/eslint/pasika/rules/component-conventions.d.ts +0 -16
  30. package/dist/eslint/pasika/rules/component-conventions.js +0 -145
  31. package/dist/eslint/pasika/rules/component-nesting.d.ts +0 -12
  32. package/dist/eslint/pasika/rules/component-nesting.js +0 -69
  33. package/dist/eslint/pasika/rules/component-placement.d.ts +0 -11
  34. package/dist/eslint/pasika/rules/component-placement.js +0 -75
  35. package/dist/eslint/pasika/rules/config-extraction.d.ts +0 -14
  36. package/dist/eslint/pasika/rules/config-extraction.js +0 -74
  37. package/dist/eslint/pasika/rules/cross-feature-import.d.ts +0 -2
  38. package/dist/eslint/pasika/rules/cross-feature-import.js +0 -75
  39. package/dist/eslint/pasika/rules/css/apply-usage.d.ts +0 -10
  40. package/dist/eslint/pasika/rules/css/apply-usage.js +0 -45
  41. package/dist/eslint/pasika/rules/css/base-layer-pair.d.ts +0 -10
  42. package/dist/eslint/pasika/rules/css/base-layer-pair.js +0 -49
  43. package/dist/eslint/pasika/rules/css/css-variable-naming.d.ts +0 -10
  44. package/dist/eslint/pasika/rules/css/css-variable-naming.js +0 -39
  45. package/dist/eslint/pasika/rules/css/custom-utility-apply.d.ts +0 -11
  46. package/dist/eslint/pasika/rules/css/custom-utility-apply.js +0 -52
  47. package/dist/eslint/pasika/rules/css/global-css-location.d.ts +0 -10
  48. package/dist/eslint/pasika/rules/css/global-css-location.js +0 -42
  49. package/dist/eslint/pasika/rules/css/helpers.d.ts +0 -29
  50. package/dist/eslint/pasika/rules/css/helpers.js +0 -93
  51. package/dist/eslint/pasika/rules/css/index.d.ts +0 -13
  52. package/dist/eslint/pasika/rules/css/index.js +0 -25
  53. package/dist/eslint/pasika/rules/css/root-variables.d.ts +0 -11
  54. package/dist/eslint/pasika/rules/css/root-variables.js +0 -53
  55. package/dist/eslint/pasika/rules/css/rule-tester.d.ts +0 -11
  56. package/dist/eslint/pasika/rules/css/rule-tester.js +0 -22
  57. package/dist/eslint/pasika/rules/css/stylesheet-ordering.d.ts +0 -11
  58. package/dist/eslint/pasika/rules/css/stylesheet-ordering.js +0 -76
  59. package/dist/eslint/pasika/rules/css/surface-utility.d.ts +0 -10
  60. package/dist/eslint/pasika/rules/css/surface-utility.js +0 -41
  61. package/dist/eslint/pasika/rules/css/theme-reset.d.ts +0 -9
  62. package/dist/eslint/pasika/rules/css/theme-reset.js +0 -36
  63. package/dist/eslint/pasika/rules/css/theme-variable-namespace.d.ts +0 -11
  64. package/dist/eslint/pasika/rules/css/theme-variable-namespace.js +0 -53
  65. package/dist/eslint/pasika/rules/cva-appearance-props.d.ts +0 -10
  66. package/dist/eslint/pasika/rules/cva-appearance-props.js +0 -77
  67. package/dist/eslint/pasika/rules/cva-boolean-variants.d.ts +0 -11
  68. package/dist/eslint/pasika/rules/cva-boolean-variants.js +0 -97
  69. package/dist/eslint/pasika/rules/data-testid-case.d.ts +0 -2
  70. package/dist/eslint/pasika/rules/data-testid-case.js +0 -76
  71. package/dist/eslint/pasika/rules/enforce-barrel-exports.d.ts +0 -9
  72. package/dist/eslint/pasika/rules/enforce-barrel-exports.js +0 -78
  73. package/dist/eslint/pasika/rules/enforce-cn-merge.d.ts +0 -21
  74. package/dist/eslint/pasika/rules/enforce-cn-merge.js +0 -86
  75. package/dist/eslint/pasika/rules/enforce-cva-variant-props.d.ts +0 -9
  76. package/dist/eslint/pasika/rules/enforce-cva-variant-props.js +0 -86
  77. package/dist/eslint/pasika/rules/filename-case.d.ts +0 -2
  78. package/dist/eslint/pasika/rules/filename-case.js +0 -123
  79. package/dist/eslint/pasika/rules/hook-complexity.d.ts +0 -12
  80. package/dist/eslint/pasika/rules/hook-complexity.js +0 -114
  81. package/dist/eslint/pasika/rules/hook-extraction.d.ts +0 -11
  82. package/dist/eslint/pasika/rules/hook-extraction.js +0 -55
  83. package/dist/eslint/pasika/rules/import-boundaries.d.ts +0 -2
  84. package/dist/eslint/pasika/rules/import-boundaries.js +0 -167
  85. package/dist/eslint/pasika/rules/import-through-index.d.ts +0 -2
  86. package/dist/eslint/pasika/rules/import-through-index.js +0 -52
  87. package/dist/eslint/pasika/rules/interactive-component.d.ts +0 -10
  88. package/dist/eslint/pasika/rules/interactive-component.js +0 -76
  89. package/dist/eslint/pasika/rules/json/index.d.ts +0 -5
  90. package/dist/eslint/pasika/rules/json/index.js +0 -9
  91. package/dist/eslint/pasika/rules/json/no-cache-flag.d.ts +0 -10
  92. package/dist/eslint/pasika/rules/json/no-cache-flag.js +0 -45
  93. package/dist/eslint/pasika/rules/json/no-vulyk-dependency.d.ts +0 -10
  94. package/dist/eslint/pasika/rules/json/no-vulyk-dependency.js +0 -41
  95. package/dist/eslint/pasika/rules/json/rule-tester.d.ts +0 -11
  96. package/dist/eslint/pasika/rules/json/rule-tester.js +0 -21
  97. package/dist/eslint/pasika/rules/jsx-hygiene.d.ts +0 -9
  98. package/dist/eslint/pasika/rules/jsx-hygiene.js +0 -108
  99. package/dist/eslint/pasika/rules/locale-dotted-path.d.ts +0 -10
  100. package/dist/eslint/pasika/rules/locale-dotted-path.js +0 -65
  101. package/dist/eslint/pasika/rules/locale-placement.d.ts +0 -13
  102. package/dist/eslint/pasika/rules/locale-placement.js +0 -163
  103. package/dist/eslint/pasika/rules/locales-location.d.ts +0 -11
  104. package/dist/eslint/pasika/rules/locales-location.js +0 -56
  105. package/dist/eslint/pasika/rules/md/doc-kind-suffix.d.ts +0 -5
  106. package/dist/eslint/pasika/rules/md/doc-kind-suffix.js +0 -29
  107. package/dist/eslint/pasika/rules/md/example-heading-description.d.ts +0 -5
  108. package/dist/eslint/pasika/rules/md/example-heading-description.js +0 -30
  109. package/dist/eslint/pasika/rules/md/glossary-term-linking.d.ts +0 -2
  110. package/dist/eslint/pasika/rules/md/glossary-term-linking.js +0 -99
  111. package/dist/eslint/pasika/rules/md/guide-folder-entry-point.d.ts +0 -2
  112. package/dist/eslint/pasika/rules/md/guide-folder-entry-point.js +0 -46
  113. package/dist/eslint/pasika/rules/md/guide-link-anchors.d.ts +0 -5
  114. package/dist/eslint/pasika/rules/md/guide-link-anchors.js +0 -50
  115. package/dist/eslint/pasika/rules/md/guide-overview-no-links.d.ts +0 -5
  116. package/dist/eslint/pasika/rules/md/guide-overview-no-links.js +0 -51
  117. package/dist/eslint/pasika/rules/md/guide-states-no-requirement.d.ts +0 -5
  118. package/dist/eslint/pasika/rules/md/guide-states-no-requirement.js +0 -26
  119. package/dist/eslint/pasika/rules/md/guide-step-single-link.d.ts +0 -5
  120. package/dist/eslint/pasika/rules/md/guide-step-single-link.js +0 -48
  121. package/dist/eslint/pasika/rules/md/guide-step-single-sentence.d.ts +0 -5
  122. package/dist/eslint/pasika/rules/md/guide-step-single-sentence.js +0 -42
  123. package/dist/eslint/pasika/rules/md/helpers.d.ts +0 -25
  124. package/dist/eslint/pasika/rules/md/helpers.js +0 -37
  125. package/dist/eslint/pasika/rules/md/index.d.ts +0 -27
  126. package/dist/eslint/pasika/rules/md/index.js +0 -53
  127. package/dist/eslint/pasika/rules/md/no-cross-document-link.d.ts +0 -5
  128. package/dist/eslint/pasika/rules/md/no-cross-document-link.js +0 -36
  129. package/dist/eslint/pasika/rules/md/no-nested-how-to.d.ts +0 -5
  130. package/dist/eslint/pasika/rules/md/no-nested-how-to.js +0 -39
  131. package/dist/eslint/pasika/rules/md/no-template-prompt.d.ts +0 -5
  132. package/dist/eslint/pasika/rules/md/no-template-prompt.js +0 -22
  133. package/dist/eslint/pasika/rules/md/overview-length.d.ts +0 -5
  134. package/dist/eslint/pasika/rules/md/overview-length.js +0 -47
  135. package/dist/eslint/pasika/rules/md/overview-present.d.ts +0 -5
  136. package/dist/eslint/pasika/rules/md/overview-present.js +0 -41
  137. package/dist/eslint/pasika/rules/md/policy-no-examples.d.ts +0 -5
  138. package/dist/eslint/pasika/rules/md/policy-no-examples.js +0 -28
  139. package/dist/eslint/pasika/rules/md/policy-single-document.d.ts +0 -5
  140. package/dist/eslint/pasika/rules/md/policy-single-document.js +0 -34
  141. package/dist/eslint/pasika/rules/md/policy-subject-headings.d.ts +0 -5
  142. package/dist/eslint/pasika/rules/md/policy-subject-headings.js +0 -58
  143. package/dist/eslint/pasika/rules/md/project-index.d.ts +0 -17
  144. package/dist/eslint/pasika/rules/md/project-index.js +0 -67
  145. package/dist/eslint/pasika/rules/md/reference-block-headings.d.ts +0 -6
  146. package/dist/eslint/pasika/rules/md/reference-block-headings.js +0 -31
  147. package/dist/eslint/pasika/rules/md/reference-no-rfc-vocabulary.d.ts +0 -6
  148. package/dist/eslint/pasika/rules/md/reference-no-rfc-vocabulary.js +0 -26
  149. package/dist/eslint/pasika/rules/md/requirement-present.d.ts +0 -5
  150. package/dist/eslint/pasika/rules/md/requirement-present.js +0 -41
  151. package/dist/eslint/pasika/rules/md/rfc-only-in-bullets.d.ts +0 -5
  152. package/dist/eslint/pasika/rules/md/rfc-only-in-bullets.js +0 -46
  153. package/dist/eslint/pasika/rules/md/rule-paired-examples.d.ts +0 -5
  154. package/dist/eslint/pasika/rules/md/rule-paired-examples.js +0 -36
  155. package/dist/eslint/pasika/rules/md/rule-tester.d.ts +0 -11
  156. package/dist/eslint/pasika/rules/md/rule-tester.js +0 -21
  157. package/dist/eslint/pasika/rules/md/support-document-placement.d.ts +0 -2
  158. package/dist/eslint/pasika/rules/md/support-document-placement.js +0 -36
  159. package/dist/eslint/pasika/rules/md/title-matches-file-name.d.ts +0 -2
  160. package/dist/eslint/pasika/rules/md/title-matches-file-name.js +0 -46
  161. package/dist/eslint/pasika/rules/named-exports.d.ts +0 -2
  162. package/dist/eslint/pasika/rules/named-exports.js +0 -49
  163. package/dist/eslint/pasika/rules/no-arbitrary-tailwind.d.ts +0 -9
  164. package/dist/eslint/pasika/rules/no-arbitrary-tailwind.js +0 -97
  165. package/dist/eslint/pasika/rules/no-eslint-disable.d.ts +0 -10
  166. package/dist/eslint/pasika/rules/no-eslint-disable.js +0 -33
  167. package/dist/eslint/pasika/rules/no-mixed-concerns.d.ts +0 -9
  168. package/dist/eslint/pasika/rules/no-mixed-concerns.js +0 -71
  169. package/dist/eslint/pasika/rules/no-util-barrel.d.ts +0 -2
  170. package/dist/eslint/pasika/rules/no-util-barrel.js +0 -48
  171. package/dist/eslint/pasika/rules/pure-function-extract.d.ts +0 -9
  172. package/dist/eslint/pasika/rules/pure-function-extract.js +0 -92
  173. package/dist/eslint/pasika/rules/shared-style-dedup.d.ts +0 -13
  174. package/dist/eslint/pasika/rules/shared-style-dedup.js +0 -91
  175. package/dist/eslint/pasika/rules/stay-flat.d.ts +0 -12
  176. package/dist/eslint/pasika/rules/stay-flat.js +0 -81
  177. package/dist/eslint/pasika/rules/support-file-placement.d.ts +0 -15
  178. package/dist/eslint/pasika/rules/support-file-placement.js +0 -70
  179. package/dist/eslint/pasika/rules/support-folder-shape.d.ts +0 -2
  180. package/dist/eslint/pasika/rules/support-folder-shape.js +0 -59
  181. package/dist/eslint/pasika/rules/type-extraction.d.ts +0 -13
  182. package/dist/eslint/pasika/rules/type-extraction.js +0 -85
  183. package/dist/eslint/pasika/rules/ui-state.d.ts +0 -9
  184. package/dist/eslint/pasika/rules/ui-state.js +0 -99
  185. package/dist/eslint/pasika/rules/util-file-name.d.ts +0 -2
  186. package/dist/eslint/pasika/rules/util-file-name.js +0 -48
  187. package/dist/eslint/pasika/rules/value-extraction.d.ts +0 -13
  188. package/dist/eslint/pasika/rules/value-extraction.js +0 -48
  189. package/enforcement/registry.json +0 -1145
@@ -1,67 +0,0 @@
1
- /**
2
- * @fileoverview Shared project index for cross-document markdown rules.
3
- * Scans the docs folder and caches document metadata.
4
- */
5
- import { readdirSync, readFileSync, statSync } from "node:fs";
6
- import path from "node:path";
7
- const KIND_BY_SUFFIX = [
8
- ["-rule.md", "rule"],
9
- ["-guide.md", "guide"],
10
- ["-reference.md", "reference"],
11
- ["-policy.md", "policy"],
12
- ];
13
- function listMarkdownFiles(dir) {
14
- return readdirSync(dir).flatMap((entry) => {
15
- const entryPath = path.join(dir, entry);
16
- if (statSync(entryPath).isDirectory()) {
17
- return entry.startsWith("_") ? [] : listMarkdownFiles(entryPath);
18
- }
19
- return entry.endsWith(".md") ? [entryPath] : [];
20
- });
21
- }
22
- function extractTitle(filePath) {
23
- const content = readFileSync(filePath, "utf8");
24
- const match = /^# (?<title>.+)$/m.exec(content);
25
- return match?.groups?.title?.trim() ?? "";
26
- }
27
- // Memoized cache keyed by docsRoot
28
- const cache = new Map();
29
- /**
30
- * Get all documents in the docs folder. Results are cached per docsRoot.
31
- */
32
- export function getProjectDocs(docsRoot) {
33
- const cached = cache.get(docsRoot);
34
- if (cached)
35
- return cached;
36
- const files = listMarkdownFiles(docsRoot);
37
- const docs = files
38
- .sort((a, b) => a.localeCompare(b))
39
- .map((filePath) => {
40
- const fileName = path.basename(filePath);
41
- const kind = KIND_BY_SUFFIX.find(([suffix]) => fileName.endsWith(suffix))?.[1];
42
- return {
43
- filePath,
44
- doc: path.relative(docsRoot, filePath).split(path.sep).join("/"),
45
- fileName,
46
- kind,
47
- title: extractTitle(filePath),
48
- };
49
- });
50
- cache.set(docsRoot, docs);
51
- return docs;
52
- }
53
- /**
54
- * Try to find the docsRoot from a file path by walking up to find the docs/ directory.
55
- */
56
- export function findDocsRoot(filePath) {
57
- let dir = path.dirname(filePath);
58
- for (;;) {
59
- if (path.basename(dir) === "docs" && statSync(dir).isDirectory()) {
60
- return dir;
61
- }
62
- const parent = path.dirname(dir);
63
- if (parent === dir)
64
- return undefined;
65
- dir = parent;
66
- }
67
- }
@@ -1,6 +0,0 @@
1
- /**
2
- * @fileoverview Reference block headings rule.
3
- * A Reference with a single lookup block must not add a section heading for it.
4
- */
5
- import type { MarkdownRuleDefinition } from "@eslint/markdown";
6
- export declare const referenceBlockHeadingsRule: MarkdownRuleDefinition;
@@ -1,31 +0,0 @@
1
- import { getFilename } from "./helpers.js";
2
- export const referenceBlockHeadingsRule = {
3
- meta: {
4
- type: "problem",
5
- docs: {
6
- description: "Reference block headings rule.",
7
- recommended: true,
8
- },
9
- },
10
- create(context) {
11
- return {
12
- root(node) {
13
- const filename = getFilename(context);
14
- if (!filename.endsWith("-reference.md"))
15
- return;
16
- const sectionHeadings = [];
17
- for (const child of node.children) {
18
- if (child.type === "heading" && child.depth >= 2) {
19
- sectionHeadings.push(child);
20
- }
21
- }
22
- if (sectionHeadings.length === 1) {
23
- context.report({
24
- node: node.children[0] ?? node,
25
- message: "reference has exactly one section heading, so either a single block is headed or a first block is not",
26
- });
27
- }
28
- },
29
- };
30
- },
31
- };
@@ -1,6 +0,0 @@
1
- /**
2
- * @fileoverview Reference documents must not use RFC 2119 vocabulary in prose.
3
- * Only naming a keyword in code spans is permitted.
4
- */
5
- import type { MarkdownRuleDefinition } from "@eslint/markdown";
6
- export declare const referenceNoRfcVocabularyRule: MarkdownRuleDefinition;
@@ -1,26 +0,0 @@
1
- import { containsRfcKeyword, getFilename } from "./helpers.js";
2
- export const referenceNoRfcVocabularyRule = {
3
- meta: {
4
- type: "problem",
5
- docs: {
6
- description: "Reference documents must not use RFC 2119 vocabulary (MUST, MUST NOT, SHOULD, SHOULD NOT, MAY) in prose.",
7
- recommended: true,
8
- },
9
- },
10
- create(context) {
11
- return {
12
- text(node) {
13
- const filename = getFilename(context);
14
- if (!filename.endsWith("-reference.md"))
15
- return;
16
- const keyword = containsRfcKeyword(node.value);
17
- if (keyword) {
18
- context.report({
19
- node,
20
- message: `reference uses RFC 2119 vocabulary: ${keyword}`,
21
- });
22
- }
23
- },
24
- };
25
- },
26
- };
@@ -1,5 +0,0 @@
1
- /**
2
- * @fileoverview Rule and policy documents must state at least one requirement.
3
- */
4
- import type { MarkdownRuleDefinition } from "@eslint/markdown";
5
- export declare const requirementPresentRule: MarkdownRuleDefinition;
@@ -1,41 +0,0 @@
1
- import { containsRfcKeyword, getFilename, getTextContent } from "./helpers.js";
2
- /** Check whether a subtree contains a bullet with RFC vocabulary. */
3
- function hasRequirement(node) {
4
- if (node.type === "listItem") {
5
- if (containsRfcKeyword(getTextContent(node)))
6
- return true;
7
- }
8
- if ("children" in node) {
9
- for (const child of node.children) {
10
- if (hasRequirement(child))
11
- return true;
12
- }
13
- }
14
- return false;
15
- }
16
- export const requirementPresentRule = {
17
- meta: {
18
- type: "problem",
19
- docs: {
20
- description: "Rule and policy documents must state at least one requirement.",
21
- recommended: true,
22
- },
23
- },
24
- create(context) {
25
- return {
26
- root(node) {
27
- const filename = getFilename(context);
28
- if (!filename.endsWith("-rule.md") && !filename.endsWith("-policy.md")) {
29
- return;
30
- }
31
- if (!hasRequirement(node)) {
32
- const kind = filename.endsWith("-rule.md") ? "rule" : "policy";
33
- context.report({
34
- node,
35
- message: `${kind} document states no requirement`,
36
- });
37
- }
38
- },
39
- };
40
- },
41
- };
@@ -1,5 +0,0 @@
1
- /**
2
- * @fileoverview RFC 2119 vocabulary must appear only in bullet points.
3
- */
4
- import type { MarkdownRuleDefinition } from "@eslint/markdown";
5
- export declare const rfcOnlyInBulletsRule: MarkdownRuleDefinition;
@@ -1,46 +0,0 @@
1
- import { containsRfcKeyword, getFilename } from "./helpers.js";
2
- /** Check whether a subtree contains a text node outside a bullet using RFC vocabulary. */
3
- function checkOutsideBullets(node, context, insideBullet) {
4
- if (node.type === "listItem") {
5
- // Everything under a list item is a bullet; stop descending for the
6
- // outside-bullet check, but nested lists still need walking.
7
- for (const child of node.children) {
8
- checkOutsideBullets(child, context, true);
9
- }
10
- return;
11
- }
12
- if (node.type === "text") {
13
- const keyword = containsRfcKeyword(node.value);
14
- if (keyword && !insideBullet) {
15
- context.report({
16
- node,
17
- message: `RFC 2119 vocabulary "${keyword}" appears outside a bullet point`,
18
- });
19
- }
20
- return;
21
- }
22
- if ("children" in node) {
23
- for (const child of node.children) {
24
- checkOutsideBullets(child, context, insideBullet);
25
- }
26
- }
27
- }
28
- export const rfcOnlyInBulletsRule = {
29
- meta: {
30
- type: "problem",
31
- docs: {
32
- description: "RFC 2119 vocabulary must appear only in bullet points.",
33
- recommended: true,
34
- },
35
- },
36
- create(context) {
37
- return {
38
- root(node) {
39
- const filename = getFilename(context);
40
- if (!filename.endsWith("-rule.md"))
41
- return;
42
- checkOutsideBullets(node, context, false);
43
- },
44
- };
45
- },
46
- };
@@ -1,5 +0,0 @@
1
- /**
2
- * @fileoverview Rule must have paired Incorrect/Correct examples.
3
- */
4
- import type { MarkdownRuleDefinition } from "@eslint/markdown";
5
- export declare const rulePairedExamplesRule: MarkdownRuleDefinition;
@@ -1,36 +0,0 @@
1
- import { getFilename, getLine, getTextContent } from "./helpers.js";
2
- export const rulePairedExamplesRule = {
3
- meta: {
4
- type: "problem",
5
- docs: {
6
- description: "Rule must have paired Incorrect/Correct examples.",
7
- recommended: true,
8
- },
9
- },
10
- create(context) {
11
- return {
12
- root(node) {
13
- const filename = getFilename(context);
14
- if (!filename.endsWith("-rule.md"))
15
- return;
16
- const exampleHeadings = [];
17
- for (const child of node.children) {
18
- if (child.type === "heading" && child.depth === 2) {
19
- const text = getTextContent(child).trim();
20
- if (/^(?:Incorrect|Correct)\b/.test(text)) {
21
- exampleHeadings.push({ line: getLine(child), text });
22
- }
23
- }
24
- }
25
- const incorrect = exampleHeadings.filter((h) => h.text.startsWith("Incorrect"));
26
- const correct = exampleHeadings.filter((h) => h.text.startsWith("Correct"));
27
- if (incorrect.length === 0 || incorrect.length !== correct.length) {
28
- context.report({
29
- node,
30
- message: `${String(incorrect.length)} Incorrect and ${String(correct.length)} Correct examples`,
31
- });
32
- }
33
- },
34
- };
35
- },
36
- };
@@ -1,11 +0,0 @@
1
- /**
2
- * Shared RuleTester wiring for the markdown rule tests.
3
- *
4
- * Each `describe` title in a rule test is the exact text of the requirement the
5
- * case pins, which is how `pasika coverage` verifies that a requirement recorded
6
- * as lint-enforced has a test behind it.
7
- */
8
- import { describe } from "node:test";
9
- import { RuleTester } from "eslint";
10
- export declare const mdRuleTester: RuleTester;
11
- export { describe };
@@ -1,21 +0,0 @@
1
- /**
2
- * Shared RuleTester wiring for the markdown rule tests.
3
- *
4
- * Each `describe` title in a rule test is the exact text of the requirement the
5
- * case pins, which is how `pasika coverage` verifies that a requirement recorded
6
- * as lint-enforced has a test behind it.
7
- */
8
- import { describe, it } from "node:test";
9
- import { RuleTester } from "eslint";
10
- import markdown from "@eslint/markdown";
11
- import { mdRules } from "../../index.js";
12
- RuleTester.describe = describe;
13
- RuleTester.it = it;
14
- export const mdRuleTester = new RuleTester({
15
- language: "markdown/gfm",
16
- plugins: {
17
- markdown,
18
- pasika: { rules: mdRules },
19
- },
20
- });
21
- export { describe };
@@ -1,2 +0,0 @@
1
- import type { MarkdownRuleDefinition } from "@eslint/markdown";
2
- export declare const supportDocumentPlacementRule: MarkdownRuleDefinition;
@@ -1,36 +0,0 @@
1
- /**
2
- * @fileoverview Rules must live in rules/, references must live in references/.
3
- */
4
- import path from "node:path";
5
- import { getFilename } from "./helpers.js";
6
- export const supportDocumentPlacementRule = {
7
- meta: {
8
- type: "problem",
9
- docs: {
10
- description: "Rules must live in rules/, references must live in references/.",
11
- recommended: true,
12
- },
13
- },
14
- create(context) {
15
- return {
16
- root(node) {
17
- const filename = getFilename(context);
18
- if (!filename.endsWith(".md"))
19
- return;
20
- const parentFolder = path.basename(path.dirname(filename));
21
- if (filename.endsWith("-rule.md") && parentFolder !== "rules") {
22
- context.report({
23
- node,
24
- message: `rule lives in "${parentFolder}/" instead of "rules/"`,
25
- });
26
- }
27
- if (filename.endsWith("-reference.md") && parentFolder !== "references") {
28
- context.report({
29
- node,
30
- message: `reference lives in "${parentFolder}/" instead of "references/"`,
31
- });
32
- }
33
- },
34
- };
35
- },
36
- };
@@ -1,2 +0,0 @@
1
- import type { MarkdownRuleDefinition } from "@eslint/markdown";
2
- export declare const titleMatchesFileNameRule: MarkdownRuleDefinition;
@@ -1,46 +0,0 @@
1
- /**
2
- * @fileoverview Document title must match the file name in kebab-case.
3
- */
4
- import path from "node:path";
5
- import { getFilename, getTextContent } from "./helpers.js";
6
- function toExpectedFileName(title) {
7
- return `${title
8
- .toLowerCase()
9
- .replaceAll(/[^a-z0-9]+/g, "-")
10
- .replace(/^-|-$/g, "")}.md`;
11
- }
12
- export const titleMatchesFileNameRule = {
13
- meta: {
14
- type: "problem",
15
- docs: {
16
- description: "Document title must match the file name in kebab-case.",
17
- recommended: true,
18
- },
19
- },
20
- create(context) {
21
- return {
22
- heading(node) {
23
- if (node.depth !== 1)
24
- return;
25
- const filename = getFilename(context);
26
- if (!filename.endsWith(".md"))
27
- return;
28
- const title = getTextContent(node).trim();
29
- const expectedFileName = toExpectedFileName(title);
30
- const actualFileName = path.basename(filename);
31
- if (!title) {
32
- context.report({
33
- node,
34
- message: "document has no `# Title` heading",
35
- });
36
- }
37
- else if (expectedFileName !== actualFileName) {
38
- context.report({
39
- node,
40
- message: `title "${title}" expects file name ${expectedFileName}`,
41
- });
42
- }
43
- },
44
- };
45
- },
46
- };
@@ -1,2 +0,0 @@
1
- import type { Rule } from "eslint";
2
- export declare const namedExportsRule: Rule.RuleModule;
@@ -1,49 +0,0 @@
1
- /**
2
- * ESLint rule: pasika/named-exports
3
- *
4
- * Enforces named exports for application files while allowing framework routing
5
- * files such as Next.js page.tsx and layout.tsx to use their required default
6
- * export contract.
7
- *
8
- * @see docs/code-organization-guide/rules/exports-and-imports-rule.md
9
- */
10
- import path from "node:path";
11
- const FRAMEWORK_DEFAULT_EXPORT_FILES = new Set([
12
- "default",
13
- "error",
14
- "layout",
15
- "loading",
16
- "not-found",
17
- "page",
18
- "route",
19
- "template",
20
- ]);
21
- function isFrameworkDefaultExportFile(filename) {
22
- const normalized = filename.replaceAll(path.sep, "/");
23
- if (!normalized.includes("/src/app/"))
24
- return false;
25
- const basename = path.basename(filename, path.extname(filename));
26
- return FRAMEWORK_DEFAULT_EXPORT_FILES.has(basename);
27
- }
28
- export const namedExportsRule = {
29
- meta: {
30
- schema: [],
31
- type: "problem",
32
- docs: {
33
- description: "Require named exports except where a framework requires a default export.",
34
- },
35
- },
36
- create(context) {
37
- if (isFrameworkDefaultExportFile(context.filename))
38
- return {};
39
- return {
40
- ExportDefaultDeclaration(node) {
41
- context.report({
42
- node,
43
- message: "Files that export values must use named exports unless a framework or third-party package requires a different export style for that file. " +
44
- "See docs/code-organization-guide/rules/exports-and-imports-rule.md",
45
- });
46
- },
47
- };
48
- },
49
- };
@@ -1,9 +0,0 @@
1
- /**
2
- * ESLint rule: pasika/no-arbitrary-tailwind
3
- *
4
- * Enforces the "Arbitrary Value Rule" — no arbitrary-value Tailwind classes.
5
- *
6
- * @see docs/styling-guide/rules/arbitrary-value-rule.md
7
- */
8
- import type { Rule } from "eslint";
9
- export declare const noArbitraryTailwindRule: Rule.RuleModule;
@@ -1,97 +0,0 @@
1
- /**
2
- * ESLint rule: pasika/no-arbitrary-tailwind
3
- *
4
- * Enforces the "Arbitrary Value Rule" — no arbitrary-value Tailwind classes.
5
- *
6
- * @see docs/styling-guide/rules/arbitrary-value-rule.md
7
- */
8
- /**
9
- * Matches Tailwind arbitrary-value utility classes like rounded-[13px], text-[#fff].
10
- *
11
- * Does NOT match variant prefixes like min-[400px]:flex-row or md:flex.
12
- * The negative lookahead (?![-\w]*:) excludes anything followed by a colon + more.
13
- *
14
- * Note: Tailwind breakpoint variants (sm:, md:, lg:, etc.) don't use brackets,
15
- * so they're never matched. Only bracket-based variants like min-[400px]: need
16
- * the lookahead exclusion.
17
- */
18
- const ARBITRARY_VALUE_RE = /(?:^|(?<=\s))(?:[a-z]+(?:-[a-z]+)*)-\[[^\]]+\](?![-\w]*:)/g;
19
- const CLASS_HELPERS = new Set(["cn", "clsx", "twMerge", "twJoin"]);
20
- export const noArbitraryTailwindRule = {
21
- meta: {
22
- schema: [],
23
- type: "problem",
24
- docs: {
25
- description: "Disallow Tailwind arbitrary-value classes like rounded-[13px].",
26
- },
27
- },
28
- create(context) {
29
- function reportClassString(node, value) {
30
- ARBITRARY_VALUE_RE.lastIndex = 0;
31
- const match = ARBITRARY_VALUE_RE.exec(value);
32
- if (!match)
33
- return;
34
- context.report({
35
- node,
36
- message: `Tailwind arbitrary-value class "${match[0]}" is not allowed. ` +
37
- "Use a named token or custom utility. " +
38
- "See docs/styling-guide/rules/arbitrary-value-rule.md",
39
- });
40
- }
41
- /** Walks every expression a class name can hide in: conditionals, arrays, and object keys. */
42
- function checkExpression(node, expression) {
43
- if (!expression)
44
- return;
45
- if (expression.type === "Literal") {
46
- if (typeof expression.value === "string")
47
- reportClassString(node, expression.value);
48
- return;
49
- }
50
- if (expression.type === "TemplateLiteral") {
51
- for (const quasi of expression.quasis)
52
- reportClassString(node, quasi.value.raw);
53
- return;
54
- }
55
- if (expression.type === "LogicalExpression") {
56
- checkExpression(node, expression.left);
57
- checkExpression(node, expression.right);
58
- return;
59
- }
60
- if (expression.type === "ConditionalExpression") {
61
- checkExpression(node, expression.consequent);
62
- checkExpression(node, expression.alternate);
63
- return;
64
- }
65
- if (expression.type === "ArrayExpression") {
66
- for (const element of expression.elements)
67
- checkExpression(node, element);
68
- return;
69
- }
70
- if (expression.type === "ObjectExpression") {
71
- // clsx and cn accept `{ "px-[3px]": isActive }`, so the keys carry classes.
72
- for (const property of expression.properties) {
73
- if (property.type === "Property")
74
- checkExpression(node, property.key);
75
- }
76
- }
77
- }
78
- return {
79
- JSXAttribute(node) {
80
- const attributeName = node.name?.name;
81
- if (attributeName !== "className" && attributeName !== "class")
82
- return;
83
- const value = node.value;
84
- if (!value)
85
- return;
86
- checkExpression(node, value.type === "JSXExpressionContainer" ? value.expression : value);
87
- },
88
- CallExpression(node) {
89
- if (node.callee.type !== "Identifier" || !CLASS_HELPERS.has(node.callee.name))
90
- return;
91
- for (const argument of node.arguments) {
92
- checkExpression(node, argument);
93
- }
94
- },
95
- };
96
- },
97
- };
@@ -1,10 +0,0 @@
1
- /**
2
- * ESLint rule: pasika/no-eslint-disable
3
- *
4
- * Code MUST NOT use eslint-disable directives, and a reported violation MUST
5
- * be fixed instead.
6
- *
7
- * @see docs/agent-policy.md
8
- */
9
- import type { Rule } from "eslint";
10
- export declare const noEslintDisableRule: Rule.RuleModule;
@@ -1,33 +0,0 @@
1
- /**
2
- * ESLint rule: pasika/no-eslint-disable
3
- *
4
- * Code MUST NOT use eslint-disable directives, and a reported violation MUST
5
- * be fixed instead.
6
- *
7
- * @see docs/agent-policy.md
8
- */
9
- export const noEslintDisableRule = {
10
- meta: {
11
- schema: [],
12
- type: "problem",
13
- docs: {
14
- description: "Forbid eslint-disable directives.",
15
- },
16
- },
17
- create(context) {
18
- return {
19
- Program() {
20
- for (const comment of context.sourceCode.getAllComments()) {
21
- if (!comment.value.includes("eslint-disable"))
22
- continue;
23
- if (!comment.loc)
24
- continue;
25
- context.report({
26
- loc: comment.loc,
27
- message: "Do not use eslint-disable directives; fix the reported violation instead.",
28
- });
29
- }
30
- },
31
- };
32
- },
33
- };
@@ -1,9 +0,0 @@
1
- /**
2
- * ESLint rule: pasika/no-mixed-concerns
3
- *
4
- * Enforces the "No Mixed Concerns Rule" — one React component per .tsx file.
5
- *
6
- * @see docs/code-organization-guide/rules/no-mixed-concerns-rule.md
7
- */
8
- import type { Rule } from "eslint";
9
- export declare const noMixedConcernsRule: Rule.RuleModule;