diff --git a/.github/workflows/publish-packages.yml b/.github/workflows/publish-packages.yml
index 3d35a9fe01da7..6c6f7e32a7f28 100644
--- a/.github/workflows/publish-packages.yml
+++ b/.github/workflows/publish-packages.yml
@@ -74,13 +74,25 @@ jobs:
# If a specific package is requested via workflow_dispatch, just publish that one
echo "matrix={\"package\":[\"$PACKAGE\"]}" >> $GITHUB_OUTPUT
else
- # Otherwise, identify all packages with changes since the last commit
CHANGED_PACKAGES=()
for pkg in $(ls -d packages/*); do
PKG_NAME=$(basename "$pkg")
- # For manual runs, include all packages. For automatic runs, only include packages with changes
+ PKG_JSON="$pkg/package.json"
+
+ # Determine if the package has changed (or include all on manual trigger)
if [ "$EVENT_NAME" == "workflow_dispatch" ] || ! git diff --quiet $COMMIT_SHA~1 $COMMIT_SHA -- "$pkg/"; then
- CHANGED_PACKAGES+=("$PKG_NAME")
+ HAS_VERSION=$(jq 'has("version")' "$PKG_JSON")
+ if [ "$HAS_VERSION" == "false" ]; then
+ # Include packages without version field
+ CHANGED_PACKAGES+=("$PKG_NAME")
+ else
+ # For packages with version field, include only if version changed
+ OLD_VERSION=$(git show $COMMIT_SHA~1:$PKG_JSON | jq -r '.version')
+ NEW_VERSION=$(jq -r '.version' "$PKG_JSON")
+ if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
+ CHANGED_PACKAGES+=("$PKG_NAME")
+ fi
+ fi
fi
done
@@ -125,8 +137,12 @@ jobs:
run: |
# Install deps
pnpm install --frozen-lockfile
- # Create a unique version using the commit SHA as a prerelease identifier
- npm version --no-git-tag-version 1.0.1-$COMMIT_SHA
+
+ HAS_VERSION=$(jq 'has("version")' package.json)
+ if [ "$HAS_VERSION" == "false" ]; then
+ # Only bump version if package has no version field
+ npm version --no-git-tag-version 1.0.1-$COMMIT_SHA
+ fi
# Check if a custom publish script exists in package.json
if jq -e '.scripts.publish' package.json > /dev/null; then
diff --git a/apps/site/.remarkrc.json b/apps/site/.remarkrc.json
index 40370dea6d613..71f00f776d25e 100644
--- a/apps/site/.remarkrc.json
+++ b/apps/site/.remarkrc.json
@@ -1,21 +1,3 @@
{
- "settings": {
- "bullet": "-",
- "resourceLink": true
- },
- "plugins": [
- "remark-frontmatter",
- "remark-preset-lint-node",
- ["remark-gfm", false],
- ["remark-lint-fenced-code-flag", false],
- ["remark-lint-first-heading-level", false],
- ["remark-lint-maximum-line-length", false],
- ["remark-lint-no-file-name-articles", false],
- ["remark-lint-no-literal-urls", false],
- ["remark-lint-no-unused-definitions", false],
- ["remark-lint-no-undefined-references", false],
- ["remark-lint-prohibited-strings", false],
- ["remark-lint-unordered-list-marker-style", "-"],
- ["remark-preset-lint-node/remark-lint-nodejs-links.js", false]
- ]
+ "plugins": ["remark-frontmatter", "@node-core/remark-lint"]
}
diff --git a/apps/site/eslint.config.js b/apps/site/eslint.config.js
index dd5bea57b1387..61ca4ccb1d6ec 100644
--- a/apps/site/eslint.config.js
+++ b/apps/site/eslint.config.js
@@ -20,7 +20,7 @@ const compatConfig = compat.config({
export default tseslint.config(
...baseConfig,
- { ignores: ['pages/en/blog/**/*.{md,mdx}/**', 'public'] },
+ { ignores: ['pages/en/blog/**/*.{md,mdx}/**', 'public', 'next-env.d.ts'] },
{
extends: [
react.configs.flat['jsx-runtime'],
diff --git a/apps/site/next-env.d.ts b/apps/site/next-env.d.ts
index 3cd7048ed9471..36a4fe488ad02 100644
--- a/apps/site/next-env.d.ts
+++ b/apps/site/next-env.d.ts
@@ -1,6 +1,7 @@
///
///
///
+///
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/apps/site/next.config.mjs b/apps/site/next.config.mjs
index bb7ad9b4e52fc..d162a178f4335 100644
--- a/apps/site/next.config.mjs
+++ b/apps/site/next.config.mjs
@@ -7,8 +7,6 @@ import { redirects, rewrites } from './next.rewrites.mjs';
/** @type {import('next').NextConfig} */
const nextConfig = {
allowedDevOrigins: ['10.1.1.232'],
- // We don't use trailing slashes on URLs from the Node.js Website
- trailingSlash: false,
// We don't want to redirect with trailing slashes
skipTrailingSlashRedirect: true,
// We allow the BASE_PATH to be overridden in case that the Website
@@ -54,7 +52,7 @@ const nextConfig = {
],
},
// On static export builds we want the output directory to be "build"
- distDir: ENABLE_STATIC_EXPORT ? 'build' : '.next',
+ distDir: ENABLE_STATIC_EXPORT ? 'build' : undefined,
// On static export builds we want to enable the export feature
output: ENABLE_STATIC_EXPORT ? 'export' : undefined,
// This configures all the Next.js rewrites, which are used for rewriting internal URLs into other internal Endpoints
@@ -65,10 +63,13 @@ const nextConfig = {
// We don't want to run Type Checking on Production Builds
// as we already check it on the CI within each Pull Request
typescript: { ignoreBuildErrors: true },
+ // Enable statically typed links
+ // @see https://nextjs.org/docs/app/api-reference/config/typescript#statically-typed-links
+ typedRoutes: true,
// We don't want to run ESLint Checking on Production Builds
// as we already check it on the CI within each Pull Request
- // we also configure ESLint to run its lint checking on all files (next lint)
- eslint: { dirs: ['.'], ignoreDuringBuilds: true },
+ // we also configure ESLint to run its lint checking on all files
+ eslint: { ignoreDuringBuilds: true },
experimental: {
// Ensure that server-side code is also minified
serverMinification: true,
diff --git a/apps/site/package.json b/apps/site/package.json
index e08cc2a3adc0e..7174e6dbe56f6 100644
--- a/apps/site/package.json
+++ b/apps/site/package.json
@@ -60,7 +60,7 @@
"feed": "~5.1.0",
"github-slugger": "~2.0.0",
"gray-matter": "~4.0.3",
- "next": "15.4.4",
+ "next": "15.5.0",
"next-intl": "~4.3.4",
"next-themes": "~0.4.6",
"postcss-calc": "~10.1.1",
@@ -81,13 +81,14 @@
"@eslint/compat": "~1.3.1",
"@eslint/eslintrc": "~3.3.1",
"@flarelabs-net/wrangler-build-time-fs-assets-polyfilling": "^0.0.1",
- "@next/eslint-plugin-next": "15.4.4",
+ "@next/eslint-plugin-next": "15.5.0",
+ "@node-core/remark-lint": "workspace:*",
"@opennextjs/cloudflare": "^1.6.4",
"@playwright/test": "^1.54.1",
"@testing-library/user-event": "~14.6.1",
"@types/mdx": "^2.0.13",
"@types/semver": "~7.7.0",
- "eslint-config-next": "15.4.4",
+ "eslint-config-next": "15.5.0",
"eslint-import-resolver-typescript": "~4.4.4",
"eslint-plugin-mdx": "~3.6.2",
"eslint-plugin-react": "~7.37.4",
@@ -95,17 +96,7 @@
"global-jsdom": "^26.0.0",
"handlebars": "4.7.8",
"jsdom": "^26.0.0",
- "remark-frontmatter": "5.0.0",
- "remark-lint-fenced-code-flag": "^4.2.0",
- "remark-lint-first-heading-level": "^4.0.1",
- "remark-lint-maximum-line-length": "^4.1.1",
- "remark-lint-no-file-name-articles": "^3.0.1",
- "remark-lint-no-literal-urls": "^4.0.1",
- "remark-lint-no-undefined-references": "^5.0.2",
- "remark-lint-no-unused-definitions": "^4.0.2",
- "remark-lint-prohibited-strings": "^4.0.0",
- "remark-lint-unordered-list-marker-style": "^4.0.1",
- "remark-preset-lint-node": "5.1.2",
+ "remark-frontmatter": "^5.0.0",
"stylelint": "16.23.0",
"stylelint-config-standard": "39.0.0",
"stylelint-order": "7.0.0",
diff --git a/apps/site/pages/en/learn/modules/how-to-use-streams.md b/apps/site/pages/en/learn/modules/how-to-use-streams.md
index a98b48bf15a98..f046574e21ef8 100644
--- a/apps/site/pages/en/learn/modules/how-to-use-streams.md
+++ b/apps/site/pages/en/learn/modules/how-to-use-streams.md
@@ -815,6 +815,7 @@ This work is derived from content published by [Matteo Collina][] in [Platformat
[`on('close')`]: https://nodejs.org/api/stream.html#event-close_1
[`on('error')`]: https://nodejs.org/api/stream.html#event-error_1
[`.read()`]: https://nodejs.org/docs/latest/api/stream.html#stream_readable_read_size
+[`_read()`]: https://nodejs.org/api/stream.html#readable_readsize
[`.write()`]: https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback
[`_write`]: https://nodejs.org/api/stream.html#writable_writechunk-encoding-callback
[`.end()`]: https://nodejs.org/api/stream.html#writableendchunk-encoding-callback
diff --git a/apps/site/pages/en/learn/modules/publishing-a-package.mdx b/apps/site/pages/en/learn/modules/publishing-a-package.mdx
index 9d5299d89a7c0..98e1b4423aeba 100644
--- a/apps/site/pages/en/learn/modules/publishing-a-package.mdx
+++ b/apps/site/pages/en/learn/modules/publishing-a-package.mdx
@@ -6,7 +6,7 @@ authors: JakobJingleheimer
# Publishing a package
-All the provided `package.json` configurations (not specifically marked “does not work”) work in Node.js 12.22.x (v12 latest, the oldest supported line) and 17.2.0 [current latest at the time][^1], and for grins, with webpack 5.53.0 and 5.63.0 respectively. These are available: [JakobJingleheimer/nodejs-module-config-examples](https://github.com/JakobJingleheimer/nodejs-module-config-examples).
+All the provided `package.json` configurations (not specifically marked “does not work”) work in Node.js 12.22.x (v12 latest, the oldest supported line) and 17.2.0 current latest at the time[^1], and for grins, with webpack 5.53.0 and 5.63.0 respectively. These are available: [JakobJingleheimer/nodejs-module-config-examples](https://github.com/JakobJingleheimer/nodejs-module-config-examples).
For curious cats, [How did we get here](#how-did-we-get-here) and [Down the rabbit-hole](#down-the-rabbit-hole) provide background and deeper explanations.
@@ -337,7 +337,7 @@ We're not in Kansas anymore, Toto.
The configurations (there are 2 options) are nearly the same as [ESM source and both CJS & ESM distribution](#esm-source-and-both-cjs-amp-esm-distribution), just exclude `packageJson.exports.import`.
-💡 Using [`"type": "module"`][^2] paired with the `.cjs` file extension (for commonjs files) yields best results. For more information on why, see [Down the rabbit-hole](#down-the-rabbit-hole) and [Gotchas](#gotchas) below.
+💡 Using `"type": "module"`[^2] paired with the `.cjs` file extension (for commonjs files) yields best results. For more information on why, see [Down the rabbit-hole](#down-the-rabbit-hole) and [Gotchas](#gotchas) below.
**Working example**: [esm-with-cjs-distro](https://github.com/JakobJingleheimer/nodejs-module-config-examples/tree/main/packages/esm/cjs-distro)
@@ -386,7 +386,7 @@ If your files explicitly _all_ use `.cjs` and/or `.mjs` file extensions (none us
}
```
-💡 Using [`"type": "module"`][^2] paired with the `.cjs` file extension (for commonjs files) yields best results. For more information on why, see [Down the rabbit-hole](#down-the-rabbit-hole) and [Gotchas](#gotchas) below.
+💡 Using `"type": "module"`[^2] paired with the `.cjs` file extension (for commonjs files) yields best results. For more information on why, see [Down the rabbit-hole](#down-the-rabbit-hole) and [Gotchas](#gotchas) below.
#### Publish a CJS distribution with an ESM wrapper
@@ -426,7 +426,7 @@ This is also almost identical to the [CJS source and dual distribution using an
}
```
-💡 Using [`"type": "module"`][^2] paired with the `.cjs` file extension (for commonjs files) yields best results. For more information on why, see [Down the rabbit-hole](#down-the-rabbit-hole) and [Gotchas](#gotchas) below.
+💡 Using `"type": "module"`[^2] paired with the `.cjs` file extension (for commonjs files) yields best results. For more information on why, see [Down the rabbit-hole](#down-the-rabbit-hole) and [Gotchas](#gotchas) below.
#### Publish both full CJS & ESM distributions
@@ -498,7 +498,7 @@ Alternatively, you can use `"default"` and `"node"` keys, which are less counter
}
```
-💡 Using [`"type": "module"`][^2] paired with the `.cjs` file extension (for commonjs files) yields best results. For more information on why, see [Down the rabbit-hole](#down-the-rabbit-hole) and [Gotchas](#gotchas) below.
+💡 Using `"type": "module"`[^2] paired with the `.cjs` file extension (for commonjs files) yields best results. For more information on why, see [Down the rabbit-hole](#down-the-rabbit-hole) and [Gotchas](#gotchas) below.
##### Use the `.mjs` (or equivalent) file extension for all source code files
diff --git a/apps/site/pages/id/about/security-reporting.mdx b/apps/site/pages/id/about/security-reporting.mdx
index 396acfce25eb6..307463d9aeb0a 100644
--- a/apps/site/pages/id/about/security-reporting.mdx
+++ b/apps/site/pages/id/about/security-reporting.mdx
@@ -46,7 +46,7 @@ Pemberitahuan keamanan akan didistribusikan melalui metode berikut.
## Komentar tentang kebijakan ini
-Jika Anda memiliki saran tentang bagaimana proses ini dapat ditingkatkan, silakan kirimkan [permintaan penarikan](https://github.com/nodejs/nodejs.org) atau [ajukan masalah (https://github.com/nodejs/security -wg/issues/new) untuk didiskusikan.
+Jika Anda memiliki saran tentang bagaimana proses ini dapat ditingkatkan, silakan kirimkan [permintaan penarikan](https://github.com/nodejs/nodejs.org) atau [ajukan masalah](https://github.com/nodejs/security-wg/issues/new) untuk didiskusikan.
## Praktik Terbaik OpenSSF
diff --git a/packages/i18n/package.json b/packages/i18n/package.json
index ee8c742919d47..e99deeabdaf7f 100644
--- a/packages/i18n/package.json
+++ b/packages/i18n/package.json
@@ -10,6 +10,11 @@
],
".": "./src/index.mjs"
},
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/nodejs/nodejs.org",
+ "directory": "packages/i18n"
+ },
"main": "./src/index.mjs",
"module": "./src/index.mjs",
"scripts": {
diff --git a/packages/rehype-shiki/package.json b/packages/rehype-shiki/package.json
index 631308016f4de..62edd03c52ae2 100644
--- a/packages/rehype-shiki/package.json
+++ b/packages/rehype-shiki/package.json
@@ -5,6 +5,11 @@
".": "./src/index.mjs",
"./*": "./src/*.mjs"
},
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/nodejs/nodejs.org",
+ "directory": "packages/rehype-shiki"
+ },
"scripts": {
"lint": "node --run lint:js",
"lint:fix": "node --run lint:js:fix",
diff --git a/packages/remark-lint/.lintstagedrc.json b/packages/remark-lint/.lintstagedrc.json
new file mode 100644
index 0000000000000..1d1673ffabe4d
--- /dev/null
+++ b/packages/remark-lint/.lintstagedrc.json
@@ -0,0 +1,4 @@
+{
+ "**/*.{js,mjs,ts,tsx,md,mdx}": ["prettier --check --write", "eslint --fix"],
+ "**/*.{json,yml}": ["prettier --check --write"]
+}
diff --git a/packages/remark-lint/README.md b/packages/remark-lint/README.md
new file mode 100644
index 0000000000000..3097a32207fa7
--- /dev/null
+++ b/packages/remark-lint/README.md
@@ -0,0 +1,123 @@
+# `@node-core/remark-lint`
+
+A [`remark-lint`](https://github.com/remarkjs/remark-lint) plugin with configurations tailored to the documentation and contribution standards of the [Node.js GitHub Organization](https://github.com/nodejs).
+
+## Installation
+
+```bash
+npm install --save-dev @node-core/remark-lint
+```
+
+## Usage
+
+Add the plugin to your `.remarkrc` or `remark.config.js`:
+
+```json
+{
+ "plugins": ["@node-core/remark-lint"]
+}
+```
+
+Run remark to lint your Markdown files:
+
+```bash
+npx remark . --frail
+```
+
+## Configuration
+
+### Released Versions
+
+Some rules, such as `node-core:yaml-comments`, validate version references against known released Node.js versions. You can provide these using the `releasedVersions` option:
+
+```json
+{
+ "plugins": [
+ [
+ "@node-core/remark-lint",
+ {
+ "releasedVersions": ["v18.0.0", "v18.1.0", "v18.2.0", "v20.0.0"]
+ }
+ ]
+ ]
+}
+```
+
+For Node.js projects, these versions can be automatically generated [using `list-released-versions-from-changelogs.mjs`](https://github.com/nodejs/node/blob/main/tools/lint-md/list-released-versions-from-changelogs.mjs).
+
+If not specified, version-related rules will accept any valid SemVer format.
+
+## Rules
+
+### `node-core:duplicate-stability-nodes`
+
+Prevents redundant stability markers in nested sections.
+
+**Not allowed:**
+
+```markdown
+# Parent Section
+
+> Stability: 2 - Stable
+
+## Child Section
+
+> Stability: 2 - Stable
+```
+
+### `node-core:hashed-self-reference`
+
+Ensures self-references use fragment-only links.
+
+**Allowed:**
+
+```markdown
+See the [Introduction](#introduction) section.
+```
+
+**Not allowed:**
+
+```markdown
+See the [Introduction](document.md#introduction) section.
+```
+
+### `node-core:ordered-references`
+
+Enforces alphabetical sorting of reference-style link definitions.
+
+**Allowed:**
+
+```markdown
+[api]: https://example.com/api
+[docs]: https://example.com/docs
+[info]: https://example.com/info
+```
+
+### `node-core:required-metadata`
+
+Requires essential metadata for documentation:
+
+- `llm_description`: A description for Large Language Models (can be inferred from first paragraph)
+- `introduced_in`: API introduction version
+
+Metadata can be provided in comments:
+
+```markdown
+
+```
+
+### `node-core:yaml-comments`
+
+Enforces structure and content of YAML comment blocks:
+
+- `added`: An array of valid version strings
+- `napiVersion`: The N-API version
+- `deprecated`: An array of valid version strings
+- `removed`: An array of valid version strings
+- `changes`: An array of:
+ - `pr-url`: Pull request URL
+ - `commit`: Commit hash (only required for security fixes)
+ - `version`: Valid version string
+ - `description`: Change description
+
+All version references must be valid SemVer, or match the provided `releasedVersions`.
diff --git a/packages/remark-lint/eslint.config.js b/packages/remark-lint/eslint.config.js
new file mode 100644
index 0000000000000..205088e6ad6ae
--- /dev/null
+++ b/packages/remark-lint/eslint.config.js
@@ -0,0 +1,17 @@
+import globals from 'globals';
+
+import baseConfig from '../../eslint.config.js';
+
+export default [
+ ...baseConfig,
+ {
+ languageOptions: {
+ globals: globals.nodeBuiltin,
+ parserOptions: {
+ // Allow nullish syntax (i.e. "?." or "??"),
+ // and top-level await
+ ecmaVersion: 'latest',
+ },
+ },
+ },
+];
diff --git a/packages/remark-lint/package.json b/packages/remark-lint/package.json
new file mode 100644
index 0000000000000..a607b9e085217
--- /dev/null
+++ b/packages/remark-lint/package.json
@@ -0,0 +1,64 @@
+{
+ "name": "@node-core/remark-lint",
+ "type": "module",
+ "version": "1.0.0",
+ "exports": {
+ ".": "./src/index.mjs",
+ "./api": "./src/api.mjs"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/nodejs/nodejs.org",
+ "directory": "packages/remark-lint"
+ },
+ "scripts": {
+ "lint": "node --run lint:js",
+ "lint:fix": "node --run lint:js:fix",
+ "lint:js": "eslint \"**/*.mjs\"",
+ "lint:js:fix": "node --run lint:js -- --fix",
+ "test": "node --run test:unit",
+ "test:unit": "cross-env NODE_NO_WARNINGS=1 node --experimental-test-coverage --test \"**/*.test.mjs\""
+ },
+ "dependencies": {
+ "remark-gfm": "^4.0.1",
+ "remark-lint-blockquote-indentation": "^4.0.1",
+ "remark-lint-checkbox-character-style": "^5.0.1",
+ "remark-lint-checkbox-content-indent": "^5.0.1",
+ "remark-lint-code-block-style": "^4.0.1",
+ "remark-lint-definition-spacing": "^4.0.1",
+ "remark-lint-fenced-code-flag": "^4.2.0",
+ "remark-lint-fenced-code-marker": "^4.0.1",
+ "remark-lint-final-definition": "^4.0.2",
+ "remark-lint-heading-style": "^4.0.1",
+ "remark-lint-maximum-line-length": "^4.1.1",
+ "remark-lint-no-consecutive-blank-lines": "^5.0.1",
+ "remark-lint-no-file-name-consecutive-dashes": "^3.0.1",
+ "remark-lint-no-file-name-outer-dashes": "^3.0.1",
+ "remark-lint-no-heading-indent": "^5.0.1",
+ "remark-lint-no-literal-urls": "^4.0.1",
+ "remark-lint-no-multiple-toplevel-headings": "^4.0.1",
+ "remark-lint-no-shell-dollars": "^4.0.1",
+ "remark-lint-no-table-indentation": "^5.0.1",
+ "remark-lint-no-tabs": "^4.0.1",
+ "remark-lint-no-trailing-spaces": "^3.0.2",
+ "remark-lint-no-unused-definitions": "^4.0.2",
+ "remark-lint-prohibited-strings": "^4.0.0",
+ "remark-lint-rule-style": "^4.0.1",
+ "remark-lint-strong-marker": "^4.0.1",
+ "remark-lint-table-cell-padding": "^5.1.1",
+ "remark-lint-table-pipes": "^5.0.1",
+ "remark-lint-unordered-list-marker-style": "^4.0.1",
+ "remark-preset-lint-recommended": "^7.0.1",
+ "semver": "^7.7.2",
+ "unified-lint-rule": "^3.0.1",
+ "unist-util-visit": "^5.0.0",
+ "yaml": "^2.8.1"
+ },
+ "devDependencies": {
+ "cross-env": "catalog:",
+ "dedent": "^1.6.0",
+ "globals": "^16.3.0",
+ "remark-parse": "^11.0.0",
+ "unified": "^11.0.5"
+ }
+}
diff --git a/packages/remark-lint/src/api.mjs b/packages/remark-lint/src/api.mjs
new file mode 100644
index 0000000000000..962ad03e715a6
--- /dev/null
+++ b/packages/remark-lint/src/api.mjs
@@ -0,0 +1,67 @@
+import remarkLintFencedCodeFlag from 'remark-lint-fenced-code-flag';
+import remarkLintMaximumLineLength from 'remark-lint-maximum-line-length';
+import remarkLintNoUnusedDefinitions from 'remark-lint-no-unused-definitions';
+import remarkLintProhibitedStrings from 'remark-lint-prohibited-strings';
+import remarkLintUnorderedListMarkerStyle from 'remark-lint-unordered-list-marker-style';
+
+import basePreset from './index.mjs';
+import duplicateStabilityNodes from './rules/duplicate-stability-nodes.mjs';
+import hashedSelfReference from './rules/hashed-self-reference.mjs';
+import orderedReferences from './rules/ordered-references.mjs';
+import requiredMetadata from './rules/required-metadata.mjs';
+import yamlComments from './rules/yaml/index.mjs';
+
+/**
+ * @typedef {Object} Options
+ * @property {Array} releasedVersions The released versions, for validating the YAML
+ */
+
+/**
+ * @param {Options} options
+ */
+export default (options = {}) => ({
+ settings: {
+ ...basePreset.settings,
+ bullet: '*',
+ },
+ plugins: [
+ ...basePreset.plugins,
+
+ // Internal Rules
+ ...[
+ duplicateStabilityNodes,
+ yamlComments,
+ hashedSelfReference,
+ orderedReferences,
+ requiredMetadata,
+ ].map(plugin => [plugin, options]),
+
+ // External Rules
+ remarkLintNoUnusedDefinitions,
+ [remarkLintFencedCodeFlag, { allowEmpty: false }],
+ [remarkLintMaximumLineLength, 120],
+ [remarkLintUnorderedListMarkerStyle, '*'],
+ [
+ remarkLintProhibitedStrings,
+ [
+ { yes: 'End-of-Life' },
+ { no: 'filesystem', yes: 'file system' },
+ { yes: 'GitHub' },
+ { no: 'hostname', yes: 'host name' },
+ { yes: 'JavaScript' },
+ { no: '[Ll]ong[ -][Tt]erm [Ss]upport', yes: 'Long Term Support' },
+ { no: 'Node', yes: 'Node.js', ignoreNextTo: '-API' },
+ { yes: 'Node.js' },
+ { no: 'Node[Jj][Ss]', yes: 'Node.js' },
+ { no: "Node\\.js's?", yes: 'the Node.js' },
+ { no: '[Nn]ote that', yes: '' },
+ { yes: 'RFC' },
+ { no: '[Rr][Ff][Cc]\\d+', yes: 'RFC ' },
+ { yes: 'TypeScript' },
+ { yes: 'Unix' },
+ { yes: 'Valgrind' },
+ { yes: 'V8' },
+ ],
+ ],
+ ],
+});
diff --git a/packages/remark-lint/src/index.mjs b/packages/remark-lint/src/index.mjs
new file mode 100644
index 0000000000000..bad1d1d8cc956
--- /dev/null
+++ b/packages/remark-lint/src/index.mjs
@@ -0,0 +1,76 @@
+import remarkGfm from 'remark-gfm';
+import remarkLintBlockquoteIndentation from 'remark-lint-blockquote-indentation';
+import remarkLintCheckboxCharacterStyle from 'remark-lint-checkbox-character-style';
+import remarkLintCheckboxContentIndent from 'remark-lint-checkbox-content-indent';
+import remarkLintCodeBlockStyle from 'remark-lint-code-block-style';
+import remarkLintDefinitionSpacing from 'remark-lint-definition-spacing';
+import remarkLintFencedCodeMarker from 'remark-lint-fenced-code-marker';
+import remarkLintFinalDefinition from 'remark-lint-final-definition';
+import remarkLintHeadingStyle from 'remark-lint-heading-style';
+import remarkLintNoConsecutiveBlankLines from 'remark-lint-no-consecutive-blank-lines';
+import remarkLintNoFileNameConsecutiveDashes from 'remark-lint-no-file-name-consecutive-dashes';
+import remarkLintNofileNameOuterDashes from 'remark-lint-no-file-name-outer-dashes';
+import remarkLintNoHeadingIndent from 'remark-lint-no-heading-indent';
+import remarkLintNoLiteralURLs from 'remark-lint-no-literal-urls';
+import remarkLintNoMultipleToplevelHeadings from 'remark-lint-no-multiple-toplevel-headings';
+import remarkLintNoShellDollars from 'remark-lint-no-shell-dollars';
+import remarkLintNoTableIndentation from 'remark-lint-no-table-indentation';
+import remarkLintNoTabs from 'remark-lint-no-tabs';
+import remarkLintNoTrailingSpaces from 'remark-lint-no-trailing-spaces';
+import remarkLintNoUnusedDefinitions from 'remark-lint-no-unused-definitions';
+import remarkLintRuleStyle from 'remark-lint-rule-style';
+import remarkLintStrongMarker from 'remark-lint-strong-marker';
+import remarkLintTableCellPadding from 'remark-lint-table-cell-padding';
+import remarkLintTablePipes from 'remark-lint-table-pipes';
+import remarkPresetLintRecommended from 'remark-preset-lint-recommended';
+
+export default {
+ settings: {
+ tightDefinitions: true,
+ emphasis: '_',
+ bullet: '-',
+ rule: '-',
+ },
+ plugins: [
+ // Enable GitHub Flavored Markdown
+ remarkGfm,
+
+ // Base plugins
+ remarkPresetLintRecommended,
+
+ // Blockquote and list rules
+ [remarkLintBlockquoteIndentation, 2],
+ [remarkLintCheckboxCharacterStyle, { checked: 'x', unchecked: ' ' }],
+ remarkLintCheckboxContentIndent,
+
+ // Code and formatting rules
+ [remarkLintCodeBlockStyle, 'fenced'],
+ [remarkLintFencedCodeMarker, '`'],
+ [remarkLintRuleStyle, '---'],
+ [remarkLintStrongMarker, '*'],
+
+ // File and filename rules
+ remarkLintNoFileNameConsecutiveDashes,
+ remarkLintNofileNameOuterDashes,
+
+ // Heading and link rules
+ remarkLintFinalDefinition,
+ [remarkLintNoUnusedDefinitions, false],
+ [remarkLintNoLiteralURLs, false],
+ [remarkLintHeadingStyle, 'atx'],
+ remarkLintNoHeadingIndent,
+ remarkLintNoMultipleToplevelHeadings,
+
+ // Layout and spacing rules
+ remarkLintDefinitionSpacing,
+ remarkLintNoConsecutiveBlankLines,
+ remarkLintNoTableIndentation,
+ remarkLintNoTabs,
+ remarkLintNoTrailingSpaces,
+ [remarkLintTableCellPadding, 'padded'],
+ remarkLintTablePipes,
+
+ // Shell and console rules
+ remarkLintNoShellDollars,
+ ],
+};
diff --git a/packages/remark-lint/src/rules/__tests__/duplicate-stability-nodes.test.mjs b/packages/remark-lint/src/rules/__tests__/duplicate-stability-nodes.test.mjs
new file mode 100644
index 0000000000000..1cb1816f2195f
--- /dev/null
+++ b/packages/remark-lint/src/rules/__tests__/duplicate-stability-nodes.test.mjs
@@ -0,0 +1,59 @@
+import { describe, it } from 'node:test';
+
+import dedent from 'dedent';
+
+import duplicateStabilityNodes from '../duplicate-stability-nodes.mjs';
+import { testRule } from './utils.mjs';
+
+const testCases = [
+ {
+ name: 'no stability nodes',
+ input: dedent`
+ # Heading 1
+
+ > No stability here.
+ `,
+ expected: [],
+ },
+ {
+ name: 'single stability blockquote',
+ input: dedent`
+ # Heading 1
+
+ > Stability: 1.0
+ `,
+ expected: [],
+ },
+ {
+ name: 'different stabilities under different headings',
+ input: dedent`
+ # Heading 1
+
+ > Stability: 1.0
+
+ ## Heading 2
+
+ > Stability: 2.0
+ `,
+ expected: [],
+ },
+ {
+ name: 'duplicate stability at deeper heading triggers message',
+ input: dedent`
+ # Heading 1
+
+ > Stability: 1.0
+
+ ## Heading 2
+
+ > Stability: 1.0
+ `,
+ expected: ['Duplicate stability node'],
+ },
+];
+
+describe('duplicate-stability-nodes', () => {
+ for (const { name, input, expected } of testCases) {
+ it(name, () => testRule(duplicateStabilityNodes, input, expected));
+ }
+});
diff --git a/packages/remark-lint/src/rules/__tests__/hashed-self-references.test.mjs b/packages/remark-lint/src/rules/__tests__/hashed-self-references.test.mjs
new file mode 100644
index 0000000000000..70a884ca6be45
--- /dev/null
+++ b/packages/remark-lint/src/rules/__tests__/hashed-self-references.test.mjs
@@ -0,0 +1,52 @@
+import { describe, it } from 'node:test';
+
+import hashedSelfReference from '../hashed-self-reference.mjs';
+import { testRule } from './utils.mjs';
+
+const testCases = [
+ {
+ name: 'ignores links to other paths',
+ input: '[external](./other.md)',
+ path: 'docs/current.md',
+ expected: [],
+ },
+ {
+ name: 'accepts hashed self-reference',
+ input: '[header](#my-heading)',
+ path: 'docs/current.md',
+ expected: [],
+ },
+ {
+ name: 'reports self-reference with fragment',
+ input: '[bad link](./current.md#section)',
+ path: 'docs/current.md',
+ expected: [
+ 'Self-reference must start with hash (expected "#section", got "./current.md#section")',
+ ],
+ },
+ {
+ name: 'reports self-reference to path only',
+ input: '[bad link](./current.md)',
+ path: 'docs/current.md',
+ expected: [
+ 'Self-reference must start with hash (expected "#", got "./current.md")',
+ ],
+ },
+ {
+ name: 'ignores external links',
+ input: '[nodejs](https://nodejs.org)',
+ path: 'docs/current.md',
+ expected: [],
+ },
+];
+
+describe('hashed-self-references', () => {
+ for (const { name, input, expected, ...options } of testCases) {
+ it(name, () =>
+ testRule(hashedSelfReference, input, expected, {
+ ...options,
+ cwd: process.cwd(),
+ })
+ );
+ }
+});
diff --git a/packages/remark-lint/src/rules/__tests__/ordered-references.test.mjs b/packages/remark-lint/src/rules/__tests__/ordered-references.test.mjs
new file mode 100644
index 0000000000000..0205fc13730d2
--- /dev/null
+++ b/packages/remark-lint/src/rules/__tests__/ordered-references.test.mjs
@@ -0,0 +1,65 @@
+import { describe, it } from 'node:test';
+
+import dedent from 'dedent';
+
+import orderedReferences from '../ordered-references.mjs';
+import { testRule } from './utils.mjs';
+
+const testCases = [
+ {
+ name: 'no definitions',
+ input: 'Just some text.',
+ expected: [],
+ },
+ {
+ name: 'single definition',
+ input: '[foo]: https://example.com',
+ expected: [],
+ },
+ {
+ name: 'ordered references',
+ input: dedent`
+ [alpha]: https://example.com/a
+ [beta]: https://example.com/b
+ [charlie]: https://example.com/c
+ `,
+ expected: [],
+ },
+ {
+ name: 'unordered references (simple case)',
+ input: dedent`
+ [beta]: https://example.com/b
+ [alpha]: https://example.com/a
+ `,
+ expected: ['Unordered reference ("alpha" should be before "beta")'],
+ },
+ {
+ name: 'unordered references (deep nesting)',
+ input: dedent`
+ [foo]: https://example.com/z
+
+ > Note:
+
+ > [bar]: https://example.com/a
+ `,
+ expected: ['Unordered reference ("bar" should be before "foo")'],
+ },
+ {
+ name: 'multiple unordered references',
+ input: dedent`
+ [zulu]: https://z.com
+ [yankee]: https://y.com
+ [alpha]: https://a.com
+ `,
+ expected: [
+ 'Unordered reference ("yankee" should be before "zulu")',
+ 'Unordered reference ("alpha" should be before "yankee")',
+ ],
+ },
+];
+
+describe('hashed-self-references', () => {
+ for (const { name, input, expected } of testCases) {
+ it(name, () => testRule(orderedReferences, input, expected));
+ }
+});
diff --git a/packages/remark-lint/src/rules/__tests__/required-metadata.test.mjs b/packages/remark-lint/src/rules/__tests__/required-metadata.test.mjs
new file mode 100644
index 0000000000000..fe03986719d8a
--- /dev/null
+++ b/packages/remark-lint/src/rules/__tests__/required-metadata.test.mjs
@@ -0,0 +1,50 @@
+import { describe, it } from 'node:test';
+
+import dedent from 'dedent';
+
+import requiredMetadata from '../required-metadata.mjs';
+import { testRule } from './utils.mjs';
+
+const testCases = [
+ {
+ name: 'both metadata comments present',
+ input: dedent`
+
+
+ `,
+ expected: [],
+ },
+ {
+ name: 'missing introduced_in',
+ input: '',
+ expected: ['Missing "introduced_in" metadata'],
+ },
+ {
+ name: 'missing llm_description, but paragraph exists',
+ input: dedent`
+
+
+ This is a short description for LLMs.
+ `,
+ expected: [],
+ },
+ {
+ name: 'missing both metadata entries',
+ input: '',
+ expected: [
+ 'Missing "introduced_in" metadata',
+ 'Missing "llm_description" metadata',
+ ],
+ },
+ {
+ name: 'only paragraph, no comments at all',
+ input: 'This is just a paragraph, nothing else.',
+ expected: ['Missing "introduced_in" metadata'],
+ },
+];
+
+describe('duplicate-stability-nodes', () => {
+ for (const { name, input, expected } of testCases) {
+ it(name, () => testRule(requiredMetadata, input, expected));
+ }
+});
diff --git a/packages/remark-lint/src/rules/__tests__/utils.mjs b/packages/remark-lint/src/rules/__tests__/utils.mjs
new file mode 100644
index 0000000000000..ab0e373bac801
--- /dev/null
+++ b/packages/remark-lint/src/rules/__tests__/utils.mjs
@@ -0,0 +1,46 @@
+import assert from 'node:assert/strict';
+import { mock } from 'node:test';
+
+import remarkParse from 'remark-parse';
+import { unified } from 'unified';
+
+/**
+ * Tests a markdown rule against a markdown string
+ */
+export const testRule = (rule, markdown, expected, vfileOptions = {}) => {
+ // Parse the markdown once
+ const tree = unified().use(remarkParse).parse(markdown);
+
+ // Create a mock vfile
+ const vfile = {
+ ...vfileOptions,
+ message: mock.fn(),
+ messages: [],
+ };
+
+ // Execute the rule
+ rule()(tree, vfile, () => {});
+
+ // Assert that the expected messages were reported
+ assert.deepEqual(
+ vfile.message.mock.calls.map(call => call.arguments[0]),
+ expected
+ );
+};
+
+/**
+ * Tests a YAML rule against a YAML string
+ */
+export function testYamlRule(rule, input, options = {}, expected) {
+ // Create a mock reporter
+ const report = mock.fn();
+
+ // Execute the rule
+ rule(input, report, options);
+
+ // Assert that the expected messages were reported
+ assert.deepEqual(
+ report.mock.calls.flatMap(call => call.arguments),
+ expected
+ );
+}
diff --git a/packages/remark-lint/src/rules/__tests__/yaml/ordered-yaml-keys.test.mjs b/packages/remark-lint/src/rules/__tests__/yaml/ordered-yaml-keys.test.mjs
new file mode 100644
index 0000000000000..44d7d131f708a
--- /dev/null
+++ b/packages/remark-lint/src/rules/__tests__/yaml/ordered-yaml-keys.test.mjs
@@ -0,0 +1,62 @@
+import { describe, it } from 'node:test';
+
+import validateKeys from '../../yaml/ordered-yaml-keys.mjs';
+import { testYamlRule } from '../utils.mjs';
+
+const testCases = [
+ {
+ name: 'does not report when keys are valid and in correct order',
+ input: {
+ added: 'v1.0.0',
+ napiVersion: 3,
+ deprecated: 'v2.0.0',
+ removed: 'v3.0.0',
+ changes: [],
+ },
+ expected: [],
+ },
+ {
+ name: 'reports invalid keys',
+ input: {
+ added: 'v1.0.0',
+ unexpected: true,
+ oops: false,
+ },
+ expected: ['Invalid key(s) found: unexpected, oops'],
+ },
+ {
+ name: 'reports out-of-order keys',
+ input: {
+ removed: 'v3.0.0',
+ added: 'v1.0.0',
+ },
+ expected: [
+ 'Key "added" is out of order. Expected order: added, napiVersion, deprecated, removed, changes',
+ ],
+ },
+ {
+ name: 'reports both invalid and out-of-order keys (first invalid)',
+ input: {
+ foo: true,
+ deprecated: 'v2.0.0',
+ added: 'v1.0.0',
+ },
+ expected: [
+ 'Invalid key(s) found: foo',
+ 'Key "added" is out of order. Expected order: added, napiVersion, deprecated, removed, changes',
+ ],
+ },
+ {
+ name: 'does not report on empty object',
+ input: {},
+ expected: [],
+ },
+];
+
+describe('ordered-yaml-keys', () => {
+ for (const { name, input, options, expected } of testCases) {
+ it(name, () => {
+ testYamlRule(validateKeys, input, options, expected);
+ });
+ }
+});
diff --git a/packages/remark-lint/src/rules/__tests__/yaml/validate-changes.test.mjs b/packages/remark-lint/src/rules/__tests__/yaml/validate-changes.test.mjs
new file mode 100644
index 0000000000000..03d7b690c4ef3
--- /dev/null
+++ b/packages/remark-lint/src/rules/__tests__/yaml/validate-changes.test.mjs
@@ -0,0 +1,147 @@
+import { describe, it } from 'node:test';
+
+import validateChanges from '../../yaml/validate-changes.mjs';
+import { testYamlRule } from '../utils.mjs';
+
+const testCases = [
+ {
+ name: 'valid change entry',
+ input: {
+ changes: [
+ {
+ version: 'v2.0.0',
+ 'pr-url': 'https://github.com/nodejs/node/pull/12345',
+ description: 'This is a valid change.',
+ },
+ ],
+ },
+ expected: [],
+ },
+ {
+ name: '"changes" is not an array',
+ input: {
+ changes: {},
+ },
+ expected: ['"changes" must be an Array'],
+ },
+ {
+ name: 'invalid PR URL',
+ input: {
+ changes: [
+ {
+ version: 'v1.0.0',
+ 'pr-url': 'ftp://example.com/invalid',
+ description: 'Something happened.',
+ },
+ ],
+ },
+ expected: [
+ 'In "changes[0]": "ftp://example.com/invalid" is not a valid PR URL.',
+ ],
+ },
+ {
+ name: 'invalid key in change',
+ input: {
+ changes: [
+ {
+ version: 'v1.0.0',
+ 'pr-url': 'https://github.com/nodejs/node/pull/123',
+ description: 'Change made.',
+ extra: true,
+ },
+ ],
+ },
+ expected: ['In "changes[0]": Invalid key(s) found: extra'],
+ },
+ {
+ name: 'invalid version in change',
+ input: {
+ changes: [
+ {
+ version: 'foo',
+ 'pr-url': 'https://github.com/nodejs/node/pull/123',
+ description: 'Change made.',
+ },
+ ],
+ },
+ expected: ['In "changes[0].version": foo is invalid'],
+ },
+ {
+ name: 'unsorted versions in change',
+ input: {
+ changes: [
+ {
+ version: ['v1.0.0', 'v2.0.0'],
+ 'pr-url': 'https://github.com/nodejs/node/pull/123',
+ description: 'Out of order.',
+ },
+ ],
+ },
+ expected: [
+ 'In "changes[0].version": Versions are unsorted (should be in descending order)',
+ ],
+ },
+ {
+ name: 'empty description',
+ input: {
+ changes: [
+ {
+ version: 'v1.0.0',
+ 'pr-url': 'https://github.com/nodejs/node/pull/123',
+ description: '',
+ },
+ ],
+ },
+ expected: ['In "changes[0]": Description cannot be empty'],
+ },
+ {
+ name: 'description missing period',
+ input: {
+ changes: [
+ {
+ version: 'v1.0.0',
+ 'pr-url': 'https://github.com/nodejs/node/pull/123',
+ description: 'Missing period',
+ },
+ ],
+ },
+ expected: ['In "changes[0]": Description must end with a "."'],
+ },
+ {
+ name: 'valid security-related change (private PR, valid commit)',
+ input: {
+ changes: [
+ {
+ version: 'v1.0.0',
+ 'pr-url': 'https://github.com/nodejs-private/node-private/pull/999',
+ // A valid commit _must_ be forty characters
+ commit: '0'.repeat(40),
+ description: 'Security fix.',
+ },
+ ],
+ },
+ expected: [],
+ },
+ {
+ name: 'security-related change with invalid commit',
+ input: {
+ changes: [
+ {
+ version: 'v1.0.0',
+ 'pr-url': 'https://github.com/nodejs-private/node-private/pull/999',
+ commit: 'invalid_commit',
+ description: 'Security fix.',
+ },
+ ],
+ },
+ expected: ['In "changes[0]": Invalid commit: "invalid_commit"'],
+ },
+];
+
+describe('validate-changes', () => {
+ for (const { name, input, options, expected } of testCases) {
+ it(name, () => {
+ testYamlRule(validateChanges, input, options, expected);
+ });
+ }
+});
diff --git a/packages/remark-lint/src/rules/__tests__/yaml/validate-versions.test.mjs b/packages/remark-lint/src/rules/__tests__/yaml/validate-versions.test.mjs
new file mode 100644
index 0000000000000..76718be916166
--- /dev/null
+++ b/packages/remark-lint/src/rules/__tests__/yaml/validate-versions.test.mjs
@@ -0,0 +1,77 @@
+import { describe, it } from 'node:test';
+
+import validateVersionRule from '../../yaml/validate-versions.mjs';
+import { testYamlRule } from '../utils.mjs';
+
+const testCases = [
+ {
+ name: 'does not report on valid, sorted versions',
+ input: {
+ added: ['v2.0.0', 'v1.0.0'],
+ removed: 'v3.0.0',
+ },
+ expected: [],
+ },
+ {
+ name: 'reports invalid version format',
+ input: {
+ added: 'foo',
+ },
+ expected: ['In "added": foo is invalid'],
+ },
+ {
+ name: 'reports unsorted versions',
+ input: {
+ deprecated: ['v1.0.0', 'v2.0.0'],
+ },
+ expected: [
+ 'In "deprecated": Versions are unsorted (should be in descending order)',
+ ],
+ },
+ {
+ name: 'handles REPLACEME correctly when alone',
+ input: {
+ added: 'REPLACEME',
+ },
+ expected: [],
+ },
+ {
+ name: 'reports REPLACEME when part of multi-entry array',
+ input: {
+ removed: ['REPLACEME', 'v1.0.0'],
+ },
+ expected: ['In "removed": REPLACEME is invalid'],
+ },
+ {
+ name: 'ignores ancient hardcoded versions (e.g. 0.1.0)',
+ input: {
+ added: 'v0.1.0',
+ },
+ expected: [],
+ },
+ {
+ name: 'supports specifying custom versions',
+ input: {
+ added: 'vCUSTOM',
+ },
+ options: {
+ releasedVersions: 'CUSTOM',
+ },
+ expected: [],
+ },
+ {
+ name: 'does not report when key is missing',
+ input: {
+ napiVersion: 4,
+ },
+ expected: [],
+ },
+];
+
+describe('validate-version', () => {
+ for (const { name, input, options, expected } of testCases) {
+ it(name, () => {
+ testYamlRule(validateVersionRule, input, options, expected);
+ });
+ }
+});
diff --git a/packages/remark-lint/src/rules/duplicate-stability-nodes.mjs b/packages/remark-lint/src/rules/duplicate-stability-nodes.mjs
new file mode 100644
index 0000000000000..0f41b039d145d
--- /dev/null
+++ b/packages/remark-lint/src/rules/duplicate-stability-nodes.mjs
@@ -0,0 +1,57 @@
+import { lintRule } from 'unified-lint-rule';
+import { visit } from 'unist-util-visit';
+
+// TODO(@avivkeller): This is re-used from doc-kit
+// Regex to match "Stability: " in blockquotes
+const STABILITY = /Stability: ([0-5](?:\.[0-3])?)/;
+
+/**
+ * Finds and reports duplicate stability nodes
+ * @type {import('unified-lint-rule').Rule}
+ */
+const duplicateStabilityNodes = (tree, vfile) => {
+ let currentDepth = 0;
+ let currentStability = -1;
+ let currentHeaderDepth = 0;
+
+ visit(tree, node => {
+ // Update the current heading depth whenever a heading node is encountered
+ if (node.type === 'heading') {
+ currentHeaderDepth = node.depth;
+ }
+
+ // Look for blockquotes which may contain stability indicators
+ if (node.type === 'blockquote') {
+ // Assume the first child is a paragraph
+ const paragraph = node.children?.[0];
+ // And the first child of that paragraph is text
+ const text = paragraph?.children?.[0];
+
+ // Ensure structure is paragraph > text
+ if (paragraph?.type === 'paragraph' && text?.type === 'text') {
+ // Try to match "Stability: X"
+ const match = text.value.match(STABILITY);
+ if (match) {
+ const stability = parseFloat(match[1]);
+ // If the heading got deeper, and stability is valid and matches previous, report a duplicate
+ if (
+ currentHeaderDepth > currentDepth &&
+ stability >= 0 &&
+ stability === currentStability
+ ) {
+ vfile.message('Duplicate stability node', node);
+ } else {
+ // Otherwise, record this stability and heading depth
+ currentDepth = currentHeaderDepth;
+ currentStability = stability;
+ }
+ }
+ }
+ }
+ });
+};
+
+export default lintRule(
+ 'node-core:duplicate-stability-nodes',
+ duplicateStabilityNodes
+);
diff --git a/packages/remark-lint/src/rules/hashed-self-reference.mjs b/packages/remark-lint/src/rules/hashed-self-reference.mjs
new file mode 100644
index 0000000000000..789c41cab3dd4
--- /dev/null
+++ b/packages/remark-lint/src/rules/hashed-self-reference.mjs
@@ -0,0 +1,45 @@
+import path from 'node:path';
+import { pathToFileURL } from 'node:url';
+
+import { lintRule } from 'unified-lint-rule';
+
+const getLinksRecursively = function* (node) {
+ if (node.url) {
+ yield node;
+ }
+
+ const { children = [] } = node;
+
+ for (const child of children) {
+ yield* getLinksRecursively(child);
+ }
+};
+
+/**
+ * Ensures that all self-references begin with `#`
+ * @type {import('unified-lint-rule').Rule}
+ */
+const hashedSelfReference = (tree, vfile) => {
+ const currentFileURL = pathToFileURL(
+ path.isAbsolute(vfile.path) ? vfile.path : path.join(vfile.cwd, vfile.path)
+ );
+
+ for (const node of getLinksRecursively(tree)) {
+ const { url } = node;
+
+ if (!url || url[0] === '#') continue;
+
+ const targetURL = new URL(url, currentFileURL);
+
+ if (targetURL.pathname === currentFileURL.pathname) {
+ const expected = url.includes('#') ? url.slice(url.indexOf('#')) : '#';
+
+ vfile.message(
+ `Self-reference must start with hash (expected "${expected}", got "${url}")`,
+ node
+ );
+ }
+ }
+};
+
+export default lintRule('node-core:hashed-self-reference', hashedSelfReference);
diff --git a/packages/remark-lint/src/rules/ordered-references.mjs b/packages/remark-lint/src/rules/ordered-references.mjs
new file mode 100644
index 0000000000000..a6366fbdb93f2
--- /dev/null
+++ b/packages/remark-lint/src/rules/ordered-references.mjs
@@ -0,0 +1,36 @@
+import { lintRule } from 'unified-lint-rule';
+
+const getDefinitionsRecursively = function* (node) {
+ if (node.type === 'definition') {
+ yield node;
+ }
+
+ const { children = [] } = node;
+
+ for (const child of children) {
+ yield* getDefinitionsRecursively(child);
+ }
+};
+
+/**
+ * Ensures references are alphabetical.
+ * @type {import('unified-lint-rule').Rule}
+ */
+const orderedReferences = (tree, vfile) => {
+ let previousLabel;
+
+ for (const node of getDefinitionsRecursively(tree)) {
+ const { label } = node;
+
+ if (previousLabel && previousLabel > label) {
+ vfile.message(
+ `Unordered reference ("${label}" should be before "${previousLabel}")`,
+ node
+ );
+ }
+
+ previousLabel = label;
+ }
+};
+
+export default lintRule('node-core:ordered-references', orderedReferences);
diff --git a/packages/remark-lint/src/rules/required-metadata.mjs b/packages/remark-lint/src/rules/required-metadata.mjs
new file mode 100644
index 0000000000000..218a44881ec38
--- /dev/null
+++ b/packages/remark-lint/src/rules/required-metadata.mjs
@@ -0,0 +1,46 @@
+import { lintRule } from 'unified-lint-rule';
+import { visit } from 'unist-util-visit';
+
+// Define the required metadata keys that must be present in the Markdown content
+const REQUIRED_KEYS = ['introduced_in', 'llm_description'];
+const METADATA_CHECKS = REQUIRED_KEYS.map(
+ key => new RegExp(``)
+);
+
+/**
+ * Ensures all needed metadata exists
+ * @type {import('unified-lint-rule').Rule}
+ */
+const hasRequiredMetadata = (tree, vfile) => {
+ const foundKeys = new Set();
+ let hasParagraph = false;
+
+ visit(tree, ['html', 'paragraph'], node => {
+ if (node.type === 'html') {
+ // Check if the HTML node contains any of the required metadata comments
+ METADATA_CHECKS.forEach((regex, i) => {
+ if (regex.test(node.value)) {
+ foundKeys.add(REQUIRED_KEYS[i]);
+ }
+ });
+ } else {
+ // Mark that a paragraph exists in the document
+ hasParagraph = true;
+ }
+ });
+
+ REQUIRED_KEYS.forEach(key => {
+ if (foundKeys.has(key)) {
+ return;
+ }
+
+ // Allow llm_description to be provided as a first paragraph
+ if (key === 'llm_description' && hasParagraph) {
+ return;
+ }
+
+ vfile.message(`Missing "${key}" metadata`, tree);
+ });
+};
+
+export default lintRule('node-core:required-metadata', hasRequiredMetadata);
diff --git a/packages/remark-lint/src/rules/yaml/index.mjs b/packages/remark-lint/src/rules/yaml/index.mjs
new file mode 100644
index 0000000000000..b1ff0884dcde3
--- /dev/null
+++ b/packages/remark-lint/src/rules/yaml/index.mjs
@@ -0,0 +1,55 @@
+import { lintRule } from 'unified-lint-rule';
+import { visit } from 'unist-util-visit';
+import { parse } from 'yaml';
+
+import orderedYamlKeys from './ordered-yaml-keys.mjs';
+import validateChanges from './validate-changes.mjs';
+import validateVersions from './validate-versions.mjs';
+
+const YAML_HTML_COMMENT_RE = /^';
+const RULES = [orderedYamlKeys, validateVersions, validateChanges];
+
+/**
+ * @callback YAMLRule
+ * @param {Record} yaml - The YAML object to validate.
+ * @param {(message: string) => void} report - Reporting function
+ * @param {import('../../api.mjs').Options} options - The options.
+ */
+
+/**
+ * Determine if a node is a YAML-bearing HTML comment.
+ * @param {import('unist').Node} node
+ */
+const isYamlHtmlComment = node =>
+ node.type === 'html' && YAML_HTML_COMMENT_RE.test(node.value);
+
+/**
+ * Lints YAML embedded inside HTML comments in Markdown AST.
+ * @type {import('unified-lint-rule').Rule}
+ */
+const yamlComments = (tree, vfile, options) => {
+ visit(tree, isYamlHtmlComment, node => {
+ const trimmed = node.value.trim();
+
+ // Consistency check for ""', node);
+ return;
+ }
+
+ // "#" comments out the first line (""
+ const parsed = parse(`#${trimmed.slice(0, -HTML_COMMENT_CLOSE.length)}`);
+
+ const report = (...args) => vfile.message(...args, node);
+
+ RULES.forEach(rule => rule(parsed, report, options));
+ });
+};
+
+export default lintRule('node-core:yaml-comments', yamlComments);
diff --git a/packages/remark-lint/src/rules/yaml/ordered-yaml-keys.mjs b/packages/remark-lint/src/rules/yaml/ordered-yaml-keys.mjs
new file mode 100644
index 0000000000000..9126a4b0959df
--- /dev/null
+++ b/packages/remark-lint/src/rules/yaml/ordered-yaml-keys.mjs
@@ -0,0 +1,58 @@
+/**
+ * Default allowed keys and their required order at the top level.
+ * Order matters for validation.
+ */
+export const DEFAULT_VALID_KEYS = [
+ 'added',
+ 'napiVersion',
+ 'deprecated',
+ 'removed',
+ 'changes',
+];
+
+/**
+ * Validate that:
+ * - Only valid keys are present
+ * - Keys appear in the expected order (relative order respected)
+ *
+ * @type {import('./index.mjs').YAMLRule}
+ * @param {readonly string[]} [validKeys=DEFAULT_VALID_KEYS] - Allowed keys in the expected order.
+ * @param {string} [prefix=''] - Message prefix for context.
+ */
+export default function orderedYamlKeys(
+ yaml,
+ report,
+ _,
+ validKeys = DEFAULT_VALID_KEYS,
+ prefix = ''
+) {
+ if (!yaml || typeof yaml !== 'object' || Array.isArray(yaml)) return;
+
+ const keys = Object.keys(yaml);
+
+ // Check for invalid keys
+ const invalidKeys = keys.filter(key => !validKeys.includes(key));
+ if (invalidKeys.length > 0) {
+ report(`${prefix}Invalid key(s) found: ${invalidKeys.join(', ')}`);
+ }
+
+ // Check key order
+ let lastIndex = -1;
+ for (const key of keys) {
+ const index = validKeys.indexOf(key);
+
+ if (index === -1) {
+ // Non-validated keys are ignored for ordering, since
+ // they were already reported as invalid above
+ continue;
+ }
+
+ if (index < lastIndex) {
+ report(
+ `${prefix}Key "${key}" is out of order. Expected order: ${validKeys.join(', ')}`
+ );
+ break;
+ }
+ lastIndex = index;
+ }
+}
diff --git a/packages/remark-lint/src/rules/yaml/validate-changes.mjs b/packages/remark-lint/src/rules/yaml/validate-changes.mjs
new file mode 100644
index 0000000000000..c22215ac72b5e
--- /dev/null
+++ b/packages/remark-lint/src/rules/yaml/validate-changes.mjs
@@ -0,0 +1,93 @@
+import orderedYamlKeys from './ordered-yaml-keys.mjs';
+import { validateVersion } from './validate-versions.mjs';
+
+const CHANGE_VALID_KEYS = ['version', 'pr-url', 'description'];
+const VALID_PR_URL_RE =
+ /^https:\/\/github\.com\/nodejs(?:-private)?\/node(?:-private)?\/pull\/\d+$/;
+const PRIVATE_PR_STARTER =
+ 'https://github.com/nodejs-private/node-private/pull/';
+const COMMIT_SHA_RE = /^[0-9a-f]{40}$/i;
+
+/**
+ * A change is security-related if it references a PR in the private Node.js repo,
+ * with a valid commit.
+ * @param {Record} change
+ * @returns {boolean}
+ */
+const isSecurityRelated = change =>
+ typeof change?.['pr-url'] === 'string' &&
+ change['pr-url'].startsWith(PRIVATE_PR_STARTER) &&
+ typeof change?.['commit'] === 'string';
+
+/**
+ * Anything below v1.0 is older than this format.
+ * @param {Record} change
+ * @returns {boolean}
+ */
+const isAncient = change =>
+ typeof change?.version === 'string' && change.version.startsWith('v0.');
+
+/**
+ * Validate the "changes" array within the YAML object.
+ * @type {import('./index.mjs').YAMLRule}
+ */
+export default function validateChanges({ changes }, report, options) {
+ if (changes === undefined) {
+ // Nothing to validate
+ return;
+ }
+
+ if (!Array.isArray(changes)) {
+ report('"changes" must be an Array');
+ return;
+ }
+
+ changes.forEach((change, index) => {
+ const prefix = `In "changes[${index}]": `;
+
+ if (!change || typeof change !== 'object' || Array.isArray(change)) {
+ report(`${prefix}Item must be an object`);
+ }
+
+ // Security-related validations
+ if (isSecurityRelated(change)) {
+ const commit = change.commit;
+
+ if (!COMMIT_SHA_RE.test(commit)) {
+ report(`${prefix}Invalid commit: "${commit}"`);
+ }
+
+ // Remove the "commit" key so we can validate keys like normal.
+ delete change.commit;
+ }
+
+ // For non-ancient entries, validate PR URL, keys, and description presence
+ if (!isAncient(change)) {
+ const prUrl = change['pr-url'];
+
+ if (!VALID_PR_URL_RE.test(prUrl)) {
+ report(`${prefix}"${prUrl}" is not a valid PR URL.`);
+ }
+
+ // Key validation
+ orderedYamlKeys(change, report, options, CHANGE_VALID_KEYS, prefix);
+ }
+
+ // Version validation
+ validateVersion(
+ change.version,
+ report,
+ options,
+ `changes[${index}].version`
+ );
+
+ // Description validation
+ if (typeof change.description !== 'string') {
+ report(`${prefix}Description must be a string`);
+ } else if (change.description.trim().length === 0) {
+ report(`${prefix}Description cannot be empty`);
+ } else if (!change.description.endsWith('.')) {
+ report(`${prefix}Description must end with a "."`);
+ }
+ });
+}
diff --git a/packages/remark-lint/src/rules/yaml/validate-versions.mjs b/packages/remark-lint/src/rules/yaml/validate-versions.mjs
new file mode 100644
index 0000000000000..d4dbb2cf8d9fa
--- /dev/null
+++ b/packages/remark-lint/src/rules/yaml/validate-versions.mjs
@@ -0,0 +1,100 @@
+import { valid, parse, gt } from 'semver';
+
+const MAX_SAFE_SEMVER = parse(
+ `${Number.MAX_SAFE_INTEGER}.${Number.MAX_SAFE_INTEGER}.${Number.MAX_SAFE_INTEGER}`
+);
+const VERSION_KEYS = ['added', 'removed', 'deprecated'];
+
+/**
+ * Checks if a version is a placeholder that should be ignored in validation
+ * @param {string} version - The version string to check
+ * @param {number} totalVersions - Total number of versions in the array
+ */
+const isPlaceholder = (version, totalVersions) =>
+ version === 'REPLACEME' && totalVersions === 1;
+
+/**
+ * Checks if a version should be ignored in validation (e.g., very old versions)
+ * @param {string} version - The version string to check
+ */
+const isIgnoredVersion = version => {
+ const parsed = parse(version);
+ return parsed?.major === 0 && parsed.minor < 2;
+};
+
+/**
+ * Determines if a version string is valid according to project rules
+ * @param {string} version - The version string to validate
+ * @param {number} totalVersions - Total number of versions in the array
+ * @param {Array} releasedVersions - The released versions
+ */
+const isValidVersion = (version, totalVersions, releasedVersions) => {
+ // Special cases that bypass normal validation
+ if (isPlaceholder(version, totalVersions) || isIgnoredVersion(version)) {
+ return true;
+ }
+
+ // Check against known released versions if available
+ if (releasedVersions.length > 0) {
+ return releasedVersions.includes(version.replace(/^v/, ''));
+ }
+
+ // Fall back to semver validation
+ return Boolean(valid(version));
+};
+
+/**
+ * Converts a version string to a comparable object
+ * @param {string} version - The version string to convert
+ */
+const getComparableVersion = version =>
+ version === 'REPLACEME' ? MAX_SAFE_SEMVER : parse(version);
+
+/**
+ * Validates a single version field in the YAML
+ * @type {import('./index.mjs').YAMLRule}
+ * @param {string} key
+ */
+export const validateVersion = (
+ input,
+ report,
+ { releasedVersions = [] },
+ key
+) => {
+ const versions = Array.isArray(input) ? input : [input];
+ const totalVersions = versions.length;
+
+ // Validate each version individually
+ versions.forEach(version => {
+ if (!isValidVersion(version, totalVersions, releasedVersions)) {
+ report(`In "${key}": ${version} is invalid`);
+ }
+ });
+
+ // Check if versions are sorted in descending order
+ for (let i = 1; i < totalVersions; i++) {
+ const prev = getComparableVersion(versions[i - 1]);
+ const curr = getComparableVersion(versions[i]);
+
+ if (gt(curr, prev)) {
+ report(
+ `In "${key}": Versions are unsorted (should be in descending order)`
+ );
+ break;
+ }
+ }
+};
+
+/**
+ * Validates version fields in a YAML document
+ * @type {import('./index.mjs').YAMLRule}
+ */
+const validateVersions = (yaml, report, options) => {
+ VERSION_KEYS.forEach(key => {
+ if (yaml[key]) {
+ validateVersion(yaml[key], report, options, key);
+ }
+ });
+};
+
+export default validateVersions;
diff --git a/packages/remark-lint/turbo.json b/packages/remark-lint/turbo.json
new file mode 100644
index 0000000000000..7a34fa4ca9091
--- /dev/null
+++ b/packages/remark-lint/turbo.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "extends": ["//"],
+ "tasks": {
+ "lint:js": {
+ "inputs": ["src/**/*.mjs"]
+ },
+ "lint:fix": {
+ "cache": false
+ },
+ "test:unit": {
+ "inputs": ["src/**/*.mjs"]
+ }
+ }
+}
diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json
index 21eb5e99bcbe0..4267399460f16 100644
--- a/packages/ui-components/package.json
+++ b/packages/ui-components/package.json
@@ -10,6 +10,11 @@
"./src/*/index.ts"
]
},
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/nodejs/nodejs.org",
+ "directory": "packages/ui-components"
+ },
"scripts": {
"compile:ts": "tsc",
"compile:css": "postcss --dir dist --base src \"src/**/*.module.css\" src/styles/index.css",
diff --git a/packages/ui-components/scripts/publish.mjs b/packages/ui-components/scripts/publish.mjs
index 5f75ef47febaa..095b0d07ce48c 100644
--- a/packages/ui-components/scripts/publish.mjs
+++ b/packages/ui-components/scripts/publish.mjs
@@ -18,7 +18,13 @@ await writeFile(
);
// Now, publish the generated `dist` folder
-spawnSync('pnpm', ['publish', '--no-git-checks'], {
+const { status, error } = spawnSync('pnpm', ['publish', '--no-git-checks'], {
cwd: 'dist',
stdio: 'inherit',
});
+
+if (error) {
+ throw error;
+}
+
+process.exitCode = status;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d5177123f1b73..e7634528d75b1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -128,13 +128,13 @@ importers:
version: 0.1.0
'@vercel/analytics':
specifier: ~1.5.0
- version: 1.5.0(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)
+ version: 1.5.0(next@15.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)
'@vercel/otel':
specifier: ~1.13.0
version: 1.13.0(@opentelemetry/api-logs@0.203.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))
'@vercel/speed-insights':
specifier: ~1.2.0
- version: 1.2.0(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)
+ version: 1.2.0(next@15.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)
classnames:
specifier: 'catalog:'
version: 2.5.1
@@ -151,17 +151,17 @@ importers:
specifier: ~4.0.3
version: 4.0.3
next:
- specifier: 15.4.4
- version: 15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ specifier: 15.5.0
+ version: 15.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
next-intl:
specifier: ~4.3.4
- version: 4.3.4(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.8.3)
+ version: 4.3.4(next@15.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.8.3)
next-themes:
specifier: ~0.4.6
version: 0.4.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
postcss-calc:
specifier: ~10.1.1
- version: 10.1.1(postcss@8.5.6)
+ version: 10.1.1(postcss@8.5.3)
react:
specifier: 'catalog:'
version: 19.1.1
@@ -209,8 +209,11 @@ importers:
specifier: ^0.0.1
version: 0.0.1
'@next/eslint-plugin-next':
- specifier: 15.4.4
- version: 15.4.4
+ specifier: 15.5.0
+ version: 15.5.0
+ '@node-core/remark-lint':
+ specifier: workspace:*
+ version: link:../../packages/remark-lint
'@opennextjs/cloudflare':
specifier: ^1.6.4
version: 1.6.4(wrangler@4.26.1)
@@ -227,8 +230,8 @@ importers:
specifier: ~7.7.0
version: 7.7.0
eslint-config-next:
- specifier: 15.4.4
- version: 15.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.32.0(jiti@2.4.2)))(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3)
+ specifier: 15.5.0
+ version: 15.5.0(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.32.0(jiti@2.4.2)))(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3)
eslint-import-resolver-typescript:
specifier: ~4.4.4
version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.32.0(jiti@2.4.2)))(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.4.2))
@@ -251,38 +254,8 @@ importers:
specifier: ^26.0.0
version: 26.1.0
remark-frontmatter:
- specifier: 5.0.0
+ specifier: ^5.0.0
version: 5.0.0
- remark-lint-fenced-code-flag:
- specifier: ^4.2.0
- version: 4.2.0
- remark-lint-first-heading-level:
- specifier: ^4.0.1
- version: 4.0.1
- remark-lint-maximum-line-length:
- specifier: ^4.1.1
- version: 4.1.1
- remark-lint-no-file-name-articles:
- specifier: ^3.0.1
- version: 3.0.1
- remark-lint-no-literal-urls:
- specifier: ^4.0.1
- version: 4.0.1
- remark-lint-no-undefined-references:
- specifier: ^5.0.2
- version: 5.0.2
- remark-lint-no-unused-definitions:
- specifier: ^4.0.2
- version: 4.0.2
- remark-lint-prohibited-strings:
- specifier: ^4.0.0
- version: 4.0.0
- remark-lint-unordered-list-marker-style:
- specifier: ^4.0.1
- version: 4.0.1
- remark-preset-lint-node:
- specifier: 5.1.2
- version: 5.1.2
stylelint:
specifier: 16.23.0
version: 16.23.0(typescript@5.8.3)
@@ -351,6 +324,124 @@ importers:
specifier: 'catalog:'
version: 10.0.0
+ packages/remark-lint:
+ dependencies:
+ remark-gfm:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-blockquote-indentation:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-checkbox-character-style:
+ specifier: ^5.0.1
+ version: 5.0.1
+ remark-lint-checkbox-content-indent:
+ specifier: ^5.0.1
+ version: 5.0.1
+ remark-lint-code-block-style:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-definition-spacing:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-fenced-code-flag:
+ specifier: ^4.2.0
+ version: 4.2.0
+ remark-lint-fenced-code-marker:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-final-definition:
+ specifier: ^4.0.2
+ version: 4.0.2
+ remark-lint-heading-style:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-maximum-line-length:
+ specifier: ^4.1.1
+ version: 4.1.1
+ remark-lint-no-consecutive-blank-lines:
+ specifier: ^5.0.1
+ version: 5.0.1
+ remark-lint-no-file-name-consecutive-dashes:
+ specifier: ^3.0.1
+ version: 3.0.1
+ remark-lint-no-file-name-outer-dashes:
+ specifier: ^3.0.1
+ version: 3.0.1
+ remark-lint-no-heading-indent:
+ specifier: ^5.0.1
+ version: 5.0.1
+ remark-lint-no-literal-urls:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-no-multiple-toplevel-headings:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-no-shell-dollars:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-no-table-indentation:
+ specifier: ^5.0.1
+ version: 5.0.1
+ remark-lint-no-tabs:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-no-trailing-spaces:
+ specifier: ^3.0.2
+ version: 3.0.2
+ remark-lint-no-unused-definitions:
+ specifier: ^4.0.2
+ version: 4.0.2
+ remark-lint-prohibited-strings:
+ specifier: ^4.0.0
+ version: 4.0.0
+ remark-lint-rule-style:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-strong-marker:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-lint-table-cell-padding:
+ specifier: ^5.1.1
+ version: 5.1.1
+ remark-lint-table-pipes:
+ specifier: ^5.0.1
+ version: 5.0.1
+ remark-lint-unordered-list-marker-style:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-preset-lint-recommended:
+ specifier: ^7.0.1
+ version: 7.0.1
+ semver:
+ specifier: ^7.7.2
+ version: 7.7.2
+ unified-lint-rule:
+ specifier: ^3.0.1
+ version: 3.0.1
+ unist-util-visit:
+ specifier: ^5.0.0
+ version: 5.0.0
+ yaml:
+ specifier: ^2.8.1
+ version: 2.8.1
+ devDependencies:
+ cross-env:
+ specifier: 'catalog:'
+ version: 10.0.0
+ dedent:
+ specifier: ^1.6.0
+ version: 1.6.0
+ globals:
+ specifier: ^16.3.0
+ version: 16.3.0
+ remark-parse:
+ specifier: ^11.0.0
+ version: 11.0.0
+ unified:
+ specifier: ^11.0.5
+ version: 11.0.5
+
packages/ui-components:
dependencies:
'@heroicons/react':
@@ -1890,56 +1981,56 @@ packages:
'@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
- '@next/env@15.4.4':
- resolution: {integrity: sha512-SJKOOkULKENyHSYXE5+KiFU6itcIb6wSBjgM92meK0HVKpo94dNOLZVdLLuS7/BxImROkGoPsjR4EnuDucqiiA==}
+ '@next/env@15.5.0':
+ resolution: {integrity: sha512-sDaprBAfzCQiOgo2pO+LhnV0Wt2wBgartjrr+dpcTORYVnnXD0gwhHhiiyIih9hQbq+JnbqH4odgcFWhqCGidw==}
- '@next/eslint-plugin-next@15.4.4':
- resolution: {integrity: sha512-1FDsyN//ai3Jd97SEd7scw5h1yLdzDACGOPRofr2GD3sEFsBylEEoL0MHSerd4n2dq9Zm/mFMqi4+NRMOreOKA==}
+ '@next/eslint-plugin-next@15.5.0':
+ resolution: {integrity: sha512-+k83U/fST66eQBjTltX2T9qUYd43ntAe+NZ5qeZVTQyTiFiHvTLtkpLKug4AnZAtuI/lwz5tl/4QDJymjVkybg==}
- '@next/swc-darwin-arm64@15.4.4':
- resolution: {integrity: sha512-eVG55dnGwfUuG+TtnUCt+mEJ+8TGgul6nHEvdb8HEH7dmJIFYOCApAaFrIrxwtEq2Cdf+0m5sG1Np8cNpw9EAw==}
+ '@next/swc-darwin-arm64@15.5.0':
+ resolution: {integrity: sha512-v7Jj9iqC6enxIRBIScD/o0lH7QKvSxq2LM8UTyqJi+S2w2QzhMYjven4vgu/RzgsdtdbpkyCxBTzHl/gN5rTRg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
- '@next/swc-darwin-x64@15.4.4':
- resolution: {integrity: sha512-zqG+/8apsu49CltEj4NAmCGZvHcZbOOOsNoTVeIXphYWIbE4l6A/vuQHyqll0flU2o3dmYCXsBW5FmbrGDgljQ==}
+ '@next/swc-darwin-x64@15.5.0':
+ resolution: {integrity: sha512-s2Nk6ec+pmYmAb/utawuURy7uvyYKDk+TRE5aqLRsdnj3AhwC9IKUBmhfnLmY/+P+DnwqpeXEFIKe9tlG0p6CA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
- '@next/swc-linux-arm64-gnu@15.4.4':
- resolution: {integrity: sha512-LRD4l2lq4R+2QCHBQVC0wjxxkLlALGJCwigaJ5FSRSqnje+MRKHljQNZgDCaKUZQzO/TXxlmUdkZP/X3KNGZaw==}
+ '@next/swc-linux-arm64-gnu@15.5.0':
+ resolution: {integrity: sha512-mGlPJMZReU4yP5fSHjOxiTYvZmwPSWn/eF/dcg21pwfmiUCKS1amFvf1F1RkLHPIMPfocxLViNWFvkvDB14Isg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@next/swc-linux-arm64-musl@15.4.4':
- resolution: {integrity: sha512-LsGUCTvuZ0690fFWerA4lnQvjkYg9gHo12A3wiPUR4kCxbx/d+SlwmonuTH2SWZI+RVGA9VL3N0S03WTYv6bYg==}
+ '@next/swc-linux-arm64-musl@15.5.0':
+ resolution: {integrity: sha512-biWqIOE17OW/6S34t1X8K/3vb1+svp5ji5QQT/IKR+VfM3B7GvlCwmz5XtlEan2ukOUf9tj2vJJBffaGH4fGRw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@next/swc-linux-x64-gnu@15.4.4':
- resolution: {integrity: sha512-aOy5yNRpLL3wNiJVkFYl6w22hdREERNjvegE6vvtix8LHRdsTHhWTpgvcYdCK7AIDCQW5ATmzr9XkPHvSoAnvg==}
+ '@next/swc-linux-x64-gnu@15.5.0':
+ resolution: {integrity: sha512-zPisT+obYypM/l6EZ0yRkK3LEuoZqHaSoYKj+5jiD9ESHwdr6QhnabnNxYkdy34uCigNlWIaCbjFmQ8FY5AlxA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@next/swc-linux-x64-musl@15.4.4':
- resolution: {integrity: sha512-FL7OAn4UkR8hKQRGBmlHiHinzOb07tsfARdGh7v0Z0jEJ3sz8/7L5bR23ble9E6DZMabSStqlATHlSxv1fuzAg==}
+ '@next/swc-linux-x64-musl@15.5.0':
+ resolution: {integrity: sha512-+t3+7GoU9IYmk+N+FHKBNFdahaReoAktdOpXHFIPOU1ixxtdge26NgQEEkJkCw2dHT9UwwK5zw4mAsURw4E8jA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@next/swc-win32-arm64-msvc@15.4.4':
- resolution: {integrity: sha512-eEdNW/TXwjYhOulQh0pffTMMItWVwKCQpbziSBmgBNFZIIRn2GTXrhrewevs8wP8KXWYMx8Z+mNU0X+AfvtrRg==}
+ '@next/swc-win32-arm64-msvc@15.5.0':
+ resolution: {integrity: sha512-d8MrXKh0A+c9DLiy1BUFwtg3Hu90Lucj3k6iKTUdPOv42Ve2UiIG8HYi3UAb8kFVluXxEfdpCoPPCSODk5fDcw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
- '@next/swc-win32-x64-msvc@15.4.4':
- resolution: {integrity: sha512-SE5pYNbn/xZKMy1RE3pAs+4xD32OI4rY6mzJa4XUkp/ItZY+OMjIgilskmErt8ls/fVJ+Ihopi2QIeW6O3TrMw==}
+ '@next/swc-win32-x64-msvc@15.5.0':
+ resolution: {integrity: sha512-Fe1tGHxOWEyQjmygWkkXSwhFcTJuimrNu52JEuwItrKJVV4iRjbWp9I7zZjwqtiNnQmxoEvoisn8wueFLrNpvQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -4427,6 +4518,14 @@ packages:
babel-plugin-macros:
optional: true
+ dedent@1.6.0:
+ resolution: {integrity: sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==}
+ peerDependencies:
+ babel-plugin-macros: ^3.1.0
+ peerDependenciesMeta:
+ babel-plugin-macros:
+ optional: true
+
deep-eql@5.0.2:
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
engines: {node: '>=6'}
@@ -4696,8 +4795,8 @@ packages:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
engines: {node: '>=12'}
- eslint-config-next@15.4.4:
- resolution: {integrity: sha512-sK/lWLUVF5om18O5w76Jt3F8uzu/LP5mVa6TprCMWkjWHUmByq80iHGHcdH7k1dLiJlj+DRIWf98d5piwRsSuA==}
+ eslint-config-next@15.5.0:
+ resolution: {integrity: sha512-Yl4hlOdBqstAuHnlBfx2RimBzWQwysM2SJNu5EzYVa2qS2ItPs7lgxL0sJJDudEx5ZZHfWPZ/6U8+FtDFWs7/w==}
peerDependencies:
eslint: ^7.23.0 || ^8.0.0 || ^9.0.0
typescript: '>=3.3.1'
@@ -5205,6 +5304,10 @@ packages:
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
engines: {node: '>=18'}
+ globals@16.3.0:
+ resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==}
+ engines: {node: '>=18'}
+
globalthis@1.0.4:
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
@@ -6280,8 +6383,8 @@ packages:
react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
- next@15.4.4:
- resolution: {integrity: sha512-kNcubvJjOL9yUOfwtZF3HfDhuhp+kVD+FM2A6Tyua1eI/xfmY4r/8ZS913MMz+oWKDlbps/dQOWdDricuIkXLw==}
+ next@15.5.0:
+ resolution: {integrity: sha512-N1lp9Hatw3a9XLt0307lGB4uTKsXDhyOKQo7uYMzX4i0nF/c27grcGXkLdb7VcT8QPYLBa8ouIyEoUQJ2OyeNQ==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true
peerDependencies:
@@ -7016,9 +7119,6 @@ packages:
remark-lint-final-newline@3.0.1:
resolution: {integrity: sha512-q5diKHD6BMbzqWqgvYPOB8AJgLrMzEMBAprNXjcpKoZ/uCRqly+gxjco+qVUMtMWSd+P+KXZZEqoa7Y6QiOudw==}
- remark-lint-first-heading-level@4.0.1:
- resolution: {integrity: sha512-ZqH476wQU2rk3L2X1Ef/FsdDZJsSkMqTkEjKyeac/hxnwDZ8ZLYYMmm4UKTgVZTtqFUkNYzgGEPAFXtrppHbJA==}
-
remark-lint-hard-break-spaces@4.1.1:
resolution: {integrity: sha512-AKDPDt39fvmr3yk38OKZEWJxxCOOUBE+96AsBfs+ExS5LW6oLa9041X5ahFDQHvHGzdoremEIaaElursaPEkNg==}
@@ -7043,9 +7143,6 @@ packages:
remark-lint-no-duplicate-definitions@4.0.1:
resolution: {integrity: sha512-Ek+A/xDkv5Nn+BXCFmf+uOrFSajCHj6CjhsHjtROgVUeEPj726yYekDBoDRA0Y3+z+U30AsJoHgf/9Jj1IFSug==}
- remark-lint-no-file-name-articles@3.0.1:
- resolution: {integrity: sha512-h31ZDDJV2T6g9WLBrXg1CJ1m8M170O/tlDPAEPGCa/rxwKvMcfum4yicaot0ZKbUZ1uEPjVSUPDeo3sU0zciCQ==}
-
remark-lint-no-file-name-consecutive-dashes@3.0.1:
resolution: {integrity: sha512-qGJRZ81sowEjv1dBodbHZ29pDZbrFpxiQQ6gBvkkHkkoYPekdnr8iUxmV38HcqH8+JNW1O4ELr+m71AA9/34Mw==}
@@ -7121,10 +7218,6 @@ packages:
remark-parse@11.0.0:
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
- remark-preset-lint-node@5.1.2:
- resolution: {integrity: sha512-ukBPfLqD05AomGL+Z3tbmBCKTaEM+9Dv8Pn0r/0vok8F95Z0wj/AY70cFhm038ID1vKBD07anky11dvigDAHlw==}
- engines: {node: '>=18.0.0'}
-
remark-preset-lint-recommended@7.0.1:
resolution: {integrity: sha512-j1CY5u48PtZl872BQ40uWSQMT3R4gXKp0FUgevMu5gW7hFMtvaCiDq+BfhzeR8XKKiW9nIMZGfIMZHostz5X4g==}
@@ -8237,6 +8330,11 @@ packages:
engines: {node: '>= 14.6'}
hasBin: true
+ yaml@2.8.1:
+ resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
@@ -10138,34 +10236,34 @@ snapshots:
'@tybys/wasm-util': 0.10.0
optional: true
- '@next/env@15.4.4': {}
+ '@next/env@15.5.0': {}
- '@next/eslint-plugin-next@15.4.4':
+ '@next/eslint-plugin-next@15.5.0':
dependencies:
fast-glob: 3.3.1
- '@next/swc-darwin-arm64@15.4.4':
+ '@next/swc-darwin-arm64@15.5.0':
optional: true
- '@next/swc-darwin-x64@15.4.4':
+ '@next/swc-darwin-x64@15.5.0':
optional: true
- '@next/swc-linux-arm64-gnu@15.4.4':
+ '@next/swc-linux-arm64-gnu@15.5.0':
optional: true
- '@next/swc-linux-arm64-musl@15.4.4':
+ '@next/swc-linux-arm64-musl@15.5.0':
optional: true
- '@next/swc-linux-x64-gnu@15.4.4':
+ '@next/swc-linux-x64-gnu@15.5.0':
optional: true
- '@next/swc-linux-x64-musl@15.4.4':
+ '@next/swc-linux-x64-musl@15.5.0':
optional: true
- '@next/swc-win32-arm64-msvc@15.4.4':
+ '@next/swc-win32-arm64-msvc@15.5.0':
optional: true
- '@next/swc-win32-x64-msvc@15.4.4':
+ '@next/swc-win32-x64-msvc@15.5.0':
optional: true
'@noble/ciphers@1.3.0': {}
@@ -10283,7 +10381,7 @@ snapshots:
express: 5.0.1
path-to-regexp: 6.3.0
urlpattern-polyfill: 10.1.0
- yaml: 2.8.0
+ yaml: 2.8.1
transitivePeerDependencies:
- aws-crt
- supports-color
@@ -12202,9 +12300,9 @@ snapshots:
mdast-util-to-string: 3.2.0
unist-util-visit: 4.1.2
- '@vercel/analytics@1.5.0(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)':
+ '@vercel/analytics@1.5.0(next@15.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)':
optionalDependencies:
- next: 15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ next: 15.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
react: 19.1.1
'@vercel/otel@1.13.0(@opentelemetry/api-logs@0.203.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))':
@@ -12217,9 +12315,9 @@ snapshots:
'@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0)
- '@vercel/speed-insights@1.2.0(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)':
+ '@vercel/speed-insights@1.2.0(next@15.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)':
optionalDependencies:
- next: 15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ next: 15.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
react: 19.1.1
'@vitest/expect@3.2.4':
@@ -12912,6 +13010,8 @@ snapshots:
dedent@1.5.3: {}
+ dedent@1.6.0: {}
+
deep-eql@5.0.2: {}
deep-is@0.1.4: {}
@@ -13303,9 +13403,9 @@ snapshots:
escape-string-regexp@5.0.0: {}
- eslint-config-next@15.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.32.0(jiti@2.4.2)))(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3):
+ eslint-config-next@15.5.0(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.32.0(jiti@2.4.2)))(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3):
dependencies:
- '@next/eslint-plugin-next': 15.4.4
+ '@next/eslint-plugin-next': 15.5.0
'@rushstack/eslint-patch': 1.12.0
'@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3)
@@ -13392,14 +13492,14 @@ snapshots:
- bluebird
- supports-color
- eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.32.0(jiti@2.4.2)):
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.4.2)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3)
eslint: 9.32.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.32.0(jiti@2.4.2)))(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.4.2))
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.32.0(jiti@2.4.2)))(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.4.2))
transitivePeerDependencies:
- supports-color
@@ -13432,7 +13532,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.32.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.32.0(jiti@2.4.2))
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.4.2))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -14014,6 +14114,8 @@ snapshots:
globals@14.0.0: {}
+ globals@16.3.0: {}
+
globalthis@1.0.4:
dependencies:
define-properties: 1.2.1
@@ -15381,11 +15483,11 @@ snapshots:
neo-async@2.6.2: {}
- next-intl@4.3.4(next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.8.3):
+ next-intl@4.3.4(next@15.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.8.3):
dependencies:
'@formatjs/intl-localematcher': 0.5.10
negotiator: 1.0.0
- next: 15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ next: 15.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
react: 19.1.1
use-intl: 4.3.4(react@19.1.1)
optionalDependencies:
@@ -15396,9 +15498,9 @@ snapshots:
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
- next@15.4.4(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
+ next@15.5.0(@opentelemetry/api@1.9.0)(@playwright/test@1.54.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
dependencies:
- '@next/env': 15.4.4
+ '@next/env': 15.5.0
'@swc/helpers': 0.5.15
caniuse-lite: 1.0.30001727
postcss: 8.4.31
@@ -15406,14 +15508,14 @@ snapshots:
react-dom: 19.1.1(react@19.1.1)
styled-jsx: 5.1.6(react@19.1.1)
optionalDependencies:
- '@next/swc-darwin-arm64': 15.4.4
- '@next/swc-darwin-x64': 15.4.4
- '@next/swc-linux-arm64-gnu': 15.4.4
- '@next/swc-linux-arm64-musl': 15.4.4
- '@next/swc-linux-x64-gnu': 15.4.4
- '@next/swc-linux-x64-musl': 15.4.4
- '@next/swc-win32-arm64-msvc': 15.4.4
- '@next/swc-win32-x64-msvc': 15.4.4
+ '@next/swc-darwin-arm64': 15.5.0
+ '@next/swc-darwin-x64': 15.5.0
+ '@next/swc-linux-arm64-gnu': 15.5.0
+ '@next/swc-linux-arm64-musl': 15.5.0
+ '@next/swc-linux-x64-gnu': 15.5.0
+ '@next/swc-linux-x64-musl': 15.5.0
+ '@next/swc-win32-arm64-msvc': 15.5.0
+ '@next/swc-win32-x64-msvc': 15.5.0
'@opentelemetry/api': 1.9.0
'@playwright/test': 1.54.1
sharp: 0.34.3
@@ -15724,12 +15826,6 @@ snapshots:
postcss-selector-parser: 7.1.0
postcss-value-parser: 4.2.0
- postcss-calc@10.1.1(postcss@8.5.6):
- dependencies:
- postcss: 8.5.6
- postcss-selector-parser: 7.1.0
- postcss-value-parser: 4.2.0
-
postcss-cli@11.0.1(jiti@2.4.2)(postcss@8.5.3)(tsx@4.20.3):
dependencies:
chokidar: 3.6.0
@@ -15751,7 +15847,7 @@ snapshots:
postcss-load-config@5.1.0(jiti@2.4.2)(postcss@8.5.3)(tsx@4.20.3):
dependencies:
lilconfig: 3.1.3
- yaml: 2.8.0
+ yaml: 2.8.1
optionalDependencies:
jiti: 2.4.2
postcss: 8.5.3
@@ -16202,6 +16298,7 @@ snapshots:
'@types/mdast': 4.0.4
quotation: 2.0.3
unified-lint-rule: 3.0.1
+ optional: true
remark-lint-final-definition@4.0.2:
dependencies:
@@ -16223,15 +16320,6 @@ snapshots:
unified-lint-rule: 3.0.1
vfile-location: 5.0.3
- remark-lint-first-heading-level@4.0.1:
- dependencies:
- '@types/mdast': 4.0.4
- mdast-util-mdx: 3.0.0
- unified-lint-rule: 3.0.1
- unist-util-visit-parents: 6.0.1
- transitivePeerDependencies:
- - supports-color
-
remark-lint-hard-break-spaces@4.1.1:
dependencies:
'@types/mdast': 4.0.4
@@ -16312,11 +16400,6 @@ snapshots:
unist-util-visit-parents: 6.0.1
vfile-message: 4.0.2
- remark-lint-no-file-name-articles@3.0.1:
- dependencies:
- '@types/mdast': 4.0.4
- unified-lint-rule: 3.0.1
-
remark-lint-no-file-name-consecutive-dashes@3.0.1:
dependencies:
'@types/mdast': 4.0.4
@@ -16523,45 +16606,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- remark-preset-lint-node@5.1.2:
- dependencies:
- js-yaml: 4.1.0
- remark-gfm: 4.0.1
- remark-lint-blockquote-indentation: 4.0.1
- remark-lint-checkbox-character-style: 5.0.1
- remark-lint-checkbox-content-indent: 5.0.1
- remark-lint-code-block-style: 4.0.1
- remark-lint-definition-spacing: 4.0.1
- remark-lint-fenced-code-flag: 4.2.0
- remark-lint-fenced-code-marker: 4.0.1
- remark-lint-file-extension: 3.0.1
- remark-lint-final-definition: 4.0.2
- remark-lint-first-heading-level: 4.0.1
- remark-lint-heading-style: 4.0.1
- remark-lint-maximum-line-length: 4.1.1
- remark-lint-no-consecutive-blank-lines: 5.0.1
- remark-lint-no-file-name-articles: 3.0.1
- remark-lint-no-file-name-consecutive-dashes: 3.0.1
- remark-lint-no-file-name-outer-dashes: 3.0.1
- remark-lint-no-heading-indent: 5.0.1
- remark-lint-no-multiple-toplevel-headings: 4.0.1
- remark-lint-no-shell-dollars: 4.0.1
- remark-lint-no-table-indentation: 5.0.1
- remark-lint-no-tabs: 4.0.1
- remark-lint-no-trailing-spaces: 3.0.2
- remark-lint-prohibited-strings: 4.0.0
- remark-lint-rule-style: 4.0.1
- remark-lint-strong-marker: 4.0.1
- remark-lint-table-cell-padding: 5.1.1
- remark-lint-table-pipes: 5.0.1
- remark-lint-unordered-list-marker-style: 4.0.1
- remark-preset-lint-recommended: 7.0.1
- semver: 7.7.2
- unified-lint-rule: 3.0.1
- unist-util-visit: 5.0.0
- transitivePeerDependencies:
- - supports-color
-
remark-preset-lint-recommended@7.0.1:
dependencies:
remark-lint: 10.0.1
@@ -17500,7 +17544,7 @@ snapshots:
vfile-message: 4.0.2
vfile-reporter: 8.1.1
vfile-statistics: 3.0.0
- yaml: 2.8.0
+ yaml: 2.8.1
transitivePeerDependencies:
- bluebird
- supports-color
@@ -17997,6 +18041,8 @@ snapshots:
yaml@2.8.0: {}
+ yaml@2.8.1: {}
+
yargs-parser@21.1.1: {}
yargs-parser@22.0.0: {}