wip use plugin page code logic

This commit is contained in:
ozakione 2024-03-26 19:13:35 +01:00
parent 49b67463bd
commit 6e40a79b6f
5 changed files with 126 additions and 58 deletions

View file

@ -14,33 +14,37 @@ import {
addTrailingPathSeparator, addTrailingPathSeparator,
aliasedSitePath, aliasedSitePath,
docuHash, docuHash,
getFolderContainingFile,
getPluginI18nPath,
Globby,
} from '@docusaurus/utils'; } from '@docusaurus/utils';
import Yaml from 'js-yaml'; import Yaml from 'js-yaml';
import {contentAuthorsSchema} from './options'; import {contentAuthorsSchema} from './options';
import type {LoadContext, Plugin} from '@docusaurus/types'; import type {LoadContext, Plugin} from '@docusaurus/types';
import type { import type {PluginOptions, Content} from '@docusaurus/plugin-showcase';
PluginOptions, import type {ShowcaseContentPaths} from './types';
Content,
ShowcaseMetadata,
} from '@docusaurus/plugin-showcase';
// https://stackoverflow.com/a/71166133 export function getContentPathList(
const walk = async (dirPath: string): Promise<any[]> => contentPaths: ShowcaseContentPaths,
Promise.all( ): string[] {
await fs.readdir(dirPath, {withFileTypes: true}).then((entries) => return [contentPaths.contentPathLocalized, contentPaths.contentPath];
entries.map((entry) => { }
const childPath = path.join(dirPath, entry.name);
return entry.isDirectory() ? walk(childPath) : childPath;
}),
),
);
export default function pluginContentShowcase( export default function pluginContentShowcase(
context: LoadContext, context: LoadContext,
options: PluginOptions, options: PluginOptions,
): Plugin<Content> { ): Plugin<Content | null> {
const {siteConfig, siteDir, generatedFilesDir} = context; const {siteConfig, siteDir, generatedFilesDir, localizationDir} = context;
const contentPaths: ShowcaseContentPaths = {
contentPath: path.resolve(siteDir, options.path),
contentPathLocalized: getPluginI18nPath({
localizationDir,
pluginName: 'docusaurus-plugin-content-pages',
pluginId: options.id,
}),
};
const pluginDataDirRoot = path.join( const pluginDataDirRoot = path.join(
generatedFilesDir, generatedFilesDir,
@ -48,35 +52,52 @@ export default function pluginContentShowcase(
); );
const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID); const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID);
const showcasePath = path.join(siteDir, options.path);
return { return {
name: 'docusaurus-plugin-showcase', name: 'docusaurus-plugin-showcase',
// getPathsToWatch() { getPathsToWatch() {
// return [path.join(siteDir, options.path, 'authors.yaml')]; const {include} = options;
// }, return getContentPathList(contentPaths).flatMap((contentPath) =>
include.map((pattern) => `${contentPath}/${pattern}`),
);
},
async loadContent(): Promise<Content> { async loadContent() {
const files: string[] = await walk(path.join(siteDir, options.path)); const {include} = options;
const filteredFiles = files
.flat(Number.POSITIVE_INFINITY)
.filter((file) => file.endsWith('.yaml'));
const contentPromises = filteredFiles.map(async (file) => { if (!(await fs.pathExists(contentPaths.contentPath))) {
const rawYaml = await fs.readFile(path.join(file), 'utf-8'); return null;
const yaml = Yaml.load(rawYaml); }
const parsedYaml = contentAuthorsSchema.validate(yaml);
if (parsedYaml.error) { // const {baseUrl} = siteConfig;
throw new Error(`Validation failed: ${parsedYaml.error.message}`, { const showcaseFiles = await Globby(include, {
cause: parsedYaml.error, cwd: contentPaths.contentPath,
ignore: options.exclude,
});
const filteredFiles = showcaseFiles.filter((file) =>
file.endsWith('.yaml'),
);
async function processPageSourceFile(relativeSource: string) {
// Lookup in localized folder in priority
const contentPath = await getFolderContainingFile(
getContentPathList(contentPaths),
relativeSource,
);
const sourcePath = path.join(contentPath, relativeSource);
const aliasedSourcePath = aliasedSitePath(sourcePath, siteDir);
const rawYaml = await fs.readFile(sourcePath, 'utf-8');
const unsafeYaml = Yaml.load(rawYaml);
const yaml = contentAuthorsSchema.validate(unsafeYaml);
if (yaml.error) {
throw new Error(`Validation failed: ${yaml.error.message}`, {
cause: yaml.error,
}); });
} }
const {title, description, preview, website, source, tags} = const {title, description, preview, website, source, tags} = yaml.value;
parsedYaml.value;
return { return {
title, title,
description, description,
@ -85,11 +106,21 @@ export default function pluginContentShowcase(
source, source,
tags, tags,
}; };
}); }
async function doProcessPageSourceFile(relativeSource: string) {
try {
return await processPageSourceFile(relativeSource);
} catch (err) {
throw new Error(
`Processing of page source file path=${relativeSource} failed.`,
{cause: err as Error},
);
}
}
const content = await Promise.all(contentPromises);
return { return {
website: content, website: await Promise.all(filteredFiles.map(doProcessPageSourceFile)),
}; };
}, },
@ -134,17 +165,19 @@ export default function pluginContentShowcase(
}, },
configureWebpack(_config, isServer, utils, content) { configureWebpack(_config, isServer, utils, content) {
const contentDirs = getContentPathList(contentPaths);
return { return {
resolve: { resolve: {
alias: { alias: {
'~blog': pluginDataDirRoot, '~showcase': pluginDataDirRoot,
}, },
}, },
module: { module: {
rules: [ rules: [
{ {
test: /\.mdx?$/i, test: /\.mdx?$/i,
include: [...showcasePath] include: contentDirs
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970 // Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
.map(addTrailingPathSeparator), .map(addTrailingPathSeparator),
use: [ use: [
@ -155,6 +188,10 @@ export default function pluginContentShowcase(
path.resolve(siteDir, dir), path.resolve(siteDir, dir),
), ),
siteDir, siteDir,
// isMDXPartial: createAbsoluteFilePathMatcher(
// options.exclude,
// contentDirs,
// ),
metadataPath: (mdxPath: string) => { metadataPath: (mdxPath: string) => {
// Note that metadataPath must be the same/in-sync as // Note that metadataPath must be the same/in-sync as
// the path from createData for each MDX. // the path from createData for each MDX.
@ -164,24 +201,15 @@ export default function pluginContentShowcase(
`${docuHash(aliasedPath)}.json`, `${docuHash(aliasedPath)}.json`,
); );
}, },
// For blog posts a title in markdown is always removed
// Blog posts title are rendered separately
removeContentTitle: true,
// Assets allow to convert some relative images paths to // Assets allow to convert some relative images paths to
// require() calls // require() calls
// createAssets: ({ createAssets: ({
// frontMatter, frontMatter,
// metadata, }: {
// }: { frontMatter: Content['website'][number];
// frontMatter: Content['website'][number]; }) => ({
// metadata: ShowcaseMetadata; image: frontMatter.preview,
// }): Assets => ({ }),
// image: frontMatter.preview,
// authorsImageUrls: metadata.frontMatter.preview.map(
// (author) => author.imageURL,
// ),
// }),
markdownConfig: siteConfig.markdown, markdownConfig: siteConfig.markdown,
}, },
}, },

View file

@ -0,0 +1,22 @@
/**
* 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 {LoaderContext} from 'webpack';
export default function markdownLoader(
this: LoaderContext<undefined>,
fileString: string,
): void {
const callback = this.async();
// const options = this.getOptions();
// TODO provide additional md processing here? like interlinking pages?
// fileString = linkify(fileString)
return callback(null, fileString);
}

View file

@ -6,6 +6,7 @@
*/ */
import {Joi, RouteBasePathSchema} from '@docusaurus/utils-validation'; import {Joi, RouteBasePathSchema} from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
import type {OptionValidationContext} from '@docusaurus/types'; import type {OptionValidationContext} from '@docusaurus/types';
import type {PluginOptions, Options} from '@docusaurus/plugin-showcase'; import type {PluginOptions, Options} from '@docusaurus/plugin-showcase';
@ -13,11 +14,16 @@ export const DEFAULT_OPTIONS: PluginOptions = {
id: 'showcase', id: 'showcase',
path: 'src/showcase/website', // Path to data on filesystem, relative to site dir. path: 'src/showcase/website', // Path to data on filesystem, relative to site dir.
routeBasePath: '/', // URL Route. routeBasePath: '/', // URL Route.
include: ['**/*.{yml,yaml,md,mdx}'], // Extensions to include.
exclude: GlobExcludeDefault,
}; };
const PluginOptionSchema = Joi.object<PluginOptions>({ const PluginOptionSchema = Joi.object<PluginOptions>({
path: Joi.string().default(DEFAULT_OPTIONS.path), path: Joi.string().default(DEFAULT_OPTIONS.path),
routeBasePath: RouteBasePathSchema.default(DEFAULT_OPTIONS.routeBasePath), 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),
}); });
export const contentAuthorsSchema = Joi.object({ export const contentAuthorsSchema = Joi.object({

View file

@ -16,6 +16,8 @@ declare module '@docusaurus/plugin-showcase' {
id?: string; id?: string;
path: string; path: string;
routeBasePath: string; routeBasePath: string;
include: string[];
exclude: string[];
}; };
export type TagType = export type TagType =

View file

@ -0,0 +1,10 @@
/**
* 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.
*/
export type ShowcaseContentPaths = {
contentPath: string;
contentPathLocalized: string;
};