@docusaurus/utils 2.0.0-beta.16 → 2.0.0-beta.19

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 +51 -1
  2. package/lib/constants.d.ts.map +1 -1
  3. package/lib/constants.js +57 -10
  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 +39 -13
  8. package/lib/dataFileUtils.js.map +1 -1
  9. package/lib/emitUtils.d.ts +32 -0
  10. package/lib/emitUtils.d.ts.map +1 -0
  11. package/lib/emitUtils.js +80 -0
  12. package/lib/emitUtils.js.map +1 -0
  13. package/lib/gitUtils.d.ts +54 -5
  14. package/lib/gitUtils.d.ts.map +1 -1
  15. package/lib/gitUtils.js +17 -14
  16. package/lib/gitUtils.js.map +1 -1
  17. package/lib/globUtils.d.ts +28 -0
  18. package/lib/globUtils.d.ts.map +1 -1
  19. package/lib/globUtils.js +36 -13
  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 +7 -6
  24. package/lib/hashUtils.js.map +1 -1
  25. package/lib/i18nUtils.d.ts +51 -0
  26. package/lib/i18nUtils.d.ts.map +1 -0
  27. package/lib/i18nUtils.js +69 -0
  28. package/lib/i18nUtils.js.map +1 -0
  29. package/lib/index.d.ts +10 -54
  30. package/lib/index.d.ts.map +1 -1
  31. package/lib/index.js +56 -256
  32. package/lib/index.js.map +1 -1
  33. package/lib/jsUtils.d.ts +45 -0
  34. package/lib/jsUtils.d.ts.map +1 -0
  35. package/lib/jsUtils.js +94 -0
  36. package/lib/jsUtils.js.map +1 -0
  37. package/lib/markdownLinks.d.ts +48 -5
  38. package/lib/markdownLinks.d.ts.map +1 -1
  39. package/lib/markdownLinks.js +29 -13
  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 +2 -1
  46. package/lib/pathUtils.d.ts.map +1 -1
  47. package/lib/pathUtils.js +16 -7
  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 +6 -2
  52. package/lib/slugger.js.map +1 -1
  53. package/lib/tags.d.ts +42 -10
  54. package/lib/tags.d.ts.map +1 -1
  55. package/lib/tags.js +40 -26
  56. package/lib/tags.js.map +1 -1
  57. package/lib/urlUtils.d.ts +57 -0
  58. package/lib/urlUtils.d.ts.map +1 -1
  59. package/lib/urlUtils.js +132 -6
  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 +8 -5
  64. package/lib/webpackUtils.js.map +1 -1
  65. package/package.json +11 -11
  66. package/src/constants.ts +65 -9
  67. package/src/dataFileUtils.ts +44 -12
  68. package/src/emitUtils.ts +99 -0
  69. package/src/gitUtils.ts +77 -17
  70. package/src/globUtils.ts +34 -13
  71. package/src/hashUtils.ts +6 -5
  72. package/src/i18nUtils.ts +115 -0
  73. package/src/index.ts +43 -307
  74. package/src/jsUtils.ts +102 -0
  75. package/src/markdownLinks.ts +71 -28
  76. package/src/markdownUtils.ts +354 -0
  77. package/src/pathUtils.ts +15 -7
  78. package/src/slugger.ts +13 -1
  79. package/src/tags.ts +53 -28
  80. package/src/urlUtils.ts +145 -7
  81. package/src/webpackUtils.ts +11 -4
  82. package/lib/markdownParser.d.ts +0 -32
  83. package/lib/markdownParser.d.ts.map +0 -1
  84. package/lib/markdownParser.js +0 -161
  85. package/lib/markdownParser.js.map +0 -1
  86. package/src/markdownParser.ts +0 -201
package/src/urlUtils.ts CHANGED
@@ -5,6 +5,21 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
+ import {removeSuffix} from './jsUtils';
9
+ import resolvePathnameUnsafe from 'resolve-pathname';
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
+ */
8
23
  export function normalizeUrl(rawUrls: string[]): string {
9
24
  const urls = [...rawUrls];
10
25
  const resultArray = [];
@@ -12,10 +27,17 @@ export function normalizeUrl(rawUrls: string[]): string {
12
27
  let hasStartingSlash = false;
13
28
  let hasEndingSlash = false;
14
29
 
30
+ const isNonEmptyArray = (arr: string[]): arr is [string, ...string[]] =>
31
+ arr.length > 0;
32
+
33
+ if (!isNonEmptyArray(urls)) {
34
+ return '';
35
+ }
36
+
15
37
  // If the first part is a plain protocol, we combine it with the next part.
16
38
  if (urls[0].match(/^[^/:]+:\/*$/) && urls.length > 1) {
17
- const first = urls.shift();
18
- if (first!.startsWith('file:') && urls[0].startsWith('/')) {
39
+ const first = urls.shift()!;
40
+ if (first.startsWith('file:') && urls[0].startsWith('/')) {
19
41
  // Force a double slash here, else we lose the information that the next
20
42
  // segment is an absolute path
21
43
  urls[0] = `${first}//${urls[0]}`;
@@ -40,7 +62,6 @@ export function normalizeUrl(rawUrls: string[]): string {
40
62
  if (i === urls.length - 1 && hasEndingSlash) {
41
63
  resultArray.push('/');
42
64
  }
43
- // eslint-disable-next-line no-continue
44
65
  continue;
45
66
  }
46
67
 
@@ -48,7 +69,7 @@ export function normalizeUrl(rawUrls: string[]): string {
48
69
  if (i > 0) {
49
70
  // Removing the starting slashes for each component but the first.
50
71
  component = component.replace(
51
- /^[/]+/,
72
+ /^\/+/,
52
73
  // Special case where the first element of rawUrls is empty
53
74
  // ["", "/hello"] => /hello
54
75
  component[0] === '/' && !hasStartingSlash ? '/' : '',
@@ -58,7 +79,7 @@ export function normalizeUrl(rawUrls: string[]): string {
58
79
  hasEndingSlash = component[component.length - 1] === '/';
59
80
  // Removing the ending slashes for each component but the last. For the
60
81
  // last component we will combine multiple slashes to a single one.
61
- component = component.replace(/[/]+$/, i < urls.length - 1 ? '' : '/');
82
+ component = component.replace(/\/+$/, i < urls.length - 1 ? '' : '/');
62
83
  }
63
84
 
64
85
  hasStartingSlash = true;
@@ -66,8 +87,8 @@ export function normalizeUrl(rawUrls: string[]): string {
66
87
  }
67
88
 
68
89
  let str = resultArray.join('/');
69
- // Each input component is now separated by a single slash
70
- // 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.
71
92
 
72
93
  // Remove trailing slash before parameters or hash.
73
94
  str = str.replace(/\/(?<search>\?|&|#[^!])/g, '$1');
@@ -85,6 +106,11 @@ export function normalizeUrl(rawUrls: string[]): string {
85
106
  return str;
86
107
  }
87
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
+ */
88
114
  export function getEditUrl(
89
115
  fileRelativePath: string,
90
116
  editUrl?: string,
@@ -94,3 +120,115 @@ export function getEditUrl(
94
120
  normalizeUrl([editUrl, fileRelativePath.replace(/\\/g, '/')])
95
121
  : undefined;
96
122
  }
123
+
124
+ /**
125
+ * Converts file path to a reasonable URL path, e.g. `'index.md'` -> `'/'`,
126
+ * `'foo/bar.js'` -> `'/foo/bar'`
127
+ */
128
+ export function fileToPath(file: string): string {
129
+ const indexRE = /(?<dirname>^|.*\/)index\.(?:mdx?|jsx?|tsx?)$/i;
130
+ const extRE = /\.(?:mdx?|jsx?|tsx?)$/;
131
+
132
+ if (indexRE.test(file)) {
133
+ return file.replace(indexRE, '/$1');
134
+ }
135
+ return `/${file.replace(extRE, '').replace(/\\/g, '/')}`;
136
+ }
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
+ */
145
+ export function encodePath(userPath: string): string {
146
+ return userPath
147
+ .split('/')
148
+ .map((item) => encodeURIComponent(item))
149
+ .join('/');
150
+ }
151
+
152
+ /**
153
+ * Whether `str` is a valid pathname. It must be absolute, and not contain
154
+ * special characters.
155
+ */
156
+ export function isValidPathname(str: string): boolean {
157
+ if (!str.startsWith('/')) {
158
+ return false;
159
+ }
160
+ try {
161
+ const parsedPathname = new URL(str, 'https://domain.com').pathname;
162
+ return parsedPathname === str || parsedPathname === encodeURI(str);
163
+ } catch {
164
+ return false;
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Resolve pathnames and fail-fast if resolution fails. Uses standard URL
170
+ * semantics (provided by `resolve-pathname` which is used internally by React
171
+ * router)
172
+ */
173
+ export function resolvePathname(to: string, from?: string): string {
174
+ return resolvePathnameUnsafe(to, from);
175
+ }
176
+ /** Appends a leading slash to `str`, if one doesn't exist. */
177
+ export function addLeadingSlash(str: string): string {
178
+ return str.startsWith('/') ? str : `/${str}`;
179
+ }
180
+
181
+ // TODO deduplicate: also present in @docusaurus/utils-common
182
+ /** Appends a trailing slash to `str`, if one doesn't exist. */
183
+ export function addTrailingSlash(str: string): string {
184
+ return str.endsWith('/') ? str : `${str}/`;
185
+ }
186
+
187
+ /** Removes the trailing slash from `str`. */
188
+ export function removeTrailingSlash(str: string): string {
189
+ return removeSuffix(str, '/');
190
+ }
191
+
192
+ /** Constructs an SSH URL that can be used to push to GitHub. */
193
+ export function buildSshUrl(
194
+ githubHost: string,
195
+ organizationName: string,
196
+ projectName: string,
197
+ githubPort?: string,
198
+ ): string {
199
+ if (githubPort) {
200
+ return `ssh://git@${githubHost}:${githubPort}/${organizationName}/${projectName}.git`;
201
+ }
202
+ return `git@${githubHost}:${organizationName}/${projectName}.git`;
203
+ }
204
+
205
+ /** Constructs an HTTP URL that can be used to push to GitHub. */
206
+ export function buildHttpsUrl(
207
+ gitCredentials: string,
208
+ githubHost: string,
209
+ organizationName: string,
210
+ projectName: string,
211
+ githubPort?: string,
212
+ ): string {
213
+ if (githubPort) {
214
+ return `https://${gitCredentials}@${githubHost}:${githubPort}/${organizationName}/${projectName}.git`;
215
+ }
216
+ return `https://${gitCredentials}@${githubHost}/${organizationName}/${projectName}.git`;
217
+ }
218
+
219
+ /**
220
+ * Whether the current URL is an SSH protocol. In addition to looking for
221
+ * `ssh:`, it will also allow protocol-less URLs like
222
+ * `git@github.com:facebook/docusaurus.git`.
223
+ */
224
+ export function hasSSHProtocol(sourceRepoUrl: string): boolean {
225
+ try {
226
+ if (new URL(sourceRepoUrl).protocol === 'ssh:') {
227
+ return true;
228
+ }
229
+ return false;
230
+ } catch {
231
+ // Fails when there isn't a protocol
232
+ return /^(?:[\w-]+@)?[\w.-]+:[\w./-]+/.test(sourceRepoUrl);
233
+ }
234
+ }
@@ -31,15 +31,22 @@ 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
- // files/images < urlLoaderLimit will be inlined as base64 strings directly in
40
+ // Files/images < urlLoaderLimit will be inlined as base64 strings directly in
37
41
  // the html
38
42
  const urlLoaderLimit = WEBPACK_URL_LOADER_LIMIT;
39
43
 
40
- // defines the path/pattern of the assets handled by webpack
41
44
  const fileLoaderFileName = (folder: AssetFolder) =>
42
- `${OUTPUT_STATIC_ASSETS_DIR_NAME}/${folder}/[name]-[contenthash].[ext]`;
45
+ path.posix.join(
46
+ OUTPUT_STATIC_ASSETS_DIR_NAME,
47
+ folder,
48
+ '[name]-[contenthash].[ext]',
49
+ );
43
50
 
44
51
  const loaders: FileLoaderUtils['loaders'] = {
45
52
  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,161 +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 = (0, tslib_1.__importDefault)(require("@docusaurus/logger"));
12
- const gray_matter_1 = (0, 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
- var _a, _b;
120
- const removeContentTitleOption = (_a = options === null || options === void 0 ? void 0 : options.removeContentTitle) !== null && _a !== void 0 ? _a : false;
121
- const content = contentUntrimmed.trim();
122
- const IMPORT_STATEMENT = /import\s+(?:[\w*{}\s\n,]+from\s+)?["'\s][@\w/_.-]+["'\s];?|\n/.source;
123
- const REGULAR_TITLE = /(?<pattern>#\s*(?<title>[^#\n{]*)+[ \t]*(?<suffix>(?:{#*[\w-]+})|#)?\n*?)/
124
- .source;
125
- const ALTERNATE_TITLE = /(?<pattern>\s*(?<title>[^\n]*)\s*\n[=]+)/.source;
126
- const regularTitleMatch = new RegExp(`^(?:${IMPORT_STATEMENT})*?${REGULAR_TITLE}`, 'g').exec(content);
127
- const alternateTitleMatch = new RegExp(`^(?:${IMPORT_STATEMENT})*?${ALTERNATE_TITLE}`, 'g').exec(content);
128
- const titleMatch = regularTitleMatch !== null && regularTitleMatch !== void 0 ? regularTitleMatch : alternateTitleMatch;
129
- const { pattern, title } = (_b = titleMatch === null || titleMatch === void 0 ? void 0 : titleMatch.groups) !== null && _b !== void 0 ? _b : {};
130
- if (!pattern || !title) {
131
- return { content, contentTitle: undefined };
132
- }
133
- const newContent = removeContentTitleOption
134
- ? content.replace(pattern, '')
135
- : content;
136
- return {
137
- content: newContent.trim(),
138
- contentTitle: toTextContentTitle(title.trim()).trim(),
139
- };
140
- }
141
- exports.parseMarkdownContentTitle = parseMarkdownContentTitle;
142
- function parseMarkdownString(markdownFileContent, options) {
143
- try {
144
- const { frontMatter, content: contentWithoutFrontMatter } = parseFrontMatter(markdownFileContent);
145
- const { content, contentTitle } = parseMarkdownContentTitle(contentWithoutFrontMatter, options);
146
- const excerpt = createExcerpt(content);
147
- return {
148
- frontMatter,
149
- content,
150
- contentTitle,
151
- excerpt,
152
- };
153
- }
154
- catch (err) {
155
- logger_1.default.error(`Error while parsing Markdown front matter.
156
- This can happen if you use special characters in front matter values (try using double quotes around that value).`);
157
- throw err;
158
- }
159
- }
160
- exports.parseMarkdownString = parseMarkdownString;
161
- //# 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
- }