chore: update miscellaneous parts of the source to reasonably modern language alternatives, remove polyfills, improve some JSDoc comments, remove traces of IE

BREAKING: In a minority of cases syntax updates may break apps running in very old browsers (such as Internet Explorer), or apps that build Docsify in a custom way with old build tools. To upgrade, build Docsify for older browsers in a custom way, or update existing build tools to handle newer syntax.

DEPRECATED: `$docsify.themeColor` is deprecated and will be eventually removed, use a `--theme-color` CSS variable in your style sheet.
This commit is contained in:
Joe Pea 2023-07-03 01:27:22 -07:00
parent 9cb76027cd
commit b621e0e4e7
73 changed files with 509 additions and 583 deletions

View File

@ -62,4 +62,3 @@ If yes, please describe the impact and migration path for existing applications:
- [ ] Firefox
- [ ] Safari
- [ ] Edge
- [ ] IE

View File

@ -46,7 +46,6 @@
- Smart full-text search plugin
- Multiple themes
- Useful plugin API
- Compatible with IE11
- Support embedded files
## Quick start

View File

@ -26,14 +26,15 @@ async function build(opts) {
await rollup
.rollup({
input: opts.input,
plugins: (opts.plugins || []).concat([
plugins: [
...(opts.plugins || []),
commonjs(),
nodeResolve(),
replace({
__VERSION__: version,
})
]),
onwarn: function (message) {
],
onwarn(message) {
if (message.code === 'UNRESOLVED_IMPORT') {
throw new Error(
`Could not resolve module ` +
@ -44,8 +45,8 @@ async function build(opts) {
}
}
})
.then(function (bundle) {
var dest = 'lib/' + (opts.output || opts.input)
.then(bundle => {
const dest = 'lib/' + (opts.output || opts.input)
console.log(dest)
return bundle.write({
@ -77,7 +78,7 @@ async function buildCore() {
}
async function buildAllPlugin() {
var plugins = [
const plugins = [
{name: 'search', input: 'search/index.js'},
{name: 'ga', input: 'ga.js'},
{name: 'gtag', input: 'gtag.js'},

View File

@ -1,17 +1,17 @@
import fs from 'fs';
import { relative } from './util.js';
var read = fs.readFileSync;
var write = fs.writeFileSync;
const read = fs.readFileSync;
const write = fs.writeFileSync;
const pkgPath = relative(import.meta, '..', 'package.json');
const pkg = JSON.parse(read(pkgPath).toString());
var version = process.env.VERSION || pkg.version;
const version = process.env.VERSION || pkg.version;
var file = relative(import.meta, '..', 'docs', '_coverpage.md');
var cover = read(file, 'utf8').toString();
const file = relative(import.meta, '..', 'docs', '_coverpage.md');
let cover = read(file, 'utf8').toString();
console.log('Replace version number in cover page...');
cover = cover.replace(
/<small>(\S+)?<\/small>/g,
'<small>' + version + '</small>'
/* html */ `<small>${version}</small>`
);
write(file, cover);

View File

@ -11,17 +11,16 @@ fs.readdir(relative('../src/themes'), (err, files) => {
}
files.map(async (file) => {
if (/\.styl/g.test(file)) {
var stylusCMD;
const stylusBin = ['node_modules', 'stylus', 'bin', 'stylus'].join(path.sep)
var cmdargs = [
let cmdargs = [
stylusBin,
`src/themes/${file}`,
'-u',
'autoprefixer-stylus'
]
cmdargs = cmdargs.concat(args)
cmdargs = [...cmdargs, ...args]
stylusCMD = spawn('node', cmdargs, { shell: true })
const stylusCMD = spawn('node', cmdargs, { shell: true })
stylusCMD.stdout.on('data', (data) => {
console.log(`[Stylus Build ] stdout: ${data}`);

View File

@ -93,17 +93,15 @@ function writeEmojiJS(emojiData) {
}
}
(async () => {
console.info('Build emoji');
console.info('Build emoji');
try {
const emojiData = await getEmojiData();
try {
const emojiData = await getEmojiData();
if (emojiData) {
writeEmojiPage(emojiData);
writeEmojiJS(emojiData);
}
} catch (err) {
console.warn(`- Error: ${err.message}`);
if (emojiData) {
writeEmojiPage(emojiData);
writeEmojiJS(emojiData);
}
})();
} catch (err) {
console.warn(`- Error: ${err.message}`);
}

View File

@ -16,7 +16,6 @@ See the [Quick start](quickstart.md) guide for more details.
- Multiple themes
- Useful plugin API
- Emoji support
- Compatible with IE11
## Examples

View File

@ -1,16 +1,16 @@
import fetch from 'fetch'
import fetch from 'fetch';
const URL = 'https://example.com'
const PORT = 8080
const URL = 'https://example.com';
const PORT = 8080;
/// [demo]
const result = fetch(`${URL}:${PORT}`)
.then(function (response) {
return response.json()
})
.then(function (myJson) {
console.log(JSON.stringify(myJson))
.then(response => {
return response.json();
})
.then(myJson => {
console.log(JSON.stringify(myJson));
});
/// [demo]
result.then(console.log).catch(console.error)
result.then(console.log).catch(console.error);

View File

@ -244,7 +244,7 @@ See https://github.com/lukeed/tinydate#patterns
window.$docsify = {
formatUpdated: '{MM}/{DD} {HH}:{mm}',
formatUpdated: function (time) {
formatUpdated(time) {
// ...
return time;
@ -341,14 +341,14 @@ window.$docsify = {
markdown: {
smartypants: true,
renderer: {
link: function () {
link() {
// ...
},
},
},
// function
markdown: function (marked, renderer) {
markdown(marked, renderer) {
// ...
return marked;
},
@ -689,18 +689,17 @@ window.$docsify = {
'/foo': '# Custom Markdown',
// RegEx match w/ synchronous function
'/bar/(.*)': function (route, matched) {
'/bar/(.*)'(route, matched) {
return '# Custom Markdown';
},
// RegEx match w/ asynchronous function
'/baz/(.*)': function (route, matched, next) {
// Requires `fetch` polyfill for legacy browsers (https://github.github.io/fetch/)
'/baz/(.*)'(route, matched, next) {
fetch('/api/users?id=12345')
.then(function (response) {
.then(response => {
next('# Custom Markdown');
})
.catch(function (err) {
.catch(err => {
// Handle error...
});
},
@ -714,7 +713,7 @@ Other than strings, route functions can return a falsy value (`null` \ `undefine
window.$docsify = {
routes: {
// accepts everything other than dogs (synchronous)
'/pets/(.+)': function(route, matched) {
'/pets/(.+)'(route, matched) {
if (matched[0] === 'dogs') {
return null;
} else {
@ -723,7 +722,7 @@ window.$docsify = {
}
// accepts everything other than cats (asynchronous)
'/pets/(.*)': function(route, matched, next) {
'/pets/(.*)'(route, matched, next) {
if (matched[0] === 'cats') {
next();
} else {
@ -741,12 +740,12 @@ Finally, if you have a specific path that has a real markdown file (and therefor
window.$docsify = {
routes: {
// if you look up /pets/cats, docsify will skip all routes and look for "pets/cats.md"
'/pets/cats': function(route, matched) {
'/pets/cats'(route, matched) {
return false;
}
// but any other pet should generate dynamic content right here
'/pets/(.+)': function(route, matched) {
'/pets/(.+)'(route, matched) {
const pet = matched[0];
return `your pet is ${pet} (but not a cat)`;
}
@ -777,11 +776,19 @@ If you have a link to the homepage in the sidebar and want it to be shown as act
For more details, see [#1131](https://github.com/docsifyjs/docsify/issues/1131).
## themeColor
## themeColor (_deprecated_)
> **Warning** Deprecated. Use the CSS var `--theme-color` in your `<style>` sheet. Example:
>
> <style>
> :root {
> --theme-color: deeppink;
> }
> </style>
- Type: `String`
Customize the theme color. Use [CSS3 variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables) feature and polyfill in older browsers.
Customize the theme color.
```js
window.$docsify = {

View File

@ -8,7 +8,6 @@
name="google-site-verification"
content="6t0LoIeFksrjF4c9sqUEsVXiQNxLp2hgoqo0KryT-sE"
/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta
name="keywords"
content="doc,docs,documentation,gitbook,creator,generator,github,jekyll,github-pages"
@ -64,7 +63,7 @@
<script src="//cdn.jsdelivr.net/npm/docsify-plugin-carbon@1"></script>
<script>
// Set html "lang" attribute based on URL
var lang = location.hash.match(/#\/(de-de|es|ru-ru|zh-cn)\//);
const lang = location.hash.match(/#\/(de-de|es|ru-ru|zh-cn)\//);
if (lang) {
document.documentElement.setAttribute('lang', lang[1]);
@ -128,9 +127,8 @@
},
vueComponents: {
'button-counter': {
template:
'<button @click="count += 1">You clicked me {{ count }} times</button>',
data: function () {
template: /* html */ `<button @click="count += 1">You clicked me {{ count }} times</button>`,
data() {
return {
count: 0,
};
@ -138,7 +136,7 @@
},
},
vueGlobalOptions: {
data: function () {
data() {
return {
count: 0,
message: 'Hello, World!',
@ -151,7 +149,7 @@
};
},
computed: {
timeOfDay: function () {
timeOfDay() {
const date = new Date();
const hours = date.getHours();
@ -165,14 +163,14 @@
},
},
methods: {
hello: function () {
hello() {
alert(this.message);
},
},
},
vueMounts: {
'#counter': {
data: function () {
data() {
return {
count: 0,
};
@ -182,7 +180,7 @@
plugins: [
DocsifyCarbon.create('CEBI6KQE', 'docsifyjsorg'),
function (hook, vm) {
hook.beforeEach(function (html) {
hook.beforeEach(html => {
if (/githubusercontent\.com/.test(vm.route.file)) {
url = vm.route.file
.replace('raw.githubusercontent.com', 'github.com')
@ -196,14 +194,14 @@
'https://github.com/docsifyjs/docsify/blob/develop/docs/' +
vm.route.file;
}
var editHtml = '[:memo: Edit Document](' + url + ')\n';
const editHtml = '[:memo: Edit Document](' + url + ')\n';
return (
editHtml +
html +
'\n\n----\n\n' +
'<a href="https://docsify.js.org" target="_blank" style="color: inherit; font-weight: normal; text-decoration: none;">Powered by docsify</a>'
);
})
});
},
],
};

View File

@ -58,7 +58,7 @@ function getAdder(int $x): int
Code blocks [dynamically created from javascript](https://docsify.js.org/#/configuration?id=executescript) can be highlighted using the method `Prism.highlightElement` like so:
```javascript
var code = document.createElement("code");
const code = document.createElement("code");
code.innerHTML = "console.log('Hello World!')";
code.setAttribute("class", "lang-javascript");
Prism.highlightElement(code);

View File

@ -7,7 +7,7 @@ window.$docsify = {
markdown: {
smartypants: true,
renderer: {
link: function() {
link() {
// ...
}
}
@ -21,7 +21,7 @@ You can completely customize the parsing rules.
```js
window.$docsify = {
markdown: function(marked, renderer) {
markdown(marked, renderer) {
// ...
return marked
@ -36,17 +36,17 @@ window.$docsify = {
// <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.css">
// <script src="//cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
var num = 0;
let num = 0;
mermaid.initialize({ startOnLoad: false });
window.$docsify = {
markdown: {
renderer: {
code: function(code, lang) {
code(code, lang) {
if (lang === "mermaid") {
return (
'<div class="mermaid">' + mermaid.render('mermaid-svg-' + num++, code) + "</div>"
);
return /* html */ `
<div class="mermaid">${mermaid.render('mermaid-svg-' + num++, code)}</div>
`;
}
return this.origin.code.apply(this, arguments);
}

View File

@ -63,11 +63,7 @@ By default, the hyperlink on the current page is recognized and the content is s
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
```
This plugin ignores diacritical marks when performing a full text search (e.g., "cafe" will also match "café"). Legacy browsers like IE11 require the following [String.normalize()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize) polyfill to ignore diacritical marks:
```html
<script src="//polyfill.io/v3/polyfill.min.js?features=String.prototype.normalize"></script>
```
This plugin ignores diacritical marks when performing a full text search (e.g., "cafe" will also match "café").
## Google Analytics

View File

@ -29,8 +29,8 @@ const HOSTNAME_WHITELIST = [
// The Util Function to hack URLs of intercepted requests
const getFixedUrl = (req) => {
var now = Date.now()
var url = new URL(req.url)
const now = Date.now()
const url = new URL(req.url)
// 1. fixed http URL
// Just keep syncing with location.protocol

View File

@ -44,7 +44,6 @@ If you don't like `npm` or have trouble installing the tool, you can manually cr
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta charset="UTF-8" />
<link

View File

@ -50,13 +50,13 @@ If you have any ideas or would like to develop a new theme, you are welcome to s
</style>
<script>
var preview = Docsify.dom.find('.demo-theme-preview');
var themes = Docsify.dom.findAll('[rel="stylesheet"]');
const preview = Docsify.dom.find('.demo-theme-preview');
const themes = Docsify.dom.findAll('[rel="stylesheet"]');
preview.onclick = function (e) {
var title = e.target.getAttribute('data-theme');
const title = e.target.getAttribute('data-theme');
themes.forEach(function (theme) {
themes.forEach(theme => {
theme.disabled = theme.title !== title;
});
};

View File

@ -24,15 +24,15 @@ Alternatively, a plugin can be stored in a separate file and "installed" using a
```js
// docsify-plugin-myplugin.js
(function () {
var myPlugin = function (hook, vm) {
{
function myPlugin(hook, vm) {
// ...
};
}
// Add plugin to docsify's plugin array
$docsify = $docsify || {};
$docsify.plugins = [].concat($docsify.plugins || [], myPlugin);
})();
window.$docsify = window.$docsify || {};
$docsify.plugins = [...($docsify.plugins || []), myPlugin];
}
```
```html
@ -51,47 +51,47 @@ Below is a plugin template with placeholders for all available lifecycle hooks.
1. Load your plugin using a standard `<script>` tag
```js
(function () {
var myPlugin = function (hook, vm) {
{
function myPlugin(hook, vm) {
// Invoked one time when docsify script is initialized
hook.init(function () {
hook.init(() => {
// ...
});
// Invoked one time when the docsify instance has mounted on the DOM
hook.mounted(function () {
hook.mounted(() => {
// ...
});
// Invoked on each page load before new markdown is transformed to HTML.
// Supports asynchronous tasks (see beforeEach documentation for details).
hook.beforeEach(function (markdown) {
hook.beforeEach(markdown => {
// ...
return markdown;
});
// Invoked on each page load after new markdown has been transformed to HTML.
// Supports asynchronous tasks (see afterEach documentation for details).
hook.afterEach(function (html) {
hook.afterEach(html => {
// ...
return html;
});
// Invoked on each page load after new HTML has been appended to the DOM
hook.doneEach(function () {
hook.doneEach(() => {
// ...
});
// Invoked one time after rendering the initial page
hook.ready(function () {
hook.ready(() => {
// ...
});
};
}
// Add plugin to docsify's plugin array
$docsify = $docsify || {};
$docsify.plugins = [].concat(myPlugin, $docsify.plugins || []);
})();
window.$docsify = window.$docsify || {};
$docsify.plugins = [myPlugin, ...($docsify.plugins || [])];
}
```
## Lifecycle Hooks
@ -103,7 +103,7 @@ Lifecycle hooks are provided via the `hook` argument passed to the plugin functi
Invoked one time when docsify script is initialized.
```js
hook.init(function () {
hook.init(() => {
// ...
});
```
@ -113,7 +113,7 @@ hook.init(function () {
Invoked one time when the docsify instance has mounted on the DOM.
```js
hook.mounted(function () {
hook.mounted(() => {
// ...
});
```
@ -123,7 +123,7 @@ hook.mounted(function () {
Invoked on each page load before new markdown is transformed to HTML.
```js
hook.beforeEach(function (markdown) {
hook.beforeEach(markdown => {
// ...
return markdown;
});
@ -132,7 +132,7 @@ hook.beforeEach(function (markdown) {
For asynchronous tasks, the hook function accepts a `next` callback as a second argument. Call this function with the final `markdown` value when ready. To prevent errors from affecting docsify and other plugins, wrap async code in a `try/catch/finally` block.
```js
hook.beforeEach(function (markdown, next) {
hook.beforeEach((markdown, next) => {
try {
// Async task(s)...
} catch (err) {
@ -148,7 +148,7 @@ hook.beforeEach(function (markdown, next) {
Invoked on each page load after new markdown has been transformed to HTML.
```js
hook.afterEach(function (html) {
hook.afterEach(html => {
// ...
return html;
});
@ -157,7 +157,7 @@ hook.afterEach(function (html) {
For asynchronous tasks, the hook function accepts a `next` callback as a second argument. Call this function with the final `html` value when ready. To prevent errors from affecting docsify and other plugins, wrap async code in a `try/catch/finally` block.
```js
hook.afterEach(function (html, next) {
hook.afterEach((html, next) => {
try {
// Async task(s)...
} catch (err) {
@ -173,7 +173,7 @@ hook.afterEach(function (html, next) {
Invoked on each page load after new HTML has been appended to the DOM.
```js
hook.doneEach(function () {
hook.doneEach(() => {
// ...
});
```
@ -183,7 +183,7 @@ hook.doneEach(function () {
Invoked one time after rendering the initial page.
```js
hook.ready(function () {
hook.ready(() => {
// ...
});
```
@ -203,15 +203,15 @@ hook.ready(function () {
window.$docsify = {
plugins: [
function pageFooter(hook, vm) {
var footer = [
'<hr/>',
'<footer>',
'<span><a href="https://github.com/QingWei-Li">cinwell</a> &copy;2017.</span>',
'<span>Proudly published with <a href="https://github.com/docsifyjs/docsify" target="_blank">docsify</a>.</span>',
'</footer>',
].join('');
const footer = /* html */ `
<hr/>
<footer>
<span><a href="https://github.com/QingWei-Li">cinwell</a> &copy;2017.</span>
<span>Proudly published with <a href="https://github.com/docsifyjs/docsify" target="_blank">docsify</a>.</span>
</footer>
`;
hook.afterEach(function (html) {
hook.afterEach(html => {
return html + footer;
});
},
@ -228,11 +228,11 @@ window.$docsify = {
// The date template pattern
$docsify.formatUpdated = '{YYYY}/{MM}/{DD} {HH}:{mm}';
hook.beforeEach(function (html) {
var url =
hook.beforeEach(html => {
const url =
'https://github.com/docsifyjs/docsify/blob/master/docs/' +
vm.route.file;
var editHtml = '[📝 EDIT DOCUMENT](' + url + ')\n';
const editHtml = '[📝 EDIT DOCUMENT](' + url + ')\n';
return (
editHtml +

View File

@ -3,7 +3,6 @@
<head>
<meta charset="UTF-8" />
<title>docsify</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="stylesheet" href="/themes/vue.css" title="vue" />
<link rel="stylesheet" href="/themes/dark.css" title="dark" disabled />
@ -27,7 +26,7 @@
<script src="//cdn.jsdelivr.net/npm/docsify-plugin-carbon@1"></script>
<script>
// Set html "lang" attribute based on URL
var lang = location.hash.match(/#\/(de-de|es|ru-ru|zh-cn)\//);
const lang = location.hash.match(/#\/(de-de|es|ru-ru|zh-cn)\//);
if (lang) {
document.documentElement.setAttribute('lang', lang[1]);
@ -88,7 +87,7 @@
plugins: [
DocsifyCarbon.create('CEBI6KQE', 'docsifyjsorg'),
function (hook, vm) {
hook.beforeEach(function (html) {
hook.beforeEach(html => {
if (/githubusercontent\.com/.test(vm.route.file)) {
url = vm.route.file
.replace('raw.githubusercontent.com', 'github.com')
@ -102,7 +101,7 @@
'https://github.com/docsifyjs/docsify/blob/develop/docs/' +
vm.route.file;
}
var editHtml = '[:memo: Edit Document](' + url + ')\n';
const editHtml = '[:memo: Edit Document](' + url + ')\n';
return (
editHtml +
html +

View File

@ -32,7 +32,7 @@ export class Docsify extends Fetch(
}
initPlugin() {
[].concat(this.config.plugins).forEach(fn => {
this.config.plugins.forEach(fn => {
try {
isFn(fn) && fn(this._lifecycle, this);
} catch (err) {

View File

@ -1,10 +1,11 @@
import { merge, hyphenate, isPrimitive, hasOwn } from './util/core.js';
import stripIndent from 'strip-indent';
import { hyphenate, isPrimitive } from './util/core.js';
const currentScript = document.currentScript;
/** @param {import('./Docsify').Docsify} vm */
export default function (vm) {
const config = merge(
const config = Object.assign(
{
auto2top: false,
autoHeader: false,
@ -30,14 +31,36 @@ export default function (vm) {
noCompileLinks: [],
noEmoji: false,
notFoundPage: true,
plugins: [],
relativePath: false,
repo: '',
routes: {},
routerMode: 'hash',
subMaxLevel: 0,
themeColor: '',
// themeColor: '',
topMargin: 0,
// Deprecations //////////////////
__themeColor: '',
get themeColor() {
return this.__themeColor;
},
set themeColor(value) {
this.__themeColor = value;
console.warn(
stripIndent(/* html */ `
$docsify.themeColor is deprecated. Use a --theme-color property in your style sheet. Example:
<style>
:root {
--theme-color: deeppink;
}
</style>
`).trim()
);
},
},
typeof window.$docsify === 'function'
? window.$docsify(vm)
: window.$docsify
@ -45,18 +68,16 @@ export default function (vm) {
const script =
currentScript ||
[].slice
.call(document.getElementsByTagName('script'))
.filter(n => /docsify\./.test(n.src))[0];
Array.from(document.getElementsByTagName('script')).filter(n =>
/docsify\./.test(n.src)
)[0];
if (script) {
for (const prop in config) {
if (hasOwn.call(config, prop)) {
const val = script.getAttribute('data-' + hyphenate(prop));
for (const prop of Object.keys(config)) {
const val = script.getAttribute('data-' + hyphenate(prop));
if (isPrimitive(val)) {
config[prop] = val === '' ? true : val;
}
if (isPrimitive(val)) {
config[prop] = val === '' ? true : val;
}
}
}

View File

@ -14,11 +14,8 @@ export function Events(Base) {
$resetEvents(source) {
const { auto2top } = this.config;
(() => {
// Rely on the browser's scroll auto-restoration when going back or forward
if (source === 'history') {
return;
}
// If 'history', rely on the browser's scroll auto-restoration when going back or forward
if (source !== 'history') {
// Scroll to ID if specified
if (this.route.query.id) {
scrollIntoView(this.route.path, this.route.query.id);
@ -27,7 +24,7 @@ export function Events(Base) {
if (source === 'navigate') {
auto2top && scroll2Top(auto2top);
}
})();
}
if (this.config.loadNavbar) {
sidebar.getAndActive(this.router, 'nav');

View File

@ -43,9 +43,7 @@ function highlight(path) {
const top = ((doc && doc.scrollTop) || document.body.scrollTop) - coverHeight;
let last;
for (let i = 0, len = anchors.length; i < len; i += 1) {
const node = anchors[i];
for (const node of anchors) {
if (node.offsetTop > top) {
if (!last) {
last = node;
@ -103,8 +101,7 @@ export function scrollActiveSidebar(router) {
lis = dom.findAll(sidebar, 'li');
}
for (let i = 0, len = lis.length; i < len; i += 1) {
const li = lis[i];
for (const li of lis) {
const a = li.querySelector('a');
if (!a) {
continue;

View File

@ -1,7 +1,13 @@
// @ts-check
/* eslint-disable no-unused-vars */
import progressbar from '../render/progressbar.js';
import { noop, hasOwn } from '../util/core.js';
import { noop } from '../util/core.js';
/** @typedef {{updatedAt: string}} CacheOpt */
/** @typedef {{content: string, opt: CacheOpt}} CacheItem */
/** @type {Record<string, CacheItem>} */
const cache = {};
/**
@ -9,14 +15,13 @@ const cache = {};
* @param {string} url Resource URL
* @param {boolean} [hasBar=false] Has progress bar
* @param {String[]} headers Array of headers
* @return {Promise} Promise response
* @return A Promise-like response with error callback (error callback is not Promise-like)
*/
// TODO update to using fetch() + Streams API instead of XMLHttpRequest. See an
// example of download progress calculation using fetch() here:
// https://streams.spec.whatwg.org/demos/
export function get(url, hasBar = false, headers = {}) {
const xhr = new XMLHttpRequest();
const on = function () {
xhr.addEventListener.apply(xhr, arguments);
};
const cached = cache[url];
if (cached) {
@ -24,16 +29,18 @@ export function get(url, hasBar = false, headers = {}) {
}
xhr.open('GET', url);
for (const i in headers) {
if (hasOwn.call(headers, i)) {
xhr.setRequestHeader(i, headers[i]);
}
for (const i of Object.keys(headers)) {
xhr.setRequestHeader(i, headers[i]);
}
xhr.send();
return {
then: function (success, error = noop) {
/**
* @param {(text: string, opt: CacheOpt) => void} success
* @param {(event: ProgressEvent<XMLHttpRequestEventTarget>) => void} error
*/
then(success, error = noop) {
if (hasBar) {
const id = setInterval(
_ =>
@ -43,22 +50,27 @@ export function get(url, hasBar = false, headers = {}) {
500
);
on('progress', progressbar);
on('loadend', evt => {
xhr.addEventListener('progress', progressbar);
xhr.addEventListener('loadend', evt => {
progressbar(evt);
clearInterval(id);
});
}
on('error', error);
on('load', ({ target }) => {
xhr.addEventListener('error', error);
xhr.addEventListener('load', event => {
const target = /** @type {XMLHttpRequest} */ (event.target);
if (target.status >= 400) {
error(target);
error(event);
} else {
if (typeof target.response !== 'string') {
throw new TypeError('Unsupported content type.');
}
const result = (cache[url] = {
content: target.response,
opt: {
updatedAt: xhr.getResponseHeader('last-modified'),
updatedAt: xhr.getResponseHeader('last-modified') ?? '',
},
});

View File

@ -16,7 +16,7 @@ function loadNested(path, qs, file, next, vm, first) {
vm.router.getFile(path + file) + qs,
false,
vm.config.requestHeaders
).then(next, _ => loadNested(path, qs, file, next, vm));
).then(next, _error => loadNested(path, qs, file, next, vm));
}
/** @typedef {import('../Docsify').Constructor} Constructor */
@ -29,7 +29,7 @@ export function Fetch(Base) {
let last;
const abort = () => last && last.abort && last.abort();
const request = (url, hasbar, requestHeaders) => {
const request = (url, requestHeaders) => {
abort();
last = get(url, true, requestHeaders);
return last;
@ -111,7 +111,7 @@ export function Fetch(Base) {
};
// and a handler that is called if content failed to fetch
const contentFailedToFetch = _ => {
const contentFailedToFetch = _error => {
this._fetchFallbackPage(path, qs, cb) || this._fetch404(file, qs, cb);
};
@ -121,7 +121,7 @@ export function Fetch(Base) {
if (typeof contents === 'string') {
contentFetched(contents);
} else {
request(file + qs, true, requestHeaders).then(
request(file + qs, requestHeaders).then(
contentFetched,
contentFailedToFetch
);
@ -129,7 +129,7 @@ export function Fetch(Base) {
});
} else {
// if the requested url is not local, just fetch the file
request(file + qs, true, requestHeaders).then(
request(file + qs, requestHeaders).then(
contentFetched,
contentFailedToFetch
);
@ -216,7 +216,7 @@ export function Fetch(Base) {
const newPath = this.router.getFile(
path.replace(new RegExp(`^/${local}`), '')
);
const req = request(newPath + qs, true, requestHeaders);
const req = request(newPath + qs, requestHeaders);
req.then(
(text, opt) =>
@ -225,7 +225,7 @@ export function Fetch(Base) {
opt,
this._loadSideAndNav(path, qs, loadSidebar, cb)
),
() => this._fetch404(path, qs, cb)
_error => this._fetch404(path, qs, cb)
);
return true;
@ -246,9 +246,9 @@ export function Fetch(Base) {
if (notFoundPage) {
const path404 = get404Path(path, this.config);
request(this.router.getFile(path404), true, requestHeaders).then(
request(this.router.getFile(path404), requestHeaders).then(
(text, opt) => this._renderMain(text, opt, fnLoadSideAndNav),
() => this._renderMain(null, {}, fnLoadSideAndNav)
_error => this._renderMain(null, {}, fnLoadSideAndNav)
);
return true;
}

View File

@ -1,6 +1,6 @@
import { marked } from 'marked';
import { isAbsolutePath, getPath, getParentPath } from '../router/util.js';
import { isFn, merge, cached, isPrimitive } from '../util/core.js';
import { isFn, cached, isPrimitive } from '../util/core.js';
import { tree as treeTpl } from './tpl.js';
import { genTree } from './gen-tree.js';
import { slugify } from './slugify.js';
@ -83,8 +83,8 @@ export class Compiler {
compile = mdConf(marked, renderer);
} else {
marked.setOptions(
merge(mdConf, {
renderer: merge(renderer, mdConf.renderer),
Object.assign(mdConf, {
renderer: Object.assign(renderer, mdConf.renderer),
})
);
compile = marked;
@ -93,7 +93,8 @@ export class Compiler {
this._marked = compile;
this.compile = text => {
let isCached = true;
// eslint-disable-next-line no-unused-vars
// FIXME: this is not cached.
const result = cached(_ => {
isCached = false;
let html = '';
@ -186,8 +187,7 @@ export class Compiler {
_matchNotCompileLink(link) {
const links = this.config.noCompileLinks || [];
for (let i = 0; i < links.length; i++) {
const n = links[i];
for (const n of links) {
const re = cachedLinks[n] || (cachedLinks[n] = new RegExp(`^${n}$`));
if (re.test(link)) {
@ -278,7 +278,7 @@ export class Compiler {
}
const tree = this.cacheTree[currentPath] || genTree(toc, level);
html = treeTpl(tree, '<ul>{inner}</ul>');
html = treeTpl(tree, /* html */ `<ul>{inner}</ul>`);
this.cacheTree[currentPath] = tree;
}

View File

@ -11,5 +11,5 @@ export const highlightCodeCompiler = ({ renderer }) =>
lang
);
return `<pre v-pre data-lang="${lang}"><code class="lang-${lang}">${text}</code></pre>`;
return /* html */ `<pre v-pre data-lang="${lang}"><code class="lang-${lang}">${text}</code></pre>`;
});

View File

@ -23,5 +23,5 @@ export const headingCompiler = ({ renderer, router, _self }) =>
nextToc.slug = url;
_self.toc.push(nextToc);
return `<h${level} id="${slug}"><a href="${url}" data-id="${slug}" class="anchor"><span>${str}</span></a></h${level}>`;
return /* html */ `<h${level} id="${slug}"><a href="${url}" data-id="${slug}" class="anchor"><span>${str}</span></a></h${level}>`;
});

View File

@ -38,11 +38,7 @@ export const imageCompiler = ({ renderer, contentBase, router }) =>
url = getPath(contentBase, getParentPath(router.getCurrentPath()), href);
}
if (attrs.length > 0) {
return `<img src="${url}" data-origin="${href}" alt="${text}" ${attrs.join(
' '
)} />`;
}
return `<img src="${url}" data-origin="${href}" alt="${text}"${attrs}>`;
return /* html */ `<img src="${url}" data-origin="${href}" alt="${text}" ${attrs.join(
' '
)} />`;
});

View File

@ -60,5 +60,5 @@ export const linkCompiler = ({
attrs.push(`title="${title}"`);
}
return `<a href="${href}" ${attrs.join(' ')}>${text}</a>`;
return /* html */ `<a href="${href}" ${attrs.join(' ')}>${text}</a>`;
});

View File

@ -8,7 +8,7 @@ export const paragraphCompiler = ({ renderer }) =>
} else if (/^\?&gt;/.test(text)) {
result = helperTpl('warn', text);
} else {
result = `<p>${text}</p>`;
result = /* html */ `<p>${text}</p>`;
}
return result;

View File

@ -2,8 +2,8 @@ export const taskListItemCompiler = ({ renderer }) =>
(renderer.listitem = text => {
const isTaskItem = /^(<input.*type="checkbox"[^>]*>)/.test(text);
const html = isTaskItem
? `<li class="task-list-item"><label>${text}</label></li>`
: `<li>${text}</li>`;
? /* html */ `<li class="task-list-item"><label>${text}</label></li>`
: /* html */ `<li>${text}</li>`;
return html;
});

View File

@ -1,6 +1,5 @@
import stripIndent from 'strip-indent';
import { get } from '../fetch/ajax.js';
import { merge } from '../util/core.js';
const cached = {};
@ -14,70 +13,72 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
}
while ((token = embedTokens[step++])) {
// eslint-disable-next-line no-shadow
const next = (function (token) {
return text => {
let embedToken;
if (text) {
if (token.embed.type === 'markdown') {
let path = token.embed.url.split('/');
path.pop();
path = path.join('/');
// Resolves relative links to absolute
text = text.replace(/\[([^[\]]+)\]\(([^)]+)\)/g, x => {
const linkBeginIndex = x.indexOf('(');
if (x.slice(linkBeginIndex, linkBeginIndex + 2) === '(.') {
return (
x.substring(0, linkBeginIndex) +
`(${window.location.protocol}//${window.location.host}${path}/` +
x.substring(linkBeginIndex + 1, x.length - 1) +
')'
);
}
return x;
});
const currentToken = token;
// This may contain YAML front matter and will need to be stripped.
const frontMatterInstalled =
($docsify.frontMatter || {}).installed || false;
if (frontMatterInstalled === true) {
text = $docsify.frontMatter.parseMarkdown(text);
}
embedToken = compile.lexer(text);
} else if (token.embed.type === 'code') {
if (token.embed.fragment) {
const fragment = token.embed.fragment;
const pattern = new RegExp(
`(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`
const next = text => {
let embedToken;
if (text) {
if (currentToken.embed.type === 'markdown') {
let path = currentToken.embed.url.split('/');
path.pop();
path = path.join('/');
// Resolves relative links to absolute
text = text.replace(/\[([^[\]]+)\]\(([^)]+)\)/g, x => {
const linkBeginIndex = x.indexOf('(');
if (x.slice(linkBeginIndex, linkBeginIndex + 2) === '(.') {
return (
x.substring(0, linkBeginIndex) +
`(${window.location.protocol}//${window.location.host}${path}/` +
x.substring(linkBeginIndex + 1, x.length - 1) +
')'
);
text = stripIndent((text.match(pattern) || [])[1] || '').trim();
}
return x;
});
embedToken = compile.lexer(
'```' +
token.embed.lang +
'\n' +
text.replace(/`/g, '@DOCSIFY_QM@') +
'\n```\n'
);
} else if (token.embed.type === 'mermaid') {
embedToken = [
{ type: 'html', text: `<div class="mermaid">\n${text}\n</div>` },
];
embedToken.links = {};
} else {
embedToken = [{ type: 'html', text }];
embedToken.links = {};
// This may contain YAML front matter and will need to be stripped.
const frontMatterInstalled =
($docsify.frontMatter || {}).installed || false;
if (frontMatterInstalled === true) {
text = $docsify.frontMatter.parseMarkdown(text);
}
}
cb({ token, embedToken });
if (++count >= embedTokens.length) {
cb({});
embedToken = compile.lexer(text);
} else if (currentToken.embed.type === 'code') {
if (currentToken.embed.fragment) {
const fragment = currentToken.embed.fragment;
const pattern = new RegExp(
`(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`
);
text = stripIndent((text.match(pattern) || [])[1] || '').trim();
}
embedToken = compile.lexer(
'```' +
currentToken.embed.lang +
'\n' +
text.replace(/`/g, '@DOCSIFY_QM@') +
'\n```\n'
);
} else if (currentToken.embed.type === 'mermaid') {
embedToken = [
{
type: 'html',
text: /* html */ `<div class="mermaid">\n${text}\n</div>`,
},
];
embedToken.links = {};
} else {
embedToken = [{ type: 'html', text }];
embedToken.links = {};
}
};
})(token);
}
cb({ token: currentToken, embedToken });
if (++count >= embedTokens.length) {
cb({});
}
};
if (token.embed.url) {
get(token.embed.url).then(next);
@ -136,7 +137,7 @@ export function prerenderEmbed({ compiler, raw = '', fetch }, done) {
}
});
merge(links, embedToken.links);
Object.assign(links, embedToken.links);
tokens = tokens
.slice(0, index)

View File

@ -16,9 +16,9 @@ function replaceEmojiShorthand(m, $1, useNativeEmoji) {
// Hat tip: https://about.gitlab.com/blog/2018/05/30/journey-in-native-unicode-emoji/#emoji-made-up-of-multiple-characters
.join('&zwj;')
.concat('&#xFE0E;');
result = `<span class="emoji">${emojiUnicode}</span>`;
result = /* html */ `<span class="emoji">${emojiUnicode}</span>`;
} else {
result = `<img src="${emojiData.baseURL}${emojiMatch}.png" alt="${$1}" class="emoji" loading="lazy">`;
result = /* html */ `<img src="${emojiData.baseURL}${emojiMatch}.png" alt="${$1}" class="emoji" loading="lazy">`;
}
}

View File

@ -18,7 +18,7 @@ export function genTree(toc, maxLevel) {
}
if (last[len]) {
last[len].children = (last[len].children || []).concat(headline);
last[len].children = [...(last[len].children || []), headline];
} else {
headlines.push(headline);
}

View File

@ -1,11 +1,10 @@
/* eslint-disable no-unused-vars */
import tinydate from 'tinydate';
import * as dom from '../util/dom.js';
import cssVars from '../util/polyfill/css-vars.js';
import { getAndActive, sticky } from '../event/sidebar.js';
import { getPath, isAbsolutePath } from '../router/util.js';
import { isMobile, inBrowser } from '../util/env.js';
import { isPrimitive, merge } from '../util/core.js';
import { isPrimitive } from '../util/core.js';
import { scrollActiveSidebar } from '../event/scroll.js';
import { Compiler } from './compiler.js';
import * as tpl from './tpl.js';
@ -56,7 +55,7 @@ function renderMain(html) {
};
if (!html) {
html = '<h1>404 - Not found</h1>';
html = /* html */ `<h1>404 - Not found</h1>`;
}
if ('Vue' in window) {
@ -167,15 +166,14 @@ function renderMain(html) {
})
.map(elm => {
// Clone global configuration
const vueConfig = merge({}, docsifyConfig.vueGlobalOptions || {});
const vueConfig = {
...docsifyConfig.vueGlobalOptions,
};
// Replace vueGlobalOptions data() return value with shared data object.
// This provides a global store for all Vue instances that receive
// vueGlobalOptions as their configuration.
if (vueGlobalData) {
vueConfig.data = function () {
return vueGlobalData;
};
vueConfig.data = () => vueGlobalData;
}
return [elm, vueConfig];
@ -458,8 +456,6 @@ export function Render(Base) {
dom.$.head.appendChild(
dom.create('div', tpl.theme(config.themeColor)).firstElementChild
);
// Polyfll
cssVars(config.themeColor);
}
this._updateRender();

View File

@ -16,13 +16,15 @@ function init() {
/**
* Render progress bar
* @param {{step: number, loaded?: undefined, total?: undefined} | {step?: undefined, loaded: number, total: number}} info
*/
export default function ({ loaded, total, step }) {
export default function (info) {
const { loaded, total, step } = info;
let num;
!barEl && init();
if (step) {
if (typeof step !== 'undefined') {
num = parseInt(barEl.style.width || 0, 10) + step;
num = num > 80 ? 80 : num;
} else {

View File

@ -1,5 +1,3 @@
import { hasOwn } from '../util/core.js';
let cache = {};
const re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g;
@ -22,7 +20,7 @@ export function slugify(str) {
.replace(/^(\d)/, '_$1');
let count = cache[slug];
count = hasOwn.call(cache, slug) ? count + 1 : 0;
count = Object.keys(cache).includes(slug) ? count + 1 : 0;
cache[slug] = count;
if (count) {

View File

@ -17,15 +17,15 @@ export function corner(data, cornerExternalLinkTarget) {
// Double check
cornerExternalLinkTarget = cornerExternalLinkTarget || '_blank';
return (
`<a href="${data}" target="${cornerExternalLinkTarget}" class="github-corner" aria-label="View source on Github">` +
'<svg viewBox="0 0 250 250" aria-hidden="true">' +
'<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>' +
'<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>' +
'<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path>' +
'</svg>' +
'</a>'
);
return /* html */ `
<a href="${data}" target="${cornerExternalLinkTarget}" class="github-corner" aria-label="View source on Github">
<svg viewBox="0 0 250 250" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path>
</svg>
</a>
`;
}
/**
@ -36,27 +36,33 @@ export function corner(data, cornerExternalLinkTarget) {
export function main(config) {
const name = config.name ? config.name : '';
const aside =
'<button class="sidebar-toggle" aria-label="Menu">' +
'<div class="sidebar-toggle-button">' +
'<span></span><span></span><span></span>' +
'</div>' +
'</button>' +
'<aside class="sidebar">' +
(config.name
? `<h1 class="app-name"><a class="app-name-link" data-nosearch>${
config.logo ? `<img alt="${name}" src=${config.logo}>` : name
}</a></h1>`
: '') +
'<div class="sidebar-nav"><!--sidebar--></div>' +
'</aside>';
return (
`<main>${aside}` +
'<section class="content">' +
'<article class="markdown-section" id="main"><!--main--></article>' +
'</section>' +
'</main>'
);
const aside = /* html */ `
<button class="sidebar-toggle" aria-label="Menu">
<div class="sidebar-toggle-button">
<span></span><span></span><span></span>
</div>
</button>
<aside class="sidebar">
${
config.name
? /* html */ `
<h1 class="app-name"><a class="app-name-link" data-nosearch>${
config.logo ? `<img alt="${name}" src=${config.logo} />` : name
}</a></h1>
`
: ''
}
<div class="sidebar-nav"><!--sidebar--></div>
</aside>
`;
return /* html */ `
<main>${aside}
<section class="content">
<article class="markdown-section" id="main"><!--main--></article>
</section>
</main>
`;
}
/**
@ -65,17 +71,20 @@ export function main(config) {
*/
export function cover() {
const SL = ', 100%, 85%';
const bgc =
'linear-gradient(to left bottom, ' +
`hsl(${Math.floor(Math.random() * 255) + SL}) 0%,` +
`hsl(${Math.floor(Math.random() * 255) + SL}) 100%)`;
const bgc = `
linear-gradient(
to left bottom,
hsl(${Math.floor(Math.random() * 255) + SL}) 0%,
hsl(${Math.floor(Math.random() * 255) + SL}) 100%
)
`;
return (
`<section class="cover show" style="background: ${bgc}">` +
'<div class="mask"></div>' +
'<div class="cover-main"><!--cover--></div>' +
'</section>'
);
return /* html */ `
<section class="cover show" style="background: ${bgc}">
<div class="mask"></div>
<div class="cover-main"><!--cover--></div>
</section>
`;
}
/**
@ -84,7 +93,10 @@ export function cover() {
* @param {String} tpl TPL list
* @return {String} Rendered tree
*/
export function tree(toc, tpl = '<ul class="app-sub-sidebar">{inner}</ul>') {
export function tree(
toc,
tpl = /* html */ `<ul class="app-sub-sidebar">{inner}</ul>`
) {
if (!toc || !toc.length) {
return '';
}
@ -92,7 +104,7 @@ export function tree(toc, tpl = '<ul class="app-sub-sidebar">{inner}</ul>') {
let innerHTML = '';
toc.forEach(node => {
const title = node.title.replace(/(<([^>]+)>)/g, '');
innerHTML += `<li><a class="section-link" href="${node.slug}" title="${title}">${node.title}</a></li>`;
innerHTML += /* html */ `<li><a class="section-link" href="${node.slug}" title="${title}">${node.title}</a></li>`;
if (node.children) {
innerHTML += tree(node.children, tpl);
}
@ -101,9 +113,12 @@ export function tree(toc, tpl = '<ul class="app-sub-sidebar">{inner}</ul>') {
}
export function helper(className, content) {
return `<p class="${className}">${content.slice(5).trim()}</p>`;
return /* html */ `<p class="${className}">${content.slice(5).trim()}</p>`;
}
/**
* @deprecated
*/
export function theme(color) {
return `<style>:root{--theme-color: ${color};}</style>`;
return /* html */ `<style>:root{--theme-color: ${color};}</style>`;
}

View File

@ -6,7 +6,7 @@ import {
replaceSlug,
resolvePath,
} from '../util.js';
import { noop, merge } from '../../util/core.js';
import { noop } from '../../util/core.js';
const cached = {};
@ -69,7 +69,7 @@ export class History {
const local = currentRoute && path[0] === '#';
const route = this.parse(replaceSlug(path));
route.query = merge({}, route.query, params);
route.query = { ...route.query, ...params };
path = route.path + stringifyQuery(route.query);
path = path.replace(/\.md(\?)|\.md$/, '$1');

View File

@ -1,6 +1,5 @@
import { isExternal, noop } from '../../util/core.js';
import { on } from '../../util/dom.js';
import { endsWith } from '../../util/str.js';
import { parseQuery, cleanPath, replaceSlug } from '../util.js';
import { History } from './base.js';
@ -23,7 +22,7 @@ export class HashHistory extends History {
// prevents the `/index.html` part of the URI from being
// remove during routing.
// See here: https://github.com/docsifyjs/docsify/pull/1372
const basePath = endsWith(path, '.html')
const basePath = path.endsWith('.html')
? path + '#/' + base
: path + '/' + base;
return /^(\/|https?:)/g.test(base) ? base : cleanPath(basePath);

View File

@ -1,4 +1,3 @@
import { supportsPushState } from '../util/env.js';
import * as dom from '../util/dom.js';
import { noop } from '../util/core.js';
import { HashHistory } from './history/hash.js';
@ -39,7 +38,7 @@ export function Router(Base) {
const mode = config.routerMode || 'hash';
let router;
if (mode === 'history' && supportsPushState) {
if (mode === 'history') {
router = new HTML5History(config);
} else {
router = new HashHistory(config);

View File

@ -13,7 +13,7 @@ export function parseQuery(query) {
}
// Simple parse
query.split('&').forEach(function (param) {
query.split('&').forEach(param => {
const parts = param.replace(/\+/g, ' ').split('=');
res[parts[0]] = parts[1] && decode(parts[1]);
@ -64,8 +64,7 @@ export const cleanPath = cached(path => {
export const resolvePath = cached(path => {
const segments = path.replace(/^\//, '').split('/');
let resolved = [];
for (let i = 0, len = segments.length; i < len; i++) {
const segment = segments[i];
for (const segment of segments) {
if (segment === '..') {
resolved.pop();
} else if (segment !== '.') {

View File

@ -1,9 +1,11 @@
/**
* Create a cached version of a pure function.
* Create a cached version of fn that given an input string returns a
* cached return value mapped from the string, regardless if fn is a new function every time.
* created.
* @param {*} fn The function call to be cached
* @void
*/
// TODO Replace this with a proper memo(fn) based on args per function.
export function cached(fn) {
const cache = Object.create(null);
return function (str) {
@ -20,33 +22,10 @@ export const hyphenate = cached(str => {
return str.replace(/([A-Z])/g, m => '-' + m.toLowerCase());
});
export const hasOwn = Object.prototype.hasOwnProperty;
/**
* Simple Object.assign polyfill
* @param {Object} to The object to be merged with
* @returns {Object} The merged object
*/
export const merge =
Object.assign ||
function (to) {
for (let i = 1; i < arguments.length; i++) {
const from = Object(arguments[i]);
for (const key in from) {
if (hasOwn.call(from, key)) {
to[key] = from[key];
}
}
}
return to;
};
/**
* Check if value is primitive
* @param {*} value Checks if a value is primitive
* @returns {Boolean} Result of the check
* @returns {value is string | number} Result of the check
*/
export function isPrimitive(value) {
return typeof value === 'string' || typeof value === 'number';
@ -61,7 +40,7 @@ export function noop() {}
/**
* Check if value is function
* @param {*} obj Any javascript object
* @returns {Boolean} True if the passed-in value is a function
* @returns {obj is Function} True if the passed-in value is a function
*/
export function isFn(obj) {
return typeof obj === 'function';

View File

@ -7,7 +7,7 @@ const cacheNode = {};
* Get Node
* @param {String|Element} el A DOM element
* @param {Boolean} noCache Flag to use or not use the cache
* @return {Element} The found node element
* @return {HTMLElement | SVGElement} The found node element
*/
export function getNode(el, noCache = false) {
if (typeof el === 'string') {
@ -46,13 +46,11 @@ export function find(el, node) {
* @param {Element} node The query
* @returns {Array<Element>} An array of DOM elements
* @example
* findAll('a') => [].slice.call(document.querySelectorAll('a'))
* findAll(nav, 'a') => [].slice.call(nav.querySelectorAll('a'))
* findAll('a') => Array.from(document.querySelectorAll('a'))
* findAll(nav, 'a') => Array.from(nav.querySelectorAll('a'))
*/
export function findAll(el, node) {
return [].slice.call(
node ? el.querySelectorAll(node) : $.querySelectorAll(el)
);
return Array.from(node ? el.querySelectorAll(node) : $.querySelectorAll(el));
}
export function create(node, tpl) {
@ -86,8 +84,8 @@ export function off(el, type, handler) {
/**
* Toggle class
* @param {String|Element} el The element that needs the class to be toggled
* @param {Element} type The type of action to be performed on the classList (toggle by default)
* @param {Element|null} el The element that needs the class to be toggled
* @param {string} type The type of action to be performed on the classList (toggle by default)
* @param {String} val Name of the class to be toggled
* @void
* @example

View File

@ -1,21 +1,3 @@
export const inBrowser = true; // True for now, may change when we add SSR.
export const isMobile = inBrowser && document.body.clientWidth <= 600;
/**
* @see https://github.com/MoOx/pjax/blob/master/lib/is-supported.js
*/
export const supportsPushState =
inBrowser &&
(function () {
// Borrowed wholesale from https://github.com/defunkt/jquery-pjax
return (
window.history &&
window.history.pushState &&
window.history.replaceState &&
// PushState isnt reliable on iOS until 5.
!navigator.userAgent.match(
/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/
)
);
})();

View File

@ -1,36 +0,0 @@
import * as dom from '../dom.js';
import { get } from '../../fetch/ajax.js';
function replaceVar(block, color) {
block.innerHTML = block.innerHTML.replace(
/var\(\s*--theme-color.*?\)/g,
color
);
}
export default function (color) {
// Variable support
if (window.CSS && window.CSS.supports && window.CSS.supports('(--v:red)')) {
return;
}
const styleBlocks = dom.findAll('style:not(.inserted),link');
[].forEach.call(styleBlocks, block => {
if (block.nodeName === 'STYLE') {
replaceVar(block, color);
} else if (block.nodeName === 'LINK') {
const href = block.getAttribute('href');
if (!/\.css$/.test(href)) {
return;
}
get(href).then(res => {
const style = dom.create('style', res);
dom.head.appendChild(style);
replaceVar(style, color);
});
}
});
}

View File

@ -1,11 +1,3 @@
export function startsWith(str, prefix) {
return str.indexOf(prefix) === 0;
}
export function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
export function removeDocsifyIgnoreTag(str) {
return str
.replace(/<!-- {docsify-ignore} -->/, '')

View File

@ -1,21 +1,17 @@
import { startsWith, endsWith } from '../util/str.js';
/**
* Adds beginning of input (^) and end of input ($) assertions if needed into a regex string
* @param {string} matcher the string to match
* @returns {string}
*/
export function makeExactMatcher(matcher) {
const matcherWithBeginningOfInput = startsWith(matcher, '^')
const matcherWithBeginningOfInput = matcher.startsWith('^')
? matcher
: `^${matcher}`;
const matcherWithBeginningAndEndOfInput = endsWith(
matcherWithBeginningOfInput,
'$'
)
? matcherWithBeginningOfInput
: `${matcherWithBeginningOfInput}$`;
const matcherWithBeginningAndEndOfInput =
matcherWithBeginningOfInput.endsWith('$')
? matcherWithBeginningOfInput
: `${matcherWithBeginningOfInput}$`;
return matcherWithBeginningAndEndOfInput;
}

View File

@ -83,7 +83,7 @@ export function VirtualRoutes(Base) {
}
return {
then: function (cb) {
then(cb) {
done = cb;
asyncMatchNextRoute();
},

View File

@ -1,4 +1,3 @@
/* eslint-disable no-unused-vars */
const fixedPath = location.href.replace('/-/', '/#/');
if (fixedPath !== location.href) {
location.href = fixedPath;
@ -28,7 +27,7 @@ function install(hook, vm) {
dom.appendTo(dom.find('.content'), div);
// eslint-disable-next-line
window.disqus_config = function() {
window.disqus_config = function () {
this.page.url = location.origin + '/-' + vm.route.path;
this.page.identifier = vm.route.path;
this.page.title = document.title;
@ -39,7 +38,7 @@ function install(hook, vm) {
if (typeof window.DISQUS !== 'undefined') {
window.DISQUS.reset({
reload: true,
config: function () {
config() {
this.page.url = location.origin + '/-' + vm.route.path;
this.page.identifier = vm.route.path;
this.page.title = document.title;
@ -49,4 +48,5 @@ function install(hook, vm) {
});
}
$docsify.plugins = [].concat(install, $docsify.plugins);
window.$docsify = window.$docsify || {};
$docsify.plugins = [install, ...($docsify.plugins || [])];

View File

@ -7,7 +7,7 @@ if (window && window.console) {
// Emoji from GitHub API
window.emojify = function (match, $1) {
return Object.prototype.hasOwnProperty.call(emojiData.data, $1) === false
? match
: `<img src="${emojiData.baseURL}${emojiData.data[$1]}" alt="${$1}" class="emoji" />`;
return $1 in emojiData.data
? /* html */ `<img src="${emojiData.baseURL}${emojiData.data[$1]}" alt="${$1}" class="emoji" />`
: match;
};

View File

@ -2,13 +2,11 @@ function handleExternalScript() {
const container = Docsify.dom.getNode('#main');
const scripts = Docsify.dom.findAll(container, 'script');
for (let i = scripts.length; i--; ) {
const script = scripts[i];
if (script && script.src) {
for (const script of scripts) {
if (script.src) {
const newScript = document.createElement('script');
Array.prototype.slice.call(script.attributes).forEach(attribute => {
Array.from(script.attributes).forEach(attribute => {
newScript[attribute.name] = attribute.value;
});
@ -22,4 +20,5 @@ const install = function (hook) {
hook.doneEach(handleExternalScript);
};
window.$docsify.plugins = [].concat(install, window.$docsify.plugins);
window.$docsify = window.$docsify || {};
$docsify.plugins = [install, ...($docsify.plugins || [])];

View File

@ -18,4 +18,5 @@ const install = function (hook, vm) {
});
};
$docsify.plugins = [].concat(install, $docsify.plugins);
window.$docsify = window.$docsify || {};
$docsify.plugins = [install, ...($docsify.plugins || [])];

View File

@ -4,8 +4,8 @@
/* eslint-disable */
import parser from './yaml.js'
var optionalByteOrderMark = '\\ufeff?'
var pattern =
const optionalByteOrderMark = '\\ufeff?'
const pattern =
'^(' +
optionalByteOrderMark +
'(= yaml =|---)' +
@ -16,12 +16,12 @@ var pattern =
'(?:\\n)?)'
// NOTE: If this pattern uses the 'g' flag the `regex` variable definition will
// need to be moved down into the functions that use it.
var regex = new RegExp(pattern, 'm')
const regex = new RegExp(pattern, 'm')
function extractor(string) {
string = string || ''
var lines = string.split(/(\r?\n)/)
const lines = string.split(/(\r?\n)/)
if (lines[0] && /= yaml =|---/.test(lines[0])) {
return parse(string)
} else {
@ -30,7 +30,7 @@ function extractor(string) {
}
function parse(string) {
var match = regex.exec(string)
const match = regex.exec(string)
if (!match) {
return {
@ -39,17 +39,11 @@ function parse(string) {
}
}
var yaml = match[match.length - 1].replace(/^\s+|\s+$/g, '')
var attributes = parser(yaml) || {}
var body = string.replace(match[0], '')
const yaml = match[match.length - 1].replace(/^\s+|\s+$/g, '')
const attributes = parser(yaml) || {}
const body = string.replace(match[0], '')
return { attributes: attributes, body: body, frontmatter: yaml }
}
function test(string) {
string = string || ''
return regex.test(string)
}
export default extractor

View File

@ -1,5 +1,5 @@
/**
* Fork https://github.com/egoist/docute/blob/master/src/utils/yaml.js
* Forked from https://github.com/egoist/docute/blob/master/src/utils/yaml.js
*/
/* eslint-disable */
/*
@ -75,37 +75,6 @@ function Block(lvl) {
}
}
// function to create an XMLHttpClient in a cross-browser manner
function createXMLHTTPRequest() {
var xmlhttp
try {
// Mozilla / Safari / IE7
xmlhttp = new XMLHttpRequest()
} catch (e) {
// IE
var XMLHTTP_IDS = new Array(
'MSXML2.XMLHTTP.5.0',
'MSXML2.XMLHTTP.4.0',
'MSXML2.XMLHTTP.3.0',
'MSXML2.XMLHTTP',
'Microsoft.XMLHTTP'
)
var success = false
for (var i = 0; i < XMLHTTP_IDS.length && !success; i++) {
try {
xmlhttp = new ActiveXObject(XMLHTTP_IDS[i])
success = true
} catch (e) {}
}
if (!success) {
throw new Error('Unable to create XMLHttpRequest.')
}
}
return xmlhttp
}
function parser(str) {
var regLevel = regex['regLevel']
var invalidLine = regex['invalidLine']

View File

@ -1,4 +1,3 @@
/* eslint-disable no-console */
// From https://github.com/egoist/vue-ga/blob/master/src/index.js
function appendScript() {
const script = document.createElement('script');
@ -37,4 +36,5 @@ const install = function (hook) {
hook.beforeEach(collect);
};
$docsify.plugins = [].concat(install, $docsify.plugins);
window.$docsify = window.$docsify || {};
$docsify.plugins = [install, ...($docsify.plugins || [])];

View File

@ -1,4 +1,4 @@
/* eslint-disable no-unused-vars */
/* global gitalk */
function install(hook) {
const dom = Docsify.dom;
@ -16,9 +16,9 @@ function install(hook) {
el.removeChild(el.firstChild);
}
// eslint-disable-next-line
gitalk.render('gitalk-container');
});
}
$docsify.plugins = [].concat(install, $docsify.plugins);
window.$docsify = window.$docsify || {};
$docsify.plugins = [install, ...($docsify.plugins || [])];

View File

@ -1,4 +1,3 @@
/* eslint-disable no-console */
// From ./ga.js
function appendScript(id) {
@ -69,4 +68,5 @@ const install = function (hook) {
hook.beforeEach(collect);
};
$docsify.plugins = [].concat(install, $docsify.plugins);
window.$docsify = window.$docsify || {};
$docsify.plugins = [install, ...($docsify.plugins || [])];

View File

@ -9,7 +9,7 @@ function init(options) {
window._paq = window._paq || [];
window._paq.push(['trackPageView']);
window._paq.push(['enableLinkTracking']);
setTimeout(function () {
setTimeout(() => {
appendScript(options);
window._paq.push(['setTrackerUrl', options.host + '/matomo.php']);
window._paq.push(['setSiteId', String(options.id)]);
@ -36,4 +36,5 @@ const install = function (hook) {
hook.beforeEach(collect);
};
$docsify.plugins = [].concat(install, $docsify.plugins);
window.$docsify = window.$docsify || {};
$docsify.plugins = [install, ...($docsify.plugins || [])];

View File

@ -111,7 +111,8 @@ function style() {
}
function tpl(defaultValue = '') {
const html = `<div class="input-wrap">
const html = /* html */ `
<div class="input-wrap">
<input type="search" value="${defaultValue}" aria-label="Search text" />
<div class="clear-button">
<svg width="26" height="24">
@ -122,7 +123,7 @@ function tpl(defaultValue = '') {
</div>
</div>
<div class="results-panel"></div>
</div>`;
`;
const el = Docsify.dom.create('div', html);
const aside = Docsify.dom.find('aside');
@ -154,17 +155,19 @@ function doSearch(value) {
let html = '';
matchs.forEach(post => {
html += `<div class="matching-post">
<a href="${post.url}">
<h2>${post.title}</h2>
<p>${post.content}</p>
</a>
</div>`;
html += /* html */ `
<div class="matching-post">
<a href="${post.url}">
<h2>${post.title}</h2>
<p>${post.content}</p>
</a>
</div>
`;
});
$panel.classList.add('show');
$clearBtn.classList.add('show');
$panel.innerHTML = html || `<p class="empty">${NO_DATA_TEXT}</p>`;
$panel.innerHTML = html || /* html */ `<p class="empty">${NO_DATA_TEXT}</p>`;
if (options.hideOtherSidebarContent) {
$sidebarNav && $sidebarNav.classList.add('hide');
$appName && $appName.classList.add('hide');

View File

@ -46,4 +46,5 @@ const install = function (hook, vm) {
});
};
$docsify.plugins = [].concat(install, $docsify.plugins);
window.$docsify = window.$docsify || {};
$docsify.plugins = [install, ...($docsify.plugins || [])];

View File

@ -84,7 +84,7 @@ export function genIndex(path, content = '', router, depth) {
let slug;
let title = '';
tokens.forEach(function (token, tokenIndex) {
tokens.forEach((token, tokenIndex) => {
if (token.type === 'heading' && token.depth <= depth) {
const { str, config } = getAndRemoveConfig(token.text);
@ -149,17 +149,19 @@ export function search(query) {
const matchingResults = [];
let data = [];
Object.keys(INDEXS).forEach(key => {
data = data.concat(Object.keys(INDEXS[key]).map(page => INDEXS[key][page]));
data = [
...data,
...Object.keys(INDEXS[key]).map(page => INDEXS[key][page]),
];
});
query = query.trim();
let keywords = query.split(/[\s\-\\/]+/);
if (keywords.length !== 1) {
keywords = [].concat(query, keywords);
keywords = [query, ...keywords];
}
for (let i = 0; i < data.length; i++) {
const post = data[i];
for (const post of data) {
let matchesScore = 0;
let resultStr = '';
let handlePostTitle = '';
@ -213,7 +215,7 @@ export function search(query) {
.substring(start, end)
.replace(
regEx,
word => `<em class="search-keyword">${word}</em>`
word => /* html */ `<em class="search-keyword">${word}</em>`
) +
'...';

View File

@ -1,23 +1,16 @@
/* eslint-disable no-unused-vars */
import mediumZoom from 'medium-zoom';
const matchesSelector =
Element.prototype.matches ||
Element.prototype.webkitMatchesSelector ||
Element.prototype.msMatchesSelector;
function install(hook) {
let zoom;
hook.doneEach(_ => {
let elms = Array.apply(
null,
let elms = Array.from(
document.querySelectorAll(
'.markdown-section img:not(.emoji):not([data-no-zoom])'
)
);
elms = elms.filter(elm => matchesSelector.call(elm, 'a img') === false);
elms = elms.filter(elm => !elm.matches('a img'));
if (zoom) {
zoom.detach();
@ -27,4 +20,5 @@ function install(hook) {
});
}
$docsify.plugins = [].concat(install, $docsify.plugins);
window.$docsify = window.$docsify || {};
$docsify.plugins = [install, ...($docsify.plugins || [])];

View File

@ -1,6 +1,7 @@
import { create } from 'browser-sync';
import path from 'path';
import url from 'url';
import { noop } from '../../src/core/util/core.js';
const browserSync = create();
@ -15,13 +16,13 @@ const __dirname = path.dirname(__filename);
export const TEST_HOST = `http://${serverConfig.hostname}:${serverConfig.port}`;
function startServer(options = {}, cb = Function.prototype) {
function startServer(options = {}, cb = noop) {
const defaults = {
...serverConfig,
middleware: [
{
route: '/_blank.html',
handle: function (req, res, next) {
handle(req, res, next) {
res.setHeader('Content-Type', 'text/html');
res.end('');
next();
@ -50,23 +51,20 @@ function startServer(options = {}, cb = Function.prototype) {
snippetOptions: {
rule: {
match: /<\/body>/i,
fn: function (snippet, match) {
fn(snippet, match) {
// Override changelog alias to load local changelog (see routes)
const newSnippet = `
const newSnippet = /* html */ `
${snippet.replace(/<script[^>]*/, '$& type="text/plain"')}
<script>
(function() {
var aliasConfig = (window && window.$docsify && window.$docsify.alias) || {};
var isIE = /*@cc_on!@*/false || !!document.documentMode;
{
const aliasConfig = (window && window.$docsify && window.$docsify.alias) || {};
// Fix /docs site configuration during tests
aliasConfig['.*?/changelog'] = '/changelog.md';
// Enable BrowserSync snippet for non-IE browsers
if (!isIE) {
document.querySelector('#__bs_script__').removeAttribute('type');
}
})();
// Enable BrowserSync snippet
document.querySelector('#__bs_script__').removeAttribute('type');
}
</script>
${match}
`;

View File

@ -1,3 +1,4 @@
/* global fail */
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';
@ -14,10 +15,9 @@ test.describe('Configuration options', () => {
plugins: [
function (hook, vm) {
hook.init(function () {
// eslint-disable-next-line no-undef
fail();
});
hook.beforeEach(function (markdown) {
hook.beforeEach(markdown => {
return `${markdown}\n\nbeforeEach`;
});
},
@ -49,7 +49,6 @@ test.describe('Configuration options', () => {
plugins: [
function (hook, vm) {
hook.ready(function () {
// eslint-disable-next-line no-undef
fail();
});
},

View File

@ -21,43 +21,43 @@ test.describe('Plugins', () => {
config: {
plugins: [
function (hook, vm) {
hook.init(function () {
hook.init(() => {
console.log('init');
});
hook.mounted(function () {
hook.mounted(() => {
console.log('mounted');
});
hook.beforeEach(function (markdown, next) {
setTimeout(function () {
hook.beforeEach((markdown, next) => {
setTimeout(() => {
console.log('beforeEach-async');
next(markdown);
}, 100);
});
hook.beforeEach(function (markdown) {
hook.beforeEach(markdown => {
console.log('beforeEach');
return markdown;
});
hook.afterEach(function (html, next) {
setTimeout(function () {
hook.afterEach((html, next) => {
setTimeout(() => {
console.log('afterEach-async');
next(html);
}, 100);
});
hook.afterEach(function (html) {
hook.afterEach(html => {
console.log('afterEach');
return html;
});
hook.doneEach(function () {
hook.doneEach(() => {
console.log('doneEach');
});
hook.ready(function () {
hook.ready(() => {
console.log('ready');
});
},
@ -77,7 +77,7 @@ test.describe('Plugins', () => {
config: {
plugins: [
function (hook, vm) {
hook.beforeEach(function (markdown) {
hook.beforeEach(markdown => {
return 'beforeEach';
});
},
@ -94,8 +94,8 @@ test.describe('Plugins', () => {
config: {
plugins: [
function (hook, vm) {
hook.beforeEach(function (markdown, next) {
setTimeout(function () {
hook.beforeEach((markdown, next) => {
setTimeout(() => {
next('beforeEach');
}, 100);
});
@ -116,7 +116,7 @@ test.describe('Plugins', () => {
config: {
plugins: [
function (hook, vm) {
hook.afterEach(function (html) {
hook.afterEach(html => {
return '<p>afterEach</p>';
});
},
@ -136,8 +136,8 @@ test.describe('Plugins', () => {
config: {
plugins: [
function (hook, vm) {
hook.afterEach(function (html, next) {
setTimeout(function () {
hook.afterEach((html, next) => {
setTimeout(() => {
next('<p>afterEach</p>');
}, 100);
});

View File

@ -101,9 +101,7 @@ test.describe('Virtual Routes - Generate Dynamic Content via Config', () => {
page,
}) => {
const routes = {
'/pets/(.*)': function (route) {
return `# Route: /pets/dog`;
},
'/pets/(.*)': route => `# Route: /pets/dog`,
};
await docsifyInit({
@ -122,7 +120,7 @@ test.describe('Virtual Routes - Generate Dynamic Content via Config', () => {
page,
}) => {
const routes = {
'/pets/(.*)': function (_, matched) {
'/pets/(.*)'(_, matched) {
return `# Pets Page (${matched[1]})`;
},
};

View File

@ -24,16 +24,14 @@ test.describe('Vue.js Compatibility', () => {
},
},
vueGlobalOptions: {
data: function () {
return {
counter: 0,
msg: 'vueglobaloptions',
};
},
data: () => ({
counter: 0,
msg: 'vueglobaloptions',
}),
},
vueMounts: {
'#vuemounts': {
data: function () {
data() {
return {
counter: 0,
msg: 'vuemounts',

View File

@ -225,11 +225,29 @@ async function docsifyInit(options = {}) {
await page.evaluate(config => {
// Restore config functions from strings
const configObj = JSON.parse(config, (key, val) =>
/^__FN__/.test(val)
? new Function(`return ${val.split('__FN__')[1]}`)()
: val
);
const configObj = JSON.parse(config, (key, val) => {
if (/^__FN__/.test(val)) {
let source = val.split('__FN__')[1];
// f.e. `foo() {}` or `'bar!?'() {}` without the `function ` prefix
const isConcise =
!source.includes('function') && !source.includes('=>');
if (isConcise) {
source = `{ ${source} }`;
} else {
source = `{ _: ${source} }`;
}
return new Function(/* js */ `
const o = ${source}
const keys = Object.keys(o)
return o[keys[0]]
`)();
} else {
return val;
}
});
window.$docsify = configObj;
}, configString);

View File

@ -1,12 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Docs Site coverpage renders and is unchanged 1`] = `
"<section class=\\"cover show\\" style=\\"background: linear-gradient(to left bottom, hsl(127, 100%, 85%) 0%,hsl(127, 100%, 85%) 100%)\\"><div class=\\"mask\\"></div><div class=\\"cover-main\\"><p><img src=\\"http://127.0.0.1:3001/_media/icon.svg\\" data-origin=\\"_media/icon.svg\\" alt=\\"logo\\"></p><h1 id=\\"docsify-4130\\"><a href=\\"#/?id=docsify-4130\\" data-id=\\"docsify-4130\\" class=\\"anchor\\"><span>docsify <small>4.13.0</small></span></a></h1><blockquote>
"<section class=\\"cover show\\" style=\\"background:
linear-gradient(
to left bottom,
hsl(127, 100%, 85%) 0%,
hsl(127, 100%, 85%) 100%
)
\\">
<div class=\\"mask\\"></div>
<div class=\\"cover-main\\"><p><img src=\\"http://127.0.0.1:3001/_media/icon.svg\\" data-origin=\\"_media/icon.svg\\" alt=\\"logo\\"></p><h1 id=\\"docsify-4130\\"><a href=\\"#/?id=docsify-4130\\" data-id=\\"docsify-4130\\" class=\\"anchor\\"><span>docsify <small>4.13.0</small></span></a></h1><blockquote>
<p>A magical documentation site generator.</p></blockquote>
<ul><li>Simple and lightweight</li><li>No statically built html files</li><li>Multiple themes</li></ul><p><a href=\\"https://github.com/docsifyjs/docsify/\\" target=\\"_blank\\" rel=\\"noopener\\">GitHub</a>
<a href=\\"#/?id=docsify\\">Getting Started</a></p></div></section>"
<a href=\\"#/?id=docsify\\">Getting Started</a></p></div>
</section>"
`;
exports[`Docs Site navbar renders and is unchanged 1`] = `"<nav class=\\"app-nav no-badge\\"><ul><li>Translations<ul><li><a href=\\"#/\\" title=\\"undefined\\" class=\\"active\\"><img src=\\"https://github.githubassets.com/images/icons/emoji/unicode/1f1ec-1f1e7.png?v8.png\\" alt=\\"uk\\" class=\\"emoji\\" loading=\\"lazy\\"> English</a></li><li><a href=\\"#/zh-cn/\\" title=\\"undefined\\"><img src=\\"https://github.githubassets.com/images/icons/emoji/unicode/1f1e8-1f1f3.png?v8.png\\" alt=\\"cn\\" class=\\"emoji\\" loading=\\"lazy\\"> 简体中文</a></li><li><a href=\\"#/de-de/\\" title=\\"undefined\\"><img src=\\"https://github.githubassets.com/images/icons/emoji/unicode/1f1e9-1f1ea.png?v8.png\\" alt=\\"de\\" class=\\"emoji\\" loading=\\"lazy\\"> Deutsch</a></li><li><a href=\\"#/es/\\" title=\\"undefined\\"><img src=\\"https://github.githubassets.com/images/icons/emoji/unicode/1f1ea-1f1f8.png?v8.png\\" alt=\\"es\\" class=\\"emoji\\" loading=\\"lazy\\"> Español</a></li><li><a href=\\"#/ru-ru/\\" title=\\"undefined\\"><img src=\\"https://github.githubassets.com/images/icons/emoji/unicode/1f1f7-1f1fa.png?v8.png\\" alt=\\"ru\\" class=\\"emoji\\" loading=\\"lazy\\"> Русский</a></li></ul></li></ul></nav>"`;
exports[`Docs Site sidebar renders and is unchanged 1`] = `"<aside class=\\"sidebar\\"><div class=\\"sidebar-nav\\"><ul><li><p>Getting started</p><ul><li><a href=\\"#/quickstart\\" title=\\"undefined\\">Quick start</a></li><li><a href=\\"#/more-pages\\" title=\\"undefined\\">Writing more pages</a></li><li><a href=\\"#/custom-navbar\\" title=\\"undefined\\">Custom navbar</a></li><li><a href=\\"#/cover\\" title=\\"undefined\\">Cover page</a></li></ul></li><li><p>Customization</p><ul><li><a href=\\"#/configuration\\" title=\\"undefined\\">Configuration</a></li><li><a href=\\"#/themes\\" title=\\"undefined\\">Themes</a></li><li><a href=\\"#/plugins\\" title=\\"undefined\\">List of Plugins</a></li><li><a href=\\"#/write-a-plugin\\" title=\\"undefined\\">Write a Plugin</a></li><li><a href=\\"#/markdown\\" title=\\"undefined\\">Markdown configuration</a></li><li><a href=\\"#/language-highlight\\" title=\\"undefined\\">Language highlighting</a></li><li><a href=\\"#/emoji\\" title=\\"undefined\\">Emoji</a></li></ul></li><li><p>Guide</p><ul><li><a href=\\"#/deploy\\" title=\\"undefined\\">Deploy</a></li><li><a href=\\"#/helpers\\" title=\\"undefined\\">Helpers</a></li><li><a href=\\"#/vue\\" title=\\"undefined\\">Vue compatibility</a></li><li><a href=\\"#/cdn\\" title=\\"undefined\\">CDN</a></li><li><a href=\\"#/pwa\\" title=\\"undefined\\">Offline Mode (PWA)</a></li><li><a href=\\"#/embed-files\\" title=\\"undefined\\">Embed Files</a></li></ul></li><li><p><a href=\\"#/awesome\\" title=\\"undefined\\">Awesome docsify</a></p></li><li><p><a href=\\"#/changelog\\" title=\\"undefined\\">Changelog</a></p></li></ul></div></aside>"`;
exports[`Docs Site sidebar renders and is unchanged 1`] = `
"<aside class=\\"sidebar\\">
<div class=\\"sidebar-nav\\"><ul><li><p>Getting started</p><ul><li><a href=\\"#/quickstart\\" title=\\"undefined\\">Quick start</a></li><li><a href=\\"#/more-pages\\" title=\\"undefined\\">Writing more pages</a></li><li><a href=\\"#/custom-navbar\\" title=\\"undefined\\">Custom navbar</a></li><li><a href=\\"#/cover\\" title=\\"undefined\\">Cover page</a></li></ul></li><li><p>Customization</p><ul><li><a href=\\"#/configuration\\" title=\\"undefined\\">Configuration</a></li><li><a href=\\"#/themes\\" title=\\"undefined\\">Themes</a></li><li><a href=\\"#/plugins\\" title=\\"undefined\\">List of Plugins</a></li><li><a href=\\"#/write-a-plugin\\" title=\\"undefined\\">Write a Plugin</a></li><li><a href=\\"#/markdown\\" title=\\"undefined\\">Markdown configuration</a></li><li><a href=\\"#/language-highlight\\" title=\\"undefined\\">Language highlighting</a></li><li><a href=\\"#/emoji\\" title=\\"undefined\\">Emoji</a></li></ul></li><li><p>Guide</p><ul><li><a href=\\"#/deploy\\" title=\\"undefined\\">Deploy</a></li><li><a href=\\"#/helpers\\" title=\\"undefined\\">Helpers</a></li><li><a href=\\"#/vue\\" title=\\"undefined\\">Vue compatibility</a></li><li><a href=\\"#/cdn\\" title=\\"undefined\\">CDN</a></li><li><a href=\\"#/pwa\\" title=\\"undefined\\">Offline Mode (PWA)</a></li><li><a href=\\"#/embed-files\\" title=\\"undefined\\">Embed Files</a></li></ul></li><li><p><a href=\\"#/awesome\\" title=\\"undefined\\">Awesome docsify</a></p></li><li><p><a href=\\"#/changelog\\" title=\\"undefined\\">Changelog</a></p></li></ul></div>
</aside>"
`;

View File

@ -137,7 +137,7 @@ describe('Emoji', function () {
test('Ignores emoji shorthand codes in html attributes', async () => {
await docsifyInit({
markdown: {
homepage: `<a href="http://domain.com/:smile:/"> <img src='http://domain.com/:smile:/file.png'> <script src=http://domain.com/:smile:/file.js></script>`,
homepage: /* html */ `<a href="http://domain.com/:smile:/"> <img src='http://domain.com/:smile:/file.png'> <script src=http://domain.com/:smile:/file.js></script>`,
},
// _logHTML: true,
});
@ -150,7 +150,7 @@ describe('Emoji', function () {
test('Ignores emoji shorthand codes in style url() values', async () => {
await docsifyInit({
markdown: {
homepage: `<style>@import url(http://domain.com/:smile/file.css);</style>`,
homepage: /* html */ `<style>@import url(http://domain.com/:smile/file.css);</style>`,
},
// _logHTML: true,
});
@ -163,7 +163,7 @@ describe('Emoji', function () {
test('Ignores emoji shorthand codes in code, pre, script, and template tags', async () => {
await docsifyInit({
markdown: {
homepage: `
homepage: /* html */ `
<pre>:100:</pre>
<code>:100:</code>

View File

@ -104,7 +104,7 @@ describe('render', function () {
const output = window.marked('![alt text](http://imageUrl)');
expect(output).toMatchInlineSnapshot(
`"<p><img src=\\"http://imageUrl\\" data-origin=\\"http://imageUrl\\" alt=\\"alt text\\"></p>"`
`"<p><img src=\\"http://imageUrl\\" data-origin=\\"http://imageUrl\\" alt=\\"alt text\\" /></p>"`
);
});

View File

@ -122,7 +122,7 @@ describe('core/render/tpl', () => {
]);
expect(result).toBe(
`<ul class="app-sub-sidebar"><li><a class="section-link" href="#/cover?id=basic-usage" title="Basic usage"><span style="color:red">Basic usage</span></a></li><li><a class="section-link" href="#/cover?id=custom-background" title="Custom background">Custom background</a></li><li><a class="section-link" href="#/cover?id=test" title="Test"><img src="/docs/_media/favicon.ico" data-origin="/_media/favicon.ico" alt="ico">Test</a></li></ul>`
/* html */ `<ul class="app-sub-sidebar"><li><a class="section-link" href="#/cover?id=basic-usage" title="Basic usage"><span style="color:red">Basic usage</span></a></li><li><a class="section-link" href="#/cover?id=custom-background" title="Custom background">Custom background</a></li><li><a class="section-link" href="#/cover?id=test" title="Test"><img src="/docs/_media/favicon.ico" data-origin="/_media/favicon.ico" alt="ico">Test</a></li></ul>`
);
});
});