refactor(types): move non-core, non-public types out of the types package (#7293)

This commit is contained in:
Joshua Chen 2022-05-03 17:15:48 +08:00 committed by GitHub
parent c7a5af7c4d
commit b49ae67521
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 397 additions and 404 deletions

View file

@ -3,7 +3,7 @@
"version": "2.0.0-beta.18",
"description": "Docusaurus Loader for MDX",
"main": "lib/index.js",
"types": "src/mdx-loader.d.ts",
"types": "lib/index.d.ts",
"publishConfig": {
"access": "public"
},

View file

@ -7,10 +7,13 @@
// TODO Types provided by MDX 2.0 https://github.com/mdx-js/mdx/blob/main/packages/mdx/types/index.d.ts
declare module '@mdx-js/mdx' {
import type {Processor} from 'unified';
import type {MDXPlugin} from '@docusaurus/mdx-loader';
import type {Processor, Plugin} from 'unified';
export type Options = {
type MDXPlugin =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[Plugin<any[]>, any] | Plugin<any[]>;
type Options = {
filepath?: string;
skipExport?: boolean;
wrapExport?: string;

View file

@ -5,240 +5,31 @@
* LICENSE file in the root directory of this source tree.
*/
import fs from 'fs-extra';
import {createCompiler} from '@mdx-js/mdx';
import logger from '@docusaurus/logger';
import emoji from 'remark-emoji';
import {
parseFrontMatter,
parseMarkdownContentTitle,
escapePath,
getFileLoaderUtils,
} from '@docusaurus/utils';
import stringifyObject from 'stringify-object';
import headings from './remark/headings';
import toc from './remark/toc';
import unwrapMdxCodeBlocks from './remark/unwrapMdxCodeBlocks';
import transformImage from './remark/transformImage';
import transformLinks from './remark/transformLinks';
import type {MDXOptions} from '@docusaurus/mdx-loader';
import type {LoaderContext} from 'webpack';
import type {Processor} from 'unified';
import {mdxLoader} from './loader';
const {
loaders: {inlineMarkdownImageFileLoader},
} = getFileLoaderUtils();
export default mdxLoader;
const pragma = `
/* @jsxRuntime classic */
/* @jsx mdx */
/* @jsxFrag mdx.Fragment */
`;
const DEFAULT_OPTIONS: MDXOptions = {
rehypePlugins: [],
remarkPlugins: [unwrapMdxCodeBlocks, emoji, headings, toc],
beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [],
};
const compilerCache = new Map<string | Options, [Processor, Options]>();
type Options = MDXOptions & {
staticDirs: string[];
siteDir: string;
isMDXPartial?: (filePath: string) => boolean;
isMDXPartialFrontMatterWarningDisabled?: boolean;
removeContentTitle?: boolean;
metadataPath?: string | ((filePath: string) => string);
createAssets?: (metadata: {
frontMatter: {[key: string]: unknown};
metadata: {[key: string]: unknown};
}) => {[key: string]: unknown};
filepath: string;
export type TOCItem = {
readonly value: string;
readonly id: string;
readonly level: number;
};
export type LoadedMDXContent<FrontMatter, Metadata, Assets = undefined> = {
/** As verbatim declared in the MDX document. */
readonly frontMatter: FrontMatter;
/** As provided by the content plugin. */
readonly metadata: Metadata;
/** A list of TOC items (headings). */
readonly toc: readonly TOCItem[];
/** First h1 title before any content. */
readonly contentTitle: string | undefined;
/**
* 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
* Usually image assets that may be collocated like `./img/thumbnail.png`.
* The loader would also bundle these assets and the client should use these
* in priority.
*/
async function readMetadataPath(metadataPath: string) {
try {
return await fs.readFile(metadataPath, 'utf8');
} catch (err) {
logger.error`MDX loader can't read MDX metadata file path=${metadataPath}. Maybe the isMDXPartial option function was not provided?`;
throw err;
}
}
/**
* Converts assets an object with Webpack require calls code.
* This is useful for mdx files to reference co-located assets using relative
* paths. Those assets should enter the Webpack assets pipeline and be hashed.
* For now, we only handle that for images and paths starting with `./`:
*
* `{image: "./myImage.png"}` => `{image: require("./myImage.png")}`
*/
function createAssetsExportCode(assets: {[key: string]: unknown}) {
if (Object.keys(assets).length === 0) {
return 'undefined';
}
// TODO implementation can be completed/enhanced
function createAssetValueCode(assetValue: unknown): string | undefined {
if (Array.isArray(assetValue)) {
const arrayItemCodes = assetValue.map(
(item) => createAssetValueCode(item) ?? 'undefined',
);
return `[${arrayItemCodes.join(', ')}]`;
}
// Only process string values starting with ./
// We could enhance this logic and check if file exists on disc?
if (typeof assetValue === 'string' && assetValue.startsWith('./')) {
// TODO do we have other use-cases than image assets?
// Probably not worth adding more support, as we want to move to Webpack 5 new asset system (https://github.com/facebook/docusaurus/pull/4708)
const inlineLoader = inlineMarkdownImageFileLoader;
return `require("${inlineLoader}${escapePath(assetValue)}").default`;
}
return undefined;
}
const assetEntries = Object.entries(assets);
const codeLines = assetEntries
.map(([key, value]) => {
const assetRequireCode = createAssetValueCode(value);
return assetRequireCode ? `"${key}": ${assetRequireCode},` : undefined;
})
.filter(Boolean);
return `{\n${codeLines.join('\n')}\n}`;
}
export default async function mdxLoader(
this: LoaderContext<Options>,
fileString: string,
): Promise<void> {
const callback = this.async();
const filePath = this.resourcePath;
const reqOptions = this.getOptions() ?? {};
const {frontMatter, content: contentWithTitle} = parseFrontMatter(fileString);
const {content, contentTitle} = parseMarkdownContentTitle(contentWithTitle, {
removeContentTitle: reqOptions.removeContentTitle,
});
const hasFrontMatter = Object.keys(frontMatter).length > 0;
if (!compilerCache.has(this.query)) {
const options: Options = {
...reqOptions,
remarkPlugins: [
...(reqOptions.beforeDefaultRemarkPlugins ?? []),
...DEFAULT_OPTIONS.remarkPlugins,
[
transformImage,
{
staticDirs: reqOptions.staticDirs,
siteDir: reqOptions.siteDir,
},
],
[
transformLinks,
{
staticDirs: reqOptions.staticDirs,
siteDir: reqOptions.siteDir,
},
],
...(reqOptions.remarkPlugins ?? []),
],
rehypePlugins: [
...(reqOptions.beforeDefaultRehypePlugins ?? []),
...DEFAULT_OPTIONS.rehypePlugins,
...(reqOptions.rehypePlugins ?? []),
],
readonly assets: Assets;
(): JSX.Element;
};
compilerCache.set(this.query, [createCompiler(options), options]);
}
const [compiler, options] = compilerCache.get(this.query)!;
let result: string;
try {
result = await compiler
.process({
contents: content,
path: this.resourcePath,
})
.then((res) => res.toString());
} catch (err) {
return callback(err as Error);
}
// MDX partials are MDX files starting with _ or in a folder starting with _
// Partial are not expected to have associated metadata files or front matter
const isMDXPartial = options.isMDXPartial?.(filePath);
if (isMDXPartial && hasFrontMatter) {
const errorMessage = `Docusaurus MDX partial files should not contain front matter.
Those partial files use the _ prefix as a convention by default, but this is configurable.
File at ${filePath} contains front matter that will be ignored:
${JSON.stringify(frontMatter, null, 2)}`;
if (!options.isMDXPartialFrontMatterWarningDisabled) {
const shouldError = process.env.NODE_ENV === 'test' || process.env.CI;
if (shouldError) {
return callback(new Error(errorMessage));
}
logger.warn(errorMessage);
}
}
function getMetadataPath(): string | undefined {
if (!isMDXPartial) {
// Read metadata for this MDX and export it.
if (options.metadataPath && typeof options.metadataPath === 'function') {
return options.metadataPath(filePath);
}
}
return undefined;
}
const metadataPath = getMetadataPath();
if (metadataPath) {
this.addDependency(metadataPath);
}
const metadataJsonString = metadataPath
? await readMetadataPath(metadataPath)
: undefined;
const metadata = metadataJsonString
? JSON.parse(metadataJsonString)
: undefined;
const assets =
reqOptions.createAssets && metadata
? reqOptions.createAssets({frontMatter, metadata})
: undefined;
const exportsCode = `
export const frontMatter = ${stringifyObject(frontMatter)};
export const contentTitle = ${stringifyObject(contentTitle)};
${metadataJsonString ? `export const metadata = ${metadataJsonString};` : ''}
${assets ? `export const assets = ${createAssetsExportCode(assets)};` : ''}
`;
const code = `
${pragma}
import React from 'react';
import { mdx } from '@mdx-js/react';
${exportsCode}
${result}
`;
return callback(null, code);
}
export type {Options, MDXPlugin, MDXOptions} from './loader';

View file

@ -0,0 +1,253 @@
/**
* 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 {createCompiler} from '@mdx-js/mdx';
import logger from '@docusaurus/logger';
import emoji from 'remark-emoji';
import {
parseFrontMatter,
parseMarkdownContentTitle,
escapePath,
getFileLoaderUtils,
} from '@docusaurus/utils';
import stringifyObject from 'stringify-object';
import headings from './remark/headings';
import toc from './remark/toc';
import unwrapMdxCodeBlocks from './remark/unwrapMdxCodeBlocks';
import transformImage from './remark/transformImage';
import transformLinks from './remark/transformLinks';
import type {LoaderContext} from 'webpack';
import type {Processor, Plugin} from 'unified';
const {
loaders: {inlineMarkdownImageFileLoader},
} = getFileLoaderUtils();
const pragma = `
/* @jsxRuntime classic */
/* @jsx mdx */
/* @jsxFrag mdx.Fragment */
`;
const DEFAULT_OPTIONS: MDXOptions = {
rehypePlugins: [],
remarkPlugins: [unwrapMdxCodeBlocks, emoji, headings, toc],
beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [],
};
const compilerCache = new Map<string | Options, [Processor, Options]>();
export type MDXPlugin =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[Plugin<any[]>, any] | Plugin<any[]>;
export type MDXOptions = {
remarkPlugins: MDXPlugin[];
rehypePlugins: MDXPlugin[];
beforeDefaultRemarkPlugins: MDXPlugin[];
beforeDefaultRehypePlugins: MDXPlugin[];
};
export type Options = MDXOptions & {
staticDirs: string[];
siteDir: string;
isMDXPartial?: (filePath: string) => boolean;
isMDXPartialFrontMatterWarningDisabled?: boolean;
removeContentTitle?: boolean;
metadataPath?: string | ((filePath: string) => string);
createAssets?: (metadata: {
frontMatter: {[key: string]: unknown};
metadata: {[key: string]: unknown};
}) => {[key: string]: unknown};
filepath: string;
};
/**
* 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: string) {
try {
return await fs.readFile(metadataPath, 'utf8');
} catch (err) {
logger.error`MDX loader can't read MDX metadata file path=${metadataPath}. Maybe the isMDXPartial option function was not provided?`;
throw err;
}
}
/**
* Converts assets an object with Webpack require calls code.
* This is useful for mdx files to reference co-located assets using relative
* paths. Those assets should enter the Webpack assets pipeline and be hashed.
* For now, we only handle that for images and paths starting with `./`:
*
* `{image: "./myImage.png"}` => `{image: require("./myImage.png")}`
*/
function createAssetsExportCode(assets: {[key: string]: unknown}) {
if (Object.keys(assets).length === 0) {
return 'undefined';
}
// TODO implementation can be completed/enhanced
function createAssetValueCode(assetValue: unknown): string | undefined {
if (Array.isArray(assetValue)) {
const arrayItemCodes = assetValue.map(
(item) => createAssetValueCode(item) ?? 'undefined',
);
return `[${arrayItemCodes.join(', ')}]`;
}
// Only process string values starting with ./
// We could enhance this logic and check if file exists on disc?
if (typeof assetValue === 'string' && assetValue.startsWith('./')) {
// TODO do we have other use-cases than image assets?
// Probably not worth adding more support, as we want to move to Webpack 5 new asset system (https://github.com/facebook/docusaurus/pull/4708)
const inlineLoader = inlineMarkdownImageFileLoader;
return `require("${inlineLoader}${escapePath(assetValue)}").default`;
}
return undefined;
}
const assetEntries = Object.entries(assets);
const codeLines = assetEntries
.map(([key, value]) => {
const assetRequireCode = createAssetValueCode(value);
return assetRequireCode ? `"${key}": ${assetRequireCode},` : undefined;
})
.filter(Boolean);
return `{\n${codeLines.join('\n')}\n}`;
}
export async function mdxLoader(
this: LoaderContext<Options>,
fileString: string,
): Promise<void> {
const callback = this.async();
const filePath = this.resourcePath;
const reqOptions = this.getOptions() ?? {};
const {frontMatter, content: contentWithTitle} = parseFrontMatter(fileString);
const {content, contentTitle} = parseMarkdownContentTitle(contentWithTitle, {
removeContentTitle: reqOptions.removeContentTitle,
});
const hasFrontMatter = Object.keys(frontMatter).length > 0;
if (!compilerCache.has(this.query)) {
const options: Options = {
...reqOptions,
remarkPlugins: [
...(reqOptions.beforeDefaultRemarkPlugins ?? []),
...DEFAULT_OPTIONS.remarkPlugins,
[
transformImage,
{
staticDirs: reqOptions.staticDirs,
siteDir: reqOptions.siteDir,
},
],
[
transformLinks,
{
staticDirs: reqOptions.staticDirs,
siteDir: reqOptions.siteDir,
},
],
...(reqOptions.remarkPlugins ?? []),
],
rehypePlugins: [
...(reqOptions.beforeDefaultRehypePlugins ?? []),
...DEFAULT_OPTIONS.rehypePlugins,
...(reqOptions.rehypePlugins ?? []),
],
};
compilerCache.set(this.query, [createCompiler(options), options]);
}
const [compiler, options] = compilerCache.get(this.query)!;
let result: string;
try {
result = await compiler
.process({
contents: content,
path: this.resourcePath,
})
.then((res) => res.toString());
} catch (err) {
return callback(err as Error);
}
// MDX partials are MDX files starting with _ or in a folder starting with _
// Partial are not expected to have associated metadata files or front matter
const isMDXPartial = options.isMDXPartial?.(filePath);
if (isMDXPartial && hasFrontMatter) {
const errorMessage = `Docusaurus MDX partial files should not contain front matter.
Those partial files use the _ prefix as a convention by default, but this is configurable.
File at ${filePath} contains front matter that will be ignored:
${JSON.stringify(frontMatter, null, 2)}`;
if (!options.isMDXPartialFrontMatterWarningDisabled) {
const shouldError = process.env.NODE_ENV === 'test' || process.env.CI;
if (shouldError) {
return callback(new Error(errorMessage));
}
logger.warn(errorMessage);
}
}
function getMetadataPath(): string | undefined {
if (!isMDXPartial) {
// Read metadata for this MDX and export it.
if (options.metadataPath && typeof options.metadataPath === 'function') {
return options.metadataPath(filePath);
}
}
return undefined;
}
const metadataPath = getMetadataPath();
if (metadataPath) {
this.addDependency(metadataPath);
}
const metadataJsonString = metadataPath
? await readMetadataPath(metadataPath)
: undefined;
const metadata = metadataJsonString
? JSON.parse(metadataJsonString)
: undefined;
const assets =
reqOptions.createAssets && metadata
? reqOptions.createAssets({frontMatter, metadata})
: undefined;
const exportsCode = `
export const frontMatter = ${stringifyObject(frontMatter)};
export const contentTitle = ${stringifyObject(contentTitle)};
${metadataJsonString ? `export const metadata = ${metadataJsonString};` : ''}
${assets ? `export const assets = ${createAssetsExportCode(assets)};` : ''}
`;
const code = `
${pragma}
import React from 'react';
import { mdx } from '@mdx-js/react';
${exportsCode}
${result}
`;
return callback(null, code);
}

View file

@ -1,37 +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 type {Plugin} from 'unified';
import type {TOCItem} from '@docusaurus/types';
export type MDXPlugin =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[Plugin<any[]>, any] | Plugin<any[]>;
export type MDXOptions = {
remarkPlugins: MDXPlugin[];
rehypePlugins: MDXPlugin[];
beforeDefaultRemarkPlugins: MDXPlugin[];
beforeDefaultRehypePlugins: MDXPlugin[];
};
export type LoadedMDXContent<FrontMatter, Metadata, Assets = undefined> = {
/** As verbatim declared in the MDX document. */
readonly frontMatter: FrontMatter;
/** As provided by the content plugin. */
readonly metadata: Metadata;
/** A list of TOC items (headings). */
readonly toc: readonly TOCItem[];
/** First h1 title before any content. */
readonly contentTitle: string | undefined;
/**
* Usually image assets that may be collocated like `./img/thumbnail.png`.
* The loader would also bundle these assets and the client should use these
* in priority.
*/
readonly assets: Assets;
(): JSX.Element;
};

View file

@ -13,7 +13,7 @@ import toString from 'mdast-util-to-string';
import visit from 'unist-util-visit';
import {toValue} from '../utils';
import type {TOCItem} from '@docusaurus/types';
import type {TOCItem} from '../..';
import type {Node, Parent} from 'unist';
import type {Heading, Literal} from 'mdast';
import type {Transformer} from 'unified';

View file

@ -7,5 +7,6 @@
"declarationMap": true,
"rootDir": "src",
"outDir": "lib"
}
},
"include": ["src"]
}

View file

@ -20,6 +20,8 @@ import {
getContentPathList,
getDataFilePath,
DEFAULT_PLUGIN_ID,
type TagsListItem,
type TagModule,
} from '@docusaurus/utils';
import {translateContent, getTranslationFiles} from './translations';
@ -31,13 +33,7 @@ import type {
BlogContentPaths,
BlogMarkdownLoaderOptions,
} from './types';
import type {
LoadContext,
Plugin,
HtmlTags,
TagsListItem,
TagModule,
} from '@docusaurus/types';
import type {LoadContext, Plugin, HtmlTags} from '@docusaurus/types';
import {
generateBlogPosts,
getSourceToPermalink,

View file

@ -7,9 +7,8 @@
declare module '@docusaurus/plugin-content-blog' {
import type {MDXOptions} from '@docusaurus/mdx-loader';
import type {FrontMatterTag} from '@docusaurus/utils';
import type {FrontMatterTag, Tag} from '@docusaurus/utils';
import type {Overwrite} from 'utility-types';
import type {Tag} from '@docusaurus/types';
export type Assets = {
/**
@ -487,7 +486,7 @@ declare module '@theme/BlogListPage' {
declare module '@theme/BlogTagsListPage' {
import type {BlogSidebar} from '@docusaurus/plugin-content-blog';
import type {TagsListItem} from '@docusaurus/types';
import type {TagsListItem} from '@docusaurus/utils';
export interface Props {
/** Blog sidebar. */
@ -503,7 +502,7 @@ declare module '@theme/BlogTagsPostsPage' {
import type {BlogSidebar} from '@docusaurus/plugin-content-blog';
import type {Content} from '@theme/BlogPostPage';
import type {Metadata} from '@theme/BlogListPage';
import type {TagModule} from '@docusaurus/types';
import type {TagModule} from '@docusaurus/utils';
export interface Props {
/** Blog sidebar. */

View file

@ -5,8 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import type {BrokenMarkdownLink, ContentPaths} from '@docusaurus/utils';
import type {Tag} from '@docusaurus/types';
import type {BrokenMarkdownLink, ContentPaths, Tag} from '@docusaurus/utils';
import type {BlogPostMetadata} from '@docusaurus/plugin-content-blog';
import type {Metadata as BlogPaginatedMetadata} from '@theme/BlogListPage';

View file

@ -7,8 +7,13 @@
declare module '@docusaurus/plugin-content-docs' {
import type {MDXOptions} from '@docusaurus/mdx-loader';
import type {ContentPaths, FrontMatterTag} from '@docusaurus/utils';
import type {TagsListItem, TagModule, Tag} from '@docusaurus/types';
import type {
ContentPaths,
FrontMatterTag,
TagsListItem,
TagModule,
Tag,
} from '@docusaurus/utils';
import type {Required} from 'utility-types';
export type Assets = {

View file

@ -8,14 +8,13 @@
/// <reference types="@docusaurus/module-type-aliases" />
import type {Sidebars} from './sidebars/types';
import type {BrokenMarkdownLink} from '@docusaurus/utils';
import type {BrokenMarkdownLink, Tag} from '@docusaurus/utils';
import type {
VersionMetadata,
LastUpdateData,
DocMetadata,
CategoryGeneratedIndexMetadata,
} from '@docusaurus/plugin-content-docs';
import type {Tag} from '@docusaurus/types';
import type {SidebarsUtils} from './sidebars/utils';
export type DocFile = {

View file

@ -1057,7 +1057,7 @@ declare module '@theme/Details' {
}
declare module '@theme/TOCItems' {
import type {TOCItem} from '@docusaurus/types';
import type {TOCItem} from '@docusaurus/mdx-loader';
export interface Props {
readonly toc: readonly TOCItem[];
@ -1085,7 +1085,7 @@ declare module '@theme/TOCItems/Tree' {
}
declare module '@theme/TOC' {
import type {TOCItem} from '@docusaurus/types';
import type {TOCItem} from '@docusaurus/mdx-loader';
// `minHeadingLevel` only comes from doc/post front matter, and won't have a
// default set by Joi. See TOC, TOCInline, TOCCollapsible for examples.
@ -1100,7 +1100,7 @@ declare module '@theme/TOC' {
}
declare module '@theme/TOCInline' {
import type {TOCItem} from '@docusaurus/types';
import type {TOCItem} from '@docusaurus/mdx-loader';
export interface Props {
readonly toc: readonly TOCItem[];
@ -1112,7 +1112,7 @@ declare module '@theme/TOCInline' {
}
declare module '@theme/TOCCollapsible' {
import type {TOCItem} from '@docusaurus/types';
import type {TOCItem} from '@docusaurus/mdx-loader';
export interface Props {
readonly className?: string;
@ -1236,7 +1236,7 @@ declare module '@theme/IconExternalLink' {
}
declare module '@theme/TagsListByLetter' {
import type {TagsListItem} from '@docusaurus/types';
import type {TagsListItem} from '@docusaurus/utils';
export interface Props {
readonly tags: readonly TagsListItem[];
@ -1245,7 +1245,7 @@ declare module '@theme/TagsListByLetter' {
}
declare module '@theme/TagsListInline' {
import type {Tag} from '@docusaurus/types';
import type {Tag} from '@docusaurus/utils';
export interface Props {
readonly tags: readonly Tag[];
@ -1254,7 +1254,7 @@ declare module '@theme/TagsListInline' {
}
declare module '@theme/Tag' {
import type {TagsListItem} from '@docusaurus/types';
import type {TagsListItem} from '@docusaurus/utils';
import type {Optional} from 'utility-types';
export interface Props extends Optional<TagsListItem, 'count'> {}

View file

@ -6,7 +6,7 @@
*/
import {translate} from '@docusaurus/Translate';
import type {TagsListItem} from '@docusaurus/types';
import type {TagsListItem} from '@docusaurus/utils';
export const translateTagsPageTitle = (): string =>
translate({

View file

@ -6,7 +6,7 @@
*/
import {useMemo} from 'react';
import type {TOCItem} from '@docusaurus/types';
import type {TOCItem} from '@docusaurus/mdx-loader';
export type TOCTreeNode = {
readonly value: string;

View file

@ -427,13 +427,13 @@ export type LoadContext = {
};
export type Props = LoadContext & {
readonly headTags: string;
readonly preBodyTags: string;
readonly postBodyTags: string;
readonly siteMetadata: SiteMetadata;
readonly routes: RouteConfig[];
readonly routesPaths: string[];
readonly plugins: LoadedPlugin[];
headTags: string;
preBodyTags: string;
postBodyTags: string;
siteMetadata: SiteMetadata;
routes: RouteConfig[];
routesPaths: string[];
plugins: LoadedPlugin[];
};
// === Plugin ===
@ -447,9 +447,7 @@ export type PluginContentLoadedActions = {
export type ConfigureWebpackUtils = {
getStyleLoaders: (
isServer: boolean,
cssOptions: {
[key: string]: unknown;
},
cssOptions: {[key: string]: unknown},
) => RuleSetRule[];
getJSLoader: (options: {
isServer: boolean;
@ -464,7 +462,7 @@ export type AllContent = {
};
// TODO improve type (not exposed by postcss-loader)
export type PostCssOptions = {[key: string]: unknown} & {plugins: unknown[]};
export type PostCssOptions = {plugins: unknown[]; [key: string]: unknown};
type HtmlTagObject = {
/**
@ -557,40 +555,6 @@ export type Plugin<Content = unknown> = {
}) => ThemeConfig;
};
export type NormalizedPluginConfig = {
/**
* The default export of the plugin module, or alternatively, what's provided
* in the config file as inline plugins. Note that if a file is like:
*
* ```ts
* export default plugin() {...}
* export validateOptions() {...}
* ```
*
* Then the static methods may not exist here. `pluginModule.module` will
* always take priority.
*/
plugin: PluginModule;
/** Options as they are provided in the config, not validated yet. */
options: PluginOptions;
/** Only available when a string is provided in config. */
pluginModule?: {
/**
* Raw module name as provided in the config. Shorthands have been resolved,
* so at least it's directly `require.resolve`able.
*/
path: string;
/** Whatever gets imported with `require`. */
module: ImportedPluginModule;
};
/**
* Different from `pluginModule.path`, this one is always an absolute path,
* used to resolve relative paths returned from lifecycles. If it's an inline
* plugin, it will be path to the config file.
*/
entryPath: string;
};
export type InitializedPlugin = Plugin & {
readonly options: Required<PluginOptions>;
readonly version: PluginVersionInformation;
@ -612,9 +576,8 @@ export type SwizzleComponentConfig = {
export type SwizzleConfig = {
components: {[componentName: string]: SwizzleComponentConfig};
// Other settings could be added here,
// For example: the ability to declare the config as exhaustive
// so that we can emit errors
// Other settings could be added here, like the ability to declare the config
// as exhaustive so that we can emit errors
};
export type PluginModule = {
@ -626,21 +589,13 @@ export type PluginModule = {
getSwizzleConfig?: () => SwizzleConfig | undefined;
};
export type ImportedPluginModule = PluginModule & {
default?: PluginModule;
};
export type Preset = {
plugins?: PluginConfig[];
themes?: PluginConfig[];
};
export type PresetModule = {
<T>(context: LoadContext, presetOptions: T): Preset;
};
export type ImportedPresetModule = PresetModule & {
default?: PresetModule;
(context: LoadContext, presetOptions: unknown): Preset;
};
// === Route registry ===
@ -775,19 +730,6 @@ export type Registry = {
];
};
/**
* Aliases used for Webpack resolution (useful for implementing swizzling)
*/
export type ThemeAliases = {
[alias: string]: string;
};
export type TOCItem = {
readonly value: string;
readonly id: string;
readonly level: number;
};
export type ClientModule = {
onRouteDidUpdate?: (args: {
previousLocation: Location | null;
@ -799,25 +741,6 @@ export type ClientModule = {
}) => (() => void) | void;
};
/** What the user configures. */
export type Tag = {
label: string;
/** Permalink to this tag's page, without the `/tags/` base path. */
permalink: string;
};
/** What the tags list page should know about each tag. */
export type TagsListItem = Tag & {
/** Number of posts/docs with this tag. */
count: number;
};
/** What the tag's own page should know about the tag. */
export type TagModule = TagsListItem & {
/** The tags list page's permalink. */
allTagsPath: string;
};
export type UseDataOptions = {
/**
* Throw an error, or simply return undefined if the data cannot be found. Use

View file

@ -6,8 +6,7 @@
*/
import Joi from './Joi';
import {isValidPathname, DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
import type {Tag} from '@docusaurus/types';
import {isValidPathname, DEFAULT_PLUGIN_ID, type Tag} from '@docusaurus/utils';
import {JoiFrontMatter} from './JoiFrontMatter';
export const PluginIdSchema = Joi.string()

View file

@ -57,6 +57,9 @@ export {
buildSshUrl,
} from './urlUtils';
export {
type Tag,
type TagsListItem,
type TagModule,
type FrontMatterTag,
normalizeFrontMatterTags,
groupTaggedItems,

View file

@ -7,7 +7,25 @@
import _ from 'lodash';
import {normalizeUrl} from './urlUtils';
import type {Tag} from '@docusaurus/types';
/** What the user configures. */
export type Tag = {
label: string;
/** Permalink to this tag's page, without the `/tags/` base path. */
permalink: string;
};
/** What the tags list page should know about each tag. */
export type TagsListItem = Tag & {
/** Number of posts/docs with this tag. */
count: number;
};
/** What the tag's own page should know about the tag. */
export type TagModule = TagsListItem & {
/** The tags list page's permalink. */
allTagsPath: string;
};
export type FrontMatterTag = string | Tag;

View file

@ -8,11 +8,11 @@
import leven from 'leven';
import _ from 'lodash';
import logger from '@docusaurus/logger';
import type {NormalizedPluginConfig} from '../../server/plugins/configs';
import type {
InitializedPlugin,
SwizzleAction,
SwizzleActionStatus,
NormalizedPluginConfig,
} from '@docusaurus/types';
export const SwizzleActions: SwizzleAction[] = ['wrap', 'eject'];

View file

@ -12,10 +12,46 @@ import {resolveModuleName} from './moduleShorthand';
import type {
LoadContext,
PluginConfig,
ImportedPluginModule,
NormalizedPluginConfig,
PluginModule,
PluginOptions,
} from '@docusaurus/types';
type ImportedPluginModule = PluginModule & {default?: PluginModule};
export type NormalizedPluginConfig = {
/**
* The default export of the plugin module, or alternatively, what's provided
* in the config file as inline plugins. Note that if a file is like:
*
* ```ts
* export default plugin() {...}
* export validateOptions() {...}
* ```
*
* Then the static methods may not exist here. `pluginModule.module` will
* always take priority.
*/
plugin: PluginModule;
/** Options as they are provided in the config, not validated yet. */
options: PluginOptions;
/** Only available when a string is provided in config. */
pluginModule?: {
/**
* Raw module name as provided in the config. Shorthands have been resolved,
* so at least it's directly `require.resolve`able.
*/
path: string;
/** Whatever gets imported with `require`. */
module: ImportedPluginModule;
};
/**
* Different from `pluginModule.path`, this one is always an absolute path,
* used to resolve relative paths returned from lifecycles. If it's an inline
* plugin, it will be path to the config file.
*/
entryPath: string;
};
async function normalizePluginConfig(
pluginConfig: Exclude<PluginConfig, false | null>,
configPath: string,

View file

@ -13,7 +13,6 @@ import type {
PluginModule,
PluginOptions,
InitializedPlugin,
NormalizedPluginConfig,
} from '@docusaurus/types';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
import {getPluginVersion} from '../siteMetadata';
@ -22,7 +21,7 @@ import {
normalizePluginOptions,
normalizeThemeConfig,
} from '@docusaurus/utils-validation';
import {loadPluginConfigs} from './configs';
import {loadPluginConfigs, type NormalizedPluginConfig} from './configs';
function getOptionValidationFunction(
normalizedPluginConfig: NormalizedPluginConfig,

View file

@ -10,11 +10,13 @@ import importFresh from 'import-fresh';
import type {
LoadContext,
PluginConfig,
ImportedPresetModule,
PresetModule,
DocusaurusConfig,
} from '@docusaurus/types';
import {resolveModuleName} from './moduleShorthand';
type ImportedPresetModule = PresetModule & {default?: PresetModule};
/**
* Calls preset functions, aggregates each of their return values, and returns
* the plugin and theme configs.

View file

@ -12,7 +12,7 @@ import {excludeJS, clientDir, createBaseConfig} from '../base';
import * as utils from '@docusaurus/utils/lib/webpackUtils';
import {posixPath} from '@docusaurus/utils';
import _ from 'lodash';
import type {Props, ThemeAliases} from '@docusaurus/types';
import type {Props} from '@docusaurus/types';
describe('babel transpilation exclude logic', () => {
it('always transpiles client dir files', () => {
@ -106,9 +106,8 @@ describe('base webpack config', () => {
});
it('creates webpack aliases', async () => {
// @ts-expect-error: Docusaurus webpack alias is always an object
const aliases: ThemeAliases =
(await createBaseConfig(props, true)).resolve?.alias ?? {};
const aliases = ((await createBaseConfig(props, true)).resolve?.alias ??
{}) as {[alias: string]: string};
// Make aliases relative so that test work on all computers
const relativeAliases = _.mapValues(aliases, (a) =>
posixPath(path.relative(props.siteDir, a)),

View file

@ -15,7 +15,12 @@ import {
Globby,
} from '@docusaurus/utils';
import _ from 'lodash';
import type {ThemeAliases, LoadedPlugin} from '@docusaurus/types';
import type {LoadedPlugin} from '@docusaurus/types';
/**
* Aliases used for Webpack resolution (useful for implementing swizzling)
*/
type ThemeAliases = {[alias: string]: string};
const ThemeFallbackDir = path.join(__dirname, '../../client/theme-fallback');