pasika 0.3.3 → 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.
- package/README.md +13 -17
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +4999 -103
- package/dist/eslint/pasika/index.d.ts +165 -53
- package/dist/eslint/pasika/index.js +4424 -89
- package/package.json +3 -3
- package/dist/cli/output.d.ts +0 -3
- package/dist/cli/output.js +0 -11
- package/dist/enforcement/coverage.d.ts +0 -55
- package/dist/enforcement/coverage.js +0 -239
- package/dist/enforcement/doctor.d.ts +0 -16
- package/dist/enforcement/doctor.js +0 -249
- package/dist/enforcement/normalize.d.ts +0 -11
- package/dist/enforcement/normalize.js +0 -21
- package/dist/enforcement/parse-docs.d.ts +0 -58
- package/dist/enforcement/parse-docs.js +0 -98
- package/dist/enforcement/types.d.ts +0 -54
- package/dist/enforcement/types.js +0 -50
- package/dist/eslint/pasika/ast-types.d.ts +0 -122
- package/dist/eslint/pasika/ast-types.js +0 -1
- package/dist/eslint/pasika/project/ccf.d.ts +0 -48
- package/dist/eslint/pasika/project/ccf.js +0 -119
- package/dist/eslint/pasika/project/index.d.ts +0 -21
- package/dist/eslint/pasika/project/index.js +0 -139
- package/dist/eslint/pasika/project/parse-module.d.ts +0 -27
- package/dist/eslint/pasika/project/parse-module.js +0 -128
- package/dist/eslint/pasika/rules/application-structure.d.ts +0 -2
- package/dist/eslint/pasika/rules/application-structure.js +0 -152
- package/dist/eslint/pasika/rules/component-conventions.d.ts +0 -16
- package/dist/eslint/pasika/rules/component-conventions.js +0 -145
- package/dist/eslint/pasika/rules/component-nesting.d.ts +0 -12
- package/dist/eslint/pasika/rules/component-nesting.js +0 -69
- package/dist/eslint/pasika/rules/component-placement.d.ts +0 -11
- package/dist/eslint/pasika/rules/component-placement.js +0 -75
- package/dist/eslint/pasika/rules/config-extraction.d.ts +0 -14
- package/dist/eslint/pasika/rules/config-extraction.js +0 -74
- package/dist/eslint/pasika/rules/cross-feature-import.d.ts +0 -2
- package/dist/eslint/pasika/rules/cross-feature-import.js +0 -75
- package/dist/eslint/pasika/rules/css/apply-usage.d.ts +0 -10
- package/dist/eslint/pasika/rules/css/apply-usage.js +0 -45
- package/dist/eslint/pasika/rules/css/base-layer-pair.d.ts +0 -10
- package/dist/eslint/pasika/rules/css/base-layer-pair.js +0 -49
- package/dist/eslint/pasika/rules/css/css-variable-naming.d.ts +0 -10
- package/dist/eslint/pasika/rules/css/css-variable-naming.js +0 -39
- package/dist/eslint/pasika/rules/css/custom-utility-apply.d.ts +0 -11
- package/dist/eslint/pasika/rules/css/custom-utility-apply.js +0 -52
- package/dist/eslint/pasika/rules/css/global-css-location.d.ts +0 -10
- package/dist/eslint/pasika/rules/css/global-css-location.js +0 -42
- package/dist/eslint/pasika/rules/css/helpers.d.ts +0 -29
- package/dist/eslint/pasika/rules/css/helpers.js +0 -93
- package/dist/eslint/pasika/rules/css/index.d.ts +0 -13
- package/dist/eslint/pasika/rules/css/index.js +0 -25
- package/dist/eslint/pasika/rules/css/root-variables.d.ts +0 -11
- package/dist/eslint/pasika/rules/css/root-variables.js +0 -53
- package/dist/eslint/pasika/rules/css/rule-tester.d.ts +0 -11
- package/dist/eslint/pasika/rules/css/rule-tester.js +0 -22
- package/dist/eslint/pasika/rules/css/stylesheet-ordering.d.ts +0 -11
- package/dist/eslint/pasika/rules/css/stylesheet-ordering.js +0 -76
- package/dist/eslint/pasika/rules/css/surface-utility.d.ts +0 -10
- package/dist/eslint/pasika/rules/css/surface-utility.js +0 -41
- package/dist/eslint/pasika/rules/css/theme-reset.d.ts +0 -9
- package/dist/eslint/pasika/rules/css/theme-reset.js +0 -36
- package/dist/eslint/pasika/rules/css/theme-variable-namespace.d.ts +0 -11
- package/dist/eslint/pasika/rules/css/theme-variable-namespace.js +0 -53
- package/dist/eslint/pasika/rules/cva-appearance-props.d.ts +0 -10
- package/dist/eslint/pasika/rules/cva-appearance-props.js +0 -77
- package/dist/eslint/pasika/rules/cva-boolean-variants.d.ts +0 -11
- package/dist/eslint/pasika/rules/cva-boolean-variants.js +0 -97
- package/dist/eslint/pasika/rules/data-testid-case.d.ts +0 -2
- package/dist/eslint/pasika/rules/data-testid-case.js +0 -76
- package/dist/eslint/pasika/rules/enforce-barrel-exports.d.ts +0 -9
- package/dist/eslint/pasika/rules/enforce-barrel-exports.js +0 -78
- package/dist/eslint/pasika/rules/enforce-cn-merge.d.ts +0 -21
- package/dist/eslint/pasika/rules/enforce-cn-merge.js +0 -86
- package/dist/eslint/pasika/rules/enforce-cva-variant-props.d.ts +0 -9
- package/dist/eslint/pasika/rules/enforce-cva-variant-props.js +0 -86
- package/dist/eslint/pasika/rules/filename-case.d.ts +0 -2
- package/dist/eslint/pasika/rules/filename-case.js +0 -123
- package/dist/eslint/pasika/rules/hook-complexity.d.ts +0 -12
- package/dist/eslint/pasika/rules/hook-complexity.js +0 -114
- package/dist/eslint/pasika/rules/hook-extraction.d.ts +0 -11
- package/dist/eslint/pasika/rules/hook-extraction.js +0 -55
- package/dist/eslint/pasika/rules/import-boundaries.d.ts +0 -2
- package/dist/eslint/pasika/rules/import-boundaries.js +0 -167
- package/dist/eslint/pasika/rules/import-through-index.d.ts +0 -2
- package/dist/eslint/pasika/rules/import-through-index.js +0 -52
- package/dist/eslint/pasika/rules/interactive-component.d.ts +0 -10
- package/dist/eslint/pasika/rules/interactive-component.js +0 -76
- package/dist/eslint/pasika/rules/json/index.d.ts +0 -5
- package/dist/eslint/pasika/rules/json/index.js +0 -9
- package/dist/eslint/pasika/rules/json/no-cache-flag.d.ts +0 -10
- package/dist/eslint/pasika/rules/json/no-cache-flag.js +0 -45
- package/dist/eslint/pasika/rules/json/no-vulyk-dependency.d.ts +0 -10
- package/dist/eslint/pasika/rules/json/no-vulyk-dependency.js +0 -41
- package/dist/eslint/pasika/rules/json/rule-tester.d.ts +0 -11
- package/dist/eslint/pasika/rules/json/rule-tester.js +0 -21
- package/dist/eslint/pasika/rules/jsx-hygiene.d.ts +0 -9
- package/dist/eslint/pasika/rules/jsx-hygiene.js +0 -108
- package/dist/eslint/pasika/rules/locale-dotted-path.d.ts +0 -10
- package/dist/eslint/pasika/rules/locale-dotted-path.js +0 -65
- package/dist/eslint/pasika/rules/locale-placement.d.ts +0 -13
- package/dist/eslint/pasika/rules/locale-placement.js +0 -163
- package/dist/eslint/pasika/rules/locales-location.d.ts +0 -11
- package/dist/eslint/pasika/rules/locales-location.js +0 -56
- package/dist/eslint/pasika/rules/md/doc-kind-suffix.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/doc-kind-suffix.js +0 -29
- package/dist/eslint/pasika/rules/md/example-heading-description.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/example-heading-description.js +0 -30
- package/dist/eslint/pasika/rules/md/glossary-term-linking.d.ts +0 -2
- package/dist/eslint/pasika/rules/md/glossary-term-linking.js +0 -99
- package/dist/eslint/pasika/rules/md/guide-folder-entry-point.d.ts +0 -2
- package/dist/eslint/pasika/rules/md/guide-folder-entry-point.js +0 -46
- package/dist/eslint/pasika/rules/md/guide-link-anchors.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/guide-link-anchors.js +0 -50
- package/dist/eslint/pasika/rules/md/guide-overview-no-links.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/guide-overview-no-links.js +0 -51
- package/dist/eslint/pasika/rules/md/guide-states-no-requirement.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/guide-states-no-requirement.js +0 -26
- package/dist/eslint/pasika/rules/md/guide-step-single-link.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/guide-step-single-link.js +0 -48
- package/dist/eslint/pasika/rules/md/guide-step-single-sentence.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/guide-step-single-sentence.js +0 -42
- package/dist/eslint/pasika/rules/md/helpers.d.ts +0 -25
- package/dist/eslint/pasika/rules/md/helpers.js +0 -37
- package/dist/eslint/pasika/rules/md/index.d.ts +0 -27
- package/dist/eslint/pasika/rules/md/index.js +0 -53
- package/dist/eslint/pasika/rules/md/no-cross-document-link.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/no-cross-document-link.js +0 -36
- package/dist/eslint/pasika/rules/md/no-nested-how-to.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/no-nested-how-to.js +0 -39
- package/dist/eslint/pasika/rules/md/no-template-prompt.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/no-template-prompt.js +0 -22
- package/dist/eslint/pasika/rules/md/overview-length.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/overview-length.js +0 -47
- package/dist/eslint/pasika/rules/md/overview-present.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/overview-present.js +0 -41
- package/dist/eslint/pasika/rules/md/policy-no-examples.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/policy-no-examples.js +0 -28
- package/dist/eslint/pasika/rules/md/policy-single-document.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/policy-single-document.js +0 -34
- package/dist/eslint/pasika/rules/md/policy-subject-headings.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/policy-subject-headings.js +0 -58
- package/dist/eslint/pasika/rules/md/project-index.d.ts +0 -17
- package/dist/eslint/pasika/rules/md/project-index.js +0 -67
- package/dist/eslint/pasika/rules/md/reference-block-headings.d.ts +0 -6
- package/dist/eslint/pasika/rules/md/reference-block-headings.js +0 -31
- package/dist/eslint/pasika/rules/md/reference-no-rfc-vocabulary.d.ts +0 -6
- package/dist/eslint/pasika/rules/md/reference-no-rfc-vocabulary.js +0 -26
- package/dist/eslint/pasika/rules/md/requirement-present.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/requirement-present.js +0 -41
- package/dist/eslint/pasika/rules/md/rfc-only-in-bullets.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/rfc-only-in-bullets.js +0 -46
- package/dist/eslint/pasika/rules/md/rule-paired-examples.d.ts +0 -5
- package/dist/eslint/pasika/rules/md/rule-paired-examples.js +0 -36
- package/dist/eslint/pasika/rules/md/rule-tester.d.ts +0 -11
- package/dist/eslint/pasika/rules/md/rule-tester.js +0 -21
- package/dist/eslint/pasika/rules/md/support-document-placement.d.ts +0 -2
- package/dist/eslint/pasika/rules/md/support-document-placement.js +0 -36
- package/dist/eslint/pasika/rules/md/title-matches-file-name.d.ts +0 -2
- package/dist/eslint/pasika/rules/md/title-matches-file-name.js +0 -46
- package/dist/eslint/pasika/rules/named-exports.d.ts +0 -2
- package/dist/eslint/pasika/rules/named-exports.js +0 -49
- package/dist/eslint/pasika/rules/no-arbitrary-tailwind.d.ts +0 -9
- package/dist/eslint/pasika/rules/no-arbitrary-tailwind.js +0 -97
- package/dist/eslint/pasika/rules/no-eslint-disable.d.ts +0 -10
- package/dist/eslint/pasika/rules/no-eslint-disable.js +0 -33
- package/dist/eslint/pasika/rules/no-mixed-concerns.d.ts +0 -9
- package/dist/eslint/pasika/rules/no-mixed-concerns.js +0 -71
- package/dist/eslint/pasika/rules/no-util-barrel.d.ts +0 -2
- package/dist/eslint/pasika/rules/no-util-barrel.js +0 -48
- package/dist/eslint/pasika/rules/pure-function-extract.d.ts +0 -9
- package/dist/eslint/pasika/rules/pure-function-extract.js +0 -92
- package/dist/eslint/pasika/rules/shared-style-dedup.d.ts +0 -13
- package/dist/eslint/pasika/rules/shared-style-dedup.js +0 -91
- package/dist/eslint/pasika/rules/stay-flat.d.ts +0 -12
- package/dist/eslint/pasika/rules/stay-flat.js +0 -81
- package/dist/eslint/pasika/rules/support-file-placement.d.ts +0 -15
- package/dist/eslint/pasika/rules/support-file-placement.js +0 -70
- package/dist/eslint/pasika/rules/support-folder-shape.d.ts +0 -2
- package/dist/eslint/pasika/rules/support-folder-shape.js +0 -59
- package/dist/eslint/pasika/rules/type-extraction.d.ts +0 -13
- package/dist/eslint/pasika/rules/type-extraction.js +0 -85
- package/dist/eslint/pasika/rules/ui-state.d.ts +0 -9
- package/dist/eslint/pasika/rules/ui-state.js +0 -99
- package/dist/eslint/pasika/rules/util-file-name.d.ts +0 -2
- package/dist/eslint/pasika/rules/util-file-name.js +0 -48
- package/dist/eslint/pasika/rules/value-extraction.d.ts +0 -13
- package/dist/eslint/pasika/rules/value-extraction.js +0 -48
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { getFilename } from "./helpers.js";
|
|
2
|
-
import { findDocsRoot, getProjectDocs } from "./project-index.js";
|
|
3
|
-
export const policySingleDocumentRule = {
|
|
4
|
-
meta: {
|
|
5
|
-
type: "problem",
|
|
6
|
-
docs: {
|
|
7
|
-
description: "A repository must not have more than one Policy document.",
|
|
8
|
-
recommended: true,
|
|
9
|
-
},
|
|
10
|
-
},
|
|
11
|
-
create(context) {
|
|
12
|
-
return {
|
|
13
|
-
root(node) {
|
|
14
|
-
const filename = getFilename(context);
|
|
15
|
-
if (!filename.endsWith("-policy.md"))
|
|
16
|
-
return;
|
|
17
|
-
const docsRoot = findDocsRoot(filename);
|
|
18
|
-
if (!docsRoot)
|
|
19
|
-
return;
|
|
20
|
-
const docs = getProjectDocs(docsRoot);
|
|
21
|
-
const policyDocs = docs.filter((d) => d.kind === "policy");
|
|
22
|
-
if (policyDocs.length > 1) {
|
|
23
|
-
// Only report on the first policy doc, so the issue is reported once.
|
|
24
|
-
if (policyDocs[0]?.filePath === filename) {
|
|
25
|
-
context.report({
|
|
26
|
-
node,
|
|
27
|
-
message: `${String(policyDocs.length)} policy documents exist`,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
},
|
|
34
|
-
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { getFilename, getLine } from "./helpers.js";
|
|
2
|
-
export const policySubjectHeadingsRule = {
|
|
3
|
-
meta: {
|
|
4
|
-
type: "problem",
|
|
5
|
-
docs: {
|
|
6
|
-
description: "Policy document must group its bullets under a heading per subject.",
|
|
7
|
-
recommended: true,
|
|
8
|
-
},
|
|
9
|
-
},
|
|
10
|
-
create(context) {
|
|
11
|
-
return {
|
|
12
|
-
root(node) {
|
|
13
|
-
const filename = getFilename(context);
|
|
14
|
-
if (!filename.endsWith("-policy.md"))
|
|
15
|
-
return;
|
|
16
|
-
// Collect headings and list items with their line numbers, in document order.
|
|
17
|
-
let titleLine = 0;
|
|
18
|
-
let firstSubjectLine = 0;
|
|
19
|
-
let overviewLine = 0;
|
|
20
|
-
let foundTitle = false;
|
|
21
|
-
let foundOverview = false;
|
|
22
|
-
for (const child of node.children) {
|
|
23
|
-
const line = getLine(child);
|
|
24
|
-
if (!foundTitle && child.type === "heading" && child.depth === 1) {
|
|
25
|
-
titleLine = line;
|
|
26
|
-
foundTitle = true;
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
if (!foundTitle)
|
|
30
|
-
continue;
|
|
31
|
-
if (!foundOverview && child.type === "paragraph" && line > titleLine) {
|
|
32
|
-
overviewLine = line;
|
|
33
|
-
foundOverview = true;
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
if (!firstSubjectLine && child.type === "heading" && child.depth === 2 && line > titleLine) {
|
|
37
|
-
firstSubjectLine = line;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
if (!titleLine || !firstSubjectLine)
|
|
41
|
-
return;
|
|
42
|
-
// Any bullet before the first subject heading (and after the overview) is ungrouped.
|
|
43
|
-
const hasUngroupedBullet = node.children.some((child) => {
|
|
44
|
-
if (child.type !== "list")
|
|
45
|
-
return false;
|
|
46
|
-
const line = getLine(child);
|
|
47
|
-
return line > overviewLine && line < firstSubjectLine;
|
|
48
|
-
});
|
|
49
|
-
if (hasUngroupedBullet) {
|
|
50
|
-
context.report({
|
|
51
|
-
node,
|
|
52
|
-
message: "policy bullet appears before the first subject heading",
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
},
|
|
58
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export interface DocMeta {
|
|
2
|
-
/** Absolute path on disk. */
|
|
3
|
-
filePath: string;
|
|
4
|
-
/** Path relative to docs root. */
|
|
5
|
-
doc: string;
|
|
6
|
-
fileName: string;
|
|
7
|
-
kind?: "guide" | "rule" | "reference" | "policy";
|
|
8
|
-
title: string;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Get all documents in the docs folder. Results are cached per docsRoot.
|
|
12
|
-
*/
|
|
13
|
-
export declare function getProjectDocs(docsRoot: string): DocMeta[];
|
|
14
|
-
/**
|
|
15
|
-
* Try to find the docsRoot from a file path by walking up to find the docs/ directory.
|
|
16
|
-
*/
|
|
17
|
-
export declare function findDocsRoot(filePath: string): string | undefined;
|
|
@@ -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,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,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,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,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,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,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;
|