feat(v2): generalize usage of _ prefix convention to exclude content files/folders (#5173)

* create a swizzleWarning partial for shared text

* Generalize usage of _ prefix convention to exclude content files/folders

* add api doc

* MDX loader should not expect metadata/frontmatter on MDX partial files
This commit is contained in:
Sébastien Lorber 2021-07-15 13:21:41 +02:00 committed by GitHub
parent 0851e0e5bf
commit 8bdb3da233
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 249 additions and 80 deletions

View file

@ -24,6 +24,19 @@ const DEFAULT_OPTIONS = {
remarkPlugins: [unwrapMdxCodeBlocks, emoji, headings, toc], remarkPlugins: [unwrapMdxCodeBlocks, emoji, headings, toc],
}; };
// When this throws, it generally means that there's no metadata file associated with this MDX document
// It can happen when using MDX partials (usually starting with _)
// That's why it's important to provide the "isMDXPartial" function in config
async function readMetadataPath(metadataPath) {
try {
return await readFile(metadataPath, 'utf8');
} catch (e) {
throw new Error(
`MDX loader can't read MDX metadata file for path ${metadataPath}. Maybe the isMDXPartial option function was not provided?`,
);
}
}
module.exports = async function docusaurusMdxLoader(fileString) { module.exports = async function docusaurusMdxLoader(fileString) {
const callback = this.async(); const callback = this.async();
@ -37,19 +50,15 @@ module.exports = async function docusaurusMdxLoader(fileString) {
const hasFrontMatter = Object.keys(frontMatter).length > 0; const hasFrontMatter = Object.keys(frontMatter).length > 0;
const filePath = this.resourcePath;
const options = { const options = {
...reqOptions, ...reqOptions,
remarkPlugins: [ remarkPlugins: [
...(reqOptions.beforeDefaultRemarkPlugins || []), ...(reqOptions.beforeDefaultRemarkPlugins || []),
...DEFAULT_OPTIONS.remarkPlugins, ...DEFAULT_OPTIONS.remarkPlugins,
[ [transformImage, {staticDir: reqOptions.staticDir, filePath}],
transformImage, [transformLinks, {staticDir: reqOptions.staticDir, filePath}],
{staticDir: reqOptions.staticDir, filePath: this.resourcePath},
],
[
transformLinks,
{staticDir: reqOptions.staticDir, filePath: this.resourcePath},
],
...(reqOptions.remarkPlugins || []), ...(reqOptions.remarkPlugins || []),
], ],
rehypePlugins: [ rehypePlugins: [
@ -58,7 +67,7 @@ module.exports = async function docusaurusMdxLoader(fileString) {
...(reqOptions.rehypePlugins || []), ...(reqOptions.rehypePlugins || []),
], ],
filepath: this.resourcePath, filepath: filePath,
}; };
let result; let result;
@ -74,27 +83,33 @@ module.exports = async function docusaurusMdxLoader(fileString) {
contentTitle, contentTitle,
)};`; )};`;
// MDX partials are MDX files starting with _ or in a folder starting with _
// Partial are not expected to have an associated metadata file or frontmatter
const isMDXPartial = options.isMDXPartial
? options.isMDXPartial(filePath)
: false;
if (isMDXPartial && hasFrontMatter) {
return callback(
new Error(`MDX partial should not contain FrontMatter: ${filePath}`),
);
}
if (!isMDXPartial) {
// Read metadata for this MDX and export it. // Read metadata for this MDX and export it.
if (options.metadataPath && typeof options.metadataPath === 'function') { if (options.metadataPath && typeof options.metadataPath === 'function') {
const metadataPath = options.metadataPath(this.resourcePath); const metadataPath = options.metadataPath(filePath);
if (metadataPath) { if (metadataPath) {
const metadata = await readMetadataPath(metadataPath);
exportStr += `\nexport const metadata = ${metadata};`;
// Add as dependency of this loader result so that we can // Add as dependency of this loader result so that we can
// recompile if metadata is changed. // recompile if metadata is changed.
this.addDependency(metadataPath); this.addDependency(metadataPath);
const metadata = await readFile(metadataPath, 'utf8'); }
exportStr += `\nexport const metadata = ${metadata};`;
} }
} }
if (
options.forbidFrontMatter &&
typeof options.forbidFrontMatter === 'function'
) {
if (options.forbidFrontMatter(this.resourcePath) && hasFrontMatter) {
return callback(new Error(`Front matter is forbidden in this file`));
}
}
const code = ` const code = `
import React from 'react'; import React from 'react';
import { mdx } from '@mdx-js/react'; import { mdx } from '@mdx-js/react';

View file

@ -0,0 +1,3 @@
This is a partial in file/folder starting with _:
It should be excluded by default

View file

@ -0,0 +1,3 @@
This is a partial in file/folder starting with _:
It should be excluded by default

View file

@ -0,0 +1,3 @@
This is a partial in file/folder starting with \_:
It should be excluded by default

View file

@ -7,12 +7,26 @@ exports[`blogFeed atom shows feed item for each post 1`] = `
<feed xmlns=\\"http://www.w3.org/2005/Atom\\"> <feed xmlns=\\"http://www.w3.org/2005/Atom\\">
<id>https://docusaurus.io/myBaseUrl/blog</id> <id>https://docusaurus.io/myBaseUrl/blog</id>
<title>Hello Blog</title> <title>Hello Blog</title>
<updated>2020-02-27T00:00:00.000Z</updated> <updated>2020-08-16T00:00:00.000Z</updated>
<generator>https://github.com/jpmonette/feed</generator> <generator>https://github.com/jpmonette/feed</generator>
<link rel=\\"alternate\\" href=\\"https://docusaurus.io/myBaseUrl/blog\\"/> <link rel=\\"alternate\\" href=\\"https://docusaurus.io/myBaseUrl/blog\\"/>
<subtitle>Hello Blog</subtitle> <subtitle>Hello Blog</subtitle>
<icon>https://docusaurus.io/myBaseUrl/image/favicon.ico</icon> <icon>https://docusaurus.io/myBaseUrl/image/favicon.ico</icon>
<rights>Copyright</rights> <rights>Copyright</rights>
<entry>
<title type=\\"html\\"><![CDATA[Complex Slug]]></title>
<id>/hey/my super path/héllô</id>
<link href=\\"https://docusaurus.io/myBaseUrl/blog/hey/my super path/héllô\\"/>
<updated>2020-08-16T00:00:00.000Z</updated>
<summary type=\\"html\\"><![CDATA[complex url slug]]></summary>
</entry>
<entry>
<title type=\\"html\\"><![CDATA[Simple Slug]]></title>
<id>/simple/slug</id>
<link href=\\"https://docusaurus.io/myBaseUrl/blog/simple/slug\\"/>
<updated>2020-08-15T00:00:00.000Z</updated>
<summary type=\\"html\\"><![CDATA[simple url slug]]></summary>
</entry>
<entry> <entry>
<title type=\\"html\\"><![CDATA[draft]]></title> <title type=\\"html\\"><![CDATA[draft]]></title>
<id>draft</id> <id>draft</id>
@ -20,6 +34,12 @@ exports[`blogFeed atom shows feed item for each post 1`] = `
<updated>2020-02-27T00:00:00.000Z</updated> <updated>2020-02-27T00:00:00.000Z</updated>
<summary type=\\"html\\"><![CDATA[this post should not be published yet]]></summary> <summary type=\\"html\\"><![CDATA[this post should not be published yet]]></summary>
</entry> </entry>
<entry>
<title type=\\"html\\"><![CDATA[some heading]]></title>
<id>some heading</id>
<link href=\\"https://docusaurus.io/myBaseUrl/blog/heading-as-title\\"/>
<updated>2019-01-02T00:00:00.000Z</updated>
</entry>
<entry> <entry>
<title type=\\"html\\"><![CDATA[date-matter]]></title> <title type=\\"html\\"><![CDATA[date-matter]]></title>
<id>date-matter</id> <id>date-matter</id>
@ -46,10 +66,24 @@ exports[`blogFeed rss shows feed item for each post 1`] = `
<title>Hello Blog</title> <title>Hello Blog</title>
<link>https://docusaurus.io/myBaseUrl/blog</link> <link>https://docusaurus.io/myBaseUrl/blog</link>
<description>Hello Blog</description> <description>Hello Blog</description>
<lastBuildDate>Thu, 27 Feb 2020 00:00:00 GMT</lastBuildDate> <lastBuildDate>Sun, 16 Aug 2020 00:00:00 GMT</lastBuildDate>
<docs>https://validator.w3.org/feed/docs/rss2.html</docs> <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
<generator>https://github.com/jpmonette/feed</generator> <generator>https://github.com/jpmonette/feed</generator>
<copyright>Copyright</copyright> <copyright>Copyright</copyright>
<item>
<title><![CDATA[Complex Slug]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/hey/my super path/héllô</link>
<guid>/hey/my super path/héllô</guid>
<pubDate>Sun, 16 Aug 2020 00:00:00 GMT</pubDate>
<description><![CDATA[complex url slug]]></description>
</item>
<item>
<title><![CDATA[Simple Slug]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/simple/slug</link>
<guid>/simple/slug</guid>
<pubDate>Sat, 15 Aug 2020 00:00:00 GMT</pubDate>
<description><![CDATA[simple url slug]]></description>
</item>
<item> <item>
<title><![CDATA[draft]]></title> <title><![CDATA[draft]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/draft</link> <link>https://docusaurus.io/myBaseUrl/blog/draft</link>
@ -57,6 +91,12 @@ exports[`blogFeed rss shows feed item for each post 1`] = `
<pubDate>Thu, 27 Feb 2020 00:00:00 GMT</pubDate> <pubDate>Thu, 27 Feb 2020 00:00:00 GMT</pubDate>
<description><![CDATA[this post should not be published yet]]></description> <description><![CDATA[this post should not be published yet]]></description>
</item> </item>
<item>
<title><![CDATA[some heading]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/heading-as-title</link>
<guid>some heading</guid>
<pubDate>Wed, 02 Jan 2019 00:00:00 GMT</pubDate>
</item>
<item> <item>
<title><![CDATA[date-matter]]></title> <title><![CDATA[date-matter]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/date-matter</link> <link>https://docusaurus.io/myBaseUrl/blog/date-matter</link>

View file

@ -9,6 +9,7 @@ import path from 'path';
import {generateBlogFeed} from '../blogUtils'; import {generateBlogFeed} from '../blogUtils';
import {LoadContext, I18n} from '@docusaurus/types'; import {LoadContext, I18n} from '@docusaurus/types';
import {PluginOptions, BlogContentPaths} from '../types'; import {PluginOptions, BlogContentPaths} from '../types';
import {DEFAULT_OPTIONS} from '../pluginOptionSchema';
const DefaultI18N: I18n = { const DefaultI18N: I18n = {
currentLocale: 'en', currentLocale: 'en',
@ -84,7 +85,8 @@ describe('blogFeed', () => {
{ {
path: 'blog', path: 'blog',
routeBasePath: 'blog', routeBasePath: 'blog',
include: ['*r*.md', '*.mdx'], // skip no-date.md - it won't play nice with snapshots include: DEFAULT_OPTIONS.include,
exclude: DEFAULT_OPTIONS.exclude,
feedOptions: { feedOptions: {
type: [feedType], type: [feedType],
copyright: 'Copyright', copyright: 'Copyright',

View file

@ -6,7 +6,6 @@
*/ */
import fs from 'fs-extra'; import fs from 'fs-extra';
import globby from 'globby';
import chalk from 'chalk'; import chalk from 'chalk';
import path from 'path'; import path from 'path';
import readingTime from 'reading-time'; import readingTime from 'reading-time';
@ -27,6 +26,7 @@ import {
getFolderContainingFile, getFolderContainingFile,
posixPath, posixPath,
replaceMarkdownLinks, replaceMarkdownLinks,
Globby,
} from '@docusaurus/utils'; } from '@docusaurus/utils';
import {LoadContext} from '@docusaurus/types'; import {LoadContext} from '@docusaurus/types';
import {validateBlogPostFrontMatter} from './blogFrontMatter'; import {validateBlogPostFrontMatter} from './blogFrontMatter';
@ -127,6 +127,7 @@ export async function generateBlogPosts(
): Promise<BlogPost[]> { ): Promise<BlogPost[]> {
const { const {
include, include,
exclude,
routeBasePath, routeBasePath,
truncateMarker, truncateMarker,
showReadingTime, showReadingTime,
@ -138,8 +139,9 @@ export async function generateBlogPosts(
} }
const {baseUrl = ''} = siteConfig; const {baseUrl = ''} = siteConfig;
const blogSourceFiles = await globby(include, { const blogSourceFiles = await Globby(include, {
cwd: contentPaths.contentPath, cwd: contentPaths.contentPath,
ignore: exclude,
}); });
const blogPosts: BlogPost[] = []; const blogPosts: BlogPost[] = [];

View file

@ -16,6 +16,7 @@ import {
reportMessage, reportMessage,
posixPath, posixPath,
addTrailingPathSeparator, addTrailingPathSeparator,
createMatcher,
} from '@docusaurus/utils'; } from '@docusaurus/utils';
import { import {
STATIC_DIR_NAME, STATIC_DIR_NAME,
@ -459,9 +460,10 @@ export default function pluginContentBlog(
beforeDefaultRemarkPlugins, beforeDefaultRemarkPlugins,
beforeDefaultRehypePlugins, beforeDefaultRehypePlugins,
staticDir: path.join(siteDir, STATIC_DIR_NAME), staticDir: path.join(siteDir, STATIC_DIR_NAME),
isMDXPartial: createMatcher(options.exclude),
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.
metadataPath: (mdxPath: string) => {
const aliasedPath = aliasedSitePath(mdxPath, siteDir); const aliasedPath = aliasedSitePath(mdxPath, siteDir);
return path.join( return path.join(
dataDir, dataDir,

View file

@ -12,6 +12,7 @@ import {
AdmonitionsSchema, AdmonitionsSchema,
URISchema, URISchema,
} from '@docusaurus/utils-validation'; } from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
export const DEFAULT_OPTIONS = { export const DEFAULT_OPTIONS = {
feedOptions: {type: ['rss', 'atom']}, feedOptions: {type: ['rss', 'atom']},
@ -32,6 +33,7 @@ export const DEFAULT_OPTIONS = {
blogSidebarTitle: 'Recent posts', blogSidebarTitle: 'Recent posts',
postsPerPage: 10, postsPerPage: 10,
include: ['*.md', '*.mdx'], include: ['*.md', '*.mdx'],
exclude: GlobExcludeDefault,
routeBasePath: 'blog', routeBasePath: 'blog',
path: 'blog', path: 'blog',
editLocalizedFiles: false, editLocalizedFiles: false,
@ -44,6 +46,7 @@ export const PluginOptionSchema = Joi.object({
// .allow('') // .allow('')
.default(DEFAULT_OPTIONS.routeBasePath), .default(DEFAULT_OPTIONS.routeBasePath),
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include), include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude),
postsPerPage: Joi.number() postsPerPage: Joi.number()
.integer() .integer()
.min(1) .min(1)

View file

@ -39,6 +39,7 @@ export interface PluginOptions extends RemarkAndRehypePluginOptions {
path: string; path: string;
routeBasePath: string; routeBasePath: string;
include: string[]; include: string[];
exclude: string[];
postsPerPage: number; postsPerPage: number;
blogListComponent: string; blogListComponent: string;
blogPostComponent: string; blogPostComponent: string;

View file

@ -0,0 +1,3 @@
This is a partial in file/folder starting with _:
It should be excluded by default

View file

@ -0,0 +1,3 @@
This is a partial in file/folder starting with _:
It should be excluded by default

View file

@ -0,0 +1,3 @@
This is a partial in file/folder starting with \_:
It should be excluded by default

View file

@ -0,0 +1,3 @@
This is a partial in file/folder starting with _:
It should be excluded by default

View file

@ -0,0 +1,3 @@
This is a partial in file/folder starting with _:
It should be excluded by default

View file

@ -0,0 +1,3 @@
This is a partial in file/folder starting with \_:
It should be excluded by default

View file

@ -12,6 +12,7 @@ import {
DefaultNumberPrefixParser, DefaultNumberPrefixParser,
DisabledNumberPrefixParser, DisabledNumberPrefixParser,
} from '../numberPrefix'; } from '../numberPrefix';
import {GlobExcludeDefault} from '@docusaurus/utils';
// the type of remark/rehype plugins is function // the type of remark/rehype plugins is function
const markdownPluginsFunctionStub = () => {}; const markdownPluginsFunctionStub = () => {};
@ -30,6 +31,7 @@ describe('normalizeDocsPluginOptions', () => {
routeBasePath: 'my-docs', // URL Route. routeBasePath: 'my-docs', // URL Route.
homePageId: 'home', // Document id for docs home page. homePageId: 'home', // Document id for docs home page.
include: ['**/*.{md,mdx}'], // Extensions to include. include: ['**/*.{md,mdx}'], // Extensions to include.
exclude: GlobExcludeDefault,
sidebarPath: 'my-sidebar', // Path to sidebar configuration for showing a list of markdown pages. sidebarPath: 'my-sidebar', // Path to sidebar configuration for showing a list of markdown pages.
sidebarItemsGenerator: DefaultSidebarItemsGenerator, sidebarItemsGenerator: DefaultSidebarItemsGenerator,
numberPrefixParser: DefaultNumberPrefixParser, numberPrefixParser: DefaultNumberPrefixParser,

View file

@ -14,6 +14,7 @@ import {
normalizeUrl, normalizeUrl,
parseMarkdownString, parseMarkdownString,
posixPath, posixPath,
Globby,
} from '@docusaurus/utils'; } from '@docusaurus/utils';
import {LoadContext} from '@docusaurus/types'; import {LoadContext} from '@docusaurus/types';
@ -28,7 +29,6 @@ import {
} from './types'; } from './types';
import getSlug from './slug'; import getSlug from './slug';
import {CURRENT_VERSION_NAME} from './constants'; import {CURRENT_VERSION_NAME} from './constants';
import globby from 'globby';
import {getDocsDirPaths} from './versions'; import {getDocsDirPaths} from './versions';
import {stripPathNumberPrefixes} from './numberPrefix'; import {stripPathNumberPrefixes} from './numberPrefix';
import {validateDocFrontMatter} from './docFrontMatter'; import {validateDocFrontMatter} from './docFrontMatter';
@ -92,11 +92,12 @@ export async function readVersionDocs(
versionMetadata: VersionMetadata, versionMetadata: VersionMetadata,
options: Pick< options: Pick<
PluginOptions, PluginOptions,
'include' | 'showLastUpdateAuthor' | 'showLastUpdateTime' 'include' | 'exclude' | 'showLastUpdateAuthor' | 'showLastUpdateTime'
>, >,
): Promise<DocFile[]> { ): Promise<DocFile[]> {
const sources = await globby(options.include, { const sources = await Globby(options.include, {
cwd: versionMetadata.contentPath, cwd: versionMetadata.contentPath,
ignore: options.exclude,
}); });
return Promise.all( return Promise.all(
sources.map((source) => readDocFile(versionMetadata, source, options)), sources.map((source) => readDocFile(versionMetadata, source, options)),

View file

@ -18,6 +18,7 @@ import {
reportMessage, reportMessage,
posixPath, posixPath,
addTrailingPathSeparator, addTrailingPathSeparator,
createMatcher,
} from '@docusaurus/utils'; } from '@docusaurus/utils';
import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types'; import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types';
import {loadSidebars, createSidebarsUtils, processSidebars} from './sidebars'; import {loadSidebars, createSidebarsUtils, processSidebars} from './sidebars';
@ -408,6 +409,7 @@ export default function pluginContentDocs(
beforeDefaultRehypePlugins, beforeDefaultRehypePlugins,
beforeDefaultRemarkPlugins, beforeDefaultRemarkPlugins,
staticDir: path.join(siteDir, STATIC_DIR_NAME), staticDir: path.join(siteDir, STATIC_DIR_NAME),
isMDXPartial: createMatcher(options.exclude),
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.

View file

@ -12,6 +12,8 @@ import {
AdmonitionsSchema, AdmonitionsSchema,
URISchema, URISchema,
} from '@docusaurus/utils-validation'; } from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
import {OptionValidationContext, ValidationResult} from '@docusaurus/types'; import {OptionValidationContext, ValidationResult} from '@docusaurus/types';
import chalk from 'chalk'; import chalk from 'chalk';
import admonitions from 'remark-admonitions'; import admonitions from 'remark-admonitions';
@ -26,6 +28,7 @@ export const DEFAULT_OPTIONS: Omit<PluginOptions, 'id' | 'sidebarPath'> = {
routeBasePath: 'docs', // URL Route. routeBasePath: 'docs', // URL Route.
homePageId: undefined, // TODO remove soon, deprecated homePageId: undefined, // TODO remove soon, deprecated
include: ['**/*.{md,mdx}'], // Extensions to include. include: ['**/*.{md,mdx}'], // Extensions to include.
exclude: GlobExcludeDefault,
sidebarItemsGenerator: DefaultSidebarItemsGenerator, sidebarItemsGenerator: DefaultSidebarItemsGenerator,
numberPrefixParser: DefaultNumberPrefixParser, numberPrefixParser: DefaultNumberPrefixParser,
docLayoutComponent: '@theme/DocPage', docLayoutComponent: '@theme/DocPage',
@ -66,6 +69,7 @@ export const OptionsSchema = Joi.object({
.default(DEFAULT_OPTIONS.routeBasePath), .default(DEFAULT_OPTIONS.routeBasePath),
homePageId: Joi.string().optional(), homePageId: Joi.string().optional(),
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include), include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude),
sidebarPath: Joi.alternatives().try( sidebarPath: Joi.alternatives().try(
Joi.boolean().invalid(true), Joi.boolean().invalid(true),
Joi.string(), Joi.string(),

View file

@ -81,6 +81,7 @@ export type PluginOptions = MetadataOptions &
RemarkAndRehypePluginOptions & { RemarkAndRehypePluginOptions & {
id: string; id: string;
include: string[]; include: string[];
exclude: string[];
docLayoutComponent: string; docLayoutComponent: string;
docItemComponent: string; docItemComponent: string;
admonitions: Record<string, unknown>; admonitions: Record<string, unknown>;

View file

@ -25,9 +25,7 @@
"@docusaurus/utils-validation": "2.0.0-beta.3", "@docusaurus/utils-validation": "2.0.0-beta.3",
"globby": "^11.0.2", "globby": "^11.0.2",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"minimatch": "^3.0.4",
"remark-admonitions": "^1.2.1", "remark-admonitions": "^1.2.1",
"slash": "^3.0.0",
"tslib": "^2.1.0", "tslib": "^2.1.0",
"webpack": "^5.40.0" "webpack": "^5.40.0"
}, },

View file

@ -5,11 +5,8 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import globby from 'globby';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import minimatch from 'minimatch';
import slash from 'slash';
import { import {
encodePath, encodePath,
fileToPath, fileToPath,
@ -18,6 +15,8 @@ import {
getPluginI18nPath, getPluginI18nPath,
getFolderContainingFile, getFolderContainingFile,
addTrailingPathSeparator, addTrailingPathSeparator,
Globby,
createMatcher,
} from '@docusaurus/utils'; } from '@docusaurus/utils';
import { import {
LoadContext, LoadContext,
@ -81,11 +80,6 @@ export default function pluginContentPages(
); );
const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID); const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID);
const excludeRegex = new RegExp(
options.exclude
.map((pattern) => minimatch.makeRe(pattern).source)
.join('|'),
);
return { return {
name: 'docusaurus-plugin-content-pages', name: 'docusaurus-plugin-content-pages',
@ -116,7 +110,7 @@ export default function pluginContentPages(
} }
const {baseUrl} = siteConfig; const {baseUrl} = siteConfig;
const pagesFiles = await globby(include, { const pagesFiles = await Globby(include, {
cwd: contentPaths.contentPath, cwd: contentPaths.contentPath,
ignore: options.exclude, ignore: options.exclude,
}); });
@ -223,20 +217,16 @@ export default function pluginContentPages(
beforeDefaultRehypePlugins, beforeDefaultRehypePlugins,
beforeDefaultRemarkPlugins, beforeDefaultRemarkPlugins,
staticDir: path.join(siteDir, STATIC_DIR_NAME), staticDir: path.join(siteDir, STATIC_DIR_NAME),
isMDXPartial: createMatcher(options.exclude),
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.
metadataPath: (mdxPath: string) => {
if (excludeRegex.test(slash(mdxPath))) {
return null;
}
const aliasedSource = aliasedSitePath(mdxPath, siteDir); const aliasedSource = aliasedSitePath(mdxPath, siteDir);
return path.join( return path.join(
dataDir, dataDir,
`${docuHash(aliasedSource)}.json`, `${docuHash(aliasedSource)}.json`,
); );
}, },
forbidFrontMatter: (mdxPath: string) =>
excludeRegex.test(slash(mdxPath)),
}, },
}, },
{ {

View file

@ -11,22 +11,19 @@ import {
RehypePluginsSchema, RehypePluginsSchema,
AdmonitionsSchema, AdmonitionsSchema,
} from '@docusaurus/utils-validation'; } from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
export const DEFAULT_OPTIONS: PluginOptions = { export const DEFAULT_OPTIONS: PluginOptions = {
path: 'src/pages', // Path to data on filesystem, relative to site dir. path: 'src/pages', // Path to data on filesystem, relative to site dir.
routeBasePath: '/', // URL Route. routeBasePath: '/', // URL Route.
include: ['**/*.{js,jsx,ts,tsx,md,mdx}'], // Extensions to include. include: ['**/*.{js,jsx,ts,tsx,md,mdx}'], // Extensions to include.
exclude: GlobExcludeDefault,
mdxPageComponent: '@theme/MDXPage', mdxPageComponent: '@theme/MDXPage',
remarkPlugins: [], remarkPlugins: [],
rehypePlugins: [], rehypePlugins: [],
beforeDefaultRehypePlugins: [], beforeDefaultRehypePlugins: [],
beforeDefaultRemarkPlugins: [], beforeDefaultRemarkPlugins: [],
admonitions: {}, admonitions: {},
exclude: [
'**/_*.{js,jsx,ts,tsx,md,mdx}',
'**/*.test.{js,ts}',
'**/__tests__/**',
],
}; };
export const PluginOptionSchema = Joi.object({ export const PluginOptionSchema = Joi.object({

View file

@ -25,6 +25,8 @@
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"globby": "^11.0.4",
"micromatch": "^4.0.4",
"resolve-pathname": "^3.0.0", "resolve-pathname": "^3.0.0",
"tslib": "^2.2.0" "tslib": "^2.2.0"
}, },
@ -33,6 +35,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/dedent": "^0.7.0", "@types/dedent": "^0.7.0",
"@types/micromatch": "^4.0.2",
"dedent": "^0.7.0" "dedent": "^0.7.0"
} }
} }

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.
*/
// Globby/Micromatch are the 2 libs we use in Docusaurus consistently
export {default as Globby} from 'globby';
import Micromatch from 'micromatch'; // Note: Micromatch is used by Globby
// The default patterns we ignore when globbing
// using _ prefix for exclusion by convention
export const GlobExcludeDefault = [
// Ignore files starting with _
'**/_*.{js,jsx,ts,tsx,md,mdx}',
// Ignore folders starting with _ (including folder content)
'**/_*/**',
// Ignore tests
'**/*.test.{js,jsx,ts,tsx}',
'**/__tests__/**',
];
type Matcher = (str: string) => boolean;
export function createMatcher(patterns: string[]): Matcher {
const regexp = new RegExp(
patterns.map((pattern) => Micromatch.makeRe(pattern).source).join('|'),
);
return (str) => regexp.test(str);
}

View file

@ -31,6 +31,7 @@ export * from './markdownParser';
export * from './markdownLinks'; export * from './markdownLinks';
export * from './escapePath'; export * from './escapePath';
export {md5Hash, simpleHash, docuHash} from './hashUtils'; export {md5Hash, simpleHash, docuHash} from './hashUtils';
export {Globby, GlobExcludeDefault, createMatcher} from './globUtils';
const fileHash = new Map(); const fileHash = new Map();
export async function generate( export async function generate(

View file

@ -5,10 +5,9 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import globby from 'globby';
import fs from 'fs-extra'; import fs from 'fs-extra';
import path from 'path'; import path from 'path';
import {fileToPath, posixPath, normalizeUrl} from '@docusaurus/utils'; import {fileToPath, posixPath, normalizeUrl, Globby} from '@docusaurus/utils';
import {ThemeAliases} from '@docusaurus/types'; import {ThemeAliases} from '@docusaurus/types';
import {sortBy} from 'lodash'; import {sortBy} from 'lodash';
@ -21,7 +20,7 @@ export default function themeAlias(
return {}; return {};
} }
const themeComponentFiles = globby.sync(['**/*.{js,jsx,ts,tsx}'], { const themeComponentFiles = Globby.sync(['**/*.{js,jsx,ts,tsx}'], {
cwd: themePath, cwd: themePath,
}); });

View file

@ -6,9 +6,8 @@
*/ */
import {flatMap} from 'lodash'; import {flatMap} from 'lodash';
import {RouteConfig} from '@docusaurus/types'; import {RouteConfig} from '@docusaurus/types';
import globby from 'globby';
import nodePath from 'path'; import nodePath from 'path';
import {posixPath} from '@docusaurus/utils'; import {posixPath, Globby} from '@docusaurus/utils';
// Recursively get the final routes (routes with no subroutes) // Recursively get the final routes (routes with no subroutes)
export function getAllFinalRoutes(routeConfig: RouteConfig[]): RouteConfig[] { export function getAllFinalRoutes(routeConfig: RouteConfig[]): RouteConfig[] {
@ -22,7 +21,7 @@ export function getAllFinalRoutes(routeConfig: RouteConfig[]): RouteConfig[] {
// See https://github.com/facebook/docusaurus/pull/4222#issuecomment-795517329 // See https://github.com/facebook/docusaurus/pull/4222#issuecomment-795517329
export async function safeGlobby( export async function safeGlobby(
patterns: string[], patterns: string[],
options?: globby.GlobbyOptions, options?: Globby.GlobbyOptions,
): Promise<string[]> { ): Promise<string[]> {
// Required for Windows support, as paths using \ should not be used by globby // Required for Windows support, as paths using \ should not be used by globby
// (also using the windows hard drive prefix like c: is not a good idea) // (also using the windows hard drive prefix like c: is not a good idea)
@ -30,5 +29,5 @@ export async function safeGlobby(
posixPath(nodePath.relative(process.cwd(), dirPath)), posixPath(nodePath.relative(process.cwd(), dirPath)),
); );
return globby(globPaths, options); return Globby(globPaths, options);
} }

View file

@ -0,0 +1,5 @@
:::caution
We discourage swizzling of components during the Docusaurus 2 beta phase. The theme components APIs are likely to evolve and have breaking changes. If possible, stick with the default appearance for now.
:::

View file

@ -72,6 +72,15 @@ module.exports = {
*/ */
routeBasePath: 'blog', routeBasePath: 'blog',
include: ['*.md', '*.mdx'], include: ['*.md', '*.mdx'],
/**
* No route will be created for matching files
*/
exclude: [
'**/_*.{js,jsx,ts,tsx,md,mdx}',
'**/_*/**',
'**/*.test.{js,jsx,ts,tsx}',
'**/__tests__/**',
],
postsPerPage: 10, postsPerPage: 10,
/** /**
* Theme components used by the blog pages. * Theme components used by the blog pages.

View file

@ -69,6 +69,15 @@ module.exports = {
*/ */
routeBasePath: 'docs', routeBasePath: 'docs',
include: ['**/*.md', '**/*.mdx'], // Extensions to include. include: ['**/*.md', '**/*.mdx'], // Extensions to include.
/**
* No route will be created for matching files
*/
exclude: [
'**/_*.{js,jsx,ts,tsx,md,mdx}',
'**/_*/**',
'**/*.test.{js,jsx,ts,tsx}',
'**/__tests__/**',
],
/** /**
* Path to sidebar configuration for showing a list of markdown pages. * Path to sidebar configuration for showing a list of markdown pages.
*/ */

View file

@ -39,11 +39,12 @@ module.exports = {
routeBasePath: '', routeBasePath: '',
include: ['**/*.{js,jsx,ts,tsx,md,mdx}'], include: ['**/*.{js,jsx,ts,tsx,md,mdx}'],
/** /**
* No Route will be created for matching files * No route will be created for matching files
*/ */
exclude: [ exclude: [
'**/_*.{js,jsx,ts,tsx,md,mdx}', '**/_*.{js,jsx,ts,tsx,md,mdx}',
'**/*.test.{js,ts}', '**/_*/**',
'**/*.test.{js,jsx,ts,tsx}',
'**/__tests__/**', '**/__tests__/**',
], ],
/** /**

View file

@ -93,11 +93,11 @@ For advanced minification of CSS bundle, we use the [advanced cssnano preset](ht
### `docusaurus swizzle [siteDir]` {#docusaurus-swizzle-sitedir} ### `docusaurus swizzle [siteDir]` {#docusaurus-swizzle-sitedir}
:::caution ```mdx-code-block
import SwizzleWarning from "./_partials/swizzleWarning.mdx"
We discourage swizzling of components during the Docusaurus 2 beta phase. The theme components APIs are likely to evolve and have breaking changes. If possible, stick with the default appearance for now. <SwizzleWarning/>
```
:::
Change any Docusaurus theme components to your liking with `npm run swizzle`. Change any Docusaurus theme components to your liking with `npm run swizzle`.

View file

@ -98,11 +98,11 @@ Use this component to render React Context providers and global stateful logic.
## Swizzling theme components {#swizzling-theme-components} ## Swizzling theme components {#swizzling-theme-components}
:::caution ```mdx-code-block
import SwizzleWarning from "./_partials/swizzleWarning.mdx"
We discourage swizzling of components during the Docusaurus 2 beta phase. The theme components APIs are likely to evolve and have breaking changes. If possible, stick with the default appearance for now. <SwizzleWarning/>
```
:::
Docusaurus Themes' components are designed to be replaceable. To make it easier for you, we created a command for you to replace theme components called `swizzle`. Docusaurus Themes' components are designed to be replaceable. To make it easier for you, we created a command for you to replace theme components called `swizzle`.

View file

@ -17,3 +17,9 @@ Did you know you can use multiple instances of the same plugin?
Using twice the blog plugin permits you to create more than one blog on the same Docusaurus website! Using twice the blog plugin permits you to create more than one blog on the same Docusaurus website!
::: :::
```mdx-code-block
import Partial from "./_partial.mdx"
<Partial />
```

View file

@ -0,0 +1 @@
This is a blog partial

View file

@ -3545,6 +3545,11 @@
"@types/connect" "*" "@types/connect" "*"
"@types/node" "*" "@types/node" "*"
"@types/braces@*":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@types/braces/-/braces-3.0.1.tgz#5a284d193cfc61abb2e5a50d36ebbc50d942a32b"
integrity sha512-+euflG6ygo4bn0JHtn4pYqcXwRtLvElQ7/nnjDu7iYG56H0+OhCd7d6Ug0IE3WcFpZozBKW2+80FUbv5QGk5AQ==
"@types/clean-css@*": "@types/clean-css@*":
version "4.2.4" version "4.2.4"
resolved "https://registry.yarnpkg.com/@types/clean-css/-/clean-css-4.2.4.tgz#4fe4705c384e6ec9ee8454bc3d49089f38dc038a" resolved "https://registry.yarnpkg.com/@types/clean-css/-/clean-css-4.2.4.tgz#4fe4705c384e6ec9ee8454bc3d49089f38dc038a"
@ -3829,6 +3834,13 @@
dependencies: dependencies:
"@types/unist" "*" "@types/unist" "*"
"@types/micromatch@^4.0.2":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/micromatch/-/micromatch-4.0.2.tgz#ce29c8b166a73bf980a5727b1e4a4d099965151d"
integrity sha512-oqXqVb0ci19GtH0vOA/U2TmHTcRY9kuZl4mqUxe0QmJAlIW13kzhuK5pi1i9+ngav8FjpSb9FVS/GE00GLX1VA==
dependencies:
"@types/braces" "*"
"@types/mime@^1": "@types/mime@^1":
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
@ -9587,10 +9599,10 @@ globby@^10.0.1, globby@^10.0.2:
merge2 "^1.2.3" merge2 "^1.2.3"
slash "^3.0.0" slash "^3.0.0"
globby@^11.0.1, globby@^11.0.2, globby@^11.0.3: globby@^11.0.1, globby@^11.0.2, globby@^11.0.3, globby@^11.0.4:
version "11.0.3" version "11.0.4"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5"
integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==
dependencies: dependencies:
array-union "^2.1.0" array-union "^2.1.0"
dir-glob "^3.0.1" dir-glob "^3.0.1"