mirror of
https://github.com/docsifyjs/docsify.git
synced 2025-12-08 19:55:52 +00:00
Merge pull request #1189 from docsifyjs/allow-config-function
Allow configs to be functions
This commit is contained in:
commit
da9af18c4e
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@ -0,0 +1,11 @@
|
||||
# http://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
126
build/build.js
126
build/build.js
@ -9,8 +9,16 @@ const version = process.env.VERSION || require('../package.json').version
|
||||
const chokidar = require('chokidar')
|
||||
const path = require('path')
|
||||
|
||||
const build = function (opts) {
|
||||
rollup
|
||||
/**
|
||||
* @param {{
|
||||
* input: string,
|
||||
* output?: string,
|
||||
* globalName?: string,
|
||||
* plugins?: Array<import('rollup').Plugin>
|
||||
* }} opts
|
||||
*/
|
||||
async function build(opts) {
|
||||
await rollup
|
||||
.rollup({
|
||||
input: opts.input,
|
||||
plugins: (opts.plugins || []).concat([
|
||||
@ -27,31 +35,35 @@ const build = function (opts) {
|
||||
var dest = 'lib/' + (opts.output || opts.input)
|
||||
|
||||
console.log(dest)
|
||||
bundle.write({
|
||||
return bundle.write({
|
||||
format: 'iife',
|
||||
output: opts.globalName ? {name: opts.globalName} : {},
|
||||
file: dest,
|
||||
strict: false
|
||||
})
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
const buildCore = function () {
|
||||
build({
|
||||
|
||||
async function buildCore() {
|
||||
const promises = []
|
||||
|
||||
promises.push(build({
|
||||
input: 'src/core/index.js',
|
||||
output: 'docsify.js'
|
||||
})
|
||||
output: 'docsify.js',
|
||||
}))
|
||||
|
||||
if (isProd) {
|
||||
build({
|
||||
promises.push(build({
|
||||
input: 'src/core/index.js',
|
||||
output: 'docsify.min.js',
|
||||
plugins: [uglify()]
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
}
|
||||
const buildAllPlugin = function () {
|
||||
|
||||
async function buildAllPlugin() {
|
||||
var plugins = [
|
||||
{name: 'search', input: 'search/index.js'},
|
||||
{name: 'ga', input: 'ga.js'},
|
||||
@ -64,8 +76,8 @@ const buildAllPlugin = function () {
|
||||
{name: 'gitalk', input: 'gitalk.js'}
|
||||
]
|
||||
|
||||
plugins.forEach(item => {
|
||||
build({
|
||||
const promises = plugins.map(item => {
|
||||
return build({
|
||||
input: 'src/plugins/' + item.input,
|
||||
output: 'plugins/' + item.name + '.js'
|
||||
})
|
||||
@ -73,47 +85,59 @@ const buildAllPlugin = function () {
|
||||
|
||||
if (isProd) {
|
||||
plugins.forEach(item => {
|
||||
build({
|
||||
promises.push(build({
|
||||
input: 'src/plugins/' + item.input,
|
||||
output: 'plugins/' + item.name + '.min.js',
|
||||
plugins: [uglify()]
|
||||
})
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (!isProd) {
|
||||
chokidar
|
||||
.watch(['src/core', 'src/plugins'], {
|
||||
atomic: true,
|
||||
awaitWriteFinish: {
|
||||
stabilityThreshold: 1000,
|
||||
pollInterval: 100
|
||||
}
|
||||
})
|
||||
.on('change', p => {
|
||||
console.log('[watch] ', p)
|
||||
const dirs = p.split(path.sep)
|
||||
if (dirs[1] === 'core') {
|
||||
buildCore()
|
||||
} else if (dirs[2]) {
|
||||
const name = path.basename(dirs[2], '.js')
|
||||
const input = `src/plugins/${name}${
|
||||
/\.js/.test(dirs[2]) ? '' : '/index'
|
||||
}.js`
|
||||
|
||||
build({
|
||||
input,
|
||||
output: 'plugins/' + name + '.js'
|
||||
})
|
||||
}
|
||||
})
|
||||
.on('ready', () => {
|
||||
console.log('[start]')
|
||||
buildCore()
|
||||
buildAllPlugin()
|
||||
})
|
||||
} else {
|
||||
await Promise.all([
|
||||
buildCore(),
|
||||
buildAllPlugin()
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
if (!isProd) {
|
||||
chokidar
|
||||
.watch(['src/core', 'src/plugins'], {
|
||||
atomic: true,
|
||||
awaitWriteFinish: {
|
||||
stabilityThreshold: 1000,
|
||||
pollInterval: 100
|
||||
}
|
||||
})
|
||||
.on('change', p => {
|
||||
console.log('[watch] ', p)
|
||||
const dirs = p.split(path.sep)
|
||||
if (dirs[1] === 'core') {
|
||||
buildCore()
|
||||
} else if (dirs[2]) {
|
||||
const name = path.basename(dirs[2], '.js')
|
||||
const input = `src/plugins/${name}${
|
||||
/\.js/.test(dirs[2]) ? '' : '/index'
|
||||
}.js`
|
||||
main().catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
build({
|
||||
input,
|
||||
output: 'plugins/' + name + '.js'
|
||||
})
|
||||
}
|
||||
})
|
||||
.on('ready', () => {
|
||||
console.log('[start]')
|
||||
buildCore()
|
||||
buildAllPlugin()
|
||||
})
|
||||
} else {
|
||||
buildCore()
|
||||
buildAllPlugin()
|
||||
}
|
||||
|
||||
14
build/css.js
14
build/css.js
@ -5,8 +5,8 @@ const {spawn} = require('child_process')
|
||||
const args = process.argv.slice(2)
|
||||
fs.readdir(path.join(__dirname, '../src/themes'), (err, files) => {
|
||||
if (err) {
|
||||
console.log('err', err)
|
||||
return
|
||||
console.error('err', err)
|
||||
process.exit(1)
|
||||
}
|
||||
files.map(async (file) => {
|
||||
if (/\.styl/g.test(file)) {
|
||||
@ -31,11 +31,17 @@ fs.readdir(path.join(__dirname, '../src/themes'), (err, files) => {
|
||||
});
|
||||
|
||||
stylusCMD.on('close', (code) => {
|
||||
console.log(`[Stylus Build ] child process exited with code ${code}`);
|
||||
const message = `[Stylus Build ] child process exited with code ${code}`
|
||||
|
||||
if (code !== 0) {
|
||||
console.error(message);
|
||||
process.exit(code)
|
||||
}
|
||||
console.log(message);
|
||||
});
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -8,5 +8,8 @@ files.forEach(file => {
|
||||
file = path.resolve('lib/themes', file)
|
||||
cssnano(fs.readFileSync(file)).then(result => {
|
||||
fs.writeFileSync(file, result.css)
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
|
||||
@ -24,11 +24,12 @@ rollup
|
||||
var dest = 'packages/docsify-server-renderer/build.js'
|
||||
|
||||
console.log(dest)
|
||||
bundle.write({
|
||||
return bundle.write({
|
||||
format: 'cjs',
|
||||
file: dest
|
||||
})
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Configuration
|
||||
|
||||
You can configure the `window.$docsify`.
|
||||
You can configure Docsify by defining `window.$docsify` as an object:
|
||||
|
||||
```html
|
||||
<script>
|
||||
@ -12,6 +12,24 @@ You can configure the `window.$docsify`.
|
||||
</script>
|
||||
```
|
||||
|
||||
The config can also be defined as a function, in which case the first arg is the Docsify `vm` instance. The function should return a config object. This can be useful for referencing `vm` in places like the markdown configuration:
|
||||
|
||||
```html
|
||||
<script>
|
||||
window.$docsify = function(vm) {
|
||||
return {
|
||||
markdown: {
|
||||
renderer: {
|
||||
code(code, lang) {
|
||||
// ... use `vm` ...
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
## el
|
||||
|
||||
- Type: `String`
|
||||
|
||||
1775
package-lock.json
generated
1775
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -81,7 +81,7 @@
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"esm": "^3.1.4",
|
||||
"husky": "^3.1.0",
|
||||
"jsdom": "^15.1.1",
|
||||
"jsdom": "^16.2.2",
|
||||
"lerna": "^3.17.0",
|
||||
"lint-staged": "^10.1.2",
|
||||
"live-server": "^1.2.1",
|
||||
@ -97,6 +97,7 @@
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-replace": "^2.2.0",
|
||||
"rollup-plugin-uglify": "^6.0.4",
|
||||
"serve-handler": "^6.1.2",
|
||||
"start-server-and-test": "^1.10.6",
|
||||
"stylus": "^0.54.5"
|
||||
},
|
||||
|
||||
23
src/core/Docsify.js
Normal file
23
src/core/Docsify.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { initMixin } from './init';
|
||||
import { routerMixin } from './router';
|
||||
import { renderMixin } from './render';
|
||||
import { fetchMixin } from './fetch';
|
||||
import { eventMixin } from './event';
|
||||
import initGlobalAPI from './global-api';
|
||||
|
||||
export function Docsify() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
const proto = Docsify.prototype;
|
||||
|
||||
initMixin(proto);
|
||||
routerMixin(proto);
|
||||
renderMixin(proto);
|
||||
fetchMixin(proto);
|
||||
eventMixin(proto);
|
||||
|
||||
/**
|
||||
* Global API
|
||||
*/
|
||||
initGlobalAPI();
|
||||
@ -2,7 +2,7 @@ import { merge, hyphenate, isPrimitive, hasOwn } from './util/core';
|
||||
|
||||
const currentScript = document.currentScript;
|
||||
|
||||
export default function() {
|
||||
export default function(vm) {
|
||||
const config = merge(
|
||||
{
|
||||
el: '#app',
|
||||
@ -36,7 +36,9 @@ export default function() {
|
||||
relativePath: false,
|
||||
topMargin: 0,
|
||||
},
|
||||
window.$docsify
|
||||
typeof window.$docsify === 'function'
|
||||
? window.$docsify(vm)
|
||||
: window.$docsify
|
||||
);
|
||||
|
||||
const script =
|
||||
|
||||
@ -6,6 +6,10 @@ import { Compiler } from './render/compiler';
|
||||
import { slugify } from './render/slugify';
|
||||
import { get } from './fetch/ajax';
|
||||
|
||||
// TODO This is deprecated, kept for backwards compatibility. Remove in next
|
||||
// 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() {
|
||||
window.Docsify = {
|
||||
util,
|
||||
|
||||
@ -1,45 +1,8 @@
|
||||
import { initMixin } from './init';
|
||||
import { routerMixin } from './router';
|
||||
import { renderMixin } from './render';
|
||||
import { fetchMixin } from './fetch';
|
||||
import { eventMixin } from './event';
|
||||
import initGlobalAPI from './global-api';
|
||||
|
||||
/**
|
||||
* Fork https://github.com/bendrucker/document-ready/blob/master/index.js
|
||||
* @param {Function} callback The callbacack to be called when the page is loaded
|
||||
* @returns {Number|void} If the page is already laoded returns the result of the setTimeout callback,
|
||||
* otherwise it only attaches the callback to the DOMContentLoaded event
|
||||
*/
|
||||
function ready(callback) {
|
||||
const state = document.readyState;
|
||||
|
||||
if (state === 'complete' || state === 'interactive') {
|
||||
return setTimeout(callback, 0);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', callback);
|
||||
}
|
||||
|
||||
function Docsify() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
const proto = Docsify.prototype;
|
||||
|
||||
initMixin(proto);
|
||||
routerMixin(proto);
|
||||
renderMixin(proto);
|
||||
fetchMixin(proto);
|
||||
eventMixin(proto);
|
||||
|
||||
/**
|
||||
* Global API
|
||||
*/
|
||||
initGlobalAPI();
|
||||
import { documentReady } from './util/dom';
|
||||
import { Docsify } from './Docsify';
|
||||
|
||||
/**
|
||||
* Run Docsify
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
ready(_ => new Docsify());
|
||||
documentReady(_ => new Docsify());
|
||||
|
||||
@ -9,7 +9,7 @@ import { initLifecycle, callHook } from './lifecycle';
|
||||
export function initMixin(proto) {
|
||||
proto._init = function() {
|
||||
const vm = this;
|
||||
vm.config = config();
|
||||
vm.config = config(vm);
|
||||
|
||||
initLifecycle(vm); // Init hooks
|
||||
initPlugin(vm); // Install plugins
|
||||
|
||||
@ -101,3 +101,19 @@ export function toggleClass(el, type, val) {
|
||||
export function style(content) {
|
||||
appendTo(head, create('style', content));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fork https://github.com/bendrucker/document-ready/blob/master/index.js
|
||||
* @param {Function} callback The callbacack to be called when the page is loaded
|
||||
* @returns {Number|void} If the page is already laoded returns the result of the setTimeout callback,
|
||||
* otherwise it only attaches the callback to the DOMContentLoaded event
|
||||
*/
|
||||
export function documentReady(callback, doc = document) {
|
||||
const state = doc.readyState;
|
||||
|
||||
if (state === 'complete' || state === 'interactive') {
|
||||
return setTimeout(callback, 0);
|
||||
}
|
||||
|
||||
doc.addEventListener('DOMContentLoaded', callback);
|
||||
}
|
||||
|
||||
@ -19,6 +19,21 @@ function ready(callback) {
|
||||
document.addEventListener('DOMContentLoaded', callback);
|
||||
}
|
||||
|
||||
module.exports.initJSDOM = initJSDOM;
|
||||
|
||||
/** @param {string} markup - The HTML document to initialize JSDOM with. */
|
||||
function initJSDOM(markup, options = {}) {
|
||||
const dom = new JSDOM(markup, options);
|
||||
|
||||
global.window = dom.window;
|
||||
global.document = dom.window.document;
|
||||
global.navigator = dom.window.navigator;
|
||||
global.location = dom.window.location;
|
||||
global.XMLHttpRequest = dom.window.XMLHttpRequest;
|
||||
|
||||
return dom;
|
||||
}
|
||||
|
||||
module.exports.init = function(
|
||||
fixture = 'default',
|
||||
config = {},
|
||||
@ -39,15 +54,9 @@ module.exports.init = function(
|
||||
|
||||
const rootPath = path.join(__dirname, 'fixtures', fixture);
|
||||
|
||||
const dom = new JSDOM(markup);
|
||||
const dom = initJSDOM(markup);
|
||||
dom.reconfigure({ url: 'file:///' + rootPath });
|
||||
|
||||
global.window = dom.window;
|
||||
global.document = dom.window.document;
|
||||
global.navigator = dom.window.navigator;
|
||||
global.location = dom.window.location;
|
||||
global.XMLHttpRequest = dom.window.XMLHttpRequest;
|
||||
|
||||
// Mimic src/core/index.js but for Node.js
|
||||
function Docsify() {
|
||||
this._init();
|
||||
|
||||
134
test/unit/docsify.test.js
Normal file
134
test/unit/docsify.test.js
Normal file
@ -0,0 +1,134 @@
|
||||
/* global before after */
|
||||
/* eslint-disable no-global-assign */
|
||||
require = require('esm')(module /* , options */);
|
||||
|
||||
const http = require('http');
|
||||
const handler = require('serve-handler');
|
||||
const { expect } = require('chai');
|
||||
const { initJSDOM } = require('../_helper');
|
||||
|
||||
const port = 9753;
|
||||
const docsifySite = 'http://127.0.0.1:' + port;
|
||||
|
||||
const markup = /* html */ `<!DOCTYPE html>
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="/lib/docsify.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
/** @type {ReturnType<typeof http.createServer>} */
|
||||
let server;
|
||||
|
||||
describe('Docsify public API', () => {
|
||||
before(async () => {
|
||||
server = http.createServer((request, response) => {
|
||||
// You pass two more arguments for config and middleware
|
||||
// More details here: https://github.com/zeit/serve-handler#options
|
||||
return handler(request, response);
|
||||
});
|
||||
|
||||
await new Promise(r => server.listen(port, r));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
server.close(err => {
|
||||
if (err) {
|
||||
console.error(err); // eslint-disable-line
|
||||
process.exit(1);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
console.log('Server closed.');
|
||||
});
|
||||
});
|
||||
|
||||
it('global APIs are available', async () => {
|
||||
// const DOM = new (require('jsdom').JSDOM)(markup, {
|
||||
const DOM = initJSDOM(markup, {
|
||||
url: docsifySite,
|
||||
runScripts: 'dangerously',
|
||||
resources: 'usable',
|
||||
});
|
||||
|
||||
const { documentReady } = require('../../src/core/util/dom');
|
||||
await new Promise(resolve => documentReady(resolve, DOM.window.document));
|
||||
|
||||
// If the script was built successfully for production, then it should load
|
||||
// and the following APIs should be available:
|
||||
expect(typeof DOM.window.Docsify).to.equal('object');
|
||||
expect(typeof DOM.window.Docsify.util).to.equal('object');
|
||||
expect(typeof DOM.window.Docsify.dom).to.equal('object');
|
||||
expect(typeof DOM.window.Docsify.get).to.equal('function');
|
||||
expect(typeof DOM.window.Docsify.slugify).to.equal('function');
|
||||
expect(typeof DOM.window.Docsify.version).to.equal('string');
|
||||
expect(typeof DOM.window.DocsifyCompiler).to.equal('function');
|
||||
expect(typeof DOM.window.marked).to.equal('function');
|
||||
expect(typeof DOM.window.Prism).to.equal('object');
|
||||
});
|
||||
|
||||
describe('Docsify config function', function() {
|
||||
it('allows $docsify to be a function', async function() {
|
||||
initJSDOM(markup, { url: docsifySite });
|
||||
|
||||
window.configFunctionCalled = false;
|
||||
|
||||
window.$docsify = function(vm) {
|
||||
// Check public API (that which is available at this point)
|
||||
expect(vm).to.be.an.instanceof(Object);
|
||||
expect(vm.constructor.name).to.equal('Docsify');
|
||||
expect(vm.$fetch).to.be.an.instanceof(Function);
|
||||
expect(vm.$resetEvents).to.be.an.instanceof(Function);
|
||||
expect(vm.route).to.be.an.instanceof(Object);
|
||||
|
||||
window.configFunctionCalled = true;
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
const { documentReady } = require('../../src/core/util/dom');
|
||||
const { Docsify } = require('../../src/core/Docsify');
|
||||
await new Promise(resolve => documentReady(resolve));
|
||||
|
||||
new Docsify(); // eslint-disable-line
|
||||
|
||||
expect(window.configFunctionCalled).to.equal(true);
|
||||
});
|
||||
|
||||
it('provides the hooks and vm API to plugins', async function() {
|
||||
initJSDOM(markup, { url: docsifySite });
|
||||
|
||||
window.pluginFunctionCalled = false;
|
||||
|
||||
window.$docsify = function(vm) {
|
||||
const vm1 = vm;
|
||||
return {
|
||||
plugins: [
|
||||
function(hook, vm2) {
|
||||
expect(vm1).to.equal(vm2);
|
||||
|
||||
expect(hook.init).to.be.an.instanceof(Function);
|
||||
expect(hook.beforeEach).to.be.an.instanceof(Function);
|
||||
expect(hook.afterEach).to.be.an.instanceof(Function);
|
||||
expect(hook.doneEach).to.be.an.instanceof(Function);
|
||||
expect(hook.mounted).to.be.an.instanceof(Function);
|
||||
expect(hook.ready).to.be.an.instanceof(Function);
|
||||
|
||||
window.pluginFunctionCalled = true;
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const { documentReady } = require('../../src/core/util/dom');
|
||||
const { Docsify } = require('../../src/core/Docsify');
|
||||
await new Promise(resolve => documentReady(resolve));
|
||||
|
||||
new Docsify(); // eslint-disable-line
|
||||
|
||||
expect(window.pluginFunctionCalled).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user