@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.
- package/lib/constants.js +1 -2
- package/lib/constants.js.map +1 -1
- package/lib/dataFileUtils.js +4 -4
- package/lib/dataFileUtils.js.map +1 -1
- package/lib/emitUtils.d.ts +23 -0
- package/lib/emitUtils.d.ts.map +1 -0
- package/lib/emitUtils.js +90 -0
- package/lib/emitUtils.js.map +1 -0
- package/lib/gitUtils.js +2 -2
- package/lib/gitUtils.js.map +1 -1
- package/lib/globUtils.js +3 -3
- package/lib/globUtils.js.map +1 -1
- package/lib/hashUtils.js +1 -1
- package/lib/hashUtils.js.map +1 -1
- package/lib/i18nUtils.d.ts +17 -0
- package/lib/i18nUtils.d.ts.map +1 -0
- package/lib/i18nUtils.js +40 -0
- package/lib/i18nUtils.js.map +1 -0
- package/lib/index.d.ts +5 -49
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +43 -249
- package/lib/index.js.map +1 -1
- package/lib/jsUtils.d.ts +17 -0
- package/lib/jsUtils.d.ts.map +1 -0
- package/lib/jsUtils.js +72 -0
- package/lib/jsUtils.js.map +1 -0
- package/lib/markdownLinks.js +1 -1
- package/lib/markdownLinks.js.map +1 -1
- package/lib/markdownParser.js +5 -6
- package/lib/markdownParser.js.map +1 -1
- package/lib/pathUtils.d.ts +1 -0
- package/lib/pathUtils.d.ts.map +1 -1
- package/lib/pathUtils.js +9 -2
- package/lib/pathUtils.js.map +1 -1
- package/lib/slugger.js +2 -2
- package/lib/slugger.js.map +1 -1
- package/lib/tags.js +2 -3
- package/lib/tags.js.map +1 -1
- package/lib/urlUtils.d.ts +11 -0
- package/lib/urlUtils.d.ts.map +1 -1
- package/lib/urlUtils.js +56 -1
- package/lib/urlUtils.js.map +1 -1
- package/lib/webpackUtils.js +1 -1
- package/lib/webpackUtils.js.map +1 -1
- package/package.json +4 -4
- package/src/emitUtils.ts +113 -0
- package/src/i18nUtils.ts +58 -0
- package/src/index.ts +26 -300
- package/src/jsUtils.ts +88 -0
- package/src/pathUtils.ts +8 -2
- 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 {
|
|
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
|
+
}
|