diff --git a/.eslintignore b/.eslintignore index aa14c2187..6a93385a2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,3 +6,4 @@ lib node_modules server.js themes +dist diff --git a/.gitignore b/.gitignore index 545b20a6f..a2461da5c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /_playwright-report /_playwright-results /lib +/dist /node_modules /.husky/_ diff --git a/.prettierignore b/.prettierignore index 6c424db81..377d4de83 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,3 +4,6 @@ lib/ themes/ _playwright-*/ emoji-data.* + +# for some reason this is needed in CI, although locally Prettier ignored dist/ already. +dist/ diff --git a/.vscode/settings.json b/.vscode/settings.json index 39497bbfc..45d2e1804 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "editor.defaultFormatter": "esbenp.prettier-vscode", - "cSpell.words": ["coverpage"] + "cSpell.words": ["coverpage"], + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/build/build.js b/build/build.js index c8b9ab545..eb1fd114a 100644 --- a/build/build.js +++ b/build/build.js @@ -17,6 +17,7 @@ const version = process.env.VERSION || pkg.version; /** * @param {{ * input: string, + * format: 'iife' | 'module', * output?: string, * globalName?: string, * plugins?: Array @@ -46,11 +47,11 @@ async function build(opts) { }, }) .then(bundle => { - const dest = 'lib/' + (opts.output || opts.input); + const dest = opts.output || opts.input; console.log(dest); return bundle.write({ - format: 'iife', + format: opts.format, output: opts.globalName ? { name: opts.globalName } : {}, file: dest, strict: false, @@ -64,7 +65,13 @@ async function buildCore() { promises.push( build({ input: 'src/core/index.js', - output: 'docsify.js', + output: 'lib/docsify.js', + format: 'iife', + }), + build({ + input: 'src/core/module.js', + output: 'dist/core/module.js', + format: 'module', }) ); @@ -72,7 +79,14 @@ async function buildCore() { promises.push( build({ input: 'src/core/index.js', - output: 'docsify.min.js', + output: 'lib/docsify.min.js', + format: 'iife', + plugins: [uglify()], + }), + build({ + input: 'src/core/module.js', + output: 'dist/core/module.min.js', + format: 'module', plugins: [uglify()], }) ); @@ -98,7 +112,7 @@ async function buildAllPlugin() { const promises = plugins.map(item => { return build({ input: 'src/plugins/' + item.input, - output: 'plugins/' + item.name + '.js', + output: 'lib/plugins/' + item.name + '.js', }); }); @@ -107,7 +121,7 @@ async function buildAllPlugin() { promises.push( build({ input: 'src/plugins/' + item.input, - output: 'plugins/' + item.name + '.min.js', + output: 'lib/plugins/' + item.name + '.min.js', plugins: [uglify()], }) ); @@ -140,7 +154,7 @@ async function main() { build({ input, - output: 'plugins/' + name + '.js', + output: 'lib/plugins/' + name + '.js', }); } }) diff --git a/docs/index.html b/docs/index.html index 0a7f78140..fe81e400b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -71,7 +71,7 @@
Loading ...
- --> + + + + + + + --> + + diff --git a/package.json b/package.json index 54efbff52..0e106bd3a 100644 --- a/package.json +++ b/package.json @@ -12,22 +12,40 @@ "type": "module", "// The 'main' and 'unpkg' fields will remain as legacy for backwards compatibility for now. We will add deprectaion warnings to these outputs to give people time to see the warnings in their apps in a non-breaking way, and eventually we can remove the legacy stuff.": "", "main": "lib/docsify.js", + "types": "dist/core/Docsify.d.ts", "unpkg": "lib/docsify.min.js", "// We're using the 'exports' field as an override of the 'main' field to provide the new ESM setup. Once we remove legacy 'main', we will remove the 'exports' field and have a simple ESM setup with only a 'main' field.": "", + "// These exports require moduleResolution:NodeNext to be enabled in the consumer.": "", + "// TODO native ESM (in browsers) does not work yet because prismjs is not ESM and Docsify source imports it as CommonJS": "", "exports": { - ".": "./src/Docsify.js", - "./*": "./*" + "./src/*": "./src/*", + "./dist/core/module.js": "./dist/core/module.js", + "./dist/core/module.min.js": "./dist/core/module.min.js", + ".": { + "types": "./dist/core/Docsify.d.ts", + "default": "./dist/core/module.min.js" + } }, "files": [ + "src", "lib", "themes" ], + "// Using 'typesVersions' here is the only way we could figure out how to get types working for imports of any subpath without any of the problems other approaches have when not using modeResolution:NodeNext (listed in https://stackoverflow.com/questions/77856692/how-to-publish-plain-jsjsdoc-library-for-typescript-consumers)": "", + "typesVersions": { + "*": { + "src/*": [ + "dist/*" + ] + } + }, "scripts": { "build:cover": "node build/cover.js", "build:css:min": "node build/mincss.js", "build:css": "mkdirp lib/themes && node build/css -o lib/themes", "build:emoji": "node ./build/emoji.js", - "build:js": "cross-env NODE_ENV=production node build/build.js", + "build:js": "npm run build:types && cross-env NODE_ENV=production node build/build.js", + "build:types": "tsc -p tsconfig.json || true", "build:test": "npm run build && npm test", "build": "rimraf lib themes && run-s build:js build:css build:css:min build:cover build:emoji", "dev": "run-p serve:dev watch:*", diff --git a/src/core/Docsify.js b/src/core/Docsify.js index ccf414c26..0cad5440a 100644 --- a/src/core/Docsify.js +++ b/src/core/Docsify.js @@ -1,3 +1,4 @@ +import prism from 'prismjs'; import { Router } from './router/index.js'; import { Render } from './render/index.js'; import { Fetch } from './fetch/index.js'; @@ -8,18 +9,31 @@ import config from './config.js'; import { isFn } from './util/core.js'; import { Lifecycle } from './init/lifecycle.js'; +export { prism }; +export { marked } from 'marked'; +export * as util from './util/index.js'; +export * as dom from './util/dom.js'; +export { Compiler } from './render/compiler.js'; +export { slugify } from './render/slugify.js'; +export { get } from './util/ajax.js'; + /** @typedef {new (...args: any[]) => any} Constructor */ +/** @typedef {import('./config.js').DocsifyConfig} DocsifyConfig */ // eslint-disable-next-line new-cap export class Docsify extends Fetch( // eslint-disable-next-line new-cap Events(Render(VirtualRoutes(Router(Lifecycle(Object))))) ) { - config = config(this); + /** @type {DocsifyConfig} */ + config; - constructor() { + /** @param {Partial} conf */ + constructor(conf = {}) { super(); + this.config = config(this, conf); + this.initLifecycle(); // Init hooks this.initPlugin(); // Install plugins this.callHook('init'); @@ -45,3 +59,5 @@ export class Docsify extends Fetch( }); } } + +export const version = '__VERSION__'; diff --git a/src/core/config.js b/src/core/config.js index 9d9f8e31d..083fd8d7d 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -3,9 +3,65 @@ import { hyphenate, isPrimitive } from './util/core.js'; const currentScript = document.currentScript; -/** @param {import('./Docsify.js').Docsify} vm */ -export default function (vm) { - const config = Object.assign( +/** +@typedef { + { + auto2top: boolean + autoHeader: boolean + basePath: string + catchPluginErrors: boolean + cornerExternalLinkTarget: '_blank' | '_self' | '_parent' | '_top' | '_unfencedTop' + coverpage: boolean | string + el: string + executeScript: null | boolean + ext: string + externalLinkRel: 'noopener' | string + externalLinkTarget: '_blank' | '_self' | '_parent' | '_top' | '_unfencedTop' + formatUpdated: string + ga: string + homepage: string + keyBindings: false | { + [commandName: string]: { + bindings: string[] + callback: Function + } + } + loadNavbar: null | boolean | string + loadSidebar: null | boolean | string + maxLevel: number + mergeNavbar: boolean + name: boolean | string + nameLink: string + nativeEmoji: boolean + noCompileLinks: string[] + noEmoji: boolean + notFoundPage: boolean | string | Record + plugins: Function[] + relativePath: boolean + repo: boolean | string + routes: Record + routerMode: 'hash' | 'history' + subMaxLevel: number, + topMargin: number, + + themeColor: string, + } +} DocsifyConfig +*/ + +/** + * @param {import('./Docsify.js').Docsify} vm + * @param {Partial} config + * @returns {DocsifyConfig} + */ +export default function (vm, config = {}) { + if (window.$docsify) { + console.warn( + 'DEPRECATION: The global $docsify config variable is deprecated. See the latest getting started docs. https://docsify.js.org/#/quickstart' + ); + } + + config = Object.assign( { auto2top: false, autoHeader: false, @@ -21,6 +77,7 @@ export default function (vm) { formatUpdated: '', ga: '', homepage: 'README.md', + keyBindings: {}, loadNavbar: null, loadSidebar: null, maxLevel: 6, @@ -37,7 +94,6 @@ export default function (vm) { routes: {}, routerMode: 'hash', subMaxLevel: 0, - // themeColor: '', topMargin: 0, // Deprecations ////////////////// @@ -63,7 +119,9 @@ export default function (vm) { typeof window.$docsify === 'function' ? window.$docsify(vm) - : window.$docsify + : window.$docsify, + + config ); // Merge default and user-specified key bindings @@ -99,6 +157,11 @@ export default function (vm) { const val = script.getAttribute('data-' + hyphenate(prop)); if (isPrimitive(val)) { + console.warn( + `DEPRECATION: data-* attributes on the docsify global script (f.e. ${ + 'data-' + hyphenate(prop) + }) are deprecated.` + ); config[prop] = val === '' ? true : val; } } @@ -124,7 +187,5 @@ export default function (vm) { config.name = ''; } - window.$docsify = config; - - return config; + return /** @type {DocsifyConfig} */ (config); } diff --git a/src/core/global-api.js b/src/core/global-api.js index 24be8ae2c..e0c3d3504 100644 --- a/src/core/global-api.js +++ b/src/core/global-api.js @@ -6,11 +6,15 @@ import { Compiler } from './render/compiler.js'; import { slugify } from './render/slugify.js'; import { get } from './util/ajax.js'; -// TODO This is deprecated, kept for backwards compatibility. Remove in a -// 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). +/** + * @deprecated This will be removed in a major release. Import what you need + * from using ES Modules as per the docs. + */ export default function initGlobalAPI() { + console.warn( + 'DEPRECATION: The Docsify global script is deprecated. See the latest getting started docs. https://docsify.js.org/#/quickstart' + ); + window.Docsify = { util, dom, diff --git a/src/core/index.js b/src/core/index.js index 897ac66b3..43fb5b9c7 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -2,10 +2,6 @@ import { documentReady } from './util/dom.js'; import { Docsify } from './Docsify.js'; import initGlobalAPI from './global-api.js'; -// TODO This global API and auto-running Docsify will be deprecated, and removed -// in a major release. Instead we'll tell users to use `new Docsify()` to create -// and manage their instance(s). - /** * Global API */ diff --git a/src/core/module.js b/src/core/module.js new file mode 100644 index 000000000..4206ebc24 --- /dev/null +++ b/src/core/module.js @@ -0,0 +1 @@ +export * from './Docsify.js'; diff --git a/src/core/module.min.js b/src/core/module.min.js new file mode 100644 index 000000000..c08b05d10 --- /dev/null +++ b/src/core/module.min.js @@ -0,0 +1,2 @@ +// This file exists for type declaration output. +export * from './Docsify.js'; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..1fc7ebd19 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + + "module": "NodeNext", + "moduleResolution": "NodeNext", + "target": "ESNext", + "lib": ["DOM", "ESNext"], + "declaration": true, + "emitDeclarationOnly": true, + "resolveJsonModule": true, + "outDir": "dist/" + }, + "include": ["src/**/*.js", "src/core/module.min.js"] +}