Eslint fixes for v4x (#989)

* UPDATE .eslintrc

* UPDATE lint task

* FIX lint errors

* CLEANUP

* FIX no-eq-null warning

* FIX many jsdoc warnings

* FIX jsdoc issues

* FIX jsdoc warnings

* FIX jsdoc

* FIX eqeq and no-eq-null

* ADD lint to travis

* UPDATE test env for eslint
This commit is contained in:
Giulio Ambrogi 2019-12-30 16:31:46 +00:00 committed by Anix
parent 157973c509
commit db97a1d22a
33 changed files with 455 additions and 344 deletions

View File

@ -1,7 +1,10 @@
{
"extends": "xo-space/browser",
"rules": {
"semi": [2, "never"],
"semi": [
2,
"never"
],
"no-return-assign": "off",
"no-unused-expressions": "off",
"no-new-func": "off",
@ -10,11 +13,22 @@
"max-params": "off",
"no-script-url": "off",
"camelcase": "off",
"no-warning-comments": "off"
"object-curly-spacing": "off",
"no-warning-comments": "off",
"no-negated-condition": "off",
"eqeqeq": "warn",
"no-eq-null": "warn",
"max-statements-per-line": "warn"
},
"globals": {
"Docsify": true,
"$docsify": true,
"process": true
},
"env": {
"browser": true,
"amd": true,
"node": true,
"jest": true
}
}
}

View File

@ -1,3 +1,6 @@
sudo: false
language: node_js
node_js: stable
script:
- npm run lint

View File

@ -5,12 +5,12 @@ const PORT = 8080
/// [demo]
const result = fetch(`${URL}:${PORT}`)
.then(function(response) {
return response.json();
.then(function (response) {
return response.json()
})
.then(function (myJson) {
console.log(JSON.stringify(myJson))
})
.then(function(myJson) {
console.log(JSON.stringify(myJson));
});
/// [demo]
result.then(console.log).catch(console.error)

View File

@ -26,7 +26,7 @@
"serve:ssr": "cross-env SSR=1 node server",
"dev": "run-p serve watch:*",
"dev:ssr": "run-p serve:ssr watch:*",
"lint": "eslint {src,packages} --fix",
"lint": "eslint . --fix",
"test": "mocha test/*/**",
"css": "node build/css",
"watch:css": "npm run css -- -o themes -w",
@ -48,8 +48,8 @@
},
"lint-staged": {
"*.js": [
"npm run lint",
"git add"
"npm run lint",
"git add"
]
},
"dependencies": {
@ -98,4 +98,4 @@
"collective": {
"url": "https://opencollective.com/docsify"
}
}
}

View File

@ -21,6 +21,7 @@ function mainTpl(config) {
if (config.repo) {
html += tpl.corner(config.repo)
}
if (config.coverpage) {
html += tpl.cover()
}
@ -154,12 +155,14 @@ export default class Renderer {
if (!res.ok) {
throw Error()
}
content = await res.text()
this.lock = 0
} else {
content = await readFileSync(filePath, 'utf8')
this.lock = 0
}
return content
} catch (e) {
this.lock = this.lock || 0

View File

@ -23,9 +23,9 @@ export default function () {
ext: '.md',
mergeNavbar: false,
formatUpdated: '',
// this config for the links inside markdown
// This config for the links inside markdown
externalLinkTarget: '_blank',
// this config for the corner
// This config for the corner
cornerExternalLinkTarget: '_blank',
externalLinkRel: 'noopener',
routerMode: 'hash',
@ -55,15 +55,19 @@ export default function () {
if (config.loadSidebar === true) {
config.loadSidebar = '_sidebar' + config.ext
}
if (config.loadNavbar === true) {
config.loadNavbar = '_navbar' + config.ext
}
if (config.coverpage === true) {
config.coverpage = '_coverpage' + config.ext
}
if (config.repo === true) {
config.repo = ''
}
if (config.name === true) {
config.name = ''
}

View File

@ -1,4 +1,4 @@
import {isMobile} from '../util/env'
import { isMobile } from '../util/env'
import * as dom from '../util/dom'
import Tweezer from 'tweezer.js'
@ -12,6 +12,7 @@ function scrollTo(el) {
if (scroller) {
scroller.stop()
}
enableScrollEvent = false
scroller = new Tweezer({
start: window.pageYOffset,
@ -30,6 +31,7 @@ function highlight(path) {
if (!enableScrollEvent) {
return
}
const sidebar = dom.getNode('.sidebar')
const anchors = dom.findAll('.anchor')
const wrap = dom.find(sidebar, '.sidebar-nav')
@ -45,14 +47,17 @@ function highlight(path) {
if (!last) {
last = node
}
break
} else {
last = node
}
}
if (!last) {
return
}
const li = nav[getNavKey(decodeURIComponent(path), last.getAttribute('data-id'))]
if (!li || li === active) {
@ -88,7 +93,7 @@ export function scrollActiveSidebar(router) {
const sidebar = dom.getNode('.sidebar')
let lis = []
if (sidebar != null) {
if (sidebar !== null && sidebar !== undefined) {
lis = dom.findAll(sidebar, 'li')
}
@ -98,10 +103,11 @@ export function scrollActiveSidebar(router) {
if (!a) {
continue
}
let href = a.getAttribute('href')
if (href !== '/') {
const {query: {id}, path} = router.parse(href)
const { query: { id }, path } = router.parse(href)
if (id) {
href = getNavKey(path, id)
}
@ -115,6 +121,7 @@ export function scrollActiveSidebar(router) {
if (isMobile) {
return
}
const path = router.getCurrentPath()
dom.off('scroll', () => highlight(path))
dom.on('scroll', () => highlight(path))

View File

@ -1,17 +1,20 @@
import {isMobile} from '../util/env'
import { isMobile } from '../util/env'
import * as dom from '../util/dom'
const title = dom.$.title
/**
* Toggle button
* @param {Element} el Button to be toggled
* @void
*/
export function btn(el) {
const toggle = _ => dom.body.classList.toggle('close')
el = dom.getNode(el)
if (el == null) {
if (el === null || el === undefined) {
return
}
dom.on(el, 'click', e => {
e.stopPropagation()
toggle()
@ -27,10 +30,11 @@ export function btn(el) {
export function collapse(el) {
el = dom.getNode(el)
if (el == null) {
if (el === null || el === undefined) {
return
}
dom.on(el, 'click', ({target}) => {
dom.on(el, 'click', ({ target }) => {
if (
target.nodeName === 'A' &&
target.nextSibling &&
@ -46,6 +50,7 @@ export function sticky() {
if (!cover) {
return
}
const coverHeight = cover.getBoundingClientRect().height
if (window.pageYOffset >= coverHeight || cover.classList.contains('hidden')) {
@ -57,18 +62,19 @@ export function sticky() {
/**
* Get and active link
* @param {object} router
* @param {string|element} el
* @param {Boolean} isParent acitve parent
* @param {Boolean} autoTitle auto set title
* @return {element}
* @param {Object} router Router
* @param {String|Element} el Target element
* @param {Boolean} isParent Active parent
* @param {Boolean} autoTitle Automatically set title
* @return {Element} Active element
*/
export function getAndActive(router, el, isParent, autoTitle) {
el = dom.getNode(el)
let links = []
if (el != null) {
if (el !== null && el !== undefined) {
links = dom.findAll(el, 'a')
}
const hash = decodeURI(router.toURL(router.getCurrentPath()))
let target

View File

@ -1,23 +1,25 @@
import progressbar from '../render/progressbar'
import {noop, hasOwn} from '../util/core'
import { noop, hasOwn } from '../util/core'
const cache = {}
/**
* Simple ajax get
* @param {string} url
* @param {boolean} [hasBar=false] has progress bar
* @return { then(resolve, reject), abort }
* Ajax GET implmentation
* @param {string} url Resource URL
* @param {boolean} [hasBar=false] Has progress bar
* @param {String[]} headers Array of headers
* @return {Promise} Promise response
*/
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) {
return {then: cb => cb(cached.content, cached.opt), abort: noop}
return { then: cb => cb(cached.content, cached.opt), abort: noop }
}
xhr.open('GET', url)
@ -26,6 +28,7 @@ export function get(url, hasBar = false, headers = {}) {
xhr.setRequestHeader(i, headers[i])
}
}
xhr.send()
return {
@ -47,7 +50,7 @@ export function get(url, hasBar = false, headers = {}) {
}
on('error', error)
on('load', ({target}) => {
on('load', ({ target }) => {
if (target.status >= 400) {
error(target)
} else {

View File

@ -1,8 +1,8 @@
import {get} from './ajax'
import {callHook} from '../init/lifecycle'
import {getParentPath, stringifyQuery} from '../router/util'
import {noop} from '../util/core'
import {getAndActive} from '../event/sidebar'
import { get } from './ajax'
import { callHook } from '../init/lifecycle'
import { getParentPath, stringifyQuery } from '../router/util'
import { noop } from '../util/core'
import { getAndActive } from '../event/sidebar'
function loadNested(path, qs, file, next, vm, first) {
path = first ? path : path.replace(/\/$/, '')
@ -30,7 +30,7 @@ export function fetchMixin(proto) {
}
const get404Path = (path, config) => {
const {notFoundPage, ext} = config
const { notFoundPage, ext } = config
const defaultPath = '_404' + (ext || '.md')
let key
let path404
@ -75,9 +75,9 @@ export function fetchMixin(proto) {
}
proto._fetch = function (cb = noop) {
const {path, query} = this.route
const { path, query } = this.route
const qs = stringifyQuery(query, ['id'])
const {loadNavbar, requestHeaders, loadSidebar} = this.config
const { loadNavbar, requestHeaders, loadSidebar } = this.config
// Abort last request
const file = this.router.getFile(path)
@ -112,7 +112,7 @@ export function fetchMixin(proto) {
}
proto._fetchCover = function () {
const {coverpage, requestHeaders} = this.config
const { coverpage, requestHeaders } = this.config
const query = this.route.query
const root = getParentPath(this.route.path)
@ -140,6 +140,7 @@ export function fetchMixin(proto) {
} else {
this._renderCover(null, coverOnly)
}
return coverOnly
}
}
@ -163,7 +164,7 @@ export function fetchMixin(proto) {
}
proto._fetchFallbackPage = function (path, qs, cb = noop) {
const {requestHeaders, fallbackLanguages, loadSidebar} = this.config
const { requestHeaders, fallbackLanguages, loadSidebar } = this.config
if (!fallbackLanguages) {
return false
@ -174,6 +175,7 @@ export function fetchMixin(proto) {
if (fallbackLanguages.indexOf(local) === -1) {
return false
}
const newPath = path.replace(new RegExp(`^/${local}`), '')
const req = request(newPath + qs, true, requestHeaders)
@ -189,16 +191,17 @@ export function fetchMixin(proto) {
return true
}
/**
* Load the 404 page
* @param path
* @param qs
* @param cb
* @returns {*}
* @param {String} path URL to be loaded
* @param {*} qs TODO: define
* @param {Function} cb Callback
* @returns {Boolean} True if the requested page is not found
* @private
*/
proto._fetch404 = function (path, qs, cb = noop) {
const {loadSidebar, requestHeaders, notFoundPage} = this.config
const { loadSidebar, requestHeaders, notFoundPage } = this.config
const fnLoadSideAndNav = this._loadSideAndNav(path, qs, loadSidebar, cb)
if (notFoundPage) {
@ -217,7 +220,7 @@ export function fetchMixin(proto) {
}
export function initFetch(vm) {
const {loadSidebar} = vm.config
const { loadSidebar } = vm.config
// Server-Side Rendering
if (vm.rendered) {
@ -225,6 +228,7 @@ export function initFetch(vm) {
if (loadSidebar && activeEl) {
activeEl.parentNode.innerHTML += window.__SUB_SIDEBAR__
}
vm._bindEventOnRendered(activeEl)
vm.$resetEvents()
callHook(vm, 'doneEach')

View File

@ -1,12 +1,15 @@
import {initMixin} from './init'
import {routerMixin} from './router'
import {renderMixin} from './render'
import {fetchMixin} from './fetch'
import {eventMixin} from './event'
import { initMixin } from './init'
import { routerMixin } from './router'
import { renderMixin } from './render'
import { fetchMixin } from './fetch'
import { eventMixin } from './event'
import initGlobalAPI from './global-api'
/**
* Fork https://github.com/bendrucker/document-ready/blob/master/index.js
* @param {Function} callback The callbacack to be called when the page is loaded
* @returns {Number|void} If the page is already laoded returns the result of the setTimeout callback,
* otherwise it only attaches the callback to the DOMContentLoaded event
*/
function ready(callback) {
const state = document.readyState

View File

@ -1,11 +1,11 @@
import marked from 'marked'
import Prism from 'prismjs'
import {helper as helperTpl, tree as treeTpl} from './tpl'
import {genTree} from './gen-tree'
import {slugify} from './slugify'
import {emojify} from './emojify'
import {isAbsolutePath, getPath, getParentPath} from '../router/util'
import {isFn, merge, cached, isPrimitive} from '../util/core'
import { helper as helperTpl, tree as treeTpl } from './tpl'
import { genTree } from './gen-tree'
import { slugify } from './slugify'
import { emojify } from './emojify'
import { isAbsolutePath, getPath, getParentPath } from '../router/util'
import { isFn, merge, cached, isPrimitive } from '../util/core'
// See https://github.com/PrismJS/prism/pull/1367
import 'prismjs/components/prism-markup-templating'
@ -26,7 +26,7 @@ export function getAndRemoveConfig(str = '') {
.trim()
}
return {str, config}
return { str, config }
}
const compileMedia = {
@ -132,7 +132,7 @@ export class Compiler {
}
compileEmbed(href, title) {
const {str, config} = getAndRemoveConfig(title)
const { str, config } = getAndRemoveConfig(title)
let embed
title = str
@ -162,9 +162,11 @@ export class Compiler {
} else if (/\.mp3/.test(href)) {
type = 'audio'
}
embed = compileMedia[type].call(this, href, title)
embed.type = type
}
embed.fragment = config.fragment
return embed
@ -186,17 +188,20 @@ export class Compiler {
_initRenderer() {
const renderer = new marked.Renderer()
const {linkTarget, linkRel, router, contentBase} = this
const { linkTarget, linkRel, router, contentBase } = this
const _self = this
const origin = {}
/**
* Render anchor tag
* @link https://github.com/markedjs/marked#overriding-renderer-methods
* @param {String} text Text content
* @param {Number} level Type of heading (h<level> tag)
* @returns {String} Heading element
*/
origin.heading = renderer.heading = function (text, level) {
let {str, config} = getAndRemoveConfig(text)
const nextToc = {level, title: str}
let { str, config } = getAndRemoveConfig(text)
const nextToc = { level, title: str }
if (/{docsify-ignore}/g.test(str)) {
str = str.replace('{docsify-ignore}', '')
@ -211,12 +216,13 @@ export class Compiler {
}
const slug = slugify(config.id || str)
const url = router.toURL(router.getCurrentPath(), {id: slug})
const url = router.toURL(router.getCurrentPath(), { id: slug })
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}>`
}
// Highlight code
origin.code = renderer.code = function (code, lang = '') {
code = code.replace(/@DOCSIFY_QM@/g, '`')
@ -227,10 +233,11 @@ export class Compiler {
return `<pre v-pre data-lang="${lang}"><code class="lang-${lang}">${hl}</code></pre>`
}
origin.link = renderer.link = function (href, title = '', text) {
let attrs = ''
const {str, config} = getAndRemoveConfig(title)
const { str, config } = getAndRemoveConfig(title)
title = str
if (
@ -241,6 +248,7 @@ export class Compiler {
if (href === _self.config.homepage) {
href = 'README'
}
href = router.toURL(href, null, router.getCurrentPath())
} else {
attrs += href.indexOf('mailto:') === 0 ? '' : ` target="${linkTarget}"`
@ -262,6 +270,7 @@ export class Compiler {
return `<a href="${href}"${attrs}>${text}</a>`
}
origin.paragraph = renderer.paragraph = function (text) {
let result
if (/^!&gt;/.test(text)) {
@ -271,13 +280,15 @@ export class Compiler {
} else {
result = `<p>${text}</p>`
}
return result
}
origin.image = renderer.image = function (href, title, text) {
let url = href
let attrs = ''
const {str, config} = getAndRemoveConfig(title)
const { str, config } = getAndRemoveConfig(title)
title = str
if (config['no-zoom']) {
@ -304,6 +315,7 @@ export class Compiler {
return `<img src="${url}"data-origin="${href}" alt="${text}"${attrs}>`
}
origin.list = renderer.list = function (body, ordered, start) {
const isTaskList = /<li class="task-list-item">/.test(body.split('class="task-list"')[0])
const isStartReq = start && start > 1
@ -315,6 +327,7 @@ export class Compiler {
return `<${tag} ${tagAttrs}>${body}</${tag}>`
}
origin.listitem = renderer.listitem = function (text) {
const isTaskItem = /^(<input.*type="checkbox"[^>]*>)/.test(text)
const html = isTaskItem ? `<li class="task-list-item"><label>${text}</label></li>` : `<li>${text}</li>`
@ -329,9 +342,12 @@ export class Compiler {
/**
* Compile sidebar
* @param {String} text Text content
* @param {Number} level Type of heading (h<level> tag)
* @returns {String} Sidebar element
*/
sidebar(text, level) {
const {toc} = this
const { toc } = this
const currentPath = this.router.getCurrentPath()
let html = ''
@ -346,9 +362,11 @@ export class Compiler {
for (let j = i; deletedHeaderLevel < toc[j].level && j < toc.length; j++) {
toc.splice(j, 1) && j-- && i++
}
i--
}
}
const tree = this.cacheTree[currentPath] || genTree(toc, level)
html = treeTpl(tree, '<ul>{inner}</ul>')
this.cacheTree[currentPath] = tree
@ -359,14 +377,17 @@ export class Compiler {
/**
* Compile sub sidebar
* @param {Number} level Type of heading (h<level> tag)
* @returns {String} Sub-sidebar element
*/
subSidebar(level) {
if (!level) {
this.toc = []
return
}
const currentPath = this.router.getCurrentPath()
const {cacheTree, toc} = this
const { cacheTree, toc } = this
toc[0] && toc[0].ignoreAllSubs && toc.splice(0)
toc[0] && toc[0].level === 1 && toc.shift()
@ -374,6 +395,7 @@ export class Compiler {
for (let i = 0; i < toc.length; i++) {
toc[i].ignoreSubHeading && toc.splice(i, 1) && i--
}
const tree = cacheTree[currentPath] || genTree(toc, level)
cacheTree[currentPath] = tree
@ -387,6 +409,8 @@ export class Compiler {
/**
* Compile cover page
* @param {Text} text Text content
* @returns {String} Cover page
*/
cover(text) {
const cacheToc = this.toc.slice()

View File

@ -1,9 +1,9 @@
import {get} from '../fetch/ajax'
import {merge} from '../util/core'
import { get } from '../fetch/ajax'
import { merge } from '../util/core'
const cached = {}
function walkFetchEmbed({embedTokens, compile, fetch}, cb) {
function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
let token
let step = 0
let count = 1
@ -23,26 +23,28 @@ function walkFetchEmbed({embedTokens, compile, fetch}, cb) {
if (token.embed.fragment) {
const fragment = token.embed.fragment
const pattern = new RegExp(`(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`)
text = ((text.match(pattern) || [])[1] || '').trim()
text = ((text.match(pattern) || [])[1] || '').trim()
}
embedToken = compile.lexer(
'```' +
token.embed.lang +
'\n' +
text.replace(/`/g, '@DOCSIFY_QM@') +
'\n```\n'
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>`}
{ type: 'html', text: `<div class="mermaid">\n${text}\n</div>` }
]
embedToken.links = {}
} else {
embedToken = [{type: 'html', text}]
embedToken = [{ type: 'html', text }]
embedToken.links = {}
}
}
cb({token, embedToken})
cb({ token, embedToken })
if (++count >= step) {
cb({})
}
@ -61,7 +63,7 @@ function walkFetchEmbed({embedTokens, compile, fetch}, cb) {
}
}
export function prerenderEmbed({compiler, raw = '', fetch}, done) {
export function prerenderEmbed({ compiler, raw = '', fetch }, done) {
let hit = cached[raw]
if (hit) {
const copy = hit.slice()
@ -96,7 +98,7 @@ export function prerenderEmbed({compiler, raw = '', fetch}, done) {
})
let moveIndex = 0
walkFetchEmbed({compile, embedTokens, fetch}, ({embedToken, token}) => {
walkFetchEmbed({ compile, embedTokens, fetch }, ({ embedToken, token }) => {
if (token) {
const index = token.index + moveIndex

View File

@ -1,9 +1,9 @@
/**
* Gen toc tree
* @link https://github.com/killercup/grock/blob/5280ae63e16c5739e9233d9009bc235ed7d79a50/styles/solarized/assets/js/behavior.coffee#L54-L81
* @param {Array} toc
* @param {Number} maxLevel
* @return {Array}
* @param {Array} toc List of TOC elements
* @param {Number} maxLevel Deep level
* @return {Array} Headlines
*/
export function genTree(toc, maxLevel) {
const headlines = []
@ -16,11 +16,13 @@ export function genTree(toc, maxLevel) {
if (level > maxLevel) {
return
}
if (last[len]) {
last[len].children = (last[len].children || []).concat(headline)
} else {
headlines.push(headline)
}
last[level] = headline
})

View File

@ -18,6 +18,7 @@ function executeScript() {
if (!script) {
return false
}
const code = script.innerText.trim()
if (!code) {
return false
@ -102,6 +103,7 @@ export function renderMixin(proto) {
// Reset toc
this.compiler.subSidebar()
}
// Bind event
this._bindEventOnRendered(activeEl)
}
@ -145,6 +147,7 @@ export function renderMixin(proto) {
callHook(this, 'afterEach', html, text => renderMain.call(this, text))
}
if (this.isHTML) {
html = this.result = text
callback()
@ -173,6 +176,7 @@ export function renderMixin(proto) {
dom.toggleClass(el, 'remove', 'show')
return
}
dom.toggleClass(el, 'add', 'show')
let html = this.coverIsHTML ? text : this.compiler.cover(text)
@ -191,10 +195,12 @@ export function renderMixin(proto) {
if (!isAbsolutePath(m[1])) {
path = getPath(this.router.getBasePath(), m[1])
}
el.style.backgroundImage = `url(${path})`
el.style.backgroundSize = 'cover'
el.style.backgroundPosition = 'center center'
}
html = html.replace(m[0], '')
}
@ -228,6 +234,7 @@ export function initRender(vm) {
if (config.repo) {
html += tpl.corner(config.repo, config.cornerExternalLinkTarge)
}
if (config.coverpage) {
html += tpl.cover()
}
@ -271,6 +278,7 @@ export function initRender(vm) {
// Polyfll
cssVars(config.themeColor)
}
vm._updateRender()
dom.toggleClass(dom.body, 'ready')
}

View File

@ -13,6 +13,7 @@ function init() {
dom.appendTo(dom.body, div)
barEl = div
}
/**
* Render progress bar
*/

View File

@ -1,8 +1,9 @@
import { isMobile } from '../util/env'
/**
* Render github corner
* @param {Object} data
* @return {String}
* @param {Object} data URL for the View Source on Github link
* @param {String} cornerExternalLinkTarge value of the target attribute of the link
* @return {String} SVG element as string
*/
export function corner(data, cornerExternalLinkTarge) {
if (!data) {
@ -29,7 +30,9 @@ export function corner(data, cornerExternalLinkTarge) {
}
/**
* Render main content
* Renders main content
* @param {Object} config Configuration object
* @returns {String} HTML of the main content
*/
export function main(config) {
const name = config.name ? config.name : ''
@ -41,11 +44,11 @@ export function main(config) {
'</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>`
: '') +
(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>'
@ -60,6 +63,7 @@ export function main(config) {
/**
* Cover Page
* @returns {String} Cover page
*/
export function cover() {
const SL = ', 100%, 85%'
@ -78,9 +82,9 @@ export function cover() {
/**
* Render tree
* @param {Array} tree
* @param {String} tpl
* @return {String}
* @param {Array} toc Array of TOC section links
* @param {String} tpl TPL list
* @return {String} Rendered tree
*/
export function tree(toc, tpl = '<ul class="app-sub-sidebar">{inner}</ul>') {
if (!toc || !toc.length) {

View File

@ -81,6 +81,7 @@ export class History {
const currentDir = currentRoute.substring(0, currentRoute.lastIndexOf('/') + 1)
return cleanPath(resolvePath(currentDir + path))
}
return cleanPath('/' + path)
}
}

View File

@ -1,7 +1,7 @@
import {History} from './base'
import {noop} from '../../util/core'
import {on} from '../../util/dom'
import {parseQuery, cleanPath, replaceSlug} from '../util'
import { History } from './base'
import { noop } from '../../util/core'
import { on } from '../../util/dom'
import { parseQuery, cleanPath, replaceSlug } from '../util'
function replaceHash(path) {
const i = location.href.indexOf('#')
@ -41,12 +41,13 @@ export class HashHistory extends History {
if (path.charAt(0) === '/') {
return replaceHash(path)
}
replaceHash('/' + path)
}
/**
* Parse the url
* @param {string} [path=location.herf]
* @param {string} [path=location.herf] URL to be parsed
* @return {object} { path, query }
*/
parse(path = location.href) {

View File

@ -1,7 +1,7 @@
import {History} from './base'
import {noop} from '../../util/core'
import {on} from '../../util/dom'
import {parseQuery, getPath} from '../util'
import { History } from './base'
import { noop } from '../../util/core'
import { on } from '../../util/dom'
import { parseQuery, getPath } from '../util'
export class HTML5History extends History {
constructor(config) {
@ -27,7 +27,7 @@ export class HTML5History extends History {
if (el.tagName === 'A' && !/_blank/.test(el.target)) {
e.preventDefault()
const url = el.href
window.history.pushState({key: url}, '', url)
window.history.pushState({ key: url }, '', url)
cb()
}
})
@ -37,7 +37,7 @@ export class HTML5History extends History {
/**
* Parse the url
* @param {string} [path=location.href]
* @param {string} [path=location.href] URL to be parsed
* @return {object} { path, query }
*/
parse(path = location.href) {

View File

@ -1,4 +1,4 @@
import {cached} from '../util/core'
import { cached } from '../util/core'
const decode = decodeURIComponent
const encode = encodeURIComponent
@ -29,6 +29,7 @@ export function stringifyQuery(obj, ignores = []) {
if (ignores.indexOf(key) > -1) {
continue
}
qs.push(
obj[key] ?
`${encode(key)}=${encode(obj[key])}`.toLowerCase() :
@ -44,9 +45,12 @@ export const isAbsolutePath = cached(path => {
})
export const getParentPath = cached(path => {
return /\/$/g.test(path) ?
path :
(path = path.match(/(\S*\/)[^/]+$/)) ? path[1] : ''
if (/\/$/g.test(path)) {
return path
}
const matchingParts = path.match(/(\S*\/)[^/]+$/)
return matchingParts ? matchingParts[1] : ''
})
export const cleanPath = cached(path => {
@ -64,6 +68,7 @@ export const resolvePath = cached(path => {
resolved.push(segment)
}
}
return '/' + resolved.join('/')
})

View File

@ -1,6 +1,9 @@
/**
* Create a cached version of a pure function.
* @param {*} fn The function call to be cached
* @void
*/
export function cached(fn) {
const cache = Object.create(null)
return function (str) {
@ -21,6 +24,8 @@ 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 ||
@ -40,18 +45,23 @@ export const merge =
/**
* Check if value is primitive
* @param {*} value Checks if a value is primitive
* @returns {Boolean} Result of the check
*/
export function isPrimitive(value) {
return typeof value === 'string' || typeof value === 'number'
}
/**
* Perform no operation.
* Performs no operation.
* @void
*/
export function noop() {}
export function noop() { }
/**
* Check if value is function
* @param {*} obj Any javascript object
* @returns {Boolean} True if the passed-in value is a function
*/
export function isFn(obj) {
return typeof obj === 'function'

View File

@ -1,19 +1,20 @@
import {isFn} from '../util/core'
import {inBrowser} from './env'
import { isFn } from '../util/core'
import { inBrowser } from './env'
const cacheNode = {}
/**
* Get Node
* @param {String|Element} el
* @param {Boolean} noCache
* @return {Element}
* @param {String|Element} el A DOM element
* @param {Boolean} noCache Flag to use or not use the cache
* @return {Element} The found node element
*/
export function getNode(el, noCache = false) {
if (typeof el === 'string') {
if (typeof window.Vue !== 'undefined') {
return find(el)
}
el = noCache ? find(el) : cacheNode[el] || (cacheNode[el] = find(el))
}
@ -27,7 +28,10 @@ export const body = inBrowser && $.body
export const head = inBrowser && $.head
/**
* Find element
* Find elements
* @param {String|Element} el The root element where to perform the search from
* @param {Element} node The query
* @returns {Element} The found DOM element
* @example
* find('nav') => document.querySelector('nav')
* find(nav, 'a') => nav.querySelector('a')
@ -38,6 +42,9 @@ export function find(el, node) {
/**
* Find all elements
* @param {String|Element} el The root element where to perform the search from
* @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'))
@ -53,6 +60,7 @@ export function create(node, tpl) {
if (tpl) {
node.innerHTML = tpl
}
return node
}
@ -78,7 +86,10 @@ 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 {String} val Name of the class to be toggled
* @void
* @example
* toggleClass(el, 'active') => el.classList.toggle('active')
* toggleClass(el, 'add', 'active') => el.classList.add('active')

View File

@ -13,6 +13,7 @@ function init(id) {
function () {
(window.ga.q = window.ga.q || []).push(arguments)
}
window.ga.l = Number(new Date())
window.ga('create', id, 'auto')
}

View File

@ -9,10 +9,10 @@ function init(options) {
window._paq = window._paq || []
window._paq.push(['trackPageView'])
window._paq.push(['enableLinkTracking'])
setTimeout(function() {
setTimeout(function () {
appendScript(options)
window._paq.push(['setTrackerUrl', options.host + '/matomo.php'])
window._paq.push(['setSiteId', options.id + ''])
window._paq.push(['setSiteId', String(options.id)])
}, 0)
}
@ -20,7 +20,8 @@ function collect() {
if (!window._paq) {
init($docsify.matomo)
}
window._paq.push(['setCustomUrl', window.location.hash.substr(1)])
window._paq.push(['setCustomUrl', window.location.hash.substr(1)])
window._paq.push(['setDocumentTitle', document.title])
window._paq.push(['trackPageView'])
}

View File

@ -139,8 +139,10 @@ function doSearch(value) {
$sidebarNav.classList.remove('hide')
$appName.classList.remove('hide')
}
return
}
const matchs = search(value)
let html = ''
@ -193,6 +195,7 @@ function updatePlaceholder(text, path) {
if (!$input) {
return
}
if (typeof text === 'string') {
$input.placeholder = text
} else {

View File

@ -8,6 +8,7 @@ const LOCAL_STORAGE = {
function resolveExpireKey(namespace) {
return namespace ? `${LOCAL_STORAGE.EXPIRE_KEY}/${namespace}` : LOCAL_STORAGE.EXPIRE_KEY
}
function resolveIndexKey(namespace) {
return namespace ? `${LOCAL_STORAGE.INDEX_KEY}/${namespace}` : LOCAL_STORAGE.INDEX_KEY
}
@ -58,14 +59,15 @@ export function genIndex(path, content = '', router, depth) {
tokens.forEach(token => {
if (token.type === 'heading' && token.depth <= depth) {
slug = router.toURL(path, {id: slugify(token.text)})
index[slug] = {slug, title: token.text, body: ''}
slug = router.toURL(path, { id: slugify(token.text) })
index[slug] = { slug, title: token.text, body: '' }
} else {
if (!slug) {
return
}
if (!index[slug]) {
index[slug] = {slug, title: '', body: ''}
index[slug] = { slug, title: '', body: '' }
} else if (index[slug].body) {
index[slug].body += '\n' + (token.text || '')
} else {
@ -78,8 +80,8 @@ export function genIndex(path, content = '', router, depth) {
}
/**
* @param {String} query
* @returns {Array}
* @param {String} query Search query
* @returns {Array} Array of results
*/
export function search(query) {
const matchingResults = []
@ -103,12 +105,12 @@ export function search(query) {
const postUrl = post.slug || ''
if (postTitle) {
keywords.forEach( keyword => {
keywords.forEach(keyword => {
// From https://github.com/sindresorhus/escape-string-regexp
const regEx = new RegExp(
keyword.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'),
'gi'
);
)
let indexTitle = -1
let indexContent = -1
@ -116,7 +118,7 @@ export function search(query) {
indexContent = postContent ? postContent.search(regEx) : -1
if (indexTitle >= 0 || indexContent >= 0) {
matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0;
matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0
if (indexContent < 0) {
indexContent = 0
}
@ -155,7 +157,7 @@ export function search(query) {
}
}
return matchingResults.sort((r1, r2) => r2.score - r1.score);
return matchingResults.sort((r1, r2) => r2.score - r1.score)
}
export function init(config, vm) {

View File

@ -1,10 +1,10 @@
// load ES6 modules in Node.js on the fly
require = require('esm')(module/*, options*/)
// Load ES6 modules in Node.js on the fly
require = require('esm')(module/* , options */) /* eslint-disable-line no-global-assign */
const path = require('path')
const {expect} = require('chai')
const { expect } = require('chai')
const {JSDOM} = require('jsdom')
const { JSDOM } = require('jsdom')
function ready(callback) {
const state = document.readyState
@ -15,9 +15,10 @@ function ready(callback) {
document.addEventListener('DOMContentLoaded', callback)
}
module.exports.init = function(fixture = 'default', config = {}, markup) {
if (markup == null) {
markup = `<!DOCTYPE html>
module.exports.init = function (fixture = 'default', config = {}, markup = null) {
if (markup === null || markup === undefined) {
markup = `<!DOCTYPE html>
<html>
<head></head>
<body>
@ -27,61 +28,63 @@ module.exports.init = function(fixture = 'default', config = {}, markup) {
</script>
</body>
</html>`
}
const rootPath = path.join(__dirname, 'fixtures', fixture)
const dom = new JSDOM(markup)
dom.reconfigure({ url: 'file:///' + rootPath })
}
global.window = dom.window
global.document = dom.window.document
global.navigator = dom.window.navigator
global.location = dom.window.location
global.XMLHttpRequest = dom.window.XMLHttpRequest
const rootPath = path.join(__dirname, 'fixtures', fixture)
// mimic src/core/index.js but for Node.js
function Docsify() {
this._init()
}
const dom = new JSDOM(markup)
dom.reconfigure({ url: 'file:///' + rootPath })
const proto = Docsify.prototype
global.window = dom.window
global.document = dom.window.document
global.navigator = dom.window.navigator
global.location = dom.window.location
global.XMLHttpRequest = dom.window.XMLHttpRequest
const {initMixin} = require('../src/core/init')
const {routerMixin} = require('../src/core//router')
const {renderMixin} = require('../src/core//render')
const {fetchMixin} = require('../src/core/fetch')
const {eventMixin} = require('../src/core//event')
// Mimic src/core/index.js but for Node.js
function Docsify() {
this._init()
}
initMixin(proto)
routerMixin(proto)
renderMixin(proto)
fetchMixin(proto)
eventMixin(proto)
const proto = Docsify.prototype
const NOT_INIT_PATTERN = '<!--main-->'
const { initMixin } = require('../src/core/init')
const { routerMixin } = require('../src/core//router')
const { renderMixin } = require('../src/core//render')
const { fetchMixin } = require('../src/core/fetch')
const { eventMixin } = require('../src/core//event')
return new Promise((resolve, reject) => {
ready(() => {
const docsify = new Docsify()
// NOTE: I was not able to get it working with a callback, but polling works usually at the first time
const id = setInterval(() => {
if (dom.window.document.body.innerHTML.indexOf(NOT_INIT_PATTERN) == -1) {
clearInterval(id)
return resolve({
docsify: docsify,
dom: dom
})
}
}, 10)
})
})
initMixin(proto)
routerMixin(proto)
renderMixin(proto)
fetchMixin(proto)
eventMixin(proto)
const NOT_INIT_PATTERN = '<!--main-->'
return new Promise(resolve => {
ready(() => {
const docsify = new Docsify()
// NOTE: I was not able to get it working with a callback, but polling works usually at the first time
const id = setInterval(() => {
if (dom.window.document.body.innerHTML.indexOf(NOT_INIT_PATTERN) === -1) {
clearInterval(id)
return resolve({
docsify: docsify,
dom: dom
})
}
}, 10)
})
})
}
module.exports.expectSameDom = function(actual, expected) {
const WHITESPACES_BETWEEN_TAGS = />(\s\s+)</g
function replacer(match, group1) {
return match.replace(group1, '')
}
expect(actual.replace(WHITESPACES_BETWEEN_TAGS, replacer).trim())
.equal(expected.replace(WHITESPACES_BETWEEN_TAGS, replacer).trim())
module.exports.expectSameDom = function (actual, expected) {
const WHITESPACES_BETWEEN_TAGS = />(\s\s+)</g
function replacer(match, group1) {
return match.replace(group1, '')
}
expect(actual.replace(WHITESPACES_BETWEEN_TAGS, replacer).trim())
.equal(expected.replace(WHITESPACES_BETWEEN_TAGS, replacer).trim())
}

View File

@ -1,14 +1,9 @@
const path = require('path')
const {expect} = require('chai')
const {init, expectSameDom} = require('../_helper')
describe('full docsify initialization', function() {
it('TODO: check generated markup', async function() {
const {docsify, dom} = await init('simple', {loadSidebar: true})
console.log(dom.window.document.body.innerHTML)
// TODO: add some expectations
})
const { init } = require('../_helper')
describe('full docsify initialization', function () {
it('TODO: check generated markup', async function () {
const { dom } = await init('simple', { loadSidebar: true })
console.log(dom.window.document.body.innerHTML)
// TODO: add some expectations
})
})

View File

@ -1,14 +1,9 @@
const path = require('path')
const {expect} = require('chai')
const {init, expectSameDom} = require('../_helper')
describe('router', function() {
it('TODO: trigger to load another page', async function() {
const {docsify} = await init()
window.location = '/?foo=bar'
// TODO: add some expectations
})
const { init } = require('../_helper')
describe('router', function () {
it('TODO: trigger to load another page', async function () {
await init()
window.location = '/?foo=bar'
// TODO: add some expectations
})
})

View File

@ -1,62 +1,61 @@
/* eslint-env node, chai, mocha */
require = require('esm')(module/*, options*/)
const {expect} = require('chai')
const {History} = require('../../src/core/router/history/base')
class MockHistory extends History {
parse(path) {
return {path}
}
}
describe('router/history/base', function () {
describe('relativePath true', function () {
var history
beforeEach(function () {
history = new MockHistory({relativePath: true})
})
it('toURL', function () {
// WHEN
const url = history.toURL('guide.md', {}, '/zh-ch/')
// THEN
expect(url).equal('/zh-ch/guide')
})
it('toURL with double dot', function () {
// WHEN
const url = history.toURL('../README.md', {}, '/zh-ch/')
// THEN
expect(url).equal('/README')
})
it('toURL child path', function () {
// WHEN
const url = history.toURL('config/example.md', {}, '/zh-ch/')
// THEN
expect(url).equal('/zh-ch/config/example')
})
it('toURL absolute path', function () {
// WHEN
const url = history.toURL('/README', {}, '/zh-ch/')
// THEN
expect(url).equal('/README')
})
})
it('toURL without relative path', function () {
const history = new MockHistory({relativePath: false})
// WHEN
const url = history.toURL('README', {}, '/zh-ch/')
// THEN
expect(url).equal('/README')
})
})
require = require('esm')(module/* , options */) /* eslint-disable-line no-global-assign */
const { expect } = require('chai')
const { History } = require('../../src/core/router/history/base')
class MockHistory extends History {
parse(path) {
return { path }
}
}
describe('router/history/base', function () {
describe('relativePath true', function () {
var history
beforeEach(function () {
history = new MockHistory({ relativePath: true })
})
it('toURL', function () {
// WHEN
const url = history.toURL('guide.md', {}, '/zh-ch/')
// THEN
expect(url).equal('/zh-ch/guide')
})
it('toURL with double dot', function () {
// WHEN
const url = history.toURL('../README.md', {}, '/zh-ch/')
// THEN
expect(url).equal('/README')
})
it('toURL child path', function () {
// WHEN
const url = history.toURL('config/example.md', {}, '/zh-ch/')
// THEN
expect(url).equal('/zh-ch/config/example')
})
it('toURL absolute path', function () {
// WHEN
const url = history.toURL('/README', {}, '/zh-ch/')
// THEN
expect(url).equal('/README')
})
})
it('toURL without relative path', function () {
const history = new MockHistory({ relativePath: false })
// WHEN
const url = history.toURL('README', {}, '/zh-ch/')
// THEN
expect(url).equal('/README')
})
})

View File

@ -1,62 +1,60 @@
const path = require('path')
const { expect } = require('chai')
const {expect} = require('chai')
const { init, expectSameDom } = require('../_helper')
const {init, expectSameDom} = require('../_helper')
describe('render', function () {
it('important content (tips)', async function () {
const { docsify } = await init()
const output = docsify.compiler.compile('!> **Time** is money, my friend!')
expect(output).equal('<p class="tip"><strong>Time</strong> is money, my friend!</p>')
})
describe('render', function() {
it('important content (tips)', async function() {
const {docsify, dom} = await init()
const output = docsify.compiler.compile('!> **Time** is money, my friend!')
expect(output).equal('<p class="tip"><strong>Time</strong> is money, my friend!</p>')
})
describe('lists', function() {
it('as unordered task list', async function() {
const {docsify, dom} = await init()
const output = docsify.compiler.compile(`
describe('lists', function () {
it('as unordered task list', async function () {
const { docsify } = await init()
const output = docsify.compiler.compile(`
- [x] Task 1
- [ ] Task 2
- [ ] Task 3`)
expect(output, `<ul class="task-list">
expect(output, `<ul class="task-list">
<li class="task-list-item"><label><input checked="" disabled="" type="checkbox"> Task 1</label></li>
<li class="task-list-item"><label><input disabled="" type="checkbox"> Task 2</label></li>
<li class="task-list-item"><label><input disabled="" type="checkbox"> Task 3</label></li>
</ul>`)
})
})
it('as ordered task list', async function() {
const {docsify, dom} = await init()
const output = docsify.compiler.compile(`
it('as ordered task list', async function () {
const { docsify } = await init()
const output = docsify.compiler.compile(`
1. [ ] Task 1
2. [x] Task 2`)
expectSameDom(output, `<ol class="task-list">
expectSameDom(output, `<ol class="task-list">
<li class="task-list-item"><label><input disabled="" type="checkbox"> Task 1</label></li>
<li class="task-list-item"><label><input checked="" disabled="" type="checkbox"> Task 2</label></li>
</ol>`)
})
})
it('normal unordered', async function() {
const {docsify, dom} = await init()
const output = docsify.compiler.compile(`
it('normal unordered', async function () {
const { docsify } = await init()
const output = docsify.compiler.compile(`
- [linktext](link)
- just text`)
expectSameDom(output, `<ul >
expectSameDom(output, `<ul >
<li><a href="#/link">linktext</a></li>
<li>just text</li>
</ul>`)
})
})
it('unordered with custom start', async function() {
const {docsify, dom} = await init()
const output = docsify.compiler.compile(`
it('unordered with custom start', async function () {
const { docsify } = await init()
const output = docsify.compiler.compile(`
1. first
2. second
text
3. third`)
expectSameDom(output, `<ol >
expectSameDom(output, `<ol >
<li>first</li>
<li>second</li>
</ol>
@ -64,17 +62,17 @@ text
<ol start="3">
<li>third</li>
</ol>`)
})
})
it('nested', async function() {
const {docsify, dom} = await init()
const output = docsify.compiler.compile(`
it('nested', async function () {
const { docsify } = await init()
const output = docsify.compiler.compile(`
- 1
- 2
- 2 a
- 2 b
- 3`)
expectSameDom(output, `<ul >
expectSameDom(output, `<ul >
<li>1</li>
<li>2<ul >
<li>2 a</li>
@ -83,7 +81,6 @@ text
</li>
<li>3</li>
</ul>`)
})
})
})
})
})

View File

@ -1,30 +1,29 @@
/* eslint-env node, chai, mocha */
require = require('esm')(module/*, options*/)
const {expect} = require('chai')
const {resolvePath} = require('../../src/core/router/util')
describe('router/util', function () {
it('resolvePath', async function () {
// WHEN
const result = resolvePath('hello.md')
// THEN
expect(result).equal('/hello.md')
})
it('resolvePath with dot', async function () {
// WHEN
const result = resolvePath('./hello.md')
// THEN
expect(result).equal('/hello.md')
})
it('resolvePath with two dots', async function () {
// WHEN
const result = resolvePath('test/../hello.md')
// THEN
expect(result).equal('/hello.md')
})
})
require = require('esm')(module/* , options */) /* eslint-disable-line no-global-assign */
const { expect } = require('chai')
const { resolvePath } = require('../../src/core/router/util')
describe('router/util', function () {
it('resolvePath', async function () {
// WHEN
const result = resolvePath('hello.md')
// THEN
expect(result).equal('/hello.md')
})
it('resolvePath with dot', async function () {
// WHEN
const result = resolvePath('./hello.md')
// THEN
expect(result).equal('/hello.md')
})
it('resolvePath with two dots', async function () {
// WHEN
const result = resolvePath('test/../hello.md')
// THEN
expect(result).equal('/hello.md')
})
})