Skip to content

Commit 3d0b15e

Browse files
committedFeb 4, 2025
feat(nx-dev): conformance rule for blog post description
1 parent 1059be6 commit 3d0b15e

File tree

5 files changed

+131
-20
lines changed

5 files changed

+131
-20
lines changed
 

Diff for: ‎nx.json

+7
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,13 @@
249249
"defaultBase": "master",
250250
"conformance": {
251251
"rules": [
252+
{
253+
"rule": "@nx/workspace-plugin/conformance-rules/blog-description",
254+
"projects": ["docs"],
255+
"options": {
256+
"mdGlobPattern": "blog/**/*.md"
257+
}
258+
},
252259
{
253260
"rule": "@nx/workspace-plugin/conformance-rules/project-package-json",
254261
"projects": [

Diff for: ‎package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@
8686
"@nx/rsbuild": "20.4.0-beta.2",
8787
"@nx/rspack": "20.4.0-beta.2",
8888
"@nx/storybook": "20.4.0-beta.2",
89+
"@nx/vite": "20.4.0-beta.2",
8990
"@nx/web": "20.4.0-beta.2",
9091
"@nx/webpack": "20.4.0-beta.2",
91-
"@nx/vite": "20.4.0-beta.2",
9292
"@phenomnomnominal/tsquery": "~5.0.1",
9393
"@playwright/test": "^1.36.1",
9494
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
@@ -102,8 +102,8 @@
102102
"@rollup/plugin-json": "^6.1.0",
103103
"@rollup/plugin-node-resolve": "^15.2.3",
104104
"@rollup/plugin-url": "^8.0.2",
105-
"@rspack/core": "1.1.6",
106105
"@rsbuild/core": "1.1.8",
106+
"@rspack/core": "1.1.6",
107107
"@rspack/dev-server": "1.0.9",
108108
"@rspack/plugin-minify": "^0.7.5",
109109
"@rspack/plugin-react-refresh": "^1.0.0",
@@ -136,6 +136,7 @@
136136
"@types/jasmine": "~2.8.6",
137137
"@types/jasminewd2": "~2.0.3",
138138
"@types/jest": "29.5.12",
139+
"@types/js-yaml": "^4.0.9",
139140
"@types/marked": "^2.0.0",
140141
"@types/node": "20.16.10",
141142
"@types/npm-package-arg": "6.1.1",
@@ -227,6 +228,7 @@
227228
"jest-runtime": "29.7.0",
228229
"jest-util": "29.7.0",
229230
"js-tokens": "^4.0.0",
231+
"js-yaml": "^4.1.0",
230232
"jsonc-eslint-parser": "^2.1.0",
231233
"jsonc-parser": "3.2.0",
232234
"kill-port": "^1.6.1",

Diff for: ‎pnpm-lock.yaml

+29-18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { readFileSync, existsSync } from 'node:fs';
2+
import { join } from 'node:path';
3+
import { load as yamlLoad } from 'js-yaml';
4+
import { workspaceRoot } from '@nx/devkit';
5+
import { sync as globSync } from 'glob';
6+
import {
7+
createConformanceRule,
8+
type ProjectFilesViolation,
9+
} from '@nx/powerpack-conformance';
10+
11+
export default createConformanceRule<{ mdGlobPattern: string }>({
12+
name: 'blog-description',
13+
category: 'consistency',
14+
description:
15+
'Ensures that blog posts have a description in their frontmatter',
16+
reporter: 'project-files-reporter',
17+
implementation: async ({ projectGraph, ruleOptions }) => {
18+
const violations: ProjectFilesViolation[] = [];
19+
const { mdGlobPattern } = ruleOptions;
20+
21+
// Look for the docs project
22+
const docsProject = Object.values(projectGraph.nodes).find(
23+
(project) => project.name === 'docs'
24+
);
25+
26+
if (!docsProject) {
27+
return {
28+
severity: 'low',
29+
details: {
30+
violations: [],
31+
},
32+
};
33+
}
34+
35+
const blogPattern = join(
36+
workspaceRoot,
37+
docsProject.data.root,
38+
mdGlobPattern
39+
);
40+
const files = findMarkdownFiles(blogPattern);
41+
42+
for (const file of files) {
43+
const content = readFileSync(file, 'utf-8');
44+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
45+
46+
// Only check files with frontmatter
47+
if (frontmatterMatch) {
48+
try {
49+
const frontmatter = yamlLoad(frontmatterMatch[1]) as Record<
50+
string,
51+
unknown
52+
>;
53+
54+
if (!frontmatter.description) {
55+
violations.push({
56+
message:
57+
'Blog posts with frontmatter must have a description field',
58+
sourceProject: docsProject.name,
59+
file: file,
60+
});
61+
}
62+
} catch (e) {
63+
// If YAML parsing fails, we skip the file
64+
continue;
65+
}
66+
}
67+
}
68+
69+
return {
70+
severity: 'high',
71+
details: {
72+
violations,
73+
},
74+
};
75+
},
76+
});
77+
78+
function findMarkdownFiles(pattern: string): string[] {
79+
return globSync(pattern);
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"type": "object",
4+
"properties": {
5+
"mdGlobPattern": {
6+
"type": "string",
7+
"description": "The glob pattern to use to find the markdown files to analyze"
8+
}
9+
},
10+
"additionalProperties": false
11+
}

0 commit comments

Comments
 (0)
Please sign in to comment.