Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
run: npm run test
- name: 🔤 Spell Check
run: npm run spellcheck
- uses: ./.github/workflows/actions/check-admonitions
- uses: ./.github/workflows/actions/check-translations
# Lint and spell check changes should be pushed
# to the branch before the branch is merge eligible.
Expand Down
92 changes: 92 additions & 0 deletions .github/workflows/actions/check-admonitions/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: 'Check Admonitions'
description: 'Fails on admonition titles using the MDX v1 syntax'
runs:
using: 'composite'
steps:
# `:::note My Title` was MDX v1 syntax that Docusaurus rewrote for us.
# With `future.v4.mdx1CompatDisabledByDefault` that shim is off, and
# the block `:::note My Title` stops being a directive: it renders as
# literal `:::note` text on the page, with no warning and a successful
# build.
#
# The other MDX v1 forms, HTML comments and `{#heading-ids}`, fail the
# build on their own, so this only covers the one that fails silently.
#
# Only the files the pull request touches are checked, so an existing
# page is never anyone else's problem to fix.
#
# The event payload has no file list, so the changed files come from a
# diff. The checkout is shallow and the base commit is fetched here
# rather than through `fetch-depth` on the checkout, which would pull
# the full history for every step in the job just to serve this one.
- name: 🔎 Check Admonitions
shell: bash
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
CHANGED_FILES: ${{ runner.temp }}/changed-files.txt
run: |
git fetch --quiet --no-tags --depth=1 origin "$BASE_SHA"
git diff --name-only --diff-filter=ACMR "$BASE_SHA" HEAD > "$CHANGED_FILES"
node <<'JS'
const { readFileSync } = require('fs');

// Files Docusaurus compiles as MDX. Under `static/usage` that is
// only the `index` files; the rest are raw code samples.
const compiled = (f) =>
!f.endsWith('README.md') &&
((/^(docs|versioned_docs|src\/pages)\//.test(f) && /\.mdx?$/.test(f)) ||
(f.startsWith('static/usage/') && f.endsWith('.mdx')));

// Defaults from `@docusaurus/mdx-loader/lib/remark/admonitions`.
const keywords =
'secondary|info|success|danger|note|tip|warning|important|caution';
const legacyTitle = new RegExp(
`^[ \\t]*:::(?:${keywords})[ \\t]+(?!\\[)\\S[^\\n]*`,
'gm'
);

// A `:::note` in a code sample is an example, not a directive.
// Blank out code samples with spaces, same length so line numbers
// still match.
const blank = (m) => m.replace(/[^\n]/g, ' ');
const mask = (t) =>
t
.replace(/^([ \t]*)(`{3,}|~{3,})[^\n]*\n[\s\S]*?^\1\2[ \t]*$/gm, blank)
.replace(/(`+)(?:(?!\1)[\s\S])+?\1/g, blank);

const files = readFileSync(process.env.CHANGED_FILES, 'utf8')
.split('\n')
.filter(compiled);

if (!files.length) {
console.log('No MDX files changed.');
process.exit(0);
}

const findings = [];
for (const file of files) {
const source = readFileSync(file, 'utf8');
for (const m of mask(source).matchAll(legacyTitle)) {
findings.push({
file,
line: source.slice(0, m.index).split('\n').length,
text: source.slice(m.index, m.index + m[0].length).trim(),
});
}
}

if (!findings.length) {
console.log(`No MDX v1 admonition titles in ${files.length} changed file(s).`);
process.exit(0);
}

// `::error` puts each one on the offending line in the Files changed
// tab and prints it in red in the log, so the failure is not something
// to go hunting for. The message leads with the fix because the found
// text starts with `:::`, which reads badly right after the delimiter.
for (const { file, line, text } of findings) {
const fix = 'Wrap the title in brackets, for example :::note[My Title]';
console.log(`::error file=${file},line=${line},title=MDX v1 admonition title::${fix}%0A%0AFound: ${text}`);
}
process.exit(1);
JS
25 changes: 17 additions & 8 deletions docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,27 @@ module.exports = {
},
},
onBrokenLinks: 'warn',
/**
* Docusaurus Faster replaces the Webpack/Babel/Terser toolchain with
* Rspack/SWC/Lightning CSS, which cuts build times and memory usage on a
* site with this many versioned pages. It becomes the default in v4.
*
* `removeLegacyPostBuildHeadAttribute` is required by the `ssgWorkerThreads`
* part of `faster`, so it has to be enabled alongside it.
*/
future: {
v4: {
/**
* Turns off the MDX v1 shims for comments, admonition titles and
* heading ids. Every page uses the native syntax, so the shims are
* no-ops, and disabling them means a page that reintroduces the old
* syntax fails the build now rather than on the upgrade. It becomes
* the default in v4.
*/
mdx1CompatDisabledByDefault: true,
/**
* Required by the `ssgWorkerThreads` part of `faster`, so it has to
* be enabled alongside it. It becomes the default in v4.
*/
removeLegacyPostBuildHeadAttribute: true,
},
/**
* Replaces the Webpack/Babel/Terser toolchain with Rspack/SWC/Lightning
* CSS, which cuts build times and memory usage on a site with this many
* versioned pages. It becomes the default in v4.
*/
faster: true,
},
markdown: {
Expand Down