This commit is contained in:
ozakione 2024-04-01 15:30:36 +02:00
parent fc34725b0b
commit 6b83e2d153
3 changed files with 36 additions and 42 deletions

View file

@ -22,3 +22,15 @@ export function validateShowcaseFrontMatter(frontMatter: {
}): ShowcaseFrontMatter { }): ShowcaseFrontMatter {
return validateFrontMatter(frontMatter, showcaseFrontMatterSchema); return validateFrontMatter(frontMatter, showcaseFrontMatterSchema);
} }
export function validateFrontMatterTags(
frontMatterTags: string[],
tagListSchema: Joi.Schema,
): void {
const result = tagListSchema.validate(frontMatterTags);
if (result.error) {
throw new Error(
`Front matter contains invalid tags: ${result.error.message}`,
);
}
}

View file

@ -8,6 +8,7 @@
import fs from 'fs-extra'; import fs from 'fs-extra';
import path from 'path'; import path from 'path';
import { import {
aliasedSitePathToRelativePath,
getFolderContainingFile, getFolderContainingFile,
getPluginI18nPath, getPluginI18nPath,
Globby, Globby,
@ -15,7 +16,10 @@ import {
import Yaml from 'js-yaml'; import Yaml from 'js-yaml';
import {Joi} from '@docusaurus/utils-validation'; import {Joi} from '@docusaurus/utils-validation';
import {validateShowcaseFrontMatter} from './frontMatter'; import {
validateFrontMatterTags,
validateShowcaseFrontMatter,
} from './frontMatter';
import {tagSchema} from './options'; import {tagSchema} from './options';
import type {LoadContext, Plugin} from '@docusaurus/types'; import type {LoadContext, Plugin} from '@docusaurus/types';
import type { import type {
@ -31,12 +35,22 @@ export function getContentPathList(
return [contentPaths.contentPathLocalized, contentPaths.contentPath]; return [contentPaths.contentPathLocalized, contentPaths.contentPath];
} }
function createTagSchema(tags: string[]): Joi.Schema {
return Joi.alternatives().try(
Joi.string().valid(...tags), // Schema for single string
Joi.array().items(Joi.string().valid(...tags)), // Schema for array of strings
);
}
async function getTagsList(filePath: string | TagOption[]): Promise<string[]> { async function getTagsList(filePath: string | TagOption[]): Promise<string[]> {
if (Array.isArray(filePath)) { if (typeof filePath === 'object') {
return Object.keys(filePath); return Object.keys(filePath);
} }
const rawYaml = await fs.readFile(filePath, 'utf-8'); const rawYaml = await fs.readFile(
aliasedSitePathToRelativePath(filePath),
'utf-8',
);
const unsafeYaml = Yaml.load(rawYaml); const unsafeYaml = Yaml.load(rawYaml);
const safeYaml = tagSchema.validate(unsafeYaml); const safeYaml = tagSchema.validate(unsafeYaml);
@ -51,25 +65,6 @@ async function getTagsList(filePath: string | TagOption[]): Promise<string[]> {
return tagLabels; return tagLabels;
} }
function createTagSchema(tags: string[]): Joi.Schema {
return Joi.alternatives().try(
Joi.string().valid(...tags), // Schema for single string
Joi.array().items(Joi.string().valid(...tags)), // Schema for array of strings
);
}
function validateFrontMatterTags(
frontMatterTags: string[],
tagListSchema: Joi.Schema,
): void {
const result = tagListSchema.validate(frontMatterTags);
if (result.error) {
throw new Error(
`Front matter contains invalid tags: ${result.error.message}`,
);
}
}
export default function pluginContentShowcase( export default function pluginContentShowcase(
context: LoadContext, context: LoadContext,
options: PluginOptions, options: PluginOptions,
@ -97,32 +92,18 @@ export default function pluginContentShowcase(
// }, // },
async loadContent(): Promise<ShowcaseItem | null> { async loadContent(): Promise<ShowcaseItem | null> {
const {include} = options;
if (!(await fs.pathExists(contentPaths.contentPath))) { if (!(await fs.pathExists(contentPaths.contentPath))) {
return null; return null;
} }
// const {baseUrl} = siteConfig; const {include} = options;
const showcaseFiles = await Globby(include, { const showcaseFiles = await Globby(include, {
cwd: contentPaths.contentPath, cwd: contentPaths.contentPath,
ignore: options.exclude, ignore: options.exclude,
}); });
const filteredShowcaseFiles = showcaseFiles.filter( const tagList = await getTagsList(options.tags);
(source) => source !== 'tags.yaml',
);
// todo refactor ugly
const tagFilePath = path.join(
await getFolderContainingFile(
getContentPathList(contentPaths),
'tags.yaml',
),
'tags.yaml',
);
const tagList = await getTagsList(tagFilePath);
const createdTagSchema = createTagSchema(tagList); const createdTagSchema = createTagSchema(tagList);
async function processShowcaseSourceFile(relativeSource: string) { async function processShowcaseSourceFile(relativeSource: string) {
@ -135,6 +116,7 @@ export default function pluginContentShowcase(
const sourcePath = path.join(contentPath, relativeSource); const sourcePath = path.join(contentPath, relativeSource);
const rawYaml = await fs.readFile(sourcePath, 'utf-8'); const rawYaml = await fs.readFile(sourcePath, 'utf-8');
// todo remove as ... because bad practice ?
const unsafeYaml = Yaml.load(rawYaml) as {[key: string]: unknown}; const unsafeYaml = Yaml.load(rawYaml) as {[key: string]: unknown};
const yaml = validateShowcaseFrontMatter(unsafeYaml); const yaml = validateShowcaseFrontMatter(unsafeYaml);
@ -156,7 +138,7 @@ export default function pluginContentShowcase(
return { return {
items: await Promise.all( items: await Promise.all(
filteredShowcaseFiles.map(doProcessShowcaseSourceFile), showcaseFiles.map(doProcessShowcaseSourceFile),
), ),
}; };
}, },

View file

@ -14,8 +14,8 @@ export const DEFAULT_OPTIONS: PluginOptions = {
id: 'showcase', id: 'showcase',
path: 'showcase', // Path to data on filesystem, relative to site dir. path: 'showcase', // Path to data on filesystem, relative to site dir.
routeBasePath: '/', // URL Route. routeBasePath: '/', // URL Route.
include: ['**/*.{yml,yaml}'], // Extensions to include. include: ['**/*.{yml,yaml}'],
exclude: GlobExcludeDefault, exclude: [...GlobExcludeDefault, 'tags.*'],
tags: '@site/showcase/tags.yaml', tags: '@site/showcase/tags.yaml',
}; };