@docusaurus/utils 2.0.0-beta.fbdeefcac → 2.0.0-rc.1

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 (112) hide show
  1. package/lib/constants.d.ts +73 -0
  2. package/lib/constants.d.ts.map +1 -0
  3. package/lib/constants.js +78 -0
  4. package/lib/constants.js.map +1 -0
  5. package/lib/dataFileUtils.d.ts +60 -0
  6. package/lib/dataFileUtils.d.ts.map +1 -0
  7. package/lib/dataFileUtils.js +91 -0
  8. package/lib/dataFileUtils.js.map +1 -0
  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 +66 -0
  14. package/lib/gitUtils.d.ts.map +1 -0
  15. package/lib/gitUtils.js +63 -0
  16. package/lib/gitUtils.js.map +1 -0
  17. package/lib/globUtils.d.ts +40 -0
  18. package/lib/globUtils.d.ts.map +1 -0
  19. package/lib/globUtils.js +71 -0
  20. package/lib/globUtils.js.map +1 -0
  21. package/lib/hashUtils.d.ts +6 -4
  22. package/lib/hashUtils.d.ts.map +1 -0
  23. package/lib/hashUtils.js +13 -10
  24. package/lib/hashUtils.js.map +1 -0
  25. package/lib/i18nUtils.d.ts +53 -0
  26. package/lib/i18nUtils.d.ts.map +1 -0
  27. package/lib/i18nUtils.js +70 -0
  28. package/lib/i18nUtils.js.map +1 -0
  29. package/lib/index.d.ts +16 -75
  30. package/lib/index.d.ts.map +1 -0
  31. package/lib/index.js +91 -395
  32. package/lib/index.js.map +1 -0
  33. package/lib/jsUtils.d.ts +28 -0
  34. package/lib/jsUtils.d.ts.map +1 -0
  35. package/lib/jsUtils.js +57 -0
  36. package/lib/jsUtils.js.map +1 -0
  37. package/lib/markdownLinks.d.ts +49 -5
  38. package/lib/markdownLinks.d.ts.map +1 -0
  39. package/lib/markdownLinks.js +57 -13
  40. package/lib/markdownLinks.js.map +1 -0
  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 +45 -1
  46. package/lib/pathUtils.d.ts.map +1 -0
  47. package/lib/pathUtils.js +92 -12
  48. package/lib/pathUtils.js.map +1 -0
  49. package/lib/shellUtils.d.ts +8 -0
  50. package/lib/shellUtils.d.ts.map +1 -0
  51. package/lib/shellUtils.js +21 -0
  52. package/lib/shellUtils.js.map +1 -0
  53. package/lib/slugger.d.ts +24 -0
  54. package/lib/slugger.d.ts.map +1 -0
  55. package/lib/slugger.js +23 -0
  56. package/lib/slugger.js.map +1 -0
  57. package/lib/tags.d.ts +59 -0
  58. package/lib/tags.d.ts.map +1 -0
  59. package/lib/tags.js +91 -0
  60. package/lib/tags.js.map +1 -0
  61. package/lib/urlUtils.d.ts +66 -0
  62. package/lib/urlUtils.d.ts.map +1 -0
  63. package/lib/urlUtils.js +207 -0
  64. package/lib/urlUtils.js.map +1 -0
  65. package/lib/webpackUtils.d.ts +35 -0
  66. package/lib/webpackUtils.d.ts.map +1 -0
  67. package/lib/webpackUtils.js +115 -0
  68. package/lib/webpackUtils.js.map +1 -0
  69. package/package.json +28 -10
  70. package/src/constants.ts +98 -0
  71. package/src/dataFileUtils.ts +122 -0
  72. package/src/deps.d.ts +10 -0
  73. package/src/emitUtils.ts +99 -0
  74. package/src/gitUtils.ts +146 -0
  75. package/src/globUtils.ts +85 -0
  76. package/src/hashUtils.ts +9 -8
  77. package/src/i18nUtils.ts +114 -0
  78. package/src/index.ts +97 -502
  79. package/src/jsUtils.ts +59 -0
  80. package/src/markdownLinks.ts +101 -30
  81. package/src/markdownUtils.ts +357 -0
  82. package/src/pathUtils.ts +93 -12
  83. package/src/shellUtils.ts +18 -0
  84. package/src/slugger.ts +36 -0
  85. package/src/tags.ts +130 -0
  86. package/src/urlUtils.ts +234 -0
  87. package/src/webpackUtils.ts +153 -0
  88. package/lib/.tsbuildinfo +0 -1
  89. package/lib/codeTranslationsUtils.d.ts +0 -11
  90. package/lib/codeTranslationsUtils.js +0 -50
  91. package/lib/escapePath.d.ts +0 -17
  92. package/lib/escapePath.js +0 -25
  93. package/lib/markdownParser.d.ts +0 -30
  94. package/lib/markdownParser.js +0 -140
  95. package/lib/posixPath.d.ts +0 -14
  96. package/lib/posixPath.js +0 -28
  97. package/src/__tests__/__fixtures__/defaultCodeTranslations/en.json +0 -4
  98. package/src/__tests__/__fixtures__/defaultCodeTranslations/fr-FR.json +0 -5
  99. package/src/__tests__/__fixtures__/defaultCodeTranslations/fr.json +0 -4
  100. package/src/__tests__/__snapshots__/index.test.ts.snap +0 -8
  101. package/src/__tests__/codeTranslationsUtils.test.ts +0 -112
  102. package/src/__tests__/escapePath.test.ts +0 -25
  103. package/src/__tests__/hashUtils.test.ts +0 -51
  104. package/src/__tests__/index.test.ts +0 -631
  105. package/src/__tests__/markdownParser.test.ts +0 -817
  106. package/src/__tests__/pathUtils.test.ts +0 -63
  107. package/src/__tests__/posixPath.test.ts +0 -25
  108. package/src/codeTranslationsUtils.ts +0 -56
  109. package/src/escapePath.ts +0 -23
  110. package/src/markdownParser.ts +0 -182
  111. package/src/posixPath.ts +0 -27
  112. package/tsconfig.json +0 -9
@@ -0,0 +1,99 @@
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 path from 'path';
9
+ import fs from 'fs-extra';
10
+ import {createHash} from 'crypto';
11
+ import {findAsyncSequential} from './jsUtils';
12
+
13
+ const fileHash = new Map<string, string>();
14
+
15
+ /**
16
+ * Outputs a file to the generated files directory. Only writes files if content
17
+ * differs from cache (for hot reload performance).
18
+ *
19
+ * @param generatedFilesDir Absolute path.
20
+ * @param file Path relative to `generatedFilesDir`. File will always be
21
+ * outputted; no need to ensure directory exists.
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
+ */
26
+ export async function generate(
27
+ generatedFilesDir: string,
28
+ file: string,
29
+ content: string,
30
+ skipCache: boolean = process.env.NODE_ENV === 'production',
31
+ ): Promise<void> {
32
+ const filepath = path.resolve(generatedFilesDir, file);
33
+
34
+ if (skipCache) {
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
+ }
43
+ return;
44
+ }
45
+
46
+ let lastHash = fileHash.get(filepath);
47
+
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.
51
+ if (!lastHash && (await fs.pathExists(filepath))) {
52
+ const lastContent = await fs.readFile(filepath, 'utf8');
53
+ lastHash = createHash('md5').update(lastContent).digest('hex');
54
+ fileHash.set(filepath, lastHash);
55
+ }
56
+
57
+ const currentHash = createHash('md5').update(content).digest('hex');
58
+
59
+ if (lastHash !== currentHash) {
60
+ await fs.outputFile(filepath, content);
61
+ fileHash.set(filepath, currentHash);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * @param permalink The URL that the HTML file corresponds to, without base URL
67
+ * @param outDir Full path to the output directory
68
+ * @param trailingSlash The site config option. If provided, only one path will
69
+ * be read.
70
+ * @returns This returns a buffer, which you have to decode string yourself if
71
+ * needed. (Not always necessary since the output isn't for human consumption
72
+ * anyways, and most HTML manipulation libs accept buffers)
73
+ * @throws Throws when the HTML file is not found at any of the potential paths.
74
+ * This should never happen as it would lead to a 404.
75
+ */
76
+ export async function readOutputHTMLFile(
77
+ permalink: string,
78
+ outDir: string,
79
+ trailingSlash: boolean | undefined,
80
+ ): Promise<Buffer> {
81
+ const withTrailingSlashPath = path.join(outDir, permalink, 'index.html');
82
+ const withoutTrailingSlashPath = path.join(
83
+ outDir,
84
+ `${permalink.replace(/\/$/, '')}.html`,
85
+ );
86
+ const HTMLPath = await findAsyncSequential(
87
+ [
88
+ trailingSlash !== false && withTrailingSlashPath,
89
+ trailingSlash !== true && withoutTrailingSlashPath,
90
+ ].filter((p): p is string => Boolean(p)),
91
+ fs.pathExists,
92
+ );
93
+ if (!HTMLPath) {
94
+ throw new Error(
95
+ `Expected output HTML file to be found at ${withTrailingSlashPath}.`,
96
+ );
97
+ }
98
+ return fs.readFile(HTMLPath);
99
+ }
@@ -0,0 +1,146 @@
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 path from 'path';
9
+ import shell from 'shelljs';
10
+
11
+ /** Custom error thrown when git is not found in `PATH`. */
12
+ export class GitNotFoundError extends Error {}
13
+
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 {@link GitNotFoundError} If git is not found in `PATH`.
23
+ * @throws {@link 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 {@link GitNotFoundError} If git is not found in `PATH`.
51
+ * @throws {@link 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(
75
+ file: string,
76
+ {
77
+ age = 'oldest',
78
+ includeAuthor = false,
79
+ }: {
80
+ age?: 'oldest' | 'newest';
81
+ includeAuthor?: boolean;
82
+ },
83
+ ): {
84
+ date: Date;
85
+ timestamp: number;
86
+ author?: string;
87
+ } {
88
+ if (!shell.which('git')) {
89
+ throw new GitNotFoundError(
90
+ `Failed to retrieve git history for "${file}" because git is not installed.`,
91
+ );
92
+ }
93
+
94
+ if (!shell.test('-f', file)) {
95
+ throw new Error(
96
+ `Failed to retrieve git history for "${file}" because the file does not exist.`,
97
+ );
98
+ }
99
+
100
+ const args = [
101
+ `--format=%ct${includeAuthor ? ',%an' : ''}`,
102
+ '--max-count=1',
103
+ age === 'oldest' ? '--follow --diff-filter=A' : undefined,
104
+ ]
105
+ .filter(Boolean)
106
+ .join(' ');
107
+
108
+ const result = shell.exec(`git log ${args} -- "${path.basename(file)}"`, {
109
+ // Setting cwd is important, see: https://github.com/facebook/docusaurus/pull/5048
110
+ cwd: path.dirname(file),
111
+ silent: true,
112
+ });
113
+ if (result.code !== 0) {
114
+ throw new Error(
115
+ `Failed to retrieve the git history for file "${file}" with exit code ${result.code}: ${result.stderr}`,
116
+ );
117
+ }
118
+ let regex = /^(?<timestamp>\d+)$/;
119
+ if (includeAuthor) {
120
+ regex = /^(?<timestamp>\d+),(?<author>.+)$/;
121
+ }
122
+
123
+ const output = result.stdout.trim();
124
+
125
+ if (!output) {
126
+ throw new FileNotTrackedError(
127
+ `Failed to retrieve the git history for file "${file}" because the file is not tracked by git.`,
128
+ );
129
+ }
130
+
131
+ const match = output.match(regex);
132
+
133
+ if (!match) {
134
+ throw new Error(
135
+ `Failed to retrieve the git history for file "${file}" with unexpected output: ${output}`,
136
+ );
137
+ }
138
+
139
+ const timestamp = Number(match.groups!.timestamp);
140
+ const date = new Date(timestamp * 1000);
141
+
142
+ if (includeAuthor) {
143
+ return {date, timestamp, author: match.groups!.author!};
144
+ }
145
+ return {date, timestamp};
146
+ }
@@ -0,0 +1,85 @@
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
+ // Globby/Micromatch are the 2 libs we use in Docusaurus consistently
9
+
10
+ import path from 'path';
11
+ import Micromatch from 'micromatch'; // Note: Micromatch is used by Globby
12
+
13
+ /** A re-export of the globby instance. */
14
+ export {default as Globby} from 'globby';
15
+
16
+ /**
17
+ * The default glob patterns we ignore when sourcing content.
18
+ * - Ignore files and folders starting with `_` recursively
19
+ * - Ignore tests
20
+ */
21
+ export const GlobExcludeDefault = [
22
+ '**/_*.{js,jsx,ts,tsx,md,mdx}',
23
+ '**/_*/**',
24
+ '**/*.test.{js,jsx,ts,tsx}',
25
+ '**/__tests__/**',
26
+ ];
27
+
28
+ type Matcher = (str: string) => boolean;
29
+
30
+ /**
31
+ * A very thin wrapper around `Micromatch.makeRe`.
32
+ *
33
+ * @see {@link createAbsoluteFilePathMatcher}
34
+ * @param patterns A list of glob patterns. If the list is empty, it defaults to
35
+ * matching none.
36
+ * @returns A matcher handle that tells if a file path is matched by any of the
37
+ * patterns.
38
+ */
39
+ export function createMatcher(patterns: string[]): Matcher {
40
+ if (patterns.length === 0) {
41
+ // `/(?:)/.test("foo")` is `true`
42
+ return () => false;
43
+ }
44
+ const regexp = new RegExp(
45
+ patterns.map((pattern) => Micromatch.makeRe(pattern).source).join('|'),
46
+ );
47
+ return (str) => regexp.test(str);
48
+ }
49
+
50
+ /**
51
+ * We use match patterns like `"** /_* /**"` (ignore the spaces), where `"_*"`
52
+ * should only be matched within a subfolder. This function would:
53
+ * - Match `/user/sebastien/website/docs/_partials/xyz.md`
54
+ * - Ignore `/user/_sebastien/website/docs/partials/xyz.md`
55
+ *
56
+ * @param patterns A list of glob patterns.
57
+ * @param rootFolders A list of root folders to resolve the glob from.
58
+ * @returns A matcher handle that tells if a file path is matched by any of the
59
+ * patterns, resolved from the first root folder that contains the path.
60
+ * @throws Throws when the returned matcher receives a path that doesn't belong
61
+ * to any of the `rootFolders`.
62
+ */
63
+ export function createAbsoluteFilePathMatcher(
64
+ patterns: string[],
65
+ rootFolders: string[],
66
+ ): Matcher {
67
+ const matcher = createMatcher(patterns);
68
+
69
+ function getRelativeFilePath(absoluteFilePath: string) {
70
+ const rootFolder = rootFolders.find((folderPath) =>
71
+ absoluteFilePath.startsWith(folderPath),
72
+ );
73
+ if (!rootFolder) {
74
+ throw new Error(
75
+ `createAbsoluteFilePathMatcher unexpected error, absoluteFilePath=${absoluteFilePath} was not contained in any of the root folders: ${rootFolders.join(
76
+ ', ',
77
+ )}`,
78
+ );
79
+ }
80
+ return path.relative(rootFolder, absoluteFilePath);
81
+ }
82
+
83
+ return (absoluteFilePath: string) =>
84
+ matcher(getRelativeFilePath(absoluteFilePath));
85
+ }
package/src/hashUtils.ts CHANGED
@@ -6,32 +6,33 @@
6
6
  */
7
7
 
8
8
  import {createHash} from 'crypto';
9
- import {kebabCase} from 'lodash';
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 === '/') {
29
30
  return 'index';
30
31
  }
31
32
  const shortHash = simpleHash(str, 3);
32
- const parsedPath = `${kebabCase(str)}-${shortHash}`;
33
+ const parsedPath = `${_.kebabCase(str)}-${shortHash}`;
33
34
  if (isNameTooLong(parsedPath)) {
34
- return `${shortName(kebabCase(str))}-${shortHash}`;
35
+ return `${shortName(_.kebabCase(str))}-${shortHash}`;
35
36
  }
36
37
  return parsedPath;
37
38
  }
@@ -0,0 +1,114 @@
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 path from 'path';
9
+ import _ from 'lodash';
10
+ import {DEFAULT_PLUGIN_ID} from './constants';
11
+ import {normalizeUrl} from './urlUtils';
12
+ import type {
13
+ TranslationFileContent,
14
+ TranslationFile,
15
+ I18n,
16
+ } from '@docusaurus/types';
17
+
18
+ /**
19
+ * Takes a list of translation file contents, and shallow-merges them into one.
20
+ */
21
+ export function mergeTranslations(
22
+ contents: TranslationFileContent[],
23
+ ): TranslationFileContent {
24
+ return contents.reduce((acc, content) => ({...acc, ...content}), {});
25
+ }
26
+
27
+ /**
28
+ * Useful to update all the messages of a translation file. Used in tests to
29
+ * simulate translations.
30
+ */
31
+ export function updateTranslationFileMessages(
32
+ translationFile: TranslationFile,
33
+ updateMessage: (message: string) => string,
34
+ ): TranslationFile {
35
+ return {
36
+ ...translationFile,
37
+ content: _.mapValues(translationFile.content, (translation) => ({
38
+ ...translation,
39
+ message: updateMessage(translation.message),
40
+ })),
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Takes everything needed and constructs a plugin i18n path. Plugins should
46
+ * expect everything it needs for translations to be found under this path.
47
+ */
48
+ export function getPluginI18nPath({
49
+ localizationDir,
50
+ pluginName,
51
+ pluginId = DEFAULT_PLUGIN_ID,
52
+ subPaths = [],
53
+ }: {
54
+ localizationDir: string;
55
+ pluginName: string;
56
+ pluginId?: string | undefined;
57
+ subPaths?: string[];
58
+ }): string {
59
+ return path.join(
60
+ localizationDir,
61
+ // Make it convenient to use for single-instance
62
+ // ie: return "docs", not "docs-default" nor "docs/default"
63
+ `${pluginName}${pluginId === DEFAULT_PLUGIN_ID ? '' : `-${pluginId}`}`,
64
+ ...subPaths,
65
+ );
66
+ }
67
+
68
+ /**
69
+ * Takes a path and returns a localized a version (which is basically `path +
70
+ * i18n.currentLocale`).
71
+ *
72
+ * This is used to resolve the `outDir` and `baseUrl` of each locale; it is NOT
73
+ * used to determine plugin localization file locations.
74
+ */
75
+ export function localizePath({
76
+ pathType,
77
+ path: originalPath,
78
+ i18n,
79
+ options = {},
80
+ }: {
81
+ /**
82
+ * FS paths will treat Windows specially; URL paths will always have a
83
+ * trailing slash to make it a valid base URL.
84
+ */
85
+ pathType: 'fs' | 'url';
86
+ /** The path, URL or file path, to be localized. */
87
+ path: string;
88
+ /** The current i18n context. */
89
+ i18n: I18n;
90
+ options?: {
91
+ /**
92
+ * By default, we don't localize the path of defaultLocale. This option
93
+ * would override that behavior. Setting `false` is useful for `yarn build
94
+ * -l zh-Hans` to always emit into the root build directory.
95
+ */
96
+ localizePath?: boolean;
97
+ };
98
+ }): string {
99
+ const shouldLocalizePath: boolean =
100
+ options.localizePath ?? i18n.currentLocale !== i18n.defaultLocale;
101
+
102
+ if (!shouldLocalizePath) {
103
+ return originalPath;
104
+ }
105
+ // FS paths need special care, for Windows support. Note: we don't use the
106
+ // locale config's `path` here, because this function is used for resolving
107
+ // outDir, which must be the same as baseUrl. When we have the baseUrl config,
108
+ // we need to sync the two.
109
+ if (pathType === 'fs') {
110
+ return path.join(originalPath, i18n.currentLocale);
111
+ }
112
+ // Url paths; add a trailing slash so it's a valid base URL
113
+ return normalizeUrl([originalPath, i18n.currentLocale, '/']);
114
+ }