mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-30 23:08:54 +02:00
wip tests
This commit is contained in:
parent
30599c362e
commit
bed9bb3063
21 changed files with 785 additions and 299 deletions
7
packages/docusaurus-plugin-content-showcase/README.md
Normal file
7
packages/docusaurus-plugin-content-showcase/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# `@docusaurus/plugin-content-showcase`
|
||||
|
||||
Showcase plugin for Docusaurus.
|
||||
|
||||
## Usage
|
||||
|
||||
See [plugin-content-showcase documentation](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-showcase).
|
|
@ -3,7 +3,7 @@
|
|||
"version": "3.0.0",
|
||||
"description": "Showcase plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin-showcase.d.ts",
|
||||
"types": "src/plugin-content-showcase.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"watch": "tsc --watch"
|
||||
|
@ -14,7 +14,7 @@
|
|||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/docusaurus.git",
|
||||
"directory": "packages/docusaurus-plugin-showcase"
|
||||
"directory": "packages/docusaurus-plugin-content-showcase"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
21
packages/docusaurus-plugin-content-showcase/src/__tests__/__fixtures__/website/docusaurus.config.js
generated
Normal file
21
packages/docusaurus-plugin-content-showcase/src/__tests__/__fixtures__/website/docusaurus.config.js
generated
Normal file
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
title: 'My Site',
|
||||
tagline: 'The tagline of my site',
|
||||
url: 'https://your-docusaurus-site.example.com',
|
||||
baseUrl: '/',
|
||||
favicon: 'img/favicon.ico',
|
||||
markdown: {
|
||||
parseFrontMatter: async (params) => {
|
||||
const result = await params.defaultParseFrontMatter(params);
|
||||
result.frontMatter.custom_frontMatter = 'added by parseFrontMatter';
|
||||
return result;
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
title: "Hello"
|
||||
description: "World"
|
||||
preview: github.com/ozakione.png
|
||||
website: "https://docusaurus.io/"
|
||||
source: "https://github.com/facebook/docusaurus"
|
||||
tags: ["opensource", "meta"]
|
|
@ -0,0 +1,19 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`docusaurus-plugin-content-showcase loads simple showcase 1`] = `
|
||||
{
|
||||
"website": [
|
||||
{
|
||||
"description": "World",
|
||||
"preview": "github.com/ozakione.png",
|
||||
"source": "https://github.com/facebook/docusaurus",
|
||||
"tags": [
|
||||
"opensource",
|
||||
"meta",
|
||||
],
|
||||
"title": "Hello",
|
||||
"website": "https://docusaurus.io/",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,479 @@
|
|||
/**
|
||||
* 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 {escapeRegexp} from '@docusaurus/utils';
|
||||
import {validateShowcaseFrontMatter} from '../yaml';
|
||||
import type {ShowcaseFrontMatter} from '@docusaurus/plugin-content-showcase';
|
||||
|
||||
function testField(params: {
|
||||
prefix: string;
|
||||
validFrontMatters: ShowcaseFrontMatter[];
|
||||
convertibleFrontMatter?: [
|
||||
ConvertibleFrontMatter: {[key: string]: unknown},
|
||||
ConvertedFrontMatter: ShowcaseFrontMatter,
|
||||
][];
|
||||
invalidFrontMatters?: [
|
||||
InvalidFrontMatter: {[key: string]: unknown},
|
||||
ErrorMessage: string,
|
||||
][];
|
||||
}) {
|
||||
// eslint-disable-next-line jest/require-top-level-describe
|
||||
test(`[${params.prefix}] accept valid values`, () => {
|
||||
params.validFrontMatters.forEach((frontMatter) => {
|
||||
expect(validateShowcaseFrontMatter(frontMatter)).toEqual(frontMatter);
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/require-top-level-describe
|
||||
test(`[${params.prefix}] convert valid values`, () => {
|
||||
params.convertibleFrontMatter?.forEach(
|
||||
([convertibleFrontMatter, convertedFrontMatter]) => {
|
||||
expect(validateShowcaseFrontMatter(convertibleFrontMatter)).toEqual(
|
||||
convertedFrontMatter,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/require-top-level-describe
|
||||
test(`[${params.prefix}] throw error for values`, () => {
|
||||
params.invalidFrontMatters?.forEach(([frontMatter, message]) => {
|
||||
try {
|
||||
validateShowcaseFrontMatter(frontMatter);
|
||||
throw new Error(
|
||||
`Doc front matter is expected to be rejected, but was accepted successfully:\n ${JSON.stringify(
|
||||
frontMatter,
|
||||
null,
|
||||
2,
|
||||
)}`,
|
||||
);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect((err as Error).message).toMatch(
|
||||
new RegExp(escapeRegexp(message)),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('doc front matter schema', () => {
|
||||
it('accepts empty object', () => {
|
||||
const frontMatter: ShowcaseFrontMatter = {};
|
||||
expect(validateShowcaseFrontMatter(frontMatter)).toEqual(frontMatter);
|
||||
});
|
||||
|
||||
it('accepts unknown field', () => {
|
||||
const frontMatter = {abc: '1'};
|
||||
expect(validateShowcaseFrontMatter(frontMatter)).toEqual(frontMatter);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter id', () => {
|
||||
testField({
|
||||
prefix: 'id',
|
||||
validFrontMatters: [{id: '123'}, {id: 'unique_id'}],
|
||||
invalidFrontMatters: [[{id: ''}, 'is not allowed to be empty']],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter title', () => {
|
||||
testField({
|
||||
prefix: 'title',
|
||||
validFrontMatters: [
|
||||
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
|
||||
{title: ''},
|
||||
{title: 'title'},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter hide_title', () => {
|
||||
testField({
|
||||
prefix: 'hide_title',
|
||||
validFrontMatters: [{hide_title: true}, {hide_title: false}],
|
||||
convertibleFrontMatter: [
|
||||
[{hide_title: 'true'}, {hide_title: true}],
|
||||
[{hide_title: 'false'}, {hide_title: false}],
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[{hide_title: 'yes'}, 'must be a boolean'],
|
||||
[{hide_title: 'no'}, 'must be a boolean'],
|
||||
[{hide_title: ''}, 'must be a boolean'],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter hide_table_of_contents', () => {
|
||||
testField({
|
||||
prefix: 'hide_table_of_contents',
|
||||
validFrontMatters: [
|
||||
{hide_table_of_contents: true},
|
||||
{hide_table_of_contents: false},
|
||||
],
|
||||
convertibleFrontMatter: [
|
||||
[{hide_table_of_contents: 'true'}, {hide_table_of_contents: true}],
|
||||
[{hide_table_of_contents: 'false'}, {hide_table_of_contents: false}],
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[{hide_table_of_contents: 'yes'}, 'must be a boolean'],
|
||||
[{hide_table_of_contents: 'no'}, 'must be a boolean'],
|
||||
[{hide_table_of_contents: ''}, 'must be a boolean'],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter keywords', () => {
|
||||
testField({
|
||||
prefix: 'keywords',
|
||||
validFrontMatters: [
|
||||
{keywords: ['hello']},
|
||||
{keywords: ['hello', 'world']},
|
||||
{keywords: ['hello', 'world']},
|
||||
{keywords: ['hello']},
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[{keywords: ''}, 'must be an array'],
|
||||
[{keywords: ['']}, 'is not allowed to be empty'],
|
||||
[{keywords: []}, 'does not contain 1 required value(s)'],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter image', () => {
|
||||
testField({
|
||||
prefix: 'image',
|
||||
validFrontMatters: [
|
||||
{image: 'https://docusaurus.io/blog/image.png'},
|
||||
{image: '/absolute/image.png'},
|
||||
{image: '../relative/image.png'},
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[{image: ''}, '"image" does not look like a valid url (value=\'\')'],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter description', () => {
|
||||
testField({
|
||||
prefix: 'description',
|
||||
validFrontMatters: [
|
||||
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
|
||||
{description: ''},
|
||||
{description: 'description'},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter slug', () => {
|
||||
testField({
|
||||
prefix: 'slug',
|
||||
validFrontMatters: [
|
||||
{slug: '/'},
|
||||
{slug: 'slug'},
|
||||
{slug: '/slug/'},
|
||||
{slug: './slug'},
|
||||
{slug: '../../slug'},
|
||||
{slug: '/api/plugins/@docusaurus'},
|
||||
{slug: '@site/api/asset'},
|
||||
{slug: 'slug1 slug2'},
|
||||
],
|
||||
invalidFrontMatters: [[{slug: ''}, 'is not allowed to be empty']],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter sidebar_label', () => {
|
||||
testField({
|
||||
prefix: 'sidebar_label',
|
||||
validFrontMatters: [{sidebar_label: 'Awesome docs'}],
|
||||
invalidFrontMatters: [[{sidebar_label: ''}, 'is not allowed to be empty']],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter sidebar_position', () => {
|
||||
testField({
|
||||
prefix: 'sidebar_position',
|
||||
validFrontMatters: [
|
||||
{sidebar_position: -5},
|
||||
{sidebar_position: -3.5},
|
||||
{sidebar_position: 0},
|
||||
{sidebar_position: 5},
|
||||
{sidebar_position: 3.5},
|
||||
],
|
||||
convertibleFrontMatter: [
|
||||
[{sidebar_position: '-1.5'}, {sidebar_position: -1.5}],
|
||||
[{sidebar_position: '1'}, {sidebar_position: 1}],
|
||||
[{sidebar_position: '1.5'}, {sidebar_position: 1.5}],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter sidebar_custom_props', () => {
|
||||
testField({
|
||||
prefix: 'sidebar_custom_props',
|
||||
validFrontMatters: [
|
||||
{sidebar_custom_props: {}},
|
||||
{sidebar_custom_props: {prop: 'custom', number: 1, boolean: true}},
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[{sidebar_custom_props: ''}, 'must be of type object'],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter custom_edit_url', () => {
|
||||
testField({
|
||||
prefix: 'custom_edit_url',
|
||||
validFrontMatters: [
|
||||
// See https://github.com/demisto/content-docs/pull/616#issuecomment-827087566
|
||||
{custom_edit_url: ''},
|
||||
{custom_edit_url: null},
|
||||
{custom_edit_url: 'https://github.com/facebook/docusaurus/markdown.md'},
|
||||
{custom_edit_url: '../../api/docs/markdown.md'},
|
||||
{custom_edit_url: '@site/api/docs/markdown.md'},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter parse_number_prefixes', () => {
|
||||
testField({
|
||||
prefix: 'parse_number_prefixes',
|
||||
validFrontMatters: [
|
||||
{parse_number_prefixes: true},
|
||||
{parse_number_prefixes: false},
|
||||
],
|
||||
convertibleFrontMatter: [
|
||||
[{parse_number_prefixes: 'true'}, {parse_number_prefixes: true}],
|
||||
[{parse_number_prefixes: 'false'}, {parse_number_prefixes: false}],
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[{parse_number_prefixes: 'yes'}, 'must be a boolean'],
|
||||
[{parse_number_prefixes: 'no'}, 'must be a boolean'],
|
||||
[{parse_number_prefixes: ''}, 'must be a boolean'],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter tags', () => {
|
||||
testField({
|
||||
prefix: 'tags',
|
||||
validFrontMatters: [{}, {tags: undefined}, {tags: ['tag1', 'tag2']}],
|
||||
convertibleFrontMatter: [[{tags: ['tag1', 42]}, {tags: ['tag1', '42']}]],
|
||||
invalidFrontMatters: [
|
||||
[
|
||||
{tags: 42},
|
||||
'"tags" does not look like a valid front matter Yaml array.',
|
||||
],
|
||||
[
|
||||
{tags: 'tag1, tag2'},
|
||||
'"tags" does not look like a valid front matter Yaml array.',
|
||||
],
|
||||
[{tags: [{}]}, '"tags[0]" does not look like a valid tag'],
|
||||
[{tags: [true]}, '"tags[0]" does not look like a valid tag'],
|
||||
[
|
||||
{tags: ['tag1', {hey: 'test'}]},
|
||||
'"tags[1]" does not look like a valid tag',
|
||||
],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('toc_min_heading_level', () => {
|
||||
testField({
|
||||
prefix: 'toc_min_heading_level',
|
||||
validFrontMatters: [
|
||||
{},
|
||||
{toc_min_heading_level: undefined},
|
||||
{toc_min_heading_level: 2},
|
||||
{toc_min_heading_level: 3},
|
||||
{toc_min_heading_level: 4},
|
||||
{toc_min_heading_level: 5},
|
||||
{toc_min_heading_level: 6},
|
||||
],
|
||||
convertibleFrontMatter: [
|
||||
[{toc_min_heading_level: '2'}, {toc_min_heading_level: 2}],
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[
|
||||
{toc_min_heading_level: 1},
|
||||
'"toc_min_heading_level" must be greater than or equal to 2',
|
||||
],
|
||||
[
|
||||
{toc_min_heading_level: 7},
|
||||
'"toc_min_heading_level" must be less than or equal to 6',
|
||||
],
|
||||
[
|
||||
{toc_min_heading_level: 'hello'},
|
||||
'"toc_min_heading_level" must be a number',
|
||||
],
|
||||
[
|
||||
{toc_min_heading_level: true},
|
||||
'"toc_min_heading_level" must be a number',
|
||||
],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('toc_max_heading_level', () => {
|
||||
testField({
|
||||
prefix: 'toc_max_heading_level',
|
||||
validFrontMatters: [
|
||||
{},
|
||||
{toc_max_heading_level: undefined},
|
||||
{toc_max_heading_level: 2},
|
||||
{toc_max_heading_level: 3},
|
||||
{toc_max_heading_level: 4},
|
||||
{toc_max_heading_level: 5},
|
||||
{toc_max_heading_level: 6},
|
||||
],
|
||||
convertibleFrontMatter: [
|
||||
[{toc_max_heading_level: '2'}, {toc_max_heading_level: 2}],
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[
|
||||
{toc_max_heading_level: 1},
|
||||
'"toc_max_heading_level" must be greater than or equal to 2',
|
||||
],
|
||||
[
|
||||
{toc_max_heading_level: 7},
|
||||
'"toc_max_heading_level" must be less than or equal to 6',
|
||||
],
|
||||
[
|
||||
{toc_max_heading_level: 'hello'},
|
||||
'"toc_max_heading_level" must be a number',
|
||||
],
|
||||
[
|
||||
{toc_max_heading_level: true},
|
||||
'"toc_max_heading_level" must be a number',
|
||||
],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('toc min/max consistency', () => {
|
||||
testField({
|
||||
prefix: 'toc min/max',
|
||||
validFrontMatters: [
|
||||
{},
|
||||
{toc_min_heading_level: undefined, toc_max_heading_level: undefined},
|
||||
{toc_min_heading_level: 2, toc_max_heading_level: 2},
|
||||
{toc_min_heading_level: 2, toc_max_heading_level: 6},
|
||||
{toc_min_heading_level: 2, toc_max_heading_level: 3},
|
||||
{toc_min_heading_level: 3, toc_max_heading_level: 3},
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[
|
||||
{toc_min_heading_level: 4, toc_max_heading_level: 3},
|
||||
'"toc_min_heading_level" must be less than or equal to ref:toc_max_heading_level',
|
||||
],
|
||||
[
|
||||
{toc_min_heading_level: 6, toc_max_heading_level: 2},
|
||||
'"toc_min_heading_level" must be less than or equal to ref:toc_max_heading_level',
|
||||
],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter draft', () => {
|
||||
testField({
|
||||
prefix: 'draft',
|
||||
validFrontMatters: [{draft: true}, {draft: false}],
|
||||
convertibleFrontMatter: [
|
||||
[{draft: 'true'}, {draft: true}],
|
||||
[{draft: 'false'}, {draft: false}],
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[{draft: 'yes'}, 'must be a boolean'],
|
||||
[{draft: 'no'}, 'must be a boolean'],
|
||||
[{draft: ''}, 'must be a boolean'],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter unlisted', () => {
|
||||
testField({
|
||||
prefix: 'unlisted',
|
||||
validFrontMatters: [{unlisted: true}, {unlisted: false}],
|
||||
convertibleFrontMatter: [
|
||||
[{unlisted: 'true'}, {unlisted: true}],
|
||||
[{unlisted: 'false'}, {unlisted: false}],
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[{unlisted: 'yes'}, 'must be a boolean'],
|
||||
[{unlisted: 'no'}, 'must be a boolean'],
|
||||
[{unlisted: ''}, 'must be a boolean'],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter draft XOR unlisted', () => {
|
||||
testField({
|
||||
prefix: 'draft XOR unlisted',
|
||||
validFrontMatters: [
|
||||
{draft: false},
|
||||
{unlisted: false},
|
||||
{draft: false, unlisted: false},
|
||||
{draft: true, unlisted: false},
|
||||
{draft: false, unlisted: true},
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[
|
||||
{draft: true, unlisted: true},
|
||||
"Can't be draft and unlisted at the same time.",
|
||||
],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShowcaseFrontMatter last_update', () => {
|
||||
testField({
|
||||
prefix: 'last_update',
|
||||
validFrontMatters: [
|
||||
{last_update: undefined},
|
||||
{last_update: {author: 'test author', date: undefined}},
|
||||
{last_update: {author: undefined, date: '1/1/2000'}},
|
||||
{last_update: {author: undefined, date: new Date('1/1/2000')}},
|
||||
{last_update: {author: 'test author', date: '1/1/2000'}},
|
||||
{last_update: {author: 'test author', date: '1995-12-17T03:24:00'}},
|
||||
{last_update: {author: undefined, date: 'December 17, 1995 03:24:00'}},
|
||||
],
|
||||
invalidFrontMatters: [
|
||||
[
|
||||
{last_update: null},
|
||||
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
|
||||
],
|
||||
[
|
||||
{last_update: {}},
|
||||
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
|
||||
],
|
||||
[
|
||||
{last_update: ''},
|
||||
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
|
||||
],
|
||||
[
|
||||
{last_update: {invalid: 'key'}},
|
||||
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
|
||||
],
|
||||
[
|
||||
{last_update: {author: 'test author', date: 'I am not a date :('}},
|
||||
'must be a valid date',
|
||||
],
|
||||
[
|
||||
{last_update: {author: 'test author', date: '2011-10-45'}},
|
||||
'must be a valid date',
|
||||
],
|
||||
[
|
||||
{last_update: {author: 'test author', date: '2011-0-10'}},
|
||||
'must be a valid date',
|
||||
],
|
||||
[
|
||||
{last_update: {author: 'test author', date: ''}},
|
||||
'must be a valid date',
|
||||
],
|
||||
],
|
||||
});
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* 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 path from 'path';
|
||||
import {loadContext} from '@docusaurus/core/src/server/site';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
|
||||
import pluginContentPages from '../index';
|
||||
import {validateOptions} from '../options';
|
||||
|
||||
describe('docusaurus-plugin-content-showcase', () => {
|
||||
it('loads simple showcase', async () => {
|
||||
const siteDir = path.join(__dirname, '__fixtures__', 'website');
|
||||
const context = await loadContext({siteDir});
|
||||
const plugin = pluginContentPages(
|
||||
context,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
path: 'src/showcase',
|
||||
},
|
||||
}),
|
||||
);
|
||||
const showcaseMetadata = await plugin.loadContent!();
|
||||
|
||||
expect(showcaseMetadata).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* 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 {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
import {validateOptions, DEFAULT_OPTIONS} from '../options';
|
||||
import type {Options} from '@docusaurus/plugin-content-pages';
|
||||
|
||||
function testValidate(options: Options) {
|
||||
return validateOptions({validate: normalizePluginOptions, options});
|
||||
}
|
||||
const defaultOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
id: 'default',
|
||||
};
|
||||
|
||||
describe('normalizeShowcasePluginOptions', () => {
|
||||
it('returns default options for undefined user options', () => {
|
||||
expect(testValidate({})).toEqual(defaultOptions);
|
||||
});
|
||||
|
||||
it('fills in default options for partially defined user options', () => {
|
||||
expect(testValidate({path: 'src/foo'})).toEqual({
|
||||
...defaultOptions,
|
||||
path: 'src/foo',
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts correctly defined user options', () => {
|
||||
const userOptions = {
|
||||
path: 'src/showcase',
|
||||
routeBasePath: '/showcase',
|
||||
include: ['**/*.{yaml,yml}'],
|
||||
exclude: ['**/$*/'],
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects bad path inputs', () => {
|
||||
expect(() => {
|
||||
testValidate({
|
||||
// @ts-expect-error: bad attribute
|
||||
path: 42,
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(`""path" must be a string"`);
|
||||
});
|
||||
|
||||
it('empty routeBasePath replace default path("/")', () => {
|
||||
expect(
|
||||
testValidate({
|
||||
routeBasePath: '',
|
||||
}),
|
||||
).toEqual({
|
||||
...defaultOptions,
|
||||
routeBasePath: '/',
|
||||
});
|
||||
});
|
||||
});
|
123
packages/docusaurus-plugin-content-showcase/src/index.ts
Normal file
123
packages/docusaurus-plugin-content-showcase/src/index.ts
Normal file
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* 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 {
|
||||
getFolderContainingFile,
|
||||
getPluginI18nPath,
|
||||
Globby,
|
||||
} from '@docusaurus/utils';
|
||||
import Yaml from 'js-yaml';
|
||||
|
||||
import {validateShowcaseFrontMatter} from './yaml';
|
||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||
import type {PluginOptions, Content} from '@docusaurus/plugin-content-showcase';
|
||||
import type {ShowcaseContentPaths} from './types';
|
||||
|
||||
export function getContentPathList(
|
||||
contentPaths: ShowcaseContentPaths,
|
||||
): string[] {
|
||||
return [contentPaths.contentPathLocalized, contentPaths.contentPath];
|
||||
}
|
||||
|
||||
export default function pluginContentShowcase(
|
||||
context: LoadContext,
|
||||
options: PluginOptions,
|
||||
): Plugin<Content | null> {
|
||||
const {siteDir, localizationDir} = context;
|
||||
|
||||
const contentPaths: ShowcaseContentPaths = {
|
||||
contentPath: path.resolve(siteDir, options.path),
|
||||
contentPathLocalized: getPluginI18nPath({
|
||||
localizationDir,
|
||||
pluginName: 'docusaurus-plugin-content-pages',
|
||||
pluginId: options.id,
|
||||
}),
|
||||
};
|
||||
|
||||
return {
|
||||
name: 'docusaurus-plugin-content-showcase',
|
||||
|
||||
// todo doesn't work
|
||||
// getPathsToWatch() {
|
||||
// const {include} = options;
|
||||
// return getContentPathList(contentPaths).flatMap((contentPath) =>
|
||||
// include.map((pattern) => `${contentPath}/${pattern}`),
|
||||
// );
|
||||
// },
|
||||
|
||||
async loadContent(): Promise<Content | null> {
|
||||
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 rawYaml = await fs.readFile(sourcePath, 'utf-8');
|
||||
const unsafeYaml = Yaml.load(rawYaml) as {[key: string]: unknown};
|
||||
return validateShowcaseFrontMatter(unsafeYaml);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
const showcaseAllData = await createData(
|
||||
'showcaseAll.json',
|
||||
JSON.stringify(content.website),
|
||||
);
|
||||
|
||||
addRoute({
|
||||
path: '/showcaseAll',
|
||||
component: '@theme/Showcase',
|
||||
modules: {
|
||||
content: showcaseAllData,
|
||||
// img: '@site/src/showcase/website/ozaki/aot.jpg',
|
||||
},
|
||||
exact: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export {validateOptions} from './options';
|
|
@ -5,24 +5,16 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
Joi,
|
||||
validateFrontMatter,
|
||||
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 {
|
||||
PluginOptions,
|
||||
Options,
|
||||
ShowcaseFrontMatter,
|
||||
} from '@docusaurus/plugin-showcase';
|
||||
import type {PluginOptions, Options} from '@docusaurus/plugin-content-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.
|
||||
include: ['**/*.{yml,yaml}'], // Extensions to include.
|
||||
exclude: GlobExcludeDefault,
|
||||
};
|
||||
|
||||
|
@ -34,15 +26,6 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
|
|||
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(),
|
||||
});
|
||||
|
||||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
|
@ -50,9 +33,3 @@ export function validateOptions({
|
|||
const validatedOptions = validate(PluginOptionSchema, options);
|
||||
return validatedOptions;
|
||||
}
|
||||
|
||||
export function validateShowcaseFrontMatter(frontMatter: {
|
||||
[key: string]: unknown;
|
||||
}): ShowcaseFrontMatter {
|
||||
return validateFrontMatter(frontMatter, contentAuthorsSchema);
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
declare module '@docusaurus/plugin-showcase' {
|
||||
declare module '@docusaurus/plugin-content-showcase' {
|
||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||
|
||||
export type Assets = {
|
||||
|
@ -43,7 +43,6 @@ declare module '@docusaurus/plugin-showcase' {
|
|||
|
||||
export type Content = {
|
||||
website: {
|
||||
type: string;
|
||||
title: string;
|
||||
description: string;
|
||||
preview: string | null; // null = use our serverless screenshot service
|
||||
|
@ -59,7 +58,7 @@ declare module '@docusaurus/plugin-showcase' {
|
|||
export default function pluginShowcase(
|
||||
context: LoadContext,
|
||||
options: PluginOptions,
|
||||
): Promise<Plugin<Content | null>>;
|
||||
): Promise<Plugin<Content>>;
|
||||
|
||||
export type ShowcaseMetadata = {
|
||||
/** Path to the Markdown source, with `@site` alias. */
|
24
packages/docusaurus-plugin-content-showcase/src/yaml.ts
Normal file
24
packages/docusaurus-plugin-content-showcase/src/yaml.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* 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} from '@docusaurus/utils-validation';
|
||||
import type {ShowcaseFrontMatter} from '@docusaurus/plugin-content-showcase';
|
||||
|
||||
const showcaseFrontMatterSchema = 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(),
|
||||
});
|
||||
|
||||
export function validateShowcaseFrontMatter(frontMatter: {
|
||||
[key: string]: unknown;
|
||||
}): ShowcaseFrontMatter {
|
||||
return validateFrontMatter(frontMatter, showcaseFrontMatterSchema);
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
# `@docusaurus/plugin-showcase`
|
||||
|
||||
Showcase plugin for Docusaurus.
|
||||
|
||||
## Usage
|
||||
|
||||
See [plugin-showcase documentation](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-showcase).
|
|
@ -1,257 +0,0 @@
|
|||
/**
|
||||
* 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';
|
|
@ -26,7 +26,7 @@
|
|||
"@docusaurus/plugin-content-blog": "3.0.0",
|
||||
"@docusaurus/plugin-content-docs": "3.0.0",
|
||||
"@docusaurus/plugin-content-pages": "3.0.0",
|
||||
"@docusaurus/plugin-showcase": "3.0.0",
|
||||
"@docusaurus/plugin-content-showcase": "3.0.0",
|
||||
"@docusaurus/theme-common": "3.0.0",
|
||||
"@docusaurus/theme-translations": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
|
|
|
@ -248,7 +248,7 @@ declare module '@theme/BlogPostItems' {
|
|||
}
|
||||
|
||||
declare module '@theme/ShowcaseDetails' {
|
||||
import type {Content} from '@docusaurus/plugin-showcase';
|
||||
import type {Content} from '@docusaurus/plugin-content-showcase';
|
||||
|
||||
export type User = Content['website'][number];
|
||||
|
||||
|
@ -260,7 +260,7 @@ declare module '@theme/ShowcaseDetails' {
|
|||
}
|
||||
|
||||
declare module '@theme/Showcase' {
|
||||
import type {Content} from '@docusaurus/plugin-showcase';
|
||||
import type {Content} from '@docusaurus/plugin-content-showcase';
|
||||
|
||||
export type User = Content['website'][number];
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import ShowcaseTooltip from '@theme/Showcase/ShowcaseTooltip';
|
|||
import ShowcaseTagSelect from '@theme/Showcase/ShowcaseTagSelect';
|
||||
import ShowcaseFilterToggle from '@theme/Showcase/ShowcaseFilterToggle';
|
||||
import type {Operator} from '@theme/Showcase/ShowcaseFilterToggle';
|
||||
import type {TagType} from '@docusaurus/plugin-showcase';
|
||||
import type {TagType} from '@docusaurus/plugin-content-showcase';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
type Users = User[];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue