@docusaurus/utils 3.10.2 → 4.0.0-canary-6808
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/constants.js +1 -1
- package/lib/constants.js.map +1 -1
- package/lib/contentVisibilityUtils.js +1 -1
- package/lib/dataFileUtils.js.map +1 -1
- package/lib/globUtils.d.ts +5 -3
- package/lib/globUtils.d.ts.map +1 -1
- package/lib/globUtils.js +9 -4
- package/lib/globUtils.js.map +1 -1
- package/lib/index.d.ts +0 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -10
- package/lib/index.js.map +1 -1
- package/lib/lastUpdateUtils.js +2 -2
- package/lib/lastUpdateUtils.js.map +1 -1
- package/lib/markdownHeadingIdUtils.js.map +1 -1
- package/lib/markdownLinks.js +1 -1
- package/lib/markdownLinks.js.map +1 -1
- package/lib/markdownUtils.d.ts.map +1 -1
- package/lib/markdownUtils.js +58 -20
- package/lib/markdownUtils.js.map +1 -1
- package/lib/moduleUtils.d.ts +3 -1
- package/lib/moduleUtils.d.ts.map +1 -1
- package/lib/moduleUtils.js +25 -14
- package/lib/moduleUtils.js.map +1 -1
- package/lib/urlUtils.d.ts +3 -3
- package/lib/urlUtils.d.ts.map +1 -1
- package/lib/urlUtils.js +20 -25
- package/lib/urlUtils.js.map +1 -1
- package/lib/vcs/gitUtils.d.ts.map +1 -1
- package/lib/vcs/gitUtils.js +27 -60
- package/lib/vcs/gitUtils.js.map +1 -1
- package/lib/vcs/vcsGitEager.d.ts.map +1 -1
- package/lib/vcs/vcsGitEager.js +3 -2
- package/lib/vcs/vcsGitEager.js.map +1 -1
- package/lib/webpackUtils.d.ts.map +1 -1
- package/lib/webpackUtils.js +0 -1
- package/lib/webpackUtils.js.map +1 -1
- package/package.json +23 -25
- package/src/constants.ts +1 -1
- package/src/contentVisibilityUtils.ts +1 -1
- package/src/dataFileUtils.ts +1 -1
- package/src/globUtils.ts +10 -4
- package/src/index.ts +0 -8
- package/src/lastUpdateUtils.ts +2 -2
- package/src/markdownHeadingIdUtils.ts +2 -2
- package/src/markdownLinks.ts +1 -1
- package/src/markdownUtils.ts +83 -20
- package/src/moduleUtils.ts +35 -14
- package/src/urlUtils.ts +25 -29
- package/src/vcs/gitUtils.ts +34 -91
- package/src/vcs/vcsGitEager.ts +4 -2
- package/src/webpackUtils.ts +0 -1
- package/lib/regExpUtils.d.ts +0 -8
- package/lib/regExpUtils.d.ts.map +0 -1
- package/lib/regExpUtils.js +0 -15
- package/lib/regExpUtils.js.map +0 -1
- package/src/regExpUtils.ts +0 -12
package/src/markdownUtils.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import logger from '@docusaurus/logger';
|
|
9
9
|
import matter from '@11ty/gray-matter';
|
|
10
|
+
|
|
10
11
|
import type {
|
|
11
12
|
ParseFrontMatter,
|
|
12
13
|
DefaultParseFrontMatter,
|
|
@@ -16,6 +17,41 @@ import type {
|
|
|
16
17
|
// server-side when we infer metadata like `title` and `description` from the
|
|
17
18
|
// content. Most parsing is still done in MDX through the mdx-loader.
|
|
18
19
|
|
|
20
|
+
const MDXEscapingUtils = (function () {
|
|
21
|
+
const MARKUP_CHARS = ['_', ':', '*', '<', '>', '~', '!', '[', ']', '{', '}'];
|
|
22
|
+
|
|
23
|
+
const markerOf = (char: string) => `\u{FFFE}${char.codePointAt(0)}\u{FFFF}`;
|
|
24
|
+
|
|
25
|
+
const MARKUP_CHARS_ESCAPE_MAP = new Map(
|
|
26
|
+
MARKUP_CHARS.map((char) => [char, markerOf(char)]),
|
|
27
|
+
);
|
|
28
|
+
const MARKUP_CHARS_UNESCAPE_MAP = new Map(
|
|
29
|
+
MARKUP_CHARS.map((char) => [markerOf(char), char]),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const MARKUP_CHARS_REGEX = new RegExp(
|
|
33
|
+
`[${RegExp.escape(MARKUP_CHARS.join(''))}]`,
|
|
34
|
+
'g',
|
|
35
|
+
);
|
|
36
|
+
const MARKUP_MARKERS_REGEX = /\u{FFFE}\d+\u{FFFF}/gu;
|
|
37
|
+
|
|
38
|
+
function escapeMDX(str: string) {
|
|
39
|
+
return str.replace(
|
|
40
|
+
MARKUP_CHARS_REGEX,
|
|
41
|
+
(char) => MARKUP_CHARS_ESCAPE_MAP.get(char)!,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function unescapeMDX(str: string) {
|
|
46
|
+
return str.replace(
|
|
47
|
+
MARKUP_MARKERS_REGEX,
|
|
48
|
+
(marker) => MARKUP_CHARS_UNESCAPE_MAP.get(marker) ?? marker,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {escapeMDX, unescapeMDX};
|
|
53
|
+
})();
|
|
54
|
+
|
|
19
55
|
/**
|
|
20
56
|
* Hacky temporary escape hatch for Crowdin bad MDX support
|
|
21
57
|
* See https://docusaurus.io/docs/i18n/crowdin#mdx
|
|
@@ -92,6 +128,7 @@ export function createExcerpt(fileString: string): string | undefined {
|
|
|
92
128
|
.split(/\r?\n/);
|
|
93
129
|
let inCode = false;
|
|
94
130
|
let inImport = false;
|
|
131
|
+
let inHTML = false;
|
|
95
132
|
let lastCodeFence = '';
|
|
96
133
|
|
|
97
134
|
for (const fileLine of fileLines) {
|
|
@@ -127,11 +164,44 @@ export function createExcerpt(fileString: string): string | undefined {
|
|
|
127
164
|
continue;
|
|
128
165
|
}
|
|
129
166
|
|
|
130
|
-
|
|
167
|
+
// Skip lines inside a multi-line JSX/HTML element. An opening "<Tag ..."
|
|
168
|
+
// with no ">" on the same line would otherwise leak into the excerpt
|
|
169
|
+
// (e.g. "<MyComponent"). Skip until the element's tag closes.
|
|
170
|
+
if (inHTML) {
|
|
171
|
+
if (fileLine.includes('>')) {
|
|
172
|
+
inHTML = false;
|
|
173
|
+
}
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (/^\s*<[a-z][^>]*$/i.test(fileLine)) {
|
|
177
|
+
inHTML = true;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Pre/postprocessing to handle MDX special chars within inline code blocks
|
|
182
|
+
// See https://github.com/facebook/docusaurus/pull/11821
|
|
183
|
+
function preprocessLine(str: string) {
|
|
184
|
+
return (
|
|
185
|
+
str
|
|
186
|
+
// Ignore internal Unicode markers found in input
|
|
187
|
+
// This ensures no possible conflict with our MDX escaping logic
|
|
188
|
+
.replace(/[\u{FFFE}\u{FFFF}]/gu, '')
|
|
189
|
+
// Unwrap inline code and escape special MDX chars within it
|
|
190
|
+
.replace(/`(?<text>.+?)`/g, (_, text) => {
|
|
191
|
+
return MDXEscapingUtils.escapeMDX(text);
|
|
192
|
+
})
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
function postProcessLine(str: string) {
|
|
196
|
+
// Restore escaped MDX chars that have been previously escaped
|
|
197
|
+
return MDXEscapingUtils.unescapeMDX(str);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const cleanedLine = preprocessLine(fileLine)
|
|
131
201
|
// Remove HTML tags.
|
|
132
202
|
.replace(/<[^>]*>/g, '')
|
|
133
203
|
// Remove Title headers
|
|
134
|
-
.replace(/^#
|
|
204
|
+
.replace(/^#(?!#).*/gm, '')
|
|
135
205
|
// Remove Markdown + ATX-style headers
|
|
136
206
|
.replace(/^#{1,6}\s*(?<text>[^#]*?)\s*#{0,6}/gm, '$1')
|
|
137
207
|
// Remove emphasis.
|
|
@@ -144,8 +214,6 @@ export function createExcerpt(fileString: string): string | undefined {
|
|
|
144
214
|
.replace(/\[\^.+?\](?:: .*$)?/g, '')
|
|
145
215
|
// Remove inline links.
|
|
146
216
|
.replace(/\[(?<alt>.*?)\][[(].*?[\])]/g, '$1')
|
|
147
|
-
// Remove inline code.
|
|
148
|
-
.replace(/`(?<text>.+?)`/g, '$1')
|
|
149
217
|
// Remove blockquotes.
|
|
150
218
|
.replace(/^\s{0,3}>\s?/g, '')
|
|
151
219
|
// Remove admonition definition.
|
|
@@ -154,10 +222,12 @@ export function createExcerpt(fileString: string): string | undefined {
|
|
|
154
222
|
.replace(/\s?:(?:::|[^:\n])+:/g, '')
|
|
155
223
|
// Remove custom Markdown heading id.
|
|
156
224
|
.replace(/\{#*[\w-]+\}/, '')
|
|
225
|
+
// Collapse whitespace left behind by the removals above.
|
|
226
|
+
.replace(/\s+/g, ' ')
|
|
157
227
|
.trim();
|
|
158
228
|
|
|
159
229
|
if (cleanedLine) {
|
|
160
|
-
return cleanedLine;
|
|
230
|
+
return postProcessLine(cleanedLine);
|
|
161
231
|
}
|
|
162
232
|
}
|
|
163
233
|
|
|
@@ -191,14 +261,7 @@ export function parseFileContentFrontMatter(fileContent: string): {
|
|
|
191
261
|
// Unfortunately, this becomes a problem when we mutate returned front matter
|
|
192
262
|
// We want to make it possible as part of the parseFrontMatter API
|
|
193
263
|
// So we make it safe to mutate by always providing a deep copy
|
|
194
|
-
const frontMatter =
|
|
195
|
-
// And of course structuredClone() doesn't work well with Date in Jest...
|
|
196
|
-
// See https://github.com/jestjs/jest/issues/2549
|
|
197
|
-
// So we parse again for tests with a {} option object
|
|
198
|
-
// This undocumented empty option object disables gray-matter caching..
|
|
199
|
-
process.env.JEST_WORKER_ID
|
|
200
|
-
? matter(fileContent, {}).data
|
|
201
|
-
: structuredClone(data);
|
|
264
|
+
const frontMatter = structuredClone(data);
|
|
202
265
|
|
|
203
266
|
return {
|
|
204
267
|
frontMatter,
|
|
@@ -247,18 +310,18 @@ export function parseMarkdownContentTitle(
|
|
|
247
310
|
const removeContentTitleOption = options?.removeContentTitle ?? false;
|
|
248
311
|
|
|
249
312
|
const content = contentUntrimmed.trim();
|
|
250
|
-
// We only need to detect import statements that will be parsed by MDX as
|
|
251
|
-
// `import` nodes, as broken syntax can't render anyways. That means any block
|
|
252
|
-
// that has `import` at the very beginning and surrounded by empty lines.
|
|
253
|
-
const
|
|
254
|
-
.replace(/^(?:import\s(?:.|\r?\n(?!\r?\n))*(?:\r?\n){2,})*/, '')
|
|
313
|
+
// We only need to detect import/export statements that will be parsed by MDX as
|
|
314
|
+
// `import` or `export` nodes, as broken syntax can't render anyways. That means any block
|
|
315
|
+
// that has `import` or `export` at the very beginning and surrounded by empty lines.
|
|
316
|
+
const contentWithoutImportExport = content
|
|
317
|
+
.replace(/^(?:(?:import|export)\s(?:.|\r?\n(?!\r?\n))*(?:\r?\n){2,})*/, '')
|
|
255
318
|
.trim();
|
|
256
319
|
|
|
257
320
|
const regularTitleMatch = /^#[ \t]+(?<title>[^ \t].*)(?:\r?\n|$)/.exec(
|
|
258
|
-
|
|
321
|
+
contentWithoutImportExport,
|
|
259
322
|
);
|
|
260
323
|
const alternateTitleMatch = /^(?<title>.*)\r?\n=+(?:\r?\n|$)/.exec(
|
|
261
|
-
|
|
324
|
+
contentWithoutImportExport,
|
|
262
325
|
);
|
|
263
326
|
|
|
264
327
|
const titleMatch = regularTitleMatch ?? alternateTitleMatch;
|
package/src/moduleUtils.ts
CHANGED
|
@@ -5,33 +5,54 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import jiti from 'jiti';
|
|
9
8
|
import logger from '@docusaurus/logger';
|
|
9
|
+
import {createJiti} from 'jiti';
|
|
10
|
+
|
|
11
|
+
const DEBUG = false;
|
|
12
|
+
|
|
13
|
+
const jiti = createJiti(__filename, {
|
|
14
|
+
// Transpilation cache, can be safely enabled
|
|
15
|
+
fsCache: true,
|
|
16
|
+
// Bypass Node.js runtime require cache for hot reloads
|
|
17
|
+
moduleCache: false,
|
|
18
|
+
|
|
19
|
+
interopDefault: true,
|
|
20
|
+
debug: DEBUG,
|
|
21
|
+
});
|
|
10
22
|
|
|
11
23
|
/*
|
|
12
24
|
jiti is able to load ESM, CJS, JSON, TS modules
|
|
13
25
|
*/
|
|
14
|
-
export async function loadFreshModule(
|
|
26
|
+
export async function loadFreshModule(
|
|
27
|
+
modulePath: string,
|
|
28
|
+
options?: {
|
|
29
|
+
default?: true; // Use this when only the default export matters
|
|
30
|
+
},
|
|
31
|
+
): Promise<unknown> {
|
|
15
32
|
if (typeof modulePath !== 'string') {
|
|
16
33
|
throw new Error(
|
|
17
34
|
logger.interpolate`Invalid module path of type "name=${typeof modulePath}" with value "name=${modulePath}"`,
|
|
18
35
|
);
|
|
19
36
|
}
|
|
20
37
|
try {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
cache: true,
|
|
24
|
-
// Bypass Node.js runtime require cache
|
|
25
|
-
// Same as "import-fresh" package we used previously
|
|
26
|
-
requireCache: false,
|
|
27
|
-
// Only take into consideration the default export
|
|
28
|
-
// For now we don't need named exports
|
|
29
|
-
// This also helps normalize return value for both CJS/ESM/TS modules
|
|
30
|
-
interopDefault: true,
|
|
31
|
-
// debug: true,
|
|
38
|
+
const module = await jiti.import(modulePath, {
|
|
39
|
+
default: options?.default,
|
|
32
40
|
});
|
|
33
41
|
|
|
34
|
-
|
|
42
|
+
if (DEBUG) {
|
|
43
|
+
console.log('Jiti module loaded', {
|
|
44
|
+
modulePath,
|
|
45
|
+
options,
|
|
46
|
+
type: typeof module,
|
|
47
|
+
keys:
|
|
48
|
+
module && typeof module === 'object'
|
|
49
|
+
? Object.keys(module)
|
|
50
|
+
: undefined,
|
|
51
|
+
module,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return module;
|
|
35
56
|
} catch (error) {
|
|
36
57
|
throw new Error(
|
|
37
58
|
logger.interpolate`Docusaurus could not load module at path path=${modulePath}`,
|
package/src/urlUtils.ts
CHANGED
|
@@ -138,8 +138,8 @@ export function fileToPath(file: string): string {
|
|
|
138
138
|
* Similar to `encodeURI`, but uses `encodeURIComponent` and assumes there's no
|
|
139
139
|
* query.
|
|
140
140
|
*
|
|
141
|
-
* `encodeURI("/question?/answer")` => `"/question?/answer
|
|
142
|
-
* `encodePath("/question?/answer#section")` => `"/question%3F/answer%
|
|
141
|
+
* `encodeURI("/question?/answer")` => `"/question?/answer"`;
|
|
142
|
+
* `encodePath("/question?/answer#section")` => `"/question%3F/answer%23section"`
|
|
143
143
|
*/
|
|
144
144
|
export function encodePath(userPath: string): string {
|
|
145
145
|
return userPath
|
|
@@ -156,26 +156,20 @@ export function isValidPathname(str: string): boolean {
|
|
|
156
156
|
if (!str.startsWith('/')) {
|
|
157
157
|
return false;
|
|
158
158
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return parsedPathname === str || parsedPathname === encodeURI(str);
|
|
162
|
-
} catch {
|
|
159
|
+
const url = URL.parse(str, 'https://domain.com');
|
|
160
|
+
if (url === null) {
|
|
163
161
|
return false;
|
|
164
162
|
}
|
|
163
|
+
const parsedPathname = url.pathname;
|
|
164
|
+
return parsedPathname === str || parsedPathname === encodeURI(str);
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
export function parseURLOrPath(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
// see https://kilianvalkhof.com/2024/javascript/the-problem-with-new-url-and-how-url-parse-fixes-that/
|
|
172
|
-
return new URL(url, base ?? 'https://example.com');
|
|
173
|
-
} catch (e) {
|
|
174
|
-
throw new Error(
|
|
175
|
-
`Can't parse URL ${url}${base ? ` with base ${base}` : ''}`,
|
|
176
|
-
{cause: e},
|
|
177
|
-
);
|
|
167
|
+
export function parseURLOrPath(str: string, base?: string | URL): URL {
|
|
168
|
+
const url = URL.parse(str, base ?? 'https://example.com');
|
|
169
|
+
if (url) {
|
|
170
|
+
return url;
|
|
178
171
|
}
|
|
172
|
+
throw new Error(`Can't parse URL ${str}${base ? ` with base ${base}` : ''}`);
|
|
179
173
|
}
|
|
180
174
|
|
|
181
175
|
export type URLPath = {pathname: string; search?: string; hash?: string};
|
|
@@ -183,15 +177,19 @@ export type URLPath = {pathname: string; search?: string; hash?: string};
|
|
|
183
177
|
export function toURLPath(url: URL): URLPath {
|
|
184
178
|
const {pathname} = url;
|
|
185
179
|
|
|
180
|
+
// Only the part before the fragment can contain the query string. A "?"
|
|
181
|
+
// inside the hash (e.g. "/foo#bar?baz") must not be read as an empty query.
|
|
182
|
+
const beforeHash = url.hash ? url.href.slice(0, -url.hash.length) : url.href;
|
|
183
|
+
|
|
186
184
|
// Fixes annoying url.search behavior
|
|
187
185
|
// "" => undefined
|
|
188
186
|
// "?" => ""
|
|
189
187
|
// "?param => "param"
|
|
190
188
|
const search = url.search
|
|
191
189
|
? url.search.slice(1)
|
|
192
|
-
:
|
|
193
|
-
|
|
194
|
-
|
|
190
|
+
: beforeHash.includes('?')
|
|
191
|
+
? ''
|
|
192
|
+
: undefined;
|
|
195
193
|
|
|
196
194
|
// Fixes annoying url.hash behavior
|
|
197
195
|
// "" => undefined
|
|
@@ -200,8 +198,8 @@ export function toURLPath(url: URL): URLPath {
|
|
|
200
198
|
const hash = url.hash
|
|
201
199
|
? url.hash.slice(1)
|
|
202
200
|
: url.href.includes('#')
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
? ''
|
|
202
|
+
: undefined;
|
|
205
203
|
|
|
206
204
|
return {
|
|
207
205
|
pathname,
|
|
@@ -319,13 +317,11 @@ export function buildHttpsUrl(
|
|
|
319
317
|
* `git@github.com:facebook/docusaurus.git`.
|
|
320
318
|
*/
|
|
321
319
|
export function hasSSHProtocol(sourceRepoUrl: string): boolean {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
return false;
|
|
327
|
-
} catch {
|
|
328
|
-
// Fails when there isn't a protocol
|
|
320
|
+
const url = URL.parse(sourceRepoUrl);
|
|
321
|
+
if (url === null) {
|
|
322
|
+
// Recognizes SCP-style addresses, implying SSH
|
|
323
|
+
// Example: git@github.com:facebook/docusaurus.git
|
|
329
324
|
return /^(?:[\w-]+@)?[\w.-]+:[\w./-]+/.test(sourceRepoUrl);
|
|
330
325
|
}
|
|
326
|
+
return url.protocol === 'ssh:';
|
|
331
327
|
}
|
package/src/vcs/gitUtils.ts
CHANGED
|
@@ -9,17 +9,13 @@ import path from 'path';
|
|
|
9
9
|
import fs from 'fs-extra';
|
|
10
10
|
import os from 'os';
|
|
11
11
|
import _ from 'lodash';
|
|
12
|
-
import execa from 'execa';
|
|
12
|
+
import {execa, execaSync} from 'execa';
|
|
13
13
|
import PQueue from 'p-queue';
|
|
14
14
|
import logger from '@docusaurus/logger';
|
|
15
15
|
|
|
16
16
|
// Quite high/conservative concurrency value (it was previously "Infinity")
|
|
17
17
|
// See https://github.com/facebook/docusaurus/pull/10915
|
|
18
|
-
const DefaultGitCommandConcurrency =
|
|
19
|
-
// TODO Docusaurus v4: bump node, availableParallelism() now always exists
|
|
20
|
-
(typeof os.availableParallelism === 'function'
|
|
21
|
-
? os.availableParallelism()
|
|
22
|
-
: os.cpus().length) * 4;
|
|
18
|
+
const DefaultGitCommandConcurrency = os.availableParallelism() * 4;
|
|
23
19
|
|
|
24
20
|
const GitCommandConcurrencyEnv = process.env.DOCUSAURUS_GIT_COMMAND_CONCURRENCY
|
|
25
21
|
? parseInt(process.env.DOCUSAURUS_GIT_COMMAND_CONCURRENCY, 10)
|
|
@@ -38,8 +34,8 @@ const GitCommandQueue = new PQueue({
|
|
|
38
34
|
|
|
39
35
|
const realHasGitFn = () => {
|
|
40
36
|
try {
|
|
41
|
-
return
|
|
42
|
-
} catch
|
|
37
|
+
return execaSync('git', ['--version']).exitCode === 0;
|
|
38
|
+
} catch {
|
|
43
39
|
return false;
|
|
44
40
|
}
|
|
45
41
|
};
|
|
@@ -147,32 +143,26 @@ export async function getFileCommitDate(
|
|
|
147
143
|
// See why: https://github.com/facebook/docusaurus/pull/10022
|
|
148
144
|
const resultFormat = includeAuthor ? 'RESULT:%ct,%an' : 'RESULT:%ct';
|
|
149
145
|
|
|
150
|
-
const args = [
|
|
151
|
-
`--format=${resultFormat}`,
|
|
152
|
-
'--max-count=1',
|
|
153
|
-
age === 'oldest' ? '--follow --diff-filter=A' : undefined,
|
|
154
|
-
]
|
|
155
|
-
.filter(Boolean)
|
|
156
|
-
.join(' ');
|
|
157
|
-
|
|
158
|
-
// Do not include GPG signature in the log output
|
|
159
|
-
// See https://github.com/facebook/docusaurus/pull/10022
|
|
160
|
-
const command = `git -c log.showSignature=false log ${args} -- "${path.basename(
|
|
161
|
-
file,
|
|
162
|
-
)}"`;
|
|
163
|
-
|
|
164
146
|
const result = (await GitCommandQueue.add(() => {
|
|
165
|
-
return execa(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
147
|
+
return execa(
|
|
148
|
+
'git',
|
|
149
|
+
[
|
|
150
|
+
// Do not include GPG signature in the log output
|
|
151
|
+
// See https://github.com/facebook/docusaurus/pull/10022
|
|
152
|
+
'-c',
|
|
153
|
+
'log.showSignature=false',
|
|
154
|
+
'log',
|
|
155
|
+
`--format=${resultFormat}`,
|
|
156
|
+
'--max-count=1',
|
|
157
|
+
...(age === 'oldest' ? ['--follow', '--diff-filter=A'] : []),
|
|
158
|
+
'--',
|
|
159
|
+
path.basename(file),
|
|
160
|
+
],
|
|
161
|
+
{
|
|
162
|
+
cwd: path.dirname(file),
|
|
163
|
+
},
|
|
174
164
|
);
|
|
175
|
-
}
|
|
165
|
+
}))!;
|
|
176
166
|
|
|
177
167
|
// We only parse the output line starting with our "RESULT:" prefix
|
|
178
168
|
// See why https://github.com/facebook/docusaurus/pull/10022
|
|
@@ -280,33 +270,20 @@ export async function isGitInsideWorktree(cwd: string): Promise<boolean> {
|
|
|
280
270
|
}
|
|
281
271
|
|
|
282
272
|
export async function getGitRepoRoot(cwd: string): Promise<string> {
|
|
283
|
-
const createErrorMessageBase = () => {
|
|
284
|
-
return `Couldn't find the git repository root directory
|
|
285
|
-
Failure while running ${logger.code(
|
|
286
|
-
'git rev-parse --show-toplevel',
|
|
287
|
-
)} from cwd=${logger.path(cwd)}`;
|
|
288
|
-
};
|
|
289
|
-
|
|
290
273
|
const result = await execa('git', ['rev-parse', '--show-toplevel'], {
|
|
291
274
|
cwd,
|
|
292
275
|
}).catch((error) => {
|
|
293
276
|
// We enter this rejection when cwd is not a dir for example
|
|
294
277
|
throw new Error(
|
|
295
|
-
|
|
278
|
+
`Couldn't find the git repository root directory
|
|
279
|
+
Failure while running ${logger.code(
|
|
280
|
+
'git rev-parse --show-toplevel',
|
|
281
|
+
)} from cwd=${logger.path(cwd)}
|
|
296
282
|
The command executed throws an error: ${error.message}`,
|
|
297
283
|
{cause: error},
|
|
298
284
|
);
|
|
299
285
|
});
|
|
300
286
|
|
|
301
|
-
if (result.exitCode !== 0) {
|
|
302
|
-
throw new Error(
|
|
303
|
-
`${createErrorMessageBase()}
|
|
304
|
-
The command returned exit code ${logger.code(result.exitCode)}: ${logger.subdue(
|
|
305
|
-
result.stderr,
|
|
306
|
-
)}`,
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
287
|
return fs.realpath.native(result.stdout.trim());
|
|
311
288
|
}
|
|
312
289
|
|
|
@@ -316,13 +293,6 @@ The command returned exit code ${logger.code(result.exitCode)}: ${logger.subdue(
|
|
|
316
293
|
export async function getGitSuperProjectRoot(
|
|
317
294
|
cwd: string,
|
|
318
295
|
): Promise<string | null> {
|
|
319
|
-
const createErrorMessageBase = () => {
|
|
320
|
-
return `Couldn't find the git superproject root directory
|
|
321
|
-
Failure while running ${logger.code(
|
|
322
|
-
'git rev-parse --show-superproject-working-tree',
|
|
323
|
-
)} from cwd=${logger.path(cwd)}`;
|
|
324
|
-
};
|
|
325
|
-
|
|
326
296
|
const result = await execa(
|
|
327
297
|
'git',
|
|
328
298
|
['rev-parse', '--show-superproject-working-tree'],
|
|
@@ -332,21 +302,15 @@ Failure while running ${logger.code(
|
|
|
332
302
|
).catch((error) => {
|
|
333
303
|
// We enter this rejection when cwd is not a dir for example
|
|
334
304
|
throw new Error(
|
|
335
|
-
|
|
305
|
+
`Couldn't find the git superproject root directory
|
|
306
|
+
Failure while running ${logger.code(
|
|
307
|
+
'git rev-parse --show-superproject-working-tree',
|
|
308
|
+
)} from cwd=${logger.path(cwd)}
|
|
336
309
|
The command executed throws an error: ${error.message}`,
|
|
337
310
|
{cause: error},
|
|
338
311
|
);
|
|
339
312
|
});
|
|
340
313
|
|
|
341
|
-
if (result.exitCode !== 0) {
|
|
342
|
-
throw new Error(
|
|
343
|
-
`${createErrorMessageBase()}
|
|
344
|
-
The command returned exit code ${logger.code(result.exitCode)}: ${logger.subdue(
|
|
345
|
-
result.stderr,
|
|
346
|
-
)}`,
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
314
|
const output = result.stdout.trim();
|
|
351
315
|
// this command only works when inside submodules
|
|
352
316
|
// otherwise it doesn't return anything when we are inside the main repo
|
|
@@ -358,33 +322,20 @@ The command returned exit code ${logger.code(result.exitCode)}: ${logger.subdue(
|
|
|
358
322
|
|
|
359
323
|
// See https://git-scm.com/book/en/v2/Git-Tools-Submodules
|
|
360
324
|
export async function getGitSubmodulePaths(cwd: string): Promise<string[]> {
|
|
361
|
-
const createErrorMessageBase = () => {
|
|
362
|
-
return `Couldn't read the list of git submodules
|
|
363
|
-
Failure while running ${logger.code(
|
|
364
|
-
'git submodule status',
|
|
365
|
-
)} from cwd=${logger.path(cwd)}`;
|
|
366
|
-
};
|
|
367
|
-
|
|
368
325
|
const result = await execa('git', ['submodule', 'status'], {
|
|
369
326
|
cwd,
|
|
370
327
|
}).catch((error) => {
|
|
371
328
|
// We enter this rejection when cwd is not a dir for example
|
|
372
329
|
throw new Error(
|
|
373
|
-
|
|
330
|
+
`Couldn't read the list of git submodules
|
|
331
|
+
Failure while running ${logger.code(
|
|
332
|
+
'git submodule status',
|
|
333
|
+
)} from cwd=${logger.path(cwd)}
|
|
374
334
|
The command executed throws an error: ${error.message}`,
|
|
375
335
|
{cause: error},
|
|
376
336
|
);
|
|
377
337
|
});
|
|
378
338
|
|
|
379
|
-
if (result.exitCode !== 0) {
|
|
380
|
-
throw new Error(
|
|
381
|
-
`${createErrorMessageBase()}
|
|
382
|
-
The command returned exit code ${logger.code(result.exitCode)}: ${logger.subdue(
|
|
383
|
-
result.stderr,
|
|
384
|
-
)}`,
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
339
|
const output = result.stdout.trim();
|
|
389
340
|
|
|
390
341
|
if (!output) {
|
|
@@ -465,20 +416,12 @@ export async function getGitRepositoryFilesInfo(
|
|
|
465
416
|
],
|
|
466
417
|
{
|
|
467
418
|
cwd,
|
|
468
|
-
encoding: 'utf-8',
|
|
469
419
|
// TODO use streaming to avoid a large buffer
|
|
470
420
|
// See https://github.com/withastro/starlight/issues/3154
|
|
471
421
|
maxBuffer: 20 * 1024 * 1024,
|
|
472
422
|
},
|
|
473
423
|
);
|
|
474
424
|
|
|
475
|
-
if (result.exitCode !== 0) {
|
|
476
|
-
throw new Error(
|
|
477
|
-
`Docusaurus failed to run the 'git log' to retrieve tracked files last update date/author.
|
|
478
|
-
The command exited with code ${result.exitCode}: ${result.stderr}`,
|
|
479
|
-
);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
425
|
const logLines = result.stdout.split('\n');
|
|
483
426
|
|
|
484
427
|
const now = Date.now();
|
package/src/vcs/vcsGitEager.ts
CHANGED
|
@@ -56,6 +56,7 @@ type InitializeResult =
|
|
|
56
56
|
| {
|
|
57
57
|
type: 'success';
|
|
58
58
|
filesMap: GitFileInfoMap;
|
|
59
|
+
siteDir: string;
|
|
59
60
|
}
|
|
60
61
|
| {
|
|
61
62
|
type: 'error';
|
|
@@ -74,7 +75,7 @@ async function initialize({
|
|
|
74
75
|
return {type: 'error', reason: 'not-in-worktree'};
|
|
75
76
|
}
|
|
76
77
|
const filesMap = await loadAllGitFilesInfoMap(siteDir);
|
|
77
|
-
return {type: 'success', filesMap};
|
|
78
|
+
return {type: 'success', filesMap, siteDir};
|
|
78
79
|
} catch (error) {
|
|
79
80
|
return {type: 'error', reason: 'unknown', cause: error as Error};
|
|
80
81
|
}
|
|
@@ -86,7 +87,8 @@ export function createVcsGitEagerConfig(): VcsConfig {
|
|
|
86
87
|
async function getGitFileInfo(filePath: string): Promise<GitFileInfo | null> {
|
|
87
88
|
const init = (await initPromise)!;
|
|
88
89
|
if (init.type === 'success') {
|
|
89
|
-
|
|
90
|
+
const key = resolve(init.siteDir, filePath);
|
|
91
|
+
return init.filesMap.get(key) ?? null;
|
|
90
92
|
} else if (init.reason === 'not-in-worktree') {
|
|
91
93
|
throw new Error(
|
|
92
94
|
`This Docusaurus site is outside any Git worktree.
|
package/src/webpackUtils.ts
CHANGED
|
@@ -18,7 +18,6 @@ export type WebpackCompilerName = 'server' | 'client';
|
|
|
18
18
|
export function getWebpackLoaderCompilerName(
|
|
19
19
|
context: LoaderContext<unknown>,
|
|
20
20
|
): WebpackCompilerName {
|
|
21
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
22
21
|
const compilerName = context._compiler?.name;
|
|
23
22
|
switch (compilerName) {
|
|
24
23
|
case 'server':
|
package/lib/regExpUtils.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/
|
|
7
|
-
export declare function escapeRegexp(string: string): string;
|
|
8
|
-
//# sourceMappingURL=regExpUtils.d.ts.map
|
package/lib/regExpUtils.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"regExpUtils.d.ts","sourceRoot":"","sources":["../src/regExpUtils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD"}
|
package/lib/regExpUtils.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.escapeRegexp = escapeRegexp;
|
|
10
|
-
const tslib_1 = require("tslib");
|
|
11
|
-
const escape_string_regexp_1 = tslib_1.__importDefault(require("escape-string-regexp"));
|
|
12
|
-
function escapeRegexp(string) {
|
|
13
|
-
return (0, escape_string_regexp_1.default)(string);
|
|
14
|
-
}
|
|
15
|
-
//# sourceMappingURL=regExpUtils.js.map
|
package/lib/regExpUtils.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"regExpUtils.js","sourceRoot":"","sources":["../src/regExpUtils.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAIH,oCAEC;;AAJD,wFAAsD;AAEtD,SAAgB,YAAY,CAAC,MAAc;IACzC,OAAO,IAAA,8BAAkB,EAAC,MAAM,CAAC,CAAC;AACpC,CAAC"}
|
package/src/regExpUtils.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import escapeStringRegexp from 'escape-string-regexp';
|
|
9
|
-
|
|
10
|
-
export function escapeRegexp(string: string): string {
|
|
11
|
-
return escapeStringRegexp(string);
|
|
12
|
-
}
|