@docusaurus/core 0.0.0-5811 → 0.0.0-5814

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,21 +13,38 @@ const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
13
13
  const react_router_config_1 = require("react-router-config");
14
14
  const utils_1 = require("@docusaurus/utils");
15
15
  const utils_2 = require("./utils");
16
- function getBrokenLinksForPage({ collectedLinks, pagePath, pageLinks, routes, }) {
17
- const allCollectedPaths = new Set(Object.keys(collectedLinks));
16
+ function matchRoutes(routeConfig, pathname) {
17
+ // @ts-expect-error: React router types RouteConfig with an actual React
18
+ // component, but we load route components with string paths.
19
+ // We don't actually access component here, so it's fine.
20
+ return (0, react_router_config_1.matchRoutes)(routeConfig, pathname);
21
+ }
22
+ function createBrokenLinksHelper({ collectedLinks, routes, }) {
23
+ const validPathnames = new Set(collectedLinks.keys());
24
+ // Matching against the route array can be expensive
25
+ // If the route is already in the valid pathnames,
26
+ // we can avoid matching against it as an optimization
27
+ const remainingRoutes = routes.filter((route) => !validPathnames.has(route.path));
28
+ function isPathnameMatchingAnyRoute(pathname) {
29
+ if (matchRoutes(remainingRoutes, pathname).length > 0) {
30
+ // IMPORTANT: this is an optimization here
31
+ // See https://github.com/facebook/docusaurus/issues/9754
32
+ // Large Docusaurus sites have many routes!
33
+ // We try to minimize calls to a possibly expensive matchRoutes function
34
+ validPathnames.add(pathname);
35
+ return true;
36
+ }
37
+ return false;
38
+ }
18
39
  function isPathBrokenLink(linkPath) {
19
40
  const pathnames = [linkPath.pathname, decodeURI(linkPath.pathname)];
20
- const matchedRoutes = pathnames
21
- // @ts-expect-error: React router types RouteConfig with an actual React
22
- // component, but we load route components with string paths.
23
- // We don't actually access component here, so it's fine.
24
- .map((l) => (0, react_router_config_1.matchRoutes)(routes, l))
25
- .flat();
26
- // The link path is broken if:
27
- // - it doesn't match any route
28
- // - it doesn't match any collected path
29
- return (matchedRoutes.length === 0 &&
30
- !pathnames.some((p) => allCollectedPaths.has(p)));
41
+ if (pathnames.some((p) => validPathnames.has(p))) {
42
+ return false;
43
+ }
44
+ if (pathnames.some(isPathnameMatchingAnyRoute)) {
45
+ return false;
46
+ }
47
+ return true;
31
48
  }
32
49
  function isAnchorBrokenLink(linkPath) {
33
50
  const { pathname, hash } = linkPath;
@@ -41,38 +58,44 @@ function getBrokenLinksForPage({ collectedLinks, pagePath, pageLinks, routes, })
41
58
  if (hash === '') {
42
59
  return false;
43
60
  }
44
- const targetPage = collectedLinks[pathname] || collectedLinks[decodeURI(pathname)];
61
+ const targetPage = collectedLinks.get(pathname) || collectedLinks.get(decodeURI(pathname));
45
62
  // link with anchor to a page that does not exist (or did not collect any
46
63
  // link/anchor) is considered as a broken anchor
47
64
  if (!targetPage) {
48
65
  return true;
49
66
  }
50
- // it's a broken anchor if the target page exists
51
- // but the anchor does not exist on that page
52
- const hashes = [hash, decodeURIComponent(hash)];
53
- return !targetPage.anchors.some((anchor) => hashes.includes(anchor));
67
+ // it's a not broken anchor if the anchor exists on the target page
68
+ if (targetPage.anchors.has(hash) ||
69
+ targetPage.anchors.has(decodeURIComponent(hash))) {
70
+ return false;
71
+ }
72
+ return true;
54
73
  }
55
- const brokenLinks = pageLinks.flatMap((link) => {
74
+ return {
75
+ collectedLinks,
76
+ isPathBrokenLink,
77
+ isAnchorBrokenLink,
78
+ };
79
+ }
80
+ function getBrokenLinksForPage({ pagePath, helper, }) {
81
+ const pageData = helper.collectedLinks.get(pagePath);
82
+ const brokenLinks = [];
83
+ pageData.links.forEach((link) => {
56
84
  const linkPath = (0, utils_1.parseURLPath)(link, pagePath);
57
- if (isPathBrokenLink(linkPath)) {
58
- return [
59
- {
60
- link,
61
- resolvedLink: (0, utils_1.serializeURLPath)(linkPath),
62
- anchor: false,
63
- },
64
- ];
85
+ if (helper.isPathBrokenLink(linkPath)) {
86
+ brokenLinks.push({
87
+ link,
88
+ resolvedLink: (0, utils_1.serializeURLPath)(linkPath),
89
+ anchor: false,
90
+ });
65
91
  }
66
- if (isAnchorBrokenLink(linkPath)) {
67
- return [
68
- {
69
- link,
70
- resolvedLink: (0, utils_1.serializeURLPath)(linkPath),
71
- anchor: true,
72
- },
73
- ];
92
+ else if (helper.isAnchorBrokenLink(linkPath)) {
93
+ brokenLinks.push({
94
+ link,
95
+ resolvedLink: (0, utils_1.serializeURLPath)(linkPath),
96
+ anchor: true,
97
+ });
74
98
  }
75
- return [];
76
99
  });
77
100
  return brokenLinks;
78
101
  }
@@ -88,14 +111,16 @@ function filterIntermediateRoutes(routesInput) {
88
111
  }
89
112
  function getBrokenLinks({ collectedLinks, routes, }) {
90
113
  const filteredRoutes = filterIntermediateRoutes(routes);
91
- return lodash_1.default.mapValues(collectedLinks, (pageCollectedData, pagePath) => {
114
+ const helper = createBrokenLinksHelper({
115
+ collectedLinks,
116
+ routes: filteredRoutes,
117
+ });
118
+ const result = {};
119
+ collectedLinks.forEach((_unused, pagePath) => {
92
120
  try {
93
- return getBrokenLinksForPage({
94
- collectedLinks,
95
- pageLinks: pageCollectedData.links,
96
- pageAnchors: pageCollectedData.anchors,
121
+ result[pagePath] = getBrokenLinksForPage({
97
122
  pagePath,
98
- routes: filteredRoutes,
123
+ helper,
99
124
  });
100
125
  }
101
126
  catch (e) {
@@ -104,6 +129,7 @@ function getBrokenLinks({ collectedLinks, routes, }) {
104
129
  });
105
130
  }
106
131
  });
132
+ return result;
107
133
  }
108
134
  function brokenLinkMessage(brokenLink) {
109
135
  const showResolvedLink = brokenLink.link !== brokenLink.resolvedLink;
@@ -204,13 +230,18 @@ function reportBrokenLinks({ brokenLinks, onBrokenLinks, onBrokenAnchors, }) {
204
230
  // JS users might call "collectLink(undefined)" for example
205
231
  // TS users might call "collectAnchor('#hash')" with/without #
206
232
  // We clean/normalize the collected data to avoid obscure errors being thrown
233
+ // We also use optimized data structures for a faster algorithm
207
234
  function normalizeCollectedLinks(collectedLinks) {
208
- return lodash_1.default.mapValues(collectedLinks, (pageCollectedData) => ({
209
- links: pageCollectedData.links.filter(lodash_1.default.isString),
210
- anchors: pageCollectedData.anchors
211
- .filter(lodash_1.default.isString)
212
- .map((anchor) => (anchor.startsWith('#') ? anchor.slice(1) : anchor)),
213
- }));
235
+ const result = new Map();
236
+ Object.entries(collectedLinks).forEach(([pathname, pageCollectedData]) => {
237
+ result.set(pathname, {
238
+ links: new Set(pageCollectedData.links.filter(lodash_1.default.isString)),
239
+ anchors: new Set(pageCollectedData.anchors
240
+ .filter(lodash_1.default.isString)
241
+ .map((anchor) => (anchor.startsWith('#') ? anchor.slice(1) : anchor))),
242
+ });
243
+ });
244
+ return result;
214
245
  }
215
246
  async function handleBrokenLinks({ collectedLinks, onBrokenLinks, onBrokenAnchors, routes, }) {
216
247
  if (onBrokenLinks === 'ignore' && onBrokenAnchors === 'ignore') {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@docusaurus/core",
3
3
  "description": "Easy to Maintain Open Source Documentation Websites",
4
- "version": "0.0.0-5811",
4
+ "version": "0.0.0-5814",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -43,13 +43,13 @@
43
43
  "@babel/runtime": "^7.22.6",
44
44
  "@babel/runtime-corejs3": "^7.22.6",
45
45
  "@babel/traverse": "^7.22.8",
46
- "@docusaurus/cssnano-preset": "0.0.0-5811",
47
- "@docusaurus/logger": "0.0.0-5811",
48
- "@docusaurus/mdx-loader": "0.0.0-5811",
46
+ "@docusaurus/cssnano-preset": "0.0.0-5814",
47
+ "@docusaurus/logger": "0.0.0-5814",
48
+ "@docusaurus/mdx-loader": "0.0.0-5814",
49
49
  "@docusaurus/react-loadable": "5.5.2",
50
- "@docusaurus/utils": "0.0.0-5811",
51
- "@docusaurus/utils-common": "0.0.0-5811",
52
- "@docusaurus/utils-validation": "0.0.0-5811",
50
+ "@docusaurus/utils": "0.0.0-5814",
51
+ "@docusaurus/utils-common": "0.0.0-5814",
52
+ "@docusaurus/utils-validation": "0.0.0-5814",
53
53
  "@slorber/static-site-generator-webpack-plugin": "^4.0.7",
54
54
  "@svgr/webpack": "^6.5.1",
55
55
  "autoprefixer": "^10.4.14",
@@ -104,8 +104,8 @@
104
104
  "webpackbar": "^5.0.2"
105
105
  },
106
106
  "devDependencies": {
107
- "@docusaurus/module-type-aliases": "0.0.0-5811",
108
- "@docusaurus/types": "0.0.0-5811",
107
+ "@docusaurus/module-type-aliases": "0.0.0-5814",
108
+ "@docusaurus/types": "0.0.0-5814",
109
109
  "@types/detect-port": "^1.3.3",
110
110
  "@types/react-dom": "^18.2.7",
111
111
  "@types/react-router-config": "^5.0.7",
@@ -124,5 +124,5 @@
124
124
  "engines": {
125
125
  "node": ">=18.0"
126
126
  },
127
- "gitHead": "06b637e03ce679fa1713ca0e467a354c55f6db5e"
127
+ "gitHead": "67b273740f8d1404edf4307185f21ab3b4b37032"
128
128
  }