diff --git a/packages/docusaurus-mdx-loader/src/index.ts b/packages/docusaurus-mdx-loader/src/index.ts index 1695d81b1975..2ffb0e415d3c 100644 --- a/packages/docusaurus-mdx-loader/src/index.ts +++ b/packages/docusaurus-mdx-loader/src/index.ts @@ -36,7 +36,8 @@ const DEFAULT_OPTIONS: RemarkAndRehypePluginOptions = { }; type Options = RemarkAndRehypePluginOptions & { - staticDir?: string; + staticDirs: string[]; + siteDir: string; isMDXPartial?: (filePath: string) => boolean; isMDXPartialFrontMatterWarningDisabled?: boolean; removeContentTitle?: boolean; @@ -123,8 +124,15 @@ export default async function mdxLoader( remarkPlugins: [ ...(reqOptions.beforeDefaultRemarkPlugins || []), ...DEFAULT_OPTIONS.remarkPlugins, - [transformImage, {staticDir: reqOptions.staticDir, filePath}], - [transformLinks, {staticDir: reqOptions.staticDir, filePath}], + [transformImage, {staticDirs: reqOptions.staticDirs, filePath}], + [ + transformLinks, + { + staticDirs: reqOptions.staticDirs, + filePath, + siteDir: reqOptions.siteDir, + }, + ], ...(reqOptions.remarkPlugins || []), ], rehypePlugins: [ diff --git a/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/fail.md b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/fail.md similarity index 100% rename from packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/fail.md rename to packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/fail.md diff --git a/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/img.md b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/img.md new file mode 100644 index 000000000000..38e15cd04fa0 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/img.md @@ -0,0 +1,19 @@ +![img](https://example.com/img.png) + +![](./static/img.png) + +![img](./static/img.png) + +![img from second static folder](/img2.png) + +![img from second static folder](./static2/img2.png) + +![img](./static/img.png 'Title') ![img](/img.png) + +![img with "quotes"](./static/img.png ''Quoted' title') + +## Heading + +```md +![img](./static/img.png) +``` diff --git a/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/noUrl.md b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/noUrl.md similarity index 100% rename from packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/noUrl.md rename to packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/noUrl.md diff --git a/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/pathname.md b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/pathname.md similarity index 100% rename from packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/pathname.md rename to packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/pathname.md diff --git a/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/img.png b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/static/img.png similarity index 100% rename from packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/img.png rename to packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/static/img.png diff --git a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/asset.pdf b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/static2/img2.png similarity index 100% rename from packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/asset.pdf rename to packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/static2/img2.png diff --git a/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__snapshots__/index.test.ts.snap index 571131fa5155..dd2e80abfc26 100644 --- a/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__snapshots__/index.test.ts.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`transformImage plugin fail if image does not exist 1`] = `"Image packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/img/doesNotExist.png used in packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/fail.md not found."`; +exports[`transformImage plugin fail if image does not exist 1`] = `"Image packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/static/img/doesNotExist.png or packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/static2/img/doesNotExist.png used in packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/fail.md not found."`; -exports[`transformImage plugin fail if image url is absent 1`] = `"Markdown image URL is mandatory in \\"packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/noUrl.md\\" file"`; +exports[`transformImage plugin fail if image url is absent 1`] = `"Markdown image URL is mandatory in \\"packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/noUrl.md\\" file"`; exports[`transformImage plugin pathname protocol 1`] = ` "![img](/img/unchecked.png) @@ -12,20 +12,22 @@ exports[`transformImage plugin pathname protocol 1`] = ` exports[`transformImage plugin transform md images to 1`] = ` "![img](https://example.com/img.png) - + -{\\"img\\"} +{\\"img\\"} -{\\"img\\"} {\\"img\\"} +{\\"img -{\\"img +{\\"img + +{\\"img\\"} {\\"img\\"} + +{\\"img ## Heading \`\`\`md -![img](./img.png) +![img](./static/img.png) \`\`\` - -{\\"img\\"} " `; diff --git a/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/img.md b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/img.md deleted file mode 100644 index 0ef7fc05350e..000000000000 --- a/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/img.md +++ /dev/null @@ -1,17 +0,0 @@ -![img](https://example.com/img.png) - -![](./img.png) - -![img](./img.png) - -![img](./img.png 'Title') ![img](/img.png) - -![img with "quotes"](./img.png ''Quoted' title') - -## Heading - -```md -![img](./img.png) -``` - -![img](img.png) diff --git a/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/index.test.ts b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/index.test.ts index 5a9492a41ab6..2e2c06aba3da 100644 --- a/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/index.test.ts +++ b/packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/index.test.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {join, relative} from 'path'; +import path from 'path'; import remark from 'remark'; import mdx from 'remark-mdx'; import vfile from 'to-vfile'; @@ -13,47 +13,48 @@ import plugin from '../index'; import headings from '../../headings/index'; const processFixture = async (name, options) => { - const path = join(__dirname, 'fixtures', `${name}.md`); - const file = await vfile.read(path); + const filePath = path.join(__dirname, `__fixtures__/${name}.md`); + const file = await vfile.read(filePath); const result = await remark() .use(headings) .use(mdx) - .use(plugin, {...options, filePath: path}) + .use(plugin, {...options, filePath}) .process(file); return result.toString(); }; -// avoid hardcoding absolute -const staticDir = `./${relative(process.cwd(), join(__dirname, 'fixtures'))}`; +const staticDirs = [ + // avoid hardcoding absolute in the snapshot + `./${path.relative( + process.cwd(), + path.join(__dirname, '__fixtures__/static'), + )}`, + `./${path.relative( + process.cwd(), + path.join(__dirname, '__fixtures__/static2'), + )}`, +]; describe('transformImage plugin', () => { test('fail if image does not exist', async () => { await expect( - processFixture('fail', { - staticDir, - }), + processFixture('fail', {staticDirs}), ).rejects.toThrowErrorMatchingSnapshot(); }); test('fail if image url is absent', async () => { await expect( - processFixture('noUrl', { - staticDir, - }), + processFixture('noUrl', {staticDirs}), ).rejects.toThrowErrorMatchingSnapshot(); }); test('transform md images to ', async () => { - const result = await processFixture('img', { - staticDir, - }); + const result = await processFixture('img', {staticDirs}); expect(result).toMatchSnapshot(); }); test('pathname protocol', async () => { - const result = await processFixture('pathname', { - staticDir, - }); + const result = await processFixture('pathname', {staticDirs}); expect(result).toMatchSnapshot(); }); }); diff --git a/packages/docusaurus-mdx-loader/src/remark/transformImage/index.ts b/packages/docusaurus-mdx-loader/src/remark/transformImage/index.ts index f46fc32a5bbc..5eb126003978 100644 --- a/packages/docusaurus-mdx-loader/src/remark/transformImage/index.ts +++ b/packages/docusaurus-mdx-loader/src/remark/transformImage/index.ts @@ -25,7 +25,7 @@ const { interface PluginOptions { filePath: string; - staticDir: string; + staticDirs: string[]; } const createJSX = (node: Image, pathUrl: string) => { @@ -63,9 +63,25 @@ async function ensureImageFileExist(imagePath: string, sourceFilePath: string) { } } +async function findImage(possiblePaths: string[], sourceFilePath: string) { + // eslint-disable-next-line no-restricted-syntax + for (const possiblePath of possiblePaths) { + if (await fs.pathExists(possiblePath)) { + return possiblePath; + } + } + throw new Error( + `Image ${possiblePaths + .map((p) => toMessageRelativeFilePath(p)) + .join(' or ')} used in ${toMessageRelativeFilePath( + sourceFilePath, + )} not found.`, + ); +} + async function processImageNode( node: Image, - {filePath, staticDir}: PluginOptions, + {filePath, staticDirs}: PluginOptions, ) { if (!node.url) { throw new Error( @@ -88,9 +104,11 @@ async function processImageNode( // images without protocol else if (path.isAbsolute(node.url)) { // absolute paths are expected to exist in the static folder - const expectedImagePath = path.join(staticDir, node.url); - await ensureImageFileExist(expectedImagePath, filePath); - createJSX(node, posixPath(expectedImagePath)); + const possibleImagePaths = staticDirs.map((dir) => + path.join(dir, node.url), + ); + const imagePath = await findImage(possibleImagePaths, filePath); + createJSX(node, posixPath(imagePath)); } // We try to convert image urls without protocol to images with require calls // going through webpack ensures that image assets exist at build time diff --git a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/asset.md b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/asset.md similarity index 97% rename from packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/asset.md rename to packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/asset.md index ac15b648daff..ad1ac5e88114 100644 --- a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/asset.md +++ b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/asset.md @@ -20,6 +20,8 @@ [asset](asset.pdf) +[asset2](/asset2.pdf) + [staticAsset.pdf](/staticAsset.pdf) [@site/static/staticAsset.pdf](@site/static/staticAsset.pdf) diff --git a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/static/staticAsset.pdf b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/asset.pdf similarity index 100% rename from packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/static/staticAsset.pdf rename to packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/asset.pdf diff --git a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/noUrl.md b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/noUrl.md similarity index 100% rename from packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/noUrl.md rename to packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/noUrl.md diff --git a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/pathname.md b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/pathname.md similarity index 100% rename from packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/pathname.md rename to packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/pathname.md diff --git a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/static/staticAsset.pdf b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/static/staticAsset.pdf new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/static/staticAssetImage.png b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/static/staticAssetImage.png similarity index 100% rename from packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/static/staticAssetImage.png rename to packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/static/staticAssetImage.png diff --git a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/static2/asset2.pdf b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/static2/asset2.pdf new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__snapshots__/index.test.ts.snap index 9bfd2f2c6324..4b6b4d89a5db 100644 --- a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__snapshots__/index.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`transformAsset plugin fail if asset url is absent 1`] = `"Markdown link URL is mandatory in \\"packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/fixtures/noUrl.md\\" file (title: asset, line: 1)."`; +exports[`transformAsset plugin fail if asset url is absent 1`] = `"Markdown link URL is mandatory in \\"packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/__fixtures__/noUrl.md\\" file (title: asset, line: 1)."`; exports[`transformAsset plugin pathname protocol 1`] = ` "[asset](pathname:///asset/unchecked.pdf) @@ -30,6 +30,8 @@ exports[`transformAsset plugin transform md links to 1`] = ` asset +asset2 + staticAsset.pdf @site/static/staticAsset.pdf diff --git a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/index.test.ts b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/index.test.ts index 836f887b8d5c..b9eaa062c521 100644 --- a/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/index.test.ts +++ b/packages/docusaurus-mdx-loader/src/remark/transformLinks/__tests__/index.test.ts @@ -5,21 +5,29 @@ * LICENSE file in the root directory of this source tree. */ -import {join} from 'path'; +import path from 'path'; import remark from 'remark'; import mdx from 'remark-mdx'; import vfile from 'to-vfile'; import plugin from '..'; import transformImage from '../../transformImage'; -const processFixture = async (name, options) => { - const path = join(__dirname, 'fixtures', `${name}.md`); - const staticDir = join(__dirname, 'fixtures', 'static'); - const file = await vfile.read(path); +const processFixture = async (name: string, options?) => { + const filePath = path.join(__dirname, `__fixtures__/${name}.md`); + const staticDirs = [ + path.join(__dirname, '__fixtures__/static'), + path.join(__dirname, '__fixtures__/static2'), + ]; + const file = await vfile.read(filePath); const result = await remark() .use(mdx) - .use(transformImage, {...options, filePath: path, staticDir}) - .use(plugin, {...options, filePath: path, staticDir}) + .use(transformImage, {...options, filePath, staticDirs}) + .use(plugin, { + ...options, + filePath, + staticDirs, + siteDir: path.join(__dirname, '__fixtures__'), + }) .process(file); return result.toString(); diff --git a/packages/docusaurus-mdx-loader/src/remark/transformLinks/index.ts b/packages/docusaurus-mdx-loader/src/remark/transformLinks/index.ts index b45907738ce1..4b2939a1593a 100644 --- a/packages/docusaurus-mdx-loader/src/remark/transformLinks/index.ts +++ b/packages/docusaurus-mdx-loader/src/remark/transformLinks/index.ts @@ -27,7 +27,8 @@ const hashRegex = /#.*$/; interface PluginOptions { filePath: string; - staticDir: string; + staticDirs: string[]; + siteDir: string; } async function ensureAssetFileExist( @@ -81,11 +82,10 @@ function toAssetRequireNode({ // If the link looks like an asset link, we'll link to the asset, // and use a require("assetUrl") (using webpack url-loader/file-loader) // instead of navigating to such link -async function convertToAssetLinkIfNeeded({ - node, - staticDir, - filePath, -}: {node: Link} & PluginOptions) { +async function convertToAssetLinkIfNeeded( + node: Link, + {filePath, siteDir, staticDirs}: PluginOptions, +) { const assetPath = node.url.replace(hashRegex, ''); const hasSiteAlias = assetPath.startsWith('@site/'); @@ -107,7 +107,6 @@ async function convertToAssetLinkIfNeeded({ } if (assetPath.startsWith('@site/')) { - const siteDir = path.join(staticDir, '..'); const fileSystemAssetPath = path.join( siteDir, assetPath.replace('@site/', ''), @@ -115,9 +114,13 @@ async function convertToAssetLinkIfNeeded({ await ensureAssetFileExist(fileSystemAssetPath, filePath); toAssetLinkNode(fileSystemAssetPath); } else if (path.isAbsolute(assetPath)) { - const fileSystemAssetPath = path.join(staticDir, assetPath); - if (await fs.pathExists(fileSystemAssetPath)) { - toAssetLinkNode(fileSystemAssetPath); + // eslint-disable-next-line no-restricted-syntax + for (const staticDir of staticDirs) { + const fileSystemAssetPath = path.join(staticDir, assetPath); + if (await fs.pathExists(fileSystemAssetPath)) { + toAssetLinkNode(fileSystemAssetPath); + return; + } } } else { const fileSystemAssetPath = path.join(path.dirname(filePath), assetPath); @@ -127,11 +130,7 @@ async function convertToAssetLinkIfNeeded({ } } -async function processLinkNode({ - node, - filePath, - staticDir, -}: {node: Link} & PluginOptions) { +async function processLinkNode(node: Link, options: PluginOptions) { if (!node.url) { // try to improve error feedback // see https://github.com/facebook/docusaurus/issues/3309#issuecomment-690371675 @@ -139,7 +138,7 @@ async function processLinkNode({ const line = node?.position?.start?.line || '?'; throw new Error( `Markdown link URL is mandatory in "${toMessageRelativeFilePath( - filePath, + options.filePath, )}" file (title: ${title}, line: ${line}).`, ); } @@ -149,14 +148,14 @@ async function processLinkNode({ return; } - await convertToAssetLinkIfNeeded({node, staticDir, filePath}); + await convertToAssetLinkIfNeeded(node, options); } const plugin: Plugin<[PluginOptions]> = (options) => { const transformer: Transformer = async (root) => { const promises: Promise[] = []; visit(root, 'link', (node: Link) => { - promises.push(processLinkNode({node, ...options})); + promises.push(processLinkNode(node, options)); }); await Promise.all(promises); }; diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 69617d46d0d9..c06d4bfadcda 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -17,10 +17,7 @@ import { addTrailingPathSeparator, createAbsoluteFilePathMatcher, } from '@docusaurus/utils'; -import { - STATIC_DIR_NAME, - DEFAULT_PLUGIN_ID, -} from '@docusaurus/core/lib/constants'; +import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; import {translateContent, getTranslationFiles} from './translations'; import { @@ -465,7 +462,10 @@ export default function pluginContentBlog( rehypePlugins, beforeDefaultRemarkPlugins, beforeDefaultRehypePlugins, - staticDir: path.join(siteDir, STATIC_DIR_NAME), + staticDirs: siteConfig.staticDirectories.map((dir) => + path.resolve(siteDir, dir), + ), + siteDir, isMDXPartial: createAbsoluteFilePathMatcher( options.exclude, contentDirs, diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index c0bc7bf97c9f..b239e3d991d6 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -7,10 +7,7 @@ import path from 'path'; -import { - STATIC_DIR_NAME, - DEFAULT_PLUGIN_ID, -} from '@docusaurus/core/lib/constants'; +import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; import { normalizeUrl, docuHash, @@ -397,7 +394,10 @@ export default function pluginContentDocs( rehypePlugins, beforeDefaultRehypePlugins, beforeDefaultRemarkPlugins, - staticDir: path.join(siteDir, STATIC_DIR_NAME), + staticDirs: siteConfig.staticDirectories.map((dir) => + path.resolve(siteDir, dir), + ), + siteDir, isMDXPartial: createAbsoluteFilePathMatcher( options.exclude, contentDirs, diff --git a/packages/docusaurus-plugin-content-pages/src/index.ts b/packages/docusaurus-plugin-content-pages/src/index.ts index c26208d1c140..4cc5626d240e 100644 --- a/packages/docusaurus-plugin-content-pages/src/index.ts +++ b/packages/docusaurus-plugin-content-pages/src/index.ts @@ -29,10 +29,7 @@ import { import {Configuration} from 'webpack'; import admonitions from 'remark-admonitions'; import {PluginOptionSchema} from './pluginOptionSchema'; -import { - DEFAULT_PLUGIN_ID, - STATIC_DIR_NAME, -} from '@docusaurus/core/lib/constants'; +import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; import { PluginOptions, @@ -209,7 +206,10 @@ export default function pluginContentPages( rehypePlugins, beforeDefaultRehypePlugins, beforeDefaultRemarkPlugins, - staticDir: path.join(siteDir, STATIC_DIR_NAME), + staticDirs: siteConfig.staticDirectories.map((dir) => + path.resolve(siteDir, dir), + ), + siteDir, isMDXPartial: createAbsoluteFilePathMatcher( options.exclude, contentDirs, diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts index 3cab9ab43112..eea0d0fe01c4 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts @@ -10,20 +10,15 @@ import remark from 'remark'; // TODO change to `../index` after migrating to ESM import npm2yarn from '../../lib/index'; import vfile from 'to-vfile'; -import {join, relative} from 'path'; +import path from 'path'; import mdx from 'remark-mdx'; -const staticDir = `./${relative(process.cwd(), join(__dirname, 'fixtures'))}`; - -const processFixture = async ( - name: string, - options: {sync?: boolean; staticDir: string}, -) => { - const path = join(__dirname, 'fixtures', `${name}.md`); - const file = await vfile.read(path); +const processFixture = async (name: string, options?: {sync?: boolean}) => { + const filePath = path.join(__dirname, 'fixtures', `${name}.md`); + const file = await vfile.read(filePath); const result = await remark() .use(mdx) - .use(npm2yarn, {...options, filePath: path}) + .use(npm2yarn, {...options, filePath}) .process(file); return result.toString(); @@ -31,41 +26,31 @@ const processFixture = async ( describe('npm2yarn plugin', () => { test('test: installation file', async () => { - const result = await processFixture('installation', { - staticDir, - }); + const result = await processFixture('installation'); expect(result).toMatchSnapshot(); }); test('test: plugin file', async () => { - const result = await processFixture('plugin', { - staticDir, - }); + const result = await processFixture('plugin'); expect(result).toMatchSnapshot(); }); test('test: language was not setted', async () => { - const result = await processFixture('syntax-not-properly-set', { - staticDir, - }); + const result = await processFixture('syntax-not-properly-set'); expect(result).toMatchSnapshot(); }); test('test: already imported tabs components above are not re-imported', async () => { - const result = await processFixture('import-tabs-above', { - staticDir, - }); + const result = await processFixture('import-tabs-above'); expect(result).toMatchSnapshot(); }); test('test: already imported tabs components below are not re-imported', async () => { - const result = await processFixture('import-tabs-below', { - staticDir, - }); + const result = await processFixture('import-tabs-below'); expect(result).toMatchSnapshot(); }); diff --git a/packages/docusaurus-types/src/index.d.ts b/packages/docusaurus-types/src/index.d.ts index 1e58a4611409..36101f75b2f0 100644 --- a/packages/docusaurus-types/src/index.d.ts +++ b/packages/docusaurus-types/src/index.d.ts @@ -58,6 +58,7 @@ export interface DocusaurusConfig { )[]; clientModules?: string[]; ssrTemplate?: string; + staticDirectories: string[]; stylesheets?: ( | string | { diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index 5ca4da640513..cd5830dc469a 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -13,7 +13,6 @@ import ReactLoadableSSRAddon from 'react-loadable-ssr-addon-v5-slorber'; import {Configuration} from 'webpack'; import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer'; import merge from 'webpack-merge'; -import {STATIC_DIR_NAME} from '../constants'; import {load, loadContext} from '../server'; import {handleBrokenLinks} from '../server/brokenLinks'; @@ -129,7 +128,7 @@ async function buildLocale({ outDir, generatedFilesDir, plugins, - siteConfig: {baseUrl, onBrokenLinks}, + siteConfig: {baseUrl, onBrokenLinks, staticDirectories}, routes, } = props; @@ -162,21 +161,16 @@ async function buildLocale({ }, }); - const staticDir = path.resolve(siteDir, STATIC_DIR_NAME); - if (await fs.pathExists(staticDir)) { - serverConfig = merge(serverConfig, { - plugins: [ - new CopyWebpackPlugin({ - patterns: [ - { - from: staticDir, - to: outDir, - }, - ], - }), - ], - }); - } + serverConfig = merge(serverConfig, { + plugins: [ + new CopyWebpackPlugin({ + patterns: staticDirectories + .map((dir) => path.resolve(siteDir, dir)) + .filter(fs.existsSync) + .map((dir) => ({from: dir, to: outDir})), + }), + ], + }); // Plugin Lifecycle - configureWebpack and configurePostCss. plugins.forEach((plugin) => { diff --git a/packages/docusaurus/src/commands/start.ts b/packages/docusaurus/src/commands/start.ts index 01137ad21176..a8ac0ed6d11b 100644 --- a/packages/docusaurus/src/commands/start.ts +++ b/packages/docusaurus/src/commands/start.ts @@ -20,7 +20,6 @@ import WebpackDevServer from 'webpack-dev-server'; import merge from 'webpack-merge'; import {load} from '../server'; import {StartCLIOptions} from '@docusaurus/types'; -import {STATIC_DIR_NAME} from '../constants'; import createClientConfig from '../webpack/client'; import { applyConfigureWebpack, @@ -187,9 +186,9 @@ export default async function start( // Reduce log verbosity, see https://github.com/facebook/docusaurus/pull/5420#issuecomment-906613105 stats: 'summary', }, - static: { + static: siteConfig.staticDirectories.map((dir) => ({ publicPath: baseUrl, - directory: path.resolve(siteDir, STATIC_DIR_NAME), + directory: path.resolve(siteDir, dir), watch: { // Useful options for our own monorepo using symlinks! // See https://github.com/webpack/webpack/issues/11612#issuecomment-879259806 @@ -197,7 +196,7 @@ export default async function start( ignored: /node_modules\/(?!@docusaurus)/, ...{pollingOptions}, }, - }, + })), historyApiFallback: { rewrites: [{from: /\/*/, to: baseUrl}], }, diff --git a/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap b/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap index af70e4957337..21b070a9b41e 100644 --- a/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap +++ b/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap @@ -40,6 +40,9 @@ Object { ], "presets": Array [], "projectName": "hello", + "staticDirectories": Array [ + "static", + ], "tagline": "Hello World", "themeConfig": Object {}, "themes": Array [], diff --git a/packages/docusaurus/src/server/configValidation.ts b/packages/docusaurus/src/server/configValidation.ts index 519161bd91a3..d873c497777c 100644 --- a/packages/docusaurus/src/server/configValidation.ts +++ b/packages/docusaurus/src/server/configValidation.ts @@ -6,7 +6,7 @@ */ import {DocusaurusConfig, I18nConfig} from '@docusaurus/types'; -import {DEFAULT_CONFIG_FILE_NAME} from '../constants'; +import {DEFAULT_CONFIG_FILE_NAME, STATIC_DIR_NAME} from '../constants'; import { Joi, logValidationBugReportHint, @@ -37,6 +37,7 @@ export const DEFAULT_CONFIG: Pick< | 'titleDelimiter' | 'noIndex' | 'baseUrlIssueBanner' + | 'staticDirectories' > = { i18n: DEFAULT_I18N_CONFIG, onBrokenLinks: 'throw', @@ -50,6 +51,7 @@ export const DEFAULT_CONFIG: Pick< titleDelimiter: '|', noIndex: false, baseUrlIssueBanner: true, + staticDirectories: [STATIC_DIR_NAME], }; const PluginSchema = Joi.alternatives() @@ -142,6 +144,9 @@ export const ConfigSchema = Joi.object({ .equal('ignore', 'log', 'warn', 'error', 'throw') .default(DEFAULT_CONFIG.onDuplicateRoutes), organizationName: Joi.string().allow(''), + staticDirectories: Joi.array() + .items(Joi.string()) + .default(DEFAULT_CONFIG.staticDirectories), projectName: Joi.string().allow(''), deploymentBranch: Joi.string().optional(), customFields: Joi.object().unknown().default(DEFAULT_CONFIG.customFields), diff --git a/packages/docusaurus/src/server/index.ts b/packages/docusaurus/src/server/index.ts index f26b974e7140..f48c3b89d85b 100644 --- a/packages/docusaurus/src/server/index.ts +++ b/packages/docusaurus/src/server/index.ts @@ -13,7 +13,6 @@ import { DEFAULT_BUILD_DIR_NAME, DEFAULT_CONFIG_FILE_NAME, GENERATED_FILES_DIR_NAME, - STATIC_DIR_NAME, } from '../constants'; import loadClientModules from './client-modules'; import loadConfig from './config'; @@ -193,7 +192,13 @@ function createBootstrapPlugin({ // Adds a "fallback" mdx loader for mdx files that are not processed by content plugins // This allows to do things such as importing repo/README.md as a partial from another doc // Not ideal solution though, but good enough for now -function createMDXFallbackPlugin({siteDir}: {siteDir: string}): LoadedPlugin { +function createMDXFallbackPlugin({ + siteDir, + siteConfig, +}: { + siteDir: string; + siteConfig: DocusaurusConfig; +}): LoadedPlugin { return { name: 'docusaurus-mdx-fallback-plugin', content: null, @@ -223,7 +228,10 @@ function createMDXFallbackPlugin({siteDir}: {siteDir: string}): LoadedPlugin { { loader: require.resolve('@docusaurus/mdx-loader'), options: { - staticDir: path.join(siteDir, STATIC_DIR_NAME), + staticDirs: siteConfig.staticDirectories.map((dir) => + path.resolve(siteDir, dir), + ), + siteDir, isMDXPartial: (_filename: string) => true, // External mdx files are always meant to be imported as partials isMDXPartialFrontMatterWarningDisabled: true, // External mdx files might have frontmatter, let's just disable the warning remarkPlugins: [admonitions], @@ -273,7 +281,7 @@ export async function load( ); plugins.push(createBootstrapPlugin({siteConfig})); - plugins.push(createMDXFallbackPlugin({siteDir})); + plugins.push(createMDXFallbackPlugin({siteDir, siteConfig})); // Load client modules. const clientModules = loadClientModules(plugins); diff --git a/website/static/dogfooding/4/docu.png b/website/_dogfooding/_asset-tests/4/docu.png similarity index 100% rename from website/static/dogfooding/4/docu.png rename to website/_dogfooding/_asset-tests/4/docu.png diff --git "a/website/static/dogfooding/4/\345\233\276\347\211\207.png" "b/website/_dogfooding/_asset-tests/4/\345\233\276\347\211\207.png" similarity index 100% rename from "website/static/dogfooding/4/\345\233\276\347\211\207.png" rename to "website/_dogfooding/_asset-tests/4/\345\233\276\347\211\207.png" diff --git a/website/static/dogfooding/javadoc/index.html b/website/_dogfooding/_asset-tests/dogfooding/javadoc/index.html similarity index 100% rename from website/static/dogfooding/javadoc/index.html rename to website/_dogfooding/_asset-tests/dogfooding/javadoc/index.html diff --git a/website/static/dogfooding/someFile.pdf b/website/_dogfooding/_asset-tests/someFile.pdf similarity index 100% rename from website/static/dogfooding/someFile.pdf rename to website/_dogfooding/_asset-tests/someFile.pdf diff --git a/website/static/dogfooding/someFile.xyz b/website/_dogfooding/_asset-tests/someFile.xyz similarity index 100% rename from website/static/dogfooding/someFile.xyz rename to website/_dogfooding/_asset-tests/someFile.xyz diff --git "a/website/static/dogfooding/\346\226\260\346\216\247\345\210\266\345\231\250\347\251\272\351\227\264/\345\233\276\347\211\207.png" "b/website/_dogfooding/_asset-tests/\346\226\260\346\216\247\345\210\266\345\231\250\347\251\272\351\227\264/\345\233\276\347\211\207.png" similarity index 100% rename from "website/static/dogfooding/\346\226\260\346\216\247\345\210\266\345\231\250\347\251\272\351\227\264/\345\233\276\347\211\207.png" rename to "website/_dogfooding/_asset-tests/\346\226\260\346\216\247\345\210\266\345\231\250\347\251\272\351\227\264/\345\233\276\347\211\207.png" diff --git a/website/_dogfooding/_pages tests/markdown-tests.md b/website/_dogfooding/_pages tests/markdown-tests.md index 0f3d0807a934..f1a417b9c3f2 100644 --- a/website/_dogfooding/_pages tests/markdown-tests.md +++ b/website/_dogfooding/_pages tests/markdown-tests.md @@ -6,13 +6,13 @@ This is a test page to see if Docusaurus markdown features are working properly See [#3337](https://github.com/facebook/docusaurus/issues/3337) -- [/dogfooding/someFile.pdf](/dogfooding/someFile.pdf) +- [/someFile.pdf](/someFile.pdf) -- [/dogfooding/someFile.xyz](/dogfooding/someFile.xyz) +- [/someFile.xyz](/someFile.xyz) -- [@site/static/dogfooding/someFile.pdf](@site/static/dogfooding/someFile.pdf) +- [@site/\_dogfooding/\_asset-tests/someFile.pdf](@site/_dogfooding/_asset-tests/someFile.pdf) -- [@site/static/dogfooding/someFile.xyz](@site/static/dogfooding/someFile.xyz) +- [@site/\_dogfooding/\_asset-tests/someFile.xyz](@site/_dogfooding/_asset-tests/someFile.xyz) ## Linking to non-SPA page hosted within website diff --git a/website/_dogfooding/_pages tests/markdownPageTests.md b/website/_dogfooding/_pages tests/markdownPageTests.md index 79059cfeca24..6b1e256edf0d 100644 --- a/website/_dogfooding/_pages tests/markdownPageTests.md +++ b/website/_dogfooding/_pages tests/markdownPageTests.md @@ -203,8 +203,8 @@ Code tag + double pipe: || ## Images edge cases -![](/dogfooding/新控制器空间/图片.png) +![](/新控制器空间/图片.png) -![](/dogfooding/4/图片.png) +![](/4/图片.png) -![](/dogfooding/4/docu.png) +![](/4/docu.png) diff --git a/website/docs/api/docusaurus.config.js.md b/website/docs/api/docusaurus.config.js.md index 13df482c5d0d..3365619a44bb 100644 --- a/website/docs/api/docusaurus.config.js.md +++ b/website/docs/api/docusaurus.config.js.md @@ -361,6 +361,20 @@ Attempting to add unknown field in the config will lead to error in build time: Error: The field(s) 'foo', 'bar' are not recognized in docusaurus.config.js ``` +### `staticDirectories` {#staticdirectories} + +An array of paths, relative to the site's directory or absolute. Files under these paths will be copied to the build output as-is. + +- Type: `string[]` + +Example: + +```js title="docusaurus.config.js" +module.exports = { + staticDirectories: ['static'], +}; +``` + ### `scripts` {#scripts} An array of scripts to load. The values can be either strings or plain objects of attribute-value maps. The `