@docusaurus/utils
Advanced tools
Comparing version 2.0.0-alpha.69 to 2.0.0-alpha.bd62be93d
@@ -7,3 +7,3 @@ /** | ||
*/ | ||
import { ReportingSeverity } from '@docusaurus/types'; | ||
import { ReportingSeverity, TranslationFileContent, TranslationFile } from '@docusaurus/types'; | ||
export declare function generate(generatedFilesDir: string, file: string, content: any, skipCache?: boolean): Promise<void>; | ||
@@ -79,5 +79,19 @@ export declare function objectWithKeySorted(obj: { | ||
}; | ||
export declare function getPluginI18nPath({ siteDir, locale, pluginName, pluginId, // TODO duplicated constant | ||
subPaths, }: { | ||
siteDir: string; | ||
locale: string; | ||
pluginName: string; | ||
pluginId?: string | undefined; | ||
subPaths?: string[]; | ||
}): string; | ||
export declare function mapAsyncSequencial<T extends unknown, R extends unknown>(array: T[], action: (t: T) => Promise<R>): Promise<R[]>; | ||
export declare function findAsyncSequential<T>(array: T[], predicate: (t: T) => Promise<boolean>): Promise<T | undefined>; | ||
export declare function findFolderContainingFile(folderPaths: string[], relativeFilePath: string): Promise<string | undefined>; | ||
export declare function getFolderContainingFile(folderPaths: string[], relativeFilePath: string): Promise<string>; | ||
export declare function reportMessage(message: string, reportingSeverity: ReportingSeverity): void; | ||
export declare function mergeTranslations(contents: TranslationFileContent[]): TranslationFileContent; | ||
export declare function getSwizzledComponent(componentPath: string): string | undefined; | ||
export declare function updateTranslationFileMessages(translationFile: TranslationFile, updateMessage: (message: string) => string): TranslationFile; | ||
export {}; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -12,3 +12,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getSwizzledComponent = exports.reportMessage = exports.getElementsAround = exports.getFilePathForRoutePath = exports.removePrefix = exports.removeSuffix = exports.removeTrailingSlash = exports.addTrailingSlash = exports.addLeadingSlash = exports.resolvePathname = exports.isValidPathname = exports.getEditUrl = exports.aliasedSitePath = exports.normalizeUrl = exports.parseMarkdownFile = exports.parseMarkdownString = exports.createExcerpt = exports.getSubFolder = exports.idx = exports.genChunkName = exports.posixPath = exports.genComponentName = exports.upperFirst = exports.docuHash = exports.simpleHash = exports.encodePath = exports.fileToPath = exports.objectWithKeySorted = exports.generate = void 0; | ||
exports.updateTranslationFileMessages = exports.getSwizzledComponent = exports.mergeTranslations = exports.reportMessage = exports.getFolderContainingFile = exports.findFolderContainingFile = exports.findAsyncSequential = exports.mapAsyncSequencial = exports.getPluginI18nPath = exports.getElementsAround = exports.getFilePathForRoutePath = exports.removePrefix = exports.removeSuffix = exports.removeTrailingSlash = exports.addTrailingSlash = exports.addLeadingSlash = exports.resolvePathname = exports.isValidPathname = exports.getEditUrl = exports.aliasedSitePath = exports.normalizeUrl = exports.parseMarkdownFile = exports.parseMarkdownString = exports.createExcerpt = exports.getSubFolder = exports.idx = exports.genChunkName = exports.posixPath = exports.genComponentName = exports.upperFirst = exports.docuHash = exports.simpleHash = exports.encodePath = exports.fileToPath = exports.objectWithKeySorted = exports.generate = void 0; | ||
const chalk_1 = __importDefault(require("chalk")); | ||
@@ -25,2 +25,3 @@ const path_1 = __importDefault(require("path")); | ||
const resolve_pathname_1 = __importDefault(require("resolve-pathname")); | ||
const lodash_1 = require("lodash"); | ||
const fileHash = new Map(); | ||
@@ -381,2 +382,48 @@ async function generate(generatedFilesDir, file, content, skipCache = process.env.NODE_ENV === 'production') { | ||
exports.getElementsAround = getElementsAround; | ||
function getPluginI18nPath({ siteDir, locale, pluginName, pluginId = 'default', // TODO duplicated constant | ||
subPaths = [], }) { | ||
return path_1.default.join(siteDir, 'i18n', | ||
// namespace first by locale: convenient to work in a single folder for a translator | ||
locale, | ||
// Make it convenient to use for single-instance | ||
// ie: return "docs", not "docs-default" nor "docs/default" | ||
`${pluginName}${ | ||
// TODO duplicate constant :( | ||
pluginId === 'default' ? '' : `-${pluginId}`}`, ...subPaths); | ||
} | ||
exports.getPluginI18nPath = getPluginI18nPath; | ||
async function mapAsyncSequencial(array, action) { | ||
const results = []; | ||
for (const t of array) { | ||
// eslint-disable-next-line no-await-in-loop | ||
const result = await action(t); | ||
results.push(result); | ||
} | ||
return results; | ||
} | ||
exports.mapAsyncSequencial = mapAsyncSequencial; | ||
async function findAsyncSequential(array, predicate) { | ||
for (const t of array) { | ||
// eslint-disable-next-line no-await-in-loop | ||
if (await predicate(t)) { | ||
return t; | ||
} | ||
} | ||
return undefined; | ||
} | ||
exports.findAsyncSequential = findAsyncSequential; | ||
// return the first folder path in which the file exists in | ||
async function findFolderContainingFile(folderPaths, relativeFilePath) { | ||
return findAsyncSequential(folderPaths, (folderPath) => fs_extra_1.default.pathExists(path_1.default.join(folderPath, relativeFilePath))); | ||
} | ||
exports.findFolderContainingFile = findFolderContainingFile; | ||
async function getFolderContainingFile(folderPaths, relativeFilePath) { | ||
const maybeFolderPath = await findFolderContainingFile(folderPaths, relativeFilePath); | ||
// should never happen, as the source was read from the FS anyway... | ||
if (!maybeFolderPath) { | ||
throw new Error(`relativeFilePath=[${relativeFilePath}] does not exist in any of these folders: \n- ${folderPaths.join('\n- ')}]`); | ||
} | ||
return maybeFolderPath; | ||
} | ||
exports.getFolderContainingFile = getFolderContainingFile; | ||
function reportMessage(message, reportingSeverity) { | ||
@@ -402,2 +449,8 @@ switch (reportingSeverity) { | ||
exports.reportMessage = reportMessage; | ||
function mergeTranslations(contents) { | ||
return contents.reduce((acc, content) => { | ||
return Object.assign(Object.assign({}, acc), content); | ||
}, {}); | ||
} | ||
exports.mergeTranslations = mergeTranslations; | ||
function getSwizzledComponent(componentPath) { | ||
@@ -410,1 +463,7 @@ const swizzledComponentPath = path_1.default.resolve(process.cwd(), 'src', componentPath); | ||
exports.getSwizzledComponent = getSwizzledComponent; | ||
// Useful to update all the messages of a translation file | ||
// Used in tests to simulate translations | ||
function updateTranslationFileMessages(translationFile, updateMessage) { | ||
return Object.assign(Object.assign({}, translationFile), { content: lodash_1.mapValues(translationFile.content, (translation) => (Object.assign(Object.assign({}, translation), { message: updateMessage(translation.message) }))) }); | ||
} | ||
exports.updateTranslationFileMessages = updateTranslationFileMessages; |
{ | ||
"name": "@docusaurus/utils", | ||
"version": "2.0.0-alpha.69", | ||
"version": "2.0.0-alpha.bd62be93d", | ||
"description": "Node utility functions for Docusaurus packages", | ||
@@ -21,3 +21,3 @@ "main": "./lib/index.js", | ||
"dependencies": { | ||
"@docusaurus/types": "2.0.0-alpha.69", | ||
"@docusaurus/types": "2.0.0-alpha.bd62be93d", | ||
"chalk": "^3.0.0", | ||
@@ -27,2 +27,3 @@ "escape-string-regexp": "^2.0.0", | ||
"gray-matter": "^4.0.2", | ||
"lodash": "^4.17.20", | ||
"lodash.camelcase": "^4.3.0", | ||
@@ -35,3 +36,3 @@ "lodash.kebabcase": "^4.1.1", | ||
}, | ||
"gitHead": "4410a9eb01b87c2df664986ba37e748ed34a3c78" | ||
"gitHead": "056b5525e788ce77b725ff6fdea2be2cdcea3cd9" | ||
} |
@@ -30,3 +30,10 @@ /** | ||
getElementsAround, | ||
mergeTranslations, | ||
mapAsyncSequencial, | ||
findAsyncSequential, | ||
findFolderContainingFile, | ||
getFolderContainingFile, | ||
updateTranslationFileMessages, | ||
} from '../index'; | ||
import {sum} from 'lodash'; | ||
@@ -564,1 +571,149 @@ describe('load utils', () => { | ||
}); | ||
describe('mergeTranslations', () => { | ||
test('should merge translations', () => { | ||
expect( | ||
mergeTranslations([ | ||
{ | ||
T1: {message: 'T1 message', description: 'T1 desc'}, | ||
T2: {message: 'T2 message', description: 'T2 desc'}, | ||
T3: {message: 'T3 message', description: 'T3 desc'}, | ||
}, | ||
{ | ||
T4: {message: 'T4 message', description: 'T4 desc'}, | ||
}, | ||
{T2: {message: 'T2 message 2', description: 'T2 desc 2'}}, | ||
]), | ||
).toEqual({ | ||
T1: {message: 'T1 message', description: 'T1 desc'}, | ||
T2: {message: 'T2 message 2', description: 'T2 desc 2'}, | ||
T3: {message: 'T3 message', description: 'T3 desc'}, | ||
T4: {message: 'T4 message', description: 'T4 desc'}, | ||
}); | ||
}); | ||
}); | ||
describe('mapAsyncSequencial', () => { | ||
function sleep(timeout: number): Promise<void> { | ||
return new Promise((resolve) => setTimeout(resolve, timeout)); | ||
} | ||
test('map sequentially', async () => { | ||
const itemToTimeout: Record<string, number> = { | ||
'1': 50, | ||
'2': 150, | ||
'3': 100, | ||
}; | ||
const items = Object.keys(itemToTimeout); | ||
const itemMapStartsAt: Record<string, number> = {}; | ||
const itemMapEndsAt: Record<string, number> = {}; | ||
const timeBefore = Date.now(); | ||
await expect( | ||
mapAsyncSequencial(items, async (item) => { | ||
const itemTimeout = itemToTimeout[item]; | ||
itemMapStartsAt[item] = Date.now(); | ||
await sleep(itemTimeout); | ||
itemMapEndsAt[item] = Date.now(); | ||
return `${item} mapped`; | ||
}), | ||
).resolves.toEqual(['1 mapped', '2 mapped', '3 mapped']); | ||
const timeAfter = Date.now(); | ||
const timeTotal = timeAfter - timeBefore; | ||
const totalTimeouts = sum(Object.values(itemToTimeout)); | ||
expect(timeTotal > totalTimeouts); | ||
expect(itemMapStartsAt['1'] > 0); | ||
expect(itemMapStartsAt['2'] > itemMapEndsAt['1']); | ||
expect(itemMapStartsAt['3'] > itemMapEndsAt['2']); | ||
}); | ||
}); | ||
describe('findAsyncSequencial', () => { | ||
function sleep(timeout: number): Promise<void> { | ||
return new Promise((resolve) => setTimeout(resolve, timeout)); | ||
} | ||
test('find sequentially', async () => { | ||
const items = ['1', '2', '3']; | ||
const findFn = jest.fn(async (item: string) => { | ||
await sleep(50); | ||
return item === '2'; | ||
}); | ||
const timeBefore = Date.now(); | ||
await expect(findAsyncSequential(items, findFn)).resolves.toEqual('2'); | ||
const timeAfter = Date.now(); | ||
expect(findFn).toHaveBeenCalledTimes(2); | ||
expect(findFn).toHaveBeenNthCalledWith(1, '1'); | ||
expect(findFn).toHaveBeenNthCalledWith(2, '2'); | ||
const timeTotal = timeAfter - timeBefore; | ||
expect(timeTotal > 100); | ||
expect(timeTotal < 150); | ||
}); | ||
}); | ||
describe('findFolderContainingFile', () => { | ||
test('find appropriate folder', async () => { | ||
await expect( | ||
findFolderContainingFile( | ||
['/abcdef', '/gehij', __dirname, '/klmn'], | ||
'index.test.ts', | ||
), | ||
).resolves.toEqual(__dirname); | ||
}); | ||
test('return undefined if no folder contain such file', async () => { | ||
await expect( | ||
findFolderContainingFile(['/abcdef', '/gehij', '/klmn'], 'index.test.ts'), | ||
).resolves.toBeUndefined(); | ||
}); | ||
}); | ||
describe('getFolderContainingFile', () => { | ||
test('get appropriate folder', async () => { | ||
await expect( | ||
getFolderContainingFile( | ||
['/abcdef', '/gehij', __dirname, '/klmn'], | ||
'index.test.ts', | ||
), | ||
).resolves.toEqual(__dirname); | ||
}); | ||
test('throw if no folder contain such file', async () => { | ||
await expect( | ||
getFolderContainingFile(['/abcdef', '/gehij', '/klmn'], 'index.test.ts'), | ||
).rejects.toThrowErrorMatchingSnapshot(); | ||
}); | ||
}); | ||
describe('updateTranslationFileMessages', () => { | ||
test('should update messages', () => { | ||
expect( | ||
updateTranslationFileMessages( | ||
{ | ||
path: 'abc', | ||
content: { | ||
t1: {message: 't1 message', description: 't1 desc'}, | ||
t2: {message: 't2 message', description: 't2 desc'}, | ||
t3: {message: 't3 message', description: 't3 desc'}, | ||
}, | ||
}, | ||
(message) => `prefix ${message} suffix`, | ||
), | ||
).toEqual({ | ||
path: 'abc', | ||
content: { | ||
t1: {message: 'prefix t1 message suffix', description: 't1 desc'}, | ||
t2: {message: 'prefix t2 message suffix', description: 't2 desc'}, | ||
t3: {message: 'prefix t3 message suffix', description: 't3 desc'}, | ||
}, | ||
}); | ||
}); | ||
}); |
113
src/index.ts
@@ -17,6 +17,11 @@ /** | ||
import {URL} from 'url'; | ||
import {ReportingSeverity} from '@docusaurus/types'; | ||
import { | ||
ReportingSeverity, | ||
TranslationFileContent, | ||
TranslationFile, | ||
} from '@docusaurus/types'; | ||
// @ts-expect-error: no typedefs :s | ||
import resolvePathnameUnsafe from 'resolve-pathname'; | ||
import {mapValues} from 'lodash'; | ||
@@ -443,2 +448,85 @@ const fileHash = new Map(); | ||
export function getPluginI18nPath({ | ||
siteDir, | ||
locale, | ||
pluginName, | ||
pluginId = 'default', // TODO duplicated constant | ||
subPaths = [], | ||
}: { | ||
siteDir: string; | ||
locale: string; | ||
pluginName: string; | ||
pluginId?: string | undefined; | ||
subPaths?: string[]; | ||
}) { | ||
return path.join( | ||
siteDir, | ||
'i18n', | ||
// namespace first by locale: convenient to work in a single folder for a translator | ||
locale, | ||
// Make it convenient to use for single-instance | ||
// ie: return "docs", not "docs-default" nor "docs/default" | ||
`${pluginName}${ | ||
// TODO duplicate constant :( | ||
pluginId === 'default' ? '' : `-${pluginId}` | ||
}`, | ||
...subPaths, | ||
); | ||
} | ||
export async function mapAsyncSequencial<T extends unknown, R extends unknown>( | ||
array: T[], | ||
action: (t: T) => Promise<R>, | ||
): Promise<R[]> { | ||
const results: R[] = []; | ||
for (const t of array) { | ||
// eslint-disable-next-line no-await-in-loop | ||
const result = await action(t); | ||
results.push(result); | ||
} | ||
return results; | ||
} | ||
export async function findAsyncSequential<T>( | ||
array: T[], | ||
predicate: (t: T) => Promise<boolean>, | ||
): Promise<T | undefined> { | ||
for (const t of array) { | ||
// eslint-disable-next-line no-await-in-loop | ||
if (await predicate(t)) { | ||
return t; | ||
} | ||
} | ||
return undefined; | ||
} | ||
// return the first folder path in which the file exists in | ||
export async function findFolderContainingFile( | ||
folderPaths: string[], | ||
relativeFilePath: string, | ||
): Promise<string | undefined> { | ||
return findAsyncSequential(folderPaths, (folderPath) => | ||
fs.pathExists(path.join(folderPath, relativeFilePath)), | ||
); | ||
} | ||
export async function getFolderContainingFile( | ||
folderPaths: string[], | ||
relativeFilePath: string, | ||
): Promise<string> { | ||
const maybeFolderPath = await findFolderContainingFile( | ||
folderPaths, | ||
relativeFilePath, | ||
); | ||
// should never happen, as the source was read from the FS anyway... | ||
if (!maybeFolderPath) { | ||
throw new Error( | ||
`relativeFilePath=[${relativeFilePath}] does not exist in any of these folders: \n- ${folderPaths.join( | ||
'\n- ', | ||
)}]`, | ||
); | ||
} | ||
return maybeFolderPath; | ||
} | ||
export function reportMessage( | ||
@@ -469,2 +557,10 @@ message: string, | ||
export function mergeTranslations( | ||
contents: TranslationFileContent[], | ||
): TranslationFileContent { | ||
return contents.reduce((acc, content) => { | ||
return {...acc, ...content}; | ||
}, {}); | ||
} | ||
export function getSwizzledComponent( | ||
@@ -483,1 +579,16 @@ componentPath: string, | ||
} | ||
// Useful to update all the messages of a translation file | ||
// Used in tests to simulate translations | ||
export function updateTranslationFileMessages( | ||
translationFile: TranslationFile, | ||
updateMessage: (message: string) => string, | ||
): TranslationFile { | ||
return { | ||
...translationFile, | ||
content: mapValues(translationFile.content, (translation) => ({ | ||
...translation, | ||
message: updateMessage(translation.message), | ||
})), | ||
}; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1755
572326
9
10
+ Addedlodash@^4.17.20
+ Added@docusaurus/types@2.0.0-alpha.bd62be93d(transitive)
- Removed@docusaurus/types@2.0.0-alpha.69(transitive)