Skip to content

Commit

Permalink
feat(v2): support for adding relative images and handling broken imag…
Browse files Browse the repository at this point in the history
…e links (#3069)

* all relative path in image url

* throw error if file doesn't present

* better error

* add @docusaurus/core to deps

* fix test
  • Loading branch information
Anshul Goyal authored Jul 21, 2020
1 parent 15e73da commit 3155dc3
Show file tree
Hide file tree
Showing 15 changed files with 162 additions and 4 deletions.
5 changes: 5 additions & 0 deletions packages/docusaurus-init/templates/bootstrap/docs/doc1.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ Reference-style: ![alt text][logo]

[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2'

Images from any folder can be used by providing path to file. Path should be relative to markdown file.

![img](../static/img/logo.svg)


---

## Code
Expand Down
4 changes: 4 additions & 0 deletions packages/docusaurus-init/templates/classic/docs/doc1.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ Reference-style: ![alt text][logo]

[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2'

Images from any folder can be used by providing path to file. Path should be relative to markdown file.

![img](../static/img/logo.svg)

---

## Code
Expand Down
4 changes: 4 additions & 0 deletions packages/docusaurus-init/templates/facebook/docs/doc1.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ Reference-style: ![alt text][logo]

[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2'

Images from any folder can be used by providing path to file. Path should be relative to markdown file.

![img](../static/img/logo.svg)

---

## Code
Expand Down
5 changes: 5 additions & 0 deletions packages/docusaurus-mdx-loader/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const matter = require('gray-matter');
const stringifyObject = require('stringify-object');
const slug = require('./remark/slug');
const rightToc = require('./remark/rightToc');
const relativePath = require('./remark/transformImage');

const DEFAULT_OPTIONS = {
rehypePlugins: [],
Expand All @@ -29,6 +30,10 @@ module.exports = async function (fileString) {
remarkPlugins: [
...(reqOptions.beforeDefaultRemarkPlugins || []),
...DEFAULT_OPTIONS.remarkPlugins,
[
relativePath,
{staticDir: reqOptions.staticDir, filePath: this.resourcePath},
],
...(reqOptions.remarkPlugins || []),
],
rehypePlugins: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`transform md images to <img /> 1`] = `
"![img](https://example.com/img.png)
<img src={require(\\"!url-loader!./img.png\\").default} />
<img alt={\\"img\\"} src={require(\\"!url-loader!./img.png\\").default} />
<img alt={\\"img\\"} src={require(\\"!url-loader!./img.png\\").default} title={\\"Title\\"} /> ![img](/img.png)
## Heading
\`\`\`md
![img](./img.png)
\`\`\`
<img alt={\\"img\\"} src={require(\\"!url-loader!./img.png\\").default} />
"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
![img](/img/post.png)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
![img](https://example.com/img.png)

![](./img.png)

![img](./img.png)

![img](./img.png 'Title') ![img](/img.png)

## Heading

```md
![img](./img.png)
```

![img](img.png)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* 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 {join} from 'path';
import remark from 'remark';
import mdx from 'remark-mdx';
import vfile from 'to-vfile';
import plugin from '../index';
import slug from '../../slug/index';

const processFixture = async (name, options) => {
const path = join(__dirname, 'fixtures', `${name}.md`);
const file = await vfile.read(path);
const result = await remark()
.use(slug)
.use(mdx)
.use(plugin, {...options, filePath: path})
.process(file);

return result.toString();
};

test('fail if image donot exists', async () => {
expect(
processFixture('fail', {staticDir: join(__dirname, 'fixtures')}),
).rejects.toBeInstanceOf(Error);
});

test('transform md images to <img />', async () => {
const result = await processFixture('img', {
staticDir: join(__dirname, 'fixtures'),
});
expect(result).toMatchSnapshot();
});
59 changes: 59 additions & 0 deletions packages/docusaurus-mdx-loader/src/remark/transformImage/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* 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 visit = require('unist-util-visit');
const path = require('path');
const url = require('url');
const fs = require('fs-extra');

const plugin = (options) => {
const transformer = (root) => {
visit(root, 'image', (node) => {
if (!url.parse(node.url).protocol) {
if (!path.isAbsolute(node.url)) {
if (
!fs.existsSync(path.join(path.dirname(options.filePath), node.url))
) {
throw new Error(
`Image ${path.join(
path.dirname(options.filePath),
node.url,
)} used in ${options.filePath} not found.`,
);
}
node.type = 'jsx';
node.value = `<img ${node.alt ? `alt={"${node.alt}"}` : ''} ${
node.url
? `src={require("!url-loader!${
node.url.startsWith('./') ? node.url : `./${node.url}`
}").default}`
: ''
} ${node.title ? `title={"${node.title}"}` : ''} />`;
if (node.url) {
delete node.url;
}
if (node.alt) {
delete node.alt;
}
if (node.title) {
delete node.title;
}
} else if (!fs.existsSync(path.join(options.staticDir, node.url))) {
throw new Error(
`Image ${path.join(options.staticDir, node.url)} used in ${
options.filePath
} not found.`,
);
}
}
});
};

return transformer;
};

module.exports = plugin;
2 changes: 1 addition & 1 deletion packages/docusaurus-plugin-content-blog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@docusaurus/mdx-loader": "^2.0.0-alpha.58",
"@docusaurus/types": "^2.0.0-alpha.58",
"@docusaurus/utils": "^2.0.0-alpha.58",
"@docusaurus/core": "2.0.0-alpha.58",
"@hapi/joi": "^17.1.1",
"feed": "^4.1.0",
"fs-extra": "^8.1.0",
Expand All @@ -28,7 +29,6 @@
"remark-admonitions": "^1.2.1"
},
"peerDependencies": {
"@docusaurus/core": "^2.0.0",
"react": "^16.8.4",
"react-dom": "^16.8.4"
},
Expand Down
2 changes: 2 additions & 0 deletions packages/docusaurus-plugin-content-blog/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import kebabCase from 'lodash.kebabcase';
import path from 'path';
import admonitions from 'remark-admonitions';
import {normalizeUrl, docuHash, aliasedSitePath} from '@docusaurus/utils';
import {STATIC_DIR_NAME} from '@docusaurus/core/lib/constants';
import {ValidationError} from '@hapi/joi';

import {
Expand Down Expand Up @@ -363,6 +364,7 @@ export default function pluginContentBlog(
options: {
remarkPlugins,
rehypePlugins,
staticDir: path.join(siteDir, STATIC_DIR_NAME),
// Note that metadataPath must be the same/in-sync as
// the path from createData for each MDX.
metadataPath: (mdxPath: string) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/docusaurus-plugin-content-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
"lodash.pickby": "^4.6.0",
"lodash.sortby": "^4.6.0",
"remark-admonitions": "^1.2.1",
"shelljs": "^0.8.4"
"shelljs": "^0.8.4",
"@docusaurus/core": "^2.0.0-alpha.58"
},
"peerDependencies": {
"@docusaurus/core": "^2.0.0",
"react": "^16.8.4",
"react-dom": "^16.8.4"
},
Expand Down
2 changes: 2 additions & 0 deletions packages/docusaurus-plugin-content-docs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import globby from 'globby';
import fs from 'fs-extra';
import path from 'path';
import admonitions from 'remark-admonitions';
import {STATIC_DIR_NAME} from '@docusaurus/core/lib/constants';
import {
normalizeUrl,
docuHash,
Expand Down Expand Up @@ -524,6 +525,7 @@ Available document ids=
options: {
remarkPlugins,
rehypePlugins,
staticDir: path.join(siteDir, STATIC_DIR_NAME),
metadataPath: (mdxPath: string) => {
// Note that metadataPath must be the same/in-sync as
// the path from createData for each MDX.
Expand Down
5 changes: 4 additions & 1 deletion website/docs/markdown-features.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -959,8 +959,11 @@ You can use images by requiring them and using an image tag through MDX:
# My markdown page

<img src={require('./assets/docusaurus-asset-example-banner.png').default} />
```

or

![](./assets/docusaurus-asset-example-banner.png)
```
The ES imports syntax also works:
```mdx
Expand Down

0 comments on commit 3155dc3

Please sign in to comment.