Skip to content

[Chore] revert merges. #1912

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 50 commits into from
Oct 26, 2022
Merged
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
c1227b2
fix: packages/docsify-server-renderer/package.json & packages/docsify…
snyk-bot Jan 13, 2022
719dcbe
fix: correctly fix missing +1, -1 (#1722)
ChoKaPeek Jan 14, 2022
799c0a4
Update LICENSE
trusktr Jan 17, 2022
e0bead3
Merge pull request #1725 from docsifyjs/update-license
trusktr Jan 23, 2022
47cb36e
mention that SSR is experimental and incomplete, prevent people from …
trusktr Jan 24, 2022
b58941e
Merge pull request #1734 from docsifyjs/mark-ssr-as-experimental
trusktr Jan 26, 2022
c49c39a
refactor: Update test environments and lint configuration (#1736)
jhildenbiddle Jan 31, 2022
f5b5282
chore: bump node-fetch in /packages/docsify-server-renderer (#1738)
dependabot[bot] Feb 1, 2022
18cd18c
docs: update readme (#1740)
sy-records Feb 2, 2022
301b516
fix: Coverpage when content > viewport height (#1744)
jhildenbiddle Feb 3, 2022
8aee074
chore: sync emojis (#1745)
sy-records Feb 4, 2022
fa6df6d
fix: Legacy bugs (styles, site plugin error, and dev server error) (#…
jhildenbiddle Feb 5, 2022
2dc5b12
fix: package.json & package-lock.json to reduce vulnerabilities (#1756)
snyk-bot Feb 24, 2022
a00cfbb
chore: bump follow-redirects from 1.14.7 to 1.14.9 (#1757)
dependabot[bot] Feb 25, 2022
fd883e6
chore: bump prismjs in /packages/docsify-server-renderer (#1760)
dependabot[bot] Mar 3, 2022
35002c9
feat: Native emoji w/ image-based fallbacks and improved parsing (#1746)
jhildenbiddle Mar 6, 2022
ba5ee26
feat: Emoji build (#1766)
jhildenbiddle Mar 8, 2022
9bff31d
chore: Remove dompurify (#1490)
jhildenbiddle Mar 8, 2022
716a48e
chore: update develop branch preview link (#1772)
sy-records Mar 15, 2022
63b2535
feat: Plugin error handling (#1742)
jhildenbiddle Mar 15, 2022
32c6b38
Fix: ready/doneEach order with async afterEach (#1770)
jhildenbiddle Mar 16, 2022
6f4f09b
docs: Update configuration options (#1773)
jhildenbiddle Mar 18, 2022
9e6a3e6
docs: Minor fixes to plugin docs (#1774)
jhildenbiddle Mar 19, 2022
093d4a0
chore: update vercel link (#1775)
sy-records Mar 19, 2022
2d13255
chore: update vercel logo (#1776)
sy-records Mar 23, 2022
bec187f
chore: Update CONTRIBUTING.md (#1782)
bhbarquero-dev Mar 31, 2022
b8b221f
chore: bump minimist from 1.2.5 to 1.2.6 (#1787)
dependabot[bot] Apr 13, 2022
8cbc7c8
Virtual Routes Support (#1799)
illBeRoy May 23, 2022
fa14210
docs: update the name for "Simplified Chinese" (#1811)
awxiaoxian2020 Jun 3, 2022
54cc5f9
fix: cornerExternalLinkTarget config. (#1814)
Koooooo-7 Jun 4, 2022
150236a
Improve README.md sentence (#1817)
shaunbharat Jun 7, 2022
1e46f2b
chore: bump jpeg-js from 0.4.3 to 0.4.4 (#1820)
dependabot[bot] Jun 17, 2022
682bf96
chore: bump parse-url from 6.0.0 to 6.0.2 (#1833)
dependabot[bot] Jul 10, 2022
19e40c2
Docs: Fix plugin insertion order in docs (#1850)
jhildenbiddle Aug 2, 2022
3c9b3d9
fix: Ignore emoji shorthand codes in URIs (#1847)
socsieng Aug 3, 2022
9832805
fix: fix docsify-ignore in seach title. (#1872)
Koooooo-7 Aug 30, 2022
9b74744
fix: fix search with no content. (#1878)
Koooooo-7 Sep 17, 2022
c98fda7
docs: Update GitHub default branch from to 'main' (#1883)
123MwanjeMike Sep 17, 2022
79a6619
chore: upgrade caniuse-lit. (#1879)
Koooooo-7 Sep 20, 2022
91272ca
chore: bump trim-newlines and lerna (#1895)
dependabot[bot] Oct 4, 2022
d27af3d
fix: filter null node first. (#1909)
Koooooo-7 Oct 26, 2022
7db2434
[build]: 4.12.3
Koooooo-7 Oct 26, 2022
8fcd92b
[build] 4.12.4
Koooooo-7 Oct 26, 2022
c044517
chore: add changelog 4.12.4
Koooooo-7 Oct 26, 2022
6ac6f52
update: update dependencies.
Koooooo-7 Oct 26, 2022
73ea28d
fix: fix test.
Koooooo-7 Oct 26, 2022
dbc4b45
fix: upgrade dependencies.
Koooooo-7 Oct 26, 2022
a6701d6
[build] 4.13.0
Koooooo-7 Oct 26, 2022
1d29eb3
chore: add changelog 4.13.0
Koooooo-7 Oct 26, 2022
78aaa37
chore: add changelog v4.13.0
Koooooo-7 Oct 26, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .codesandbox/ci.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"sandboxes": ["2d17z"],
"packages": [".", "packages/docsify-server-renderer"]
"packages": [".", "packages/docsify-server-renderer"],
"node": "16"
}
11 changes: 5 additions & 6 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
.git
packages/docsify-server-renderer/build.js
node_modules
**/*.md
build
server.js
docs
lib
node_modules
packages/docsify-server-renderer/build.js
server.js
themes
build
docs/
**/*.md
41 changes: 31 additions & 10 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
const prettierConfig = require('./.prettierrc');

module.exports = {
root: true,
parser: 'babel-eslint',
extends: [
'eslint:recommended',
'plugin:import/recommended',
'plugin:prettier/recommended', // Must be last
],
parser: '@babel/eslint-parser',
parserOptions: {
sourceType: 'module',
ecmaVersion: 2019,
},
plugins: ['prettier', 'import'],
env: {
browser: true,
node: true,
es6: true,
node: true,
},
plugins: ['prettier', 'import'],
extends: ['eslint:recommended', 'plugin:import/recommended'],
settings: {
'import/ignore': ['node_modules', '.json$'],
},
rules: {
'prettier/prettier': ['error'],
camelcase: ['warn'],
'no-useless-escape': ['warn'],
curly: ['error', 'all'],
'dot-notation': ['error'],
eqeqeq: ['error'],
@@ -33,9 +37,23 @@ module.exports = {
'no-proto': ['error'],
'no-return-assign': ['error'],
'no-self-compare': ['error'],
'no-shadow': ['warn'],
'no-shadow-restricted-names': ['error'],
'no-shadow': [
'error',
{
allow: [
'Events',
'Fetch',
'Lifecycle',
'Render',
'Router',
'VirtualRoutes',
],
},
],
'no-unused-vars': ['error', { args: 'none' }],
'no-useless-call': ['error'],
'no-useless-escape': ['warn'],
'no-var': ['error'],
'no-void': ['error'],
'no-with': ['error'],
@@ -46,18 +64,21 @@ module.exports = {

// Import rules
// Search way how integrate with `lerna`
'import/no-unresolved': 'off',
'import/imports-first': ['error'],
'import/newline-after-import': ['error'],
'import/no-duplicates': ['error'],
'import/no-mutable-exports': ['error'],
'import/no-named-as-default': ['error'],
'import/no-named-as-default-member': ['error'],
'import/no-named-as-default': ['error'],
'import/no-unresolved': 'off',
'import/order': ['warn'],

// Prettier (Must be last)
'prettier/prettier': ['warn', prettierConfig],
},
globals: {
Docsify: 'writable',
$docsify: 'writable',
Docsify: 'writable',
dom: 'writable',
},
};
30 changes: 0 additions & 30 deletions .github/workflows/lint.yml

This file was deleted.

81 changes: 59 additions & 22 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -2,41 +2,78 @@ name: Build & Test

on:
push:
branches:
- master
- develop
branches: [master, develop]
pull_request:
branches:
- master
- develop
branches: [master, develop]

jobs:
build:
lint:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['lts/*']
steps:
- uses: actions/checkout@v2
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci --ignore-scripts
- name: Build
run: npm run build
- name: Lint
run: npm run lint

test-jest:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
node-version: [12.x, 14.x]
node-version: ['lts/*']
os: ['macos-latest', 'ubuntu-latest', 'windows-latest']
steps:
- uses: actions/checkout@v2
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci --ignore-scripts
- name: Build
run: npm run build
- name: Unit Tests
run: npm run test:unit -- --ci --runInBand
- name: Integration Tests
run: npm run test:integration -- --ci --runInBand

test-playwright:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['lts/*']
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- name: bootstrap
run: npm run bootstrap
- name: unit tests
run: npm run test:unit -- -ci --runInBand
- name: integration tests
run: npm run test:integration -- -ci --runInBand
- uses: microsoft/playwright-github-action@v1
- name: e2e tests
run: npm run test:e2e -- --ci --runInBand
- name: Upload artifacts (diff output)
cache: 'npm'
- name: Install dependencies
run: npm ci --ignore-scripts
- name: Build
run: npm run build
- name: Install Playwright
run: npx playwright install --with-deps
- name: E2E Tests (Playwright)
run: npm run test:e2e
- name: Store artifacts
uses: actions/upload-artifact@v2
if: failure()
with:
name: ${{ matrix.os }}-${{ matrix.node-version }}-diff-output
path: ${{ github.workspace }}/test/**/__diff_output__/*
name: ${{ matrix.os }}-${{ matrix.node-version }}-artifacts
path: |
_playwright-results/
_playwright-report/
11 changes: 6 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
*.log
.DS_Store
.idea
__diff_output__
lib/
node_modules
themes/
*.log
/_playwright-report
/_playwright-results
/lib
/node_modules
/themes

# exceptions
!.gitkeep
2 changes: 1 addition & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.eslintignore
.eslintrc
.github/
.github
.gitignore
.travis.yml
2 changes: 1 addition & 1 deletion .prettierrc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
arrowParens: 'avoid',
singleQuote: true,
trailingComma: 'es5',
};
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "esbenp.prettier-vscode",
"cSpell.words": ["coverpage"]
}
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
# [4.13.0](https://github.com/docsifyjs/docsify/compare/v4.12.4...v4.13.0) (2022-10-26)


### Bug Fixes

* cornerExternalLinkTarget config. ([#1814](https://github.com/docsifyjs/docsify/issues/1814)) ([54cc5f9](https://github.com/docsifyjs/docsify/commit/54cc5f939b9499ae56604f589eef4e3f1c13cdc5))
* correctly fix missing +1, -1 ([#1722](https://github.com/docsifyjs/docsify/issues/1722)) ([719dcbe](https://github.com/docsifyjs/docsify/commit/719dcbea5cb0c8b0835f8e9fc473b094feecb7ec))
* Coverpage when content > viewport height ([#1744](https://github.com/docsifyjs/docsify/issues/1744)) ([301b516](https://github.com/docsifyjs/docsify/commit/301b5169613e95765eda335a4b21d0f9f9cbbbfd)), closes [#381](https://github.com/docsifyjs/docsify/issues/381)
* filter null node first. ([#1909](https://github.com/docsifyjs/docsify/issues/1909)) ([d27af3d](https://github.com/docsifyjs/docsify/commit/d27af3dd09a882fce4f1e2774065de57a3501858))
* fix docsify-ignore in seach title. ([#1872](https://github.com/docsifyjs/docsify/issues/1872)) ([9832805](https://github.com/docsifyjs/docsify/commit/9832805681cc6099e9c78deecf6dc0c6fb61fd9b))
* fix search with no content. ([#1878](https://github.com/docsifyjs/docsify/issues/1878)) ([9b74744](https://github.com/docsifyjs/docsify/commit/9b74744299ece0108573a142e5a2e949dc569254))
* Ignore emoji shorthand codes in URIs ([#1847](https://github.com/docsifyjs/docsify/issues/1847)) ([3c9b3d9](https://github.com/docsifyjs/docsify/commit/3c9b3d9702bb05a5ff45a4ce4233e144cf1ebecb)), closes [#1823](https://github.com/docsifyjs/docsify/issues/1823)
* Legacy bugs (styles, site plugin error, and dev server error) ([#1743](https://github.com/docsifyjs/docsify/issues/1743)) ([fa6df6d](https://github.com/docsifyjs/docsify/commit/fa6df6d58487ec294e22be04ac159dfb5745bd66))
* package.json & package-lock.json to reduce vulnerabilities ([#1756](https://github.com/docsifyjs/docsify/issues/1756)) ([2dc5b12](https://github.com/docsifyjs/docsify/commit/2dc5b12b715e3ad1922a6401e3fd9837a99ef9c0))
* packages/docsify-server-renderer/package.json & packages/docsify-server-renderer/package-lock.json to reduce vulnerabilities ([#1715](https://github.com/docsifyjs/docsify/issues/1715)) ([c1227b2](https://github.com/docsifyjs/docsify/commit/c1227b22cb8a3fb6c362ca8ac109082ab2251cc3))


### Features

* Emoji build ([#1766](https://github.com/docsifyjs/docsify/issues/1766)) ([ba5ee26](https://github.com/docsifyjs/docsify/commit/ba5ee26f00e957b58173f96b1901f6ffd3d8e5f5))
* Native emoji w/ image-based fallbacks and improved parsing ([#1746](https://github.com/docsifyjs/docsify/issues/1746)) ([35002c9](https://github.com/docsifyjs/docsify/commit/35002c92b762f0d12e51582d7de7914fa380596a)), closes [#779](https://github.com/docsifyjs/docsify/issues/779)
* Plugin error handling ([#1742](https://github.com/docsifyjs/docsify/issues/1742)) ([63b2535](https://github.com/docsifyjs/docsify/commit/63b2535a45a98945ec897277f4fbddec2ddba887))



## [4.12.2](https://github.com/docsifyjs/docsify/compare/v4.12.1...v4.12.2) (2022-01-06)


4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ We welcome any type of contribution, not only code. You can help with

## Your First Contribution

Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github).

## Submitting code

@@ -64,4 +64,4 @@ Thank you to all our sponsors! (please ask your company to also support this ope
<a href="https://opencollective.com/docsify/sponsor/8/website" target="_blank"><img src="https://opencollective.com/docsify/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/docsify/sponsor/9/website" target="_blank"><img src="https://opencollective.com/docsify/sponsor/9/avatar.svg"></a>

<!-- This `CONTRIBUTING.md` is based on @nayafia's template https://github.com/nayafia/contributing-template -->
<!-- This `CONTRIBUTING.md` is based on @nayafia's template https://github.com/nayafia/contributing-template -->
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2016 - present cinwell.li
Copyright (c) 2016 - present Docsify Contributors (https://github.com/docsifyjs/docsify/graphs/contributors)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -12,9 +12,7 @@
<a href="#backers"><img alt="Backers on Open Collective" src="https://opencollective.com/docsify/backers/badge.svg?style=flat-square"></a>
<a href="#sponsors">
<img alt="Sponsors on Open Collective" src="https://opencollective.com/docsify/sponsors/badge.svg?style=flat-square"></a>
<a><img src="https://github.com/docsifyjs/docsify/workflows/Unit%20tests%20Suite/badge.svg?branch=develop&amp;event=push" alt="Unit tests Suite"></a>
<a><img src="https://github.com/docsifyjs/docsify/workflows/Linting%20Checks/badge.svg?branch=develop&amp;event=push" alt="Linting Checks"></a>
<a><img src="https://github.com/docsifyjs/docsify/workflows/Testing%20the%20e2e%20test%20suites/badge.svg?branch=develop&amp;event=push" alt="Testing the e2e test suites"></a>
<a href="https://github.com/docsifyjs/docsify/actions/workflows/test.yml"><img src="https://github.com/docsifyjs/docsify/actions/workflows/test.yml/badge.svg" alt="Build & Test"></a>
<a href="https://www.npmjs.com/package/docsify"><img alt="npm" src="https://img.shields.io/npm/v/docsify.svg?style=flat-square"></a>
<a href="https://github.com/QingWei-Li/donate"><img alt="donate" src="https://img.shields.io/badge/%24-donate-ff69b4.svg?style=flat-square"></a>
<a href="https://discord.gg/3NwKFyR"><img alt="Join Discord community and chat about Docsify" src="https://img.shields.io/discord/713647066802421792.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2&cacheSeconds=60"></a>
@@ -31,7 +29,7 @@

## Links

- [`develop` branch preview](https://docsifyjs.netlify.com/)
- [`develop` branch preview](https://docsify-preview.vercel.app/)
- [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)
@@ -46,7 +44,7 @@
- Multiple themes
- Useful plugin API
- Compatible with IE11
- Support SSR ([example](https://github.com/docsifyjs/docsify-ssr-demo))
- Experimental SSR support ([example](https://github.com/docsifyjs/docsify-ssr-demo))
- Support embedded files

## Quick start
@@ -72,7 +70,7 @@ Move to [awesome-docsify](https://github.com/docsifyjs/awesome-docsify#showcase)

### Online one-click setup for Contributing

You can use Gitpod(A free online VS Code-like IDE) for contributing. With single click it'll launch a workspace and automatically:
You can use Gitpod (a free online VS Code-like IDE) for contributing. With a single click it'll launch a workspace and automatically:

- clone the docsify repo.
- install the dependencies.
@@ -126,6 +124,4 @@ This project exists thanks to all the people who contribute. [[Contribute](CONTR

## Special Thanks

_Vercel_ has given us a Pro account.

<a href="https://vercel.com/?utm_source=docsifyjsdocs" target="_blank"><img src="docs/_media/vercel_logo.svg" width="100px"></a>
A preview of Docsify's PR and develop branch is <a href="https://vercel.com/?utm_source=docsifyjs&utm_campaign=oss" target="_blank">Powered by <img src="https://cdn.jsdelivr.net/gh/docsifyjs/docsify/docs/_media/vercel_logo.svg" alt="Vercel" width="133px"></a>
109 changes: 109 additions & 0 deletions build/emoji.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
const axios = require('axios');
const fs = require('fs');
const path = require('path');

const filePaths = {
emojiMarkdown: path.resolve(process.cwd(), 'docs', 'emoji.md'),
emojiJS: path.resolve(
process.cwd(),
'src',
'core',
'render',
'emoji-data.js'
),
};

async function getEmojiData() {
const emojiDataURL = 'https://api.github.com/emojis';

console.info(`- Fetching emoji data from ${emojiDataURL}`);

const response = await axios.get(emojiDataURL);
const baseURL = Object.values(response.data)
.find(url => /unicode\//)
.split('unicode/')[0];
const data = { ...response.data };

// Remove base URL from emoji URLs
Object.entries(data).forEach(
([key, value]) => (data[key] = value.replace(baseURL, ''))
);

console.info(`- Retrieved ${Object.keys(data).length} emoji entries`);

return {
baseURL,
data,
};
}

function writeEmojiPage(emojiData) {
const isExistingPage = fs.existsSync(filePaths.emojiMarkdown);
const emojiPage =
(isExistingPage && fs.readFileSync(filePaths.emojiMarkdown, 'utf8')) ||
`<!-- START -->\n\n<!-- END -->`;
const emojiRegEx = /(<!--\s*START.*-->\n)([\s\S]*)(\n<!--\s*END.*-->)/;
const emojiMatch = emojiPage.match(emojiRegEx);
const emojiMarkdownStart = emojiMatch[1].trim();
const emojiMarkdown = emojiMatch[2].trim();
const emojiMarkdownEnd = emojiMatch[3].trim();
const newEmojiMarkdown = Object.keys(emojiData.data)
.reduce(
(preVal, curVal) =>
(preVal += `:${curVal}: ` + '`' + `:${curVal}:` + '`' + '\n\n'),
''
)
.trim();

if (emojiMarkdown !== newEmojiMarkdown) {
const newEmojiPage = emojiPage.replace(
emojiMatch[0],
`${emojiMarkdownStart}\n\n${newEmojiMarkdown}\n\n${emojiMarkdownEnd}`
);

fs.writeFileSync(filePaths.emojiMarkdown, newEmojiPage);

console.info(
`- ${!isExistingPage ? 'Created' : 'Updated'}: ${filePaths.emojiMarkdown}`
);
} else {
console.info(`- No changes: ${filePaths.emojiMarkdown}`);
}
}

function writeEmojiJS(emojiData) {
const isExistingPage = fs.existsSync(filePaths.emojiJS);
const emojiJS = isExistingPage && fs.readFileSync(filePaths.emojiJS, 'utf8');
const newEmojiJS = [
'/* eslint-disable */\n',
'// =============================================================================',
'// DO NOT EDIT: This file is auto-generated by an /build/emoji.js',
'// =============================================================================\n',
`export default ${JSON.stringify(emojiData, {}, 2)}`,
].join('\n');

if (!emojiJS || emojiJS !== newEmojiJS) {
fs.writeFileSync(filePaths.emojiJS, newEmojiJS);

console.info(
`- ${!isExistingPage ? 'Created' : 'Updated'}: ${filePaths.emojiJS}`
);
} else {
console.info(`- No changes: ${filePaths.emojiJS}`);
}
}

(async () => {
console.info('Build emoji');

try {
const emojiData = await getEmojiData();

if (emojiData) {
writeEmojiPage(emojiData);
writeEmojiJS(emojiData);
}
} catch (err) {
console.warn(`- Error: ${err.message}`);
}
})();
8 changes: 1 addition & 7 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
## What it is

docsify generates your documentation website on the fly. Unlike GitBook, it does not generate static html files. Instead, it smartly loads and parses your Markdown files and displays them as a website. To start using it, all you need to do is create an `index.html` and [deploy it on GitHub Pages](deploy.md).
Docsify generates your documentation website on the fly. Unlike GitBook, it does not generate static html files. Instead, it smartly loads and parses your Markdown files and displays them as a website. To start using it, all you need to do is create an `index.html` and [deploy it on GitHub Pages](deploy.md).

See the [Quick start](quickstart.md) guide for more details.

@@ -30,9 +30,3 @@ Please consider donating if you think docsify is helpful to you or that my work
## Community

Users and the development team are usually in the [Discord server](https://discord.gg/3NwKFyR).

## Special Thanks

_Vercel_ has given us a Pro account.

<a href="https://vercel.com/?utm_source=docsifyjsdocs" target="_blank"><img src="https://cdn.jsdelivr.net/gh/docsifyjs/docsify/docs/_media/vercel_logo.svg" width="100px"></a>
2 changes: 1 addition & 1 deletion docs/_coverpage.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
![logo](_media/icon.svg)

# docsify <small>4.12.2</small>
# docsify <small>4.13.0</small>

> A magical documentation site generator.
6 changes: 6 additions & 0 deletions docs/_media/powered-by-vercel.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/_navbar.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
- Translations
- [:uk: English](/)
- [:cn: 中文](/zh-cn/)
- [:cn: 简体中文](/zh-cn/)
- [:de: Deutsch](/de-de/)
- [:es: Español](/es/)
- [:ru: Русский](/ru-ru/)
1 change: 1 addition & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
- [Write a Plugin](write-a-plugin.md)
- [Markdown configuration](markdown.md)
- [Language highlighting](language-highlight.md)
- [Emoji](emoji.md)

- Guide

726 changes: 455 additions & 271 deletions docs/configuration.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/custom-navbar.md
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ If you need custom navigation, you can create a HTML-based navigation bar.
<body>
<nav>
<a href="#/">EN</a>
<a href="#/zh-cn/">中文</a>
<a href="#/zh-cn/">简体中文</a>
</nav>
<div id="app"></div>
</body>
6 changes: 3 additions & 3 deletions docs/deploy.md
Original file line number Diff line number Diff line change
@@ -7,14 +7,14 @@ Similar to [GitBook](https://www.gitbook.com), you can deploy files to GitHub Pa
There are three places to populate your docs for your GitHub repository:

- `docs/` folder
- master branch
- main branch
- gh-pages branch

It is recommended that you save your files to the `./docs` subfolder of the `master` branch of your repository. Then select `master branch /docs folder` as your GitHub Pages source in your repository's settings page.
It is recommended that you save your files to the `./docs` subfolder of the `main` branch of your repository. Then select `main branch /docs folder` as your GitHub Pages source in your repository's settings page.

![GitHub Pages](_images/deploy-github-pages.png)

!> You can also save files in the root directory and select `master branch`.
!> You can also save files in the root directory and select `main branch`.
You'll need to place a `.nojekyll` file in the deploy location (such as `/docs` or the gh-pages branch)

## GitLab Pages
3,759 changes: 3,759 additions & 0 deletions docs/emoji.md

Large diffs are not rendered by default.

51 changes: 19 additions & 32 deletions docs/index.html
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@

<body>
<div id="app">Loading ...</div>
<script src="//cdn.jsdelivr.net/npm/docsify-plugin-carbon@1/index.js"></script>
<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)\//);
@@ -118,21 +118,21 @@
'/zh-cn/': '搜索',
'/': 'Search',
},
pathNamespaces: ['/es', '/de-de', '/ru-ru', '/zh-cn']
pathNamespaces: ['/es', '/de-de', '/ru-ru', '/zh-cn'],
},
vueComponents: {
'button-counter': {
template:
'<button @click="count += 1">You clicked me {{ count }} times</button>',
data: function() {
data: function () {
return {
count: 0,
};
},
},
},
vueGlobalOptions: {
data: function() {
data: function () {
return {
count: 0,
message: 'Hello, World!',
@@ -145,7 +145,7 @@
};
},
computed: {
timeOfDay: function() {
timeOfDay: function () {
const date = new Date();
const hours = date.getHours();

@@ -159,14 +159,14 @@
},
},
methods: {
hello: function() {
hello: function () {
alert(this.message);
},
},
},
vueMounts: {
'#counter': {
data: function() {
data: function () {
return {
count: 0,
};
@@ -175,8 +175,8 @@
},
plugins: [
DocsifyCarbon.create('CEBI6KQE', 'docsifyjsorg'),
function(hook, vm) {
hook.beforeEach(function(html) {
function (hook, vm) {
hook.beforeEach(function (html) {
if (/githubusercontent\.com/.test(vm.route.file)) {
url = vm.route.file
.replace('raw.githubusercontent.com', 'github.com')
@@ -197,16 +197,7 @@
'\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>'
);
}),
hook.afterEach(function(html) {
if (vm.route.path === '/') {
return html;
}
return (
html +
'<br/> <i>Vercel</i> has given us a Pro account <br/> <a href="https://vercel.com/?utm_source=docsifyjsdocs" target="_blank"><img src="https://cdn.jsdelivr.net/gh/docsifyjs/docsify/docs/_media/vercel_logo.svg" alt="Vercel" width="100" height="64"></a>'
);
});
})
},
],
};
@@ -218,19 +209,15 @@
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-nginx.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-php.min.js"></script>
<script>
(function() {
function loadJS(src, attrs) {
document.write(
'<script src="' + src + '" ' + (attrs || '') + '><\/script>'
);
}

// Public site only
if (/docsify/.test(location.host)) {
loadJS('//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/ga.min.js');
loadJS('//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/matomo.min.js');
}
})();
// Public site only
if (/docsify/.test(location.host)) {
document.write(
'<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/ga.min.js"><\/script>'
);
document.write(
'<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/matomo.min.js"><\/script>'
);
}
</script>
<script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.min.js"></script>
<!-- <script src="//cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script> -->
42 changes: 23 additions & 19 deletions docs/plugins.md
Original file line number Diff line number Diff line change
@@ -4,12 +4,13 @@

By default, the hyperlink on the current page is recognized and the content is saved in `localStorage`. You can also specify the path to the files.

<!-- prettier-ignore -->
```html
<script>
window.$docsify = {
search: 'auto', // default
search : [
search: [
'/', // => /README.md
'/guide', // => /guide.md
'/get-started', // => /get-started.md
@@ -25,15 +26,15 @@ By default, the hyperlink on the current page is recognized and the content is s
// Localization
placeholder: {
'/zh-cn/': '搜索',
'/': 'Type to search'
'/': 'Type to search',
},
noData: 'No Results!',
// Localization
noData: {
'/zh-cn/': '找不到结果',
'/': 'No Results'
'/': 'No Results',
},
// Headline depth, 1 - 6
@@ -54,9 +55,9 @@ By default, the hyperlink on the current page is recognized and the content is s
// You can provide a regexp to match prefixes. In this case,
// the matching substring will be used to identify the index
pathNamespaces: /^(\/(zh-cn|ru-ru))?(\/(v1|v2))?/
}
}
pathNamespaces: /^(\/(zh-cn|ru-ru))?(\/(v1|v2))?/,
},
};
</script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
@@ -75,30 +76,31 @@ Install the plugin and configure the track id.
```html
<script>
window.$docsify = {
ga: 'UA-XXXXX-Y'
}
ga: 'UA-XXXXX-Y',
};
</script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/ga.min.js"></script>
```

Configure by `data-ga`.

<!-- prettier-ignore -->
```html
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js" data-ga="UA-XXXXX-Y"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/ga.min.js"></script>
```

## emoji
## Emoji

The default is to support parsing emoji. For example `:100:` will be parsed to :100:. But it is not precise because there is no matching non-emoji string. If you need to correctly parse the emoji string, you need install this plugin.
Renders a larger collection of emoji shorthand codes. Without this plugin, Docsify is able to render only a limited number of emoji shorthand codes.

!> Deprecated as of v4.13. Docsify no longer requires this plugin for full emoji support.

```html
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/emoji.min.js"></script>
```

?> If you don't want to parse to emoji, you can use __colon_<span>_</span> or `&#58;`. If you need to use in the title, we recommend using `&#58;`. For example, `&#58;100:`

## External Script

If the script on the page is an external one (imports a js file via `src` attribute), you'll need this plugin to make it work.
@@ -118,7 +120,7 @@ Medium's image zoom. Based on [medium-zoom](https://github.com/francoischalifour
Exclude the special image

```markdown
![](image.png ":no-zoom")
![](image.png ':no-zoom')
```

## Edit on github
@@ -150,8 +152,8 @@ Disqus comments. https://disqus.com/
```html
<script>
window.$docsify = {
disqus: 'shortname'
}
disqus: 'shortname',
};
</script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/disqus.min.js"></script>
```
@@ -161,7 +163,7 @@ Disqus comments. https://disqus.com/
[Gitalk](https://github.com/gitalk/gitalk) is a modern comment component based on Github Issue and Preact.

```html
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/gitalk/dist/gitalk.css">
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/gitalk/dist/gitalk.css" />

<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/gitalk.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/gitalk/dist/gitalk.min.js"></script>
@@ -171,10 +173,12 @@ Disqus comments. https://disqus.com/
clientSecret: 'Github Application Client Secret',
repo: 'Github repo',
owner: 'Github repo owner',
admin: ['Github repo collaborators, only these guys can initialize github issues'],
admin: [
'Github repo collaborators, only these guys can initialize github issues',
],
// facebook-like distraction free mode
distractionFreeMode: false
})
distractionFreeMode: false,
});
</script>
```

55 changes: 31 additions & 24 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
@@ -18,9 +18,9 @@ docsify init ./docs

After the `init` is complete, you can see the file list in the `./docs` subdirectory.

* `index.html` as the entry file
* `README.md` as the home page
* `.nojekyll` prevents GitHub Pages from ignoring files that begin with an underscore
- `index.html` as the entry file
- `README.md` as the home page
- `.nojekyll` prevents GitHub Pages from ignoring files that begin with an underscore

You can easily update the documentation in `./docs/README.md`, of course you can add [more pages](more-pages.md).

@@ -43,21 +43,24 @@ 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 rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/themes/vue.css" />
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
//...
}
</script>
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
</body>
<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
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify@4/themes/vue.css"
/>
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
//...
};
</script>
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
</body>
</html>
```

@@ -75,7 +78,10 @@ Specifying a major version in the URL (`@4`) will allow your site will receive n
If you prefer to lock docsify to a specific version, specify the full version after the `@` symbol in the URL. This is the safest way to ensure your site will look and behave the same way regardless of any changes made to future versions of docsify.

```html
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4.11.4/themes/vue.css">
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify@4.11.4/themes/vue.css"
/>
<script src="//cdn.jsdelivr.net/npm/docsify@4.11.4"></script>
```

@@ -86,6 +92,7 @@ If you have Python installed on your system, you can easily use it to run a stat
```python2
cd docs && python -m SimpleHTTPServer 3000
```

```python3
cd docs && python -m http.server 3000
```
@@ -107,11 +114,11 @@ You should set the `data-app` attribute if you changed `el`:

<div data-app id="main">Please wait...</div>

<script>
window.$docsify = {
el: '#main'
}
</script>
<script>
window.$docsify = {
el: '#main',
};
</script>
```

Compare [el configuration](configuration.md#el).
9 changes: 8 additions & 1 deletion docs/ssr.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# Server-Side Rendering

!> :construction: SSR support is experimental and incomplete. We are working on it. Plugins and
some features of Docsify will not work in SSR mode yet. :construction:

<!--
This link is dead.
See https://docsify.now.sh
-->

Repo in https://github.com/docsifyjs/docsify-ssr-demo
Sample repo at https://github.com/docsifyjs/docsify-ssr-demo

## Why SSR?

- Better SEO
- Feeling cool

258 changes: 196 additions & 62 deletions docs/write-a-plugin.md
Original file line number Diff line number Diff line change
@@ -1,85 +1,234 @@
# Write a plugin

A plugin is simply a function that takes `hook` as an argument. The hook supports handling of asynchronous tasks.
A docsify plugin is a function with the ability to execute custom JavaScript code at various stages of Docsify's lifecycle.

## Full configuration
## Setup

Docsify plugins can be added directly to the `plugins` array:

```js
window.$docsify = {
plugins: [
function(hook, vm) {
hook.init(function() {
// Called when the script starts running, only trigger once, no arguments,
});
function myPlugin1(hook, vm) {
// ...
},
function myPlugin2(hook, vm) {
// ...
},
],
};
```

hook.beforeEach(function(content) {
// Invoked each time before parsing the Markdown file.
// ...
return content;
});
Alternatively, a plugin can be stored in a separate file and "installed" using a standard `<script>` tag:

hook.afterEach(function(html, next) {
// Invoked each time after the Markdown file is parsed.
// beforeEach and afterEach support asynchronous。
// ...
// call `next(html)` when task is done.
next(html);
});
```js
// docsify-plugin-myplugin.js

hook.doneEach(function() {
// Invoked each time after the data is fully loaded, no arguments,
// ...
});
(function () {
var myPlugin = function (hook, vm) {
// ...
};

hook.mounted(function() {
// Called after initial completion. Only trigger once, no arguments.
});
// Add plugin to docsify's plugin array
$docsify = $docsify || {};
$docsify.plugins = [].concat($docsify.plugins || [], myPlugin);
})();
```

hook.ready(function() {
// Called after initial completion, no arguments.
});
}
]
};
```html
<script src="docsify-plugin-myplugin.js"></script>
```

## Template

Below is a plugin template with placeholders for all available lifecycle hooks.

1. Copy the template
1. Modify the `myPlugin` name as appropriate
1. Add your plugin logic
1. Remove unused lifecycle hooks
1. Save the file as `docsify-plugin-[name].js`
1. Load your plugin using a standard `<script>` tag

```js
(function () {
var myPlugin = function (hook, vm) {
// Invoked one time when docsify script is initialized
hook.init(function () {
// ...
});

// Invoked one time when the docsify instance has mounted on the DOM
hook.mounted(function () {
// ...
});

// Invoked on each page load before new markdown is transformed to HTML.
// Supports asynchronous tasks (see beforeEach documentation for details).
hook.beforeEach(function (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) {
// ...
return html;
});

// Invoked on each page load after new HTML has been appended to the DOM
hook.doneEach(function () {
// ...
});

// Invoked one time after rendering the initial page
hook.ready(function () {
// ...
});
};

// Add plugin to docsify's plugin array
$docsify = $docsify || {};
$docsify.plugins = [].concat(myPlugin, $docsify.plugins || []);
})();
```

## Lifecycle Hooks

Lifecycle hooks are provided via the `hook` argument passed to the plugin function.

### init()

Invoked one time when docsify script is initialized.

```js
hook.init(function () {
// ...
});
```

### mounted()

Invoked one time when the docsify instance has mounted on the DOM.

```js
hook.mounted(function () {
// ...
});
```

### beforeEach()

Invoked on each page load before new markdown is transformed to HTML.

```js
hook.beforeEach(function (markdown) {
// ...
return 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) {
try {
// Async task(s)...
} catch (err) {
// ...
} finally {
next(markdown);
}
});
```

### afterEach()

Invoked on each page load after new markdown has been transformed to HTML.

```js
hook.afterEach(function (html) {
// ...
return 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) {
try {
// Async task(s)...
} catch (err) {
// ...
} finally {
next(html);
}
});
```

### doneEach()

Invoked on each page load after new HTML has been appended to the DOM.

```js
hook.doneEach(function () {
// ...
});
```

!> You can get internal methods through `window.Docsify`. Get the current instance through the second argument.
### ready()

## Example
Invoked one time after rendering the initial page.

#### footer
```js
hook.ready(function () {
// ...
});
```

Add a footer component to each page.
## Tips

- Access Docsify methods and properties using `window.Docsify`
- Access the current Docsify instance using the `vm` argument
- Developers who prefer using a debugger can set the [`catchPluginErrors`](configuration#catchpluginerrors) configuration option to `false` to allow their debugger to pause JavaScript execution on error
- Be sure to test your plugin on all supported platforms and with related configuration options (if applicable) before publishing

## Examples

#### Page Footer

```js
window.$docsify = {
plugins: [
function(hook) {
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>'
'</footer>',
].join('');

hook.afterEach(function(html) {
hook.afterEach(function (html) {
return html + footer;
});
}
]
},
],
};
```

### Edit Button
### Edit Button (GitHub)

```js
window.$docsify = {
// The date template pattern
formatUpdated: '{YYYY}/{MM}/{DD} {HH}:{mm}',
plugins: [
function(hook, vm) {
hook.beforeEach(function(html) {
function editButton(hook, vm) {
// The date template pattern
$docsify.formatUpdated = '{YYYY}/{MM}/{DD} {HH}:{mm}';

hook.beforeEach(function (html) {
var url =
'https://github.com/docsifyjs/docsify/blob/master/docs/' +
vm.route.file;
@@ -93,22 +242,7 @@ window.$docsify = {
editHtml
);
});
}
]
},
],
};
```

## Tips

### Get docsify version

```
console.log(window.Docsify.version)
```

Current version: <span id='tip-version'>loading</span>

<script>
document.getElementById('tip-version').innerText = Docsify.version
document.getElementsByClassName("lang-js")[2].innerHTML = document.getElementsByClassName("lang-js")[2].innerHTML.replace(/Last modified .*'/,"Last modified {docsify-updated<span>}'</span>")
</script>
12 changes: 6 additions & 6 deletions index.html
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@

<body>
<div id="app"></div>
<script src="//cdn.jsdelivr.net/npm/docsify-plugin-carbon@1/index.min.js"></script>
<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)\//);
@@ -82,12 +82,12 @@
'/zh-cn/': '搜索',
'/': 'Search',
},
pathNamespaces: ['/es', '/de-de', '/ru-ru', '/zh-cn']
pathNamespaces: ['/es', '/de-de', '/ru-ru', '/zh-cn'],
},
plugins: [
DocsifyCarbon.create('CEBI6KQE', 'docsifyjsorg'),
function(hook, vm) {
hook.beforeEach(function(html) {
function (hook, vm) {
hook.beforeEach(function (html) {
if (/githubusercontent\.com/.test(vm.route.file)) {
url = vm.route.file
.replace('raw.githubusercontent.com', 'github.com')
@@ -106,7 +106,8 @@
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>'
'<a href="https://docsify.js.org" target="_blank" style="color: inherit; font-weight: normal; text-decoration: none;">Powered by docsify</a>\n\n' +
'<a href="https://vercel.com/?utm_source=docsifyjs&utm_campaign=oss" target="_blank" title="Vercel has given us a Pro account"><img src="/docs/_media/powered-by-vercel.svg" alt="Vercel" width="150"></a>'
);
});
},
@@ -115,7 +116,6 @@
</script>
<script src="/lib/docsify.js"></script>
<script src="/lib/plugins/search.js"></script>
<script src="/lib/plugins/emoji.js"></script>
<script src="/lib/plugins/front-matter.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-bash.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-markdown.min.js"></script>
46 changes: 7 additions & 39 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -2,60 +2,28 @@ const { TEST_HOST } = require('./test/config/server.js');

const sharedConfig = {
errorOnDeprecated: true,
globals: {
TEST_HOST,
},
globalSetup: './test/config/jest.setup.js',
globalTeardown: './test/config/jest.teardown.js',
resetModules: true,
restoreMocks: true,
setupFilesAfterEnv: ['<rootDir>/test/config/jest.setup-tests.js'],
testEnvironment: 'jsdom',
testURL: `${TEST_HOST}/_blank.html`,
};

module.exports = {
// Adding globals to config root for easier importing into .eslint.js, but
// as of Jest 26.4.2 these globals need to be added to each project config
// as well.
globals: sharedConfig.globals,
projects: [
// Unit Tests (Jest)
// Unit Tests
{
...sharedConfig,
displayName: 'unit',
setupFilesAfterEnv: ['<rootDir>/test/config/jest.setup-tests.js'],
...sharedConfig,
testMatch: ['<rootDir>/test/unit/*.test.js'],
testURL: `${TEST_HOST}/_blank.html`,
},
// Integration Tests (Jest)
// Integration Tests
{
...sharedConfig,
displayName: 'integration',
setupFilesAfterEnv: ['<rootDir>/test/config/jest.setup-tests.js'],
testMatch: ['<rootDir>/test/integration/*.test.js'],
testURL: `${TEST_HOST}/_blank.html`,
},
// E2E Tests (Jest + Playwright)
{
...sharedConfig,
displayName: 'e2e',
preset: 'jest-playwright-preset',
setupFilesAfterEnv: [
'<rootDir>/test/config/jest-playwright.setup-tests.js',
],
testEnvironmentOptions: {
'jest-playwright': {
// prettier-ignore
browsers: [
'chromium',
'firefox',
'webkit',
],
launchOptions: {
// headless: false,
// devtools: true,
},
},
},
testMatch: ['<rootDir>/test/e2e/*.test.js'],
testMatch: ['<rootDir>/test/integration/*.test.js'],
},
],
};
43,729 changes: 32,590 additions & 11,139 deletions package-lock.json

Large diffs are not rendered by default.

75 changes: 37 additions & 38 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "docsify",
"version": "4.12.2",
"version": "4.13.0",
"description": "A magical documentation generator.",
"author": {
"name": "qingwei-li",
@@ -21,39 +21,40 @@
],
"scripts": {
"bootstrap": "npm i && lerna bootstrap && npm run build:ssr",
"serve": "node server",
"serve:ssr": "cross-env SSR=1 node server",
"dev": "run-p serve watch:*",
"dev:ssr": "run-p serve:ssr watch:*",
"lint": "eslint .",
"fixlint": "eslint . --fix",
"test": "jest",
"build:test": "npm run build && npm test",
"test:e2e": "jest --selectProjects e2e",
"test:integration": "jest --selectProjects integration",
"test:unit": "jest --selectProjects unit",
"css": "node build/css",
"watch:css": "npm run css -- -o themes -w",
"watch:js": "node build/build.js",
"build:cover": "node build/cover.js",
"build:css:min": "mkdirp lib/themes && npm run css -- -o lib/themes && node build/mincss.js",
"build:css": "mkdirp themes && npm run css -- -o themes",
"build:emoji": "node ./build/emoji.js",
"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",
"prepare": "npm run build",
"pub:next": "cross-env RELEASE_TAG=next sh build/release.sh",
"pub": "sh build/release.sh",
"postinstall": "opencollective-postinstall",
"build:test": "npm run build && npm test",
"build": "rimraf lib themes && run-s build:js build:css build:css:min build:ssr build:cover build:emoji",
"css": "node build/css",
"dev:ssr": "run-p serve:ssr watch:*",
"dev": "run-p serve watch:*",
"docker:build:test": "npm run docker:cli -- build:test",
"docker:build": "docker build -f Dockerfile -t docsify-test:local .",
"docker:clean": "docker rmi docsify-test:local",
"docker:cli": "docker run --rm -it --ipc=host --mount type=bind,source=$(pwd)/test,target=/app/test docsify-test:local",
"docker:rebuild": "npm run docker:clean && npm run docker:build",
"docker:test": "npm run docker:cli -- test",
"docker:build:test": "npm run docker:cli -- build:test",
"docker:test:e2e": "npm run docker:cli -- test:e2e",
"docker:test:integration": "npm run docker:cli -- test:integration",
"docker:test:unit": "npm run docker:cli -- test:unit",
"docker:cli": "docker run --rm -it --ipc=host --mount type=bind,source=$(pwd)/test,target=/app/test docsify-test:local"
"docker:test": "npm run docker:cli -- test",
"lint:fix": "eslint . --fix",
"lint": "eslint .",
"postinstall": "opencollective-postinstall",
"prepare": "npm run build",
"pub:next": "cross-env RELEASE_TAG=next sh build/release.sh",
"pub": "sh build/release.sh",
"serve:ssr": "cross-env SSR=1 node server",
"serve": "node server",
"test:e2e": "playwright test",
"test:integration": "jest --selectProjects integration",
"test:unit": "jest --selectProjects unit",
"test": "jest && run-s test:e2e",
"watch:css": "npm run css -- -o themes -w",
"watch:js": "node build/build.js"
},
"husky": {
"hooks": {
@@ -64,45 +65,43 @@
"*.js": "eslint --fix"
},
"dependencies": {
"dompurify": "^2.3.1",
"marked": "^1.2.9",
"medium-zoom": "^1.0.6",
"opencollective-postinstall": "^2.0.2",
"prismjs": "^1.23.0",
"prismjs": "^1.27.0",
"strip-indent": "^3.0.0",
"tinydate": "^1.3.0",
"tweezer.js": "^1.4.0"
},
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/eslint-parser": "^7.16.5",
"@babel/preset-env": "^7.11.5",
"@playwright/test": "^1.18.1",
"autoprefixer-stylus": "^1.0.0",
"axios": "^0.21.1",
"babel-eslint": "^10.0.3",
"babel-jest": "^26.3.0",
"babel-jest": "^27.4.6",
"browser-sync": "^2.26.12",
"chokidar": "^3.4.2",
"common-tags": "^1.8.0",
"conventional-changelog-cli": "^2.1.0",
"copy-dir": "^1.2.0",
"cross-env": "^6.0.3",
"cssnano": "^4.1.10",
"eslint": "^5.16.0",
"eslint": "^8.7.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-jest": "^24.0.2",
"eslint-plugin-jest-playwright": "^0.2.1",
"eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-jest": "^26.0.0",
"eslint-plugin-playwright": "^0.8.0",
"eslint-plugin-prettier": "^4.0.0",
"husky": "^3.1.0",
"jest": "^26.4.2",
"jest-image-snapshot": "^4.2.0",
"jest-playwright-preset": "^1.4.1",
"lerna": "^3.22.1",
"jest": "^27.4.7",
"lerna": "^5.5.1",
"lint-staged": "^10.4.0",
"live-server": "^1.2.1",
"mkdirp": "^0.5.1",
"npm-run-all": "^4.1.5",
"playwright": "^1.8.0",
"prettier": "^1.19.1",
"prettier": "^2.5.1",
"rimraf": "^3.0.0",
"rollup": "^1.23.1",
"rollup-plugin-async": "^1.2.0",
7 changes: 3 additions & 4 deletions packages/docsify-server-renderer/index.js
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@ import { resolve, basename } from 'path';
import resolvePathname from 'resolve-pathname';
import fetch from 'node-fetch';
import debug from 'debug';
import DOMPurify from 'dompurify';
import { AbstractHistory } from '../../src/core/router/history/abstract';
import { Compiler } from '../../src/core/render/compiler';
import { isAbsolutePath } from '../../src/core/router/util';
@@ -123,10 +122,10 @@ export default class Renderer {
this._renderHtml('cover', await this._render(coverFile), 'cover');
}

const html = this.isRemoteUrl
? DOMPurify.sanitize(this.html, { ADD_TAGS: ['script'] })
: this.html;
const html = this.html;

this.html = this.template;

return html;
}

390 changes: 276 additions & 114 deletions packages/docsify-server-renderer/package-lock.json
5 changes: 2 additions & 3 deletions packages/docsify-server-renderer/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "docsify-server-renderer",
"version": "4.12.2",
"version": "4.13.0",
"description": "docsify server renderer",
"author": {
"name": "qingwei-li",
@@ -16,8 +16,7 @@
},
"dependencies": {
"debug": "^4.3.3",
"docsify": "^4.12.1",
"dompurify": "^2.3.2",
"docsify": "^4.12.4",
"node-fetch": "^2.6.6",
"resolve-pathname": "^3.0.0"
}
64 changes: 64 additions & 0 deletions playwright.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const { devices } = require('@playwright/test');

/**
* @see https://playwright.dev/docs/test-configuration
* @type {import('@playwright/test').PlaywrightTestConfig}
*/
const config = {
// Setup / Teardown
globalSetup: './test/config/playwright.setup.js',
globalTeardown: './test/config/playwright.teardown.js',

// Test Execution
expect: {
timeout: 5000,
},
retries: process.env.CI ? 2 : 0, // Retry on CI only
testDir: './test/e2e',
timeout: 30 * 1000,
workers: process.env.CI ? 1 : undefined, // No parallel tests on CI
forbidOnly: !!process.env.CI, // Fail on CI if test.only in source

// Output
outputDir: './_playwright-results/', // screenshots, videos, traces, etc.
reporter: [
[
'html',
{
open: 'never',
outputFolder: '_playwright-report',
},
],
],
snapshotDir: './test/e2e/__snapshots__',

// Config - Shared
// See https://playwright.dev/docs/api/class-testoptions
use: {
actionTimeout: 0,
baseURL: `${process.env.TEST_HOST}`, // Allow relative page.goto() (e.g. `await page.goto('/')`).
trace: 'on-first-retry',
},

// Projects
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] }
// },
],
};

module.exports = config;
4 changes: 4 additions & 0 deletions sandbox.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"template": "node",
"node": "16"
}
21 changes: 17 additions & 4 deletions src/core/Docsify.js
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import { Router } from './router/index.js';
import { Render } from './render/index.js';
import { Fetch } from './fetch/index.js';
import { Events } from './event/index.js';
import { VirtualRoutes } from './virtual-routes/index.js';
import initGlobalAPI from './global-api.js';

import config from './config.js';
@@ -11,7 +12,10 @@ import { Lifecycle } from './init/lifecycle';
/** @typedef {new (...args: any[]) => any} Constructor */

// eslint-disable-next-line new-cap
export class Docsify extends Fetch(Events(Render(Router(Lifecycle(Object))))) {
export class Docsify extends Fetch(
// eslint-disable-next-line new-cap
Events(Render(VirtualRoutes(Router(Lifecycle(Object)))))
) {
constructor() {
super();

@@ -28,9 +32,18 @@ export class Docsify extends Fetch(Events(Render(Router(Lifecycle(Object))))) {
}

initPlugin() {
[]
.concat(this.config.plugins)
.forEach(fn => isFn(fn) && fn(this._lifecycle, this));
[].concat(this.config.plugins).forEach(fn => {
try {
isFn(fn) && fn(this._lifecycle, this);
} catch (err) {
if (this.config.catchPluginErrors) {
const errTitle = 'Docsify plugin error';
console.error(errTitle, err);
} else {
throw err;
}
}
});
}
}

48 changes: 25 additions & 23 deletions src/core/config.js
Original file line number Diff line number Diff line change
@@ -3,38 +3,40 @@ import { merge, hyphenate, isPrimitive, hasOwn } from './util/core';
const currentScript = document.currentScript;

/** @param {import('./Docsify').Docsify} vm */
export default function(vm) {
export default function (vm) {
const config = merge(
{
el: '#app',
repo: '',
maxLevel: 6,
subMaxLevel: 0,
loadSidebar: null,
loadNavbar: null,
homepage: 'README.md',
coverpage: '',
basePath: '',
auto2top: false,
name: '',
themeColor: '',
nameLink: window.location.pathname,
autoHeader: false,
basePath: '',
catchPluginErrors: true,
cornerExternalLinkTarget: '_blank',
coverpage: '',
crossOriginLinks: [],
el: '#app',
executeScript: null,
noEmoji: false,
ga: '',
ext: '.md',
mergeNavbar: false,
formatUpdated: '',
// This config for the links inside markdown
externalLinkTarget: '_blank',
// This config for the corner
cornerExternalLinkTarget: '_blank',
externalLinkRel: 'noopener',
routerMode: 'hash',
externalLinkTarget: '_blank',
formatUpdated: '',
ga: '',
homepage: 'README.md',
loadNavbar: null,
loadSidebar: null,
maxLevel: 6,
mergeNavbar: false,
name: '',
nameLink: window.location.pathname,
nativeEmoji: false,
noCompileLinks: [],
crossOriginLinks: [],
noEmoji: false,
notFoundPage: true,
relativePath: false,
repo: '',
routes: {},
routerMode: 'hash',
subMaxLevel: 0,
themeColor: '',
topMargin: 0,
},
typeof window.$docsify === 'function'
4 changes: 2 additions & 2 deletions src/core/fetch/ajax.js
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ const cache = {};
*/
export function get(url, hasBar = false, headers = {}) {
const xhr = new XMLHttpRequest();
const on = function() {
const on = function () {
xhr.addEventListener.apply(xhr, arguments);
};

@@ -33,7 +33,7 @@ export function get(url, hasBar = false, headers = {}) {
xhr.send();

return {
then: function(success, error = noop) {
then: function (success, error = noop) {
if (hasBar) {
const id = setInterval(
_ =>
55 changes: 36 additions & 19 deletions src/core/fetch/index.js
Original file line number Diff line number Diff line change
@@ -96,25 +96,44 @@ export function Fetch(Base) {
// Abort last request

const file = this.router.getFile(path);
const req = request(file + qs, true, requestHeaders);

this.isRemoteUrl = isExternal(file);
// Current page is html
this.isHTML = /\.html$/g.test(file);

// Load main content
req.then(
(text, opt) =>
this._renderMain(
text,
opt,
this._loadSideAndNav(path, qs, loadSidebar, cb)
),
_ => {
this._fetchFallbackPage(path, qs, cb) ||
this._fetch404(file, qs, cb);
}
);
// create a handler that should be called if content was fetched successfully
const contentFetched = (text, opt) => {
this._renderMain(
text,
opt,
this._loadSideAndNav(path, qs, loadSidebar, cb)
);
};

// and a handler that is called if content failed to fetch
const contentFailedToFetch = _ => {
this._fetchFallbackPage(path, qs, cb) || this._fetch404(file, qs, cb);
};

// attempt to fetch content from a virtual route, and fallback to fetching the actual file
if (!this.isRemoteUrl) {
this.matchVirtualRoute(path).then(contents => {
if (typeof contents === 'string') {
contentFetched(contents);
} else {
request(file + qs, true, requestHeaders).then(
contentFetched,
contentFailedToFetch
);
}
});
} else {
// if the requested url is not local, just fetch the file
request(file + qs, true, requestHeaders).then(
contentFetched,
contentFailedToFetch
);
}

// Load nav
loadNavbar &&
@@ -152,11 +171,9 @@ export function Fetch(Base) {
if (path) {
path = this.router.getFile(root + path);
this.coverIsHTML = /\.html$/g.test(path);
get(
path + stringifyQuery(query, ['id']),
false,
requestHeaders
).then(text => this._renderCover(text, coverOnly));
get(path + stringifyQuery(query, ['id']), false, requestHeaders).then(
text => this._renderCover(text, coverOnly)
);
} else {
this._renderCover(null, coverOnly);
}
2 changes: 1 addition & 1 deletion src/core/global-api.js
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ import { get } from './fetch/ajax';
// major release. We'll tell people to get everything from the DOCSIFY global
// when using the global build, but we'll highly recommend for them to import
// from the ESM build (f.e. lib/docsify.esm.js and lib/docsify.min.esm.js).
export default function() {
export default function () {
window.Docsify = {
util,
dom,
38 changes: 31 additions & 7 deletions src/core/init/lifecycle.js
Original file line number Diff line number Diff line change
@@ -29,22 +29,46 @@ export function Lifecycle(Base) {

callHook(hookName, data, next = noop) {
const queue = this._hooks[hookName];
const catchPluginErrors = this.config.catchPluginErrors;

const step = function(index) {
const step = function (index) {
const hookFn = queue[index];

if (index >= queue.length) {
next(data);
} else if (typeof hookFn === 'function') {
const errTitle = 'Docsify plugin error';

if (hookFn.length === 2) {
hookFn(data, result => {
data = result;
try {
hookFn(data, result => {
data = result;
step(index + 1);
});
} catch (err) {
if (catchPluginErrors) {
console.error(errTitle, err);
} else {
throw err;
}

step(index + 1);
});
}
} else {
const result = hookFn(data);
data = result === undefined ? data : result;
step(index + 1);
try {
const result = hookFn(data);

data = result === undefined ? data : result;
step(index + 1);
} catch (err) {
if (catchPluginErrors) {
console.error(errTitle, err);
} else {
throw err;
}

step(index + 1);
}
}
} else {
step(index + 1);
9 changes: 5 additions & 4 deletions src/core/render/compiler.js
Original file line number Diff line number Diff line change
@@ -28,8 +28,9 @@ const compileMedia = {
},
iframe(url, title) {
return {
html: `<iframe src="${url}" ${title ||
'width=100% height=400'}></iframe>`,
html: `<iframe src="${url}" ${
title || 'width=100% height=400'
}></iframe>`,
};
},
video(url, title) {
@@ -103,7 +104,7 @@ export class Compiler {
html = compile.parser(text);
}

html = config.noEmoji ? html : emojify(html);
html = config.noEmoji ? html : emojify(html, config.nativeEmoji);
slugify.clear();

return html;
@@ -204,7 +205,7 @@ export class Compiler {
* @param {Number} level Type of heading (h<level> tag)
* @returns {String} Heading element
*/
origin.heading = renderer.heading = function(text, level) {
origin.heading = renderer.heading = function (text, level) {
let { str, config } = getAndRemoveConfig(text);
const nextToc = { level, title: removeAtag(str) };

2 changes: 1 addition & 1 deletion src/core/render/compiler/code.js
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import Prism from 'prismjs';
import 'prismjs/components/prism-markup-templating';

export const highlightCodeCompiler = ({ renderer }) =>
(renderer.code = function(code, lang = 'markup') {
(renderer.code = function (code, lang = 'markup') {
const langOrMarkup = Prism.languages[lang] || Prism.languages.markup;
const text = Prism.highlight(
code.replace(/@DOCSIFY_QM@/g, '`'),
2 changes: 1 addition & 1 deletion src/core/render/embed.js
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {

while ((token = embedTokens[step++])) {
// eslint-disable-next-line no-shadow
const next = (function(token) {
const next = (function (token) {
return text => {
let embedToken;
if (text) {
1,885 changes: 1,885 additions & 0 deletions src/core/render/emoji-data.js

Large diffs are not rendered by default.

63 changes: 45 additions & 18 deletions src/core/render/emojify.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,49 @@
import { inBrowser } from '../util/env';
import emojiData from './emoji-data.js';

function replace(m, $1) {
return (
'<img class="emoji" src="https://github.githubassets.com/images/icons/emoji/' +
$1 +
'.png" alt="' +
$1 +
'" />'
);
function replaceEmojiShorthand(m, $1, useNativeEmoji) {
const emojiMatch = emojiData.data[$1];

let result = m;

if (emojiMatch) {
if (useNativeEmoji && /unicode/.test(emojiMatch)) {
const emojiUnicode = emojiMatch
.replace('unicode/', '')
.replace(/\.png.*/, '')
.split('-')
.map(u => `&#x${u};`)
// Separate multi-character emoji with zero width joiner sequence (ZWJ)
// 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>`;
} else {
result = `<img src="${emojiData.baseURL}${emojiMatch}.png" alt="${$1}" class="emoji" loading="lazy">`;
}
}

return result;
}

export function emojify(text) {
return text
.replace(/:\+1:/g, ':thumbsup:')
.replace(/:-1:/g, ':thumbsdown:')
.replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g, m =>
m.replace(/:/g, '__colon__')
)
.replace(/:(\w+?):/gi, (inBrowser && window.emojify) || replace)
.replace(/__colon__/g, ':');
export function emojify(text, useNativeEmoji) {
return (
text
// Mark colons in tags
.replace(
/<(code|pre|script|template)[^>]*?>[\s\S]+?<\/(code|pre|script|template)>/g,
m => m.replace(/:/g, '__colon__')
)
// Mark colons in comments
.replace(/<!--[\s\S]+?-->/g, m => m.replace(/:/g, '__colon__'))
// Mark colons in URIs
.replace(/([a-z]{2,}:)?\/\/[^\s'">)]+/gi, m =>
m.replace(/:/g, '__colon__')
)
// Replace emoji shorthand codes
.replace(/:([a-z0-9_\-+]+?):/g, (m, $1) =>
replaceEmojiShorthand(m, $1, useNativeEmoji)
)
// Restore colons in tags and comments
.replace(/__colon__/g, ':')
);
}
21 changes: 9 additions & 12 deletions src/core/render/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* eslint-disable no-unused-vars */
import tinydate from 'tinydate';
import DOMPurify from 'dompurify';
import * as dom from '../util/dom';
import cssVars from '../util/polyfill/css-vars';
import { getAndActive, sticky } from '../event/sidebar';
@@ -174,7 +173,7 @@ function renderMain(html) {
// This provides a global store for all Vue instances that receive
// vueGlobalOptions as their configuration.
if (vueGlobalData) {
vueConfig.data = function() {
vueConfig.data = function () {
return vueGlobalData;
};
}
@@ -261,7 +260,9 @@ export function Render(Base) {
[
document.querySelector('aside.sidebar'),
document.querySelector('button.sidebar-toggle'),
].forEach(node => node.parentNode.removeChild(node));
]
.filter(e => !!e)
.forEach(node => node.parentNode.removeChild(node));
document.querySelector('section.content').style.right = 'unset';
document.querySelector('section.content').style.left = 'unset';
document.querySelector('section.content').style.position = 'relative';
@@ -322,15 +323,15 @@ export function Render(Base) {
);
}

this.callHook('afterEach', html, hookData =>
renderMain.call(this, hookData)
);
this.callHook('afterEach', html, hookData => {
renderMain.call(this, hookData);
next();
});
};

if (this.isHTML) {
html = this.result = text;
callback();
next();
} else {
prerenderEmbed(
{
@@ -339,11 +340,7 @@ export function Render(Base) {
},
tokens => {
html = this.compiler.compile(tokens);
html = this.isRemoteUrl
? DOMPurify.sanitize(html, { ADD_TAGS: ['script'] })
: html;
callback();
next();
}
);
}
@@ -418,7 +415,7 @@ export function Render(Base) {

if (el) {
if (config.repo) {
html += tpl.corner(config.repo, config.cornerExternalLinkTarge);
html += tpl.corner(config.repo, config.cornerExternalLinkTarget);
}

if (config.coverpage) {
2 changes: 1 addition & 1 deletion src/core/render/progressbar.js
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ function init() {
/**
* Render progress bar
*/
export default function({ loaded, total, step }) {
export default function ({ loaded, total, step }) {
let num;

!barEl && init();
2 changes: 1 addition & 1 deletion src/core/render/slugify.js
Original file line number Diff line number Diff line change
@@ -32,6 +32,6 @@ export function slugify(str) {
return slug;
}

slugify.clear = function() {
slugify.clear = function () {
cache = {};
};
8 changes: 4 additions & 4 deletions src/core/render/tpl.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/**
* Render github corner
* @param {Object} data URL for the View Source on Github link
* @param {String} cornerExternalLinkTarge value of the target attribute of the link
* @param {String} cornerExternalLinkTarget value of the target attribute of the link
* @return {String} SVG element as string
*/
export function corner(data, cornerExternalLinkTarge) {
export function corner(data, cornerExternalLinkTarget) {
if (!data) {
return '';
}
@@ -15,10 +15,10 @@ export function corner(data, cornerExternalLinkTarge) {

data = data.replace(/^git\+/, '');
// Double check
cornerExternalLinkTarge = cornerExternalLinkTarge || '_blank';
cornerExternalLinkTarget = cornerExternalLinkTarget || '_blank';

return (
`<a href="${data}" target="${cornerExternalLinkTarge}" class="github-corner" aria-label="View source on Github">` +
`<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>' +
3 changes: 2 additions & 1 deletion src/core/router/history/hash.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { noop } from '../../util/core';
import { on } from '../../util/dom';
import { parseQuery, cleanPath, replaceSlug, endsWith } from '../util';
import { endsWith } from '../../util/str';
import { parseQuery, cleanPath, replaceSlug } from '../util';
import { History } from './base';

function replaceHash(path) {
6 changes: 1 addition & 5 deletions src/core/router/util.js
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ export function parseQuery(query) {
}

// Simple parse
query.split('&').forEach(function(param) {
query.split('&').forEach(function (param) {
const parts = param.replace(/\+/g, ' ').split('=');

res[parts[0]] = parts[1] && decode(parts[1]);
@@ -113,7 +113,3 @@ export function getPath(...args) {
export const replaceSlug = cached(path => {
return path.replace('#', '?id=');
});

export function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
4 changes: 2 additions & 2 deletions src/core/util/core.js
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

export function cached(fn) {
const cache = Object.create(null);
return function(str) {
return function (str) {
const key = isPrimitive(str) ? str : JSON.stringify(str);
const hit = cache[key];
return hit || (cache[key] = fn(str));
@@ -29,7 +29,7 @@ export const hasOwn = Object.prototype.hasOwnProperty;
*/
export const merge =
Object.assign ||
function(to) {
function (to) {
for (let i = 1; i < arguments.length; i++) {
const from = Object(arguments[i]);

2 changes: 1 addition & 1 deletion src/core/util/env.js
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ export const isMobile = inBrowser && document.body.clientWidth <= 600;
*/
export const supportsPushState =
inBrowser &&
(function() {
(function () {
// Borrowed wholesale from https://github.com/defunkt/jquery-pjax
return (
window.history &&
2 changes: 1 addition & 1 deletion src/core/util/polyfill/css-vars.js
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ function replaceVar(block, color) {
);
}

export default function(color) {
export default function (color) {
// Variable support
if (window.CSS && window.CSS.supports && window.CSS.supports('(--v:red)')) {
return;
16 changes: 16 additions & 0 deletions src/core/util/str.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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} -->/, '')
.replace(/{docsify-ignore}/, '')
.replace(/<!-- {docsify-ignore-all} -->/, '')
.replace(/{docsify-ignore-all}/, '')
.trim();
}
21 changes: 21 additions & 0 deletions src/core/virtual-routes/exact-match.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { startsWith, endsWith } from '../util/str';

/**
* 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, '^')
? matcher
: `^${matcher}`;

const matcherWithBeginningAndEndOfInput = endsWith(
matcherWithBeginningOfInput,
'$'
)
? matcherWithBeginningOfInput
: `${matcherWithBeginningOfInput}$`;

return matcherWithBeginningAndEndOfInput;
}
93 changes: 93 additions & 0 deletions src/core/virtual-routes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { makeExactMatcher } from './exact-match';
import { createNextFunction } from './next';

/** @typedef {import('../Docsify').Constructor} Constructor */

/** @typedef {Record<string, string | VirtualRouteHandler>} VirtualRoutesMap */
/** @typedef {(route: string, match: RegExpMatchArray | null) => string | void | Promise<string | void> } VirtualRouteHandler */

/**
* @template {!Constructor} T
* @param {T} Base - The class to extend
*/
export function VirtualRoutes(Base) {
return class VirtualRoutes extends Base {
/**
* Gets the Routes object from the configuration
* @returns {VirtualRoutesMap}
*/
routes() {
return this.config.routes || {};
}

/**
* Attempts to match the given path with a virtual route.
* @param {string} path the path of the route to match
* @returns {Promise<string | null>} resolves to string if route was matched, otherwise null
*/
matchVirtualRoute(path) {
const virtualRoutes = this.routes();
const virtualRoutePaths = Object.keys(virtualRoutes);

let done = () => null;

/**
* This is a tail recursion that iterates over all the available routes.
* It can result in one of two ways:
* 1. Call itself (essentially reviewing the next route)
* 2. Call the "done" callback with the result (either the contents, or "null" if no match was found)
*/
function asyncMatchNextRoute() {
const virtualRoutePath = virtualRoutePaths.shift();
if (!virtualRoutePath) {
return done(null);
}

const matcher = makeExactMatcher(virtualRoutePath);
const matched = path.match(matcher);

if (!matched) {
return asyncMatchNextRoute();
}

const virtualRouteContentOrFn = virtualRoutes[virtualRoutePath];

if (typeof virtualRouteContentOrFn === 'string') {
const contents = virtualRouteContentOrFn;
return done(contents);
}

if (typeof virtualRouteContentOrFn === 'function') {
const fn = virtualRouteContentOrFn;

const [next, onNext] = createNextFunction();
onNext(contents => {
if (typeof contents === 'string') {
return done(contents);
} else if (contents === false) {
return done(null);
} else {
return asyncMatchNextRoute();
}
});

if (fn.length <= 2) {
const returnedValue = fn(path, matched);
return next(returnedValue);
} else {
return fn(path, matched, next);
}
}

return asyncMatchNextRoute();
}

return {
then: function (cb) {
done = cb;
asyncMatchNextRoute();
},
};
}
};
}
21 changes: 21 additions & 0 deletions src/core/virtual-routes/next.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/** @typedef {((value: any) => void) => void} OnNext */
/** @typedef {(value: any) => void} NextFunction */

/**
* Creates a pair of a function and an event emitter.
* When the function is called, the event emitter calls the given callback with the value that was passed to the function.
* @returns {[NextFunction, OnNext]}
*/
export function createNextFunction() {
let storedCb = () => null;

function next(value) {
storedCb(value);
}

function onNext(cb) {
storedCb = cb;
}

return [next, onNext];
}
2 changes: 1 addition & 1 deletion src/plugins/disqus.js
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ function install(hook, vm) {
if (typeof window.DISQUS !== 'undefined') {
window.DISQUS.reset({
reload: true,
config: function() {
config: function () {
this.page.url = location.origin + '/-' + vm.route.path;
this.page.identifier = vm.route.path;
this.page.title = document.title;
1,820 changes: 9 additions & 1,811 deletions src/plugins/emoji.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/plugins/external-script.js
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ function handleExternalScript() {
}
}

const install = function(hook) {
const install = function (hook) {
hook.doneEach(handleExternalScript);
};

4 changes: 2 additions & 2 deletions src/plugins/front-matter/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import parser from './parser';

const install = function(hook, vm) {
const install = function (hook, vm) {
// Used to remove front matter from embedded pages if installed.
vm.config.frontMatter = {};
vm.config.frontMatter.installed = true;
vm.config.frontMatter.parseMarkdown = function(content) {
vm.config.frontMatter.parseMarkdown = function (content) {
const { body } = parser(content);
return body;
};
4 changes: 2 additions & 2 deletions src/plugins/ga.js
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ function init(id) {
appendScript();
window.ga =
window.ga ||
function() {
function () {
(window.ga.q = window.ga.q || []).push(arguments);
};

@@ -28,7 +28,7 @@ function collect() {
window.ga('send', 'pageview');
}

const install = function(hook) {
const install = function (hook) {
if (!$docsify.ga) {
console.error('[Docsify] ga is required.');
return;
4 changes: 2 additions & 2 deletions src/plugins/matomo.js
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ 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', String(options.id)]);
@@ -26,7 +26,7 @@ function collect() {
window._paq.push(['trackPageView']);
}

const install = function(hook) {
const install = function (hook) {
if (!$docsify.matomo) {
// eslint-disable-next-line no-console
console.error('[Docsify] matomo is required.');
12 changes: 9 additions & 3 deletions src/plugins/search/component.js
Original file line number Diff line number Diff line change
@@ -33,9 +33,8 @@ function style() {
outline: none;
border: none;
width: 100%;
padding: 0 7px;
line-height: 36px;
font-size: 14px;
padding: 0.6em 7px;
font-size: inherit;
border: 1px solid transparent;
}
@@ -51,6 +50,13 @@ function style() {
-moz-appearance: none;
appearance: none;
}
.search input::-ms-clear {
display: none;
height: 0;
width: 0;
}
.search .clear-button {
cursor: pointer;
width: 36px;
2 changes: 1 addition & 1 deletion src/plugins/search/index.js
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ const CONFIG = {
pathNamespaces: undefined,
};

const install = function(hook, vm) {
const install = function (hook, vm) {
const { util } = Docsify;
const opts = vm.config.search || CONFIG;

31 changes: 15 additions & 16 deletions src/plugins/search/search.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable no-unused-vars */
import { getAndRemoveConfig } from '../../core/render/utils';
import { removeDocsifyIgnoreTag } from '../../core/util/str';

let INDEXS = {};

@@ -58,7 +59,7 @@ function getTableData(token) {
if (!token.text && token.type === 'table') {
token.cells.unshift(token.header);
token.text = token.cells
.map(function(rows) {
.map(function (rows) {
return rows.join(' | ');
})
.join(' |\n ');
@@ -85,23 +86,20 @@ export function genIndex(path, content = '', router, depth) {
let slug;
let title = '';

tokens.forEach(function(token, tokenIndex) {
tokens.forEach(function (token, tokenIndex) {
if (token.type === 'heading' && token.depth <= depth) {
const { str, config } = getAndRemoveConfig(token.text);

const text = removeDocsifyIgnoreTag(token.text);

if (config.id) {
slug = router.toURL(path, { id: slugify(config.id) });
} else {
slug = router.toURL(path, { id: slugify(escapeHtml(token.text)) });
slug = router.toURL(path, { id: slugify(escapeHtml(text)) });
}

if (str) {
title = str
.replace(/<!-- {docsify-ignore} -->/, '')
.replace(/{docsify-ignore}/, '')
.replace(/<!-- {docsify-ignore-all} -->/, '')
.replace(/{docsify-ignore-all}/, '')
.trim();
title = removeDocsifyIgnoreTag(str);
}

index[slug] = { slug, title: title, body: '' };
@@ -213,14 +211,15 @@ export function search(query) {
}

const matchContent =
handlePostContent &&
'...' +
handlePostContent
.substring(start, end)
.replace(
regEx,
word => `<em class="search-keyword">${word}</em>`
) +
'...';
handlePostContent
.substring(start, end)
.replace(
regEx,
word => `<em class="search-keyword">${word}</em>`
) +
'...';

resultStr += matchContent;
}
14 changes: 10 additions & 4 deletions src/themes/basic/_coverpage.styl
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
section.cover
position relative
align-items center
background-position center center
background-repeat no-repeat
background-size cover
height 100vh
width 100vw
min-height 100vh
width 100%
display none

&.show
@@ -15,12 +16,12 @@ section.cover
opacity 0.8
position absolute
top 0
height 100%
bottom 0
width 100%

.cover-main
flex 1
margin -20px 16px 0
margin 0 16px
text-align center
position: relative

@@ -63,11 +64,13 @@ section.cover
padding 0

.cover-main > p:last-child a
border-color $color-primary
border-color var(--theme-color, $color-primary)
border-radius 2rem
border-style solid
border-width 1px
box-sizing border-box
color $color-primary
color var(--theme-color, $color-primary)
display inline-block
font-size 1.05rem
@@ -78,6 +81,7 @@ section.cover
transition all 0.15s ease

&:last-child
background-color $color-primary
background-color var(--theme-color, $color-primary)
color #fff

@@ -89,8 +93,10 @@ section.cover
color inherit

blockquote > p > a
border-bottom 2px solid $color-primary
border-bottom 2px solid var(--theme-color, $color-primary)
transition color 0.3s

&:hover
color $color-primary
color var(--theme-color, $color-primary)
17 changes: 15 additions & 2 deletions src/themes/basic/_layout.styl
Original file line number Diff line number Diff line change
@@ -21,11 +21,17 @@ div#app
&:empty::before
content 'Loading...'

.emoji
height 1.2rem
img.emoji
height 1.2em
vertical-align middle

span.emoji
font-family "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"
font-size 1.2em
vertical-align middle

.progress
background-color $color-primary
background-color var(--theme-color, $color-primary)
height 2px
left 0px
@@ -37,9 +43,11 @@ div#app
z-index 999999

.search a:hover
color $color-primary
color var(--theme-color, $color-primary)

.search .search-keyword
color $color-primary
color var(--theme-color, $color-primary)
font-style normal
font-weight bold
@@ -108,10 +116,13 @@ li input[type='checkbox']
transition color 0.3s

&:hover
color $color-primary
color var(--theme-color, $color-primary)

&.active
border-bottom 2px solid $color-primary
border-bottom 2px solid var(--theme-color, $color-primary)
color $color-primary
color var(--theme-color, $color-primary)

/* navbar dropdown */
@@ -172,6 +183,7 @@ li input[type='checkbox']

svg
color $color-bg
fill $color-primary
fill var(--theme-color, $color-primary)
height 80px
width 80px
@@ -284,6 +296,7 @@ main.hidden
opacity 0.4

span
background-color $color-primary
background-color var(--theme-color, $color-primary)
display block
margin-bottom 4px
2 changes: 2 additions & 0 deletions src/themes/buble.styl
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ $sidebar-width = 16rem
font-weight 600

.markdown-section a
color $color-primary
color var(--theme-color, $color-primary)

.markdown-section p, .markdown-section ul, .markdown-section ol
@@ -83,6 +84,7 @@ $sidebar-width = 16rem
margin 0

.markdown-section blockquote
border-left 4px solid $color-primary
border-left 4px solid var(--theme-color, $color-primary)
color #858585
margin 2em 0
5 changes: 5 additions & 0 deletions src/themes/dark.styl
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ body
padding 0

ul li.active > a
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600

@@ -43,6 +44,7 @@ body
font-weight 600

.markdown-section a
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600

@@ -79,6 +81,7 @@ body
padding-left 1.5rem

.markdown-section blockquote
border-left 4px solid $color-primary
border-left 4px solid var(--theme-color, $color-primary)
color #858585
margin 2em 0
@@ -138,6 +141,7 @@ body
color #2973b7

.token.string
color $color-primary
color var(--theme-color, $color-primary)

.token.selector
@@ -150,6 +154,7 @@ body
color #22a2c9

.token.attr-value, .token.control, .token.directive, .token.unit
color $color-primary
color var(--theme-color, $color-primary)

.token.keyword
7 changes: 6 additions & 1 deletion src/themes/dolphin.styl
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ body

ul li.active > a
border-right 2px solid
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600

@@ -52,9 +53,10 @@ body
font-weight 600

.markdown-section a
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600

&:hover
text-decoration underline

@@ -91,6 +93,7 @@ body
padding-left 1.5rem

.markdown-section blockquote
border-left 4px solid $color-primary
border-left 4px solid var(--theme-color, $color-primary)
color #858585
margin 2em 0
@@ -150,6 +153,7 @@ body
color #2973b7

.token.string
color $color-primary
color var(--theme-color, $color-primary)

.token.selector
@@ -162,6 +166,7 @@ body
color #22a2c9

.token.attr-value, .token.control, .token.directive, .token.unit
color $color-primary
color var(--theme-color, $color-primary)

.token.keyword, .token.function
5 changes: 5 additions & 0 deletions src/themes/vue.styl
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ body

ul li.active > a
border-right 2px solid
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600

@@ -52,6 +53,7 @@ body
font-weight 600

.markdown-section a
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600

@@ -88,6 +90,7 @@ body
padding-left 1.5rem

.markdown-section blockquote
border-left 4px solid $color-primary
border-left 4px solid var(--theme-color, $color-primary)
color #858585
margin 2em 0
@@ -204,6 +207,7 @@ body
color #2973b7

.token.string
color $color-primary
color var(--theme-color, $color-primary)

.token.selector
@@ -216,6 +220,7 @@ body
color #22a2c9

.token.attr-value, .token.control, .token.directive, .token.unit
color $color-primary
color var(--theme-color, $color-primary)

.token.keyword, .token.function
2 changes: 1 addition & 1 deletion test/README.md
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@

## Global Variables

- `TEST_HOST`: Test server ip:port
- `process.env.TEST_HOST`: Test server ip:port

## CLI commands

54 changes: 0 additions & 54 deletions test/config/jest-playwright.setup-tests.js

This file was deleted.

2 changes: 2 additions & 0 deletions test/config/jest.setup-tests.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* global afterEach, beforeAll, beforeEach */

import mock from 'xhr-mock';

const sideEffects = {
5 changes: 5 additions & 0 deletions test/config/playwright.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const server = require('./server.js');

module.exports = async config => {
await server.startAsync();
};
5 changes: 5 additions & 0 deletions test/config/playwright.teardown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const server = require('./server.js');

module.exports = async config => {
server.stop();
};
36 changes: 24 additions & 12 deletions test/config/server.js
Original file line number Diff line number Diff line change
@@ -2,10 +2,9 @@ const browserSync = require('browser-sync').create();
const path = require('path');

const hasStartArg = process.argv.includes('--start');

const serverConfig = {
host: '127.0.0.1',
port: 3001,
hostname: '127.0.0.1',
port: hasStartArg ? 3002 : 3001,
};

function startServer(options = {}, cb = Function.prototype) {
@@ -14,7 +13,7 @@ function startServer(options = {}, cb = Function.prototype) {
middleware: [
{
route: '/_blank.html',
handle: function(req, res, next) {
handle: function (req, res, next) {
res.setHeader('Content-Type', 'text/html');
res.end('');
next();
@@ -26,7 +25,8 @@ function startServer(options = {}, cb = Function.prototype) {
rewriteRules: [
// Replace docsify-related CDN URLs with local paths
{
match: /(https?:)?\/\/cdn\.jsdelivr\.net\/npm\/docsify(@\d?\.?\d?\.?\d)?\/lib\//g,
match:
/(https?:)?\/\/cdn\.jsdelivr\.net\/npm\/docsify(@\d?\.?\d?\.?\d)?\/lib\//g,
replace: '/lib/',
},
],
@@ -42,20 +42,28 @@ function startServer(options = {}, cb = Function.prototype) {
snippetOptions: {
rule: {
match: /<\/body>/i,
fn: function(snippet, match) {
fn: function (snippet, match) {
// Override changelog alias to load local changelog (see routes)
const injectJS = `
const newSnippet = `
${snippet.replace(/<script[^>]*/, '$& type="text/plain"')}
<script>
// Fix /docs site configuration during tests
(function() {
const aliasConfig = (window && window.$docsify && window.$docsify.alias) || {};
var aliasConfig = (window && window.$docsify && window.$docsify.alias) || {};
var isIE = /*@cc_on!@*/false || !!document.documentMode;

// 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');
}
})();
</script>
${match}
`;

return injectJS + snippet + match;
return newSnippet;
},
},
},
@@ -64,6 +72,10 @@ function startServer(options = {}, cb = Function.prototype) {

console.log('\n');

// Set TEST_HOST environment variable
process.env.TEST_HOST = `http://${serverConfig.hostname}:${serverConfig.port}`;

// Start server
browserSync.init(
// Config
{
@@ -93,7 +105,7 @@ function stopServer() {
if (hasStartArg) {
startServer({
open: true,
port: serverConfig.port + 1,
port: serverConfig.port,
directory: true,
startPath: '/docs',
});
@@ -107,5 +119,5 @@ module.exports = {
start: startServer,
startAsync: startServerAsync,
stop: stopServer,
TEST_HOST: `http://${serverConfig.host}:${serverConfig.port}`,
TEST_HOST: `http://${serverConfig.hostname}:${serverConfig.port}`,
};
2 changes: 1 addition & 1 deletion test/e2e/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
extends: ['plugin:jest-playwright/recommended'],
extends: ['plugin:playwright/playwright-test'],
};
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
67 changes: 67 additions & 0 deletions test/e2e/configuration.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const docsifyInit = require('../helpers/docsify-init');
const { test, expect } = require('./fixtures/docsify-init-fixture');

test.describe('Configuration options', () => {
test('catchPluginErrors:true (handles uncaught errors)', async ({ page }) => {
let consoleMsg, errorMsg;

page.on('console', msg => (consoleMsg = msg.text()));
page.on('pageerror', err => (errorMsg = err.message));

await docsifyInit({
config: {
catchPluginErrors: true,
plugins: [
function (hook, vm) {
hook.init(function () {
// eslint-disable-next-line no-undef
fail();
});
hook.beforeEach(function (markdown) {
return `${markdown}\n\nbeforeEach`;
});
},
],
},
markdown: {
homepage: '# Hello World',
},
// _logHTML: true,
});

const mainElm = page.locator('#main');

expect(errorMsg).toBeUndefined();
expect(consoleMsg).toContain('Docsify plugin error');
await expect(mainElm).toContainText('Hello World');
await expect(mainElm).toContainText('beforeEach');
});

test('catchPluginErrors:false (throws uncaught errors)', async ({ page }) => {
let consoleMsg, errorMsg;

page.on('console', msg => (consoleMsg = msg.text()));
page.on('pageerror', err => (errorMsg = err.message));

await docsifyInit({
config: {
catchPluginErrors: false,
plugins: [
function (hook, vm) {
hook.ready(function () {
// eslint-disable-next-line no-undef
fail();
});
},
],
},
markdown: {
homepage: '# Hello World',
},
// _logHTML: true,
});

expect(consoleMsg).toBeUndefined();
expect(errorMsg).toContain('fail');
});
});
Loading