From b49ae67521adf8530ab444135a68d18a64a74d67 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 3 May 2022 17:15:48 +0800 Subject: [PATCH] refactor(types): move non-core, non-public types out of the types package (#7293) --- packages/docusaurus-mdx-loader/package.json | 2 +- packages/docusaurus-mdx-loader/src/deps.d.ts | 9 +- packages/docusaurus-mdx-loader/src/index.ts | 255 ++---------------- packages/docusaurus-mdx-loader/src/loader.ts | 253 +++++++++++++++++ .../docusaurus-mdx-loader/src/mdx-loader.d.ts | 37 --- .../src/remark/toc/index.ts | 2 +- packages/docusaurus-mdx-loader/tsconfig.json | 3 +- .../src/index.ts | 10 +- .../src/plugin-content-blog.d.ts | 7 +- .../src/types.ts | 3 +- .../src/plugin-content-docs.d.ts | 9 +- .../src/types.ts | 3 +- .../src/theme-classic.d.ts | 14 +- .../src/utils/tagsUtils.ts | 2 +- .../src/utils/tocUtils.ts | 2 +- packages/docusaurus-types/src/index.d.ts | 101 +------ .../src/validationSchemas.ts | 3 +- packages/docusaurus-utils/src/index.ts | 3 + packages/docusaurus-utils/src/tags.ts | 20 +- .../docusaurus/src/commands/swizzle/common.ts | 2 +- .../docusaurus/src/server/plugins/configs.ts | 40 ++- .../docusaurus/src/server/plugins/init.ts | 3 +- .../docusaurus/src/server/plugins/presets.ts | 4 +- .../src/webpack/__tests__/base.test.ts | 7 +- .../docusaurus/src/webpack/aliases/index.ts | 7 +- 25 files changed, 397 insertions(+), 404 deletions(-) create mode 100644 packages/docusaurus-mdx-loader/src/loader.ts delete mode 100644 packages/docusaurus-mdx-loader/src/mdx-loader.d.ts diff --git a/packages/docusaurus-mdx-loader/package.json b/packages/docusaurus-mdx-loader/package.json index 72da1dcfb8..47c1eb0d20 100644 --- a/packages/docusaurus-mdx-loader/package.json +++ b/packages/docusaurus-mdx-loader/package.json @@ -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" }, diff --git a/packages/docusaurus-mdx-loader/src/deps.d.ts b/packages/docusaurus-mdx-loader/src/deps.d.ts index d27fd5334b..7f8fb25a2f 100644 --- a/packages/docusaurus-mdx-loader/src/deps.d.ts +++ b/packages/docusaurus-mdx-loader/src/deps.d.ts @@ -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] | Plugin; + + type Options = { filepath?: string; skipExport?: boolean; wrapExport?: string; diff --git a/packages/docusaurus-mdx-loader/src/index.ts b/packages/docusaurus-mdx-loader/src/index.ts index 4991f774cf..ad7887dcc7 100644 --- a/packages/docusaurus-mdx-loader/src/index.ts +++ b/packages/docusaurus-mdx-loader/src/index.ts @@ -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: [], +export type TOCItem = { + readonly value: string; + readonly id: string; + readonly level: number; }; -const compilerCache = new Map(); - -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 LoadedMDXContent = { + /** 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; }; - -/** - * 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 default async function mdxLoader( - this: LoaderContext, - fileString: string, -): Promise { - 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); -} +export type {Options, MDXPlugin, MDXOptions} from './loader'; diff --git a/packages/docusaurus-mdx-loader/src/loader.ts b/packages/docusaurus-mdx-loader/src/loader.ts new file mode 100644 index 0000000000..af18cd3982 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/loader.ts @@ -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(); + +export type MDXPlugin = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [Plugin, any] | Plugin; +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, + fileString: string, +): Promise { + 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); +} diff --git a/packages/docusaurus-mdx-loader/src/mdx-loader.d.ts b/packages/docusaurus-mdx-loader/src/mdx-loader.d.ts deleted file mode 100644 index 5e2e0a201b..0000000000 --- a/packages/docusaurus-mdx-loader/src/mdx-loader.d.ts +++ /dev/null @@ -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] | Plugin; -export type MDXOptions = { - remarkPlugins: MDXPlugin[]; - rehypePlugins: MDXPlugin[]; - beforeDefaultRemarkPlugins: MDXPlugin[]; - beforeDefaultRehypePlugins: MDXPlugin[]; -}; - -export type LoadedMDXContent = { - /** 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; -}; diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/index.ts b/packages/docusaurus-mdx-loader/src/remark/toc/index.ts index 8f5626de5a..b0a888c079 100644 --- a/packages/docusaurus-mdx-loader/src/remark/toc/index.ts +++ b/packages/docusaurus-mdx-loader/src/remark/toc/index.ts @@ -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'; diff --git a/packages/docusaurus-mdx-loader/tsconfig.json b/packages/docusaurus-mdx-loader/tsconfig.json index aee99fc0f3..4cc153c7f1 100644 --- a/packages/docusaurus-mdx-loader/tsconfig.json +++ b/packages/docusaurus-mdx-loader/tsconfig.json @@ -7,5 +7,6 @@ "declarationMap": true, "rootDir": "src", "outDir": "lib" - } + }, + "include": ["src"] } diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 22b1a20c19..57fc7840ea 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -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, diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index f25d572eee..e17128655b 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -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. */ diff --git a/packages/docusaurus-plugin-content-blog/src/types.ts b/packages/docusaurus-plugin-content-blog/src/types.ts index 84f76fa927..f40e57e00a 100644 --- a/packages/docusaurus-plugin-content-blog/src/types.ts +++ b/packages/docusaurus-plugin-content-blog/src/types.ts @@ -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'; diff --git a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts index 97e8c46b0e..644477d037 100644 --- a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts +++ b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts @@ -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 = { diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index b12fffaa9e..ee964f533e 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -8,14 +8,13 @@ /// 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 = { diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index c20cf73929..cbbf6bf58d 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -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 {} diff --git a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts index c29afab1cf..23ab3d40df 100644 --- a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts @@ -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({ diff --git a/packages/docusaurus-theme-common/src/utils/tocUtils.ts b/packages/docusaurus-theme-common/src/utils/tocUtils.ts index 0d1ee35df1..8f12d78071 100644 --- a/packages/docusaurus-theme-common/src/utils/tocUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/tocUtils.ts @@ -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; diff --git a/packages/docusaurus-types/src/index.d.ts b/packages/docusaurus-types/src/index.d.ts index d3cd057e7f..821152d37e 100644 --- a/packages/docusaurus-types/src/index.d.ts +++ b/packages/docusaurus-types/src/index.d.ts @@ -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 = { }) => 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; 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 = { - (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 diff --git a/packages/docusaurus-utils-validation/src/validationSchemas.ts b/packages/docusaurus-utils-validation/src/validationSchemas.ts index 385efd4ff2..f8e9a1c36e 100644 --- a/packages/docusaurus-utils-validation/src/validationSchemas.ts +++ b/packages/docusaurus-utils-validation/src/validationSchemas.ts @@ -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() diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 07ff78dc98..fb2c66435e 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -57,6 +57,9 @@ export { buildSshUrl, } from './urlUtils'; export { + type Tag, + type TagsListItem, + type TagModule, type FrontMatterTag, normalizeFrontMatterTags, groupTaggedItems, diff --git a/packages/docusaurus-utils/src/tags.ts b/packages/docusaurus-utils/src/tags.ts index 88408ef110..1020d9ee2e 100644 --- a/packages/docusaurus-utils/src/tags.ts +++ b/packages/docusaurus-utils/src/tags.ts @@ -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; diff --git a/packages/docusaurus/src/commands/swizzle/common.ts b/packages/docusaurus/src/commands/swizzle/common.ts index 4c6eeda813..4fdf01e4f2 100644 --- a/packages/docusaurus/src/commands/swizzle/common.ts +++ b/packages/docusaurus/src/commands/swizzle/common.ts @@ -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']; diff --git a/packages/docusaurus/src/server/plugins/configs.ts b/packages/docusaurus/src/server/plugins/configs.ts index 5c4b7711c0..b7b7bbf6cc 100644 --- a/packages/docusaurus/src/server/plugins/configs.ts +++ b/packages/docusaurus/src/server/plugins/configs.ts @@ -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, configPath: string, diff --git a/packages/docusaurus/src/server/plugins/init.ts b/packages/docusaurus/src/server/plugins/init.ts index 46a010324c..4bc0aa92da 100644 --- a/packages/docusaurus/src/server/plugins/init.ts +++ b/packages/docusaurus/src/server/plugins/init.ts @@ -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, diff --git a/packages/docusaurus/src/server/plugins/presets.ts b/packages/docusaurus/src/server/plugins/presets.ts index 27788b747d..b45b06915b 100644 --- a/packages/docusaurus/src/server/plugins/presets.ts +++ b/packages/docusaurus/src/server/plugins/presets.ts @@ -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. diff --git a/packages/docusaurus/src/webpack/__tests__/base.test.ts b/packages/docusaurus/src/webpack/__tests__/base.test.ts index d3aff2ab54..93b1de8c19 100644 --- a/packages/docusaurus/src/webpack/__tests__/base.test.ts +++ b/packages/docusaurus/src/webpack/__tests__/base.test.ts @@ -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)), diff --git a/packages/docusaurus/src/webpack/aliases/index.ts b/packages/docusaurus/src/webpack/aliases/index.ts index 984876571c..b48184b8ab 100644 --- a/packages/docusaurus/src/webpack/aliases/index.ts +++ b/packages/docusaurus/src/webpack/aliases/index.ts @@ -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');