diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 0bd565e..0000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,2 +0,0 @@ - \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..35d6b90 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + + + + + +## Bug Report + +#### Steps to reproduce + + + +#### What is current behaviour + + + +#### What is the expected behaviour + + + +#### Other relevant information + + +- [ ] Bug does still occur when all/other plugins are disabled? + +- Your OS: +- Node.js version: +- npm/yarn version: +- Browser version: +- Docsify version: +- Docsify plugins: + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..501e4de --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,25 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + + + + + +## Feature request + +#### What problem does this feature solve? + + + +#### What does the proposed API look like? + + + +#### How should this be implemented in your opinion? + + + +#### Are you willing to work on this yourself? diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 79a5281..bb31bd4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,54 @@ -Please makes sure these boxes are checked before submitting your PR, thank you! + + + + + + +**Summary** + +**What kind of change does this PR introduce?** (check at least one) + +- [ ] Bugfix +- [ ] Feature +- [ ] Code style update +- [ ] Refactor +- [ ] Docs +- [ ] Build-related changes +- [ ] Other, please describe: + +If changing the UI of default theme, please provide the **before/after** screenshot: + +**Does this PR introduce a breaking change?** (check one) + +- [ ] Yes +- [ ] No + +If yes, please describe the impact and migration path for existing applications: + +**The PR fulfills these requirements:** + +- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number) + +You have tested in the following browsers: (Providing a detailed version will be better.) + +- [ ] Chrome +- [ ] Firefox +- [ ] Safari +- [ ] Edge +- [ ] IE + +If adding a **new feature**, the PR's description includes: + +- [ ] A convincing reason for adding this feature +- [ ] Related documents have been updated +- [ ] Related tests have been updated + +To avoid wasting your time, it's best to open a **feature request issue** first and wait for approval before working on it. + + +**Other information:** + +--- -* [ ] Make sure you are merging your commits to `master` branch. -* [ ] Add some descriptions and refer relative issues for you PR. * [ ] DO NOT include files inside `lib` directory. + diff --git a/.gitignore b/.gitignore index 7235d84..ea4b5b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ *.log .DS_Store -/themes/* -!.gitkeep -node_modules -lib .idea +node_modules +themes/ +lib/ +# exceptions +!.gitkeep \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a50a98..267d402 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ + +## [4.9.4](https://github.com/docsifyjs/docsify/compare/v4.9.2...v4.9.4) (2019-05-05) + + + + +## [4.9.2](https://github.com/docsifyjs/docsify/compare/v4.9.1...v4.9.2) (2019-04-21) + + +### Bug Fixes + +* re-render gitalk when router changed ([11ea1f8](https://github.com/docsifyjs/docsify/commit/11ea1f8)) + + +### Features + +* allows relative path, fixed [#590](https://github.com/docsifyjs/docsify/issues/590) ([31654f1](https://github.com/docsifyjs/docsify/commit/31654f1)) + + + ## [4.9.1](https://github.com/docsifyjs/docsify/compare/v4.9.0...v4.9.1) (2019-02-21) diff --git a/README.md b/README.md index a1ae1c8..b8da0d8 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ ## Links +- [`develop` branch preview](https://docsifyjs.netlify.com/) - [Documentation](https://docsify.js.org) - [CLI](https://github.com/docsifyjs/docsify-cli) - CDN: [UNPKG](https://unpkg.com/docsify/) | [jsDelivr](https://cdn.jsdelivr.net/npm/docsify/) | [cdnjs](https://cdnjs.com/libraries/docsify) @@ -40,7 +41,7 @@ - Smart full-text search plugin - Multiple themes - Useful plugin API -- Compatible with IE10+ +- Compatible with IE11 - Support SSR ([example](https://github.com/docsifyjs/docsify-ssr-demo)) - Support embedded files @@ -54,7 +55,7 @@ Look at [this tutorial](https://docsify.js.org/#/quickstart) These projects are using docsify to generate their sites. Pull requests welcome :blush: -Move to [awesome-docsify](https://github.com/docsifyjs/awesome-docsify) +Move to [awesome-docsify](https://github.com/docsifyjs/awesome-docsify#showcase) ## Similar projects diff --git a/build/build.js b/build/build.js index b9068b9..7b61224 100644 --- a/build/build.js +++ b/build/build.js @@ -55,6 +55,7 @@ const buildAllPlugin = function () { var plugins = [ {name: 'search', input: 'search/index.js'}, {name: 'ga', input: 'ga.js'}, + {name: 'matomo', input: 'matomo.js'}, {name: 'emoji', input: 'emoji.js'}, {name: 'external-script', input: 'external-script.js'}, {name: 'front-matter', input: 'front-matter/index.js'}, diff --git a/build/release.sh b/build/release.sh old mode 100644 new mode 100755 index da15a38..a328322 --- a/build/release.sh +++ b/build/release.sh @@ -29,7 +29,6 @@ if [[ $REPLY =~ ^[Yy]$ ]]; then # commit git add -A - git add -f lib/ -A git commit -m "[build] $VERSION $RELEASE_TAG" npm --no-git-tag-version version $VERSION --message "[release] $VERSION $RELEASE_TAG" diff --git a/docs/README.md b/docs/README.md index d27f1ae..e625420 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,12 +16,12 @@ See the [Quick start](quickstart.md) guide for more details. - Multiple themes - Useful plugin API - Emoji support -- Compatible with IE10+ +- Compatible with IE11 - Support server-side rendering ([example](https://github.com/docsifyjs/docsify-ssr-demo)) ## Examples -Check out the [Showcase](https://github.com/docsifyjs/docsify/#showcase) to see docsify in use. +Check out the [Showcase](https://github.com/docsifyjs/awesome-docsify#showcase) to see docsify in use. ## Donate diff --git a/docs/_coverpage.md b/docs/_coverpage.md index 0a18f97..1decbd6 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -1,6 +1,6 @@ ![logo](_media/icon.svg) -# docsify 4.9.1 +# docsify 4.9.4 > A magical documentation site generator. diff --git a/docs/_sidebar.md b/docs/_sidebar.md index c8ab579..051514b 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -23,7 +23,6 @@ - [Offline Mode(PWA)](pwa.md) - [Server-Side Rendering(SSR)](ssr.md) - [Embed Files](embed-files.md) - - [Generate static html](static.md) - [Awesome docsify](awesome.md) - [Changelog](changelog.md) diff --git a/docs/configuration.md b/docs/configuration.md index 830748f..ba48475 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -150,6 +150,46 @@ window.$docsify = { }; ``` +## relativePath + +- Type: `Boolean` +- Default: `false` + +If **true** links are relative to the current context. + +For example, the directory structure is as follows: + +```text +. +โ””โ”€โ”€ docs + โ”œโ”€โ”€ README.md + โ”œโ”€โ”€ guide.md + โ””โ”€โ”€ zh-cn + โ”œโ”€โ”€ README.md + โ”œโ”€โ”€ guide.md + โ””โ”€โ”€ config + โ””โ”€โ”€ example.md +``` + +With relative path **enabled** and current URL `http://domain.com/zh-cn/README`, given links will resolve to: + +```text +guide.md => http://domain.com/zh-cn/guide +config/example.md => http://domain.com/zh-cn/config/example +../README.md => http://domain.com/README +/README.md => http://domain.com/README +``` + +```js +window.$docsify = { + // Relative path enabled + relativePath: true, + + // Relative path disabled (default value) + relativePath: false +}; +``` + ## coverpage - Type: `Boolean|String|String[]|Object` diff --git a/docs/deploy.md b/docs/deploy.md index 7d47e0f..a879f2e 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -15,7 +15,7 @@ It is recommended that you save your files to the `./docs` subfolder of the `mas ![github pages](_images/deploy-github-pages.png) !> You can also save files in the root directory and select `master branch`. -You'll need to place a `.nojekyll` file in the deploy location (such as `/docs` or the gh-pages branch +You'll need to place a `.nojekyll` file in the deploy location (such as `/docs` or the gh-pages branch) ## GitLab Pages diff --git a/docs/helpers.md b/docs/helpers.md index 9075448..b047d36 100644 --- a/docs/helpers.md +++ b/docs/helpers.md @@ -7,72 +7,24 @@ docsify extends Markdown syntax to make your documents more readable. Important content like: ```markdown -> [!] **Time** is money, my friend! +!> **Time** is money, my friend! ``` is rendered as: -> [!] **Time** is money, my friend! +!> **Time** is money, my friend! ## General tips General tips like: ```markdown -> [?] _TODO_ unit test +?> _TODO_ unit test ``` are rendered as: -> [?] _TODO_ unit test - -## More tips - -```markdown -> [x] bad - -> [v] good -``` - -> [x] bad - -> [v] good - -## Details - -````markdown -> [details] Sample code -> -> js code -> -> ```javascript -> console.log("foo"); -> ``` - -> [details:open] Sample code open -> -> js code -> -> ```javascript -> console.log("foo"); -> ``` -```` - -> [details] Sample code -> -> js code -> -> ```javascript -> console.log("foo"); -> ``` - -> [details:open] Sample code open -> -> js code -> -> ```javascript -> console.log("foo"); -> ``` +?> _TODO_ unit test ## Ignore to compile link @@ -87,13 +39,13 @@ It will be compiled to `link` and will be loaded `/demo/R Now you can do that ```md -[link](/demo/ ":ignore") +[link](/demo/ ':ignore') ``` You will get `link`html. Do not worry, you can still set title for link. ```md -[link](/demo/ ":ignore title") +[link](/demo/ ':ignore title') link ``` @@ -101,14 +53,14 @@ You will get `link`html. Do not worry, you can still set ti ## Set target attribute for link ```md -[link](/demo ":target=_blank") -[link](/demo2 ":target=_self") +[link](/demo ':target=_blank') +[link](/demo2 ':target=_self') ``` ## Disable link ```md -[link](/demo ":disabled") +[link](/demo ':disabled') ``` ## Github Task Lists @@ -132,17 +84,17 @@ You will get `link`html. Do not worry, you can still set ti ## Image resizing ```md -![logo](https://docsify.js.org/_media/icon.svg ":size=50x100") -![logo](https://docsify.js.org/_media/icon.svg ":size=100") +![logo](https://docsify.js.org/_media/icon.svg ':size=50x100') +![logo](https://docsify.js.org/_media/icon.svg ':size=100') -![logo](https://docsify.js.org/_media/icon.svg ":size=10%") +![logo](https://docsify.js.org/_media/icon.svg ':size=10%') ``` -![logo](https://docsify.js.org/_media/icon.svg ":size=50x100") -![logo](https://docsify.js.org/_media/icon.svg ":size=100") -![logo](https://docsify.js.org/_media/icon.svg ":size=10%") +![logo](https://docsify.js.org/_media/icon.svg ':size=50x100') +![logo](https://docsify.js.org/_media/icon.svg ':size=100') +![logo](https://docsify.js.org/_media/icon.svg ':size=10%') ## Customise ID for headings diff --git a/docs/index.html b/docs/index.html index bff4ca1..6d0e008 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,137 +1,104 @@ - - - docsify - - - - - - - - - - - - - - -
Loading ...
- + + + + +
Loading ...
+ - - - - - - - - - + DocsifyCodefund.create('fae1f9a4-870c-4c25-b8e0-c80464f7a95c') + ] + } + + + + + + + + + + + + + diff --git a/docs/more-pages.md b/docs/more-pages.md index 38184a2..46e7df8 100644 --- a/docs/more-pages.md +++ b/docs/more-pages.md @@ -72,6 +72,16 @@ You can specify `alias` to avoid unnecessary fallback. !> You can create a `README.md` file in a subdirectory to use it as the landing page for the route. +## Set Page Titles from Sidebar Selection + +A page's `title` tag is generated from the _selected_ sidebar item name. For better SEO, you can customize the title by specifying a string after the filename. + +```markdown + +* [Home](/) +* [Guide](guide.md "The greatest guide in the world") +``` + ## Table of Contents Once you've created `_sidebar.md`, the sidebar content is automatically generated based on the headers in the markdown files. diff --git a/docs/static.md b/docs/static.md deleted file mode 100644 index 98bcb75..0000000 --- a/docs/static.md +++ /dev/null @@ -1,50 +0,0 @@ -# Generate static html - -> _Experimental feature_ - -Generating static html files is good for SEO and speeds up the first rendering. - -But this is not an advantage of docsify. If you only need powerful static documentation, choose another documentation tool like Gitbook or Vuepress. - -## Configuration - -You can configure it in a special config file. - -_config.js_ - -```js -module.exports = { - template: ` - - - - My Doc - - - - - - - - - -`, // or html file path - - config: { - // docsify config - coverpage: true - } -}; -``` - -## Generate - -Please use docsify-cli 5.0+ and run this command. - -```sh -docsify static docs -c config.js -``` - -## Simple Demo - -This is an example showing the docsify official documentation generating static files. [docsify-static-demo](https://github.com/docsifyjs/docsify-static-demo) diff --git a/docs/write-a-plugin.md b/docs/write-a-plugin.md index d6605d4..baa894e 100644 --- a/docs/write-a-plugin.md +++ b/docs/write-a-plugin.md @@ -79,7 +79,7 @@ window.$docsify = { function(hook, vm) { hook.beforeEach(function(html) { var url = - 'https://github.com/docsifyjs/docsify/blob/master/docs' + + 'https://github.com/docsifyjs/docsify/blob/master/docs/' + vm.route.file; var editHtml = '[๐Ÿ“ EDIT DOCUMENT](' + url + ')\n'; diff --git a/index.html b/index.html index f15433b..59e6911 100644 --- a/index.html +++ b/index.html @@ -21,48 +21,64 @@ + + + + diff --git a/package-lock.json b/package-lock.json index a0582ad..bb18bcb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "docsify", - "version": "4.9.1", + "version": "4.9.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c61835b..1661f40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "docsify", - "version": "4.9.1", + "version": "4.9.4", "description": "A magical documentation generator.", "author": { "name": "qingwei-li", @@ -32,11 +32,11 @@ "watch:css": "run-p 'css -- -o themes -w'", "watch:js": "node build/build.js", "build:css:min": "mkdir lib/themes && run-p 'css -- -o lib/themes' && node build/mincss.js", - "build:css": "mkdir themes && run-p 'css -- -o themes'", + "build:css": "mkdir -p themes && run-p 'css -- -o themes'", "build:js": "cross-env NODE_ENV=production node build/build.js", "build:ssr": "node build/ssr.js", "build:cover": "node build/cover.js", - "build": "rimraf lib themes && run-s build:js build:css build:css:min build:ssr build:cover", + "build": "rimraf lib themes/* && run-s build:js build:css build:css:min build:ssr build:cover", "pub:next": "cross-env RELEASE_TAG=next sh build/release.sh", "pub": "sh build/release.sh", "postinstall": "opencollective postinstall" diff --git a/packages/docsify-server-renderer/index.js b/packages/docsify-server-renderer/index.js index e65018e..21fb5c7 100644 --- a/packages/docsify-server-renderer/index.js +++ b/packages/docsify-server-renderer/index.js @@ -22,7 +22,7 @@ function mainTpl(config) { html += tpl.corner(config.repo) } if (config.coverpage) { - html += '' + html += tpl.cover() } html += tpl.main(config) @@ -31,13 +31,12 @@ function mainTpl(config) { } export default class Renderer { - constructor({template, config, cache, path}) { + constructor({template, config, cache}) { this.html = template this.config = config = Object.assign({}, config, { routerMode: 'history' }) this.cache = cache - this.path = path this.router = new AbstractHistory(config) this.compiler = new Compiler(config, this.router) @@ -53,19 +52,9 @@ export default class Renderer { } _getPath(url) { - const path = resolve(this.path, url) - const file = this.router.getFile(path) - return file - } + const file = this.router.getFile(url) - async render(url) { - const content = await this.renderToString(url) - - return { - content, - url: this.router.parse(url).path, - path: this._getPath(url) - } + return isAbsolutePath(file) ? file : cwd(`./${file}`) } async renderToString(url) { @@ -77,35 +66,32 @@ export default class Renderer { if (loadSidebar) { const name = loadSidebar === true ? '_sidebar.md' : loadSidebar - const sidebarFile = this._getPath(resolvePathname(name, url)) + const sidebarFile = this._getPath(resolve(url, `./${name}`)) this._renderHtml('sidebar', await this._render(sidebarFile, 'sidebar')) } if (loadNavbar) { const name = loadNavbar === true ? '_navbar.md' : loadNavbar - const navbarFile = this._getPath(resolvePathname(name, url)) + const navbarFile = this._getPath(resolve(url, `./${name}`)) this._renderHtml('navbar', await this._render(navbarFile, 'navbar')) } if (coverpage) { - let name = null - - if (typeof coverpage === 'string' || coverpage === true) { - if (url === 'README.md') { - name = coverpage === true ? '_coverpage.md' : coverpage + let path = null + if (typeof coverpage === 'string') { + if (url === '/') { + path = coverpage } } else if (Array.isArray(coverpage)) { - name = coverpage.indexOf(url) > -1 && '_coverpage.md' + path = coverpage.indexOf(url) > -1 && '_coverpage.md' } else { const cover = coverpage[url] - name = cover === true ? '_coverpage.md' : cover + path = cover === true ? '_coverpage.md' : cover } - if (name) { - const coverFile = this._getPath(resolvePathname(name, url)) + const coverFile = this._getPath(resolve(url, `./${path}`)) - this._renderHtml('cover', await this._render(coverFile, 'cover')) - } + this._renderHtml('cover', await this._render(coverFile), 'cover') } const html = this.html @@ -122,7 +108,6 @@ export default class Renderer { async _render(path, type) { let html = await this._loadFile(path) - const {subMaxLevel, maxLevel} = this.config let tokens @@ -141,8 +126,7 @@ export default class Renderer { tokens = await new Promise(r => { prerenderEmbed( { - // Url is absolute path - fetch: url => this._loadFile(this._getPath('.' + url)), + fetch: url => this._loadFile(this._getPath(url)), compiler: this.compiler, raw: html }, @@ -179,7 +163,7 @@ export default class Renderer { return content } catch (e) { this.lock = this.lock || 0 - if (++this.lock > 4) { + if (++this.lock > 10) { this.lock = 0 return } diff --git a/packages/docsify-server-renderer/package-lock.json b/packages/docsify-server-renderer/package-lock.json index ccb68d4..e8ff0a6 100644 --- a/packages/docsify-server-renderer/package-lock.json +++ b/packages/docsify-server-renderer/package-lock.json @@ -37,5 +37,5 @@ "integrity": "sha1-6DWIAbhrg7F1YNTjw4LXrvIQCUQ=" } }, - "version": "4.9.1" + "version": "4.9.4" } diff --git a/packages/docsify-server-renderer/package.json b/packages/docsify-server-renderer/package.json index 9960401..5ff206e 100644 --- a/packages/docsify-server-renderer/package.json +++ b/packages/docsify-server-renderer/package.json @@ -1,6 +1,6 @@ { "name": "docsify-server-renderer", - "version": "4.9.1", + "version": "4.9.4", "description": "docsify server renderer", "author": { "name": "qingwei-li", diff --git a/src/core/config.js b/src/core/config.js index 0a8763a..a1386b2 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -25,7 +25,8 @@ export default function () { formatUpdated: '', externalLinkTarget: '_blank', routerMode: 'hash', - noCompileLinks: [] + noCompileLinks: [], + relativePath: false }, window.$docsify ) diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js index 502d938..354b6a9 100644 --- a/src/core/render/compiler.js +++ b/src/core/render/compiler.js @@ -1,12 +1,6 @@ import marked from 'marked' import Prism from 'prismjs' -import { - helper as helperTpl, - newHelper as newHelperTpl, - tree as treeTpl, - cover as coverTpl, - details as detailsTpl -} from './tpl' +import {helper as helperTpl, tree as treeTpl} from './tpl' import {genTree} from './gen-tree' import {slugify} from './slugify' import {emojify} from './emojify' @@ -197,7 +191,7 @@ export class Compiler { /** * Render anchor tag - * @link https://marked.js.org/#/USING_PRO.md#renderer + * @link https://github.com/markedjs/marked#overriding-renderer-methods */ origin.heading = renderer.heading = function (text, level) { let {str, config} = getAndRemoveConfig(text) @@ -309,68 +303,22 @@ export class Compiler { return `${text}` } origin.list = renderer.list = function (body, ordered, start) { - const isTaskList = /
  • /.test( - body.split('class="task-list"')[0] - ) + const isTaskList = /
  • /.test(body.split('class="task-list"')[0]) const isStartReq = start && start > 1 const tag = ordered ? 'ol' : 'ul' const tagAttrs = [ - isTaskList ? 'class="task-list"' : '', - isStartReq ? `start="${start}"` : '' - ] - .join(' ') - .trim() + (isTaskList ? 'class="task-list"' : ''), + (isStartReq ? `start="${start}"` : '') + ].join(' ').trim() return `<${tag} ${tagAttrs}>${body}` } origin.listitem = renderer.listitem = function (text) { const isTaskItem = /^(]*>)/.test(text) - const html = isTaskItem ? - `
  • ` : - `
  • ${text}
  • ` + const html = isTaskItem ? `
  • ` : `
  • ${text}
  • ` return html } - origin.blockquote = renderer.blockquote = function (quote) { - const m = quote.match(/^\(\[\S+\])/) - - if (m) { - const text = quote.replace(m[1], '') - switch (m[1]) { - case '[!]': - result = newHelperTpl('docsify-tip', text) - break - case '[?]': - result = newHelperTpl('docsify-warn', text) - break - case '[x]': - result = newHelperTpl('docsify-error', text) - break - case '[v]': - result = newHelperTpl('docsify-success', text) - break - case '[details]': - case '[details:open]': - let summary = false - const html = text.replace( - /^\([^<]+)<\/p>([\s\S]+)/, - (m, m1, m2) => { - summary = m1 - return m2 - } - ) - const open = Boolean(/:open/.test(m[1])) - - result = detailsTpl({open, summary, html}) - break - default: - return quote - } - return result - } - - return quote - } renderer.origin = origin @@ -381,13 +329,25 @@ export class Compiler { * Compile sidebar */ sidebar(text, level) { + const {toc} = this const currentPath = this.router.getCurrentPath() let html = '' if (text) { html = this.compile(text) } else { - const tree = this.cacheTree[currentPath] || genTree(this.toc, level) + for (let i = 0; i < toc.length; i++) { + if (toc[i].ignoreSubHeading) { + const deletedHeaderLevel = toc[i].level + toc.splice(i, 1) + // Remove headers who are under current header + 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, '') this.cacheTree[currentPath] = tree } @@ -412,7 +372,6 @@ 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 @@ -427,37 +386,12 @@ export class Compiler { /** * Compile cover page */ - cover(text, isHTML = false) { - let html = text - if (!isHTML) { - const cacheToc = this.toc.slice() - html = this.compile(text) || '' - this.toc = cacheToc.slice() - } + cover(text) { + const cacheToc = this.toc.slice() + const html = this.compile(text) - const m = html - .trim() - .match('

    ([^<]*?)

    $') - let style = '' - let hasMask = false - if (m) { - if (m[2] === 'color') { - style = `background: ${m[1] + (m[3] || '')}` - } else { - let path = m[1] - hasMask = true - if (!isAbsolutePath(m[1])) { - path = getPath(this.router.getBasePath(), m[1]) - } - style = `background-image: url(${path}); background-size: cover; background-position: center center;` - } - html = html.replace(m[0], '') - } + this.toc = cacheToc.slice() - return coverTpl({ - style, - hasMask, - text: html - }) + return html } } diff --git a/src/core/render/embed.js b/src/core/render/embed.js index 726bfce..8218374 100644 --- a/src/core/render/embed.js +++ b/src/core/render/embed.js @@ -22,10 +22,8 @@ function walkFetchEmbed({embedTokens, compile, fetch}, cb) { } 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}\\]` - ) - text = ((text.match(pattern) || [])[1] || '').trim() + const pattern = new RegExp(`(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`) + text = ((text.match(pattern)ย || [])[1] || '').trim() } embedToken = compile.lexer( '```' + diff --git a/src/core/render/index.js b/src/core/render/index.js index 936d507..fae1bc5 100644 --- a/src/core/render/index.js +++ b/src/core/render/index.js @@ -175,10 +175,30 @@ export function renderMixin(proto) { } dom.toggleClass(el, 'add', 'show') - let html = this.compiler.cover(text, this.coverIsHTML) + let html = this.coverIsHTML ? text : this.compiler.cover(text) - this._renderTo('.cover-placeholder', html, true) + const m = html + .trim() + .match('

    ([^<]*?)

    $') + if (m) { + if (m[2] === 'color') { + el.style.background = m[1] + (m[3] || '') + } else { + let path = m[1] + + dom.toggleClass(el, 'add', 'has-mask') + 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], '') + } + + this._renderTo('.cover-main', html) sticky() } @@ -208,9 +228,8 @@ export function initRender(vm) { if (config.repo) { html += tpl.corner(config.repo) } - if (config.coverpage) { - html += '
    ' + html += tpl.cover() } if (config.logo) { diff --git a/src/core/render/tpl.js b/src/core/render/tpl.js index a29f155..71f5652 100644 --- a/src/core/render/tpl.js +++ b/src/core/render/tpl.js @@ -57,19 +57,16 @@ export function main(config) { /** * Cover Page */ -export function cover({style, text, hasMask}) { +export function cover() { const SL = ', 100%, 85%' - style = - style || - 'background: 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 ( - `
    ` + - `
    ${text}
    ` + + `
    ` + + '
    ' + '
    ' + '
    ' ) @@ -87,9 +84,7 @@ export function tree(toc, tpl = '
      {inner}
    ') { } let innerHTML = '' toc.forEach(node => { - innerHTML += `
  • ${ - node.title - }
  • ` + innerHTML += `
  • ${node.title}
  • ` if (node.children) { innerHTML += tree(node.children, tpl) } @@ -101,17 +96,6 @@ export function helper(className, content) { return `

    ${content.slice(5).trim()}

    ` } -export function newHelper(className, content) { - return `
    ${content}
    ` -} - export function theme(color) { return `` } - -export function details({open, summary, html}) { - return `
    ${ - summary ? `${summary}` : '' - } - ${html}
    ` -} diff --git a/src/core/router/history/abstract.js b/src/core/router/history/abstract.js index 9731af1..2c0bd95 100644 --- a/src/core/router/history/abstract.js +++ b/src/core/router/history/abstract.js @@ -7,7 +7,7 @@ export class AbstractHistory extends History { this.mode = 'abstract' } - parse(path = '') { + parse(path) { let query = '' const queryIndex = path.indexOf('?') diff --git a/src/core/router/history/base.js b/src/core/router/history/base.js index 6ed56a8..7ea763d 100644 --- a/src/core/router/history/base.js +++ b/src/core/router/history/base.js @@ -3,7 +3,8 @@ import { isAbsolutePath, stringifyQuery, cleanPath, - replaceSlug + replaceSlug, + resolvePath } from '../util' import {noop, merge} from '../../util/core' @@ -21,11 +22,9 @@ function getAlias(path, alias, last) { } function getFileName(path, ext) { - return /\.\w+$/.test(path) ? + return new RegExp(`\\.(${ext.replace(/^\./, '')}|html)$`, 'g').test(path) ? path : - /\/$/g.test(path) ? - `${path}README${ext}` : - `${path}${ext}` + /\/$/g.test(path) ? `${path}README${ext}` : `${path}${ext}` } export class History { @@ -75,9 +74,13 @@ export class History { if (local) { const idIndex = currentRoute.indexOf('?') path = - (idIndex > 0 ? currentRoute.substr(0, idIndex) : currentRoute) + path + (idIndex > 0 ? currentRoute.substring(0, idIndex) : currentRoute) + path } + if (this.config.relativePath && path.indexOf('/') !== 0) { + const currentDir = currentRoute.substring(0, currentRoute.lastIndexOf('/') + 1) + return cleanPath(resolvePath(currentDir + path)) + } return cleanPath('/' + path) } } diff --git a/src/core/router/util.js b/src/core/router/util.js index 13c8109..2ed88c5 100644 --- a/src/core/router/util.js +++ b/src/core/router/util.js @@ -46,15 +46,27 @@ export const isAbsolutePath = cached(path => { export const getParentPath = cached(path => { return /\/$/g.test(path) ? path : - (path = path.match(/(\S*\/)[^/]+$/)) ? - path[1] : - '' + (path = path.match(/(\S*\/)[^/]+$/)) ? path[1] : '' }) export const cleanPath = cached(path => { return path.replace(/^\/+/, '/').replace(/([^:])\/{2,}/g, '$1/') }) +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] + if (segment === '..') { + resolved.pop() + } else if (segment !== '.') { + resolved.push(segment) + } + } + return '/' + resolved.join('/') +}) + export function getPath(...args) { return cleanPath(args.join('/')) } diff --git a/src/plugins/matomo.js b/src/plugins/matomo.js new file mode 100644 index 0000000..7b61cba --- /dev/null +++ b/src/plugins/matomo.js @@ -0,0 +1,37 @@ +function appendScript(options) { + const script = document.createElement('script') + script.async = true + script.src = options.host + '/matomo.js' + document.body.appendChild(script) +} + +function init(options) { + window._paq = window._paq || [] + window._paq.push(['trackPageView']) + window._paq.push(['enableLinkTracking']) + setTimeout(function() { + appendScript(options) + window._paq.push(['setTrackerUrl', options.host + '/matomo.php']) + window._paq.push(['setSiteId', options.id + '']) + }, 0) +} + +function collect() { + if (!window._paq) { + init($docsify.matomo) + } + window._paq.push(['setCustomUrl', window.location.hash.substr(1)]) + window._paq.push(['setDocumentTitle', document.title]) + window._paq.push(['trackPageView']) +} + +const install = function (hook) { + if (!$docsify.matomo) { + console.error('[Docsify] matomo is required.') + return + } + + hook.beforeEach(collect) +} + +$docsify.plugins = [].concat(install, $docsify.plugins) diff --git a/src/plugins/search/search.js b/src/plugins/search/search.js index 4f14ecd..2c9febf 100644 --- a/src/plugins/search/search.js +++ b/src/plugins/search/search.js @@ -96,29 +96,27 @@ export function search(query) { for (let i = 0; i < data.length; i++) { const post = data[i] - let isMatch = false + let matchesScore = 0 let resultStr = '' const postTitle = post.title && post.title.trim() const postContent = post.body && post.body.trim() const postUrl = post.slug || '' - if (postTitle && postContent) { - keywords.forEach(keyword => { + if (postTitle) { + 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 - indexTitle = postTitle && postTitle.search(regEx) - indexContent = postContent && postContent.search(regEx) + indexTitle = postTitle ? postTitle.search(regEx) : -1 + indexContent = postContent ? postContent.search(regEx) : -1 - if (indexTitle < 0 && indexContent < 0) { - isMatch = false - } else { - isMatch = true + if (indexTitle >= 0 || indexContent >= 0) { + matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0; if (indexContent < 0) { indexContent = 0 } @@ -144,11 +142,12 @@ export function search(query) { } }) - if (isMatch) { + if (matchesScore > 0) { const matchingPost = { title: escapeHtml(postTitle), - content: resultStr, - url: postUrl + content: postContent ? resultStr : '', + url: postUrl, + score: matchesScore } matchingResults.push(matchingPost) @@ -156,7 +155,7 @@ export function search(query) { } } - return matchingResults + return matchingResults.sort((r1, r2) => r2.score - r1.score); } export function init(config, vm) { diff --git a/src/themes/basic/_layout.styl b/src/themes/basic/_layout.styl index a3556d9..387fd79 100644 --- a/src/themes/basic/_layout.styl +++ b/src/themes/basic/_layout.styl @@ -321,9 +321,6 @@ body.sticky border-bottom 1px solid #eee margin 2em 0 -.markdown-section summary - cursor pointer - .markdown-section iframe border 1px solid #eee /* fix horizontal overflow on iOS Safari */ @@ -354,7 +351,6 @@ body.sticky background-color #f8f8f8 .markdown-section p.tip -.markdown-section .docsify-tip background-color #f8f8f8 border-bottom-right-radius 2px border-left 4px solid #f66 @@ -386,23 +382,9 @@ body.sticky color $color-text .markdown-section p.warn -.markdown-section .docsify-warn background rgba($color-primary, 0.1) border-radius 2px padding 1rem - margin: 2em 0 - -.markdown-section .docsify-success - background rgba(#42b983, 0.1) - border-radius 2px - padding 1rem - margin: 2em 0 - -.markdown-section .docsify-error - background rgba(#f66, 0.1) - border-radius 2px - margin: 2em 0 - padding 1rem .markdown-section ul.task-list > li list-style-type none diff --git a/src/themes/dark.styl b/src/themes/dark.styl index 7c5eb50..02ca630 100644 --- a/src/themes/dark.styl +++ b/src/themes/dark.styl @@ -216,7 +216,6 @@ pre::after top 0 .markdown-section p.tip -.markdown-section .docsify-tip background-color #282828 color #657b83 diff --git a/test/unit/base.js b/test/unit/base.js new file mode 100644 index 0000000..036700b --- /dev/null +++ b/test/unit/base.js @@ -0,0 +1,62 @@ +/* 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') + }) +}) diff --git a/test/unit/util.js b/test/unit/util.js new file mode 100644 index 0000000..1e65daf --- /dev/null +++ b/test/unit/util.js @@ -0,0 +1,30 @@ +/* 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') + }) +}) diff --git a/themes/.gitkeep b/themes/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/themes/.gitkeep @@ -0,0 +1 @@ +