Skip to content

Commit 39b524c

Browse files
committed
chore: add current lint file
1 parent 25b7225 commit 39b524c

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

lint.ts

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { readFile, readdir, stat } from "node:fs/promises";
2+
import * as path from "node:path";
3+
import * as marked from "marked";
4+
import grayMatter from "gray-matter";
5+
6+
const files = await readdir(".", { withFileTypes: true });
7+
const dirs = files.filter((f) => {
8+
return (
9+
f.isDirectory() && !f.name.startsWith(".") && f.name !== "node_modules"
10+
);
11+
});
12+
13+
let badExit = false;
14+
15+
// error reports an error to the console and sets badExit to true
16+
// so that the process will exit with a non-zero exit code.
17+
const error = (...data: readonly unknown[]) => {
18+
console.error(...data);
19+
badExit = true;
20+
};
21+
22+
const verifyCodeBlocks = (
23+
tokens: readonly marked.Token[],
24+
res = {
25+
codeIsTF: false,
26+
codeIsHCL: false,
27+
},
28+
) => {
29+
for (const token of tokens) {
30+
// Check in-depth.
31+
if (token.type === "list") {
32+
verifyCodeBlocks(token.items, res);
33+
continue;
34+
}
35+
36+
if (token.type === "list_item") {
37+
if (token.tokens === undefined) {
38+
throw new Error("Tokens are missing for type list_item");
39+
}
40+
41+
verifyCodeBlocks(token.tokens, res);
42+
continue;
43+
}
44+
45+
if (token.type === "code") {
46+
if (token.lang === "tf") {
47+
res.codeIsTF = true;
48+
}
49+
if (token.lang === "hcl") {
50+
res.codeIsHCL = true;
51+
}
52+
}
53+
}
54+
return res;
55+
};
56+
57+
// Ensures that each README has the proper format.
58+
// Exits with 0 if all is good!
59+
for (const dir of dirs) {
60+
const readme = path.join(dir.name, "README.md");
61+
// Ensure exists
62+
try {
63+
await stat(readme);
64+
} catch (ex) {
65+
throw new Error(`Missing README.md in ${dir.name}`);
66+
}
67+
const content = await readFile(readme, "utf8");
68+
const matter = grayMatter(content);
69+
const data = matter.data as {
70+
display_name?: string;
71+
description?: string;
72+
icon?: string;
73+
maintainer_github?: string;
74+
partner_github?: string;
75+
verified?: boolean;
76+
tags?: string[];
77+
};
78+
if (!data.display_name) {
79+
error(dir.name, "missing display_name");
80+
}
81+
if (!data.description) {
82+
error(dir.name, "missing description");
83+
}
84+
if (!data.icon) {
85+
error(dir.name, "missing icon");
86+
}
87+
if (!data.maintainer_github) {
88+
error(dir.name, "missing maintainer_github");
89+
}
90+
91+
try {
92+
await stat(path.join(".", dir.name, data.icon ?? ""));
93+
} catch (ex) {
94+
error(dir.name, "icon does not exist", data.icon);
95+
}
96+
97+
const tokens = marked.lexer(content);
98+
// Ensure there is an h1 and some text, then a code block
99+
100+
let h1 = false;
101+
let code = false;
102+
let paragraph = false;
103+
let version = true;
104+
105+
for (const token of tokens) {
106+
if (token.type === "heading" && token.depth === 1) {
107+
h1 = true;
108+
continue;
109+
}
110+
if (h1 && token.type === "heading") {
111+
break;
112+
}
113+
if (token.type === "paragraph") {
114+
paragraph = true;
115+
continue;
116+
}
117+
if (token.type === "code") {
118+
code = true;
119+
if (token.lang === "tf" && !token.text.includes("version")) {
120+
version = false;
121+
error(dir.name, "missing version in tf code block");
122+
}
123+
}
124+
}
125+
if (!h1) {
126+
error(dir.name, "missing h1");
127+
}
128+
if (!paragraph) {
129+
error(dir.name, "missing paragraph after h1");
130+
}
131+
if (!code) {
132+
error(dir.name, "missing example code block after paragraph");
133+
}
134+
135+
const { codeIsTF, codeIsHCL } = verifyCodeBlocks(tokens);
136+
if (!codeIsTF) {
137+
error(dir.name, "missing example tf code block");
138+
}
139+
if (codeIsHCL) {
140+
error(dir.name, "hcl code block should be tf");
141+
}
142+
}
143+
144+
if (badExit) {
145+
process.exit(1);
146+
}

0 commit comments

Comments
 (0)