Skip to content

Commit 6be2965

Browse files
committed
WIP add tests for isExternal and SSR (to make sure SSR works and thus isExternal using DOMPUrify works within SSR)
1 parent 22f1b2b commit 6be2965

File tree

17 files changed

+191
-89
lines changed

17 files changed

+191
-89
lines changed

build/ssr.js

+18-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
var rollup = require('rollup')
2-
var buble = require('rollup-plugin-buble')
3-
var async = require('rollup-plugin-async')
4-
var replace = require('rollup-plugin-replace')
1+
var rollup = require('rollup');
2+
var buble = require('rollup-plugin-buble');
3+
var async = require('rollup-plugin-async');
4+
var replace = require('rollup-plugin-replace');
55

66
rollup
77
.rollup({
@@ -10,26 +10,26 @@ rollup
1010
async(),
1111
replace({
1212
__VERSION__: process.env.VERSION || require('../package.json').version,
13-
'process.env.SSR': true
13+
'process.env.SSR': true,
1414
}),
1515
buble({
1616
transforms: {
17-
generator: false
18-
}
19-
})
17+
generator: false,
18+
},
19+
}),
2020
],
21-
onwarn: function () {}
21+
onwarn: function() {},
2222
})
23-
.then(function (bundle) {
24-
var dest = 'packages/docsify-server-renderer/build.js'
23+
.then(function(bundle) {
24+
var dest = 'packages/docsify-server-renderer/build.js';
2525

26-
console.log(dest)
26+
console.log(dest);
2727
return bundle.write({
2828
format: 'cjs',
29-
file: dest
30-
})
31-
})
32-
.catch(function (err) {
33-
console.error(err)
34-
process.exit(1)
29+
file: dest,
30+
});
3531
})
32+
.catch(function(err) {
33+
console.error(err);
34+
process.exit(1);
35+
});

package-lock.json

+32
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"dev:ssr": "run-p serve:ssr watch:*",
2929
"lint": "eslint .",
3030
"fixlint": "eslint . --fix",
31-
"test": "mocha ./test/**/*.test.js",
31+
"test": "mocha `glob './test/**/*.test.js'` `glob './packages/docsify-server-renderer/src/**/*.test.js'`",
3232
"testServer": "node cypress/setup.js",
3333
"test:e2e": "start-server-and-test testServer http://localhost:3000 cy:run",
3434
"posttest:e2e": "rimraf cypress/fixtures/docs",
@@ -48,7 +48,7 @@
4848
"pub": "sh build/release.sh",
4949
"postinstall": "opencollective-postinstall"
5050
},
51-
"husky": {
51+
"husky-OFF": {
5252
"hooks": {
5353
"pre-commit": "lint-staged"
5454
}
@@ -71,6 +71,7 @@
7171
"babel-eslint": "^10.0.3",
7272
"chai": "^4.2.0",
7373
"chokidar": "^3.2.1",
74+
"cli-glob": "^0.1.0",
7475
"conventional-changelog-cli": "^2.0.25",
7576
"copy-dir": "^1.2.0",
7677
"cross-env": "^6.0.3",

packages/docsify-server-renderer/index.js

+5-17
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,15 @@ import { Compiler } from '../../src/core/render/compiler';
99
import { isAbsolutePath } from '../../src/core/router/util';
1010
import * as tpl from '../../src/core/render/tpl';
1111
import { prerenderEmbed } from '../../src/core/render/embed';
12+
import { getServerHTMLTemplate } from './template';
13+
import { isExternal } from './src/utils';
14+
15+
export { getServerHTMLTemplate };
1216

1317
function cwd(...args) {
1418
return resolve(process.cwd(), ...args);
1519
}
1620

17-
// Borrowed from https://j11y.io/snippets/getting-a-fully-qualified-url.
18-
function qualifyURL(url) {
19-
const img = document.createElement('img');
20-
img.src = url; // set string url
21-
url = img.src; // get qualified url
22-
img.src = ''; // prevent the server request
23-
return url;
24-
}
25-
26-
function isExternal(url) {
27-
url = qualifyURL(url);
28-
url = new URL(url);
29-
return url.origin !== location.origin;
30-
}
31-
3221
function mainTpl(config) {
3322
let html = `<nav class="app-nav${
3423
config.repo ? '' : ' no-badge'
@@ -48,12 +37,11 @@ function mainTpl(config) {
4837
}
4938

5039
export default class Renderer {
51-
constructor({ template, config, cache }) {
40+
constructor({ template, config }) {
5241
this.html = template;
5342
this.config = config = Object.assign({}, config, {
5443
routerMode: 'history',
5544
});
56-
this.cache = cache;
5745

5846
this.router = new AbstractHistory(config);
5947
this.compiler = new Compiler(config, this.router);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Borrowed from https://j11y.io/snippets/getting-a-fully-qualified-url.
2+
export function qualifyURL(url) {
3+
const img = document.createElement('img');
4+
img.src = url; // set string url
5+
url = img.src; // get qualified url
6+
img.src = ''; // prevent the server request
7+
return url;
8+
}
9+
10+
export function isExternal(url) {
11+
url = qualifyURL(url);
12+
url = new URL(url);
13+
return url.origin !== location.origin;
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* eslint-disable no-global-assign */
2+
require = require('esm')(module /* , options */);
3+
const { expect } = require('chai');
4+
const { initJSDOM } = require('../../../test/_helper');
5+
const { isExternal } = require('./utils');
6+
7+
describe('isExternal', () => {
8+
it('detects whether links are external or not', async () => {
9+
initJSDOM('', {
10+
url: 'http://127.0.0.1:3000',
11+
runScripts: 'dangerously',
12+
resources: 'usable',
13+
});
14+
15+
expect(isExternal).to.be.instanceOf(Function);
16+
expect(isExternal('/foo.md')).to.be.false;
17+
expect(isExternal('//foo.md')).to.be.true;
18+
expect(isExternal('//127.0.0.1:3000/foo.md')).to.be.false;
19+
expect(isExternal('http://127.0.0.1:3001/foo.md')).to.be.true;
20+
expect(isExternal('https://google.com/foo.md')).to.be.true;
21+
expect(isExternal('//google.com/foo.md')).to.be.true;
22+
expect(isExternal('/google.com/foo.md')).to.be.false;
23+
});
24+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>docsify</title>
6+
<meta
7+
name="viewport"
8+
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
9+
/>
10+
<link rel="stylesheet" href="/themes/vue.css" title="vue" />
11+
</head>
12+
<body>
13+
<!--inject-app-->
14+
<!--inject-config-->
15+
<script src="/lib/docsify.js"></script>
16+
</body>
17+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
4+
const tmplPath = path.resolve(__dirname, 'template.html');
5+
6+
export function getServerHTMLTemplate() {
7+
return fs.readFileSync(tmplPath).toString();
8+
}

server.js

+28-29
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
const liveServer = require('live-server')
2-
const isSSR = !!process.env.SSR
3-
const middleware = []
1+
const liveServer = require('live-server');
2+
const isSSR = !!process.env.SSR;
3+
const middleware = [];
44

55
if (isSSR) {
6-
const Renderer = require('./packages/docsify-server-renderer/build.js')
6+
const { initJSDOM } = require('./test/_helper');
7+
8+
const dom = initJSDOM('', {
9+
url: 'https://127.0.0.1:3000',
10+
});
11+
12+
require = require('esm')(module /* , options */);
13+
14+
const {
15+
Renderer,
16+
getServerHTMLTemplate,
17+
} = require('./packages/docsify-server-renderer/index');
18+
19+
debugger;
20+
721
const renderer = new Renderer({
8-
template: `
9-
<!DOCTYPE html>
10-
<html lang="en">
11-
<head>
12-
<meta charset="UTF-8">
13-
<title>docsify</title>
14-
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
15-
<link rel="stylesheet" href="/themes/vue.css" title="vue">
16-
</head>
17-
<body>
18-
<!--inject-app-->
19-
<!--inject-config-->
20-
<script src="/lib/docsify.js"></script>
21-
</body>
22-
</html>`,
22+
template: getServerHTMLTemplate(),
2323
config: {
2424
name: 'docsify',
2525
repo: 'docsifyjs/docsify',
@@ -32,24 +32,23 @@ if (isSSR) {
3232
'/de-de/changelog': '/changelog',
3333
'/zh-cn/changelog': '/changelog',
3434
'/changelog':
35-
'https://raw.githubusercontent.com/docsifyjs/docsify/master/CHANGELOG'
36-
}
35+
'https://raw.githubusercontent.com/docsifyjs/docsify/master/CHANGELOG',
36+
},
3737
},
38-
path: './'
39-
})
38+
});
4039

4140
middleware.push(function(req, res, next) {
4241
if (/\.(css|js)$/.test(req.url)) {
43-
return next()
42+
return next();
4443
}
45-
renderer.renderToString(req.url).then(html => res.end(html))
46-
})
44+
renderer.renderToString(req.url).then(html => res.end(html));
45+
});
4746
}
4847

4948
const params = {
5049
port: 3000,
5150
watch: ['lib', 'docs', 'themes'],
52-
middleware
53-
}
51+
middleware,
52+
};
5453

55-
liveServer.start(params)
54+
liveServer.start(params);

src/core/fetch/index.js

+1-15
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { callHook } from '../init/lifecycle';
33
import { getParentPath, stringifyQuery } from '../router/util';
44
import { noop } from '../util/core';
55
import { getAndActive } from '../event/sidebar';
6+
import { isExternal } from '../../../packages/docsify-server-renderer/src/utils';
67
import { get } from './ajax';
78

89
function loadNested(path, qs, file, next, vm, first) {
@@ -20,21 +21,6 @@ function loadNested(path, qs, file, next, vm, first) {
2021
).then(next, _ => loadNested(path, qs, file, next, vm));
2122
}
2223

23-
// Borrowed from https://j11y.io/snippets/getting-a-fully-qualified-url.
24-
function qualifyURL(url) {
25-
const img = document.createElement('img');
26-
img.src = url; // set string url
27-
url = img.src; // get qualified url
28-
img.src = ''; // prevent the server request
29-
return url;
30-
}
31-
32-
export function isExternal(url) {
33-
url = qualifyURL(url);
34-
url = new URL(url);
35-
return url.origin !== location.origin;
36-
}
37-
3824
export function fetchMixin(proto) {
3925
let last;
4026

src/core/render/compiler.js

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export class Compiler {
109109
return html;
110110
})(text);
111111

112+
// TODO parse() expects an arg, but here it does not receive an arg so it fails.
112113
const curFileName = this.router.parse().file;
113114

114115
if (isCached) {

src/core/render/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { getPath, isAbsolutePath } from '../router/util';
99
import { isMobile, inBrowser } from '../util/env';
1010
import { isPrimitive } from '../util/core';
1111
import { scrollActiveSidebar } from '../event/scroll';
12-
import { isExternal } from '../fetch';
12+
import { isExternal } from '../../../packages/docsify-server-renderer/src/utils';
1313
import { Compiler } from './compiler';
1414
import * as tpl from './tpl';
1515
import { prerenderEmbed } from './embed';

src/core/router/history/abstract.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export class AbstractHistory extends History {
77
this.mode = 'abstract';
88
}
99

10-
parse(path) {
10+
parse(path = location.href) {
1111
let query = '';
1212

1313
const queryIndex = path.indexOf('?');

src/plugins/search/component.js

+1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ function updateOptions(opts) {
228228
}
229229

230230
export function init(opts, vm) {
231+
// TODO FIXME parse() expects an argument
231232
const keywords = vm.router.parse().query.s;
232233

233234
updateOptions(opts);

test/_helper.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ function ready(callback) {
2121

2222
module.exports.initJSDOM = initJSDOM;
2323

24-
/** @param {string} markup - The HTML document to initialize JSDOM with. */
24+
/**
25+
* Creates a JSDOM instance and assigns the following variables to Node's
26+
* `global`: window, document, navigator, location, XMLHttpRequest.
27+
*
28+
* @param {string} markup - The HTML document to initialize JSDOM with.
29+
* @param {object} options - Options to pass to JSDOM. See https://github.com/jsdom/jsdom#customizing-jsdom
30+
*/
2531
function initJSDOM(markup = '', options = {}) {
2632
const dom = new JSDOM(markup, options);
2733

test/unit/docsify.test.js

-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ describe('Docsify public API', () => {
4646
});
4747

4848
it('global APIs are available', async () => {
49-
// const DOM = new (require('jsdom').JSDOM)(markup, {
5049
const DOM = initJSDOM(markup, {
5150
url: docsifySite,
5251
runScripts: 'dangerously',

0 commit comments

Comments
 (0)