diff --git a/nx.json b/nx.json index 10dd33b1cd60ba..0d38dfbafcdc8f 100644 --- a/nx.json +++ b/nx.json @@ -249,6 +249,13 @@ "defaultBase": "master", "conformance": { "rules": [ + { + "rule": "@nx/workspace-plugin/conformance-rules/blog-description", + "projects": ["docs"], + "options": { + "mdGlobPattern": "blog/**/*.md" + } + }, { "rule": "@nx/workspace-plugin/conformance-rules/project-package-json", "projects": [ diff --git a/package.json b/package.json index c935bf430f8cf3..64bc9e613bb83e 100644 --- a/package.json +++ b/package.json @@ -86,9 +86,9 @@ "@nx/rsbuild": "20.4.0-beta.2", "@nx/rspack": "20.4.0-beta.2", "@nx/storybook": "20.4.0-beta.2", + "@nx/vite": "20.4.0-beta.2", "@nx/web": "20.4.0-beta.2", "@nx/webpack": "20.4.0-beta.2", - "@nx/vite": "20.4.0-beta.2", "@phenomnomnominal/tsquery": "~5.0.1", "@playwright/test": "^1.36.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", @@ -102,8 +102,8 @@ "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-url": "^8.0.2", - "@rspack/core": "1.1.6", "@rsbuild/core": "1.1.8", + "@rspack/core": "1.1.6", "@rspack/dev-server": "1.0.9", "@rspack/plugin-minify": "^0.7.5", "@rspack/plugin-react-refresh": "^1.0.0", @@ -136,6 +136,7 @@ "@types/jasmine": "~2.8.6", "@types/jasminewd2": "~2.0.3", "@types/jest": "29.5.12", + "@types/js-yaml": "^4.0.9", "@types/marked": "^2.0.0", "@types/node": "20.16.10", "@types/npm-package-arg": "6.1.1", @@ -227,6 +228,7 @@ "jest-runtime": "29.7.0", "jest-util": "29.7.0", "js-tokens": "^4.0.0", + "js-yaml": "^4.1.0", "jsonc-eslint-parser": "^2.1.0", "jsonc-parser": "3.2.0", "kill-port": "^1.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f4590f1a18f5f6..f5f68b524de940 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -484,6 +484,9 @@ importers: '@types/jest': specifier: 29.5.12 version: 29.5.12 + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 '@types/marked': specifier: ^2.0.0 version: 2.0.5 @@ -757,6 +760,9 @@ importers: js-tokens: specifier: ^4.0.0 version: 4.0.0 + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 jsonc-eslint-parser: specifier: ^2.1.0 version: 2.4.0 @@ -993,7 +999,7 @@ importers: version: 5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4) webpack-dev-server: specifier: 5.0.4 - version: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0))(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)) + version: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4)(webpack@5.88.0) webpack-merge: specifier: ^5.8.0 version: 5.10.0 @@ -6958,6 +6964,9 @@ packages: '@types/jest@29.5.12': resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + '@types/jsdom@20.0.1': resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} @@ -22970,7 +22979,7 @@ snapshots: tsconfig-paths-webpack-plugin: 4.0.0 tslib: 2.8.1 webpack: 5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4) - webpack-dev-server: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0))(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)) + webpack-dev-server: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4)(webpack@5.88.0) webpack-node-externals: 3.0.0 webpack-subresource-integrity: 5.1.0(html-webpack-plugin@5.5.0(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)))(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)) transitivePeerDependencies: @@ -23341,7 +23350,7 @@ snapshots: webpack: 5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4) optionalDependencies: type-fest: 3.13.1 - webpack-dev-server: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0))(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)) + webpack-dev-server: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4)(webpack@5.88.0) webpack-hot-middleware: 2.26.1 '@pnpm/lockfile-types@6.0.0': @@ -24006,7 +24015,7 @@ snapshots: mime-types: 2.1.35 p-retry: 4.6.2 webpack-dev-middleware: 7.4.2(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)) - webpack-dev-server: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0))(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)) + webpack-dev-server: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4)(webpack@5.88.0) ws: 8.18.0(bufferutil@4.0.7) transitivePeerDependencies: - '@types/express' @@ -24983,6 +24992,8 @@ snapshots: expect: 29.7.0 pretty-format: 29.7.0 + '@types/js-yaml@4.0.9': {} + '@types/jsdom@20.0.1': dependencies: '@types/node': 20.16.10 @@ -26015,7 +26026,7 @@ snapshots: webpack: 5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0) optionalDependencies: - webpack-dev-server: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0))(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)) + webpack-dev-server: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4)(webpack@5.88.0) '@xstate/immer@0.3.1(immer@9.0.21)(xstate@4.34.0)': dependencies: @@ -28759,8 +28770,8 @@ snapshots: '@typescript-eslint/parser': 8.6.0(eslint@8.57.0)(typescript@5.7.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.20.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.20.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.10.1(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) @@ -28783,33 +28794,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.20.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.3.7(supports-color@8.1.1) enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.20.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) fast-glob: 3.3.3 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 is-glob: 4.0.3 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.20.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.20.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: '@typescript-eslint/parser': 8.6.0(eslint@8.57.0)(typescript@5.7.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.20.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -28823,14 +28834,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.20.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: '@typescript-eslint/parser': 8.6.0(eslint@8.57.0)(typescript@5.7.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.20.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -28868,7 +28879,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.20.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -28879,7 +28890,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.20.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -37664,7 +37675,7 @@ snapshots: webpack: 5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4) webpack-merge: 5.10.0 optionalDependencies: - webpack-dev-server: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0))(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)) + webpack-dev-server: 5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4)(webpack@5.88.0) webpack-dev-middleware@6.1.3(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)): dependencies: @@ -37698,7 +37709,7 @@ snapshots: optionalDependencies: webpack: 5.97.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.24.2)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0)) - webpack-dev-server@5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0))(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)): + webpack-dev-server@5.0.4(bufferutil@4.0.7)(webpack-cli@5.1.4)(webpack@5.88.0): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 diff --git a/tools/workspace-plugin/src/conformance-rules/blog-description/index.ts b/tools/workspace-plugin/src/conformance-rules/blog-description/index.ts new file mode 100644 index 00000000000000..5bce214d914e02 --- /dev/null +++ b/tools/workspace-plugin/src/conformance-rules/blog-description/index.ts @@ -0,0 +1,80 @@ +import { readFileSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { load as yamlLoad } from 'js-yaml'; +import { workspaceRoot } from '@nx/devkit'; +import { sync as globSync } from 'glob'; +import { + createConformanceRule, + type ProjectFilesViolation, +} from '@nx/powerpack-conformance'; + +export default createConformanceRule<{ mdGlobPattern: string }>({ + name: 'blog-description', + category: 'consistency', + description: + 'Ensures that blog posts have a description in their frontmatter', + reporter: 'project-files-reporter', + implementation: async ({ projectGraph, ruleOptions }) => { + const violations: ProjectFilesViolation[] = []; + const { mdGlobPattern } = ruleOptions; + + // Look for the docs project + const docsProject = Object.values(projectGraph.nodes).find( + (project) => project.name === 'docs' + ); + + if (!docsProject) { + return { + severity: 'low', + details: { + violations: [], + }, + }; + } + + const blogPattern = join( + workspaceRoot, + docsProject.data.root, + mdGlobPattern + ); + const files = findMarkdownFiles(blogPattern); + + for (const file of files) { + const content = readFileSync(file, 'utf-8'); + const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); + + // Only check files with frontmatter + if (frontmatterMatch) { + try { + const frontmatter = yamlLoad(frontmatterMatch[1]) as Record< + string, + unknown + >; + + if (!frontmatter.description) { + violations.push({ + message: + 'Blog posts with frontmatter must have a description field', + sourceProject: docsProject.name, + file: file, + }); + } + } catch (e) { + // If YAML parsing fails, we skip the file + continue; + } + } + } + + return { + severity: 'high', + details: { + violations, + }, + }; + }, +}); + +function findMarkdownFiles(pattern: string): string[] { + return globSync(pattern); +} diff --git a/tools/workspace-plugin/src/conformance-rules/blog-description/schema.json b/tools/workspace-plugin/src/conformance-rules/blog-description/schema.json new file mode 100644 index 00000000000000..64ad255b534ba7 --- /dev/null +++ b/tools/workspace-plugin/src/conformance-rules/blog-description/schema.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "mdGlobPattern": { + "type": "string", + "description": "The glob pattern to use to find the markdown files to analyze" + } + }, + "additionalProperties": false +}