Skip to content

Upgrade Docusaurus 2.0.0-beta.5 → 3.10.2 - #186

Merged
caglarpir merged 2 commits into
mapillary:mainfrom
caglarpir:docusaurus-3
Sep 8, 2026
Merged

Upgrade Docusaurus 2.0.0-beta.5 → 3.10.2#186
caglarpir merged 2 commits into
mapillary:mainfrom
caglarpir:docusaurus-3

Conversation

@caglarpir

Copy link
Copy Markdown
Contributor

The docs site was pinned to a 2021 Docusaurus beta. That single pin held the whole JS dependency tree in place and is the root cause behind most of the ~145 open docs/yarn.lock advisories — Dependabot has been reporting security_update_not_possible for exactly this, e.g. decode-uri-component could not move past 0.2.0 because @docusaurus/core@2.0.0-beta.5 required it transitively.

It is also why #175 could not land: webpack 5.110.3 is incompatible with the beta. I isolated that before closing #175 — Node 20 alone builds fine, Node 20 + webpack 5.110.3 fails with ValidationError: Progress Plugin has been initialized using an options object that does not match the API schema. This PR gets webpack to 5.110.3 as a side effect, because v3 is compatible with it.

Changes

  • @docusaurus/core + preset-classic 2.0.0-beta.5^3.10.2
  • react/react-dom ^17^18.3.1, @mdx-js/react ^1^3 (v3 peer requirements)
  • clsx ^1^2, added prism-react-renderer (required by the v3 preset)
  • Dropped @svgr/webpack, file-loader, url-loader, and a stray yarn dependency — all unused here; every svg import in src/ is commented out
  • engines.node: ">=20.0", matching Docusaurus 3
  • CI node-version 14.x20.x in documentation.yml (14 is EOL, and v3 will not run on it)

Two config migrations

markdown.format: 'detect' — MDX v3 is far stricter than v1 and tried to evaluate literal braces in the generated API docs as JSX:

Error: MDX compilation failed for file "docs/mapillary/mapillary.interface.md"
Cause: Could not parse expression with acorn   (line 376)

Line 376 is 22. mesh - { id: string, url: string } - URL to the mesh, straight out of a Python docstring. 'detect' parses .md as CommonMark and reserves MDX for .mdx. Escaping the braces instead would be undone by the next scripts/documentation.py run, since those files are generated.

onBrokenMarkdownLinks moved under markdown.hooks, where v3 expects it. The top-level option is deprecated and removed in v4.

Verified locally on Node 20

  • yarn build succeeds, 43 HTML pages generated, no errors and no deprecation warnings
  • The page that previously failed to compile now renders { id: string, url: string } as literal text — confirmed in the built HTML rather than assumed

Security impact

Package Before After
webpack 5.74.0 5.110.3
websocket-driver 0.7.4 0.7.5
svgo 1.3.2 3.3.5
browserslist 4.14.2 4.28.9
shell-quote 1.7.2 1.10.0
postcss (8.x tree) 8.4.16 8.5.28
body-parser 1.20.0 1.20.6
js-yaml 3.14.1 3.15.2
axios / ua-parser-js / decode-uri-component present removed entirely

Known remainder

postcss@7.0.39 survives, pulled in by the old stylelint@13 devDependency (via @stylelint/postcss-markdown / postcss-css-in-js). Clearing it means upgrading stylelint 13 → 16, which needs a config migration in .stylelintrc.js. The same applies to eslint@7. Those tools are not run by any workflow — only npm run build is — so I left them out to keep this reviewable. Worth a follow-up.

The two remaining build warnings (inline blog authors, missing truncation markers) are about the leftover Docusaurus sample blog posts and predate this change.

Test plan

CI checks job is the real test — it runs yarn install --frozen-lockfile && npm run build on the new lockfile and Node 20.

The docs site was pinned to a 2021 Docusaurus beta, which held the entire
JS dependency tree in place and made most of the docs/yarn.lock security
advisories unfixable individually. Dependabot had been reporting
security_update_not_possible for exactly this reason, e.g.
decode-uri-component could not move past 0.2.0 because
@docusaurus/core@2.0.0-beta.5 required it transitively.

Changes:
- @docusaurus/core and preset-classic 2.0.0-beta.5 -> ^3.10.2
- react/react-dom ^17 -> ^18.3.1, @mdx-js/react ^1 -> ^3 (v3 peer deps)
- clsx ^1 -> ^2, prism-react-renderer added (required by v3 preset)
- dropped @svgr/webpack, file-loader, url-loader and the stray 'yarn'
  dependency: all unused here, every svg import in src/ is commented out
- engines.node >=20.0, matching Docusaurus 3's own requirement
- CI node-version 14.x -> 20.x in documentation.yml (14 is EOL, and
  Docusaurus 3 will not run on it)

Two config migrations were needed:

markdown.format: 'detect' - MDX v3 is stricter than v1 and tried to
evaluate literal braces in the generated API docs as JSX expressions,
failing on "{ id: string, url: string }" in mapillary.interface.md.
'detect' parses .md as CommonMark and reserves MDX for .mdx. Escaping the
braces instead would have been undone by the next scripts/documentation.py
run, since those files are generated from Python docstrings.

onBrokenMarkdownLinks moved under markdown.hooks, where v3 expects it;
the top-level option is deprecated and removed in v4.

Verified locally on Node 20: build succeeds, 43 HTML pages generated, and
the page that previously failed to compile now renders its braces as
literal text.

Notable transitive results: webpack 5.74.0 -> 5.110.3 (the bump mapillary#175
attempted and could not land, since 5.110.3 is incompatible with the
beta), websocket-driver 0.7.4 -> 0.7.5, svgo 1.3.2 -> 3.3.5, browserslist
4.14.2 -> 4.28.9, shell-quote 1.7.2 -> 1.10.0, postcss 8.4.16 -> 8.5.28,
and axios, ua-parser-js and decode-uri-component dropped entirely.
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 7, 2026
@caglarpir

Copy link
Copy Markdown
Contributor Author

Followed up with a differential comparison of the two built sites, since "the build succeeds" says nothing about whether the site still renders. Built main (2.0.0-beta.5) and this branch (3.10.2) on Node 20 and compared the rendered output page by page.

Two things came out of it that were not in the original description.

1. Seven doc URLs change — please read before merging

Docusaurus applies a category index convention: a doc whose filename matches its folder becomes that category’s index page. That convention postdates beta.5, so the upgrade activates it.

/docs/mapillary.config/mapillary.config       ->  /docs/mapillary.config
/docs/mapillary.config.api/mapillary.config.api  ->  /docs/mapillary.config.api
/docs/mapillary.controller/mapillary.controller  ->  /docs/mapillary.controller
/docs/mapillary.models/mapillary.models       ->  /docs/mapillary.models
/docs/mapillary.models.api/mapillary.models.api  ->  /docs/mapillary.models.api
/docs/mapillary.utils/mapillary.utils         ->  /docs/mapillary.utils
/docs/mapillary/mapillary                     ->  /docs/mapillary

Assessment:

  • All 42 internal links still resolve — 0 unresolvable across the site. The sidebar and navbar regenerate against the new paths.
  • Zero references to the old URLs anywhere in the repo.
  • Only external bookmarks or inbound links would break. Worth noting the published site has not been redeployed since 2025-07-21 (the gh-release job fails on Permission denied (publickey) — a dead GH_PAGES_DEPLOY key), so what is live is already a year stale.

If you would rather preserve the old paths, @docusaurus/plugin-client-redirects would map them in ~10 lines. I have not added it — the new URLs are arguably cleaner and nothing references the old ones — but it is your call and I did not want to make it silently.

Also gained: blog/archive and blog/authors (new v3 pages). Lost: docs/tags (blog tag index, restructured in v3).

2. The upgrade fixes a pre-existing rendering bug

Every shared page’s text changed, which looked alarming until I checked the cause. The generated API docs were displaying raw markdown asterisks to readers:

old build new build
literal ** in HTML 80 0
<strong> tags 51 90
<em> tags 2 40

Parameter names and types were rendering as literal **bbox** and (*dict*) instead of bold and italic. They now render correctly. Every "page text shrunk" result is asterisks being consumed as markup rather than shown — content gained, not lost.

Verified no page lost more than 15% of its vocabulary; the only words dropped sitewide are chrome (Copy button label, the 🌜/🌞 dark-mode emoji, now SVG icons).

Full results

PAGE INVENTORY    old 42 -> new 43   (7 renamed, 2 added, 1 removed)
CONTENT           no page lost >15% of vocabulary
CHROME            navbar / footer / sidebar / css / js all present
INTERNAL LINKS    42 checked, 0 unresolvable
ASSETS            img 11 -> 11, css 1 -> 1, js 60 -> 68

Docusaurus treats a doc whose filename matches its folder as that
category's index page, a convention introduced after 2.0.0-beta.5.
Upgrading therefore shortens seven API doc URLs, e.g.

    /docs/mapillary.config/mapillary.config -> /docs/mapillary.config

Nothing in the repo references the old paths and every internal link
regenerates, but external bookmarks and inbound links would 404. This
adds @docusaurus/plugin-client-redirects and maps each old path to its
new one.

Verified by building and following each redirect: all seven stubs are
emitted with the correct target, and the content reached through them
matches what the old URL served. The only differences are chrome the
v3 theme renders differently (prev/next arrows, dark-mode toggle icons).

Build output goes 43 -> 50 pages: 43 real pages plus 7 redirect stubs.
Internal link check still reports 0 unresolvable across 49 links.

docs/tags is intentionally not redirected. It existed in v2 as an empty
tag index with no tags to list; v3 omits it rather than emitting an
empty page.
@caglarpir

Copy link
Copy Markdown
Contributor Author

Added @docusaurus/plugin-client-redirects so the seven moved URLs keep working — this addresses the breaking-change caveat raised above, so the PR should now be non-breaking for external links.

What was added

The plugin plus a mapping for each path the category-index convention shortens:

redirects: [
  mapillary, mapillary.config, mapillary.config.api,
  mapillary.controller, mapillary.models, mapillary.models.api,
  mapillary.utils,
].map((name) => ({ from: `/docs/${name}/${name}`, to: `/docs/${name}` })),

Verified, not assumed

Every redirect stub is emitted with the right target:

OK  /docs/mapillary/mapillary                      -> /mapillary-python-sdk/docs/mapillary
OK  /docs/mapillary.config/mapillary.config        -> /mapillary-python-sdk/docs/mapillary.config
OK  /docs/mapillary.config.api/mapillary.config.api -> /mapillary-python-sdk/docs/mapillary.config.api
OK  /docs/mapillary.controller/mapillary.controller -> /mapillary-python-sdk/docs/mapillary.controller
OK  /docs/mapillary.models/mapillary.models        -> /mapillary-python-sdk/docs/mapillary.models
OK  /docs/mapillary.models.api/mapillary.models.api -> /mapillary-python-sdk/docs/mapillary.models.api
OK  /docs/mapillary.utils/mapillary.utils          -> /mapillary-python-sdk/docs/mapillary.utils

I then followed each redirect and compared the content it lands on against what the old URL used to serve. All seven resolve to equivalent content. The only words that differ are theme chrome the v3 renders differently — the «/» prev-next arrows and the 🌜/🌞 dark-mode toggle, now SVG icons.

Re-ran the full differential check afterwards:

PAGE INVENTORY    43 real pages + 7 redirect stubs = 50
INTERNAL LINKS    49 checked, 0 unresolvable
CHROME            navbar / footer / sidebar / css / js all present
ASSETS            img 11 -> 11, css 1 -> 1

One deliberate omission

docs/tags is not redirected. It existed in v2 as an empty tag index with no tags to list; v3 omits it rather than emitting an empty page. Redirecting a page that never had content seemed worse than letting it go, but happy to add it if you disagree.

@caglarpir
caglarpir merged commit 76beaba into mapillary:main Sep 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant