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

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 (51) hide show
  1. package/lib/constants.js +1 -2
  2. package/lib/constants.js.map +1 -1
  3. package/lib/dataFileUtils.js +4 -4
  4. package/lib/dataFileUtils.js.map +1 -1
  5. package/lib/emitUtils.d.ts +23 -0
  6. package/lib/emitUtils.d.ts.map +1 -0
  7. package/lib/emitUtils.js +90 -0
  8. package/lib/emitUtils.js.map +1 -0
  9. package/lib/gitUtils.js +2 -2
  10. package/lib/gitUtils.js.map +1 -1
  11. package/lib/globUtils.js +3 -3
  12. package/lib/globUtils.js.map +1 -1
  13. package/lib/hashUtils.js +1 -1
  14. package/lib/hashUtils.js.map +1 -1
  15. package/lib/i18nUtils.d.ts +17 -0
  16. package/lib/i18nUtils.d.ts.map +1 -0
  17. package/lib/i18nUtils.js +40 -0
  18. package/lib/i18nUtils.js.map +1 -0
  19. package/lib/index.d.ts +5 -49
  20. package/lib/index.d.ts.map +1 -1
  21. package/lib/index.js +43 -249
  22. package/lib/index.js.map +1 -1
  23. package/lib/jsUtils.d.ts +17 -0
  24. package/lib/jsUtils.d.ts.map +1 -0
  25. package/lib/jsUtils.js +72 -0
  26. package/lib/jsUtils.js.map +1 -0
  27. package/lib/markdownLinks.js +1 -1
  28. package/lib/markdownLinks.js.map +1 -1
  29. package/lib/markdownParser.js +5 -6
  30. package/lib/markdownParser.js.map +1 -1
  31. package/lib/pathUtils.d.ts +1 -0
  32. package/lib/pathUtils.d.ts.map +1 -1
  33. package/lib/pathUtils.js +9 -2
  34. package/lib/pathUtils.js.map +1 -1
  35. package/lib/slugger.js +2 -2
  36. package/lib/slugger.js.map +1 -1
  37. package/lib/tags.js +2 -3
  38. package/lib/tags.js.map +1 -1
  39. package/lib/urlUtils.d.ts +11 -0
  40. package/lib/urlUtils.d.ts.map +1 -1
  41. package/lib/urlUtils.js +56 -1
  42. package/lib/urlUtils.js.map +1 -1
  43. package/lib/webpackUtils.js +1 -1
  44. package/lib/webpackUtils.js.map +1 -1
  45. package/package.json +4 -4
  46. package/src/emitUtils.ts +113 -0
  47. package/src/i18nUtils.ts +58 -0
  48. package/src/index.ts +26 -300
  49. package/src/jsUtils.ts +88 -0
  50. package/src/pathUtils.ts +8 -2
  51. package/src/urlUtils.ts +53 -0
package/src/index.ts CHANGED
@@ -5,23 +5,6 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import logger from '@docusaurus/logger';
9
- import path from 'path';
10
- import {createHash} from 'crypto';
11
- import _ from 'lodash';
12
- import fs from 'fs-extra';
13
- import {URL} from 'url';
14
- import type {
15
- ReportingSeverity,
16
- TranslationFileContent,
17
- TranslationFile,
18
- } from '@docusaurus/types';
19
-
20
- import resolvePathnameUnsafe from 'resolve-pathname';
21
-
22
- import {simpleHash, docuHash} from './hashUtils';
23
- import {DEFAULT_PLUGIN_ID} from './constants';
24
-
25
8
  export {
26
9
  NODE_MAJOR_VERSION,
27
10
  NODE_MINOR_VERSION,
@@ -37,8 +20,32 @@ export {
37
20
  DEFAULT_PLUGIN_ID,
38
21
  WEBPACK_URL_LOADER_LIMIT,
39
22
  } from './constants';
23
+ export {generate, genChunkName, readOutputHTMLFile} from './emitUtils';
40
24
  export {getFileCommitDate, GitNotFoundError} from './gitUtils';
41
- export {normalizeUrl, getEditUrl} from './urlUtils';
25
+ export {
26
+ mergeTranslations,
27
+ updateTranslationFileMessages,
28
+ getPluginI18nPath,
29
+ } from './i18nUtils';
30
+ export {
31
+ removeSuffix,
32
+ removePrefix,
33
+ getElementsAround,
34
+ mapAsyncSequential,
35
+ findAsyncSequential,
36
+ reportMessage,
37
+ } from './jsUtils';
38
+ export {
39
+ normalizeUrl,
40
+ getEditUrl,
41
+ fileToPath,
42
+ encodePath,
43
+ isValidPathname,
44
+ resolvePathname,
45
+ addLeadingSlash,
46
+ addTrailingSlash,
47
+ removeTrailingSlash,
48
+ } from './urlUtils';
42
49
  export {
43
50
  type Tag,
44
51
  type FrontMatterTag,
@@ -69,6 +76,7 @@ export {
69
76
  toMessageRelativeFilePath,
70
77
  aliasedSitePath,
71
78
  escapePath,
79
+ addTrailingPathSeparator,
72
80
  } from './pathUtils';
73
81
  export {md5Hash, simpleHash, docuHash} from './hashUtils';
74
82
  export {
@@ -85,285 +93,3 @@ export {
85
93
  findFolderContainingFile,
86
94
  getFolderContainingFile,
87
95
  } from './dataFileUtils';
88
-
89
- const fileHash = new Map<string, string>();
90
- export async function generate(
91
- generatedFilesDir: string,
92
- file: string,
93
- content: string,
94
- skipCache: boolean = process.env.NODE_ENV === 'production',
95
- ): Promise<void> {
96
- const filepath = path.join(generatedFilesDir, file);
97
-
98
- if (skipCache) {
99
- await fs.ensureDir(path.dirname(filepath));
100
- await fs.writeFile(filepath, content);
101
- return;
102
- }
103
-
104
- let lastHash = fileHash.get(filepath);
105
-
106
- // If file already exists but its not in runtime cache yet,
107
- // we try to calculate the content hash and then compare
108
- // This is to avoid unnecessary overwriting and we can reuse old file.
109
- if (!lastHash && (await fs.pathExists(filepath))) {
110
- const lastContent = await fs.readFile(filepath, 'utf8');
111
- lastHash = createHash('md5').update(lastContent).digest('hex');
112
- fileHash.set(filepath, lastHash);
113
- }
114
-
115
- const currentHash = createHash('md5').update(content).digest('hex');
116
-
117
- if (lastHash !== currentHash) {
118
- await fs.ensureDir(path.dirname(filepath));
119
- await fs.writeFile(filepath, content);
120
- fileHash.set(filepath, currentHash);
121
- }
122
- }
123
-
124
- const indexRE = /(?<dirname>^|.*\/)index\.(?:mdx?|jsx?|tsx?)$/i;
125
- const extRE = /\.(?:mdx?|jsx?|tsx?)$/;
126
-
127
- /**
128
- * Convert filepath to url path.
129
- * Example: 'index.md' -> '/', 'foo/bar.js' -> '/foo/bar',
130
- */
131
- export function fileToPath(file: string): string {
132
- if (indexRE.test(file)) {
133
- return file.replace(indexRE, '/$1');
134
- }
135
- return `/${file.replace(extRE, '').replace(/\\/g, '/')}`;
136
- }
137
-
138
- export function encodePath(userPath: string): string {
139
- return userPath
140
- .split('/')
141
- .map((item) => encodeURIComponent(item))
142
- .join('/');
143
- }
144
-
145
- const chunkNameCache = new Map();
146
- /**
147
- * Generate unique chunk name given a module path.
148
- */
149
- export function genChunkName(
150
- modulePath: string,
151
- prefix?: string,
152
- preferredName?: string,
153
- shortId: boolean = process.env.NODE_ENV === 'production',
154
- ): string {
155
- let chunkName: string | undefined = chunkNameCache.get(modulePath);
156
- if (!chunkName) {
157
- if (shortId) {
158
- chunkName = simpleHash(modulePath, 8);
159
- } else {
160
- let str = modulePath;
161
- if (preferredName) {
162
- const shortHash = simpleHash(modulePath, 3);
163
- str = `${preferredName}${shortHash}`;
164
- }
165
- const name = str === '/' ? 'index' : docuHash(str);
166
- chunkName = prefix ? `${prefix}---${name}` : name;
167
- }
168
- chunkNameCache.set(modulePath, chunkName);
169
- }
170
- return chunkName;
171
- }
172
-
173
- export function isValidPathname(str: string): boolean {
174
- if (!str.startsWith('/')) {
175
- return false;
176
- }
177
- try {
178
- // weird, but is there a better way?
179
- const parsedPathname = new URL(str, 'https://domain.com').pathname;
180
- return parsedPathname === str || parsedPathname === encodeURI(str);
181
- } catch {
182
- return false;
183
- }
184
- }
185
-
186
- // resolve pathname and fail fast if resolution fails
187
- export function resolvePathname(to: string, from?: string): string {
188
- return resolvePathnameUnsafe(to, from);
189
- }
190
- export function addLeadingSlash(str: string): string {
191
- return str.startsWith('/') ? str : `/${str}`;
192
- }
193
-
194
- export function addTrailingPathSeparator(str: string): string {
195
- return str.endsWith(path.sep)
196
- ? str
197
- : // If this is Windows, we need to change the forward slash to backward
198
- `${str.replace(/\/$/, '')}${path.sep}`;
199
- }
200
-
201
- // TODO deduplicate: also present in @docusaurus/utils-common
202
- export function addTrailingSlash(str: string): string {
203
- return str.endsWith('/') ? str : `${str}/`;
204
- }
205
- export function removeTrailingSlash(str: string): string {
206
- return removeSuffix(str, '/');
207
- }
208
-
209
- export function removeSuffix(str: string, suffix: string): string {
210
- if (suffix === '') {
211
- return str; // always returns "" otherwise!
212
- }
213
- return str.endsWith(suffix) ? str.slice(0, -suffix.length) : str;
214
- }
215
-
216
- export function removePrefix(str: string, prefix: string): string {
217
- return str.startsWith(prefix) ? str.slice(prefix.length) : str;
218
- }
219
-
220
- export function getElementsAround<T>(
221
- array: T[],
222
- aroundIndex: number,
223
- ): {
224
- next: T | undefined;
225
- previous: T | undefined;
226
- } {
227
- const min = 0;
228
- const max = array.length - 1;
229
- if (aroundIndex < min || aroundIndex > max) {
230
- throw new Error(
231
- `Valid "aroundIndex" for array (of size ${array.length}) are between ${min} and ${max}, but you provided ${aroundIndex}.`,
232
- );
233
- }
234
- const previous = aroundIndex === min ? undefined : array[aroundIndex - 1];
235
- const next = aroundIndex === max ? undefined : array[aroundIndex + 1];
236
- return {previous, next};
237
- }
238
-
239
- export function getPluginI18nPath({
240
- siteDir,
241
- locale,
242
- pluginName,
243
- pluginId = DEFAULT_PLUGIN_ID,
244
- subPaths = [],
245
- }: {
246
- siteDir: string;
247
- locale: string;
248
- pluginName: string;
249
- pluginId?: string | undefined;
250
- subPaths?: string[];
251
- }): string {
252
- return path.join(
253
- siteDir,
254
- 'i18n',
255
- // namespace first by locale: convenient to work in a single folder for a
256
- // translator
257
- locale,
258
- // Make it convenient to use for single-instance
259
- // ie: return "docs", not "docs-default" nor "docs/default"
260
- `${pluginName}${pluginId === DEFAULT_PLUGIN_ID ? '' : `-${pluginId}`}`,
261
- ...subPaths,
262
- );
263
- }
264
-
265
- /**
266
- * @param permalink The URL that the HTML file corresponds to, without base URL
267
- * @param outDir Full path to the output directory
268
- * @param trailingSlash The site config option. If provided, only one path will
269
- * be read.
270
- * @returns This returns a buffer, which you have to decode string yourself if
271
- * needed. (Not always necessary since the output isn't for human consumption
272
- * anyways, and most HTML manipulation libs accept buffers)
273
- */
274
- export async function readOutputHTMLFile(
275
- permalink: string,
276
- outDir: string,
277
- trailingSlash: boolean | undefined,
278
- ): Promise<Buffer> {
279
- const withTrailingSlashPath = path.join(outDir, permalink, 'index.html');
280
- const withoutTrailingSlashPath = path.join(
281
- outDir,
282
- `${permalink.replace(/\/$/, '')}.html`,
283
- );
284
- if (trailingSlash) {
285
- return fs.readFile(withTrailingSlashPath);
286
- } else if (trailingSlash === false) {
287
- return fs.readFile(withoutTrailingSlashPath);
288
- }
289
- const HTMLPath = await findAsyncSequential(
290
- [withTrailingSlashPath, withoutTrailingSlashPath],
291
- fs.pathExists,
292
- );
293
- if (!HTMLPath) {
294
- throw new Error(
295
- `Expected output HTML file to be found at ${withTrailingSlashPath}`,
296
- );
297
- }
298
- return fs.readFile(HTMLPath);
299
- }
300
-
301
- export async function mapAsyncSequential<T, R>(
302
- array: T[],
303
- action: (t: T) => Promise<R>,
304
- ): Promise<R[]> {
305
- const results: R[] = [];
306
- for (const t of array) {
307
- const result = await action(t);
308
- results.push(result);
309
- }
310
- return results;
311
- }
312
-
313
- export async function findAsyncSequential<T>(
314
- array: T[],
315
- predicate: (t: T) => Promise<boolean>,
316
- ): Promise<T | undefined> {
317
- for (const t of array) {
318
- if (await predicate(t)) {
319
- return t;
320
- }
321
- }
322
- return undefined;
323
- }
324
-
325
- export function reportMessage(
326
- message: string,
327
- reportingSeverity: ReportingSeverity,
328
- ): void {
329
- switch (reportingSeverity) {
330
- case 'ignore':
331
- break;
332
- case 'log':
333
- logger.info(message);
334
- break;
335
- case 'warn':
336
- logger.warn(message);
337
- break;
338
- case 'error':
339
- logger.error(message);
340
- break;
341
- case 'throw':
342
- throw new Error(message);
343
- default:
344
- throw new Error(
345
- `Unexpected "reportingSeverity" value: ${reportingSeverity}.`,
346
- );
347
- }
348
- }
349
-
350
- export function mergeTranslations(
351
- contents: TranslationFileContent[],
352
- ): TranslationFileContent {
353
- return contents.reduce((acc, content) => ({...acc, ...content}), {});
354
- }
355
-
356
- // Useful to update all the messages of a translation file
357
- // Used in tests to simulate translations
358
- export function updateTranslationFileMessages(
359
- translationFile: TranslationFile,
360
- updateMessage: (message: string) => string,
361
- ): TranslationFile {
362
- return {
363
- ...translationFile,
364
- content: _.mapValues(translationFile.content, (translation) => ({
365
- ...translation,
366
- message: updateMessage(translation.message),
367
- })),
368
- };
369
- }
package/src/jsUtils.ts ADDED
@@ -0,0 +1,88 @@
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 type {ReportingSeverity} from '@docusaurus/types';
9
+ import logger from '@docusaurus/logger';
10
+
11
+ export function removeSuffix(str: string, suffix: string): string {
12
+ if (suffix === '') {
13
+ return str; // always returns "" otherwise!
14
+ }
15
+ return str.endsWith(suffix) ? str.slice(0, -suffix.length) : str;
16
+ }
17
+
18
+ export function removePrefix(str: string, prefix: string): string {
19
+ return str.startsWith(prefix) ? str.slice(prefix.length) : str;
20
+ }
21
+
22
+ export function getElementsAround<T>(
23
+ array: T[],
24
+ aroundIndex: number,
25
+ ): {
26
+ next: T | undefined;
27
+ previous: T | undefined;
28
+ } {
29
+ const min = 0;
30
+ const max = array.length - 1;
31
+ if (aroundIndex < min || aroundIndex > max) {
32
+ throw new Error(
33
+ `Valid "aroundIndex" for array (of size ${array.length}) are between ${min} and ${max}, but you provided ${aroundIndex}.`,
34
+ );
35
+ }
36
+ const previous = aroundIndex === min ? undefined : array[aroundIndex - 1];
37
+ const next = aroundIndex === max ? undefined : array[aroundIndex + 1];
38
+ return {previous, next};
39
+ }
40
+
41
+ export async function mapAsyncSequential<T, R>(
42
+ array: T[],
43
+ action: (t: T) => Promise<R>,
44
+ ): Promise<R[]> {
45
+ const results: R[] = [];
46
+ for (const t of array) {
47
+ const result = await action(t);
48
+ results.push(result);
49
+ }
50
+ return results;
51
+ }
52
+
53
+ export async function findAsyncSequential<T>(
54
+ array: T[],
55
+ predicate: (t: T) => Promise<boolean>,
56
+ ): Promise<T | undefined> {
57
+ for (const t of array) {
58
+ if (await predicate(t)) {
59
+ return t;
60
+ }
61
+ }
62
+ return undefined;
63
+ }
64
+
65
+ export function reportMessage(
66
+ message: string,
67
+ reportingSeverity: ReportingSeverity,
68
+ ): void {
69
+ switch (reportingSeverity) {
70
+ case 'ignore':
71
+ break;
72
+ case 'log':
73
+ logger.info(message);
74
+ break;
75
+ case 'warn':
76
+ logger.warn(message);
77
+ break;
78
+ case 'error':
79
+ logger.error(message);
80
+ break;
81
+ case 'throw':
82
+ throw new Error(message);
83
+ default:
84
+ throw new Error(
85
+ `Unexpected "reportingSeverity" value: ${reportingSeverity}.`,
86
+ );
87
+ }
88
+ }
package/src/pathUtils.ts CHANGED
@@ -5,10 +5,9 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- // Based on https://github.com/gatsbyjs/gatsby/pull/21518/files
9
-
10
8
  import path from 'path';
11
9
 
10
+ // Based on https://github.com/gatsbyjs/gatsby/pull/21518/files
12
11
  // MacOS (APFS) and Windows (NTFS) filename length limit = 255 chars,
13
12
  // Others = 255 bytes
14
13
  const MAX_PATH_SEGMENT_CHARS = 255;
@@ -113,3 +112,10 @@ export function escapePath(str: string): string {
113
112
  // Remove the " around the json string;
114
113
  return escaped.substring(1, escaped.length - 1);
115
114
  }
115
+
116
+ export function addTrailingPathSeparator(str: string): string {
117
+ return str.endsWith(path.sep)
118
+ ? str
119
+ : // If this is Windows, we need to change the forward slash to backward
120
+ `${str.replace(/\/$/, '')}${path.sep}`;
121
+ }
package/src/urlUtils.ts CHANGED
@@ -5,6 +5,9 @@
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
+
8
11
  export function normalizeUrl(rawUrls: string[]): string {
9
12
  const urls = [...rawUrls];
10
13
  const resultArray = [];
@@ -94,3 +97,53 @@ export function getEditUrl(
94
97
  normalizeUrl([editUrl, fileRelativePath.replace(/\\/g, '/')])
95
98
  : undefined;
96
99
  }
100
+
101
+ /**
102
+ * Convert filepath to url path.
103
+ * Example: 'index.md' -> '/', 'foo/bar.js' -> '/foo/bar',
104
+ */
105
+ export function fileToPath(file: string): string {
106
+ const indexRE = /(?<dirname>^|.*\/)index\.(?:mdx?|jsx?|tsx?)$/i;
107
+ const extRE = /\.(?:mdx?|jsx?|tsx?)$/;
108
+
109
+ if (indexRE.test(file)) {
110
+ return file.replace(indexRE, '/$1');
111
+ }
112
+ return `/${file.replace(extRE, '').replace(/\\/g, '/')}`;
113
+ }
114
+
115
+ export function encodePath(userPath: string): string {
116
+ return userPath
117
+ .split('/')
118
+ .map((item) => encodeURIComponent(item))
119
+ .join('/');
120
+ }
121
+
122
+ export function isValidPathname(str: string): boolean {
123
+ if (!str.startsWith('/')) {
124
+ return false;
125
+ }
126
+ try {
127
+ // weird, but is there a better way?
128
+ const parsedPathname = new URL(str, 'https://domain.com').pathname;
129
+ return parsedPathname === str || parsedPathname === encodeURI(str);
130
+ } catch {
131
+ return false;
132
+ }
133
+ }
134
+
135
+ // resolve pathname and fail fast if resolution fails
136
+ export function resolvePathname(to: string, from?: string): string {
137
+ return resolvePathnameUnsafe(to, from);
138
+ }
139
+ export function addLeadingSlash(str: string): string {
140
+ return str.startsWith('/') ? str : `/${str}`;
141
+ }
142
+
143
+ // TODO deduplicate: also present in @docusaurus/utils-common
144
+ export function addTrailingSlash(str: string): string {
145
+ return str.endsWith('/') ? str : `${str}/`;
146
+ }
147
+ export function removeTrailingSlash(str: string): string {
148
+ return removeSuffix(str, '/');
149
+ }