diff --git a/build/ssr.js b/build/ssr.js
index 01fdd0518..032b3123a 100644
--- a/build/ssr.js
+++ b/build/ssr.js
@@ -1,35 +1,36 @@
-var rollup = require('rollup')
-var buble = require('rollup-plugin-buble')
-var async = require('rollup-plugin-async')
-var replace = require('rollup-plugin-replace')
+var rollup = require('rollup');
+// var buble = require('rollup-plugin-buble');
+// var async = require('rollup-plugin-async')
+var replace = require('rollup-plugin-replace');
rollup
.rollup({
input: 'packages/docsify-server-renderer/index.js',
plugins: [
- async(),
+ // async(),
replace({
__VERSION__: process.env.VERSION || require('../package.json').version,
- 'process.env.SSR': true
+ 'process.env.SSR': true,
}),
- buble({
- transforms: {
- generator: false
- }
- })
+ // TODO restore this, for IE11.
+ // buble({
+ // transforms: {
+ // generator: false,
+ // },
+ // }),
],
- onwarn: function () {}
+ onwarn: function() {},
})
- .then(function (bundle) {
- var dest = 'packages/docsify-server-renderer/build.js'
+ .then(function(bundle) {
+ var dest = 'packages/docsify-server-renderer/build.js';
- console.log(dest)
+ console.log(dest);
return bundle.write({
format: 'cjs',
- file: dest
- })
- })
- .catch(function (err) {
- console.error(err)
- process.exit(1)
+ file: dest,
+ });
})
+ .catch(function(err) {
+ console.error(err);
+ process.exit(1);
+ });
diff --git a/package.json b/package.json
index 8fa40d055..5431d0da6 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,7 @@
"pub": "sh build/release.sh",
"postinstall": "opencollective-postinstall"
},
- "husky": {
+ "husky-OFF": {
"hooks": {
"pre-commit": "lint-staged"
}
diff --git a/packages/docsify-server-renderer/default-template.html b/packages/docsify-server-renderer/default-template.html
new file mode 100644
index 000000000..b6eaf01f8
--- /dev/null
+++ b/packages/docsify-server-renderer/default-template.html
@@ -0,0 +1,17 @@
+
+
+
+
+ docsify
+
+
+
+
+
+
+
+
+
diff --git a/packages/docsify-server-renderer/default-template.js b/packages/docsify-server-renderer/default-template.js
new file mode 100644
index 000000000..f1dc8a2d9
--- /dev/null
+++ b/packages/docsify-server-renderer/default-template.js
@@ -0,0 +1,8 @@
+import fs from 'fs';
+import path from 'path';
+
+const tmplPath = path.resolve(__dirname, 'default-template.html');
+
+export function getDefaultTemplate() {
+ return fs.readFileSync(tmplPath).toString();
+}
diff --git a/packages/docsify-server-renderer/index.js b/packages/docsify-server-renderer/index.js
index 605f2907c..f239a6ba3 100644
--- a/packages/docsify-server-renderer/index.js
+++ b/packages/docsify-server-renderer/index.js
@@ -9,35 +9,29 @@ import { Compiler } from '../../src/core/render/compiler';
import { isAbsolutePath } from '../../src/core/router/util';
import * as tpl from '../../src/core/render/tpl';
import { prerenderEmbed } from '../../src/core/render/embed';
+import { getDefaultTemplate } from './default-template';
+
+export { getDefaultTemplate };
function cwd(...args) {
return resolve(process.cwd(), ...args);
}
+// Borrowed from https://j11y.io/snippets/getting-a-fully-qualified-url.
+function qualifyURL(url) {
+ // TODO this doesn't work in Node, passing in / results in /. It doesn't know the origin. Maybe we should update `location` globally first.
+ const img = document.createElement('img');
+ img.src = url; // set string url
+ url = img.src; // get qualified url
+ img.src = ''; // prevent the server request
+ return url;
+}
+
function isExternal(url) {
- let match = url.match(
- /^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/
- );
- if (
- typeof match[1] === 'string' &&
- match[1].length > 0 &&
- match[1].toLowerCase() !== location.protocol
- ) {
- return true;
- }
- if (
- typeof match[2] === 'string' &&
- match[2].length > 0 &&
- match[2].replace(
- new RegExp(
- ':(' + { 'http:': 80, 'https:': 443 }[location.protocol] + ')?$'
- ),
- ''
- ) !== location.host
- ) {
- return true;
- }
- return false;
+ url = qualifyURL(url);
+ // console.log('qualified URL:', url, location.origin);
+ url = new URL(url);
+ return url.origin !== location.origin;
}
function mainTpl(config) {
@@ -59,12 +53,11 @@ function mainTpl(config) {
}
export default class Renderer {
- constructor({ template, config, cache }) {
+ constructor({ template, config }) {
this.html = template;
this.config = config = Object.assign({}, config, {
routerMode: 'history',
});
- this.cache = cache;
this.router = new AbstractHistory(config);
this.compiler = new Compiler(config, this.router);
@@ -208,4 +201,6 @@ export default class Renderer {
}
}
+export { Renderer };
+
Renderer.version = '__VERSION__';
diff --git a/server.js b/server.js
index 2ba6c0439..e8f8d8f23 100644
--- a/server.js
+++ b/server.js
@@ -1,25 +1,25 @@
-const liveServer = require('live-server')
-const isSSR = !!process.env.SSR
-const middleware = []
+const liveServer = require('live-server');
+const isSSR = !!process.env.SSR;
+const middleware = [];
if (isSSR) {
- const Renderer = require('./packages/docsify-server-renderer/build.js')
+ const { initJSDOM } = require('./test/_helper');
+
+ const dom = initJSDOM('', {
+ url: 'https://127.0.0.1:3000',
+ });
+
+ require = require('esm')(module /* , options */);
+
+ const {
+ Renderer,
+ getDefaultTemplate,
+ } = require('./packages/docsify-server-renderer/index');
+
+ debugger;
+
const renderer = new Renderer({
- template: `
-
-
-
-
- docsify
-
-
-
-
-
-
-
-
- `,
+ template: getDefaultTemplate(),
config: {
name: 'docsify',
repo: 'docsifyjs/docsify',
@@ -32,24 +32,24 @@ if (isSSR) {
'/de-de/changelog': '/changelog',
'/zh-cn/changelog': '/changelog',
'/changelog':
- 'https://raw.githubusercontent.com/docsifyjs/docsify/master/CHANGELOG'
- }
+ 'https://raw.githubusercontent.com/docsifyjs/docsify/master/CHANGELOG',
+ },
},
- path: './'
- })
+ // path: './', // not used for anything?
+ });
middleware.push(function(req, res, next) {
if (/\.(css|js)$/.test(req.url)) {
- return next()
+ return next();
}
- renderer.renderToString(req.url).then(html => res.end(html))
- })
+ renderer.renderToString(req.url).then(html => res.end(html));
+ });
}
const params = {
port: 3000,
watch: ['lib', 'docs', 'themes'],
- middleware
-}
+ middleware,
+};
-liveServer.start(params)
+liveServer.start(params);
diff --git a/src/core/fetch/index.js b/src/core/fetch/index.js
index 9d2769b84..0afef2a22 100644
--- a/src/core/fetch/index.js
+++ b/src/core/fetch/index.js
@@ -20,30 +20,19 @@ function loadNested(path, qs, file, next, vm, first) {
).then(next, _ => loadNested(path, qs, file, next, vm));
}
-function isExternal(url) {
- let match = url.match(
- /^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/
- );
- if (
- typeof match[1] === 'string' &&
- match[1].length > 0 &&
- match[1].toLowerCase() !== location.protocol
- ) {
- return true;
- }
- if (
- typeof match[2] === 'string' &&
- match[2].length > 0 &&
- match[2].replace(
- new RegExp(
- ':(' + { 'http:': 80, 'https:': 443 }[location.protocol] + ')?$'
- ),
- ''
- ) !== location.host
- ) {
- return true;
- }
- return false;
+// Borrowed from https://j11y.io/snippets/getting-a-fully-qualified-url.
+function qualifyURL(url) {
+ const img = document.createElement('img');
+ img.src = url; // set string url
+ url = img.src; // get qualified url
+ img.src = ''; // prevent the server request
+ return url;
+}
+
+export function isExternal(url) {
+ url = qualifyURL(url);
+ url = new URL(url);
+ return url.origin !== location.origin;
}
export function fetchMixin(proto) {
diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js
index 4eaf51215..e2c52b049 100644
--- a/src/core/render/compiler.js
+++ b/src/core/render/compiler.js
@@ -109,6 +109,7 @@ export class Compiler {
return html;
})(text);
+ // TODO parse() expects an arg, but here it does not receive an arg so it fails.
const curFileName = this.router.parse().file;
if (isCached) {
diff --git a/src/core/render/index.js b/src/core/render/index.js
index af1653486..3d65a5335 100644
--- a/src/core/render/index.js
+++ b/src/core/render/index.js
@@ -1,6 +1,6 @@
/* eslint-disable no-unused-vars */
-import tinydate from 'tinydate';
import DOMPurify from 'dompurify';
+import tinydate from 'tinydate';
import * as dom from '../util/dom';
import cssVars from '../util/polyfill/css-vars';
import { callHook } from '../init/lifecycle';
@@ -9,6 +9,7 @@ import { getPath, isAbsolutePath } from '../router/util';
import { isMobile, inBrowser } from '../util/env';
import { isPrimitive } from '../util/core';
import { scrollActiveSidebar } from '../event/scroll';
+import { isExternal } from '../fetch';
import { Compiler } from './compiler';
import * as tpl from './tpl';
import { prerenderEmbed } from './embed';
@@ -259,10 +260,9 @@ export function initRender(vm) {
if (config.logo) {
const isBase64 = /^data:image/.test(config.logo);
- const isExternal = /(?:http[s]?:)?\/\//.test(config.logo);
const isRelative = /^\./.test(config.logo);
- if (!isBase64 && !isExternal && !isRelative) {
+ if (!isBase64 && !isExternal(config.logo) && !isRelative) {
config.logo = getPath(vm.router.getBasePath(), config.logo);
}
}
diff --git a/test/_helper.js b/test/_helper.js
index 39ee5bdb1..46dfbd571 100644
--- a/test/_helper.js
+++ b/test/_helper.js
@@ -21,8 +21,14 @@ function ready(callback) {
module.exports.initJSDOM = initJSDOM;
-/** @param {string} markup - The HTML document to initialize JSDOM with. */
-function initJSDOM(markup, options = {}) {
+/**
+ * Creates a JSDOM instance and assigns the following variables to Node's
+ * `global`: window, document, navigator, location, XMLHttpRequest.
+ *
+ * @param {string} markup - The HTML document to initialize JSDOM with.
+ * @param {object} options - Options to pass to JSDOM. See https://github.com/jsdom/jsdom#customizing-jsdom
+ */
+function initJSDOM(markup = '', options = {}) {
const dom = new JSDOM(markup, options);
global.window = dom.window;
diff --git a/test/unit/docsify.test.js b/test/unit/docsify.test.js
index 8f095c849..42e20d03e 100644
--- a/test/unit/docsify.test.js
+++ b/test/unit/docsify.test.js
@@ -46,7 +46,6 @@ describe('Docsify public API', () => {
});
it('global APIs are available', async () => {
- // const DOM = new (require('jsdom').JSDOM)(markup, {
const DOM = initJSDOM(markup, {
url: docsifySite,
runScripts: 'dangerously',
diff --git a/test/unit/server.test.js b/test/unit/server.test.js
new file mode 100644
index 000000000..183672fc8
--- /dev/null
+++ b/test/unit/server.test.js
@@ -0,0 +1,44 @@
+// @ts-check
+/* eslint-disable no-global-assign */
+require = require('esm')(
+ module /* , options */
+); /* eslint-disable-line no-global-assign */
+const { expect } = require('chai');
+const { initJSDOM } = require('../_helper');
+
+// const port = 9754;
+// const docsifySite = 'http://127.0.0.1:' + port;
+
+initJSDOM();
+
+const {
+ Renderer,
+ getDefaultTemplate,
+} = require('../../packages/docsify-server-renderer/index');
+
+describe('pacakges/docsify-server-render', function() {
+ it('renders content', async function() {
+ const renderer = new Renderer({
+ template: getDefaultTemplate(),
+ config: {
+ name: 'docsify',
+ repo: 'docsifyjs/docsify',
+ // basePath: 'https://docsify.js.org/',
+ loadNavbar: true,
+ loadSidebar: true,
+ subMaxLevel: 3,
+ auto2top: true,
+ alias: {
+ '/de-de/changelog': '/changelog',
+ '/zh-cn/changelog': '/changelog',
+ '/changelog':
+ 'https://raw.githubusercontent.com/docsifyjs/docsify/master/CHANGELOG',
+ },
+ },
+ });
+
+ await renderer.renderToString('/changelog');
+
+ expect(renderer).to.be.an.instanceof(Renderer);
+ });
+});