Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: showcase #9967

Draft
wants to merge 87 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
2b37b51
wip init
OzakIOne Mar 20, 2024
f6b0d46
refactor: apply lint autofix
OzakIOne Mar 20, 2024
cd3908b
wip
OzakIOne Mar 21, 2024
a2516dc
refactor: apply lint autofix
OzakIOne Mar 21, 2024
7cf981c
wip routes per yaml
OzakIOne Mar 21, 2024
ec35998
refactor: apply lint autofix
OzakIOne Mar 21, 2024
8d1b174
wip
OzakIOne Mar 24, 2024
7daa9a1
refactor: apply lint autofix
OzakIOne Mar 24, 2024
c542c81
ShowcaseDetails component
OzakIOne Mar 24, 2024
dddb8cf
cspell autolint fix
OzakIOne Mar 24, 2024
1c825e0
refactor: apply lint autofix
OzakIOne Mar 24, 2024
9790934
wip markdown process: crash
OzakIOne Mar 25, 2024
49b6746
refactor: apply lint autofix
OzakIOne Mar 25, 2024
6e40a79
wip use plugin page code logic
OzakIOne Mar 26, 2024
984518e
fix: warning
OzakIOne Mar 26, 2024
993efd2
wip process markdown
OzakIOne Mar 26, 2024
8bee535
wip images
OzakIOne Mar 27, 2024
022efa3
refactor: apply lint autofix
OzakIOne Mar 27, 2024
30599c3
Update packages/docusaurus-plugin-showcase/package.json
OzakIOne Mar 29, 2024
bed9bb3
wip tests
OzakIOne Mar 29, 2024
c42c15e
refactor: apply lint autofix
OzakIOne Mar 29, 2024
e147034
wip tests
OzakIOne Mar 29, 2024
cdb7c07
consistent file naming
OzakIOne Mar 29, 2024
46c57d6
wip tags file validation
OzakIOne Mar 31, 2024
4a221f3
wip working tags from yaml
OzakIOne Apr 1, 2024
fc34725
prettier
OzakIOne Apr 1, 2024
6b83e2d
wip
OzakIOne Apr 1, 2024
c3f32ab
wip
OzakIOne Apr 1, 2024
8ab6dda
wip
OzakIOne Apr 1, 2024
45af11f
add tests
OzakIOne Apr 3, 2024
a9d6bcf
wip
OzakIOne Apr 4, 2024
91aa292
improve type
OzakIOne Apr 4, 2024
4cecd0c
wip
OzakIOne Apr 4, 2024
6670b7a
fix types
OzakIOne Apr 5, 2024
d9885db
dynamic validation
OzakIOne Apr 5, 2024
9eab6ae
move schema creation outside of loadcontent
OzakIOne Apr 5, 2024
0fdb9d6
wip tests
OzakIOne Apr 8, 2024
290cdb4
wip tests
OzakIOne Apr 8, 2024
65affab
wip tests
OzakIOne Apr 8, 2024
867b604
wip
OzakIOne Apr 9, 2024
e1c51bf
wip
OzakIOne Apr 9, 2024
c89f8be
Merge branch 'main' into ozaki/showcase
OzakIOne Apr 11, 2024
7a9418c
Merge branch 'main' into ozaki/showcase
OzakIOne Apr 11, 2024
047b5f0
versions
slorber Apr 11, 2024
3cef207
refactor: apply lint autofix
slorber Apr 11, 2024
c0a6520
yarnlock
slorber Apr 11, 2024
6435e9a
Merge remote-tracking branch 'origin/ozaki/showcase' into ozaki/showcase
slorber Apr 11, 2024
2af5b44
remove popper/tooltip
slorber Apr 11, 2024
8f9408c
refactor: apply lint autofix
slorber Apr 11, 2024
81acd87
showcase plugin client API setup
slorber Apr 11, 2024
4f64b33
Merge remote-tracking branch 'origin/ozaki/showcase' into ozaki/showcase
slorber Apr 11, 2024
0cfdd9d
hooks bypass extreme wip
OzakIOne Apr 11, 2024
c0c0f83
update with client showcase
OzakIOne Apr 15, 2024
7b76e46
fix type error
OzakIOne Apr 16, 2024
8504ad3
revert changes
OzakIOne Apr 16, 2024
1fb54b6
Merge branch 'main' into ozaki/showcase
OzakIOne Apr 16, 2024
ff16819
refactor: apply lint autofix
OzakIOne Apr 16, 2024
52bf244
wip showcase provider :D
OzakIOne Apr 16, 2024
c7cbafc
feat: new route props
OzakIOne Apr 16, 2024
5b6626b
wip
OzakIOne Apr 16, 2024
8c12b1c
wip
OzakIOne Apr 17, 2024
a4cb0ff
refactor: apply lint autofix
OzakIOne Apr 17, 2024
61a7650
add all sites
OzakIOne Apr 17, 2024
c903765
refactor: apply lint autofix
OzakIOne Apr 17, 2024
2855df5
allow null options
OzakIOne Apr 17, 2024
38a71a6
fix tests
OzakIOne Apr 17, 2024
c9a05e2
wip
OzakIOne Apr 19, 2024
8c1770b
admin script
OzakIOne Apr 19, 2024
7869eb9
Merge branch 'main' into ozaki/showcase
OzakIOne Apr 20, 2024
82858f8
fix build
OzakIOne Apr 20, 2024
77aaf4a
refactor: apply lint autofix
OzakIOne Apr 20, 2024
34a0e3f
update admin script
OzakIOne Apr 20, 2024
40000d0
refactor clean
OzakIOne Apr 20, 2024
510539f
refactor: apply lint autofix
OzakIOne Apr 20, 2024
c8b7e6d
screenshot api
OzakIOne Apr 20, 2024
01657b0
Merge branch 'main' into ozaki/showcase
slorber May 3, 2024
2c99553
Fix packages
slorber May 3, 2024
57665a8
adjust ts configs for showcase
slorber May 3, 2024
080949b
fix packages
slorber May 3, 2024
841c81f
normalize tsBuildInfoFile
slorber May 3, 2024
99a4c5f
Merge branch 'main' into ozaki/showcase
slorber May 13, 2024
7e417aa
remove bad MDX import
slorber May 13, 2024
8ad0747
wip routeContext
OzakIOne May 16, 2024
633397a
wip
OzakIOne May 16, 2024
77ee19e
fix unknown word test
OzakIOne May 16, 2024
27a57d0
refactor: apply lint autofix
OzakIOne May 16, 2024
dee675c
feat route context
OzakIOne May 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/docusaurus-plugin-showcase/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.tsbuildinfo*
tsconfig*
__tests__
7 changes: 7 additions & 0 deletions packages/docusaurus-plugin-showcase/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `@docusaurus/plugin-showcase`
OzakIOne marked this conversation as resolved.
Show resolved Hide resolved

Showcase plugin for Docusaurus.

## Usage

See [plugin-showcase documentation](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-showcase).
37 changes: 37 additions & 0 deletions packages/docusaurus-plugin-showcase/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@docusaurus/plugin-showcase",
OzakIOne marked this conversation as resolved.
Show resolved Hide resolved
"version": "3.0.0",
"description": "Showcase plugin for Docusaurus.",
"main": "lib/index.js",
"types": "src/plugin-showcase.d.ts",
"scripts": {
"build": "tsc",
"watch": "tsc --watch"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/facebook/docusaurus.git",
"directory": "packages/docusaurus-plugin-showcase"
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"fs-extra": "^11.1.1",
"js-yaml": "^4.1.0",
"tslib": "^2.6.0",
"webpack": "^5.88.1"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"engines": {
"node": ">=18.0"
}
}
257 changes: 257 additions & 0 deletions packages/docusaurus-plugin-showcase/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/**
* 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 {
DEFAULT_PLUGIN_ID,
addTrailingPathSeparator,
aliasedSitePath,
docuHash,
getFolderContainingFile,
getPluginI18nPath,
Globby,
parseMarkdownFile,
aliasedSitePathToRelativePath,
createAbsoluteFilePathMatcher,
} from '@docusaurus/utils';
import Yaml from 'js-yaml';

import {validateShowcaseFrontMatter} from './options';
import type {LoadContext, Plugin, RouteMetadata} from '@docusaurus/types';
import type {PluginOptions, Content} from '@docusaurus/plugin-showcase';
import type {ShowcaseContentPaths} from './types';

export function getContentPathList(
contentPaths: ShowcaseContentPaths,
): string[] {
return [contentPaths.contentPathLocalized, contentPaths.contentPath];
}

const isMarkdownSource = (source: string) =>
source.endsWith('.md') || source.endsWith('.mdx');

export default function pluginContentShowcase(
context: LoadContext,
options: PluginOptions,
): Plugin<Content | null> {
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(
generatedFilesDir,
'docusaurus-plugin-showcase',
);
const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID);

return {
name: 'docusaurus-plugin-showcase',

// todo doesn't work
// getPathsToWatch() {
// const {include} = options;
// return getContentPathList(contentPaths).flatMap((contentPath) =>
// include.map((pattern) => `${contentPath}/${pattern}`),
// );
// },

async loadContent() {
const {include} = options;

if (!(await fs.pathExists(contentPaths.contentPath))) {
return null;
}

// const {baseUrl} = siteConfig;
const showcaseFiles = await Globby(include, {
cwd: contentPaths.contentPath,
ignore: options.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 aliasedSourcePath = aliasedSitePath(sourcePath, siteDir);
if (!isMarkdownSource(sourcePath)) {
const rawYaml = await fs.readFile(sourcePath, 'utf-8');
const unsafeYaml = Yaml.load(rawYaml) as {[key: string]: unknown};
const yaml = validateShowcaseFrontMatter(unsafeYaml);
return {
type: 'yaml',
...yaml,
};
}
const rawMarkdown = await fs.readFile(sourcePath, 'utf-8');
const {frontMatter: unsafeFrontMatter, content} =
await parseMarkdownFile({
filePath: sourcePath,
fileContent: rawMarkdown,
parseFrontMatter: siteConfig.markdown?.parseFrontMatter,
});
const frontMatter = validateShowcaseFrontMatter(unsafeFrontMatter);
return {
type: 'markdown',
...frontMatter,
content,
sourcePath: aliasedSourcePath,
};
}

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 as Error},
);
}
}

return {
website: await Promise.all(
showcaseFiles.map(doProcessShowcaseSourceFile),
),
};
},

async contentLoaded({content, actions}) {
if (!content) {
return;
}

const {addRoute, createData} = actions;

function createPageRouteMetadata(
metadata: Content['website'][number],
): RouteMetadata {
return {
sourceFilePath: aliasedSitePathToRelativePath(metadata.sourcePath!),
// TODO add support for last updated date in the page plugin
// at least for Markdown files
// lastUpdatedAt: metadata.lastUpdatedAt,
lastUpdatedAt: undefined,
};
}

await Promise.all(
content.website.map(async (item) => {
if (item.type === 'yaml') {
return;
}
await createData(
`${docuHash(item.sourcePath!)}.json`,
JSON.stringify(item),
);

const routeMetadata = createPageRouteMetadata(item);

const mdxPath = aliasedSitePathToRelativePath(item.sourcePath!);
console.log('mdxPath', mdxPath);

addRoute({
path: `/showcaseAll/${item.title}`,
component: '@theme/ShowcaseDetails',
metadata: routeMetadata,
modules: {
content: item.sourcePath!,
},
exact: true,
});
}),
);

const showcaseAllData = await createData(
'showcaseAll.json',
JSON.stringify(content.website),
);

addRoute({
path: '/showcaseAll',
component: '@theme/Showcase',
modules: {
content: showcaseAllData,
},
exact: true,
});
},

configureWebpack() {
const contentDirs = getContentPathList(contentPaths);

return {
resolve: {
alias: {
'~showcase': pluginDataDirRoot,
},
},
module: {
rules: [
{
test: /\.mdx?$/i,
include: contentDirs
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
.map(addTrailingPathSeparator),
use: [
{
loader: require.resolve('@docusaurus/mdx-loader'),
options: {
staticDirs: siteConfig.staticDirectories.map((dir) =>
path.resolve(siteDir, dir),
),
siteDir,
isMDXPartial: createAbsoluteFilePathMatcher(
options.exclude,
contentDirs,
),
metadataPath: (mdxPath: string) => {
// Note that metadataPath must be the same/in-sync as
// the path from createData for each MDX.
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
return path.join(
dataDir,
`${docuHash(aliasedPath)}.json`,
);
},
// Assets allow to convert some relative images paths to
// require() calls
createAssets: ({
frontMatter,
}: {
frontMatter: Content['website'][number];
}) => ({
image: frontMatter.preview,
}),
markdownConfig: siteConfig.markdown,
},
},
{
loader: path.resolve(__dirname, './markdownLoader.js'),
},
].filter(Boolean),
},
],
},
};
},
};
}

export {validateOptions} from './options';
22 changes: 22 additions & 0 deletions packages/docusaurus-plugin-showcase/src/markdownLoader.ts
Original file line number Diff line number Diff line change
@@ -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);
}
58 changes: 58 additions & 0 deletions packages/docusaurus-plugin-showcase/src/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* 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 {
Joi,
validateFrontMatter,
RouteBasePathSchema,
} from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
import type {OptionValidationContext} from '@docusaurus/types';
import type {
PluginOptions,
Options,
ShowcaseFrontMatter,
} from '@docusaurus/plugin-showcase';

export const DEFAULT_OPTIONS: PluginOptions = {
id: 'showcase',
path: 'src/showcase/website', // Path to data on filesystem, relative to site dir.
routeBasePath: '/', // URL Route.
include: ['**/*.{yml,yaml,md,mdx}'], // Extensions to include.
exclude: GlobExcludeDefault,
};

const PluginOptionSchema = Joi.object<PluginOptions>({
path: Joi.string().default(DEFAULT_OPTIONS.path),
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),
});

const contentAuthorsSchema = Joi.object({
title: Joi.string().required(),
description: Joi.string().required(),
preview: Joi.string().required(),
website: Joi.string().required(),
source: Joi.string().required(),
tags: Joi.array().items(Joi.string()).required(),
});
OzakIOne marked this conversation as resolved.
Show resolved Hide resolved

export function validateOptions({
validate,
options,
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
const validatedOptions = validate(PluginOptionSchema, options);
return validatedOptions;
}

export function validateShowcaseFrontMatter(frontMatter: {
[key: string]: unknown;
}): ShowcaseFrontMatter {
return validateFrontMatter(frontMatter, contentAuthorsSchema);
}
OzakIOne marked this conversation as resolved.
Show resolved Hide resolved