Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): allow sourcing from multiple static directories #4095

Merged
merged 16 commits into from
Nov 18, 2021
Merged
14 changes: 11 additions & 3 deletions packages/docusaurus-mdx-loader/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,49 @@
* 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';
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 = [
`./${path.relative(process.cwd(), path.join(__dirname, 'fixtures'))}`,
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
];

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 <img />', 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();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const {

interface PluginOptions {
filePath: string;
staticDir: string;
staticDirs: string[];
}

const createJSX = (node: Image, pathUrl: string) => {
Expand Down Expand Up @@ -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(
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,26 @@
* 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')];
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
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();
Expand Down
35 changes: 17 additions & 18 deletions packages/docusaurus-mdx-loader/src/remark/transformLinks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const hashRegex = /#.*$/;

interface PluginOptions {
filePath: string;
staticDir: string;
staticDirs: string[];
siteDir: string;
}

async function ensureAssetFileExist(
Expand Down Expand Up @@ -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/');
Expand All @@ -107,17 +107,20 @@ async function convertToAssetLinkIfNeeded({
}

if (assetPath.startsWith('@site/')) {
const siteDir = path.join(staticDir, '..');
const fileSystemAssetPath = path.join(
siteDir,
assetPath.replace('@site/', ''),
);
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);
Expand All @@ -127,19 +130,15 @@ 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
const title = node.title || (node.children[0] as Literal)?.value || '?';
const line = node?.position?.start?.line || '?';
throw new Error(
`Markdown link URL is mandatory in "${toMessageRelativeFilePath(
filePath,
options.filePath,
)}" file (title: ${title}, line: ${line}).`,
);
}
Expand All @@ -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<void>[] = [];
visit(root, 'link', (node: Link) => {
promises.push(processLinkNode({node, ...options}));
promises.push(processLinkNode(node, options));
});
await Promise.all(promises);
};
Expand Down
10 changes: 5 additions & 5 deletions packages/docusaurus-plugin-content-blog/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -465,7 +462,10 @@ export default function pluginContentBlog(
rehypePlugins,
beforeDefaultRemarkPlugins,
beforeDefaultRehypePlugins,
staticDir: path.join(siteDir, STATIC_DIR_NAME),
staticDirs: siteConfig.staticDirectories.map((dir) =>
path.join(siteDir, dir),
),
siteDir,
isMDXPartial: createAbsoluteFilePathMatcher(
options.exclude,
contentDirs,
Expand Down
10 changes: 5 additions & 5 deletions packages/docusaurus-plugin-content-docs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -397,7 +394,10 @@ export default function pluginContentDocs(
rehypePlugins,
beforeDefaultRehypePlugins,
beforeDefaultRemarkPlugins,
staticDir: path.join(siteDir, STATIC_DIR_NAME),
staticDirs: siteConfig.staticDirectories.map((dir) =>
path.join(siteDir, dir),
),
siteDir,
isMDXPartial: createAbsoluteFilePathMatcher(
options.exclude,
contentDirs,
Expand Down
10 changes: 5 additions & 5 deletions packages/docusaurus-plugin-content-pages/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -209,7 +206,10 @@ export default function pluginContentPages(
rehypePlugins,
beforeDefaultRehypePlugins,
beforeDefaultRemarkPlugins,
staticDir: path.join(siteDir, STATIC_DIR_NAME),
staticDirs: siteConfig.staticDirectories.map((dir) =>
path.join(siteDir, dir),
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
),
siteDir,
isMDXPartial: createAbsoluteFilePathMatcher(
options.exclude,
contentDirs,
Expand Down
Loading