@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
@@ -13,15 +13,25 @@ import type {ContentPaths} from './markdownLinks';
13
13
  import logger from '@docusaurus/logger';
14
14
 
15
15
  type DataFileParams = {
16
+ /** Path to the potential data file, relative to `contentPaths` */
16
17
  filePath: string;
18
+ /**
19
+ * Includes the base path and localized path, both of which are eligible for
20
+ * sourcing data files. Both paths should be absolute.
21
+ */
17
22
  contentPaths: ContentPaths;
18
23
  };
19
24
 
25
+ /**
26
+ * Looks for a data file in the potential content paths; loads a localized data
27
+ * file in priority.
28
+ *
29
+ * @returns An absolute path to the data file, or `undefined` if there isn't one.
30
+ */
20
31
  export async function getDataFilePath({
21
32
  filePath,
22
33
  contentPaths,
23
34
  }: DataFileParams): Promise<string | undefined> {
24
- // Loads a localized data file in priority
25
35
  const contentPath = await findFolderContainingFile(
26
36
  getContentPathList(contentPaths),
27
37
  filePath,
@@ -33,11 +43,17 @@ export async function getDataFilePath({
33
43
  }
34
44
 
35
45
  /**
36
- * Looks up for a data file in the content paths, returns the normalized object.
37
- * Throws when validation fails; returns undefined when file not found
46
+ * Looks up for a data file in the content paths, returns the object validated
47
+ * and normalized according to the `validate` callback.
48
+ *
49
+ * @returns `undefined` when file not found
50
+ * @throws Throws when validation fails, displaying a helpful context message.
38
51
  */
39
52
  export async function getDataFileData<T>(
40
- params: DataFileParams & {fileType: string},
53
+ params: DataFileParams & {
54
+ /** Used for the "The X file looks invalid" message. */
55
+ fileType: string;
56
+ },
41
57
  validate: (content: unknown) => T,
42
58
  ): Promise<T | undefined> {
43
59
  const filePath = await getDataFilePath(params);
@@ -49,18 +65,26 @@ export async function getDataFileData<T>(
49
65
  const unsafeContent = Yaml.load(contentString);
50
66
  return validate(unsafeContent);
51
67
  } catch (err) {
52
- // TODO replace later by error cause, see https://v8.dev/features/error-cause
53
68
  logger.error`The ${params.fileType} file at path=${filePath} looks invalid.`;
54
69
  throw err;
55
70
  }
56
71
  }
57
72
 
58
- // Order matters: we look in priority in localized folder
73
+ /**
74
+ * Takes the `contentPaths` data structure and returns an ordered path list
75
+ * indicating their priorities. For all data, we look in the localized folder
76
+ * in priority.
77
+ */
59
78
  export function getContentPathList(contentPaths: ContentPaths): string[] {
60
79
  return [contentPaths.contentPathLocalized, contentPaths.contentPath];
61
80
  }
62
81
 
63
- // return the first folder path in which the file exists in
82
+ /**
83
+ * @param folderPaths a list of absolute paths.
84
+ * @param relativeFilePath file path relative to each `folderPaths`.
85
+ * @returns the first folder path in which the file exists, or `undefined` if
86
+ * none is found.
87
+ */
64
88
  export async function findFolderContainingFile(
65
89
  folderPaths: string[],
66
90
  relativeFilePath: string,
@@ -70,6 +94,16 @@ export async function findFolderContainingFile(
70
94
  );
71
95
  }
72
96
 
97
+ /**
98
+ * Fail-fast alternative to `findFolderContainingFile`.
99
+ *
100
+ * @param folderPaths a list of absolute paths.
101
+ * @param relativeFilePath file path relative to each `folderPaths`.
102
+ * @returns the first folder path in which the file exists.
103
+ * @throws Throws if no file can be found. You should use this method only when
104
+ * you actually know the file exists (e.g. when the `relativeFilePath` is read
105
+ * with a glob and you are just trying to localize it)
106
+ */
73
107
  export async function getFolderContainingFile(
74
108
  folderPaths: string[],
75
109
  relativeFilePath: string,
@@ -78,12 +112,10 @@ export async function getFolderContainingFile(
78
112
  folderPaths,
79
113
  relativeFilePath,
80
114
  );
81
- // should never happen, as the source was read from the FS anyway...
82
115
  if (!maybeFolderPath) {
83
116
  throw new Error(
84
- `File "${relativeFilePath}" does not exist in any of these folders:\n- ${folderPaths.join(
85
- '\n- ',
86
- )}]`,
117
+ `File "${relativeFilePath}" does not exist in any of these folders:
118
+ - ${folderPaths.join('\n- ')}`,
87
119
  );
88
120
  }
89
121
  return maybeFolderPath;
package/src/emitUtils.ts CHANGED
@@ -13,6 +13,16 @@ import {findAsyncSequential} from './jsUtils';
13
13
 
14
14
  const fileHash = new Map<string, string>();
15
15
 
16
+ /**
17
+ * Outputs a file to the generated files directory. Only writes files if content
18
+ * differs from cache (for hot reload performance).
19
+ *
20
+ * @param generatedFilesDir Absolute path.
21
+ * @param file Path relative to `generatedFilesDir`.
22
+ * @param content String content to write.
23
+ * @param skipCache If `true` (defaults as `true` for production), file is
24
+ * force-rewritten, skipping cache.
25
+ */
16
26
  export async function generate(
17
27
  generatedFilesDir: string,
18
28
  file: string,
@@ -22,16 +32,22 @@ export async function generate(
22
32
  const filepath = path.join(generatedFilesDir, file);
23
33
 
24
34
  if (skipCache) {
25
- await fs.ensureDir(path.dirname(filepath));
26
- await fs.writeFile(filepath, content);
35
+ await fs.outputFile(filepath, content);
36
+ // Cache still needs to be reset, otherwise, writing "A", "B", and "A" where
37
+ // "B" skips cache will cause the last "A" not be able to overwrite as the
38
+ // first "A" remains in cache. But if the file never existed in cache, no
39
+ // need to register it.
40
+ if (fileHash.get(filepath)) {
41
+ fileHash.set(filepath, createHash('md5').update(content).digest('hex'));
42
+ }
27
43
  return;
28
44
  }
29
45
 
30
46
  let lastHash = fileHash.get(filepath);
31
47
 
32
- // If file already exists but its not in runtime cache yet,
33
- // we try to calculate the content hash and then compare
34
- // This is to avoid unnecessary overwriting and we can reuse old file.
48
+ // If file already exists but it's not in runtime cache yet, we try to
49
+ // calculate the content hash and then compare. This is to avoid unnecessary
50
+ // overwriting and we can reuse old file.
35
51
  if (!lastHash && (await fs.pathExists(filepath))) {
36
52
  const lastContent = await fs.readFile(filepath, 'utf8');
37
53
  lastHash = createHash('md5').update(lastContent).digest('hex');
@@ -41,13 +57,12 @@ export async function generate(
41
57
  const currentHash = createHash('md5').update(content).digest('hex');
42
58
 
43
59
  if (lastHash !== currentHash) {
44
- await fs.ensureDir(path.dirname(filepath));
45
- await fs.writeFile(filepath, content);
60
+ await fs.outputFile(filepath, content);
46
61
  fileHash.set(filepath, currentHash);
47
62
  }
48
63
  }
49
64
 
50
- const chunkNameCache = new Map();
65
+ const chunkNameCache = new Map<string, string>();
51
66
 
52
67
  /**
53
68
  * Generate unique chunk name given a module path.
@@ -58,7 +73,7 @@ export function genChunkName(
58
73
  preferredName?: string,
59
74
  shortId: boolean = process.env.NODE_ENV === 'production',
60
75
  ): string {
61
- let chunkName: string | undefined = chunkNameCache.get(modulePath);
76
+ let chunkName = chunkNameCache.get(modulePath);
62
77
  if (!chunkName) {
63
78
  if (shortId) {
64
79
  chunkName = simpleHash(modulePath, 8);
@@ -84,6 +99,8 @@ export function genChunkName(
84
99
  * @returns This returns a buffer, which you have to decode string yourself if
85
100
  * needed. (Not always necessary since the output isn't for human consumption
86
101
  * anyways, and most HTML manipulation libs accept buffers)
102
+ * @throws Throws when the HTML file is not found at any of the potential paths.
103
+ * This should never happen as it would lead to a 404.
87
104
  */
88
105
  export async function readOutputHTMLFile(
89
106
  permalink: string,
package/src/gitUtils.ts CHANGED
@@ -8,9 +8,70 @@
8
8
  import path from 'path';
9
9
  import shell from 'shelljs';
10
10
 
11
+ /** Custom error thrown when git is not found in `PATH`. */
11
12
  export class GitNotFoundError extends Error {}
12
13
 
13
- export const getFileCommitDate = (
14
+ /** Custom error thrown when the current file is not tracked by git. */
15
+ export class FileNotTrackedError extends Error {}
16
+
17
+ /**
18
+ * Fetches the git history of a file and returns a relevant commit date.
19
+ * It gets the commit date instead of author date so that amended commits
20
+ * can have their dates updated.
21
+ *
22
+ * @throws {GitNotFoundError} If git is not found in `PATH`.
23
+ * @throws {FileNotTrackedError} If the current file is not tracked by git.
24
+ * @throws Also throws when `git log` exited with non-zero, or when it outputs
25
+ * unexpected text.
26
+ */
27
+ export function getFileCommitDate(
28
+ /** Absolute path to the file. */
29
+ file: string,
30
+ args: {
31
+ /**
32
+ * `"oldest"` is the commit that added the file, following renames;
33
+ * `"newest"` is the last commit that edited the file.
34
+ */
35
+ age?: 'oldest' | 'newest';
36
+ /** Use `includeAuthor: true` to get the author information as well. */
37
+ includeAuthor?: false;
38
+ },
39
+ ): {
40
+ /** Relevant commit date. */
41
+ date: Date;
42
+ /** Timestamp in **seconds**, as returned from git. */
43
+ timestamp: number;
44
+ };
45
+ /**
46
+ * Fetches the git history of a file and returns a relevant commit date.
47
+ * It gets the commit date instead of author date so that amended commits
48
+ * can have their dates updated.
49
+ *
50
+ * @throws {GitNotFoundError} If git is not found in `PATH`.
51
+ * @throws {FileNotTrackedError} If the current file is not tracked by git.
52
+ * @throws Also throws when `git log` exited with non-zero, or when it outputs
53
+ * unexpected text.
54
+ */
55
+ export function getFileCommitDate(
56
+ /** Absolute path to the file. */
57
+ file: string,
58
+ args: {
59
+ /**
60
+ * `"oldest"` is the commit that added the file, following renames;
61
+ * `"newest"` is the last commit that edited the file.
62
+ */
63
+ age?: 'oldest' | 'newest';
64
+ includeAuthor: true;
65
+ },
66
+ ): {
67
+ /** Relevant commit date. */
68
+ date: Date;
69
+ /** Timestamp in **seconds**, as returned from git. */
70
+ timestamp: number;
71
+ /** The author's name, as returned from git. */
72
+ author: string;
73
+ };
74
+ export function getFileCommitDate(
14
75
  file: string,
15
76
  {
16
77
  age = 'oldest',
@@ -23,7 +84,7 @@ export const getFileCommitDate = (
23
84
  date: Date;
24
85
  timestamp: number;
25
86
  author?: string;
26
- } => {
87
+ } {
27
88
  if (!shell.which('git')) {
28
89
  throw new GitNotFoundError(
29
90
  `Failed to retrieve git history for "${file}" because git is not installed.`,
@@ -36,9 +97,6 @@ export const getFileCommitDate = (
36
97
  );
37
98
  }
38
99
 
39
- const fileBasename = path.basename(file);
40
- const fileDirname = path.dirname(file);
41
-
42
100
  let formatArg = '--format=%ct';
43
101
  if (includeAuthor) {
44
102
  formatArg += ',%an';
@@ -52,10 +110,10 @@ export const getFileCommitDate = (
52
110
  }
53
111
 
54
112
  const result = shell.exec(
55
- `git log ${extraArgs} ${formatArg} -- "${fileBasename}"`,
113
+ `git log ${extraArgs} ${formatArg} -- "${path.basename(file)}"`,
56
114
  {
57
115
  // cwd is important, see: https://github.com/facebook/docusaurus/pull/5048
58
- cwd: fileDirname,
116
+ cwd: path.dirname(file),
59
117
  silent: true,
60
118
  },
61
119
  );
@@ -70,24 +128,26 @@ export const getFileCommitDate = (
70
128
  }
71
129
 
72
130
  const output = result.stdout.trim();
131
+
132
+ if (!output) {
133
+ throw new FileNotTrackedError(
134
+ `Failed to retrieve the git history for file "${file}" because the file is not tracked by git.`,
135
+ );
136
+ }
137
+
73
138
  const match = output.match(regex);
74
139
 
75
- if (
76
- !match ||
77
- !match.groups ||
78
- !match.groups.timestamp ||
79
- (includeAuthor && !match.groups.author)
80
- ) {
140
+ if (!match) {
81
141
  throw new Error(
82
142
  `Failed to retrieve the git history for file "${file}" with unexpected output: ${output}`,
83
143
  );
84
144
  }
85
145
 
86
- const timestamp = Number(match.groups.timestamp);
146
+ const timestamp = Number(match.groups!.timestamp);
87
147
  const date = new Date(timestamp * 1000);
88
148
 
89
149
  if (includeAuthor) {
90
- return {date, timestamp, author: match.groups.author};
150
+ return {date, timestamp, author: match.groups!.author!};
91
151
  }
92
152
  return {date, timestamp};
93
- };
153
+ }
package/src/globUtils.ts CHANGED
@@ -10,24 +10,31 @@
10
10
  import Micromatch from 'micromatch'; // Note: Micromatch is used by Globby
11
11
  import path from 'path';
12
12
 
13
+ /** A re-export of the globby instance. */
13
14
  export {default as Globby} from 'globby';
14
15
 
15
- // The default patterns we ignore when globbing
16
- // using _ prefix for exclusion by convention
16
+ /**
17
+ * The default glob patterns we ignore when sourcing content.
18
+ * - Ignore files and folders starting with `_` recursively
19
+ * - Ignore tests
20
+ */
17
21
  export const GlobExcludeDefault = [
18
- // Ignore files starting with _
19
22
  '**/_*.{js,jsx,ts,tsx,md,mdx}',
20
-
21
- // Ignore folders starting with _ (including folder content)
22
23
  '**/_*/**',
23
-
24
- // Ignore tests
25
24
  '**/*.test.{js,jsx,ts,tsx}',
26
25
  '**/__tests__/**',
27
26
  ];
28
27
 
29
28
  type Matcher = (str: string) => boolean;
30
29
 
30
+ /**
31
+ * A very thin wrapper around `Micromatch.makeRe`.
32
+ *
33
+ * @see {@link createAbsoluteFilePathMatcher}
34
+ * @param patterns A list of glob patterns.
35
+ * @returns A matcher handle that tells if a file path is matched by any of the
36
+ * patterns.
37
+ */
31
38
  export function createMatcher(patterns: string[]): Matcher {
32
39
  const regexp = new RegExp(
33
40
  patterns.map((pattern) => Micromatch.makeRe(pattern).source).join('|'),
@@ -35,10 +42,19 @@ export function createMatcher(patterns: string[]): Matcher {
35
42
  return (str) => regexp.test(str);
36
43
  }
37
44
 
38
- // We use match patterns like '**/_*/**',
39
- // This function permits to help to:
40
- // Match /user/sebastien/website/docs/_partials/xyz.md
41
- // Ignore /user/_sebastien/website/docs/partials/xyz.md
45
+ /**
46
+ * We use match patterns like `"** /_* /**"` (ignore the spaces), where `"_*"`
47
+ * should only be matched within a subfolder. This function would:
48
+ * - Match `/user/sebastien/website/docs/_partials/xyz.md`
49
+ * - Ignore `/user/_sebastien/website/docs/partials/xyz.md`
50
+ *
51
+ * @param patterns A list of glob patterns.
52
+ * @param rootFolders A list of root folders to resolve the glob from.
53
+ * @returns A matcher handle that tells if a file path is matched by any of the
54
+ * patterns, resolved from the first root folder that contains the path.
55
+ * @throws Throws when the returned matcher receives a path that doesn't belong
56
+ * to any of the `rootFolders`.
57
+ */
42
58
  export function createAbsoluteFilePathMatcher(
43
59
  patterns: string[],
44
60
  rootFolders: string[],
@@ -51,8 +67,8 @@ export function createAbsoluteFilePathMatcher(
51
67
  );
52
68
  if (!rootFolder) {
53
69
  throw new Error(
54
- `createAbsoluteFilePathMatcher unexpected error, absoluteFilePath=${absoluteFilePath} was not contained in any of the root folders ${JSON.stringify(
55
- rootFolders,
70
+ `createAbsoluteFilePathMatcher unexpected error, absoluteFilePath=${absoluteFilePath} was not contained in any of the root folders: ${rootFolders.join(
71
+ ', ',
56
72
  )}`,
57
73
  );
58
74
  }
package/src/hashUtils.ts CHANGED
@@ -9,20 +9,21 @@ import {createHash} from 'crypto';
9
9
  import _ from 'lodash';
10
10
  import {shortName, isNameTooLong} from './pathUtils';
11
11
 
12
+ /** Thin wrapper around `crypto.createHash("md5")`. */
12
13
  export function md5Hash(str: string): string {
13
14
  return createHash('md5').update(str).digest('hex');
14
15
  }
15
16
 
17
+ /** Creates an MD5 hash and truncates it to the given length. */
16
18
  export function simpleHash(str: string, length: number): string {
17
- return md5Hash(str).substr(0, length);
19
+ return md5Hash(str).substring(0, length);
18
20
  }
19
21
 
20
22
  // Based on https://github.com/gatsbyjs/gatsby/pull/21518/files
21
23
  /**
22
- * Given an input string, convert to kebab-case and append a hash.
23
- * Avoid str collision.
24
- * Also removes part of the string if its larger than the allowed
25
- * filename per OS. Avoids ERRNAMETOOLONG error.
24
+ * Given an input string, convert to kebab-case and append a hash, avoiding name
25
+ * collision. Also removes part of the string if its larger than the allowed
26
+ * filename per OS, avoiding `ERRNAMETOOLONG` error.
26
27
  */
27
28
  export function docuHash(str: string): string {
28
29
  if (str === '/') {
package/src/i18nUtils.ts CHANGED
@@ -8,16 +8,21 @@
8
8
  import path from 'path';
9
9
  import _ from 'lodash';
10
10
  import type {TranslationFileContent, TranslationFile} from '@docusaurus/types';
11
- import {DEFAULT_PLUGIN_ID} from './constants';
11
+ import {DEFAULT_PLUGIN_ID, I18N_DIR_NAME} from './constants';
12
12
 
13
+ /**
14
+ * Takes a list of translation file contents, and shallow-merges them into one.
15
+ */
13
16
  export function mergeTranslations(
14
17
  contents: TranslationFileContent[],
15
18
  ): TranslationFileContent {
16
19
  return contents.reduce((acc, content) => ({...acc, ...content}), {});
17
20
  }
18
21
 
19
- // Useful to update all the messages of a translation file
20
- // Used in tests to simulate translations
22
+ /**
23
+ * Useful to update all the messages of a translation file. Used in tests to
24
+ * simulate translations.
25
+ */
21
26
  export function updateTranslationFileMessages(
22
27
  translationFile: TranslationFile,
23
28
  updateMessage: (message: string) => string,
@@ -31,6 +36,10 @@ export function updateTranslationFileMessages(
31
36
  };
32
37
  }
33
38
 
39
+ /**
40
+ * Takes everything needed and constructs a plugin i18n path. Plugins should
41
+ * expect everything it needs for translations to be found under this path.
42
+ */
34
43
  export function getPluginI18nPath({
35
44
  siteDir,
36
45
  locale,
@@ -46,7 +55,7 @@ export function getPluginI18nPath({
46
55
  }): string {
47
56
  return path.join(
48
57
  siteDir,
49
- 'i18n',
58
+ I18N_DIR_NAME,
50
59
  // namespace first by locale: convenient to work in a single folder for a
51
60
  // translator
52
61
  locale,
package/src/index.ts CHANGED
@@ -13,15 +13,21 @@ export {
13
13
  BABEL_CONFIG_FILE_NAME,
14
14
  GENERATED_FILES_DIR_NAME,
15
15
  SRC_DIR_NAME,
16
- STATIC_DIR_NAME,
16
+ DEFAULT_STATIC_DIR_NAME,
17
17
  OUTPUT_STATIC_ASSETS_DIR_NAME,
18
18
  THEME_PATH,
19
+ I18N_DIR_NAME,
20
+ CODE_TRANSLATIONS_FILE_NAME,
19
21
  DEFAULT_PORT,
20
22
  DEFAULT_PLUGIN_ID,
21
23
  WEBPACK_URL_LOADER_LIMIT,
22
24
  } from './constants';
23
25
  export {generate, genChunkName, readOutputHTMLFile} from './emitUtils';
24
- export {getFileCommitDate, GitNotFoundError} from './gitUtils';
26
+ export {
27
+ getFileCommitDate,
28
+ FileNotTrackedError,
29
+ GitNotFoundError,
30
+ } from './gitUtils';
25
31
  export {
26
32
  mergeTranslations,
27
33
  updateTranslationFileMessages,
@@ -30,7 +36,6 @@ export {
30
36
  export {
31
37
  removeSuffix,
32
38
  removePrefix,
33
- getElementsAround,
34
39
  mapAsyncSequential,
35
40
  findAsyncSequential,
36
41
  reportMessage,
@@ -45,12 +50,13 @@ export {
45
50
  addLeadingSlash,
46
51
  addTrailingSlash,
47
52
  removeTrailingSlash,
53
+ hasSSHProtocol,
54
+ buildHttpsUrl,
55
+ buildSshUrl,
48
56
  } from './urlUtils';
49
57
  export {
50
58
  type Tag,
51
59
  type FrontMatterTag,
52
- type TaggedItemGroup,
53
- normalizeFrontMatterTag,
54
60
  normalizeFrontMatterTags,
55
61
  groupTaggedItems,
56
62
  } from './tags';
@@ -60,12 +66,12 @@ export {
60
66
  parseFrontMatter,
61
67
  parseMarkdownContentTitle,
62
68
  parseMarkdownString,
63
- } from './markdownParser';
69
+ writeMarkdownHeadingId,
70
+ type WriteHeadingIDOptions,
71
+ } from './markdownUtils';
64
72
  export {
65
73
  type ContentPaths,
66
74
  type BrokenMarkdownLink,
67
- type ReplaceMarkdownLinksParams,
68
- type ReplaceMarkdownLinksReturn,
69
75
  replaceMarkdownLinks,
70
76
  } from './markdownLinks';
71
77
  export {type SluggerOptions, type Slugger, createSlugger} from './slugger';
package/src/jsUtils.ts CHANGED
@@ -8,36 +8,27 @@
8
8
  import type {ReportingSeverity} from '@docusaurus/types';
9
9
  import logger from '@docusaurus/logger';
10
10
 
11
+ /** Removes a given string suffix from `str`. */
11
12
  export function removeSuffix(str: string, suffix: string): string {
12
13
  if (suffix === '') {
13
- return str; // always returns "" otherwise!
14
+ // str.slice(0, 0) is ""
15
+ return str;
14
16
  }
15
17
  return str.endsWith(suffix) ? str.slice(0, -suffix.length) : str;
16
18
  }
17
19
 
20
+ /** Removes a given string prefix from `str`. */
18
21
  export function removePrefix(str: string, prefix: string): string {
19
22
  return str.startsWith(prefix) ? str.slice(prefix.length) : str;
20
23
  }
21
24
 
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
-
25
+ /**
26
+ * `Array#map` for async operations where order matters.
27
+ * @param array The array to traverse.
28
+ * @param action An async action to be performed on every array item. Will be
29
+ * awaited before working on the next.
30
+ * @returns The list of results returned from every `action(item)`
31
+ */
41
32
  export async function mapAsyncSequential<T, R>(
42
33
  array: T[],
43
34
  action: (t: T) => Promise<R>,
@@ -50,6 +41,14 @@ export async function mapAsyncSequential<T, R>(
50
41
  return results;
51
42
  }
52
43
 
44
+ /**
45
+ * `Array#find` for async operations where order matters.
46
+ * @param array The array to traverse.
47
+ * @param predicate An async predicate to be called on every array item. Should
48
+ * return a boolean indicating whether the currently element should be returned.
49
+ * @returns The function immediately returns the first item on which `predicate`
50
+ * returns `true`, or `undefined` if none matches the predicate.
51
+ */
53
52
  export async function findAsyncSequential<T>(
54
53
  array: T[],
55
54
  predicate: (t: T) => Promise<boolean>,
@@ -62,6 +61,21 @@ export async function findAsyncSequential<T>(
62
61
  return undefined;
63
62
  }
64
63
 
64
+ /**
65
+ * Takes a message and reports it according to the severity that the user wants.
66
+ *
67
+ * - `ignore`: completely no-op
68
+ * - `log`: uses the `INFO` log level
69
+ * - `warn`: uses the `WARN` log level
70
+ * - `error`: uses the `ERROR` log level
71
+ * - `throw`: aborts the process, throws the error.
72
+ *
73
+ * Since the logger doesn't have logging level filters yet, these severities
74
+ * mostly just differ by their colors.
75
+ *
76
+ * @throws In addition to throwing when `reportingSeverity === "throw"`, this
77
+ * function also throws if `reportingSeverity` is not one of the above.
78
+ */
65
79
  export function reportMessage(
66
80
  message: string,
67
81
  reportingSeverity: ReportingSeverity,