@docusaurus/utils 2.0.0-beta.14 → 2.0.0-beta.15
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/dataFileUtils.d.ts +24 -0
- package/lib/dataFileUtils.d.ts.map +1 -0
- package/lib/dataFileUtils.js +65 -0
- package/lib/dataFileUtils.js.map +1 -0
- package/lib/globUtils.d.ts.map +1 -1
- package/lib/globUtils.js +2 -2
- package/lib/globUtils.js.map +1 -1
- package/lib/index.d.ts +18 -41
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +40 -139
- package/lib/index.js.map +1 -1
- package/lib/markdownLinks.d.ts.map +1 -1
- package/lib/markdownLinks.js +12 -4
- package/lib/markdownLinks.js.map +1 -1
- package/lib/markdownParser.d.ts +4 -3
- package/lib/markdownParser.d.ts.map +1 -1
- package/lib/markdownParser.js +30 -18
- package/lib/markdownParser.js.map +1 -1
- package/lib/pathUtils.d.ts +29 -0
- package/lib/pathUtils.d.ts.map +1 -1
- package/lib/pathUtils.js +69 -5
- package/lib/pathUtils.js.map +1 -1
- package/lib/tags.d.ts +1 -1
- package/lib/tags.d.ts.map +1 -1
- package/lib/tags.js +4 -5
- package/lib/tags.js.map +1 -1
- package/lib/{normalizeUrl.d.ts → urlUtils.d.ts} +2 -1
- package/lib/urlUtils.d.ts.map +1 -0
- package/lib/{normalizeUrl.js → urlUtils.js} +17 -4
- package/lib/{normalizeUrl.js.map → urlUtils.js.map} +1 -1
- package/lib/webpackUtils.js +3 -3
- package/lib/webpackUtils.js.map +1 -1
- package/package.json +6 -5
- package/src/dataFileUtils.ts +90 -0
- package/src/globUtils.ts +2 -1
- package/src/index.ts +48 -173
- package/src/markdownLinks.ts +10 -3
- package/src/markdownParser.ts +32 -20
- package/src/pathUtils.ts +70 -4
- package/src/tags.ts +5 -4
- package/src/{normalizeUrl.ts → urlUtils.ts} +17 -2
- package/src/webpackUtils.ts +4 -4
- package/lib/escapePath.d.ts +0 -18
- package/lib/escapePath.d.ts.map +0 -1
- package/lib/escapePath.js +0 -26
- package/lib/escapePath.js.map +0 -1
- package/lib/mdxUtils.d.ts +0 -17
- package/lib/mdxUtils.d.ts.map +0 -1
- package/lib/mdxUtils.js +0 -31
- package/lib/mdxUtils.js.map +0 -1
- package/lib/normalizeUrl.d.ts.map +0 -1
- package/lib/posixPath.d.ts +0 -15
- package/lib/posixPath.d.ts.map +0 -1
- package/lib/posixPath.js +0 -29
- package/lib/posixPath.js.map +0 -1
- package/src/escapePath.ts +0 -23
- package/src/mdxUtils.ts +0 -32
- package/src/posixPath.ts +0 -27
package/src/index.ts
CHANGED
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
import logger from '@docusaurus/logger';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import {createHash} from 'crypto';
|
|
11
|
-
import {
|
|
12
|
-
import escapeStringRegexp from 'escape-string-regexp';
|
|
11
|
+
import {mapValues} from 'lodash';
|
|
13
12
|
import fs from 'fs-extra';
|
|
14
13
|
import {URL} from 'url';
|
|
15
|
-
import {
|
|
14
|
+
import type {
|
|
16
15
|
ReportingSeverity,
|
|
17
16
|
TranslationFileContent,
|
|
18
17
|
TranslationFile,
|
|
@@ -20,32 +19,22 @@ import {
|
|
|
20
19
|
|
|
21
20
|
import resolvePathnameUnsafe from 'resolve-pathname';
|
|
22
21
|
|
|
23
|
-
import {posixPath as posixPathImport} from './posixPath';
|
|
24
22
|
import {simpleHash, docuHash} from './hashUtils';
|
|
25
|
-
import {normalizeUrl} from './normalizeUrl';
|
|
26
23
|
import {DEFAULT_PLUGIN_ID} from './constants';
|
|
27
24
|
|
|
28
25
|
export * from './constants';
|
|
29
|
-
export * from './
|
|
30
|
-
export * from './normalizeUrl';
|
|
26
|
+
export * from './urlUtils';
|
|
31
27
|
export * from './tags';
|
|
32
|
-
|
|
33
|
-
export const posixPath = posixPathImport;
|
|
34
|
-
|
|
35
28
|
export * from './markdownParser';
|
|
36
29
|
export * from './markdownLinks';
|
|
37
|
-
export * from './escapePath';
|
|
38
30
|
export * from './slugger';
|
|
39
|
-
export
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
GlobExcludeDefault,
|
|
43
|
-
createMatcher,
|
|
44
|
-
createAbsoluteFilePathMatcher,
|
|
45
|
-
} from './globUtils';
|
|
31
|
+
export * from './pathUtils';
|
|
32
|
+
export * from './hashUtils';
|
|
33
|
+
export * from './globUtils';
|
|
46
34
|
export * from './webpackUtils';
|
|
35
|
+
export * from './dataFileUtils';
|
|
47
36
|
|
|
48
|
-
const fileHash = new Map();
|
|
37
|
+
const fileHash = new Map<string, string>();
|
|
49
38
|
export async function generate(
|
|
50
39
|
generatedFilesDir: string,
|
|
51
40
|
file: string,
|
|
@@ -80,18 +69,6 @@ export async function generate(
|
|
|
80
69
|
}
|
|
81
70
|
}
|
|
82
71
|
|
|
83
|
-
export function objectWithKeySorted<T>(
|
|
84
|
-
obj: Record<string, T>,
|
|
85
|
-
): Record<string, T> {
|
|
86
|
-
// https://github.com/lodash/lodash/issues/1459#issuecomment-460941233
|
|
87
|
-
return Object.keys(obj)
|
|
88
|
-
.sort()
|
|
89
|
-
.reduce((acc: Record<string, T>, key: string) => {
|
|
90
|
-
acc[key] = obj[key];
|
|
91
|
-
return acc;
|
|
92
|
-
}, {});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
72
|
const indexRE = /(^|.*\/)index\.(md|mdx|js|jsx|ts|tsx)$/i;
|
|
96
73
|
const extRE = /\.(md|mdx|js|jsx|ts|tsx)$/;
|
|
97
74
|
|
|
@@ -106,44 +83,13 @@ export function fileToPath(file: string): string {
|
|
|
106
83
|
return `/${file.replace(extRE, '').replace(/\\/g, '/')}`;
|
|
107
84
|
}
|
|
108
85
|
|
|
109
|
-
export function encodePath(
|
|
110
|
-
return
|
|
86
|
+
export function encodePath(userPath: string): string {
|
|
87
|
+
return userPath
|
|
111
88
|
.split('/')
|
|
112
89
|
.map((item) => encodeURIComponent(item))
|
|
113
90
|
.join('/');
|
|
114
91
|
}
|
|
115
92
|
|
|
116
|
-
/**
|
|
117
|
-
* Convert first string character to the upper case.
|
|
118
|
-
* E.g: docusaurus -> Docusaurus
|
|
119
|
-
*/
|
|
120
|
-
export function upperFirst(str: string): string {
|
|
121
|
-
return str ? str.charAt(0).toUpperCase() + str.slice(1) : '';
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Generate unique React Component Name.
|
|
126
|
-
* E.g: /foo-bar -> FooBar096
|
|
127
|
-
*/
|
|
128
|
-
export function genComponentName(pagePath: string): string {
|
|
129
|
-
if (pagePath === '/') {
|
|
130
|
-
return 'index';
|
|
131
|
-
}
|
|
132
|
-
const pageHash = docuHash(pagePath);
|
|
133
|
-
return upperFirst(camelCase(pageHash));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// When you want to display a path in a message/warning/error,
|
|
137
|
-
// it's more convenient to:
|
|
138
|
-
// - make it relative to cwd()
|
|
139
|
-
// - convert to posix (ie not using windows \ path separator)
|
|
140
|
-
// This way, Jest tests can run more reliably on any computer/CI
|
|
141
|
-
// on both Unix/Windows
|
|
142
|
-
// For Windows users this is not perfect (as they see / instead of \) but it's probably good enough
|
|
143
|
-
export function toMessageRelativeFilePath(filePath: string): string {
|
|
144
|
-
return posixPath(path.relative(process.cwd(), filePath));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
93
|
const chunkNameCache = new Map();
|
|
148
94
|
/**
|
|
149
95
|
* Generate unique chunk name given a module path.
|
|
@@ -172,52 +118,6 @@ export function genChunkName(
|
|
|
172
118
|
return chunkName;
|
|
173
119
|
}
|
|
174
120
|
|
|
175
|
-
// Too dynamic
|
|
176
|
-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
|
177
|
-
export function idx(target: any, keyPaths?: string | (string | number)[]): any {
|
|
178
|
-
return (
|
|
179
|
-
target &&
|
|
180
|
-
keyPaths &&
|
|
181
|
-
(Array.isArray(keyPaths)
|
|
182
|
-
? keyPaths.reduce((obj, key) => obj && obj[key], target)
|
|
183
|
-
: target[keyPaths])
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Given a filepath and dirpath, get the first directory.
|
|
189
|
-
*/
|
|
190
|
-
export function getSubFolder(file: string, refDir: string): string | null {
|
|
191
|
-
const separator = escapeStringRegexp(path.sep);
|
|
192
|
-
const baseDir = escapeStringRegexp(path.basename(refDir));
|
|
193
|
-
const regexSubFolder = new RegExp(
|
|
194
|
-
`${baseDir}${separator}(.*?)${separator}.*`,
|
|
195
|
-
);
|
|
196
|
-
const match = regexSubFolder.exec(file);
|
|
197
|
-
return match && match[1];
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Alias filepath relative to site directory, very useful so that we
|
|
202
|
-
* don't expose user's site structure.
|
|
203
|
-
* Example: some/path/to/website/docs/foo.md -> @site/docs/foo.md
|
|
204
|
-
*/
|
|
205
|
-
export function aliasedSitePath(filePath: string, siteDir: string): string {
|
|
206
|
-
const relativePath = posixPath(path.relative(siteDir, filePath));
|
|
207
|
-
// Cannot use path.join() as it resolves '../' and removes
|
|
208
|
-
// the '@site'. Let webpack loader resolve it.
|
|
209
|
-
return `@site/${relativePath}`;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export function getEditUrl(
|
|
213
|
-
fileRelativePath: string,
|
|
214
|
-
editUrl?: string,
|
|
215
|
-
): string | undefined {
|
|
216
|
-
return editUrl
|
|
217
|
-
? normalizeUrl([editUrl, posixPath(fileRelativePath)])
|
|
218
|
-
: undefined;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
121
|
export function isValidPathname(str: string): boolean {
|
|
222
122
|
if (!str.startsWith('/')) {
|
|
223
123
|
return false;
|
|
@@ -240,7 +140,10 @@ export function addLeadingSlash(str: string): string {
|
|
|
240
140
|
}
|
|
241
141
|
|
|
242
142
|
export function addTrailingPathSeparator(str: string): string {
|
|
243
|
-
return str.endsWith(path.sep)
|
|
143
|
+
return str.endsWith(path.sep)
|
|
144
|
+
? str
|
|
145
|
+
: // If this is Windows, we need to change the forward slash to backward
|
|
146
|
+
`${str.replace(/\/$/, '')}${path.sep}`;
|
|
244
147
|
}
|
|
245
148
|
|
|
246
149
|
// TODO deduplicate: also present in @docusaurus/utils-common
|
|
@@ -306,7 +209,40 @@ export function getPluginI18nPath({
|
|
|
306
209
|
);
|
|
307
210
|
}
|
|
308
211
|
|
|
309
|
-
|
|
212
|
+
/**
|
|
213
|
+
* @param permalink The URL that the HTML file corresponds to, without base URL
|
|
214
|
+
* @param outDir Full path to the output directory
|
|
215
|
+
* @param trailingSlash The site config option. If provided, only one path will be read.
|
|
216
|
+
* @returns This returns a buffer, which you have to decode string yourself if
|
|
217
|
+
* needed. (Not always necessary since the output isn't for human consumption
|
|
218
|
+
* anyways, and most HTML manipulation libs accept buffers)
|
|
219
|
+
*/
|
|
220
|
+
export async function readOutputHTMLFile(
|
|
221
|
+
permalink: string,
|
|
222
|
+
outDir: string,
|
|
223
|
+
trailingSlash: boolean | undefined,
|
|
224
|
+
): Promise<Buffer> {
|
|
225
|
+
const withTrailingSlashPath = path.join(outDir, permalink, 'index.html');
|
|
226
|
+
const withoutTrailingSlashPath = path.join(outDir, `${permalink}.html`);
|
|
227
|
+
if (trailingSlash) {
|
|
228
|
+
return fs.readFile(withTrailingSlashPath);
|
|
229
|
+
} else if (trailingSlash === false) {
|
|
230
|
+
return fs.readFile(withoutTrailingSlashPath);
|
|
231
|
+
} else {
|
|
232
|
+
const HTMLPath = await findAsyncSequential(
|
|
233
|
+
[withTrailingSlashPath, withoutTrailingSlashPath],
|
|
234
|
+
fs.pathExists,
|
|
235
|
+
);
|
|
236
|
+
if (!HTMLPath) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
`Expected output HTML file to be found at ${withTrailingSlashPath}`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
return fs.readFile(HTMLPath);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export async function mapAsyncSequential<T, R>(
|
|
310
246
|
array: T[],
|
|
311
247
|
action: (t: T) => Promise<R>,
|
|
312
248
|
): Promise<R[]> {
|
|
@@ -332,35 +268,6 @@ export async function findAsyncSequential<T>(
|
|
|
332
268
|
return undefined;
|
|
333
269
|
}
|
|
334
270
|
|
|
335
|
-
// return the first folder path in which the file exists in
|
|
336
|
-
export async function findFolderContainingFile(
|
|
337
|
-
folderPaths: string[],
|
|
338
|
-
relativeFilePath: string,
|
|
339
|
-
): Promise<string | undefined> {
|
|
340
|
-
return findAsyncSequential(folderPaths, (folderPath) =>
|
|
341
|
-
fs.pathExists(path.join(folderPath, relativeFilePath)),
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
export async function getFolderContainingFile(
|
|
346
|
-
folderPaths: string[],
|
|
347
|
-
relativeFilePath: string,
|
|
348
|
-
): Promise<string> {
|
|
349
|
-
const maybeFolderPath = await findFolderContainingFile(
|
|
350
|
-
folderPaths,
|
|
351
|
-
relativeFilePath,
|
|
352
|
-
);
|
|
353
|
-
// should never happen, as the source was read from the FS anyway...
|
|
354
|
-
if (!maybeFolderPath) {
|
|
355
|
-
throw new Error(
|
|
356
|
-
`File "${relativeFilePath}" does not exist in any of these folders:\n- ${folderPaths.join(
|
|
357
|
-
'\n- ',
|
|
358
|
-
)}]`,
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
return maybeFolderPath;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
271
|
export function reportMessage(
|
|
365
272
|
message: string,
|
|
366
273
|
reportingSeverity: ReportingSeverity,
|
|
@@ -392,20 +299,6 @@ export function mergeTranslations(
|
|
|
392
299
|
return contents.reduce((acc, content) => ({...acc, ...content}), {});
|
|
393
300
|
}
|
|
394
301
|
|
|
395
|
-
export function getSwizzledComponent(
|
|
396
|
-
componentPath: string,
|
|
397
|
-
): string | undefined {
|
|
398
|
-
const swizzledComponentPath = path.resolve(
|
|
399
|
-
process.cwd(),
|
|
400
|
-
'src',
|
|
401
|
-
componentPath,
|
|
402
|
-
);
|
|
403
|
-
|
|
404
|
-
return fs.existsSync(swizzledComponentPath)
|
|
405
|
-
? swizzledComponentPath
|
|
406
|
-
: undefined;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
302
|
// Useful to update all the messages of a translation file
|
|
410
303
|
// Used in tests to simulate translations
|
|
411
304
|
export function updateTranslationFileMessages(
|
|
@@ -420,21 +313,3 @@ export function updateTranslationFileMessages(
|
|
|
420
313
|
})),
|
|
421
314
|
};
|
|
422
315
|
}
|
|
423
|
-
|
|
424
|
-
// Input: ## Some heading {#some-heading}
|
|
425
|
-
// Output: {text: "## Some heading", id: "some-heading"}
|
|
426
|
-
export function parseMarkdownHeadingId(heading: string): {
|
|
427
|
-
text: string;
|
|
428
|
-
id?: string;
|
|
429
|
-
} {
|
|
430
|
-
const customHeadingIdRegex = /^(.*?)\s*\{#([\w-]+)\}$/;
|
|
431
|
-
const matches = customHeadingIdRegex.exec(heading);
|
|
432
|
-
if (matches) {
|
|
433
|
-
return {
|
|
434
|
-
text: matches[1],
|
|
435
|
-
id: matches[2],
|
|
436
|
-
};
|
|
437
|
-
} else {
|
|
438
|
-
return {text: heading, id: undefined};
|
|
439
|
-
}
|
|
440
|
-
}
|
package/src/markdownLinks.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import path from 'path';
|
|
9
|
-
import {aliasedSitePath} from './
|
|
9
|
+
import {aliasedSitePath} from './pathUtils';
|
|
10
10
|
|
|
11
11
|
export type ContentPaths = {
|
|
12
12
|
contentPath: string;
|
|
@@ -45,9 +45,16 @@ export function replaceMarkdownLinks<T extends ContentPaths>({
|
|
|
45
45
|
|
|
46
46
|
// Replace internal markdown linking (except in fenced blocks).
|
|
47
47
|
let fencedBlock = false;
|
|
48
|
+
let lastCodeFence = '';
|
|
48
49
|
const lines = fileString.split('\n').map((line) => {
|
|
49
50
|
if (line.trim().startsWith('```')) {
|
|
50
|
-
|
|
51
|
+
if (!fencedBlock) {
|
|
52
|
+
fencedBlock = true;
|
|
53
|
+
[lastCodeFence] = line.trim().match(/^`+/)!;
|
|
54
|
+
// If we are in a ````-fenced block, all ``` would be plain text instead of fences
|
|
55
|
+
} else if (line.trim().match(/^`+/)![0].length >= lastCodeFence.length) {
|
|
56
|
+
fencedBlock = false;
|
|
57
|
+
}
|
|
51
58
|
}
|
|
52
59
|
if (fencedBlock) {
|
|
53
60
|
return line;
|
|
@@ -57,7 +64,7 @@ export function replaceMarkdownLinks<T extends ContentPaths>({
|
|
|
57
64
|
// Replace inline-style links or reference-style links e.g:
|
|
58
65
|
// This is [Document 1](doc1.md) -> we replace this doc1.md with correct link
|
|
59
66
|
// [doc1]: doc1.md -> we replace this doc1.md with correct link
|
|
60
|
-
const mdRegex = /(?:(?:\]\()|(?:\]:\s?))(?!https)([^'")\]\s>]+\.mdx?)/g;
|
|
67
|
+
const mdRegex = /(?:(?:\]\()|(?:\]:\s?))(?!https?)([^'")\]\s>]+\.mdx?)/g;
|
|
61
68
|
let mdMatch = mdRegex.exec(modifiedLine);
|
|
62
69
|
while (mdMatch !== null) {
|
|
63
70
|
// Replace it to correct html link.
|
package/src/markdownParser.ts
CHANGED
|
@@ -6,9 +6,26 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import logger from '@docusaurus/logger';
|
|
9
|
-
import fs from 'fs-extra';
|
|
10
9
|
import matter from 'gray-matter';
|
|
11
10
|
|
|
11
|
+
// Input: ## Some heading {#some-heading}
|
|
12
|
+
// Output: {text: "## Some heading", id: "some-heading"}
|
|
13
|
+
export function parseMarkdownHeadingId(heading: string): {
|
|
14
|
+
text: string;
|
|
15
|
+
id?: string;
|
|
16
|
+
} {
|
|
17
|
+
const customHeadingIdRegex = /^(.*?)\s*\{#([\w-]+)\}$/;
|
|
18
|
+
const matches = customHeadingIdRegex.exec(heading);
|
|
19
|
+
if (matches) {
|
|
20
|
+
return {
|
|
21
|
+
text: matches[1],
|
|
22
|
+
id: matches[2],
|
|
23
|
+
};
|
|
24
|
+
} else {
|
|
25
|
+
return {text: heading, id: undefined};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
12
29
|
// Hacky way of stripping out import statements from the excerpt
|
|
13
30
|
// TODO: Find a better way to do so, possibly by compiling the Markdown content,
|
|
14
31
|
// stripping out HTML tags and obtaining the first line.
|
|
@@ -19,6 +36,7 @@ export function createExcerpt(fileString: string): string | undefined {
|
|
|
19
36
|
.replace(/^[^\n]*\n[=]+/g, '')
|
|
20
37
|
.split('\n');
|
|
21
38
|
let inCode = false;
|
|
39
|
+
let lastCodeFence = '';
|
|
22
40
|
|
|
23
41
|
/* eslint-disable no-continue */
|
|
24
42
|
// eslint-disable-next-line no-restricted-syntax
|
|
@@ -35,7 +53,15 @@ export function createExcerpt(fileString: string): string | undefined {
|
|
|
35
53
|
|
|
36
54
|
// Skip code block line.
|
|
37
55
|
if (fileLine.trim().startsWith('```')) {
|
|
38
|
-
|
|
56
|
+
if (!inCode) {
|
|
57
|
+
inCode = true;
|
|
58
|
+
[lastCodeFence] = fileLine.trim().match(/^`+/)!;
|
|
59
|
+
// If we are in a ````-fenced block, all ``` would be plain text instead of fences
|
|
60
|
+
} else if (
|
|
61
|
+
fileLine.trim().match(/^`+/)![0].length >= lastCodeFence.length
|
|
62
|
+
) {
|
|
63
|
+
inCode = false;
|
|
64
|
+
}
|
|
39
65
|
continue;
|
|
40
66
|
} else if (inCode) {
|
|
41
67
|
continue;
|
|
@@ -82,8 +108,8 @@ export function parseFrontMatter(markdownFileContent: string): {
|
|
|
82
108
|
} {
|
|
83
109
|
const {data, content} = matter(markdownFileContent);
|
|
84
110
|
return {
|
|
85
|
-
frontMatter: data
|
|
86
|
-
content: content
|
|
111
|
+
frontMatter: data,
|
|
112
|
+
content: content.trim(),
|
|
87
113
|
};
|
|
88
114
|
}
|
|
89
115
|
|
|
@@ -166,22 +192,8 @@ export function parseMarkdownString(
|
|
|
166
192
|
excerpt,
|
|
167
193
|
};
|
|
168
194
|
} catch (e) {
|
|
169
|
-
logger.error(`Error while parsing Markdown
|
|
170
|
-
This can happen if you use special characters in
|
|
195
|
+
logger.error(`Error while parsing Markdown front matter.
|
|
196
|
+
This can happen if you use special characters in front matter values (try using double quotes around that value).`);
|
|
171
197
|
throw e;
|
|
172
198
|
}
|
|
173
199
|
}
|
|
174
|
-
|
|
175
|
-
export async function parseMarkdownFile(
|
|
176
|
-
source: string,
|
|
177
|
-
options?: {removeContentTitle?: boolean},
|
|
178
|
-
): Promise<ParsedMarkdown> {
|
|
179
|
-
const markdownString = await fs.readFile(source, 'utf-8');
|
|
180
|
-
try {
|
|
181
|
-
return parseMarkdownString(markdownString, options);
|
|
182
|
-
} catch (e) {
|
|
183
|
-
throw new Error(
|
|
184
|
-
`Error while parsing Markdown file ${source}: "${(e as Error).message}".`,
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
}
|
package/src/pathUtils.ts
CHANGED
|
@@ -7,22 +7,25 @@
|
|
|
7
7
|
|
|
8
8
|
// Based on https://github.com/gatsbyjs/gatsby/pull/21518/files
|
|
9
9
|
|
|
10
|
+
import path from 'path';
|
|
11
|
+
|
|
10
12
|
// MacOS (APFS) and Windows (NTFS) filename length limit = 255 chars, Others = 255 bytes
|
|
11
13
|
const MAX_PATH_SEGMENT_CHARS = 255;
|
|
12
14
|
const MAX_PATH_SEGMENT_BYTES = 255;
|
|
13
15
|
// Space for appending things to the string like file extensions and so on
|
|
14
16
|
const SPACE_FOR_APPENDING = 10;
|
|
15
17
|
|
|
16
|
-
const isMacOs = process.platform ===
|
|
17
|
-
const isWindows = process.platform ===
|
|
18
|
+
const isMacOs = () => process.platform === 'darwin';
|
|
19
|
+
const isWindows = () => process.platform === 'win32';
|
|
18
20
|
|
|
19
21
|
export const isNameTooLong = (str: string): boolean =>
|
|
20
|
-
|
|
22
|
+
// This is actually not entirely correct: we can't assume FS from OS. But good enough?
|
|
23
|
+
isMacOs() || isWindows()
|
|
21
24
|
? str.length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_CHARS // MacOS (APFS) and Windows (NTFS) filename length limit (255 chars)
|
|
22
25
|
: Buffer.from(str).length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_BYTES; // Other (255 bytes)
|
|
23
26
|
|
|
24
27
|
export const shortName = (str: string): string => {
|
|
25
|
-
if (isMacOs || isWindows) {
|
|
28
|
+
if (isMacOs() || isWindows()) {
|
|
26
29
|
const overflowingChars = str.length - MAX_PATH_SEGMENT_CHARS;
|
|
27
30
|
return str.slice(
|
|
28
31
|
0,
|
|
@@ -39,3 +42,66 @@ export const shortName = (str: string): string => {
|
|
|
39
42
|
)
|
|
40
43
|
.toString();
|
|
41
44
|
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Convert Windows backslash paths to posix style paths.
|
|
48
|
+
* E.g: endi\lie -> endi/lie
|
|
49
|
+
*
|
|
50
|
+
* Returns original path if the posix counterpart is not valid Windows path.
|
|
51
|
+
* This makes the legacy code that uses posixPath safe; but also makes it less
|
|
52
|
+
* useful when you actually want a path with forward slashes (e.g. for URL)
|
|
53
|
+
*
|
|
54
|
+
* Adopted from https://github.com/sindresorhus/slash/blob/main/index.js
|
|
55
|
+
*/
|
|
56
|
+
export function posixPath(str: string): string {
|
|
57
|
+
const isExtendedLengthPath = /^\\\\\?\\/.test(str);
|
|
58
|
+
|
|
59
|
+
// Forward slashes are only valid Windows paths when they don't contain non-ascii characters.
|
|
60
|
+
// eslint-disable-next-line no-control-regex
|
|
61
|
+
const hasNonAscii = /[^\u0000-\u0080]+/.test(str);
|
|
62
|
+
|
|
63
|
+
if (isExtendedLengthPath || hasNonAscii) {
|
|
64
|
+
return str;
|
|
65
|
+
}
|
|
66
|
+
return str.replace(/\\/g, '/');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// When you want to display a path in a message/warning/error,
|
|
70
|
+
// it's more convenient to:
|
|
71
|
+
// - make it relative to cwd()
|
|
72
|
+
// - convert to posix (ie not using windows \ path separator)
|
|
73
|
+
// This way, Jest tests can run more reliably on any computer/CI
|
|
74
|
+
// on both Unix/Windows
|
|
75
|
+
// For Windows users this is not perfect (as they see / instead of \) but it's probably good enough
|
|
76
|
+
export function toMessageRelativeFilePath(filePath: string): string {
|
|
77
|
+
return posixPath(path.relative(process.cwd(), filePath));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Alias filepath relative to site directory, very useful so that we
|
|
82
|
+
* don't expose user's site structure.
|
|
83
|
+
* Example: some/path/to/website/docs/foo.md -> @site/docs/foo.md
|
|
84
|
+
*/
|
|
85
|
+
export function aliasedSitePath(filePath: string, siteDir: string): string {
|
|
86
|
+
const relativePath = posixPath(path.relative(siteDir, filePath));
|
|
87
|
+
// Cannot use path.join() as it resolves '../' and removes
|
|
88
|
+
// the '@site'. Let webpack loader resolve it.
|
|
89
|
+
return `@site/${relativePath}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* When you have a path like C:\X\Y
|
|
94
|
+
* It is not safe to use directly when generating code
|
|
95
|
+
* For example, this would fail due to unescaped \: `<img src={require('${filePath}')} />`
|
|
96
|
+
* But this would work: `<img src={require('${escapePath(filePath)}')} />`
|
|
97
|
+
*
|
|
98
|
+
* posixPath can't be used in all cases, because forward slashes are only valid
|
|
99
|
+
* Windows paths when they don't contain non-ascii characters, and posixPath
|
|
100
|
+
* doesn't escape those that fail to be converted.
|
|
101
|
+
*/
|
|
102
|
+
export function escapePath(str: string): string {
|
|
103
|
+
const escaped = JSON.stringify(str);
|
|
104
|
+
|
|
105
|
+
// Remove the " around the json string;
|
|
106
|
+
return escaped.substring(1, escaped.length - 1);
|
|
107
|
+
}
|
package/src/tags.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {kebabCase, uniq, uniqBy} from 'lodash';
|
|
9
|
-
import {normalizeUrl} from './
|
|
9
|
+
import {normalizeUrl} from './urlUtils';
|
|
10
10
|
|
|
11
11
|
export type Tag = {
|
|
12
12
|
label: string;
|
|
@@ -47,10 +47,11 @@ export function normalizeFrontMatterTag(
|
|
|
47
47
|
|
|
48
48
|
export function normalizeFrontMatterTags(
|
|
49
49
|
tagsPath: string,
|
|
50
|
-
frontMatterTags: FrontMatterTag[] | undefined,
|
|
50
|
+
frontMatterTags: FrontMatterTag[] | undefined = [],
|
|
51
51
|
): Tag[] {
|
|
52
|
-
const tags =
|
|
53
|
-
|
|
52
|
+
const tags = frontMatterTags.map((tag) =>
|
|
53
|
+
normalizeFrontMatterTag(tagsPath, tag),
|
|
54
|
+
);
|
|
54
55
|
|
|
55
56
|
return uniqBy(tags, (tag) => tag.permalink);
|
|
56
57
|
}
|
|
@@ -15,7 +15,12 @@ export function normalizeUrl(rawUrls: string[]): string {
|
|
|
15
15
|
// If the first part is a plain protocol, we combine it with the next part.
|
|
16
16
|
if (urls[0].match(/^[^/:]+:\/*$/) && urls.length > 1) {
|
|
17
17
|
const first = urls.shift();
|
|
18
|
-
|
|
18
|
+
if (first!.startsWith('file:') && urls[0].startsWith('/')) {
|
|
19
|
+
// Force a double slash here, else we lose the information that the next segment is an absolute path
|
|
20
|
+
urls[0] = `${first}//${urls[0]}`;
|
|
21
|
+
} else {
|
|
22
|
+
urls[0] = first + urls[0];
|
|
23
|
+
}
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
// There must be two or three slashes in the file protocol,
|
|
@@ -71,10 +76,20 @@ export function normalizeUrl(rawUrls: string[]): string {
|
|
|
71
76
|
str = parts.shift() + (parts.length > 0 ? '?' : '') + parts.join('&');
|
|
72
77
|
|
|
73
78
|
// Dedupe forward slashes in the entire path, avoiding protocol slashes.
|
|
74
|
-
str = str.replace(/([
|
|
79
|
+
str = str.replace(/([^:/]\/)\/+/g, '$1');
|
|
75
80
|
|
|
76
81
|
// Dedupe forward slashes at the beginning of the path.
|
|
77
82
|
str = str.replace(/^\/+/g, '/');
|
|
78
83
|
|
|
79
84
|
return str;
|
|
80
85
|
}
|
|
86
|
+
|
|
87
|
+
export function getEditUrl(
|
|
88
|
+
fileRelativePath: string,
|
|
89
|
+
editUrl?: string,
|
|
90
|
+
): string | undefined {
|
|
91
|
+
return editUrl
|
|
92
|
+
? // Don't use posixPath for this: we need to force a forward slash path
|
|
93
|
+
normalizeUrl([editUrl, fileRelativePath.replace(/\\/g, '/')])
|
|
94
|
+
: undefined;
|
|
95
|
+
}
|
package/src/webpackUtils.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type {RuleSetRule} from 'webpack';
|
|
9
9
|
import path from 'path';
|
|
10
|
-
import {
|
|
10
|
+
import {escapePath} from './pathUtils';
|
|
11
11
|
import {
|
|
12
12
|
WEBPACK_URL_LOADER_LIMIT,
|
|
13
13
|
OUTPUT_STATIC_ASSETS_DIR_NAME,
|
|
@@ -61,12 +61,12 @@ export function getFileLoaderUtils(): FileLoaderUtils {
|
|
|
61
61
|
// Maybe with the ideal image plugin, all md images should be "ideal"?
|
|
62
62
|
// This is used to force url-loader+file-loader on markdown images
|
|
63
63
|
// https://webpack.js.org/concepts/loaders/#inline
|
|
64
|
-
inlineMarkdownImageFileLoader: `!${
|
|
64
|
+
inlineMarkdownImageFileLoader: `!${escapePath(
|
|
65
65
|
require.resolve('url-loader'),
|
|
66
66
|
)}?limit=${urlLoaderLimit}&name=${fileLoaderFileName(
|
|
67
67
|
'images',
|
|
68
|
-
)}&fallback=${
|
|
69
|
-
inlineMarkdownLinkFileLoader: `!${
|
|
68
|
+
)}&fallback=${escapePath(require.resolve('file-loader'))}!`,
|
|
69
|
+
inlineMarkdownLinkFileLoader: `!${escapePath(
|
|
70
70
|
require.resolve('file-loader'),
|
|
71
71
|
)}?name=${fileLoaderFileName('files')}!`,
|
|
72
72
|
};
|
package/lib/escapePath.d.ts
DELETED
|
@@ -1,18 +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
|
-
* When you have a path like C:\X\Y
|
|
9
|
-
* It is not safe to use directly when generating code
|
|
10
|
-
* For example, this would fail due to unescaped \: `<img src={require('${filePath}')} />`
|
|
11
|
-
* But this would work: `<img src={require('${escapePath(filePath)}')} />`
|
|
12
|
-
*
|
|
13
|
-
* Workaround for issue in posixPath, maybe we won't need it anymore soon?
|
|
14
|
-
* https://github.com/facebook/docusaurus/issues/4730#issuecomment-833530370
|
|
15
|
-
* https://github.com/sindresorhus/slash/pull/16#issuecomment-833528479
|
|
16
|
-
*/
|
|
17
|
-
export declare function escapePath(str: string): string;
|
|
18
|
-
//# sourceMappingURL=escapePath.d.ts.map
|
package/lib/escapePath.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"escapePath.d.ts","sourceRoot":"","sources":["../src/escapePath.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK9C"}
|
package/lib/escapePath.js
DELETED
|
@@ -1,26 +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.escapePath = void 0;
|
|
10
|
-
/**
|
|
11
|
-
* When you have a path like C:\X\Y
|
|
12
|
-
* It is not safe to use directly when generating code
|
|
13
|
-
* For example, this would fail due to unescaped \: `<img src={require('${filePath}')} />`
|
|
14
|
-
* But this would work: `<img src={require('${escapePath(filePath)}')} />`
|
|
15
|
-
*
|
|
16
|
-
* Workaround for issue in posixPath, maybe we won't need it anymore soon?
|
|
17
|
-
* https://github.com/facebook/docusaurus/issues/4730#issuecomment-833530370
|
|
18
|
-
* https://github.com/sindresorhus/slash/pull/16#issuecomment-833528479
|
|
19
|
-
*/
|
|
20
|
-
function escapePath(str) {
|
|
21
|
-
const escaped = JSON.stringify(str);
|
|
22
|
-
// Remove the " around the json string;
|
|
23
|
-
return escaped.substring(1, escaped.length - 1);
|
|
24
|
-
}
|
|
25
|
-
exports.escapePath = escapePath;
|
|
26
|
-
//# sourceMappingURL=escapePath.js.map
|
package/lib/escapePath.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"escapePath.js","sourceRoot":"","sources":["../src/escapePath.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH;;;;;;;;;GASG;AACH,SAAgB,UAAU,CAAC,GAAW;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAEpC,uCAAuC;IACvC,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAClD,CAAC;AALD,gCAKC"}
|
package/lib/mdxUtils.d.ts
DELETED
|
@@ -1,17 +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
|
-
* Transform mdx text to plain html text
|
|
9
|
-
* Initially created to convert MDX blog posts to HTML for the RSS feed
|
|
10
|
-
* without import/export nodes
|
|
11
|
-
*
|
|
12
|
-
* TODO not ideal implementation, won't work well with MDX elements!
|
|
13
|
-
* TODO theme+global site config should be able to declare MDX comps in scope for rendering the RSS feeds
|
|
14
|
-
* see also https://github.com/facebook/docusaurus/issues/4625
|
|
15
|
-
*/
|
|
16
|
-
export declare function mdxToHtml(mdxStr: string): string;
|
|
17
|
-
//# sourceMappingURL=mdxUtils.d.ts.map
|
package/lib/mdxUtils.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mdxUtils.d.ts","sourceRoot":"","sources":["../src/mdxUtils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,GAEb,MAAM,CAMR"}
|