@docusaurus/utils 2.0.0-beta.17 → 2.0.0-beta.18

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 (86) hide show
  1. package/lib/constants.d.ts +49 -1
  2. package/lib/constants.d.ts.map +1 -1
  3. package/lib/constants.js +53 -8
  4. package/lib/constants.js.map +1 -1
  5. package/lib/dataFileUtils.d.ts +38 -2
  6. package/lib/dataFileUtils.d.ts.map +1 -1
  7. package/lib/dataFileUtils.js +34 -8
  8. package/lib/dataFileUtils.js.map +1 -1
  9. package/lib/emitUtils.d.ts +12 -0
  10. package/lib/emitUtils.d.ts.map +1 -1
  11. package/lib/emitUtils.js +24 -7
  12. package/lib/emitUtils.js.map +1 -1
  13. package/lib/gitUtils.d.ts +54 -5
  14. package/lib/gitUtils.d.ts.map +1 -1
  15. package/lib/gitUtils.js +14 -11
  16. package/lib/gitUtils.js.map +1 -1
  17. package/lib/globUtils.d.ts +27 -0
  18. package/lib/globUtils.d.ts.map +1 -1
  19. package/lib/globUtils.js +28 -10
  20. package/lib/globUtils.js.map +1 -1
  21. package/lib/hashUtils.d.ts +5 -4
  22. package/lib/hashUtils.d.ts.map +1 -1
  23. package/lib/hashUtils.js +6 -5
  24. package/lib/hashUtils.js.map +1 -1
  25. package/lib/i18nUtils.d.ts +11 -0
  26. package/lib/i18nUtils.d.ts.map +1 -1
  27. package/lib/i18nUtils.js +12 -3
  28. package/lib/i18nUtils.js.map +1 -1
  29. package/lib/index.d.ts +7 -7
  30. package/lib/index.d.ts.map +1 -1
  31. package/lib/index.js +16 -11
  32. package/lib/index.js.map +1 -1
  33. package/lib/jsUtils.d.ts +32 -4
  34. package/lib/jsUtils.d.ts.map +1 -1
  35. package/lib/jsUtils.js +35 -13
  36. package/lib/jsUtils.js.map +1 -1
  37. package/lib/markdownLinks.d.ts +48 -5
  38. package/lib/markdownLinks.d.ts.map +1 -1
  39. package/lib/markdownLinks.js +21 -11
  40. package/lib/markdownLinks.js.map +1 -1
  41. package/lib/markdownUtils.d.ts +112 -0
  42. package/lib/markdownUtils.d.ts.map +1 -0
  43. package/lib/markdownUtils.js +271 -0
  44. package/lib/markdownUtils.js.map +1 -0
  45. package/lib/pathUtils.d.ts +1 -1
  46. package/lib/pathUtils.d.ts.map +1 -1
  47. package/lib/pathUtils.js +2 -2
  48. package/lib/pathUtils.js.map +1 -1
  49. package/lib/slugger.d.ts +10 -0
  50. package/lib/slugger.d.ts.map +1 -1
  51. package/lib/slugger.js +4 -0
  52. package/lib/slugger.js.map +1 -1
  53. package/lib/tags.d.ts +31 -10
  54. package/lib/tags.d.ts.map +1 -1
  55. package/lib/tags.js +38 -23
  56. package/lib/tags.js.map +1 -1
  57. package/lib/urlUtils.d.ts +48 -2
  58. package/lib/urlUtils.d.ts.map +1 -1
  59. package/lib/urlUtils.js +81 -9
  60. package/lib/urlUtils.js.map +1 -1
  61. package/lib/webpackUtils.d.ts +5 -0
  62. package/lib/webpackUtils.d.ts.map +1 -1
  63. package/lib/webpackUtils.js +6 -2
  64. package/lib/webpackUtils.js.map +1 -1
  65. package/package.json +9 -9
  66. package/src/constants.ts +61 -9
  67. package/src/dataFileUtils.ts +43 -11
  68. package/src/emitUtils.ts +26 -9
  69. package/src/gitUtils.ts +76 -16
  70. package/src/globUtils.ts +29 -13
  71. package/src/hashUtils.ts +6 -5
  72. package/src/i18nUtils.ts +13 -4
  73. package/src/index.ts +14 -8
  74. package/src/jsUtils.ts +34 -20
  75. package/src/markdownLinks.ts +64 -27
  76. package/src/markdownUtils.ts +354 -0
  77. package/src/pathUtils.ts +2 -2
  78. package/src/slugger.ts +13 -1
  79. package/src/tags.ts +39 -27
  80. package/src/urlUtils.ts +96 -10
  81. package/src/webpackUtils.ts +10 -2
  82. package/lib/markdownParser.d.ts +0 -32
  83. package/lib/markdownParser.d.ts.map +0 -1
  84. package/lib/markdownParser.js +0 -160
  85. package/lib/markdownParser.js.map +0 -1
  86. package/src/markdownParser.ts +0 -201
package/src/urlUtils.ts CHANGED
@@ -8,6 +8,18 @@
8
8
  import {removeSuffix} from './jsUtils';
9
9
  import resolvePathnameUnsafe from 'resolve-pathname';
10
10
 
11
+ /**
12
+ * Much like `path.join`, but much better. Takes an array of URL segments, and
13
+ * joins them into a reasonable URL.
14
+ *
15
+ * - `["file:", "/home", "/user/", "website"]` => `file:///home/user/website`
16
+ * - `["file://", "home", "/user/", "website"]` => `file://home/user/website` (relative!)
17
+ * - Remove trailing slash before parameters or hash.
18
+ * - Replace `?` in query parameters with `&`.
19
+ * - Dedupe forward slashes in the entire path, avoiding protocol slashes.
20
+ *
21
+ * @throws {TypeError} If any of the URL segment is not a string, this throws.
22
+ */
11
23
  export function normalizeUrl(rawUrls: string[]): string {
12
24
  const urls = [...rawUrls];
13
25
  const resultArray = [];
@@ -15,10 +27,17 @@ export function normalizeUrl(rawUrls: string[]): string {
15
27
  let hasStartingSlash = false;
16
28
  let hasEndingSlash = false;
17
29
 
30
+ const isNonEmptyArray = (arr: string[]): arr is [string, ...string[]] =>
31
+ arr.length > 0;
32
+
33
+ if (!isNonEmptyArray(urls)) {
34
+ return '';
35
+ }
36
+
18
37
  // If the first part is a plain protocol, we combine it with the next part.
19
38
  if (urls[0].match(/^[^/:]+:\/*$/) && urls.length > 1) {
20
- const first = urls.shift();
21
- if (first!.startsWith('file:') && urls[0].startsWith('/')) {
39
+ const first = urls.shift()!;
40
+ if (first.startsWith('file:') && urls[0].startsWith('/')) {
22
41
  // Force a double slash here, else we lose the information that the next
23
42
  // segment is an absolute path
24
43
  urls[0] = `${first}//${urls[0]}`;
@@ -43,7 +62,6 @@ export function normalizeUrl(rawUrls: string[]): string {
43
62
  if (i === urls.length - 1 && hasEndingSlash) {
44
63
  resultArray.push('/');
45
64
  }
46
- // eslint-disable-next-line no-continue
47
65
  continue;
48
66
  }
49
67
 
@@ -51,7 +69,7 @@ export function normalizeUrl(rawUrls: string[]): string {
51
69
  if (i > 0) {
52
70
  // Removing the starting slashes for each component but the first.
53
71
  component = component.replace(
54
- /^[/]+/,
72
+ /^\/+/,
55
73
  // Special case where the first element of rawUrls is empty
56
74
  // ["", "/hello"] => /hello
57
75
  component[0] === '/' && !hasStartingSlash ? '/' : '',
@@ -61,7 +79,7 @@ export function normalizeUrl(rawUrls: string[]): string {
61
79
  hasEndingSlash = component[component.length - 1] === '/';
62
80
  // Removing the ending slashes for each component but the last. For the
63
81
  // last component we will combine multiple slashes to a single one.
64
- component = component.replace(/[/]+$/, i < urls.length - 1 ? '' : '/');
82
+ component = component.replace(/\/+$/, i < urls.length - 1 ? '' : '/');
65
83
  }
66
84
 
67
85
  hasStartingSlash = true;
@@ -69,8 +87,8 @@ export function normalizeUrl(rawUrls: string[]): string {
69
87
  }
70
88
 
71
89
  let str = resultArray.join('/');
72
- // Each input component is now separated by a single slash
73
- // except the possible first plain protocol part.
90
+ // Each input component is now separated by a single slash except the possible
91
+ // first plain protocol part.
74
92
 
75
93
  // Remove trailing slash before parameters or hash.
76
94
  str = str.replace(/\/(?<search>\?|&|#[^!])/g, '$1');
@@ -88,6 +106,11 @@ export function normalizeUrl(rawUrls: string[]): string {
88
106
  return str;
89
107
  }
90
108
 
109
+ /**
110
+ * Takes a file's path, relative to its content folder, and computes its edit
111
+ * URL. If `editUrl` is `undefined`, this returns `undefined`, as is the case
112
+ * when the user doesn't want an edit URL in her config.
113
+ */
91
114
  export function getEditUrl(
92
115
  fileRelativePath: string,
93
116
  editUrl?: string,
@@ -99,8 +122,8 @@ export function getEditUrl(
99
122
  }
100
123
 
101
124
  /**
102
- * Convert filepath to url path.
103
- * Example: 'index.md' -> '/', 'foo/bar.js' -> '/foo/bar',
125
+ * Converts file path to a reasonable URL path, e.g. `'index.md'` -> `'/'`,
126
+ * `'foo/bar.js'` -> `'/foo/bar'`
104
127
  */
105
128
  export function fileToPath(file: string): string {
106
129
  const indexRE = /(?<dirname>^|.*\/)index\.(?:mdx?|jsx?|tsx?)$/i;
@@ -112,6 +135,13 @@ export function fileToPath(file: string): string {
112
135
  return `/${file.replace(extRE, '').replace(/\\/g, '/')}`;
113
136
  }
114
137
 
138
+ /**
139
+ * Similar to `encodeURI`, but uses `encodeURIComponent` and assumes there's no
140
+ * query.
141
+ *
142
+ * `encodeURI("/question?/answer")` => `"/question?/answer#section"`;
143
+ * `encodePath("/question?/answer#section")` => `"/question%3F/answer%23foo"`
144
+ */
115
145
  export function encodePath(userPath: string): string {
116
146
  return userPath
117
147
  .split('/')
@@ -119,6 +149,10 @@ export function encodePath(userPath: string): string {
119
149
  .join('/');
120
150
  }
121
151
 
152
+ /**
153
+ * Whether `str` is a valid pathname. It must be absolute, and not contain
154
+ * special characters.
155
+ */
122
156
  export function isValidPathname(str: string): boolean {
123
157
  if (!str.startsWith('/')) {
124
158
  return false;
@@ -132,18 +166,70 @@ export function isValidPathname(str: string): boolean {
132
166
  }
133
167
  }
134
168
 
135
- // resolve pathname and fail fast if resolution fails
169
+ /**
170
+ * Resolve pathnames and fail-fast if resolution fails. Uses standard URL
171
+ * semantics (provided by `resolve-pathname` which is used internally by React
172
+ * router)
173
+ */
136
174
  export function resolvePathname(to: string, from?: string): string {
137
175
  return resolvePathnameUnsafe(to, from);
138
176
  }
177
+ /** Appends a leading slash to `str`, if one doesn't exist. */
139
178
  export function addLeadingSlash(str: string): string {
140
179
  return str.startsWith('/') ? str : `/${str}`;
141
180
  }
142
181
 
143
182
  // TODO deduplicate: also present in @docusaurus/utils-common
183
+ /** Appends a trailing slash to `str`, if one doesn't exist. */
144
184
  export function addTrailingSlash(str: string): string {
145
185
  return str.endsWith('/') ? str : `${str}/`;
146
186
  }
187
+
188
+ /** Removes the trailing slash from `str`. */
147
189
  export function removeTrailingSlash(str: string): string {
148
190
  return removeSuffix(str, '/');
149
191
  }
192
+
193
+ /** Constructs an SSH URL that can be used to push to GitHub. */
194
+ export function buildSshUrl(
195
+ githubHost: string,
196
+ organizationName: string,
197
+ projectName: string,
198
+ githubPort?: string,
199
+ ): string {
200
+ if (githubPort) {
201
+ return `ssh://git@${githubHost}:${githubPort}/${organizationName}/${projectName}.git`;
202
+ }
203
+ return `git@${githubHost}:${organizationName}/${projectName}.git`;
204
+ }
205
+
206
+ /** Constructs an HTTP URL that can be used to push to GitHub. */
207
+ export function buildHttpsUrl(
208
+ gitCredentials: string,
209
+ githubHost: string,
210
+ organizationName: string,
211
+ projectName: string,
212
+ githubPort?: string,
213
+ ): string {
214
+ if (githubPort) {
215
+ return `https://${gitCredentials}@${githubHost}:${githubPort}/${organizationName}/${projectName}.git`;
216
+ }
217
+ return `https://${gitCredentials}@${githubHost}/${organizationName}/${projectName}.git`;
218
+ }
219
+
220
+ /**
221
+ * Whether the current URL is an SSH protocol. In addition to looking for
222
+ * `ssh:`, it will also allow protocol-less URLs like
223
+ * `git@github.com:facebook/docusaurus.git`.
224
+ */
225
+ export function hasSSHProtocol(sourceRepoUrl: string): boolean {
226
+ try {
227
+ if (new URL(sourceRepoUrl).protocol === 'ssh:') {
228
+ return true;
229
+ }
230
+ return false;
231
+ } catch {
232
+ // Fails when there isn't a protocol
233
+ return /^(?:[\w-]+@)?[\w.-]+:[\w./-]+/.test(sourceRepoUrl);
234
+ }
235
+ }
@@ -31,7 +31,11 @@ type FileLoaderUtils = {
31
31
  };
32
32
  };
33
33
 
34
- // Inspired by https://github.com/gatsbyjs/gatsby/blob/8e6e021014da310b9cc7d02e58c9b3efe938c665/packages/gatsby/src/utils/webpack-utils.ts#L447
34
+ /**
35
+ * Returns unified loader configurations to be used for various file types.
36
+ *
37
+ * Inspired by https://github.com/gatsbyjs/gatsby/blob/8e6e021014da310b9cc7d02e58c9b3efe938c665/packages/gatsby/src/utils/webpack-utils.ts#L447
38
+ */
35
39
  export function getFileLoaderUtils(): FileLoaderUtils {
36
40
  // files/images < urlLoaderLimit will be inlined as base64 strings directly in
37
41
  // the html
@@ -39,7 +43,11 @@ export function getFileLoaderUtils(): FileLoaderUtils {
39
43
 
40
44
  // defines the path/pattern of the assets handled by webpack
41
45
  const fileLoaderFileName = (folder: AssetFolder) =>
42
- `${OUTPUT_STATIC_ASSETS_DIR_NAME}/${folder}/[name]-[contenthash].[ext]`;
46
+ path.posix.join(
47
+ OUTPUT_STATIC_ASSETS_DIR_NAME,
48
+ folder,
49
+ '[name]-[contenthash].[ext]',
50
+ );
43
51
 
44
52
  const loaders: FileLoaderUtils['loaders'] = {
45
53
  file: (options: {folder: AssetFolder}) => ({
@@ -1,32 +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 parseMarkdownHeadingId(heading: string): {
8
- text: string;
9
- id?: string;
10
- };
11
- export declare function createExcerpt(fileString: string): string | undefined;
12
- export declare function parseFrontMatter(markdownFileContent: string): {
13
- frontMatter: Record<string, unknown>;
14
- content: string;
15
- };
16
- export declare function parseMarkdownContentTitle(contentUntrimmed: string, options?: {
17
- removeContentTitle?: boolean;
18
- }): {
19
- content: string;
20
- contentTitle: string | undefined;
21
- };
22
- declare type ParsedMarkdown = {
23
- frontMatter: Record<string, unknown>;
24
- content: string;
25
- contentTitle: string | undefined;
26
- excerpt: string | undefined;
27
- };
28
- export declare function parseMarkdownString(markdownFileContent: string, options?: {
29
- removeContentTitle?: boolean;
30
- }): ParsedMarkdown;
31
- export {};
32
- //# sourceMappingURL=markdownParser.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"markdownParser.d.ts","sourceRoot":"","sources":["../src/markdownParser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAUA;AAKD,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAyEpE;AAED,wBAAgB,gBAAgB,CAAC,mBAAmB,EAAE,MAAM,GAAG;IAC7D,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB,CAMA;AAcD,wBAAgB,yBAAyB,CACvC,gBAAgB,EAAE,MAAM,EACxB,OAAO,CAAC,EAAE;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAC,GACvC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;CAAC,CAkCrD;AAED,aAAK,cAAc,GAAG;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,mBAAmB,EAAE,MAAM,EAC3B,OAAO,CAAC,EAAE;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAC,GACvC,cAAc,CAuBhB"}
@@ -1,160 +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.parseMarkdownString = exports.parseMarkdownContentTitle = exports.parseFrontMatter = exports.createExcerpt = exports.parseMarkdownHeadingId = void 0;
10
- const tslib_1 = require("tslib");
11
- const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
12
- const gray_matter_1 = tslib_1.__importDefault(require("gray-matter"));
13
- // Input: ## Some heading {#some-heading}
14
- // Output: {text: "## Some heading", id: "some-heading"}
15
- function parseMarkdownHeadingId(heading) {
16
- const customHeadingIdRegex = /^(?<text>.*?)\s*\{#(?<id>[\w-]+)\}$/;
17
- const matches = customHeadingIdRegex.exec(heading);
18
- if (matches) {
19
- return {
20
- text: matches.groups.text,
21
- id: matches.groups.id,
22
- };
23
- }
24
- return { text: heading, id: undefined };
25
- }
26
- exports.parseMarkdownHeadingId = parseMarkdownHeadingId;
27
- // Hacky way of stripping out import statements from the excerpt
28
- // TODO: Find a better way to do so, possibly by compiling the Markdown content,
29
- // stripping out HTML tags and obtaining the first line.
30
- function createExcerpt(fileString) {
31
- const fileLines = fileString
32
- .trimLeft()
33
- // Remove Markdown alternate title
34
- .replace(/^[^\n]*\n[=]+/g, '')
35
- .split('\n');
36
- let inCode = false;
37
- let lastCodeFence = '';
38
- /* eslint-disable no-continue */
39
- for (const fileLine of fileLines) {
40
- // Skip empty line.
41
- if (!fileLine.trim()) {
42
- continue;
43
- }
44
- // Skip import/export declaration.
45
- if (/^(?:import|export)\s.*/.test(fileLine)) {
46
- continue;
47
- }
48
- // Skip code block line.
49
- if (fileLine.trim().startsWith('```')) {
50
- if (!inCode) {
51
- inCode = true;
52
- [lastCodeFence] = fileLine.trim().match(/^`+/);
53
- // If we are in a ````-fenced block, all ``` would be plain text instead
54
- // of fences
55
- }
56
- else if (fileLine.trim().match(/^`+/)[0].length >= lastCodeFence.length) {
57
- inCode = false;
58
- }
59
- continue;
60
- }
61
- else if (inCode) {
62
- continue;
63
- }
64
- const cleanedLine = fileLine
65
- // Remove HTML tags.
66
- .replace(/<[^>]*>/g, '')
67
- // Remove Title headers
68
- .replace(/^#\s*[^#]*\s*#?/gm, '')
69
- // Remove Markdown + ATX-style headers
70
- .replace(/^#{1,6}\s*(?<text>[^#]*)\s*(?:#{1,6})?/gm, '$1')
71
- // Remove emphasis.
72
- .replace(/(?<opening>[*_]{1,3})(?<text>.*?)\1/g, '$2')
73
- // Remove strikethroughs.
74
- .replace(/~~(?<text>\S.*\S)~~/g, '$1')
75
- // Remove images.
76
- .replace(/!\[(?<alt>.*?)\][[(].*?[\])]/g, '$1')
77
- // Remove footnotes.
78
- .replace(/\[\^.+?\](?:: .*?$)?/g, '')
79
- // Remove inline links.
80
- .replace(/\[(?<alt>.*?)\][[(].*?[\])]/g, '$1')
81
- // Remove inline code.
82
- .replace(/`(?<text>.+?)`/g, '$1')
83
- // Remove blockquotes.
84
- .replace(/^\s{0,3}>\s?/g, '')
85
- // Remove admonition definition.
86
- .replace(/:::.*/, '')
87
- // Remove Emoji names within colons include preceding whitespace.
88
- .replace(/\s?:(?:::|[^:\n])+:/g, '')
89
- // Remove custom Markdown heading id.
90
- .replace(/{#*[\w-]+}/, '')
91
- .trim();
92
- if (cleanedLine) {
93
- return cleanedLine;
94
- }
95
- }
96
- return undefined;
97
- }
98
- exports.createExcerpt = createExcerpt;
99
- function parseFrontMatter(markdownFileContent) {
100
- const { data, content } = (0, gray_matter_1.default)(markdownFileContent);
101
- return {
102
- frontMatter: data,
103
- content: content.trim(),
104
- };
105
- }
106
- exports.parseFrontMatter = parseFrontMatter;
107
- /**
108
- * Try to convert markdown heading to text. Does not need to be perfect, it is
109
- * only used as a fallback when frontMatter.title is not provided. For now, we
110
- * just unwrap possible inline code blocks (# `config.js`)
111
- */
112
- function toTextContentTitle(contentTitle) {
113
- if (contentTitle.startsWith('`') && contentTitle.endsWith('`')) {
114
- return contentTitle.substring(1, contentTitle.length - 1);
115
- }
116
- return contentTitle;
117
- }
118
- function parseMarkdownContentTitle(contentUntrimmed, options) {
119
- const removeContentTitleOption = options?.removeContentTitle ?? false;
120
- const content = contentUntrimmed.trim();
121
- const IMPORT_STATEMENT = /import\s+(?:[\w*{}\s\n,]+from\s+)?["'\s][@\w/_.-]+["'\s];?|\n/.source;
122
- const REGULAR_TITLE = /(?<pattern>#\s*(?<title>[^#\n{]*)+[ \t]*(?<suffix>(?:{#*[\w-]+})|#)?\n*?)/
123
- .source;
124
- const ALTERNATE_TITLE = /(?<pattern>\s*(?<title>[^\n]*)\s*\n[=]+)/.source;
125
- const regularTitleMatch = new RegExp(`^(?:${IMPORT_STATEMENT})*?${REGULAR_TITLE}`, 'g').exec(content);
126
- const alternateTitleMatch = new RegExp(`^(?:${IMPORT_STATEMENT})*?${ALTERNATE_TITLE}`, 'g').exec(content);
127
- const titleMatch = regularTitleMatch ?? alternateTitleMatch;
128
- const { pattern, title } = titleMatch?.groups ?? {};
129
- if (!pattern || !title) {
130
- return { content, contentTitle: undefined };
131
- }
132
- const newContent = removeContentTitleOption
133
- ? content.replace(pattern, '')
134
- : content;
135
- return {
136
- content: newContent.trim(),
137
- contentTitle: toTextContentTitle(title.trim()).trim(),
138
- };
139
- }
140
- exports.parseMarkdownContentTitle = parseMarkdownContentTitle;
141
- function parseMarkdownString(markdownFileContent, options) {
142
- try {
143
- const { frontMatter, content: contentWithoutFrontMatter } = parseFrontMatter(markdownFileContent);
144
- const { content, contentTitle } = parseMarkdownContentTitle(contentWithoutFrontMatter, options);
145
- const excerpt = createExcerpt(content);
146
- return {
147
- frontMatter,
148
- content,
149
- contentTitle,
150
- excerpt,
151
- };
152
- }
153
- catch (err) {
154
- logger_1.default.error(`Error while parsing Markdown front matter.
155
- This can happen if you use special characters in front matter values (try using double quotes around that value).`);
156
- throw err;
157
- }
158
- }
159
- exports.parseMarkdownString = parseMarkdownString;
160
- //# sourceMappingURL=markdownParser.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"markdownParser.js","sourceRoot":"","sources":["../src/markdownParser.ts"],"names":[],"mappings":"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"}
@@ -1,201 +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 logger from '@docusaurus/logger';
9
- import matter from 'gray-matter';
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 = /^(?<text>.*?)\s*\{#(?<id>[\w-]+)\}$/;
18
- const matches = customHeadingIdRegex.exec(heading);
19
- if (matches) {
20
- return {
21
- text: matches.groups!.text,
22
- id: matches.groups!.id,
23
- };
24
- }
25
- return {text: heading, id: undefined};
26
- }
27
-
28
- // Hacky way of stripping out import statements from the excerpt
29
- // TODO: Find a better way to do so, possibly by compiling the Markdown content,
30
- // stripping out HTML tags and obtaining the first line.
31
- export function createExcerpt(fileString: string): string | undefined {
32
- const fileLines = fileString
33
- .trimLeft()
34
- // Remove Markdown alternate title
35
- .replace(/^[^\n]*\n[=]+/g, '')
36
- .split('\n');
37
- let inCode = false;
38
- let lastCodeFence = '';
39
-
40
- /* eslint-disable no-continue */
41
- for (const fileLine of fileLines) {
42
- // Skip empty line.
43
- if (!fileLine.trim()) {
44
- continue;
45
- }
46
-
47
- // Skip import/export declaration.
48
- if (/^(?:import|export)\s.*/.test(fileLine)) {
49
- continue;
50
- }
51
-
52
- // Skip code block line.
53
- if (fileLine.trim().startsWith('```')) {
54
- if (!inCode) {
55
- inCode = true;
56
- [lastCodeFence] = fileLine.trim().match(/^`+/)!;
57
- // If we are in a ````-fenced block, all ``` would be plain text instead
58
- // of fences
59
- } else if (
60
- fileLine.trim().match(/^`+/)![0].length >= lastCodeFence.length
61
- ) {
62
- inCode = false;
63
- }
64
- continue;
65
- } else if (inCode) {
66
- continue;
67
- }
68
-
69
- const cleanedLine = fileLine
70
- // Remove HTML tags.
71
- .replace(/<[^>]*>/g, '')
72
- // Remove Title headers
73
- .replace(/^#\s*[^#]*\s*#?/gm, '')
74
- // Remove Markdown + ATX-style headers
75
- .replace(/^#{1,6}\s*(?<text>[^#]*)\s*(?:#{1,6})?/gm, '$1')
76
- // Remove emphasis.
77
- .replace(/(?<opening>[*_]{1,3})(?<text>.*?)\1/g, '$2')
78
- // Remove strikethroughs.
79
- .replace(/~~(?<text>\S.*\S)~~/g, '$1')
80
- // Remove images.
81
- .replace(/!\[(?<alt>.*?)\][[(].*?[\])]/g, '$1')
82
- // Remove footnotes.
83
- .replace(/\[\^.+?\](?:: .*?$)?/g, '')
84
- // Remove inline links.
85
- .replace(/\[(?<alt>.*?)\][[(].*?[\])]/g, '$1')
86
- // Remove inline code.
87
- .replace(/`(?<text>.+?)`/g, '$1')
88
- // Remove blockquotes.
89
- .replace(/^\s{0,3}>\s?/g, '')
90
- // Remove admonition definition.
91
- .replace(/:::.*/, '')
92
- // Remove Emoji names within colons include preceding whitespace.
93
- .replace(/\s?:(?:::|[^:\n])+:/g, '')
94
- // Remove custom Markdown heading id.
95
- .replace(/{#*[\w-]+}/, '')
96
- .trim();
97
-
98
- if (cleanedLine) {
99
- return cleanedLine;
100
- }
101
- }
102
-
103
- return undefined;
104
- }
105
-
106
- export function parseFrontMatter(markdownFileContent: string): {
107
- frontMatter: Record<string, unknown>;
108
- content: string;
109
- } {
110
- const {data, content} = matter(markdownFileContent);
111
- return {
112
- frontMatter: data,
113
- content: content.trim(),
114
- };
115
- }
116
-
117
- /**
118
- * Try to convert markdown heading to text. Does not need to be perfect, it is
119
- * only used as a fallback when frontMatter.title is not provided. For now, we
120
- * just unwrap possible inline code blocks (# `config.js`)
121
- */
122
- function toTextContentTitle(contentTitle: string): string {
123
- if (contentTitle.startsWith('`') && contentTitle.endsWith('`')) {
124
- return contentTitle.substring(1, contentTitle.length - 1);
125
- }
126
- return contentTitle;
127
- }
128
-
129
- export function parseMarkdownContentTitle(
130
- contentUntrimmed: string,
131
- options?: {removeContentTitle?: boolean},
132
- ): {content: string; contentTitle: string | undefined} {
133
- const removeContentTitleOption = options?.removeContentTitle ?? false;
134
-
135
- const content = contentUntrimmed.trim();
136
-
137
- const IMPORT_STATEMENT =
138
- /import\s+(?:[\w*{}\s\n,]+from\s+)?["'\s][@\w/_.-]+["'\s];?|\n/.source;
139
- const REGULAR_TITLE =
140
- /(?<pattern>#\s*(?<title>[^#\n{]*)+[ \t]*(?<suffix>(?:{#*[\w-]+})|#)?\n*?)/
141
- .source;
142
- const ALTERNATE_TITLE = /(?<pattern>\s*(?<title>[^\n]*)\s*\n[=]+)/.source;
143
-
144
- const regularTitleMatch = new RegExp(
145
- `^(?:${IMPORT_STATEMENT})*?${REGULAR_TITLE}`,
146
- 'g',
147
- ).exec(content);
148
- const alternateTitleMatch = new RegExp(
149
- `^(?:${IMPORT_STATEMENT})*?${ALTERNATE_TITLE}`,
150
- 'g',
151
- ).exec(content);
152
-
153
- const titleMatch = regularTitleMatch ?? alternateTitleMatch;
154
- const {pattern, title} = titleMatch?.groups ?? {};
155
-
156
- if (!pattern || !title) {
157
- return {content, contentTitle: undefined};
158
- }
159
- const newContent = removeContentTitleOption
160
- ? content.replace(pattern, '')
161
- : content;
162
- return {
163
- content: newContent.trim(),
164
- contentTitle: toTextContentTitle(title.trim()).trim(),
165
- };
166
- }
167
-
168
- type ParsedMarkdown = {
169
- frontMatter: Record<string, unknown>;
170
- content: string;
171
- contentTitle: string | undefined;
172
- excerpt: string | undefined;
173
- };
174
-
175
- export function parseMarkdownString(
176
- markdownFileContent: string,
177
- options?: {removeContentTitle?: boolean},
178
- ): ParsedMarkdown {
179
- try {
180
- const {frontMatter, content: contentWithoutFrontMatter} =
181
- parseFrontMatter(markdownFileContent);
182
-
183
- const {content, contentTitle} = parseMarkdownContentTitle(
184
- contentWithoutFrontMatter,
185
- options,
186
- );
187
-
188
- const excerpt = createExcerpt(content);
189
-
190
- return {
191
- frontMatter,
192
- content,
193
- contentTitle,
194
- excerpt,
195
- };
196
- } catch (err) {
197
- logger.error(`Error while parsing Markdown front matter.
198
- This can happen if you use special characters in front matter values (try using double quotes around that value).`);
199
- throw err;
200
- }
201
- }