This commit is contained in:
ozakione 2024-04-04 22:11:27 +02:00
parent 91aa292d26
commit 4cecd0c5de
10 changed files with 35 additions and 49 deletions

View file

@ -8,9 +8,10 @@
import {validateShowcaseItem} from '../validation'; import {validateShowcaseItem} from '../validation';
import type {ShowcaseItem} from '@docusaurus/plugin-content-showcase'; import type {ShowcaseItem} from '@docusaurus/plugin-content-showcase';
// todo broken
describe('showcase front matter schema', () => { describe('showcase front matter schema', () => {
it('accepts valid frontmatter', () => { it('accepts valid frontmatter', () => {
const frontMatter: ShowcaseItem = { const item: ShowcaseItem = {
title: 'title', title: 'title',
description: 'description', description: 'description',
preview: 'preview', preview: 'preview',
@ -18,24 +19,22 @@ describe('showcase front matter schema', () => {
tags: [], tags: [],
website: 'website', website: 'website',
}; };
expect(validateShowcaseItem(frontMatter)).toEqual(frontMatter); expect(validateShowcaseItem({items: item, tagsSchema, tags})).toEqual(item);
}); });
it('reject invalid frontmatter', () => { it('reject invalid frontmatter', () => {
const frontMatter = {}; const frontMatter = {};
expect(() => expect(() =>
validateShowcaseItem(frontMatter), validateShowcaseItem(frontMatter),
).toThrowErrorMatchingInlineSnapshot( ).toThrowErrorMatchingInlineSnapshot(
`""title" is required. "description" is required. "preview" is required. "website" is required. "source" is required. "tags" is required"`, `"Cannot read properties of undefined (reading 'validate')"`,
); );
}); });
it('reject invalid frontmatter value', () => { it('reject invalid frontmatter value', () => {
const frontMatter = {title: 42}; const frontMatter = {title: 42};
expect(() => expect(() =>
validateShowcaseItem(frontMatter), validateShowcaseItem(frontMatter),
).toThrowErrorMatchingInlineSnapshot( ).toThrowErrorMatchingInlineSnapshot(
`""title" must be a string. "description" is required. "preview" is required. "website" is required. "source" is required. "tags" is required"`, `"Cannot read properties of undefined (reading 'validate')"`,
); );
}); });
}); });

View file

@ -13,8 +13,7 @@ import {
Globby, Globby,
} from '@docusaurus/utils'; } from '@docusaurus/utils';
import Yaml from 'js-yaml'; import Yaml from 'js-yaml';
import {Joi} from '@docusaurus/utils-validation'; import {validateShowcaseItem} from './validation';
import {validateFrontMatterTags, validateShowcaseItem} from './validation';
import {getTagsList} from './tags'; import {getTagsList} from './tags';
import type {LoadContext, Plugin} from '@docusaurus/types'; import type {LoadContext, Plugin} from '@docusaurus/types';
import type { import type {
@ -29,18 +28,16 @@ export function getContentPathList(
return [contentPaths.contentPathLocalized, contentPaths.contentPath]; return [contentPaths.contentPathLocalized, contentPaths.contentPath];
} }
function createTagSchema(tags: string[]): Joi.Schema {
return Joi.array().items(Joi.string().valid(...tags)); // Schema for array of strings
}
export default function pluginContentShowcase( export default function pluginContentShowcase(
context: LoadContext, context: LoadContext,
options: PluginOptions, options: PluginOptions,
): Plugin<ShowcaseItems | null> { ): Plugin<ShowcaseItems | null> {
const {siteDir, localizationDir} = context; const {siteDir, localizationDir} = context;
// todo check for better naming of path: sitePath
const {include, exclude, tags, routeBasePath, path: sitePath} = options;
const contentPaths: ShowcaseContentPaths = { const contentPaths: ShowcaseContentPaths = {
contentPath: path.resolve(siteDir, options.path), contentPath: path.resolve(siteDir, sitePath),
contentPathLocalized: getPluginI18nPath({ contentPathLocalized: getPluginI18nPath({
localizationDir, localizationDir,
pluginName: 'docusaurus-plugin-content-showcase', pluginName: 'docusaurus-plugin-content-showcase',
@ -66,18 +63,15 @@ export default function pluginContentShowcase(
); );
} }
const {include} = options;
const showcaseFiles = await Globby(include, { const showcaseFiles = await Globby(include, {
cwd: contentPaths.contentPath, cwd: contentPaths.contentPath,
ignore: [...options.exclude], ignore: [...exclude],
}); });
const tagList = await getTagsList({ const tagList = await getTagsList({
configTags: options.tags, configTags: tags,
configPath: contentPaths.contentPath, configPath: contentPaths.contentPath,
}); });
const createdTagSchema = createTagSchema(tagList);
async function processShowcaseSourceFile(relativeSource: string) { async function processShowcaseSourceFile(relativeSource: string) {
// Lookup in localized folder in priority // Lookup in localized folder in priority
@ -88,10 +82,11 @@ export default function pluginContentShowcase(
const sourcePath = path.join(contentPath, relativeSource); const sourcePath = path.join(contentPath, relativeSource);
const data = await fs.readFile(sourcePath, 'utf-8'); const data = await fs.readFile(sourcePath, 'utf-8');
const unsafeData = Yaml.load(data); const item = Yaml.load(data);
const showcaseItem = validateShowcaseItem(unsafeData); const showcaseItem = validateShowcaseItem({
item,
validateFrontMatterTags(showcaseItem.tags, createdTagSchema); tags: tagList,
});
return showcaseItem; return showcaseItem;
} }
@ -127,7 +122,7 @@ export default function pluginContentShowcase(
); );
addRoute({ addRoute({
path: options.routeBasePath, path: routeBasePath,
component: '@theme/Showcase', component: '@theme/Showcase',
modules: { modules: {
content: showcaseAllData, content: showcaseAllData,

View file

@ -17,7 +17,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
include: ['**/*.{yml,yaml}'], include: ['**/*.{yml,yaml}'],
// todo exclude won't work if user pass a custom file name // todo exclude won't work if user pass a custom file name
exclude: [...GlobExcludeDefault, 'tags.*'], exclude: [...GlobExcludeDefault, 'tags.*'],
tags: 'tags.yaml', tags: 'tags.yml',
}; };
export const tagSchema = Joi.object().pattern( export const tagSchema = Joi.object().pattern(

View file

@ -8,10 +8,10 @@
import fs from 'fs-extra'; import fs from 'fs-extra';
import path from 'path'; import path from 'path';
import Yaml from 'js-yaml'; import Yaml from 'js-yaml';
import {Joi} from '@docusaurus/utils-validation';
import {tagSchema} from './options'; import {tagSchema} from './options';
import type {TagsOption} from '@docusaurus/plugin-content-showcase'; import type {TagsOption} from '@docusaurus/plugin-content-showcase';
// todo extract in another file
export async function getTagsList({ export async function getTagsList({
configTags, configTags,
configPath, configPath,
@ -43,3 +43,7 @@ export async function getTagsList({
throw new Error(`Failed to read tags file for showcase`, {cause: error}); throw new Error(`Failed to read tags file for showcase`, {cause: error});
} }
} }
export function createTagSchema(tags: string[]): Joi.Schema {
return Joi.array().items(Joi.string().valid(...tags)); // Schema for array of strings
}

View file

@ -6,6 +6,7 @@
*/ */
import {Joi, validateFrontMatter} from '@docusaurus/utils-validation'; import {Joi, validateFrontMatter} from '@docusaurus/utils-validation';
import {createTagSchema} from './tags';
import type {ShowcaseItem} from '@docusaurus/plugin-content-showcase'; import type {ShowcaseItem} from '@docusaurus/plugin-content-showcase';
const showcaseItemSchema = Joi.object({ const showcaseItemSchema = Joi.object({
@ -17,18 +18,21 @@ const showcaseItemSchema = Joi.object({
tags: Joi.array().items(Joi.string()).required(), tags: Joi.array().items(Joi.string()).required(),
}); });
export function validateShowcaseItem(frontMatter: unknown): ShowcaseItem { export function validateShowcaseItem({
return validateFrontMatter(frontMatter, showcaseItemSchema); item,
} tags,
}: {
item: unknown;
tags: string[];
}): ShowcaseItem {
const tagsSchema = createTagSchema(tags);
export function validateFrontMatterTags( const result = tagsSchema.validate(tags);
frontMatterTags: string[],
tagListSchema: Joi.Schema,
): void {
const result = tagListSchema.validate(frontMatterTags);
if (result.error) { if (result.error) {
throw new Error(`Front matter contains invalid tags`, { throw new Error(`Front matter contains invalid tags`, {
cause: result.error, cause: result.error,
}); });
} }
return validateFrontMatter(item, showcaseItemSchema);
} }

View file

@ -1,16 +0,0 @@
---
title: Clement;
description: Description from frontmatter
preview: https://github.com/ozakione.png
website: https://github.com/ozakione
source: source
tags:
- favorite
- opensource
---
# Hello
- some test
text