pasika 0.3.0 → 0.3.2
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 +7 -5
- package/dist/cli/index.js +32 -38
- package/dist/cli/output.d.ts +3 -0
- package/dist/cli/output.js +11 -0
- package/dist/enforcement/coverage.js +25 -29
- package/dist/enforcement/doctor.d.ts +16 -0
- package/dist/enforcement/doctor.js +249 -0
- 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/enforcement/registry.json +196 -190
- package/package.json +28 -19
- package/dist/enforcement/docs-check.d.ts +0 -17
- package/dist/enforcement/docs-check.js +0 -159
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pasika",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Reusable agent setup package",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
"./eslint": {
|
|
13
13
|
"types": "./dist/eslint/pasika/index.d.ts",
|
|
14
14
|
"import": "./dist/eslint/pasika/index.js"
|
|
15
|
-
}
|
|
15
|
+
},
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"pasika": "dist/cli/index.js"
|
|
16
20
|
},
|
|
17
21
|
"files": [
|
|
18
22
|
"dist",
|
|
@@ -20,35 +24,40 @@
|
|
|
20
24
|
],
|
|
21
25
|
"scripts": {
|
|
22
26
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
27
|
+
"coverage": "tsx cli/index.ts coverage",
|
|
28
|
+
"docs": "eslint docs --config eslint.docs.config.ts",
|
|
23
29
|
"fix": "eslint . --fix",
|
|
24
30
|
"lint": "eslint .",
|
|
25
31
|
"prepack": "npm run build",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
"test": "node --import tsx --test 'eslint/**/*.test.ts' 'enforcement/**/*.test.ts'",
|
|
33
|
+
"typecheck": "tsc --noEmit"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"commander": "^12.1.0",
|
|
37
|
+
"zod": "^4.4.3"
|
|
30
38
|
},
|
|
31
39
|
"devDependencies": {
|
|
40
|
+
"@eslint/css": "^1.4.0",
|
|
41
|
+
"@eslint/css-tree": "^4.0.5",
|
|
42
|
+
"@eslint/json": "^2.0.1",
|
|
43
|
+
"@eslint/markdown": "^8.0.3",
|
|
44
|
+
"@humanwhocodes/momoa": "^3.3.12",
|
|
32
45
|
"@types/estree": "^1.0.9",
|
|
33
|
-
"@types/
|
|
46
|
+
"@types/mdast": "^4.0.4",
|
|
47
|
+
"@types/node": "^26.4.0",
|
|
34
48
|
"@typescript-eslint/parser": "^8.68.0",
|
|
35
|
-
"eslint": "^
|
|
49
|
+
"@typescript-eslint/utils": "^8.68.0",
|
|
50
|
+
"eslint": "^10.9.1",
|
|
51
|
+
"jiti": "^2.7.0",
|
|
36
52
|
"prettier": "^3.8.1",
|
|
37
53
|
"tsx": "^4.23.12",
|
|
38
54
|
"typescript": "^5.9.2",
|
|
39
|
-
"zirka": "^0.0.
|
|
40
|
-
},
|
|
41
|
-
"engines": {
|
|
42
|
-
"node": ">=22"
|
|
43
|
-
},
|
|
44
|
-
"dependencies": {
|
|
45
|
-
"commander": "^12.1.0",
|
|
46
|
-
"zod": "^4.4.3"
|
|
47
|
-
},
|
|
48
|
-
"bin": {
|
|
49
|
-
"pasika": "dist/cli/index.js"
|
|
55
|
+
"zirka": "^0.0.39"
|
|
50
56
|
},
|
|
51
57
|
"peerDependencies": {
|
|
52
58
|
"typescript": ">=5.0.0"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=22"
|
|
53
62
|
}
|
|
54
63
|
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { type ParsedDoc } from "./parse-docs.js";
|
|
2
|
-
/**
|
|
3
|
-
* Identifiers of the documentation checks. The enforcement registry points at
|
|
4
|
-
* these, so renaming one is a change the registry has to follow.
|
|
5
|
-
*/
|
|
6
|
-
export declare const DOCS_CHECKS: readonly ["doc-kind-suffix", "title-matches-file-name", "overview-present", "overview-length", "guide-overview-no-links", "guide-step-single-sentence", "guide-step-single-link", "guide-states-no-requirement", "guide-folder-entry-point", "requirement-present", "rule-paired-examples", "example-heading-description", "policy-no-examples", "policy-single-document", "no-cross-document-link", "reference-no-rfc-vocabulary", "reference-block-headings", "support-document-placement", "no-template-prompt"];
|
|
7
|
-
export type DocsCheck = (typeof DOCS_CHECKS)[number];
|
|
8
|
-
export interface DocsFinding {
|
|
9
|
-
doc: string;
|
|
10
|
-
line: number;
|
|
11
|
-
check: DocsCheck;
|
|
12
|
-
message: string;
|
|
13
|
-
}
|
|
14
|
-
export declare function checkDocs(docsRoot: string): {
|
|
15
|
-
docs: ParsedDoc[];
|
|
16
|
-
findings: DocsFinding[];
|
|
17
|
-
};
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { parseDocs, RFC_2119 } from "./parse-docs.js";
|
|
3
|
-
/**
|
|
4
|
-
* Identifiers of the documentation checks. The enforcement registry points at
|
|
5
|
-
* these, so renaming one is a change the registry has to follow.
|
|
6
|
-
*/
|
|
7
|
-
export const DOCS_CHECKS = [
|
|
8
|
-
"doc-kind-suffix",
|
|
9
|
-
"title-matches-file-name",
|
|
10
|
-
"overview-present",
|
|
11
|
-
"overview-length",
|
|
12
|
-
"guide-overview-no-links",
|
|
13
|
-
"guide-step-single-sentence",
|
|
14
|
-
"guide-step-single-link",
|
|
15
|
-
"guide-states-no-requirement",
|
|
16
|
-
"guide-folder-entry-point",
|
|
17
|
-
"requirement-present",
|
|
18
|
-
"rule-paired-examples",
|
|
19
|
-
"example-heading-description",
|
|
20
|
-
"policy-no-examples",
|
|
21
|
-
"policy-single-document",
|
|
22
|
-
"no-cross-document-link",
|
|
23
|
-
"reference-no-rfc-vocabulary",
|
|
24
|
-
"reference-block-headings",
|
|
25
|
-
"support-document-placement",
|
|
26
|
-
"no-template-prompt",
|
|
27
|
-
];
|
|
28
|
-
function countSentences(text) {
|
|
29
|
-
// Abbreviations inside requirement text are rare, so a period followed by
|
|
30
|
-
// whitespace is a reliable sentence boundary here.
|
|
31
|
-
return text.split(/[.!?](?:\s+|$)/).filter((part) => part.trim() !== "").length;
|
|
32
|
-
}
|
|
33
|
-
function checkDoc(doc, allDocs) {
|
|
34
|
-
const findings = [];
|
|
35
|
-
const add = (line, check, message) => {
|
|
36
|
-
findings.push({ doc: doc.doc, line, check, message });
|
|
37
|
-
};
|
|
38
|
-
if (!doc.kind) {
|
|
39
|
-
add(1, "doc-kind-suffix", "file name carries no -guide, -rule, -reference, or -policy suffix");
|
|
40
|
-
return findings;
|
|
41
|
-
}
|
|
42
|
-
const expectedFileName = `${doc.title
|
|
43
|
-
.toLowerCase()
|
|
44
|
-
.replaceAll(/[^a-z0-9]+/g, "-")
|
|
45
|
-
.replace(/^-|-$/g, "")}.md`;
|
|
46
|
-
if (doc.title === "") {
|
|
47
|
-
add(1, "title-matches-file-name", "document has no `# Title` heading");
|
|
48
|
-
}
|
|
49
|
-
else if (expectedFileName !== doc.fileName) {
|
|
50
|
-
add(1, "title-matches-file-name", `title "${doc.title}" expects file name ${expectedFileName}`);
|
|
51
|
-
}
|
|
52
|
-
if (doc.overview === undefined) {
|
|
53
|
-
add(2, "overview-present", "no overview follows the title");
|
|
54
|
-
}
|
|
55
|
-
else if (countSentences(doc.overview) > 2) {
|
|
56
|
-
add(2, "overview-length", `overview uses ${String(countSentences(doc.overview))} sentences, at most two are allowed`);
|
|
57
|
-
}
|
|
58
|
-
if (doc.kind === "guide") {
|
|
59
|
-
if (doc.overview?.includes("](")) {
|
|
60
|
-
add(2, "guide-overview-no-links", "guide overview links another document");
|
|
61
|
-
}
|
|
62
|
-
for (const step of doc.steps) {
|
|
63
|
-
if (countSentences(step.text) > 1) {
|
|
64
|
-
add(step.line, "guide-step-single-sentence", `step uses ${String(countSentences(step.text))} sentences`);
|
|
65
|
-
}
|
|
66
|
-
if (step.links.length > 1) {
|
|
67
|
-
add(step.line, "guide-step-single-link", `step links ${String(step.links.length)} documents`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
if (doc.requirements.length > 0) {
|
|
71
|
-
const first = doc.requirements[0];
|
|
72
|
-
add(first?.line ?? 1, "guide-states-no-requirement", "guide states a requirement with RFC 2119 vocabulary");
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
for (const link of doc.docLinks) {
|
|
77
|
-
add(link.line, "no-cross-document-link", `${doc.kind} links another document: ${link.target}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if ((doc.kind === "rule" || doc.kind === "policy") && doc.requirements.length === 0) {
|
|
81
|
-
add(1, "requirement-present", `${doc.kind} document states no requirement`);
|
|
82
|
-
}
|
|
83
|
-
if (doc.kind === "rule") {
|
|
84
|
-
const incorrect = doc.exampleHeadings.filter((heading) => heading.text.startsWith("Incorrect"));
|
|
85
|
-
const correct = doc.exampleHeadings.filter((heading) => heading.text.startsWith("Correct"));
|
|
86
|
-
if (incorrect.length === 0 || incorrect.length !== correct.length) {
|
|
87
|
-
add(1, "rule-paired-examples", `${String(incorrect.length)} Incorrect and ${String(correct.length)} Correct examples`);
|
|
88
|
-
}
|
|
89
|
-
for (const heading of doc.exampleHeadings) {
|
|
90
|
-
if (!/^(?:Incorrect|Correct) — .+/.test(heading.text)) {
|
|
91
|
-
add(heading.line, "example-heading-description", `example heading has no em-dash description: ${heading.text}`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (doc.kind === "policy") {
|
|
96
|
-
for (const heading of doc.exampleHeadings) {
|
|
97
|
-
add(heading.line, "policy-no-examples", `policy document contains an example: ${heading.text}`);
|
|
98
|
-
}
|
|
99
|
-
const policyDocs = allDocs.filter((other) => other.kind === "policy");
|
|
100
|
-
if (policyDocs.length > 1 && policyDocs[0] === doc) {
|
|
101
|
-
add(1, "policy-single-document", `${String(policyDocs.length)} policy documents exist`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (doc.kind === "reference") {
|
|
105
|
-
doc.proseWithoutCode.forEach((line, index) => {
|
|
106
|
-
const match = RFC_2119.exec(line);
|
|
107
|
-
if (match) {
|
|
108
|
-
add(index + 1, "reference-no-rfc-vocabulary", `reference uses RFC 2119 vocabulary: ${match.groups?.keyword ?? ""}`);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
if (doc.sectionHeadings.length === 1) {
|
|
112
|
-
add(doc.sectionHeadings[0]?.line ?? 1, "reference-block-headings", "reference has exactly one section heading, so either a single block is headed or a first block is not");
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
// Rules and references a guide owns live in its rules/ and references/ folders.
|
|
116
|
-
const parentFolder = path.basename(path.dirname(doc.filePath));
|
|
117
|
-
if (doc.kind === "rule" && parentFolder !== "rules") {
|
|
118
|
-
add(1, "support-document-placement", `rule lives in "${parentFolder}/" instead of "rules/"`);
|
|
119
|
-
}
|
|
120
|
-
if (doc.kind === "reference" && parentFolder !== "references") {
|
|
121
|
-
add(1, "support-document-placement", `reference lives in "${parentFolder}/" instead of "references/"`);
|
|
122
|
-
}
|
|
123
|
-
doc.prose.forEach((line, index) => {
|
|
124
|
-
if (/^\s*\[[A-Z0-9].*\]\s*$/.test(line)) {
|
|
125
|
-
add(index + 1, "no-template-prompt", "leftover bracketed template prompt");
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
return findings;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* A folder that holds `rules/` or `references/` is a guide folder, so it needs a
|
|
132
|
-
* guide named after it as its entry point. Other guides may share that folder.
|
|
133
|
-
*/
|
|
134
|
-
function checkGuideFolders(docs) {
|
|
135
|
-
const guideFolders = new Set(docs
|
|
136
|
-
.filter((doc) => ["rules", "references"].includes(path.basename(path.dirname(doc.filePath))))
|
|
137
|
-
.map((doc) => path.dirname(path.dirname(doc.filePath))));
|
|
138
|
-
return [...guideFolders]
|
|
139
|
-
.sort((left, right) => left.localeCompare(right))
|
|
140
|
-
.flatMap((folder) => {
|
|
141
|
-
const expectedEntryPoint = `${path.basename(folder)}.md`;
|
|
142
|
-
const hasEntryPoint = docs.some((doc) => doc.kind === "guide" && path.dirname(doc.filePath) === folder && doc.fileName === expectedEntryPoint);
|
|
143
|
-
if (hasEntryPoint)
|
|
144
|
-
return [];
|
|
145
|
-
const anyDoc = docs.find((doc) => doc.filePath.startsWith(`${folder}${path.sep}`));
|
|
146
|
-
return [
|
|
147
|
-
{
|
|
148
|
-
doc: anyDoc ? path.dirname(path.dirname(anyDoc.doc)) : path.basename(folder),
|
|
149
|
-
line: 1,
|
|
150
|
-
check: "guide-folder-entry-point",
|
|
151
|
-
message: `folder holds support documents but has no ${expectedEntryPoint} entry point`,
|
|
152
|
-
},
|
|
153
|
-
];
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
export function checkDocs(docsRoot) {
|
|
157
|
-
const docs = parseDocs(docsRoot);
|
|
158
|
-
return { docs, findings: [...docs.flatMap((doc) => checkDoc(doc, docs)), ...checkGuideFolders(docs)] };
|
|
159
|
-
}
|