@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.
Files changed (58) hide show
  1. package/lib/dataFileUtils.d.ts +24 -0
  2. package/lib/dataFileUtils.d.ts.map +1 -0
  3. package/lib/dataFileUtils.js +65 -0
  4. package/lib/dataFileUtils.js.map +1 -0
  5. package/lib/globUtils.d.ts.map +1 -1
  6. package/lib/globUtils.js +2 -2
  7. package/lib/globUtils.js.map +1 -1
  8. package/lib/index.d.ts +18 -41
  9. package/lib/index.d.ts.map +1 -1
  10. package/lib/index.js +40 -139
  11. package/lib/index.js.map +1 -1
  12. package/lib/markdownLinks.d.ts.map +1 -1
  13. package/lib/markdownLinks.js +12 -4
  14. package/lib/markdownLinks.js.map +1 -1
  15. package/lib/markdownParser.d.ts +4 -3
  16. package/lib/markdownParser.d.ts.map +1 -1
  17. package/lib/markdownParser.js +30 -18
  18. package/lib/markdownParser.js.map +1 -1
  19. package/lib/pathUtils.d.ts +29 -0
  20. package/lib/pathUtils.d.ts.map +1 -1
  21. package/lib/pathUtils.js +69 -5
  22. package/lib/pathUtils.js.map +1 -1
  23. package/lib/tags.d.ts +1 -1
  24. package/lib/tags.d.ts.map +1 -1
  25. package/lib/tags.js +4 -5
  26. package/lib/tags.js.map +1 -1
  27. package/lib/{normalizeUrl.d.ts → urlUtils.d.ts} +2 -1
  28. package/lib/urlUtils.d.ts.map +1 -0
  29. package/lib/{normalizeUrl.js → urlUtils.js} +17 -4
  30. package/lib/{normalizeUrl.js.map → urlUtils.js.map} +1 -1
  31. package/lib/webpackUtils.js +3 -3
  32. package/lib/webpackUtils.js.map +1 -1
  33. package/package.json +6 -5
  34. package/src/dataFileUtils.ts +90 -0
  35. package/src/globUtils.ts +2 -1
  36. package/src/index.ts +48 -173
  37. package/src/markdownLinks.ts +10 -3
  38. package/src/markdownParser.ts +32 -20
  39. package/src/pathUtils.ts +70 -4
  40. package/src/tags.ts +5 -4
  41. package/src/{normalizeUrl.ts → urlUtils.ts} +17 -2
  42. package/src/webpackUtils.ts +4 -4
  43. package/lib/escapePath.d.ts +0 -18
  44. package/lib/escapePath.d.ts.map +0 -1
  45. package/lib/escapePath.js +0 -26
  46. package/lib/escapePath.js.map +0 -1
  47. package/lib/mdxUtils.d.ts +0 -17
  48. package/lib/mdxUtils.d.ts.map +0 -1
  49. package/lib/mdxUtils.js +0 -31
  50. package/lib/mdxUtils.js.map +0 -1
  51. package/lib/normalizeUrl.d.ts.map +0 -1
  52. package/lib/posixPath.d.ts +0 -15
  53. package/lib/posixPath.d.ts.map +0 -1
  54. package/lib/posixPath.js +0 -29
  55. package/lib/posixPath.js.map +0 -1
  56. package/src/escapePath.ts +0 -23
  57. package/src/mdxUtils.ts +0 -32
  58. 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 {camelCase, mapValues} from 'lodash';
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 './mdxUtils';
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 {md5Hash, simpleHash, docuHash} from './hashUtils';
40
- export {
41
- Globby,
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(userpath: string): string {
110
- return userpath
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) ? str : `${str}${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
- export async function mapAsyncSequencial<T, R>(
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
- }
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import path from 'path';
9
- import {aliasedSitePath} from './index';
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
- fencedBlock = !fencedBlock;
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.
@@ -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
- inCode = !inCode;
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?.trim() ?? '',
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 frontmatter.
170
- This can happen if you use special characters in frontmatter values (try using double quotes around that value).`);
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 === `darwin`;
17
- const isWindows = process.platform === `win32`;
18
+ const isMacOs = () => process.platform === 'darwin';
19
+ const isWindows = () => process.platform === 'win32';
18
20
 
19
21
  export const isNameTooLong = (str: string): boolean =>
20
- isMacOs || isWindows
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 './normalizeUrl';
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
- frontMatterTags?.map((tag) => normalizeFrontMatterTag(tagsPath, tag)) ?? [];
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
- urls[0] = first + urls[0];
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(/([^:]\/)\/+/g, '$1');
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
+ }
@@ -7,7 +7,7 @@
7
7
 
8
8
  import type {RuleSetRule} from 'webpack';
9
9
  import path from 'path';
10
- import {posixPath} from './posixPath';
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: `!${posixPath(
64
+ inlineMarkdownImageFileLoader: `!${escapePath(
65
65
  require.resolve('url-loader'),
66
66
  )}?limit=${urlLoaderLimit}&name=${fileLoaderFileName(
67
67
  'images',
68
- )}&fallback=${posixPath(require.resolve('file-loader'))}!`,
69
- inlineMarkdownLinkFileLoader: `!${posixPath(
68
+ )}&fallback=${escapePath(require.resolve('file-loader'))}!`,
69
+ inlineMarkdownLinkFileLoader: `!${escapePath(
70
70
  require.resolve('file-loader'),
71
71
  )}?name=${fileLoaderFileName('files')}!`,
72
72
  };
@@ -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
@@ -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
@@ -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
@@ -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"}