mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-24 22:46:57 +02:00
fix(v2): handle multiple asset links in one line properly (#3653)
* fix(v2): handle multiple asset links in one line properly * Fixes and improvements * Make TypeScript happy * Use relative path for image link * Add example for JSX element inside asset link
This commit is contained in:
parent
cf99862d29
commit
999ae5759c
9 changed files with 79 additions and 71 deletions
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
const toString = require('mdast-util-to-string');
|
const toString = require('mdast-util-to-string');
|
||||||
const visit = require('unist-util-visit');
|
const visit = require('unist-util-visit');
|
||||||
const escapeHtml = require('escape-html');
|
const {toValue} = require('../utils');
|
||||||
|
|
||||||
/** @typedef {import('@docusaurus/types').MarkdownRightTableOfContents} TOC */
|
/** @typedef {import('@docusaurus/types').MarkdownRightTableOfContents} TOC */
|
||||||
/** @typedef {import('unist').Node} Node */
|
/** @typedef {import('unist').Node} Node */
|
||||||
|
@ -23,33 +23,6 @@ const escapeHtml = require('escape-html');
|
||||||
* @property {StringValuedNode[]} children
|
* @property {StringValuedNode[]} children
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// https://github.com/syntax-tree/mdast#heading
|
|
||||||
/**
|
|
||||||
* @param {StringValuedNode | undefined} node
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function toValue(node) {
|
|
||||||
if (node && node.type) {
|
|
||||||
switch (node.type) {
|
|
||||||
case 'text':
|
|
||||||
return escapeHtml(node.value);
|
|
||||||
case 'heading':
|
|
||||||
return node.children.map(toValue).join('');
|
|
||||||
case 'inlineCode':
|
|
||||||
return `<code>${escapeHtml(node.value)}</code>`;
|
|
||||||
case 'emphasis':
|
|
||||||
return `<em>${node.children.map(toValue).join('')}</em>`;
|
|
||||||
case 'strong':
|
|
||||||
return `<strong>${node.children.map(toValue).join('')}</strong>`;
|
|
||||||
case 'delete':
|
|
||||||
return `<del>${node.children.map(toValue).join('')}</del>`;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return toString(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visit all headings. We `slug` all headings (to account for
|
// Visit all headings. We `slug` all headings (to account for
|
||||||
// duplicates), but only take h2 and h3 headings.
|
// duplicates), but only take h2 and h3 headings.
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,11 +12,11 @@ exports[`transformImage plugin pathname protocol 1`] = `
|
||||||
exports[`transformImage plugin transform md images to <img /> 1`] = `
|
exports[`transformImage plugin transform md images to <img /> 1`] = `
|
||||||
"
|
"
|
||||||
|
|
||||||
<img src={require(\\"!url-loader?limit=10000&name=assets/images/[name]-[hash].[ext]&fallback=file-loader!./img.png\\").default} />
|
<img src={require(\\"!url-loader?limit=10000&name=assets/images/[name]-[hash].[ext]&fallback=file-loader!./img.png\\").default} />
|
||||||
|
|
||||||
<img alt={\\"img\\"} src={require(\\"!url-loader?limit=10000&name=assets/images/[name]-[hash].[ext]&fallback=file-loader!./img.png\\").default} />
|
<img alt={\\"img\\"} src={require(\\"!url-loader?limit=10000&name=assets/images/[name]-[hash].[ext]&fallback=file-loader!./img.png\\").default} />
|
||||||
|
|
||||||
<img alt={\\"img\\"} src={require(\\"!url-loader?limit=10000&name=assets/images/[name]-[hash].[ext]&fallback=file-loader!./img.png\\").default} title={\\"Title\\"} /> <img alt={\\"img\\"} src={require(\\"!url-loader?limit=10000&name=assets/images/[name]-[hash].[ext]&fallback=file-loader!packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/img.png\\").default} />
|
<img alt={\\"img\\"} src={require(\\"!url-loader?limit=10000&name=assets/images/[name]-[hash].[ext]&fallback=file-loader!./img.png\\").default} title=\\"Title\\" /> <img alt={\\"img\\"} src={require(\\"!url-loader?limit=10000&name=assets/images/[name]-[hash].[ext]&fallback=file-loader!packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/fixtures/img.png\\").default} />
|
||||||
|
|
||||||
## Heading
|
## Heading
|
||||||
|
|
||||||
|
@ -24,6 +24,6 @@ exports[`transformImage plugin transform md images to <img /> 1`] = `
|
||||||

|

|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
<img alt={\\"img\\"} src={require(\\"!url-loader?limit=10000&name=assets/images/[name]-[hash].[ext]&fallback=file-loader!./img.png\\").default} />
|
<img alt={\\"img\\"} src={require(\\"!url-loader?limit=10000&name=assets/images/[name]-[hash].[ext]&fallback=file-loader!./img.png\\").default} />
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -9,6 +9,7 @@ const visit = require('unist-util-visit');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
const escapeHtml = require('escape-html');
|
||||||
const {getFileLoaderUtils} = require('@docusaurus/core/lib/webpack/utils');
|
const {getFileLoaderUtils} = require('@docusaurus/core/lib/webpack/utils');
|
||||||
const {posixPath} = require('@docusaurus/utils');
|
const {posixPath} = require('@docusaurus/utils');
|
||||||
|
|
||||||
|
@ -18,11 +19,11 @@ const {
|
||||||
|
|
||||||
const createJSX = (node, pathUrl) => {
|
const createJSX = (node, pathUrl) => {
|
||||||
node.type = 'jsx';
|
node.type = 'jsx';
|
||||||
node.value = `<img ${node.alt ? `alt={"${node.alt}"}` : ''} ${
|
node.value = `<img ${node.alt ? `alt={"${node.alt}"} ` : ''}${
|
||||||
node.url
|
node.url
|
||||||
? `src={require("${inlineMarkdownImageFileLoader}${pathUrl}").default}`
|
? `src={require("${inlineMarkdownImageFileLoader}${pathUrl}").default}`
|
||||||
: ''
|
: ''
|
||||||
} ${node.title ? `title={"${node.title}"}` : ''} />`;
|
}${node.title ? ` title="${escapeHtml(node.title)}"` : ''} />`;
|
||||||
|
|
||||||
if (node.url) {
|
if (node.url) {
|
||||||
delete node.url;
|
delete node.url;
|
||||||
|
|
|
@ -10,11 +10,11 @@ exports[`transformAsset plugin pathname protocol 1`] = `
|
||||||
exports[`transformAsset plugin transform md links to <a /> 1`] = `
|
exports[`transformAsset plugin transform md links to <a /> 1`] = `
|
||||||
"[asset](https://example.com/asset.pdf)
|
"[asset](https://example.com/asset.pdf)
|
||||||
|
|
||||||
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./asset.pdf').default} ></a>
|
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./asset.pdf').default}></a>
|
||||||
|
|
||||||
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./asset.pdf').default} >asset</a>
|
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./asset.pdf').default}>asset</a>
|
||||||
|
|
||||||
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./asset.pdf').default} title={Title} >asset</a> 
|
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./asset.pdf').default}title=\\"Title\\">asset</a>
|
||||||
|
|
||||||
## Heading
|
## Heading
|
||||||
|
|
||||||
|
@ -26,10 +26,16 @@ exports[`transformAsset plugin transform md links to <a /> 1`] = `
|
||||||
|
|
||||||
[assets](/github/!file-loader!/assets.pdf)
|
[assets](/github/!file-loader!/assets.pdf)
|
||||||
|
|
||||||
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./asset.pdf').default} >asset</a>
|
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./asset.pdf').default}>asset</a>
|
||||||
|
|
||||||
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./static/staticAsset.pdf').default} >staticAsset.pdf</a>
|
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./static/staticAsset.pdf').default}>staticAsset.pdf</a>
|
||||||
|
|
||||||
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./static/staticAsset.pdf').default} >@site/static/staticAsset.pdf</a>
|
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./static/staticAsset.pdf').default}>@site/static/staticAsset.pdf</a>
|
||||||
|
|
||||||
|
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./static/staticAsset.pdf').default}>Just staticAsset.pdf</a>, and <a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./static/staticAsset.pdf').default}><strong>awesome</strong> staticAsset 2.pdf 'It is really "AWESOME"'</a>, but also <a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./static/staticAsset.pdf').default}>coded <code>staticAsset 3.pdf</code></a>
|
||||||
|
|
||||||
|
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./static/staticAssetImage.png').default}><img alt={\\"Clickable Docusaurus logo\\"} src={require(\\"!url-loader?limit=10000&name=assets/images/[name]-[hash].[ext]&fallback=file-loader!./static/staticAssetImage.png\\").default} /></a>
|
||||||
|
|
||||||
|
<a target=\\"_blank\\" href={require('!file-loader?name=assets/files/[name]-[hash].[ext]!./asset.pdf').default}><span style={{color: \\"red\\"}}>Stylized link to asset file</span></a>
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
[asset](./asset.pdf)
|
[asset](./asset.pdf)
|
||||||
|
|
||||||
[asset](asset.pdf 'Title') 
|
[asset](asset.pdf 'Title')
|
||||||
|
|
||||||
## Heading
|
## Heading
|
||||||
|
|
||||||
|
@ -21,3 +21,9 @@
|
||||||
[staticAsset.pdf](/staticAsset.pdf)
|
[staticAsset.pdf](/staticAsset.pdf)
|
||||||
|
|
||||||
[@site/static/staticAsset.pdf](@site/static/staticAsset.pdf)
|
[@site/static/staticAsset.pdf](@site/static/staticAsset.pdf)
|
||||||
|
|
||||||
|
[Just staticAsset.pdf](/staticAsset.pdf), and [**awesome** staticAsset 2.pdf 'It is really "AWESOME"'](/staticAsset.pdf), but also [coded `staticAsset 3.pdf`](/staticAsset.pdf)
|
||||||
|
|
||||||
|
[](/staticAssetImage.png)
|
||||||
|
|
||||||
|
[<span style={{color: "red"}}>Stylized link to asset file</span>](./asset.pdf)
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
|
@ -10,15 +10,15 @@ import remark from 'remark';
|
||||||
import mdx from 'remark-mdx';
|
import mdx from 'remark-mdx';
|
||||||
import vfile from 'to-vfile';
|
import vfile from 'to-vfile';
|
||||||
import plugin from '..';
|
import plugin from '..';
|
||||||
import slug from '../../slug';
|
import transformImage from '../../transformImage';
|
||||||
|
|
||||||
const processFixture = async (name, options) => {
|
const processFixture = async (name, options) => {
|
||||||
const path = join(__dirname, 'fixtures', `${name}.md`);
|
const path = join(__dirname, 'fixtures', `${name}.md`);
|
||||||
const staticDir = join(__dirname, 'fixtures', 'static');
|
const staticDir = join(__dirname, 'fixtures', 'static');
|
||||||
const file = await vfile.read(path);
|
const file = await vfile.read(path);
|
||||||
const result = await remark()
|
const result = await remark()
|
||||||
.use(slug)
|
|
||||||
.use(mdx)
|
.use(mdx)
|
||||||
|
.use(transformImage, {...options, filePath: path, staticDir})
|
||||||
.use(plugin, {...options, filePath: path, staticDir})
|
.use(plugin, {...options, filePath: path, staticDir})
|
||||||
.process(file);
|
.process(file);
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ const visit = require('unist-util-visit');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
const escapeHtml = require('escape-html');
|
||||||
|
const {toValue} = require('../utils');
|
||||||
const {getFileLoaderUtils} = require('@docusaurus/core/lib/webpack/utils');
|
const {getFileLoaderUtils} = require('@docusaurus/core/lib/webpack/utils');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -35,7 +37,9 @@ async function ensureAssetFileExist(fileSystemAssetPath, sourceFilePath) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// transform the link node to a jsx link with a require() call
|
// transform the link node to a jsx link with a require() call
|
||||||
function toAssetRequireNode({node, index, parent, filePath, requireAssetPath}) {
|
function toAssetRequireNode({node, filePath, requireAssetPath}) {
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
|
||||||
let relativeRequireAssetPath = posixPath(
|
let relativeRequireAssetPath = posixPath(
|
||||||
path.relative(path.dirname(filePath), requireAssetPath),
|
path.relative(path.dirname(filePath), requireAssetPath),
|
||||||
);
|
);
|
||||||
|
@ -45,35 +49,18 @@ function toAssetRequireNode({node, index, parent, filePath, requireAssetPath}) {
|
||||||
? relativeRequireAssetPath
|
? relativeRequireAssetPath
|
||||||
: `./${relativeRequireAssetPath}`;
|
: `./${relativeRequireAssetPath}`;
|
||||||
|
|
||||||
const hrefProp = `require('${inlineMarkdownLinkFileLoader}${relativeRequireAssetPath}').default`;
|
const href = `require('${inlineMarkdownLinkFileLoader}${relativeRequireAssetPath}').default`;
|
||||||
|
const children = (node.children || []).map((n) => toValue(n)).join('');
|
||||||
|
const title = node.title ? `title="${escapeHtml(node.title)}"` : '';
|
||||||
|
|
||||||
node.type = 'jsx';
|
node.type = 'jsx';
|
||||||
|
node.value = `<a target="_blank" href={${href}}${title}>${children}</a>`;
|
||||||
node.value = `<a target="_blank" href={${hrefProp}} ${
|
|
||||||
node.title ? `title={${node.title}}` : ''
|
|
||||||
} >`;
|
|
||||||
|
|
||||||
const linkText = (node.children[0] && node.children[0].value) || '';
|
|
||||||
delete node.children;
|
|
||||||
|
|
||||||
parent.children.splice(index + 1, 0, {
|
|
||||||
type: 'text',
|
|
||||||
value: linkText,
|
|
||||||
});
|
|
||||||
|
|
||||||
parent.children.splice(index + 2, 0, {type: 'jsx', value: '</a>'});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the link looks like an asset link, we'll link to the asset,
|
// 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)
|
// and use a require("assetUrl") (using webpack url-loader/file-loader)
|
||||||
// instead of navigating to such link
|
// instead of navigating to such link
|
||||||
async function convertToAssetLinkIfNeeded({
|
async function convertToAssetLinkIfNeeded({node, staticDir, filePath}) {
|
||||||
node,
|
|
||||||
index,
|
|
||||||
parent,
|
|
||||||
staticDir,
|
|
||||||
filePath,
|
|
||||||
}) {
|
|
||||||
const assetPath = node.url;
|
const assetPath = node.url;
|
||||||
|
|
||||||
const hasSiteAlias = assetPath.startsWith('@site/');
|
const hasSiteAlias = assetPath.startsWith('@site/');
|
||||||
|
@ -89,8 +76,6 @@ async function convertToAssetLinkIfNeeded({
|
||||||
function toAssetLinkNode(requireAssetPath) {
|
function toAssetLinkNode(requireAssetPath) {
|
||||||
toAssetRequireNode({
|
toAssetRequireNode({
|
||||||
node,
|
node,
|
||||||
index,
|
|
||||||
parent,
|
|
||||||
filePath,
|
filePath,
|
||||||
requireAssetPath,
|
requireAssetPath,
|
||||||
});
|
});
|
||||||
|
@ -117,7 +102,7 @@ async function convertToAssetLinkIfNeeded({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processLinkNode({node, index, parent, filePath, staticDir}) {
|
async function processLinkNode({node, _index, _parent, filePath, staticDir}) {
|
||||||
if (!node.url) {
|
if (!node.url) {
|
||||||
// try to improve error feedback
|
// try to improve error feedback
|
||||||
// see https://github.com/facebook/docusaurus/issues/3309#issuecomment-690371675
|
// see https://github.com/facebook/docusaurus/issues/3309#issuecomment-690371675
|
||||||
|
@ -139,8 +124,6 @@ async function processLinkNode({node, index, parent, filePath, staticDir}) {
|
||||||
|
|
||||||
await convertToAssetLinkIfNeeded({
|
await convertToAssetLinkIfNeeded({
|
||||||
node,
|
node,
|
||||||
index,
|
|
||||||
parent,
|
|
||||||
staticDir,
|
staticDir,
|
||||||
filePath,
|
filePath,
|
||||||
});
|
});
|
||||||
|
|
39
packages/docusaurus-mdx-loader/src/remark/utils/index.js
Normal file
39
packages/docusaurus-mdx-loader/src/remark/utils/index.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const escapeHtml = require('escape-html');
|
||||||
|
const toString = require('mdast-util-to-string');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {StringValuedNode | undefined} node
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function toValue(node) {
|
||||||
|
if (node && node.type) {
|
||||||
|
switch (node.type) {
|
||||||
|
case 'text':
|
||||||
|
return escapeHtml(node.value);
|
||||||
|
case 'heading':
|
||||||
|
return node.children.map(toValue).join('');
|
||||||
|
case 'inlineCode':
|
||||||
|
return `<code>${escapeHtml(node.value)}</code>`;
|
||||||
|
case 'emphasis':
|
||||||
|
return `<em>${node.children.map(toValue).join('')}</em>`;
|
||||||
|
case 'strong':
|
||||||
|
return `<strong>${node.children.map(toValue).join('')}</strong>`;
|
||||||
|
case 'delete':
|
||||||
|
return `<del>${node.children.map(toValue).join('')}</del>`;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return toString(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
toValue,
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue