mirror of
https://gitee.com/docsifyjs/docsify.git
synced 2024-11-29 10:38:48 +08:00
chore: Update lint configuration (ESLint 9, Prettier 3) (#2438)
* Update linting configuration (eslint, prettier) * Fix lint issues following eslint prettier update * Change ESLint config to allow boolean coercion * Switch to default import name per docs * Fix suppression of error details * Update JSDoc comments * Update waiForFunctin to provide error details --------- Co-authored-by: Koy Zhuang <koy@ko8e24.top>
This commit is contained in:
parent
6552853fef
commit
f5412dc7b0
@ -1,11 +0,0 @@
|
||||
# Directories
|
||||
.git
|
||||
build
|
||||
dist
|
||||
docs
|
||||
lib
|
||||
node_modules
|
||||
|
||||
# Files
|
||||
**/*.md
|
||||
server.js
|
@ -1,83 +0,0 @@
|
||||
const prettierConfig = require('./.prettierrc.json');
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
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,
|
||||
es6: true,
|
||||
node: true,
|
||||
},
|
||||
settings: {
|
||||
'import/ignore': ['node_modules', '.json$'],
|
||||
},
|
||||
rules: {
|
||||
camelcase: ['warn'],
|
||||
curly: ['error', 'all'],
|
||||
'dot-notation': ['error'],
|
||||
eqeqeq: ['error'],
|
||||
'handle-callback-err': ['error'],
|
||||
'new-cap': ['error'],
|
||||
'no-alert': ['error'],
|
||||
'no-caller': ['error'],
|
||||
'no-eval': ['error'],
|
||||
'no-labels': ['error'],
|
||||
'no-lonely-if': ['error'],
|
||||
'no-new': ['error'],
|
||||
'no-proto': ['error'],
|
||||
'no-return-assign': ['error'],
|
||||
'no-self-compare': ['error'],
|
||||
'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'],
|
||||
radix: ['error'],
|
||||
'spaced-comment': ['error', 'always'],
|
||||
strict: ['error', 'global'],
|
||||
yoda: ['error', 'never'],
|
||||
|
||||
// Import rules
|
||||
'import/imports-first': ['error'],
|
||||
'import/newline-after-import': ['error'],
|
||||
'import/no-duplicates': ['error'],
|
||||
'import/no-mutable-exports': ['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',
|
||||
dom: 'writable',
|
||||
},
|
||||
};
|
@ -13,6 +13,6 @@ let cover = read(file, 'utf8').toString();
|
||||
console.log('Replace version number in cover page...');
|
||||
cover = cover.replace(
|
||||
/<small>(\S+)?<\/small>/g,
|
||||
/* html */ `<small>${version}</small>`
|
||||
/* html */ `<small>${version}</small>`,
|
||||
);
|
||||
write(file, cover);
|
||||
|
@ -9,7 +9,7 @@ const filePaths = {
|
||||
'src',
|
||||
'core',
|
||||
'render',
|
||||
'emoji-data.js'
|
||||
'emoji-data.js',
|
||||
),
|
||||
};
|
||||
|
||||
@ -26,7 +26,7 @@ async function getEmojiData() {
|
||||
|
||||
// Remove base URL from emoji URLs
|
||||
Object.entries(data).forEach(
|
||||
([key, value]) => (data[key] = value.replace(baseURL, ''))
|
||||
([key, value]) => (data[key] = value.replace(baseURL, '')),
|
||||
);
|
||||
|
||||
console.info(`- Retrieved ${Object.keys(data).length} emoji entries`);
|
||||
@ -41,7 +41,7 @@ function writeEmojiPage(emojiData) {
|
||||
const isExistingPage = fs.existsSync(filePaths.emojiMarkdown);
|
||||
const emojiPage =
|
||||
(isExistingPage && fs.readFileSync(filePaths.emojiMarkdown, 'utf8')) ||
|
||||
`<!-- START -->\n\n<!-- END -->`;
|
||||
'<!-- START -->\n\n<!-- END -->';
|
||||
const emojiRegEx = /(<!--\s*START.*-->\n)([\s\S]*)(\n<!--\s*END.*-->)/;
|
||||
const emojiMatch = emojiPage.match(emojiRegEx);
|
||||
const emojiMarkdownStart = emojiMatch[1].trim();
|
||||
@ -51,20 +51,20 @@ function writeEmojiPage(emojiData) {
|
||||
.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}`
|
||||
`${emojiMarkdownStart}\n\n${newEmojiMarkdown}\n\n${emojiMarkdownEnd}`,
|
||||
);
|
||||
|
||||
fs.writeFileSync(filePaths.emojiMarkdown, newEmojiPage);
|
||||
|
||||
console.info(
|
||||
`- ${!isExistingPage ? 'Created' : 'Updated'}: ${filePaths.emojiMarkdown}`
|
||||
`- ${!isExistingPage ? 'Created' : 'Updated'}: ${filePaths.emojiMarkdown}`,
|
||||
);
|
||||
} else {
|
||||
console.info(`- No changes: ${filePaths.emojiMarkdown}`);
|
||||
@ -86,7 +86,7 @@ function writeEmojiJS(emojiData) {
|
||||
fs.writeFileSync(filePaths.emojiJS, newEmojiJS);
|
||||
|
||||
console.info(
|
||||
`- ${!isExistingPage ? 'Created' : 'Updated'}: ${filePaths.emojiJS}`
|
||||
`- ${!isExistingPage ? 'Created' : 'Updated'}: ${filePaths.emojiJS}`,
|
||||
);
|
||||
} else {
|
||||
console.info(`- No changes: ${filePaths.emojiJS}`);
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
@ -47,7 +47,7 @@ window.$docsify = {
|
||||
return /* html */ `
|
||||
<div class="mermaid">${mermaid.render(
|
||||
'mermaid-svg-' + num++,
|
||||
code
|
||||
code,
|
||||
)}</div>
|
||||
`;
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ self.addEventListener('fetch', event => {
|
||||
.then(resp => resp || fetched)
|
||||
.catch(_ => {
|
||||
/* eat any errors */
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Update the cache with the version we fetched (only for ok status)
|
||||
@ -93,11 +93,11 @@ self.addEventListener('fetch', event => {
|
||||
Promise.all([fetchedCopy, caches.open(RUNTIME)])
|
||||
.then(
|
||||
([response, cache]) =>
|
||||
response.ok && cache.put(event.request, response)
|
||||
response.ok && cache.put(event.request, response),
|
||||
)
|
||||
.catch(_ => {
|
||||
/* eat any errors */
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
79
eslint.config.js
Normal file
79
eslint.config.js
Normal file
@ -0,0 +1,79 @@
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
import playwrightPlugin from 'eslint-plugin-playwright';
|
||||
import jestPlugin from 'eslint-plugin-jest';
|
||||
import globals from 'globals';
|
||||
import js from '@eslint/js';
|
||||
|
||||
export default [
|
||||
// Ignore (Must be first item in array)
|
||||
{
|
||||
ignores: [
|
||||
// Directories
|
||||
'_playwright-*',
|
||||
'dist',
|
||||
'docs',
|
||||
'lib',
|
||||
'node_modules',
|
||||
// Files
|
||||
'**/*.md',
|
||||
'CHANGELOG.md',
|
||||
'emoji-data.*',
|
||||
'HISTORY.md',
|
||||
],
|
||||
},
|
||||
// ESLint Recommended
|
||||
js.configs.recommended,
|
||||
// Configuration: Prettier
|
||||
eslintConfigPrettier,
|
||||
// All Files
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
$docsify: 'readonly',
|
||||
Docsify: 'readonly',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'array-callback-return': ['error'],
|
||||
'block-scoped-var': ['error'],
|
||||
curly: ['error'],
|
||||
'dot-notation': ['error'],
|
||||
eqeqeq: ['error'],
|
||||
'no-implicit-coercion': ['error', { boolean: false }],
|
||||
'no-implicit-globals': ['error'],
|
||||
'no-loop-func': ['error'],
|
||||
'no-return-assign': ['error'],
|
||||
'no-template-curly-in-string': ['error'],
|
||||
'no-unneeded-ternary': ['error'],
|
||||
'no-unused-vars': ['error', { args: 'none' }],
|
||||
'no-useless-computed-key': ['error'],
|
||||
'no-useless-return': ['error'],
|
||||
'no-var': ['error'],
|
||||
'prefer-const': [
|
||||
'error',
|
||||
{
|
||||
destructuring: 'all',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// Source Files
|
||||
{
|
||||
files: ['src/**'],
|
||||
rules: {
|
||||
'no-console': ['warn'],
|
||||
},
|
||||
},
|
||||
// Tests: E2E (Playwright)
|
||||
{
|
||||
files: ['test/e2e/**'],
|
||||
...playwrightPlugin.configs['flat/recommended'],
|
||||
},
|
||||
// Tests: Integration & Unit (Jest)
|
||||
{
|
||||
files: ['test/{integration,unit}/**'],
|
||||
...jestPlugin.configs['flat/recommended'],
|
||||
},
|
||||
];
|
@ -40,7 +40,7 @@ export default async function middleware(request) {
|
||||
const indexHTML = await fetch(indexURL).then(res => res.text());
|
||||
const previewHTML = rewriteRules.reduce(
|
||||
(html, rule) => html.replace(rule.match, rule.replace),
|
||||
indexHTML
|
||||
indexHTML,
|
||||
);
|
||||
|
||||
return new Response(previewHTML, {
|
||||
|
1077
package-lock.json
generated
1077
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@ -44,7 +44,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.24.5",
|
||||
"@babel/preset-env": "^7.11.5",
|
||||
"@eslint/js": "^8.43.0",
|
||||
"@eslint/js": "^9.3.0",
|
||||
"@playwright/test": "^1.44.0",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-commonjs": "^25.0.2",
|
||||
@ -58,14 +58,13 @@
|
||||
"conventional-changelog-cli": "^3.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"cssnano": "^7.0.1",
|
||||
"eslint": "^8.43.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint": "^9.3.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-jest": "^28.5.0",
|
||||
"eslint-plugin-playwright": "^1.6.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"glob": "^10.3.15",
|
||||
"globals": "^13.20.0",
|
||||
"globals": "^15.3.0",
|
||||
"husky": "^9.0.11",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
@ -73,7 +72,7 @@
|
||||
"marked": "^12.0.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss-cli": "^11.0.0",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier": "^3.2.5",
|
||||
"rimraf": "^5.0.7",
|
||||
"rollup": "^4.17.2",
|
||||
"stylus": "^0.63.0",
|
||||
@ -98,7 +97,7 @@
|
||||
"docker:test:integration": "npm run docker:cli -- test:integration",
|
||||
"docker:test:unit": "npm run docker:cli -- test:unit",
|
||||
"docker:test": "npm run docker:cli -- test",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint:fix": "prettier . --write && eslint . --fix",
|
||||
"lint": "prettier . --check && eslint .",
|
||||
"postinstall": "opencollective-postinstall && npx husky install",
|
||||
"prepare": "npm run build",
|
||||
|
@ -37,7 +37,7 @@ const pluginConfigs = pluginPaths.map(pluginPath => {
|
||||
// =============================================================================
|
||||
const currentYear = new Date().getFullYear();
|
||||
const { homepage, license, version } = JSON.parse(
|
||||
await fs.readFile(path.join(import.meta.dirname, 'package.json'), 'utf8')
|
||||
await fs.readFile(path.join(import.meta.dirname, 'package.json'), 'utf8'),
|
||||
);
|
||||
const baseConfig = {
|
||||
output: {
|
||||
|
@ -10,10 +10,8 @@ import { Lifecycle } from './init/lifecycle.js';
|
||||
|
||||
/** @typedef {new (...args: any[]) => any} Constructor */
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
export class Docsify extends Fetch(
|
||||
// eslint-disable-next-line new-cap
|
||||
Events(Render(VirtualRoutes(Router(Lifecycle(Object)))))
|
||||
Events(Render(VirtualRoutes(Router(Lifecycle(Object))))),
|
||||
) {
|
||||
config = config(this);
|
||||
|
||||
@ -37,6 +35,8 @@ export class Docsify extends Fetch(
|
||||
} catch (err) {
|
||||
if (this.config.catchPluginErrors) {
|
||||
const errTitle = 'Docsify plugin error';
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(errTitle, err);
|
||||
} else {
|
||||
throw err;
|
||||
|
@ -49,6 +49,8 @@ export default function (vm) {
|
||||
set themeColor(value) {
|
||||
if (value) {
|
||||
this.__themeColor = value;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
stripIndent(`
|
||||
$docsify.themeColor is deprecated. Use a --theme-color property in your style sheet. Example:
|
||||
@ -57,7 +59,7 @@ export default function (vm) {
|
||||
--theme-color: deeppink;
|
||||
}
|
||||
</style>
|
||||
`).trim()
|
||||
`).trim(),
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -65,7 +67,7 @@ export default function (vm) {
|
||||
|
||||
typeof window.$docsify === 'function'
|
||||
? window.$docsify(vm)
|
||||
: window.$docsify
|
||||
: window.$docsify,
|
||||
);
|
||||
|
||||
// Merge default and user-specified key bindings
|
||||
@ -86,14 +88,14 @@ export default function (vm) {
|
||||
},
|
||||
},
|
||||
// User-specified
|
||||
config.keyBindings
|
||||
config.keyBindings,
|
||||
);
|
||||
}
|
||||
|
||||
const script =
|
||||
currentScript ||
|
||||
Array.from(document.getElementsByTagName('script')).filter(n =>
|
||||
/docsify\./.test(n.src)
|
||||
/docsify\./.test(n.src),
|
||||
)[0];
|
||||
|
||||
if (script) {
|
||||
|
@ -27,7 +27,7 @@ export function Events(Base) {
|
||||
if (topMargin) {
|
||||
document.documentElement.style.setProperty(
|
||||
'scroll-padding-top',
|
||||
`${topMargin}px`
|
||||
`${topMargin}px`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ export function Events(Base) {
|
||||
a.compareDocumentPosition(b) &
|
||||
Node.DOCUMENT_POSITION_FOLLOWING
|
||||
? -1
|
||||
: 1
|
||||
: 1,
|
||||
)[0]
|
||||
: // Get first and only item in set.
|
||||
// May be undefined if no headings are in view.
|
||||
@ -115,7 +115,7 @@ export function Events(Base) {
|
||||
},
|
||||
{
|
||||
rootMargin: '0% 0% -50% 0%', // Top half of viewport
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
headingElms.forEach(elm => {
|
||||
@ -172,7 +172,7 @@ export function Events(Base) {
|
||||
// Handle keyboard events
|
||||
dom.on('keydown', e => {
|
||||
const isTextEntry = document.activeElement.matches(
|
||||
'input, select, textarea'
|
||||
'input, select, textarea',
|
||||
);
|
||||
|
||||
if (isTextEntry) {
|
||||
@ -192,9 +192,9 @@ export function Events(Base) {
|
||||
(modifierKeys.includes(k) && e[k + 'Key']) ||
|
||||
e.key === k || // Ex: " ", "a"
|
||||
e.code.toLowerCase() === k || // "space"
|
||||
e.code.toLowerCase() === `key${k}` // "keya"
|
||||
)
|
||||
)
|
||||
e.code.toLowerCase() === `key${k}`, // "keya"
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
matchingConfigs.forEach(({ callback }) => {
|
||||
@ -262,7 +262,7 @@ export function Events(Base) {
|
||||
dom.on(
|
||||
dom.body,
|
||||
'click',
|
||||
() => dom.body.classList.contains('close') && toggle()
|
||||
() => dom.body.classList.contains('close') && toggle(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -297,7 +297,7 @@ export function Events(Base) {
|
||||
onRender() {
|
||||
const currentPath = this.router.toURL(this.router.getCurrentPath());
|
||||
const currentTitle = dom.find(
|
||||
`.sidebar a[href='${currentPath}']`
|
||||
`.sidebar a[href='${currentPath}']`,
|
||||
)?.innerText;
|
||||
|
||||
// Update page title
|
||||
@ -327,7 +327,7 @@ export function Events(Base) {
|
||||
// Anchor link
|
||||
if (query.id) {
|
||||
const headingElm = dom.find(
|
||||
`.markdown-section :where(h1, h2, h3, h4, h5)[id="${query.id}"]`
|
||||
`.markdown-section :where(h1, h2, h3, h4, h5)[id="${query.id}"]`,
|
||||
);
|
||||
|
||||
if (headingElm) {
|
||||
@ -402,7 +402,7 @@ export function Events(Base) {
|
||||
.find(
|
||||
a =>
|
||||
href.includes(a.getAttribute('href')) ||
|
||||
href.includes(decodeURI(a.getAttribute('href')))
|
||||
href.includes(decodeURI(a.getAttribute('href'))),
|
||||
);
|
||||
const oldActive = dom.find(navElm, 'li.active');
|
||||
|
||||
@ -434,7 +434,7 @@ export function Events(Base) {
|
||||
const newActive = dom
|
||||
.find(
|
||||
sidebar,
|
||||
`a[href="${href}"], a[href="${decodeURIComponent(href)}"]`
|
||||
`a[href="${href}"], a[href="${decodeURIComponent(href)}"]`,
|
||||
)
|
||||
?.closest('li');
|
||||
|
||||
@ -467,7 +467,7 @@ export function Events(Base) {
|
||||
const newPage = dom
|
||||
.find(
|
||||
sidebar,
|
||||
`a[href="${path}"], a[href="${decodeURIComponent(path)}"]`
|
||||
`a[href="${path}"], a[href="${decodeURIComponent(path)}"]`,
|
||||
)
|
||||
?.closest('li');
|
||||
|
||||
@ -496,7 +496,7 @@ export function Events(Base) {
|
||||
document.addEventListener(
|
||||
'scrollend',
|
||||
() => (this.#isScrolling = false),
|
||||
{ once: true }
|
||||
{ once: true },
|
||||
);
|
||||
}
|
||||
// Browsers w/o native scrollend event support (Safari)
|
||||
@ -515,7 +515,7 @@ export function Events(Base) {
|
||||
document.addEventListener('scroll', callback, false);
|
||||
}
|
||||
},
|
||||
{ once: true }
|
||||
{ once: true },
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -22,7 +22,7 @@ export function Fetch(Base) {
|
||||
get(
|
||||
vm.router.getFile(path + file) + qs,
|
||||
false,
|
||||
vm.config.requestHeaders
|
||||
vm.config.requestHeaders,
|
||||
).then(next, _error => this.#loadNested(path, qs, file, next, vm));
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ export function Fetch(Base) {
|
||||
|
||||
_fetch(cb = noop) {
|
||||
const { query } = this.route;
|
||||
let { path } = this.route;
|
||||
const { path } = this.route;
|
||||
|
||||
// Prevent loading remote content via URL hash
|
||||
// Ex: https://foo.com/#//bar.com/file.md
|
||||
@ -107,7 +107,7 @@ export function Fetch(Base) {
|
||||
this._renderMain(
|
||||
text,
|
||||
opt,
|
||||
this._loadSideAndNav(path, qs, loadSidebar, cb)
|
||||
this._loadSideAndNav(path, qs, loadSidebar, cb),
|
||||
);
|
||||
};
|
||||
|
||||
@ -125,7 +125,7 @@ export function Fetch(Base) {
|
||||
} else {
|
||||
this.#request(file + qs, requestHeaders).then(
|
||||
contentFetched,
|
||||
contentFailedToFetch
|
||||
contentFailedToFetch,
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -133,7 +133,7 @@ export function Fetch(Base) {
|
||||
// if the requested url is not local, just fetch the file
|
||||
this.#request(file + qs, requestHeaders).then(
|
||||
contentFetched,
|
||||
contentFailedToFetch
|
||||
contentFailedToFetch,
|
||||
);
|
||||
}
|
||||
|
||||
@ -145,7 +145,7 @@ export function Fetch(Base) {
|
||||
loadNavbar,
|
||||
text => this._renderNav(text),
|
||||
this,
|
||||
true
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -174,7 +174,7 @@ export function Fetch(Base) {
|
||||
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)
|
||||
text => this._renderCover(text, coverOnly),
|
||||
);
|
||||
} else {
|
||||
this._renderCover(null, coverOnly);
|
||||
@ -216,7 +216,7 @@ export function Fetch(Base) {
|
||||
}
|
||||
|
||||
const newPath = this.router.getFile(
|
||||
path.replace(new RegExp(`^/${local}`), '')
|
||||
path.replace(new RegExp(`^/${local}`), ''),
|
||||
);
|
||||
const req = this.#request(newPath + qs, requestHeaders);
|
||||
|
||||
@ -225,9 +225,9 @@ export function Fetch(Base) {
|
||||
this._renderMain(
|
||||
text,
|
||||
opt,
|
||||
this._loadSideAndNav(path, qs, loadSidebar, cb)
|
||||
this._loadSideAndNav(path, qs, loadSidebar, cb),
|
||||
),
|
||||
_error => this._fetch404(path, qs, cb)
|
||||
_error => this._fetch404(path, qs, cb),
|
||||
);
|
||||
|
||||
return true;
|
||||
@ -250,7 +250,7 @@ export function Fetch(Base) {
|
||||
|
||||
this.#request(this.router.getFile(path404), requestHeaders).then(
|
||||
(text, opt) => this._renderMain(text, opt, fnLoadSideAndNav),
|
||||
_error => this._renderMain(null, {}, fnLoadSideAndNav)
|
||||
_error => this._renderMain(null, {}, fnLoadSideAndNav),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ export function Lifecycle(Base) {
|
||||
});
|
||||
} catch (err) {
|
||||
if (catchPluginErrors) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(errTitle, err);
|
||||
} else {
|
||||
throw err;
|
||||
@ -62,6 +63,7 @@ export function Lifecycle(Base) {
|
||||
step(index + 1);
|
||||
} catch (err) {
|
||||
if (catchPluginErrors) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(errTitle, err);
|
||||
} else {
|
||||
throw err;
|
||||
|
@ -85,7 +85,7 @@ export class Compiler {
|
||||
marked.setOptions(
|
||||
Object.assign(mdConf, {
|
||||
renderer: Object.assign(renderer, mdConf.renderer),
|
||||
})
|
||||
}),
|
||||
);
|
||||
compile = marked;
|
||||
}
|
||||
@ -152,7 +152,7 @@ export class Compiler {
|
||||
href = getPath(
|
||||
this.contentBase,
|
||||
getParentPath(this.router.getCurrentPath()),
|
||||
href
|
||||
href,
|
||||
);
|
||||
}
|
||||
|
||||
@ -282,7 +282,7 @@ export class Compiler {
|
||||
}
|
||||
|
||||
const tree = this.cacheTree[currentPath] || genTree(toc, level);
|
||||
html = treeTpl(tree, /* html */ `<ul>{inner}</ul>`);
|
||||
html = treeTpl(tree, /* html */ '<ul>{inner}</ul>');
|
||||
this.cacheTree[currentPath] = tree;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ export const highlightCodeCompiler = ({ renderer }) =>
|
||||
const text = Prism.highlight(
|
||||
code.replace(/@DOCSIFY_QM@/g, '`'),
|
||||
langOrMarkup,
|
||||
lang
|
||||
lang,
|
||||
);
|
||||
|
||||
return /* html */ `<pre data-lang="${lang}"><code class="lang-${lang}" tabindex="0">${text}</code></pre>`;
|
||||
|
@ -4,7 +4,7 @@ import { isAbsolutePath, getPath, getParentPath } from '../../router/util.js';
|
||||
export const imageCompiler = ({ renderer, contentBase, router }) =>
|
||||
(renderer.image = (href, title, text) => {
|
||||
let url = href;
|
||||
let attrs = [];
|
||||
const attrs = [];
|
||||
|
||||
const { str, config } = getAndRemoveConfig(title);
|
||||
title = str;
|
||||
@ -39,6 +39,6 @@ export const imageCompiler = ({ renderer, contentBase, router }) =>
|
||||
}
|
||||
|
||||
return /* html */ `<img src="${url}" data-origin="${href}" alt="${text}" ${attrs.join(
|
||||
' '
|
||||
' ',
|
||||
)} />`;
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ export const linkCompiler = ({
|
||||
compilerClass,
|
||||
}) =>
|
||||
(renderer.link = (href, title = '', text) => {
|
||||
let attrs = [];
|
||||
const attrs = [];
|
||||
const { str, config } = getAndRemoveConfig(title);
|
||||
linkTarget = config.target || linkTarget;
|
||||
linkRel =
|
||||
@ -42,8 +42,8 @@ export const linkCompiler = ({
|
||||
href.indexOf('mailto:') === 0
|
||||
? ''
|
||||
: linkRel !== ''
|
||||
? ` rel="${linkRel}"`
|
||||
: ''
|
||||
? ` rel="${linkRel}"`
|
||||
: '',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
export const taskListCompiler = ({ renderer }) =>
|
||||
(renderer.list = (body, ordered, start) => {
|
||||
const isTaskList = /<li class="task-list-item">/.test(
|
||||
body.split('class="task-list"')[0]
|
||||
body.split('class="task-list"')[0],
|
||||
);
|
||||
const isStartReq = start && start > 1;
|
||||
const tag = ordered ? 'ol' : 'ul';
|
||||
|
@ -15,6 +15,7 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
|
||||
while ((token = embedTokens[step++])) {
|
||||
const currentToken = token;
|
||||
|
||||
// eslint-disable-next-line no-loop-func
|
||||
const next = text => {
|
||||
let embedToken;
|
||||
if (text) {
|
||||
@ -48,7 +49,7 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
|
||||
if (currentToken.embed.fragment) {
|
||||
const fragment = currentToken.embed.fragment;
|
||||
const pattern = new RegExp(
|
||||
`(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`
|
||||
`(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`,
|
||||
);
|
||||
text = stripIndent((text.match(pattern) || [])[1] || '').trim();
|
||||
}
|
||||
@ -58,7 +59,7 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
|
||||
currentToken.embed.lang +
|
||||
'\n' +
|
||||
text.replace(/`/g, '@DOCSIFY_QM@') +
|
||||
'\n```\n'
|
||||
'\n```\n',
|
||||
);
|
||||
} else if (currentToken.embed.type === 'mermaid') {
|
||||
embedToken = [
|
||||
@ -89,7 +90,7 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
|
||||
}
|
||||
|
||||
export function prerenderEmbed({ compiler, raw = '', fetch }, done) {
|
||||
let hit = cached[raw];
|
||||
const hit = cached[raw];
|
||||
if (hit) {
|
||||
const copy = hit.slice();
|
||||
copy.links = hit.links;
|
||||
@ -117,7 +118,7 @@ export function prerenderEmbed({ compiler, raw = '', fetch }, done) {
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* eslint-disable */
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// DO NOT EDIT: This file is auto-generated by an /build/emoji.js
|
||||
|
@ -31,17 +31,17 @@ export function emojify(text, useNativeEmoji) {
|
||||
// Mark colons in tags
|
||||
.replace(
|
||||
/<(code|pre|script|template)[^>]*?>[\s\S]+?<\/(code|pre|script|template)>/g,
|
||||
m => m.replace(/:/g, '__colon__')
|
||||
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__')
|
||||
m.replace(/:/g, '__colon__'),
|
||||
)
|
||||
// Replace emoji shorthand codes
|
||||
.replace(/:([a-z0-9_\-+]+?):/g, (m, $1) =>
|
||||
replaceEmojiShorthand(m, $1, useNativeEmoji)
|
||||
replaceEmojiShorthand(m, $1, useNativeEmoji),
|
||||
)
|
||||
// Restore colons in tags and comments
|
||||
.replace(/__colon__/g, ':')
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import tinydate from 'tinydate';
|
||||
import * as dom from '../util/dom.js';
|
||||
import { getPath, isAbsolutePath } from '../router/util.js';
|
||||
@ -47,8 +46,8 @@ export function Render(Base) {
|
||||
typeof fn === 'function'
|
||||
? fn(updated)
|
||||
: typeof fn === 'string'
|
||||
? tinydate(fn)(new Date(updated))
|
||||
: updated;
|
||||
? tinydate(fn)(new Date(updated))
|
||||
: updated;
|
||||
|
||||
return html.replace(/{docsify-updated}/g, updated);
|
||||
}
|
||||
@ -101,7 +100,7 @@ export function Render(Base) {
|
||||
const vueGlobalOptions = docsifyConfig.vueGlobalOptions || {};
|
||||
const vueMountData = [];
|
||||
const vueComponentNames = Object.keys(
|
||||
docsifyConfig.vueComponents || {}
|
||||
docsifyConfig.vueComponents || {},
|
||||
);
|
||||
|
||||
// Register global vueComponents
|
||||
@ -131,7 +130,7 @@ export function Render(Base) {
|
||||
dom.find(markdownElm, cssSelector),
|
||||
docsifyConfig.vueMounts[cssSelector],
|
||||
])
|
||||
.filter(([elm, vueConfig]) => elm)
|
||||
.filter(([elm, vueConfig]) => elm),
|
||||
);
|
||||
|
||||
// Template syntax, vueComponents, vueGlobalOptions ...
|
||||
@ -189,7 +188,7 @@ export function Render(Base) {
|
||||
}
|
||||
|
||||
return [elm, vueConfig];
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Not found mounts but import Vue resource
|
||||
@ -244,7 +243,7 @@ export function Render(Base) {
|
||||
el.setAttribute('href', nameLink);
|
||||
} else if (typeof nameLink === 'object') {
|
||||
const match = Object.keys(nameLink).filter(
|
||||
key => path.indexOf(key) > -1
|
||||
key => path.indexOf(key) > -1,
|
||||
)[0];
|
||||
|
||||
el.setAttribute('href', nameLink[match]);
|
||||
@ -262,7 +261,7 @@ export function Render(Base) {
|
||||
|
||||
if (skipLink?.constructor === Object) {
|
||||
const matchingPath = Object.keys(skipLink).find(path =>
|
||||
vm.route.path.startsWith(path.startsWith('/') ? path : `/${path}`)
|
||||
vm.route.path.startsWith(path.startsWith('/') ? path : `/${path}`),
|
||||
);
|
||||
const matchingText = matchingPath && skipLink[matchingPath];
|
||||
|
||||
@ -355,7 +354,7 @@ export function Render(Base) {
|
||||
html = this.#formatUpdated(
|
||||
html,
|
||||
opt.updatedAt,
|
||||
this.config.formatUpdated
|
||||
this.config.formatUpdated,
|
||||
);
|
||||
}
|
||||
|
||||
@ -377,7 +376,7 @@ export function Render(Base) {
|
||||
tokens => {
|
||||
html = this.compiler.compile(tokens);
|
||||
callback();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -389,7 +388,7 @@ export function Render(Base) {
|
||||
dom.toggleClass(
|
||||
dom.getNode('main'),
|
||||
coverOnly ? 'add' : 'remove',
|
||||
'hidden'
|
||||
'hidden',
|
||||
);
|
||||
if (!text) {
|
||||
dom.toggleClass(el, 'remove', 'show');
|
||||
@ -440,7 +439,6 @@ export function Render(Base) {
|
||||
// Init markdown compiler
|
||||
this.compiler = new Compiler(config, this.router);
|
||||
if (inBrowser) {
|
||||
/* eslint-disable-next-line camelcase */
|
||||
window.__current_docsify_compiler__ = this.compiler;
|
||||
}
|
||||
|
||||
@ -494,7 +492,7 @@ export function Render(Base) {
|
||||
|
||||
if (config.themeColor) {
|
||||
dom.$.head.appendChild(
|
||||
dom.create('div', tpl.theme(config.themeColor)).firstElementChild
|
||||
dom.create('div', tpl.theme(config.themeColor)).firstElementChild,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ export default function (info) {
|
||||
|
||||
if (num >= 95) {
|
||||
clearTimeout(timeId);
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
||||
timeId = setTimeout(_ => {
|
||||
barEl.style.opacity = 0;
|
||||
barEl.style.width = '0%';
|
||||
|
@ -95,7 +95,7 @@ export function cover() {
|
||||
*/
|
||||
export function tree(
|
||||
toc,
|
||||
tpl = /* html */ `<ul class="app-sub-sidebar">{inner}</ul>`
|
||||
tpl = /* html */ '<ul class="app-sub-sidebar">{inner}</ul>',
|
||||
) {
|
||||
if (!toc || !toc.length) {
|
||||
return '';
|
||||
|
@ -26,7 +26,7 @@ export class History {
|
||||
? this.#getAlias(
|
||||
path.replace(this.#cached[match], alias[match]),
|
||||
alias,
|
||||
path
|
||||
path,
|
||||
)
|
||||
: path;
|
||||
}
|
||||
@ -35,8 +35,8 @@ export class History {
|
||||
return new RegExp(`\\.(${ext.replace(/^\./, '')}|html)$`, 'g').test(path)
|
||||
? path
|
||||
: /\/$/g.test(path)
|
||||
? `${path}README${ext}`
|
||||
: `${path}${ext}`;
|
||||
? `${path}README${ext}`
|
||||
: `${path}${ext}`;
|
||||
}
|
||||
|
||||
getBasePath() {
|
||||
@ -88,7 +88,7 @@ export class History {
|
||||
if (this.config.relativePath && path.indexOf('/') !== 0) {
|
||||
const currentDir = currentRoute.substring(
|
||||
0,
|
||||
currentRoute.lastIndexOf('/') + 1
|
||||
currentRoute.lastIndexOf('/') + 1,
|
||||
);
|
||||
return cleanPath(resolvePath(currentDir + path));
|
||||
}
|
||||
|
@ -43,7 +43,6 @@ export function Router(Base) {
|
||||
this.updateRender();
|
||||
lastRoute = this.route;
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
router.onchange(params => {
|
||||
this.updateRender();
|
||||
this._updateRender();
|
||||
|
@ -33,7 +33,7 @@ export function stringifyQuery(obj, ignores = []) {
|
||||
qs.push(
|
||||
obj[key]
|
||||
? `${encode(key)}=${encode(obj[key])}`.toLowerCase()
|
||||
: encode(key)
|
||||
: encode(key),
|
||||
);
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ export const cleanPath = cached(path => {
|
||||
|
||||
export const resolvePath = cached(path => {
|
||||
const segments = path.replace(/^\//, '').split('/');
|
||||
let resolved = [];
|
||||
const resolved = [];
|
||||
for (const segment of segments) {
|
||||
if (segment === '..') {
|
||||
resolved.pop();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// @ts-check
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
import progressbar from '../render/progressbar.js';
|
||||
import { noop } from './core.js';
|
||||
|
||||
@ -53,7 +53,7 @@ export function get(url, hasBar = false, headers = {}) {
|
||||
progressbar({
|
||||
step: Math.floor(Math.random() * 5 + 1),
|
||||
}),
|
||||
500
|
||||
500,
|
||||
);
|
||||
|
||||
xhr.addEventListener('progress', progressbar);
|
||||
|
@ -52,8 +52,8 @@ export function isFn(obj) {
|
||||
* @returns {Boolean} True if the passed-in url is external
|
||||
*/
|
||||
export function isExternal(url) {
|
||||
let match = url.match(
|
||||
/^([^:/?#]+:)?(?:\/{2,}([^/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/
|
||||
const match = url.match(
|
||||
/^([^:/?#]+:)?(?:\/{2,}([^/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/,
|
||||
);
|
||||
|
||||
if (
|
||||
@ -68,9 +68,9 @@ export function isExternal(url) {
|
||||
match[2].length > 0 &&
|
||||
match[2].replace(
|
||||
new RegExp(
|
||||
':(' + { 'http:': 80, 'https:': 443 }[location.protocol] + ')?$'
|
||||
':(' + { 'http:': 80, 'https:': 443 }[location.protocol] + ')?$',
|
||||
),
|
||||
''
|
||||
'',
|
||||
) !== location.host
|
||||
) {
|
||||
return true;
|
||||
|
@ -26,7 +26,6 @@ function install(hook, vm) {
|
||||
div.style = `width: ${main.clientWidth}px; margin: 0 auto 20px;`;
|
||||
dom.appendTo(dom.find('.content'), div);
|
||||
|
||||
// eslint-disable-next-line
|
||||
window.disqus_config = function () {
|
||||
this.page.url = location.origin + '/-' + vm.route.path;
|
||||
this.page.identifier = vm.route.path;
|
||||
|
@ -2,6 +2,7 @@ import emojiData from '../core/render/emoji-data.js';
|
||||
|
||||
// Deprecation notice
|
||||
if (window && window.console) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info('Docsify emoji plugin has been deprecated as of v4.13');
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ var errors = [],
|
||||
item: new RegExp('^-\\s+'),
|
||||
trim: new RegExp('^\\s+|\\s+$'),
|
||||
comment: new RegExp(
|
||||
'([^\\\'\\"#]+([\\\'\\"][^\\\'\\"]*[\\\'\\"])*)*(#.*)?'
|
||||
'([^\\\'\\"#]+([\\\'\\"][^\\\'\\"]*[\\\'\\"])*)*(#.*)?',
|
||||
),
|
||||
};
|
||||
|
||||
|
@ -29,6 +29,7 @@ function collect() {
|
||||
|
||||
const install = function (hook) {
|
||||
if (!$docsify.ga) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('[Docsify] ga is required.');
|
||||
return;
|
||||
}
|
||||
|
@ -51,16 +51,15 @@ function collect() {
|
||||
|
||||
// usage: https://developers.google.com/analytics/devguides/collection/gtagjs/pages
|
||||
window.gtag('event', 'page_view', {
|
||||
/* eslint-disable camelcase */
|
||||
page_title: document.title,
|
||||
page_location: location.href,
|
||||
page_path: location.pathname,
|
||||
/* eslint-disable camelcase */
|
||||
});
|
||||
}
|
||||
|
||||
const install = function (hook) {
|
||||
if (!$docsify.gtag) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('[Docsify] gtag is required.');
|
||||
return;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { search } from './search.js';
|
||||
|
||||
let NO_DATA_TEXT = '';
|
||||
@ -216,7 +215,7 @@ function bindEvents() {
|
||||
'click',
|
||||
e =>
|
||||
['A', 'H2', 'P', 'EM'].indexOf(e.target.tagName) === -1 &&
|
||||
e.stopPropagation()
|
||||
e.stopPropagation(),
|
||||
);
|
||||
Docsify.dom.on($input, 'input', e => {
|
||||
clearTimeout(timeId);
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import {
|
||||
init as initComponent,
|
||||
update as updateComponent,
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import {
|
||||
getAndRemoveConfig,
|
||||
getAndRemoveDocisfyIgnoreConfig,
|
||||
@ -178,9 +177,9 @@ export function search(query) {
|
||||
const regEx = new RegExp(
|
||||
escapeHtml(ignoreDiacriticalMarks(keyword)).replace(
|
||||
/[|\\{}()[\]^$+*?.]/g,
|
||||
'\\$&'
|
||||
'\\$&',
|
||||
),
|
||||
'gi'
|
||||
'gi',
|
||||
);
|
||||
let indexTitle = -1;
|
||||
let indexContent = -1;
|
||||
@ -217,7 +216,7 @@ export function search(query) {
|
||||
.substring(start, end)
|
||||
.replace(
|
||||
regEx,
|
||||
word => /* html */ `<em class="search-keyword">${word}</em>`
|
||||
word => /* html */ `<em class="search-keyword">${word}</em>`,
|
||||
) +
|
||||
'...';
|
||||
|
||||
@ -254,7 +253,7 @@ export function init(config, vm) {
|
||||
if (Array.isArray(config.pathNamespaces)) {
|
||||
namespaceSuffix =
|
||||
config.pathNamespaces.filter(
|
||||
prefix => path.slice(0, prefix.length) === prefix
|
||||
prefix => path.slice(0, prefix.length) === prefix,
|
||||
)[0] || namespaceSuffix;
|
||||
} else if (config.pathNamespaces instanceof RegExp) {
|
||||
const matches = path.match(config.pathNamespaces);
|
||||
@ -297,7 +296,7 @@ export function init(config, vm) {
|
||||
result => {
|
||||
INDEXS[path] = genIndex(path, result, vm.router, config.depth);
|
||||
len === ++count && saveData(config.maxAge, expireKey, indexKey);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ function install(hook) {
|
||||
hook.doneEach(_ => {
|
||||
let elms = Array.from(
|
||||
document.querySelectorAll(
|
||||
'.markdown-section img:not(.emoji):not([data-no-zoom])'
|
||||
)
|
||||
'.markdown-section img:not(.emoji):not([data-no-zoom])',
|
||||
),
|
||||
);
|
||||
|
||||
elms = elms.filter(elm => !elm.matches('a img'));
|
||||
|
@ -110,7 +110,7 @@ beforeEach(async () => {
|
||||
// Mock IntersectionObserver
|
||||
// -----------------------------------------------------------------------------
|
||||
[global, window].forEach(
|
||||
obj => (obj.IntersectionObserver = IntersectionObserver)
|
||||
obj => (obj.IntersectionObserver = IntersectionObserver),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -17,7 +17,7 @@ export async function startServer() {
|
||||
// problematic for testing and CI/CD.
|
||||
if (bsServer.getOption('port') !== settings.port) {
|
||||
console.log(
|
||||
`\nPort ${settings.port} not available. Exiting process.\n`
|
||||
`\nPort ${settings.port} not available. Exiting process.\n`,
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ['plugin:playwright/playwright-test'],
|
||||
};
|
@ -21,7 +21,7 @@ test.describe('Creating a Docsify site (e2e tests in Playwright)', () => {
|
||||
await page.addScriptTag({ url: '/dist/docsify.js' });
|
||||
|
||||
// Wait for docsify to initialize
|
||||
await page.waitForSelector('#main');
|
||||
await page.locator('#main').waitFor();
|
||||
|
||||
// Create handle for JavaScript object in browser
|
||||
const $docsify = await page.evaluate(() => window.$docsify);
|
||||
@ -42,7 +42,7 @@ test.describe('Creating a Docsify site (e2e tests in Playwright)', () => {
|
||||
const mainElm = page.locator('#main');
|
||||
await expect(mainElm).toHaveCount(1);
|
||||
await expect(mainElm).toContainText(
|
||||
'A magical documentation site generator'
|
||||
'A magical documentation site generator',
|
||||
);
|
||||
});
|
||||
|
||||
@ -128,11 +128,14 @@ test.describe('Creating a Docsify site (e2e tests in Playwright)', () => {
|
||||
|
||||
// Verify docsifyInitConfig.script was added to the DOM
|
||||
expect(
|
||||
await page.evaluate(scriptText => {
|
||||
return [...document.querySelectorAll('script')].some(
|
||||
elm => elm.textContent.replace(/\s+/g, '') === scriptText
|
||||
);
|
||||
}, docsifyInitConfig.script.replace(/\s+/g, ''))
|
||||
await page.evaluate(
|
||||
scriptText => {
|
||||
return [...document.querySelectorAll('script')].some(
|
||||
elm => elm.textContent.replace(/\s+/g, '') === scriptText,
|
||||
);
|
||||
},
|
||||
docsifyInitConfig.script.replace(/\s+/g, ''),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
// Verify docsifyInitConfig.script was executed
|
||||
@ -141,17 +144,20 @@ test.describe('Creating a Docsify site (e2e tests in Playwright)', () => {
|
||||
// Verify docsifyInitConfig.styleURLs were added to the DOM
|
||||
for (const styleURL of docsifyInitConfig.styleURLs) {
|
||||
await expect(
|
||||
page.locator(`link[rel*="stylesheet"][href$="${styleURL}"]`)
|
||||
page.locator(`link[rel*="stylesheet"][href$="${styleURL}"]`),
|
||||
).toHaveCount(1);
|
||||
}
|
||||
|
||||
// Verify docsifyInitConfig.style was added to the DOM
|
||||
expect(
|
||||
await page.evaluate(styleText => {
|
||||
return [...document.querySelectorAll('style')].some(
|
||||
elm => elm.textContent.replace(/\s+/g, '') === styleText
|
||||
);
|
||||
}, docsifyInitConfig.style.replace(/\s+/g, ''))
|
||||
await page.evaluate(
|
||||
styleText => {
|
||||
return [...document.querySelectorAll('style')].some(
|
||||
elm => elm.textContent.replace(/\s+/g, '') === styleText,
|
||||
);
|
||||
},
|
||||
docsifyInitConfig.style.replace(/\s+/g, ''),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
// Verify docsify navigation and docsifyInitConfig.routes
|
||||
|
@ -12,7 +12,7 @@ test.describe('Index file hosting', () => {
|
||||
test('should serve from index file', async ({ page }) => {
|
||||
await docsifyInit(sharedOptions);
|
||||
await expect(page.locator('#main')).toContainText(
|
||||
'A magical documentation site generator'
|
||||
'A magical documentation site generator',
|
||||
);
|
||||
expect(page.url()).toMatch(/index\.html#\/$/);
|
||||
});
|
||||
|
@ -12,7 +12,7 @@ test.describe('Security - Cross Site Scripting (XSS)', () => {
|
||||
};
|
||||
const slashStrings = ['//', '///'];
|
||||
|
||||
for (let slashString of slashStrings) {
|
||||
for (const slashString of slashStrings) {
|
||||
const hash = `#${slashString}domain.com/file.md`;
|
||||
|
||||
test(`should not load remote content from hash (${hash})`, async ({
|
||||
|
@ -8,9 +8,7 @@ import { test, expect } from './fixtures/docsify-init-fixture.js';
|
||||
*/
|
||||
async function navigateToRoute(page, route) {
|
||||
await page.evaluate(r => (window.location.hash = r), route);
|
||||
// TODO: playwright eslint now recommends not using networkidle
|
||||
// eslint-disable-next-line
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('load');
|
||||
}
|
||||
|
||||
test.describe('Virtual Routes - Generate Dynamic Content via Config', () => {
|
||||
@ -60,7 +58,7 @@ test.describe('Virtual Routes - Generate Dynamic Content via Config', () => {
|
||||
'/my-awesome-async-function-route': async function (
|
||||
route,
|
||||
matched,
|
||||
next
|
||||
next,
|
||||
) {
|
||||
setTimeout(() => next('# My Awesome Function Route'), 100);
|
||||
},
|
||||
@ -101,7 +99,7 @@ test.describe('Virtual Routes - Generate Dynamic Content via Config', () => {
|
||||
page,
|
||||
}) => {
|
||||
const routes = {
|
||||
'/pets/(.*)': route => `# Route: /pets/dog`,
|
||||
'/pets/(.*)': route => '# Route: /pets/dog',
|
||||
};
|
||||
|
||||
await docsifyInit({
|
||||
|
@ -83,7 +83,7 @@ test.describe('Vue.js Compatibility', () => {
|
||||
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------------
|
||||
test(`Parse templates and render content when import Vue resources`, async ({
|
||||
test('Parse templates and render content when import Vue resources', async ({
|
||||
page,
|
||||
}) => {
|
||||
const docsifyInitConfig = {
|
||||
@ -116,7 +116,7 @@ test.describe('Vue.js Compatibility', () => {
|
||||
await expect(page.locator('#vuefor')).toHaveText('12345');
|
||||
await expect(page.locator('#vuecomponent')).toHaveText('0');
|
||||
await expect(page.locator('#vueglobaloptions p')).toHaveText(
|
||||
'vueglobaloptions'
|
||||
'vueglobaloptions',
|
||||
);
|
||||
await expect(page.locator('#vueglobaloptions > span')).toHaveText('0');
|
||||
await expect(page.locator('#vuemounts p')).toHaveText('vuemounts');
|
||||
@ -136,7 +136,7 @@ test.describe('Vue.js Compatibility', () => {
|
||||
});
|
||||
}
|
||||
|
||||
test(`ignores content when Vue is not present`, async ({ page }) => {
|
||||
test('ignores content when Vue is not present', async ({ page }) => {
|
||||
const docsifyInitConfig = getSharedConfig();
|
||||
|
||||
await docsifyInit(docsifyInitConfig);
|
||||
@ -148,7 +148,7 @@ test.describe('Vue.js Compatibility', () => {
|
||||
await expect(page.locator('#vuescript p')).toHaveText('---');
|
||||
});
|
||||
|
||||
test(`ignores content when vueGlobalOptions is undefined`, async ({
|
||||
test('ignores content when vueGlobalOptions is undefined', async ({
|
||||
page,
|
||||
}) => {
|
||||
const docsifyInitConfig = getSharedConfig();
|
||||
@ -166,7 +166,7 @@ test.describe('Vue.js Compatibility', () => {
|
||||
await expect(page.locator('#vuescript p')).toHaveText('vuescript');
|
||||
});
|
||||
|
||||
test(`ignores content when vueMounts is undefined`, async ({ page }) => {
|
||||
test('ignores content when vueMounts is undefined', async ({ page }) => {
|
||||
const docsifyInitConfig = getSharedConfig();
|
||||
|
||||
docsifyInitConfig.config.vueMounts['#vuemounts'] = undefined;
|
||||
@ -176,13 +176,13 @@ test.describe('Vue.js Compatibility', () => {
|
||||
await expect(page.locator('#vuefor')).toHaveText('12345');
|
||||
await expect(page.locator('#vuecomponent')).toHaveText('0');
|
||||
await expect(page.locator('#vueglobaloptions p')).toHaveText(
|
||||
'vueglobaloptions'
|
||||
'vueglobaloptions',
|
||||
);
|
||||
await expect(page.locator('#vuemounts p')).toHaveText('vueglobaloptions');
|
||||
await expect(page.locator('#vuescript p')).toHaveText('vuescript');
|
||||
});
|
||||
|
||||
test(`ignores <script> when executeScript is false`, async ({ page }) => {
|
||||
test('ignores <script> when executeScript is false', async ({ page }) => {
|
||||
const docsifyInitConfig = getSharedConfig();
|
||||
|
||||
docsifyInitConfig.config.executeScript = false;
|
||||
|
@ -65,7 +65,7 @@ async function docsifyInit(options = {}) {
|
||||
style: '',
|
||||
styleURLs: [],
|
||||
testURL: `${process.env.TEST_HOST}/docsify-init.html`,
|
||||
waitForSelector: '#main > *',
|
||||
waitForSelector: '#main > *:first-child',
|
||||
};
|
||||
const settings = {
|
||||
...defaults,
|
||||
@ -83,7 +83,7 @@ async function docsifyInit(options = {}) {
|
||||
if (config.basePath) {
|
||||
config.basePath = new URL(
|
||||
config.basePath,
|
||||
process.env.TEST_HOST
|
||||
process.env.TEST_HOST,
|
||||
).href;
|
||||
}
|
||||
};
|
||||
@ -114,16 +114,16 @@ async function docsifyInit(options = {}) {
|
||||
...options.markdown,
|
||||
})
|
||||
.filter(([key, markdown]) => key && markdown)
|
||||
.map(([key, markdown]) => [key, stripIndent`${markdown}`])
|
||||
.map(([key, markdown]) => [key, stripIndent`${markdown}`]),
|
||||
);
|
||||
},
|
||||
get routes() {
|
||||
const helperRoutes = {
|
||||
[settings.testURL]: stripIndent`${settings.html}`,
|
||||
['README.md']: settings.markdown.homepage,
|
||||
['_coverpage.md']: settings.markdown.coverpage,
|
||||
['_navbar.md']: settings.markdown.navbar,
|
||||
['_sidebar.md']: settings.markdown.sidebar,
|
||||
'README.md': settings.markdown.homepage,
|
||||
'_coverpage.md': settings.markdown.coverpage,
|
||||
'_navbar.md': settings.markdown.navbar,
|
||||
'_sidebar.md': settings.markdown.sidebar,
|
||||
};
|
||||
|
||||
const finalRoutes = Object.fromEntries(
|
||||
@ -139,7 +139,7 @@ async function docsifyInit(options = {}) {
|
||||
.href,
|
||||
// Strip indentation from responseText
|
||||
stripIndent`${responseText}`,
|
||||
])
|
||||
]),
|
||||
);
|
||||
|
||||
return finalRoutes;
|
||||
@ -165,7 +165,7 @@ async function docsifyInit(options = {}) {
|
||||
};
|
||||
const reFileExtentionFromURL = new RegExp(
|
||||
'(?:.)(' + Object.keys(contentTypes).join('|') + ')(?:[?#].*)?$',
|
||||
'i'
|
||||
'i',
|
||||
);
|
||||
|
||||
if (isJSDOM) {
|
||||
@ -219,7 +219,7 @@ async function docsifyInit(options = {}) {
|
||||
} else if (isPlaywright) {
|
||||
// Convert config functions to strings
|
||||
const configString = JSON.stringify(settings.config, (key, val) =>
|
||||
typeof val === 'function' ? `__FN__${val.toString()}` : val
|
||||
typeof val === 'function' ? `__FN__${val.toString()}` : val,
|
||||
);
|
||||
|
||||
await page.evaluate(config => {
|
||||
@ -303,13 +303,16 @@ async function docsifyInit(options = {}) {
|
||||
styleElm.textContent = stripIndent`${settings.style}`;
|
||||
headElm.appendChild(styleElm);
|
||||
} else if (isPlaywright) {
|
||||
await page.evaluate(data => {
|
||||
const headElm = document.querySelector('head');
|
||||
const styleElm = document.createElement('style');
|
||||
await page.evaluate(
|
||||
data => {
|
||||
const headElm = document.querySelector('head');
|
||||
const styleElm = document.createElement('style');
|
||||
|
||||
styleElm.textContent = data;
|
||||
headElm.appendChild(styleElm);
|
||||
}, stripIndent`${settings.style}`);
|
||||
styleElm.textContent = data;
|
||||
headElm.appendChild(styleElm);
|
||||
},
|
||||
stripIndent`${settings.style}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,8 +332,9 @@ async function docsifyInit(options = {}) {
|
||||
if (isJSDOM) {
|
||||
await waitForSelector(settings.waitForSelector);
|
||||
} else if (isPlaywright) {
|
||||
await page.waitForSelector(settings.waitForSelector);
|
||||
await page.waitForLoadState('networkidle');
|
||||
// await page.waitForSelector(settings.waitForSelector);
|
||||
await page.locator(settings.waitForSelector).waitFor();
|
||||
await page.waitForLoadState('load');
|
||||
}
|
||||
|
||||
// Log HTML to console
|
||||
@ -345,11 +349,11 @@ async function docsifyInit(options = {}) {
|
||||
if (selector) {
|
||||
if (isJSDOM) {
|
||||
htmlArr = [...document.querySelectorAll(selector)].map(
|
||||
elm => elm.outerHTML
|
||||
elm => elm.outerHTML,
|
||||
);
|
||||
} else {
|
||||
htmlArr = await page.evaluateAll(selector, elms =>
|
||||
elms.map(e => e.outerHTML)
|
||||
elms.map(e => e.outerHTML),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@ -364,11 +368,9 @@ async function docsifyInit(options = {}) {
|
||||
html = prettier.format(html, { parser: 'html' });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(html);
|
||||
});
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`docsify-init(): unable to match selector '${selector}'`);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,10 @@ const defaults = {
|
||||
* @param {Function} fn function to be evaluated until truthy
|
||||
* @param {*} arg optional argument to pass to `fn`
|
||||
* @param {Object} options optional parameters
|
||||
* @returns {Promise} promise which resolves to function result
|
||||
* @param {number} options.delay delay between fn invocations
|
||||
* @param {number} options.timeout timeout in milliseconds
|
||||
* @returns {Promise} promise which resolves to the truthy fn return value or
|
||||
* rejects to an error object or last non-truthy fn return value
|
||||
*/
|
||||
function waitForFunction(fn, arg, options = {}) {
|
||||
const settings = {
|
||||
@ -19,14 +22,15 @@ function waitForFunction(fn, arg, options = {}) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let timeElapsed = 0;
|
||||
let lastError;
|
||||
|
||||
const int = setInterval(() => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = fn(arg);
|
||||
} catch (e) {
|
||||
// Continue...
|
||||
} catch (err) {
|
||||
lastError = err;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
@ -37,11 +41,10 @@ function waitForFunction(fn, arg, options = {}) {
|
||||
timeElapsed += settings.delay;
|
||||
|
||||
if (timeElapsed >= settings.timeout) {
|
||||
const msg = `waitForFunction did not return a truthy value (${
|
||||
settings.timeout
|
||||
} ms): ${fn.toString()}\n`;
|
||||
|
||||
reject(msg);
|
||||
console.error(
|
||||
`\nwaitForFunction did not return a truthy value within ${settings.timeout} ms.\n`,
|
||||
);
|
||||
reject(lastError || result);
|
||||
}
|
||||
}, settings.delay);
|
||||
});
|
||||
@ -52,6 +55,8 @@ function waitForFunction(fn, arg, options = {}) {
|
||||
*
|
||||
* @param {String} cssSelector CSS selector to query for
|
||||
* @param {Object} options optional parameters
|
||||
* @param {number} options.delay delay between checks
|
||||
* @param {number} options.timeout timeout in milliseconds
|
||||
* @returns {Promise} promise which resolves to first matching element
|
||||
*/
|
||||
function waitForSelector(cssSelector, options = {}) {
|
||||
@ -87,6 +92,8 @@ function waitForSelector(cssSelector, options = {}) {
|
||||
* @param {String} cssSelector CSS selector to query for
|
||||
* @param {String} text text to match
|
||||
* @param {Object} options optional parameters
|
||||
* @param {number} options.delay delay between checks
|
||||
* @param {number} options.timeout timeout in milliseconds
|
||||
* @returns {Promise} promise which resolves to first matching element that contains specified text
|
||||
*/
|
||||
function waitForText(cssSelector, text, options = {}) {
|
||||
|
@ -1 +0,0 @@
|
||||
module.exports = require('../unit/.eslintrc.cjs');
|
@ -137,7 +137,8 @@ describe('Emoji', function () {
|
||||
test('Ignores emoji shorthand codes in html attributes', async () => {
|
||||
await docsifyInit({
|
||||
markdown: {
|
||||
homepage: /* html */ `<a href="http://domain.com/:smile:/"> <img src='http://domain.com/:smile:/file.png'> <script src=http://domain.com/:smile:/file.js></script>`,
|
||||
homepage:
|
||||
/* html */ '<a href="http://domain.com/:smile:/"> <img src=\'http://domain.com/:smile:/file.png\'> <script src=http://domain.com/:smile:/file.js></script>',
|
||||
},
|
||||
// _logHTML: true,
|
||||
});
|
||||
@ -150,7 +151,8 @@ describe('Emoji', function () {
|
||||
test('Ignores emoji shorthand codes in style url() values', async () => {
|
||||
await docsifyInit({
|
||||
markdown: {
|
||||
homepage: /* html */ `<style>@import url(http://domain.com/:smile/file.css);</style>`,
|
||||
homepage:
|
||||
/* html */ '<style>@import url(http://domain.com/:smile/file.css);</style>',
|
||||
},
|
||||
// _logHTML: true,
|
||||
});
|
||||
|
@ -12,7 +12,7 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
|
||||
|
||||
// Verify options.markdown content was rendered
|
||||
expect(document.querySelector('#main').textContent).toContain(
|
||||
'A magical documentation site generator'
|
||||
'A magical documentation site generator',
|
||||
);
|
||||
});
|
||||
|
||||
@ -78,7 +78,7 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
|
||||
expect(typeof window.$docsify).toBe('object');
|
||||
expect(window.$docsify).toHaveProperty('themeColor', 'red');
|
||||
expect(document.querySelector('.app-name').textContent).toContain(
|
||||
'Docsify Name'
|
||||
'Docsify Name',
|
||||
);
|
||||
|
||||
// Verify docsifyInitConfig.markdown content was rendered
|
||||
@ -94,7 +94,7 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
|
||||
// Verify docsifyInitConfig.scriptURLs were added to the DOM
|
||||
for (const scriptURL of docsifyInitConfig.scriptURLs) {
|
||||
const matchElm = document.querySelector(
|
||||
`script[data-src$="${scriptURL}"]`
|
||||
`script[data-src$="${scriptURL}"]`,
|
||||
);
|
||||
expect(matchElm).toBeTruthy();
|
||||
}
|
||||
@ -108,8 +108,8 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
|
||||
[...document.querySelectorAll('script')].some(
|
||||
elm =>
|
||||
elm.textContent.replace(/\s+/g, '') ===
|
||||
docsifyInitConfig.script.replace(/\s+/g, '')
|
||||
)
|
||||
docsifyInitConfig.script.replace(/\s+/g, ''),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
// Verify docsifyInitConfig.script was executed
|
||||
@ -118,7 +118,7 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
|
||||
// Verify docsifyInitConfig.styleURLs were added to the DOM
|
||||
for (const styleURL of docsifyInitConfig.styleURLs) {
|
||||
const matchElm = document.querySelector(
|
||||
`link[rel*="stylesheet"][href$="${styleURL}"]`
|
||||
`link[rel*="stylesheet"][href$="${styleURL}"]`,
|
||||
);
|
||||
expect(matchElm).toBeTruthy();
|
||||
}
|
||||
@ -128,14 +128,14 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
|
||||
[...document.querySelectorAll('style')].some(
|
||||
elm =>
|
||||
elm.textContent.replace(/\s+/g, '') ===
|
||||
docsifyInitConfig.style.replace(/\s+/g, '')
|
||||
)
|
||||
docsifyInitConfig.style.replace(/\s+/g, ''),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
// Verify docsify navigation and docsifyInitConfig.routes
|
||||
document.querySelector('a[href="#/test"]').click();
|
||||
expect(
|
||||
await waitForFunction(() => /#\/test$/.test(window.location.href))
|
||||
await waitForFunction(() => /#\/test$/.test(window.location.href)),
|
||||
).toBeTruthy();
|
||||
expect(await waitForText('#main', 'This is a custom route')).toBeTruthy();
|
||||
});
|
||||
|
@ -16,14 +16,14 @@ describe('render', function () {
|
||||
const output = window.marked('!> Important content');
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p class="tip">Important content</p>"`
|
||||
'"<p class="tip">Important content</p>"',
|
||||
);
|
||||
});
|
||||
|
||||
test('general tip', () => {
|
||||
const output = window.marked('?> General tip');
|
||||
|
||||
expect(output).toMatchInlineSnapshot(`"<p class="warn">General tip</p>"`);
|
||||
expect(output).toMatchInlineSnapshot('"<p class="warn">General tip</p>"');
|
||||
});
|
||||
});
|
||||
|
||||
@ -42,7 +42,7 @@ describe('render', function () {
|
||||
`);
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<ul class="task-list"><li class="task-list-item"><label><input checked="" disabled="" type="checkbox"> Task 1</label></li><li class="task-list-item"><label><input disabled="" type="checkbox"> Task 2</label></li><li class="task-list-item"><label><input disabled="" type="checkbox"> Task 3</label></li></ul>"`
|
||||
'"<ul class="task-list"><li class="task-list-item"><label><input checked="" disabled="" type="checkbox"> Task 1</label></li><li class="task-list-item"><label><input disabled="" type="checkbox"> Task 2</label></li><li class="task-list-item"><label><input disabled="" type="checkbox"> Task 3</label></li></ul>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -53,7 +53,7 @@ describe('render', function () {
|
||||
`);
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<ol class="task-list"><li class="task-list-item"><label><input disabled="" type="checkbox"> Task 1</label></li><li class="task-list-item"><label><input checked="" disabled="" type="checkbox"> Task 2</label></li></ol>"`
|
||||
'"<ol class="task-list"><li class="task-list-item"><label><input disabled="" type="checkbox"> Task 1</label></li><li class="task-list-item"><label><input checked="" disabled="" type="checkbox"> Task 2</label></li></ol>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -64,7 +64,7 @@ describe('render', function () {
|
||||
`);
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<ul ><li><a href="#/link" >linktext</a></li><li>just text</li></ul>"`
|
||||
'"<ul ><li><a href="#/link" >linktext</a></li><li>just text</li></ul>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -79,7 +79,7 @@ describe('render', function () {
|
||||
`);
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<ol ><li>first</li><li>second</li></ol><p>text</p><ol start="3"><li>third</li></ol>"`
|
||||
'"<ol ><li>first</li><li>second</li></ol><p>text</p><ol start="3"><li>third</li></ol>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -93,7 +93,7 @@ describe('render', function () {
|
||||
`);
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<ul ><li>1</li><li>2<ul ><li>2 a</li><li>2 b</li></ul></li><li>3</li></ul>"`
|
||||
'"<ul ><li>1</li><li>2<ul ><li>2 a</li><li>2 b</li></ul></li><li>3</li></ul>"',
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -109,27 +109,27 @@ describe('render', function () {
|
||||
const output = window.marked('![alt text](http://imageUrl)');
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" /></p>"`
|
||||
'"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" /></p>"',
|
||||
);
|
||||
});
|
||||
|
||||
test('class', async function () {
|
||||
const output = window.marked(
|
||||
"![alt text](http://imageUrl ':class=someCssClass')"
|
||||
"![alt text](http://imageUrl ':class=someCssClass')",
|
||||
);
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" class="someCssClass" /></p>"`
|
||||
'"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" class="someCssClass" /></p>"',
|
||||
);
|
||||
});
|
||||
|
||||
test('id', async function () {
|
||||
const output = window.marked(
|
||||
"![alt text](http://imageUrl ':id=someCssID')"
|
||||
"![alt text](http://imageUrl ':id=someCssID')",
|
||||
);
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" id="someCssID" /></p>"`
|
||||
'"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" id="someCssID" /></p>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -137,17 +137,17 @@ describe('render', function () {
|
||||
const output = window.marked("![alt text](http://imageUrl ':no-zoom')");
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" data-no-zoom /></p>"`
|
||||
'"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" data-no-zoom /></p>"',
|
||||
);
|
||||
});
|
||||
|
||||
test('width and height', async function () {
|
||||
const output = window.marked(
|
||||
"![alt text](http://imageUrl ':size=WIDTHxHEIGHT')"
|
||||
"![alt text](http://imageUrl ':size=WIDTHxHEIGHT')",
|
||||
);
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" width="WIDTH" height="HEIGHT" /></p>"`
|
||||
'"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" width="WIDTH" height="HEIGHT" /></p>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -155,7 +155,7 @@ describe('render', function () {
|
||||
const output = window.marked("![alt text](http://imageUrl ':size=50')");
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" width="50" /></p>"`
|
||||
'"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" width="50" /></p>"',
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -171,7 +171,7 @@ describe('render', function () {
|
||||
const output = window.marked('# h1 tag');
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<h1 id="h1-tag" tabindex="-1"><a href="#/?id=h1-tag" data-id="h1-tag" class="anchor"><span>h1 tag</span></a></h1>"`
|
||||
'"<h1 id="h1-tag" tabindex="-1"><a href="#/?id=h1-tag" data-id="h1-tag" class="anchor"><span>h1 tag</span></a></h1>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -179,7 +179,7 @@ describe('render', function () {
|
||||
const output = window.marked('## h2 tag');
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<h2 id="h2-tag" tabindex="-1"><a href="#/?id=h2-tag" data-id="h2-tag" class="anchor"><span>h2 tag</span></a></h2>"`
|
||||
'"<h2 id="h2-tag" tabindex="-1"><a href="#/?id=h2-tag" data-id="h2-tag" class="anchor"><span>h2 tag</span></a></h2>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -187,7 +187,7 @@ describe('render', function () {
|
||||
const output = window.marked('### h3 tag');
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<h3 id="h3-tag" tabindex="-1"><a href="#/?id=h3-tag" data-id="h3-tag" class="anchor"><span>h3 tag</span></a></h3>"`
|
||||
'"<h3 id="h3-tag" tabindex="-1"><a href="#/?id=h3-tag" data-id="h3-tag" class="anchor"><span>h3 tag</span></a></h3>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -195,7 +195,7 @@ describe('render', function () {
|
||||
const output = window.marked('#### h4 tag');
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<h4 id="h4-tag" tabindex="-1"><a href="#/?id=h4-tag" data-id="h4-tag" class="anchor"><span>h4 tag</span></a></h4>"`
|
||||
'"<h4 id="h4-tag" tabindex="-1"><a href="#/?id=h4-tag" data-id="h4-tag" class="anchor"><span>h4 tag</span></a></h4>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -203,7 +203,7 @@ describe('render', function () {
|
||||
const output = window.marked('##### h5 tag');
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<h5 id="h5-tag" tabindex="-1"><a href="#/?id=h5-tag" data-id="h5-tag" class="anchor"><span>h5 tag</span></a></h5>"`
|
||||
'"<h5 id="h5-tag" tabindex="-1"><a href="#/?id=h5-tag" data-id="h5-tag" class="anchor"><span>h5 tag</span></a></h5>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -211,7 +211,7 @@ describe('render', function () {
|
||||
const output = window.marked('###### h6 tag');
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<h6 id="h6-tag" tabindex="-1"><a href="#/?id=h6-tag" data-id="h6-tag" class="anchor"><span>h6 tag</span></a></h6>"`
|
||||
'"<h6 id="h6-tag" tabindex="-1"><a href="#/?id=h6-tag" data-id="h6-tag" class="anchor"><span>h6 tag</span></a></h6>"',
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -227,7 +227,7 @@ describe('render', function () {
|
||||
const output = window.marked('[alt text](http://url)');
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><a href="http://url" target="_blank" rel="noopener">alt text</a></p>"`
|
||||
'"<p><a href="http://url" target="_blank" rel="noopener">alt text</a></p>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -239,7 +239,7 @@ describe('render', function () {
|
||||
const output = window.marked('[alt text](http://www.example.com)');
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><a href="http://www.example.com" target="_blank" rel="noopener">alt text</a></p>"`
|
||||
'"<p><a href="http://www.example.com" target="_blank" rel="noopener">alt text</a></p>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -247,7 +247,7 @@ describe('render', function () {
|
||||
const output = window.marked("[alt text](http://url ':disabled')");
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><a href="javascript:void(0)" target="_blank" rel="noopener" disabled>alt text</a></p>"`
|
||||
'"<p><a href="javascript:void(0)" target="_blank" rel="noopener" disabled>alt text</a></p>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -255,7 +255,7 @@ describe('render', function () {
|
||||
const output = window.marked("[alt text](http://url ':target=_self')");
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><a href="http://url" target="_self" >alt text</a></p>"`
|
||||
'"<p><a href="http://url" target="_self" >alt text</a></p>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -263,17 +263,17 @@ describe('render', function () {
|
||||
const output = window.marked("[alt text](/url ':target=_blank')");
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><a href="#/url" target="_blank">alt text</a></p>"`
|
||||
'"<p><a href="#/url" target="_blank">alt text</a></p>"',
|
||||
);
|
||||
});
|
||||
|
||||
test('class', async function () {
|
||||
const output = window.marked(
|
||||
"[alt text](http://url ':class=someCssClass')"
|
||||
"[alt text](http://url ':class=someCssClass')",
|
||||
);
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><a href="http://url" target="_blank" rel="noopener" class="someCssClass">alt text</a></p>"`
|
||||
'"<p><a href="http://url" target="_blank" rel="noopener" class="someCssClass">alt text</a></p>"',
|
||||
);
|
||||
});
|
||||
|
||||
@ -281,7 +281,7 @@ describe('render', function () {
|
||||
const output = window.marked("[alt text](http://url ':id=someCssID')");
|
||||
|
||||
expect(output).toMatchInlineSnapshot(
|
||||
`"<p><a href="http://url" target="_blank" rel="noopener" id="someCssID">alt text</a></p>"`
|
||||
'"<p><a href="http://url" target="_blank" rel="noopener" id="someCssID">alt text</a></p>"',
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -297,7 +297,7 @@ describe('render', function () {
|
||||
|
||||
expect(elm.textContent).toBe(expectText);
|
||||
expect(elm.outerHTML).toMatchInlineSnapshot(
|
||||
`"<button id="skip-to-content">Skip to main content</button>"`
|
||||
'"<button id="skip-to-content">Skip to main content</button>"',
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
'jest/globals': true,
|
||||
},
|
||||
extends: ['plugin:jest/recommended', 'plugin:jest/style'],
|
||||
plugins: ['jest'],
|
||||
};
|
@ -27,7 +27,7 @@ describe('core/util', () => {
|
||||
|
||||
test('non external local url with more /', () => {
|
||||
const result = isExternal(
|
||||
`//////////////////${location.host}/docsify/demo.md`
|
||||
`//////////////////${location.host}/docsify/demo.md`,
|
||||
);
|
||||
|
||||
expect(result).toBeFalsy();
|
||||
@ -54,7 +54,7 @@ describe('core/util', () => {
|
||||
|
||||
test('external url with more /', () => {
|
||||
const result = isExternal(
|
||||
'//////////////////example.github.io/docsify/demo.md'
|
||||
'//////////////////example.github.io/docsify/demo.md',
|
||||
);
|
||||
|
||||
expect(result).toBeTruthy();
|
||||
|
@ -25,7 +25,7 @@ describe('core/render/utils', () => {
|
||||
test('getAndRemoveDocisfyIgnorConfig from <!-- {docsify-ignore} -->', () => {
|
||||
const { content, ignoreAllSubs, ignoreSubHeading } =
|
||||
getAndRemoveDocisfyIgnoreConfig(
|
||||
'My Ignore Title<!-- {docsify-ignore} -->'
|
||||
'My Ignore Title<!-- {docsify-ignore} -->',
|
||||
);
|
||||
expect(content).toBe('My Ignore Title');
|
||||
expect(ignoreSubHeading).toBeTruthy();
|
||||
@ -35,7 +35,7 @@ describe('core/render/utils', () => {
|
||||
test('getAndRemoveDocisfyIgnorConfig from <!-- {docsify-ignore-all} -->', () => {
|
||||
const { content, ignoreAllSubs, ignoreSubHeading } =
|
||||
getAndRemoveDocisfyIgnoreConfig(
|
||||
'My Ignore Title<!-- {docsify-ignore-all} -->'
|
||||
'My Ignore Title<!-- {docsify-ignore-all} -->',
|
||||
);
|
||||
expect(content).toBe('My Ignore Title');
|
||||
expect(ignoreAllSubs).toBeTruthy();
|
||||
@ -64,18 +64,18 @@ describe('core/render/utils', () => {
|
||||
describe('getAndRemoveConfig()', () => {
|
||||
test('parse simple config', () => {
|
||||
const result = getAndRemoveConfig(
|
||||
`[filename](_media/example.md ':include')`
|
||||
"[filename](_media/example.md ':include')",
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
config: {},
|
||||
str: `[filename](_media/example.md ':include')`,
|
||||
str: "[filename](_media/example.md ':include')",
|
||||
});
|
||||
});
|
||||
|
||||
test('parse config with arguments', () => {
|
||||
const result = getAndRemoveConfig(
|
||||
`[filename](_media/example.md ':include :foo=bar :baz test')`
|
||||
"[filename](_media/example.md ':include :foo=bar :baz test')",
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
@ -83,18 +83,18 @@ describe('core/render/utils', () => {
|
||||
foo: 'bar',
|
||||
baz: true,
|
||||
},
|
||||
str: `[filename](_media/example.md ':include test')`,
|
||||
str: "[filename](_media/example.md ':include test')",
|
||||
});
|
||||
});
|
||||
|
||||
test('parse config with double quotes', () => {
|
||||
const result = getAndRemoveConfig(
|
||||
`[filename](_media/example.md ":include")`
|
||||
'[filename](_media/example.md ":include")',
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
config: {},
|
||||
str: `[filename](_media/example.md ":include")`,
|
||||
str: '[filename](_media/example.md ":include")',
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -122,7 +122,7 @@ describe('core/render/tpl', () => {
|
||||
]);
|
||||
|
||||
expect(result).toBe(
|
||||
/* html */ `<ul class="app-sub-sidebar"><li><a class="section-link" href="#/cover?id=basic-usage" title="Basic usage"><span style="color:red">Basic usage</span></a></li><li><a class="section-link" href="#/cover?id=custom-background" title="Custom background">Custom background</a></li><li><a class="section-link" href="#/cover?id=test" title="Test"><img src="/docs/_media/favicon.ico" data-origin="/_media/favicon.ico" alt="ico">Test</a></li></ul>`
|
||||
/* html */ '<ul class="app-sub-sidebar"><li><a class="section-link" href="#/cover?id=basic-usage" title="Basic usage"><span style="color:red">Basic usage</span></a></li><li><a class="section-link" href="#/cover?id=custom-background" title="Custom background">Custom background</a></li><li><a class="section-link" href="#/cover?id=test" title="Test"><img src="/docs/_media/favicon.ico" data-origin="/_media/favicon.ico" alt="ico">Test</a></li></ul>',
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -130,12 +130,12 @@ describe('core/render/tpl', () => {
|
||||
describe('core/render/slugify', () => {
|
||||
test('slugify()', () => {
|
||||
const result = slugify(
|
||||
`Bla bla bla <svg aria-label="broken" class="broken" viewPort="0 0 1 1"><circle cx="0.5" cy="0.5"/></svg>`
|
||||
'Bla bla bla <svg aria-label="broken" class="broken" viewPort="0 0 1 1"><circle cx="0.5" cy="0.5"/></svg>',
|
||||
);
|
||||
const result2 = slugify(
|
||||
`Another <span style="font-size: 1.2em" class="foo bar baz">broken <span class="aaa">example</span></span>`
|
||||
'Another <span style="font-size: 1.2em" class="foo bar baz">broken <span class="aaa">example</span></span>',
|
||||
);
|
||||
expect(result).toBe(`bla-bla-bla-`);
|
||||
expect(result2).toBe(`another-broken-example`);
|
||||
expect(result).toBe('bla-bla-bla-');
|
||||
expect(result2).toBe('another-broken-example');
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user