diff --git a/docs/configuration.md b/docs/configuration.md
index 1b2d04878..2b1bf3f04 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -616,3 +616,24 @@ window.$docsify = {
topMargin: 90, // default: 0
};
```
+
+## rootRelativeImageURL
+
+- Type: `Boolean|String`
+- Default: `false`
+
+Configure image URL rendering behavior when inserting markdown images when the image URL is root-relative, i.e. starts with '/'.
+
+By default, Docsify resolves all image paths against the current page's parent path.
+
+E.g. if the current path is `/advanced/guide`, `` would render as `
`.
+
+```js
+ window.$docsify = {
+ rootRelativeImageURL = false // default behaviour
+
+ rootRelativeImageURL = true //  renders as
+
+ rootRelativeImageURL = 'my-root-path' //  renders as  {
crossOriginLinks: [],
relativePath: false,
topMargin: 0,
+ rootRelativeImageURL: false,
},
typeof window.$docsify === 'function'
? window.$docsify(vm)
@@ -79,6 +80,10 @@ export default function(vm) {
config.name = '';
}
+ if (config.rootRelativeImageURL === true) {
+ config.rootRelativeImageURL = '';
+ }
+
window.$docsify = config;
return config;
diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js
index 17e0a8144..d14b12e79 100644
--- a/src/core/render/compiler.js
+++ b/src/core/render/compiler.js
@@ -68,6 +68,7 @@ export class Compiler {
this.linkRel =
this.linkTarget === '_blank' ? config.externalLinkRel || 'noopener' : '';
this.contentBase = router.getBasePath();
+ this.rootRelativeImageURL = config.rootRelativeImageURL;
const renderer = this._initRenderer();
this.heading = renderer.heading;
@@ -193,7 +194,13 @@ export class Compiler {
_initRenderer() {
const renderer = new marked.Renderer();
- const { linkTarget, linkRel, router, contentBase } = this;
+ const {
+ linkTarget,
+ linkRel,
+ router,
+ contentBase,
+ rootRelativeImageURL,
+ } = this;
const _self = this;
const origin = {};
@@ -249,7 +256,12 @@ export class Compiler {
compilerClass: _self,
});
origin.paragraph = paragraphCompiler({ renderer });
- origin.image = imageCompiler({ renderer, contentBase, router });
+ origin.image = imageCompiler({
+ renderer,
+ contentBase,
+ router,
+ rootRelativeImageURL,
+ });
origin.list = taskListCompiler({ renderer });
origin.listitem = taskListItemCompiler({ renderer });
diff --git a/src/core/render/compiler/image.js b/src/core/render/compiler/image.js
index 982e5e55e..97510e310 100644
--- a/src/core/render/compiler/image.js
+++ b/src/core/render/compiler/image.js
@@ -1,7 +1,17 @@
import { getAndRemoveConfig } from '../utils';
-import { isAbsolutePath, getPath, getParentPath } from '../../router/util';
-
-export const imageCompiler = ({ renderer, contentBase, router }) =>
+import {
+ isAbsolutePath,
+ isPathRootRelative,
+ getPath,
+ getParentPath,
+} from '../../router/util';
+
+export const imageCompiler = ({
+ renderer,
+ contentBase,
+ router,
+ rootRelativeImageURL,
+}) =>
(renderer.image = (href, title, text) => {
let url = href;
let attrs = [];
@@ -35,7 +45,10 @@ export const imageCompiler = ({ renderer, contentBase, router }) =>
}
if (!isAbsolutePath(href)) {
- url = getPath(contentBase, getParentPath(router.getCurrentPath()), href);
+ url =
+ isPathRootRelative(href) && rootRelativeImageURL !== false
+ ? getPath('/' + String(rootRelativeImageURL), href)
+ : getPath(contentBase, getParentPath(router.getCurrentPath()), href);
}
if (attrs.length > 0) {
diff --git a/src/core/router/util.js b/src/core/router/util.js
index fc3e2f79d..4ddd3c847 100644
--- a/src/core/router/util.js
+++ b/src/core/router/util.js
@@ -44,6 +44,10 @@ export const isAbsolutePath = cached(path => {
return /(:|(\/{2}))/g.test(path);
});
+export const isPathRootRelative = cached(path => {
+ return path[0] === '/';
+});
+
export const removeParams = cached(path => {
return path.split(/[?#]/)[0];
});
diff --git a/test/_helper.js b/test/_helper.js
index 39ee5bdb1..c513ffe2b 100644
--- a/test/_helper.js
+++ b/test/_helper.js
@@ -54,7 +54,9 @@ module.exports.init = function(
const rootPath = path.join(__dirname, 'fixtures', fixture);
- const dom = initJSDOM(markup);
+ const runScriptInJSDom =
+ Object.values(config).length !== 0 ? 'dangerously' : undefined;
+ const dom = initJSDOM(markup, { runScripts: runScriptInJSDom });
dom.reconfigure({ url: 'file:///' + rootPath });
// Mimic src/core/index.js but for Node.js
diff --git a/test/unit/render.test.js b/test/unit/render.test.js
index fe5bade7f..94a1287a7 100644
--- a/test/unit/render.test.js
+++ b/test/unit/render.test.js
@@ -1,3 +1,4 @@
+const path = require('path');
const { expect } = require('chai');
const { init, expectSameDom } = require('../_helper');
@@ -104,6 +105,23 @@ describe('render', function() {
);
});
+ it('relative image url', async function() {
+ const { docsify } = await init();
+ const output = docsify.compiler.compile(
+ ''
+ );
+ const expectedCompiledPath = path.posix.join(
+ // must use posix here because if run on windows, paths are joined using backslash (\)
+ __dirname,
+ '../fixtures/default',
+ 'some-path/image.png'
+ );
+ expectSameDom(
+ output,
+ `<p><img src=)
`
+ );
+ });
+
it('class', async function() {
const { docsify } = await init();
const output = docsify.compiler.compile(
@@ -165,6 +183,53 @@ describe('render', function() {
);
});
});
+
+ describe('compiling root-relative image src path', async function() {
+ it('behaves normally if config.rootRelativeImageURL is set to false', async function() {
+ const { docsify } = await init('default', {
+ rootRelativeImageURL: false,
+ });
+ const rootRelativePathOutput = docsify.compiler.compile(
+ ''
+ );
+ const expectedCompiledPath = path.posix.join(
+ // must use posix here because if run on windows, file paths use backslash (\)
+ __dirname,
+ '../fixtures/default',
+ 'some-path/image.png'
+ );
+ expectSameDom(
+ rootRelativePathOutput,
+ `
`
+ );
+ });
+
+ it('only uses the image href if config.rootRelativeImageURL is set to true', async function() {
+ const { docsify } = await init('default', {
+ rootRelativeImageURL: true,
+ });
+ const rootRelativePathOutput = docsify.compiler.compile(
+ ''
+ );
+ expectSameDom(
+ rootRelativePathOutput,
+ `
`
+ );
+ });
+
+ it('uses config.rootRelativeImageURL as a prefix for the compiled image path, if passed as a string', async function() {
+ const { docsify } = await init('default', {
+ rootRelativeImageURL: 'docs',
+ });
+ const rootRelativePathOutput = docsify.compiler.compile(
+ ''
+ );
+ expectSameDom(
+ rootRelativePathOutput,
+ `
`
+ );
+ });
+ });
});
describe('heading', function() {