@docusaurus/core 0.0.0-5811 → 0.0.0-5813
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.
- package/lib/server/brokenLinks.js +79 -48
- package/package.json +10 -10
|
@@ -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
|
|
17
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
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
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
114
|
+
const helper = createBrokenLinksHelper({
|
|
115
|
+
collectedLinks,
|
|
116
|
+
routes: filteredRoutes,
|
|
117
|
+
});
|
|
118
|
+
const result = {};
|
|
119
|
+
collectedLinks.forEach((_unused, pagePath) => {
|
|
92
120
|
try {
|
|
93
|
-
|
|
94
|
-
collectedLinks,
|
|
95
|
-
pageLinks: pageCollectedData.links,
|
|
96
|
-
pageAnchors: pageCollectedData.anchors,
|
|
121
|
+
result[pagePath] = getBrokenLinksForPage({
|
|
97
122
|
pagePath,
|
|
98
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
.filter(lodash_1.default.isString)
|
|
212
|
-
|
|
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-
|
|
4
|
+
"version": "0.0.0-5813",
|
|
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-
|
|
47
|
-
"@docusaurus/logger": "0.0.0-
|
|
48
|
-
"@docusaurus/mdx-loader": "0.0.0-
|
|
46
|
+
"@docusaurus/cssnano-preset": "0.0.0-5813",
|
|
47
|
+
"@docusaurus/logger": "0.0.0-5813",
|
|
48
|
+
"@docusaurus/mdx-loader": "0.0.0-5813",
|
|
49
49
|
"@docusaurus/react-loadable": "5.5.2",
|
|
50
|
-
"@docusaurus/utils": "0.0.0-
|
|
51
|
-
"@docusaurus/utils-common": "0.0.0-
|
|
52
|
-
"@docusaurus/utils-validation": "0.0.0-
|
|
50
|
+
"@docusaurus/utils": "0.0.0-5813",
|
|
51
|
+
"@docusaurus/utils-common": "0.0.0-5813",
|
|
52
|
+
"@docusaurus/utils-validation": "0.0.0-5813",
|
|
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-
|
|
108
|
-
"@docusaurus/types": "0.0.0-
|
|
107
|
+
"@docusaurus/module-type-aliases": "0.0.0-5813",
|
|
108
|
+
"@docusaurus/types": "0.0.0-5813",
|
|
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": "
|
|
127
|
+
"gitHead": "0f04893f7d821f3e0b741017ee7adcac940cba62"
|
|
128
128
|
}
|