@docusaurus/core 0.0.0-5797 → 0.0.0-5801

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.
@@ -11,10 +11,10 @@ export const createStatefulBrokenLinks = () => {
11
11
  const allLinks = new Set();
12
12
  return {
13
13
  collectAnchor: (anchor) => {
14
- allAnchors.add(anchor);
14
+ typeof anchor !== 'undefined' && allAnchors.add(anchor);
15
15
  },
16
16
  collectLink: (link) => {
17
- allLinks.add(link);
17
+ typeof link !== 'undefined' && allLinks.add(link);
18
18
  },
19
19
  getCollectedAnchors: () => [...allAnchors],
20
20
  getCollectedLinks: () => [...allLinks],
@@ -97,11 +97,16 @@ function Link({ isNavLink, to, href, activeClassName, isActive, 'data-noBrokenLi
97
97
  }
98
98
  };
99
99
  }, [ioRef, targetLink, IOSupported, isInternal]);
100
+ // It is simple local anchor link targeting current page?
100
101
  const isAnchorLink = targetLink?.startsWith('#') ?? false;
102
+ // Should we use a regular <a> tag instead of React-Router Link component?
101
103
  const isRegularHtmlLink = !targetLink || !isInternal || isAnchorLink;
102
- if (!isRegularHtmlLink && !noBrokenLinkCheck) {
104
+ if (!noBrokenLinkCheck && (isAnchorLink || !isRegularHtmlLink)) {
103
105
  brokenLinks.collectLink(targetLink);
104
106
  }
107
+ if (props.id) {
108
+ brokenLinks.collectAnchor(props.id);
109
+ }
105
110
  return isRegularHtmlLink ? (
106
111
  // eslint-disable-next-line jsx-a11y/anchor-has-content, @docusaurus/no-html-links
107
112
  <a ref={innerRef} href={targetLink} {...(targetLinkUnprefixed &&
@@ -14,15 +14,20 @@ 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
16
  function getBrokenLinksForPage({ collectedLinks, pagePath, pageLinks, routes, }) {
17
- // console.log('routes:', routes);
17
+ const allCollectedPaths = new Set(Object.keys(collectedLinks));
18
18
  function isPathBrokenLink(linkPath) {
19
- const matchedRoutes = [linkPath.pathname, decodeURI(linkPath.pathname)]
19
+ const pathnames = [linkPath.pathname, decodeURI(linkPath.pathname)];
20
+ const matchedRoutes = pathnames
20
21
  // @ts-expect-error: React router types RouteConfig with an actual React
21
22
  // component, but we load route components with string paths.
22
23
  // We don't actually access component here, so it's fine.
23
24
  .map((l) => (0, react_router_config_1.matchRoutes)(routes, l))
24
25
  .flat();
25
- return matchedRoutes.length === 0;
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)));
26
31
  }
27
32
  function isAnchorBrokenLink(linkPath) {
28
33
  const { pathname, hash } = linkPath;
@@ -30,6 +35,12 @@ function getBrokenLinksForPage({ collectedLinks, pagePath, pageLinks, routes, })
30
35
  if (hash === undefined) {
31
36
  return false;
32
37
  }
38
+ // Link has empty hash ("#", "/page#"...): we do not report it as broken
39
+ // Empty hashes are used for various weird reasons, by us and other users...
40
+ // See for example: https://github.com/facebook/docusaurus/pull/6003
41
+ if (hash === '') {
42
+ return false;
43
+ }
33
44
  const targetPage = collectedLinks[pathname] || collectedLinks[decodeURI(pathname)];
34
45
  // link with anchor to a page that does not exist (or did not collect any
35
46
  // link/anchor) is considered as a broken anchor
@@ -38,7 +49,8 @@ function getBrokenLinksForPage({ collectedLinks, pagePath, pageLinks, routes, })
38
49
  }
39
50
  // it's a broken anchor if the target page exists
40
51
  // but the anchor does not exist on that page
41
- return !targetPage.anchors.includes(hash);
52
+ const hashes = [hash, decodeURIComponent(hash)];
53
+ return !targetPage.anchors.some((anchor) => hashes.includes(anchor));
42
54
  }
43
55
  const brokenLinks = pageLinks.flatMap((link) => {
44
56
  const linkPath = (0, utils_1.parseURLPath)(link, pagePath);
@@ -76,13 +88,22 @@ function filterIntermediateRoutes(routesInput) {
76
88
  }
77
89
  function getBrokenLinks({ collectedLinks, routes, }) {
78
90
  const filteredRoutes = filterIntermediateRoutes(routes);
79
- return lodash_1.default.mapValues(collectedLinks, (pageCollectedData, pagePath) => getBrokenLinksForPage({
80
- collectedLinks,
81
- pageLinks: pageCollectedData.links,
82
- pageAnchors: pageCollectedData.anchors,
83
- pagePath,
84
- routes: filteredRoutes,
85
- }));
91
+ return lodash_1.default.mapValues(collectedLinks, (pageCollectedData, pagePath) => {
92
+ try {
93
+ return getBrokenLinksForPage({
94
+ collectedLinks,
95
+ pageLinks: pageCollectedData.links,
96
+ pageAnchors: pageCollectedData.anchors,
97
+ pagePath,
98
+ routes: filteredRoutes,
99
+ });
100
+ }
101
+ catch (e) {
102
+ throw new Error(`Unable to get broken links for page ${pagePath}.`, {
103
+ cause: e,
104
+ });
105
+ }
106
+ });
86
107
  }
87
108
  function brokenLinkMessage(brokenLink) {
88
109
  const showResolvedLink = brokenLink.link !== brokenLink.resolvedLink;
@@ -179,11 +200,26 @@ function reportBrokenLinks({ brokenLinks, onBrokenLinks, onBrokenAnchors, }) {
179
200
  logger_1.default.report(onBrokenAnchors)(anchorErrorMessage);
180
201
  }
181
202
  }
203
+ // Users might use the useBrokenLinks() API in weird unexpected ways
204
+ // JS users might call "collectLink(undefined)" for example
205
+ // TS users might call "collectAnchor('#hash')" with/without #
206
+ // We clean/normalize the collected data to avoid obscure errors being thrown
207
+ 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
+ }));
214
+ }
182
215
  async function handleBrokenLinks({ collectedLinks, onBrokenLinks, onBrokenAnchors, routes, }) {
183
216
  if (onBrokenLinks === 'ignore' && onBrokenAnchors === 'ignore') {
184
217
  return;
185
218
  }
186
- const brokenLinks = getBrokenLinks({ routes, collectedLinks });
219
+ const brokenLinks = getBrokenLinks({
220
+ routes,
221
+ collectedLinks: normalizeCollectedLinks(collectedLinks),
222
+ });
187
223
  reportBrokenLinks({ brokenLinks, onBrokenLinks, onBrokenAnchors });
188
224
  }
189
225
  exports.handleBrokenLinks = handleBrokenLinks;
@@ -159,6 +159,14 @@ ${JSON.stringify(routeConfig)}`);
159
159
  props,
160
160
  });
161
161
  }
162
+ /**
163
+ * Old stuff
164
+ * As far as I understand, this is what permits to SSG the 404.html file
165
+ * This is rendered through the catch-all ComponentCreator("*") route
166
+ * Note CDNs only understand the 404.html file by convention
167
+ * The extension probably permits to avoid emitting "/404/index.html"
168
+ */
169
+ const NotFoundRoutePath = '/404.html';
162
170
  /**
163
171
  * Routes are prepared into three temp files:
164
172
  *
@@ -175,7 +183,7 @@ function loadRoutes(routeConfigs, baseUrl, onDuplicateRoutes) {
175
183
  routesConfig: '',
176
184
  routesChunkNames: {},
177
185
  registry: {},
178
- routesPaths: [(0, utils_1.normalizeUrl)([baseUrl, '404.html'])],
186
+ routesPaths: [(0, utils_1.normalizeUrl)([baseUrl, NotFoundRoutePath])],
179
187
  };
180
188
  // `genRouteCode` would mutate `res`
181
189
  const routeConfigSerialized = routeConfigs
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-5797",
4
+ "version": "0.0.0-5801",
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-5797",
47
- "@docusaurus/logger": "0.0.0-5797",
48
- "@docusaurus/mdx-loader": "0.0.0-5797",
46
+ "@docusaurus/cssnano-preset": "0.0.0-5801",
47
+ "@docusaurus/logger": "0.0.0-5801",
48
+ "@docusaurus/mdx-loader": "0.0.0-5801",
49
49
  "@docusaurus/react-loadable": "5.5.2",
50
- "@docusaurus/utils": "0.0.0-5797",
51
- "@docusaurus/utils-common": "0.0.0-5797",
52
- "@docusaurus/utils-validation": "0.0.0-5797",
50
+ "@docusaurus/utils": "0.0.0-5801",
51
+ "@docusaurus/utils-common": "0.0.0-5801",
52
+ "@docusaurus/utils-validation": "0.0.0-5801",
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-5797",
108
- "@docusaurus/types": "0.0.0-5797",
107
+ "@docusaurus/module-type-aliases": "0.0.0-5801",
108
+ "@docusaurus/types": "0.0.0-5801",
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": "b1487520689968837819e5b837e0acbf071b9d0e"
127
+ "gitHead": "44100c84778ec4735110e7a878269af287facaa7"
128
128
  }