@docusaurus/utils
Advanced tools
Comparing version 2.0.0-alpha.69 to 2.0.0-alpha.378053ac5
@@ -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.378053ac5", | ||
"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.378053ac5", | ||
"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": "de6131ca7167543245c4a468122559901a442519" | ||
} |
@@ -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'}, | ||
}, | ||
}); | ||
}); | ||
}); |
117
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'; | ||
@@ -265,3 +270,3 @@ const fileHash = new Map(); | ||
throw new Error(`Error while parsing markdown front matter. | ||
This can happen if you use special characteres like : in frontmatter values (try using "" around that value) | ||
This can happen if you use special characters like : in frontmatter values (try using "" around that value) | ||
${e.message}`); | ||
@@ -394,3 +399,3 @@ } | ||
// resolve pathname and fail fast if resolution fails | ||
export function resolvePathname(to: string, from?: string) { | ||
export function resolvePathname(to: string, from?: string): string { | ||
return resolvePathnameUnsafe(to, from); | ||
@@ -445,2 +450,85 @@ } | ||
export function getPluginI18nPath({ | ||
siteDir, | ||
locale, | ||
pluginName, | ||
pluginId = 'default', // TODO duplicated constant | ||
subPaths = [], | ||
}: { | ||
siteDir: string; | ||
locale: string; | ||
pluginName: string; | ||
pluginId?: string | undefined; | ||
subPaths?: string[]; | ||
}): 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( | ||
@@ -471,2 +559,10 @@ message: string, | ||
export function mergeTranslations( | ||
contents: TranslationFileContent[], | ||
): TranslationFileContent { | ||
return contents.reduce((acc, content) => { | ||
return {...acc, ...content}; | ||
}, {}); | ||
} | ||
export function getSwizzledComponent( | ||
@@ -485,1 +581,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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1755
513145
9
10
1
+ Addedlodash@^4.17.20
+ Added@docusaurus/types@2.0.0-alpha.378053ac5(transitive)
- Removed@docusaurus/types@2.0.0-alpha.69(transitive)