@docusaurus/utils 2.0.0-beta.15a2b59f9 → 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.d.ts +20 -0
- package/lib/constants.d.ts.map +1 -0
- package/lib/constants.js +26 -0
- package/lib/constants.js.map +1 -0
- package/lib/dataFileUtils.d.ts +24 -0
- package/lib/dataFileUtils.d.ts.map +1 -0
- package/lib/dataFileUtils.js +65 -0
- package/lib/dataFileUtils.js.map +1 -0
- 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.d.ts +17 -0
- package/lib/gitUtils.d.ts.map +1 -0
- package/lib/gitUtils.js +63 -0
- package/lib/gitUtils.js.map +1 -0
- package/lib/globUtils.d.ts +12 -0
- package/lib/globUtils.d.ts.map +1 -0
- package/lib/globUtils.js +48 -0
- package/lib/globUtils.js.map +1 -0
- package/lib/hashUtils.d.ts +1 -0
- package/lib/hashUtils.d.ts.map +1 -0
- package/lib/hashUtils.js +7 -5
- package/lib/hashUtils.js.map +1 -0
- 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 +15 -75
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +84 -395
- package/lib/index.js.map +1 -0
- 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.d.ts +1 -0
- package/lib/markdownLinks.d.ts.map +1 -0
- package/lib/markdownLinks.js +36 -11
- package/lib/markdownLinks.js.map +1 -0
- package/lib/markdownParser.d.ts +5 -3
- package/lib/markdownParser.d.ts.map +1 -0
- package/lib/markdownParser.js +72 -52
- package/lib/markdownParser.js.map +1 -0
- package/lib/pathUtils.d.ts +44 -0
- package/lib/pathUtils.d.ts.map +1 -0
- package/lib/pathUtils.js +88 -10
- package/lib/pathUtils.js.map +1 -0
- package/lib/slugger.d.ts +14 -0
- package/lib/slugger.d.ts.map +1 -0
- package/lib/slugger.js +19 -0
- package/lib/slugger.js.map +1 -0
- package/lib/tags.d.ts +27 -0
- package/lib/tags.d.ts.map +1 -0
- package/lib/tags.js +76 -0
- package/lib/tags.js.map +1 -0
- package/lib/urlUtils.d.ts +20 -0
- package/lib/urlUtils.d.ts.map +1 -0
- package/lib/urlUtils.js +136 -0
- package/lib/urlUtils.js.map +1 -0
- package/lib/webpackUtils.d.ts +30 -0
- package/lib/webpackUtils.d.ts.map +1 -0
- package/lib/webpackUtils.js +112 -0
- package/lib/webpackUtils.js.map +1 -0
- package/package.json +20 -10
- package/src/constants.ts +38 -0
- package/src/dataFileUtils.ts +90 -0
- package/src/deps.d.ts +10 -0
- package/src/emitUtils.ts +113 -0
- package/src/gitUtils.ts +93 -0
- package/src/globUtils.ts +64 -0
- package/src/hashUtils.ts +3 -3
- package/src/i18nUtils.ts +58 -0
- package/src/index.ts +87 -502
- package/src/jsUtils.ts +88 -0
- package/src/markdownLinks.ts +35 -13
- package/src/markdownParser.ts +76 -57
- package/src/pathUtils.ts +87 -8
- package/src/slugger.ts +24 -0
- package/src/tags.ts +105 -0
- package/src/urlUtils.ts +149 -0
- package/src/webpackUtils.ts +146 -0
- package/lib/.tsbuildinfo +0 -1
- package/lib/codeTranslationsUtils.d.ts +0 -11
- package/lib/codeTranslationsUtils.js +0 -50
- package/lib/escapePath.d.ts +0 -17
- package/lib/escapePath.js +0 -25
- package/lib/posixPath.d.ts +0 -14
- package/lib/posixPath.js +0 -28
- package/src/__tests__/__fixtures__/defaultCodeTranslations/en.json +0 -4
- package/src/__tests__/__fixtures__/defaultCodeTranslations/fr-FR.json +0 -5
- package/src/__tests__/__fixtures__/defaultCodeTranslations/fr.json +0 -4
- package/src/__tests__/__snapshots__/index.test.ts.snap +0 -8
- package/src/__tests__/codeTranslationsUtils.test.ts +0 -112
- package/src/__tests__/escapePath.test.ts +0 -25
- package/src/__tests__/hashUtils.test.ts +0 -51
- package/src/__tests__/index.test.ts +0 -631
- package/src/__tests__/markdownParser.test.ts +0 -817
- package/src/__tests__/pathUtils.test.ts +0 -63
- package/src/__tests__/posixPath.test.ts +0 -25
- package/src/codeTranslationsUtils.ts +0 -56
- package/src/escapePath.ts +0 -23
- package/src/posixPath.ts +0 -27
- package/tsconfig.json +0 -9
package/src/emitUtils.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
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 {simpleHash, docuHash} from './hashUtils';
|
|
12
|
+
import {findAsyncSequential} from './jsUtils';
|
|
13
|
+
|
|
14
|
+
const fileHash = new Map<string, string>();
|
|
15
|
+
|
|
16
|
+
export async function generate(
|
|
17
|
+
generatedFilesDir: string,
|
|
18
|
+
file: string,
|
|
19
|
+
content: string,
|
|
20
|
+
skipCache: boolean = process.env.NODE_ENV === 'production',
|
|
21
|
+
): Promise<void> {
|
|
22
|
+
const filepath = path.join(generatedFilesDir, file);
|
|
23
|
+
|
|
24
|
+
if (skipCache) {
|
|
25
|
+
await fs.ensureDir(path.dirname(filepath));
|
|
26
|
+
await fs.writeFile(filepath, content);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let lastHash = fileHash.get(filepath);
|
|
31
|
+
|
|
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.
|
|
35
|
+
if (!lastHash && (await fs.pathExists(filepath))) {
|
|
36
|
+
const lastContent = await fs.readFile(filepath, 'utf8');
|
|
37
|
+
lastHash = createHash('md5').update(lastContent).digest('hex');
|
|
38
|
+
fileHash.set(filepath, lastHash);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const currentHash = createHash('md5').update(content).digest('hex');
|
|
42
|
+
|
|
43
|
+
if (lastHash !== currentHash) {
|
|
44
|
+
await fs.ensureDir(path.dirname(filepath));
|
|
45
|
+
await fs.writeFile(filepath, content);
|
|
46
|
+
fileHash.set(filepath, currentHash);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const chunkNameCache = new Map();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generate unique chunk name given a module path.
|
|
54
|
+
*/
|
|
55
|
+
export function genChunkName(
|
|
56
|
+
modulePath: string,
|
|
57
|
+
prefix?: string,
|
|
58
|
+
preferredName?: string,
|
|
59
|
+
shortId: boolean = process.env.NODE_ENV === 'production',
|
|
60
|
+
): string {
|
|
61
|
+
let chunkName: string | undefined = chunkNameCache.get(modulePath);
|
|
62
|
+
if (!chunkName) {
|
|
63
|
+
if (shortId) {
|
|
64
|
+
chunkName = simpleHash(modulePath, 8);
|
|
65
|
+
} else {
|
|
66
|
+
let str = modulePath;
|
|
67
|
+
if (preferredName) {
|
|
68
|
+
const shortHash = simpleHash(modulePath, 3);
|
|
69
|
+
str = `${preferredName}${shortHash}`;
|
|
70
|
+
}
|
|
71
|
+
const name = str === '/' ? 'index' : docuHash(str);
|
|
72
|
+
chunkName = prefix ? `${prefix}---${name}` : name;
|
|
73
|
+
}
|
|
74
|
+
chunkNameCache.set(modulePath, chunkName);
|
|
75
|
+
}
|
|
76
|
+
return chunkName;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param permalink The URL that the HTML file corresponds to, without base URL
|
|
81
|
+
* @param outDir Full path to the output directory
|
|
82
|
+
* @param trailingSlash The site config option. If provided, only one path will
|
|
83
|
+
* be read.
|
|
84
|
+
* @returns This returns a buffer, which you have to decode string yourself if
|
|
85
|
+
* needed. (Not always necessary since the output isn't for human consumption
|
|
86
|
+
* anyways, and most HTML manipulation libs accept buffers)
|
|
87
|
+
*/
|
|
88
|
+
export async function readOutputHTMLFile(
|
|
89
|
+
permalink: string,
|
|
90
|
+
outDir: string,
|
|
91
|
+
trailingSlash: boolean | undefined,
|
|
92
|
+
): Promise<Buffer> {
|
|
93
|
+
const withTrailingSlashPath = path.join(outDir, permalink, 'index.html');
|
|
94
|
+
const withoutTrailingSlashPath = path.join(
|
|
95
|
+
outDir,
|
|
96
|
+
`${permalink.replace(/\/$/, '')}.html`,
|
|
97
|
+
);
|
|
98
|
+
if (trailingSlash) {
|
|
99
|
+
return fs.readFile(withTrailingSlashPath);
|
|
100
|
+
} else if (trailingSlash === false) {
|
|
101
|
+
return fs.readFile(withoutTrailingSlashPath);
|
|
102
|
+
}
|
|
103
|
+
const HTMLPath = await findAsyncSequential(
|
|
104
|
+
[withTrailingSlashPath, withoutTrailingSlashPath],
|
|
105
|
+
fs.pathExists,
|
|
106
|
+
);
|
|
107
|
+
if (!HTMLPath) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Expected output HTML file to be found at ${withTrailingSlashPath}`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return fs.readFile(HTMLPath);
|
|
113
|
+
}
|
package/src/gitUtils.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
export class GitNotFoundError extends Error {}
|
|
12
|
+
|
|
13
|
+
export const getFileCommitDate = (
|
|
14
|
+
file: string,
|
|
15
|
+
{
|
|
16
|
+
age = 'oldest',
|
|
17
|
+
includeAuthor = false,
|
|
18
|
+
}: {
|
|
19
|
+
age?: 'oldest' | 'newest';
|
|
20
|
+
includeAuthor?: boolean;
|
|
21
|
+
},
|
|
22
|
+
): {
|
|
23
|
+
date: Date;
|
|
24
|
+
timestamp: number;
|
|
25
|
+
author?: string;
|
|
26
|
+
} => {
|
|
27
|
+
if (!shell.which('git')) {
|
|
28
|
+
throw new GitNotFoundError(
|
|
29
|
+
`Failed to retrieve git history for "${file}" because git is not installed.`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!shell.test('-f', file)) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Failed to retrieve git history for "${file}" because the file does not exist.`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const fileBasename = path.basename(file);
|
|
40
|
+
const fileDirname = path.dirname(file);
|
|
41
|
+
|
|
42
|
+
let formatArg = '--format=%ct';
|
|
43
|
+
if (includeAuthor) {
|
|
44
|
+
formatArg += ',%an';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let extraArgs = '--max-count=1';
|
|
48
|
+
if (age === 'oldest') {
|
|
49
|
+
// --follow is necessary to follow file renames
|
|
50
|
+
// --diff-filter=A ensures we only get the commit which (A)dded the file
|
|
51
|
+
extraArgs += ' --follow --diff-filter=A';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const result = shell.exec(
|
|
55
|
+
`git log ${extraArgs} ${formatArg} -- "${fileBasename}"`,
|
|
56
|
+
{
|
|
57
|
+
// cwd is important, see: https://github.com/facebook/docusaurus/pull/5048
|
|
58
|
+
cwd: fileDirname,
|
|
59
|
+
silent: true,
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
if (result.code !== 0) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Failed to retrieve the git history for file "${file}" with exit code ${result.code}: ${result.stderr}`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
let regex = /^(?<timestamp>\d+)$/;
|
|
68
|
+
if (includeAuthor) {
|
|
69
|
+
regex = /^(?<timestamp>\d+),(?<author>.+)$/;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const output = result.stdout.trim();
|
|
73
|
+
const match = output.match(regex);
|
|
74
|
+
|
|
75
|
+
if (
|
|
76
|
+
!match ||
|
|
77
|
+
!match.groups ||
|
|
78
|
+
!match.groups.timestamp ||
|
|
79
|
+
(includeAuthor && !match.groups.author)
|
|
80
|
+
) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Failed to retrieve the git history for file "${file}" with unexpected output: ${output}`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const timestamp = Number(match.groups.timestamp);
|
|
87
|
+
const date = new Date(timestamp * 1000);
|
|
88
|
+
|
|
89
|
+
if (includeAuthor) {
|
|
90
|
+
return {date, timestamp, author: match.groups.author};
|
|
91
|
+
}
|
|
92
|
+
return {date, timestamp};
|
|
93
|
+
};
|
package/src/globUtils.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
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 Micromatch from 'micromatch'; // Note: Micromatch is used by Globby
|
|
11
|
+
import path from 'path';
|
|
12
|
+
|
|
13
|
+
export {default as Globby} from 'globby';
|
|
14
|
+
|
|
15
|
+
// The default patterns we ignore when globbing
|
|
16
|
+
// using _ prefix for exclusion by convention
|
|
17
|
+
export const GlobExcludeDefault = [
|
|
18
|
+
// Ignore files starting with _
|
|
19
|
+
'**/_*.{js,jsx,ts,tsx,md,mdx}',
|
|
20
|
+
|
|
21
|
+
// Ignore folders starting with _ (including folder content)
|
|
22
|
+
'**/_*/**',
|
|
23
|
+
|
|
24
|
+
// Ignore tests
|
|
25
|
+
'**/*.test.{js,jsx,ts,tsx}',
|
|
26
|
+
'**/__tests__/**',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
type Matcher = (str: string) => boolean;
|
|
30
|
+
|
|
31
|
+
export function createMatcher(patterns: string[]): Matcher {
|
|
32
|
+
const regexp = new RegExp(
|
|
33
|
+
patterns.map((pattern) => Micromatch.makeRe(pattern).source).join('|'),
|
|
34
|
+
);
|
|
35
|
+
return (str) => regexp.test(str);
|
|
36
|
+
}
|
|
37
|
+
|
|
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
|
|
42
|
+
export function createAbsoluteFilePathMatcher(
|
|
43
|
+
patterns: string[],
|
|
44
|
+
rootFolders: string[],
|
|
45
|
+
): Matcher {
|
|
46
|
+
const matcher = createMatcher(patterns);
|
|
47
|
+
|
|
48
|
+
function getRelativeFilePath(absoluteFilePath: string) {
|
|
49
|
+
const rootFolder = rootFolders.find((folderPath) =>
|
|
50
|
+
absoluteFilePath.startsWith(folderPath),
|
|
51
|
+
);
|
|
52
|
+
if (!rootFolder) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`createAbsoluteFilePathMatcher unexpected error, absoluteFilePath=${absoluteFilePath} was not contained in any of the root folders ${JSON.stringify(
|
|
55
|
+
rootFolders,
|
|
56
|
+
)}`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return path.relative(rootFolder, absoluteFilePath);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (absoluteFilePath: string) =>
|
|
63
|
+
matcher(getRelativeFilePath(absoluteFilePath));
|
|
64
|
+
}
|
package/src/hashUtils.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {createHash} from 'crypto';
|
|
9
|
-
import
|
|
9
|
+
import _ from 'lodash';
|
|
10
10
|
import {shortName, isNameTooLong} from './pathUtils';
|
|
11
11
|
|
|
12
12
|
export function md5Hash(str: string): string {
|
|
@@ -29,9 +29,9 @@ export function docuHash(str: string): string {
|
|
|
29
29
|
return 'index';
|
|
30
30
|
}
|
|
31
31
|
const shortHash = simpleHash(str, 3);
|
|
32
|
-
const parsedPath = `${kebabCase(str)}-${shortHash}`;
|
|
32
|
+
const parsedPath = `${_.kebabCase(str)}-${shortHash}`;
|
|
33
33
|
if (isNameTooLong(parsedPath)) {
|
|
34
|
-
return `${shortName(kebabCase(str))}-${shortHash}`;
|
|
34
|
+
return `${shortName(_.kebabCase(str))}-${shortHash}`;
|
|
35
35
|
}
|
|
36
36
|
return parsedPath;
|
|
37
37
|
}
|
package/src/i18nUtils.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
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 type {TranslationFileContent, TranslationFile} from '@docusaurus/types';
|
|
11
|
+
import {DEFAULT_PLUGIN_ID} from './constants';
|
|
12
|
+
|
|
13
|
+
export function mergeTranslations(
|
|
14
|
+
contents: TranslationFileContent[],
|
|
15
|
+
): TranslationFileContent {
|
|
16
|
+
return contents.reduce((acc, content) => ({...acc, ...content}), {});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Useful to update all the messages of a translation file
|
|
20
|
+
// Used in tests to simulate translations
|
|
21
|
+
export function updateTranslationFileMessages(
|
|
22
|
+
translationFile: TranslationFile,
|
|
23
|
+
updateMessage: (message: string) => string,
|
|
24
|
+
): TranslationFile {
|
|
25
|
+
return {
|
|
26
|
+
...translationFile,
|
|
27
|
+
content: _.mapValues(translationFile.content, (translation) => ({
|
|
28
|
+
...translation,
|
|
29
|
+
message: updateMessage(translation.message),
|
|
30
|
+
})),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getPluginI18nPath({
|
|
35
|
+
siteDir,
|
|
36
|
+
locale,
|
|
37
|
+
pluginName,
|
|
38
|
+
pluginId = DEFAULT_PLUGIN_ID,
|
|
39
|
+
subPaths = [],
|
|
40
|
+
}: {
|
|
41
|
+
siteDir: string;
|
|
42
|
+
locale: string;
|
|
43
|
+
pluginName: string;
|
|
44
|
+
pluginId?: string | undefined;
|
|
45
|
+
subPaths?: string[];
|
|
46
|
+
}): string {
|
|
47
|
+
return path.join(
|
|
48
|
+
siteDir,
|
|
49
|
+
'i18n',
|
|
50
|
+
// namespace first by locale: convenient to work in a single folder for a
|
|
51
|
+
// translator
|
|
52
|
+
locale,
|
|
53
|
+
// Make it convenient to use for single-instance
|
|
54
|
+
// ie: return "docs", not "docs-default" nor "docs/default"
|
|
55
|
+
`${pluginName}${pluginId === DEFAULT_PLUGIN_ID ? '' : `-${pluginId}`}`,
|
|
56
|
+
...subPaths,
|
|
57
|
+
);
|
|
58
|
+
}
|