This commit is contained in:
ozakione 2024-04-09 16:39:51 +02:00
parent 65affab608
commit 867b604919
11 changed files with 141 additions and 86 deletions

View File

@ -26,7 +26,7 @@ import {
} from '@docusaurus/utils';
import {validatePageFrontMatter} from './frontMatter';
import type {LoadContext, Plugin, RouteMetadata} from '@docusaurus/types';
import type {PagesContentPaths} from './types';
import type {ShowcaseContentPaths} from './types';
import type {
PluginOptions,
Metadata,
@ -34,7 +34,9 @@ import type {
PageFrontMatter,
} from '@docusaurus/plugin-content-pages';
export function getContentPathList(contentPaths: PagesContentPaths): string[] {
export function getContentPathList(
contentPaths: ShowcaseContentPaths,
): string[] {
return [contentPaths.contentPathLocalized, contentPaths.contentPath];
}
@ -47,7 +49,7 @@ export default function pluginContentPages(
): Plugin<LoadedContent | null> {
const {siteConfig, siteDir, generatedFilesDir, localizationDir} = context;
const contentPaths: PagesContentPaths = {
const contentPaths: ShowcaseContentPaths = {
contentPath: path.resolve(siteDir, options.path),
contentPathLocalized: getPluginI18nPath({
localizationDir,

View File

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
export type PagesContentPaths = {
export type ShowcaseContentPaths = {
contentPath: string;
contentPathLocalized: string;
};

View File

@ -9,13 +9,13 @@ import path from 'path';
import {loadContext} from '@docusaurus/core/src/server/site';
import {normalizePluginOptions} from '@docusaurus/utils-validation';
import {fromPartial} from '@total-typescript/shoehorn';
import pluginContentPages from '../index';
import pluginContentShowcase from '../index';
import {validateOptions} from '../options';
import type {PluginOptions} from '@docusaurus/plugin-content-showcase';
const loadPluginContent = async (siteDir: string, options: PluginOptions) => {
const context = await loadContext({siteDir});
const plugin = await pluginContentPages(
const plugin = await pluginContentShowcase(
context,
validateOptions({
validate: normalizePluginOptions,

View File

@ -101,7 +101,6 @@ describe('normalizeShowcasePluginOptions', () => {
},
},
};
// todo fix ts error
expect(testValidate(userOptions)).toEqual({
...defaultOptions,
...userOptions,

View File

@ -26,7 +26,6 @@ const invalidTags = {
message: 'Open-Source Docusaurus sites can be useful for inspiration!',
id: 'showcase.tag.opensource.description',
},
// todo throw an error with `getTagsList tagSchema`
color: '#39c',
},
};
@ -55,7 +54,7 @@ describe('showcase tags', () => {
});
await expect(() => tagList).rejects.toThrowErrorMatchingInlineSnapshot(
`"There was an error extracting tags: Color must be a hexadecimal color string (e.g., #RRGGBB #rrggbb)"`,
`"There was an error extracting tags: Color must be a hexadecimal color string (e.g., #14cfc3 #E9669E)"`,
);
});
});

View File

@ -38,7 +38,6 @@ async function prepareSchema() {
return createShowcaseItemSchema(tagList);
}
// todo broken
describe('showcase item schema', () => {
it('accepts valid item', async () => {
const item: ShowcaseItem = {

View File

@ -7,14 +7,11 @@
import fs from 'fs-extra';
import path from 'path';
import {
getFolderContainingFile,
getPluginI18nPath,
Globby,
} from '@docusaurus/utils';
import Yaml from 'js-yaml';
import {createShowcaseItemSchema, validateShowcaseItem} from './validation';
import {getPluginI18nPath} from '@docusaurus/utils';
import {createShowcaseItemSchema} from './validation';
import {getTagsList} from './tags';
import {processContentLoaded} from './lifecycle/contentLoaded';
import {processLoadContent} from './lifecycle/loadContent';
import type {LoadContext, Plugin} from '@docusaurus/types';
import type {
PluginOptions,
@ -34,14 +31,14 @@ export default async function pluginContentShowcase(
): Promise<Plugin<ShowcaseItems | null>> {
const {siteDir, localizationDir} = context;
// todo check for better naming of path: sitePath
const {include, exclude, tags, routeBasePath, path: sitePath} = options;
const {include, exclude, tags, routeBasePath, path: sitePath, id} = options;
const contentPaths: ShowcaseContentPaths = {
contentPath: path.resolve(siteDir, sitePath),
contentPathLocalized: getPluginI18nPath({
localizationDir,
pluginName: 'docusaurus-plugin-content-showcase',
pluginId: options.id,
pluginId: id,
}),
};
@ -55,13 +52,16 @@ export default async function pluginContentShowcase(
return {
name: 'docusaurus-plugin-content-showcase',
// todo doesn't work
// getPathsToWatch() {
// const {include} = options;
// return getContentPathList(contentPaths).flatMap((contentPath) =>
// include.map((pattern) => `${contentPath}/${pattern}`),
// );
// },
// TODO doesn't work
getPathsToWatch() {
console.log(
'getContentPathList(contentPaths):',
getContentPathList(contentPaths),
);
return getContentPathList(contentPaths).flatMap((contentPath) =>
include.map((pattern) => `${contentPath}/${pattern}`),
);
},
async loadContent(): Promise<ShowcaseItems | null> {
if (!(await fs.pathExists(contentPaths.contentPath))) {
@ -70,45 +70,12 @@ export default async function pluginContentShowcase(
);
}
const showcaseFiles = await Globby(include, {
cwd: contentPaths.contentPath,
ignore: [...exclude],
return processLoadContent({
include,
exclude,
contentPaths,
showcaseItemSchema,
});
async function processShowcaseSourceFile(relativeSource: string) {
// Lookup in localized folder in priority
const contentPath = await getFolderContainingFile(
getContentPathList(contentPaths),
relativeSource,
);
const sourcePath = path.join(contentPath, relativeSource);
const data = await fs.readFile(sourcePath, 'utf-8');
const item = Yaml.load(data);
const showcaseItem = validateShowcaseItem({
item,
showcaseItemSchema,
});
return showcaseItem;
}
async function doProcessShowcaseSourceFile(relativeSource: string) {
try {
return await processShowcaseSourceFile(relativeSource);
} catch (err) {
throw new Error(
`Processing of page source file path=${relativeSource} failed.`,
{cause: err},
);
}
}
return {
items: await Promise.all(
showcaseFiles.map(doProcessShowcaseSourceFile),
),
};
},
async contentLoaded({content, actions}) {
@ -118,19 +85,11 @@ export default async function pluginContentShowcase(
const {addRoute, createData} = actions;
const showcaseAllData = await createData(
'showcaseAll.json',
JSON.stringify(content.items),
);
addRoute({
path: routeBasePath,
component: '@theme/Showcase',
modules: {
content: showcaseAllData,
// img: '@site/src/showcase/website/ozaki/aot.jpg',
},
exact: true,
await processContentLoaded({
content,
routeBasePath,
addRoute,
createData,
});
},
};

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type {ShowcaseItems} from '@docusaurus/plugin-content-showcase';
import type {PluginContentLoadedActions} from '@docusaurus/types';
export async function processContentLoaded({
content,
routeBasePath,
addRoute,
createData,
}: {
content: ShowcaseItems;
routeBasePath: string;
addRoute: PluginContentLoadedActions['addRoute'];
createData: PluginContentLoadedActions['createData'];
}): Promise<void> {
const showcaseAllData = await createData(
'showcaseAll.json',
JSON.stringify(content.items),
);
addRoute({
path: routeBasePath,
component: '@theme/Showcase',
modules: {
content: showcaseAllData,
},
exact: true,
});
}

View File

@ -0,0 +1,68 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import fs from 'fs-extra';
import path from 'path';
import Yaml from 'js-yaml';
import {
Globby,
getContentPathList,
getFolderContainingFile,
} from '@docusaurus/utils';
import {validateShowcaseItem} from '../validation';
import type {ShowcaseContentPaths} from '../types';
import type {ShowcaseItems} from '@docusaurus/plugin-content-showcase';
export async function processLoadContent({
include,
exclude,
contentPaths,
showcaseItemSchema,
}: {
include: string[];
exclude: string[];
contentPaths: ShowcaseContentPaths;
showcaseItemSchema: any;
}): Promise<ShowcaseItems | null> {
const showcaseFiles = await Globby(include, {
cwd: contentPaths.contentPath,
ignore: [...exclude],
});
async function processShowcaseSourceFile(relativeSource: string) {
// Lookup in localized folder in priority
const contentPath = await getFolderContainingFile(
getContentPathList(contentPaths),
relativeSource,
);
const sourcePath = path.join(contentPath, relativeSource);
const data = await fs.readFile(sourcePath, 'utf-8');
const item = Yaml.load(data);
const showcaseItem = validateShowcaseItem({
item,
showcaseItemSchema,
});
return showcaseItem;
}
async function doProcessShowcaseSourceFile(relativeSource: string) {
try {
return await processShowcaseSourceFile(relativeSource);
} catch (err) {
throw new Error(
`Processing of page source file path=${relativeSource} failed.`,
{cause: err},
);
}
}
return {
items: await Promise.all(showcaseFiles.map(doProcessShowcaseSourceFile)),
};
}

View File

@ -12,11 +12,10 @@ import type {OptionValidationContext} from '@docusaurus/types';
import type {PluginOptions, Options} from '@docusaurus/plugin-content-showcase';
export const DEFAULT_OPTIONS: PluginOptions = {
id: 'showcase',
path: 'showcase', // Path to data on filesystem, relative to site dir.
routeBasePath: '/showcase', // URL Route.
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.*'],
tags: 'tags.yml',
};
@ -26,9 +25,7 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
routeBasePath: RouteBasePathSchema.default(DEFAULT_OPTIONS.routeBasePath),
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude),
id: Joi.string().default(DEFAULT_OPTIONS.id),
tags: Joi.alternatives()
// todo ozaki understand this
.try(Joi.string().default(DEFAULT_OPTIONS.tags), tagSchema)
.default(DEFAULT_OPTIONS.tags),
});

View File

@ -20,12 +20,11 @@ export const tagSchema = Joi.object().pattern(
id: Joi.string().required(),
}).required(),
color: Joi.string()
// todo doesnt work ???
.pattern(/^#[\dA-Fa-f]{6}$/)
.required()
.messages({
'string.pattern.base':
'Color must be a hexadecimal color string (e.g., #RRGGBB #rrggbb)',
'Color must be a hexadecimal color string (e.g., #14cfc3 #E9669E)',
}),
}),
);
@ -38,8 +37,6 @@ export async function getTagsList({
configPath: string;
}): Promise<string[]> {
if (typeof configTags === 'object') {
console.log('Gettings Tag List and validating');
tagSchema.describe();
const tags = tagSchema.validate(configTags);
if (tags.error) {
throw new Error(
@ -72,5 +69,5 @@ export async function getTagsList({
}
export function createTagSchema(tags: string[]): Joi.Schema {
return Joi.array().items(Joi.string().valid(...tags)); // Schema for array of strings
return Joi.array().items(Joi.string().valid(...tags));
}