refactor: convert to ES Modules and remove traces of CommonJS except in Rollup config because some dependencies are still CommonJS

BREAKING: The new project layout might break in some tooling setups.

We've added an exports field to `package.json` to specify where
statements like `import ... from 'docsify'` will import from, and left
the `main` and `unpkg` fields as-is for backwards compatibility with the
global <script> import method. Most people who use a non-module
`<script>` tag to import Docsify will not notice a difference. Anyone
else who is importing Docsify into a specilized build setup using
`import` statements has a chance of being broken, so we've marked this
as BREAKING.
This commit is contained in:
Joe Pea 2023-06-24 23:57:42 -07:00
parent 8d5d20480a
commit 62d756c447
87 changed files with 1187 additions and 755 deletions

View File

@ -1,4 +1,4 @@
const prettierConfig = require('./.prettierrc');
const prettierConfig = require('./.prettierrc.json');
module.exports = {
root: true,

View File

@ -1,5 +1,4 @@
.eslintignore
.eslintrc
.eslintrc.cjs
.github
.gitignore
.travis.yml

View File

@ -1,4 +0,0 @@
module.exports = {
arrowParens: 'avoid',
singleQuote: true,
};

4
.prettierrc.json Normal file
View File

@ -0,0 +1,4 @@
{
"arrowParens": "avoid",
"singleQuote": true
}

19
.vscode/launch.json vendored
View File

@ -7,18 +7,18 @@
{
"type": "node",
"request": "launch",
"name": "Run tests",
"name": "Run unit tests",
"runtimeExecutable": "npm",
"runtimeArgs": ["run-script", "test"],
"runtimeArgs": ["test"],
"console": "integratedTerminal"
},
{
"type": "node",
"request": "launch",
"name": "Run current test file",
"runtimeExecutable": "npm",
"runtimeArgs": ["run-script", "test"],
"args": ["--", "-i", "${relativeFile}", "--testPathIgnorePatterns"],
"runtimeExecutable": "npx",
"runtimeArgs": ["jest"],
"args": ["-i", "${relativeFile}", "--testPathIgnorePatterns"],
"console": "integratedTerminal"
},
{
@ -41,10 +41,9 @@
"type": "node",
"request": "launch",
"name": "Update current test file snapshot(s)",
"runtimeExecutable": "npm",
"runtimeArgs": ["run-script", "test"],
"runtimeExecutable": "npx",
"runtimeArgs": ["jest"],
"args": [
"--",
"-i",
"${relativeFile}",
"--updateSnapshot",
@ -56,8 +55,8 @@
"type": "node",
"request": "launch",
"name": "Update selected test name snapshot(s)",
"runtimeExecutable": "npm",
"runtimeArgs": ["run-script", "test"],
"runtimeExecutable": "npx",
"runtimeArgs": ["jest"],
"args": [
"--",
"-i",

View File

@ -32,7 +32,10 @@
- [`develop` branch preview](https://docsify-preview.vercel.app/)
- [Documentation](https://docsify.js.org)
- [CLI](https://github.com/docsifyjs/docsify-cli)
- CDN: [UNPKG](https://unpkg.com/docsify/) | [jsDelivr](https://cdn.jsdelivr.net/npm/docsify/) | [cdnjs](https://cdnjs.com/libraries/docsify)
- CDN:
- [UNPKG](https://unpkg.com/docsify/)
- [jsDelivr](https://cdn.jsdelivr.net/npm/docsify/)
- [cdnjs](https://cdnjs.com/libraries/docsify)
- [Awesome docsify](https://github.com/docsifyjs/awesome-docsify)
- [Community chat](https://discord.gg/3NwKFyR)

View File

@ -1,12 +0,0 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
};

12
babel.config.json Normal file
View File

@ -0,0 +1,12 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
]
}

View File

@ -1,13 +1,19 @@
const rollup = require('rollup')
const buble = require('rollup-plugin-buble')
const commonjs = require('rollup-plugin-commonjs')
const nodeResolve = require('rollup-plugin-node-resolve')
const { uglify } = require('rollup-plugin-uglify')
const replace = require('rollup-plugin-replace')
const isProd = process.env.NODE_ENV === 'production'
const version = process.env.VERSION || require('../package.json').version
const chokidar = require('chokidar')
const path = require('path')
import rollup from 'rollup';
import buble from 'rollup-plugin-buble';
import commonjs from 'rollup-plugin-commonjs';
import nodeResolve from 'rollup-plugin-node-resolve';
import { uglify } from 'rollup-plugin-uglify';
import replace from 'rollup-plugin-replace';
import chokidar from 'chokidar';
import path from 'path';
import { relative } from './util.js';
import { promises as fs } from 'fs';
const pkgPath = relative(import.meta, '..', 'package.json');
const pkgString = (await fs.readFile(pkgPath)).toString();
const pkg = JSON.parse(pkgString);
const isProd = process.env.NODE_ENV === 'production';
const version = process.env.VERSION || pkg.version;
/**
* @param {{

View File

@ -1,14 +1,17 @@
var fs = require('fs')
var read = fs.readFileSync
var write = fs.writeFileSync
var version = process.env.VERSION || require('../package.json').version
import fs from 'fs';
import { relative } from './util.js';
var read = fs.readFileSync;
var write = fs.writeFileSync;
const pkgPath = relative(import.meta, '..', 'package.json');
const pkg = JSON.parse(read(pkgPath).toString());
var version = process.env.VERSION || pkg.version;
var file = __dirname + '/../docs/_coverpage.md'
var cover = read(file, 'utf8').toString()
var file = relative(import.meta, '..', 'docs', '_coverpage.md');
var cover = read(file, 'utf8').toString();
console.log('Replace version number in cover page...')
console.log('Replace version number in cover page...');
cover = cover.replace(
/<small>(\S+)?<\/small>/g,
'<small>' + version + '</small>'
)
write(file, cover)
);
write(file, cover);

View File

@ -1,9 +1,10 @@
const fs = require('fs')
const path = require('path')
const {spawn} = require('child_process')
import fs from 'fs'
import path from 'path'
import {spawn} from 'child_process'
const relative = path => new URL(path, import.meta.url);
const args = process.argv.slice(2)
fs.readdir(path.join(__dirname, '../src/themes'), (err, files) => {
fs.readdir(relative('../src/themes'), (err, files) => {
if (err) {
console.error('err', err)
process.exit(1)

View File

@ -1,6 +1,6 @@
const axios = require('axios');
const fs = require('fs');
const path = require('path');
import axios from 'axios';
import fs from 'fs';
import path from 'path';
const filePaths = {
emojiMarkdown: path.resolve(process.cwd(), 'docs', 'emoji.md'),

View File

@ -1,12 +1,12 @@
const cssnano = require('cssnano').process
const path = require('path')
const fs = require('fs')
import cssnano from 'cssnano';
import path from 'path'
import fs from 'fs'
files = fs.readdirSync(path.resolve('lib/themes'))
const files = fs.readdirSync(path.resolve('lib/themes'))
files.forEach(file => {
file = path.resolve('lib/themes', file)
cssnano(fs.readFileSync(file)).then(result => {
cssnano.process(fs.readFileSync(file)).then(result => {
fs.writeFileSync(file, result.css)
}).catch(e => {
console.error(e)

6
build/util.js Normal file
View File

@ -0,0 +1,6 @@
import url from 'url';
import path from 'path';
/** Get a new path relative to the current module (pass import.meta). */
export const relative = (meta, ...to) =>
path.resolve(path.dirname(url.fileURLToPath(meta.url)), ...to);

View File

@ -1,4 +1,4 @@
const { TEST_HOST } = require('./test/config/server.js');
import { TEST_HOST } from './test/config/server.js';
const sharedConfig = {
errorOnDeprecated: true,
@ -11,7 +11,8 @@ const sharedConfig = {
testURL: `${TEST_HOST}/_blank.html`,
};
module.exports = {
export default {
transform: {},
projects: [
// Unit Tests
{

1341
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,19 +2,22 @@
"name": "docsify",
"version": "4.13.0",
"description": "A magical documentation generator.",
"author": {
"name": "qingwei-li",
"email": "cinwell.li@gmail.com",
"url": "https://github.com/QingWei-Li"
},
"homepage": "https://docsify.js.org",
"repository": "github:docsifyjs/docsify",
"authors": "https://github.com/docsifyjs/docsify/graphs/contributors",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/docsifyjs/docsify.git"
"collective": {
"url": "https://opencollective.com/docsify"
},
"type": "module",
"// The 'main' and 'unpkg' fields will remain as legacy for backwards compatbility 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",
"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.": "",
"exports": {
".": "./src/Docsify.js",
"./*": "./*"
},
"files": [
"lib",
"themes"
@ -46,9 +49,10 @@
"pub": "sh build/release.sh",
"serve": "node server",
"test:e2e": "playwright test",
"test:integration": "jest --selectProjects integration",
"test:unit": "jest --selectProjects unit",
"test": "jest && run-s test:e2e",
"test:integration": "npm run jest -- --selectProjects integration",
"test:unit": "npm run jest -- --selectProjects unit",
"test": "npm run jest && run-s test:e2e",
"jest": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
"watch:css": "npm run css -- -o themes -w",
"watch:js": "node build/build.js"
},
@ -73,7 +77,9 @@
"@babel/core": "^7.11.6",
"@babel/eslint-parser": "^7.16.5",
"@babel/preset-env": "^7.11.5",
"@eslint/js": "^8.43.0",
"@playwright/test": "^1.18.1",
"@types/eslint": "^8.40.2",
"autoprefixer-stylus": "^1.0.0",
"axios": "^0.21.1",
"babel-jest": "^27.4.6",
@ -84,19 +90,20 @@
"copy-dir": "^1.2.0",
"cross-env": "^7.0.3",
"cssnano": "^4.1.10",
"eslint": "^8.7.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-playwright": "^0.12.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint": "^8.43.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.2.2",
"eslint-plugin-playwright": "^0.15.1",
"eslint-plugin-prettier": "^4.2.1",
"globals": "^13.20.0",
"husky": "^8.0.3",
"jest": "^27.4.7",
"lint-staged": "^13.2.2",
"live-server": "^1.2.1",
"mkdirp": "^3.0.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.5.1",
"prettier": "^2.8.8",
"rimraf": "^3.0.0",
"rollup": "^1.23.1",
"rollup-plugin-async": "^1.2.0",
@ -117,8 +124,5 @@
"documentation",
"creator",
"generator"
],
"collective": {
"url": "https://opencollective.com/docsify"
}
]
}

View File

@ -1,4 +1,4 @@
const { devices } = require('@playwright/test');
import { devices } from '@playwright/test';
/**
* @see https://playwright.dev/docs/test-configuration
@ -61,4 +61,4 @@ const config = {
],
};
module.exports = config;
export default config;

View File

@ -1,10 +1,10 @@
const liveServer = require('live-server')
const middleware = []
import liveServer from 'live-server';
const middleware = [];
const params = {
port: 3000,
watch: ['lib', 'docs', 'themes'],
middleware
}
middleware,
};
liveServer.start(params)
liveServer.start(params);

View File

@ -6,8 +6,8 @@ import { VirtualRoutes } from './virtual-routes/index.js';
import initGlobalAPI from './global-api.js';
import config from './config.js';
import { isFn } from './util/core';
import { Lifecycle } from './init/lifecycle';
import { isFn } from './util/core.js';
import { Lifecycle } from './init/lifecycle.js';
/** @typedef {new (...args: any[]) => any} Constructor */

View File

@ -1,4 +1,4 @@
import { merge, hyphenate, isPrimitive, hasOwn } from './util/core';
import { merge, hyphenate, isPrimitive, hasOwn } from './util/core.js';
const currentScript = document.currentScript;

View File

@ -1,7 +1,7 @@
import { isMobile } from '../util/env';
import { body, on } from '../util/dom';
import * as sidebar from './sidebar';
import { scrollIntoView, scroll2Top } from './scroll';
import { isMobile } from '../util/env.js';
import { body, on } from '../util/dom.js';
import * as sidebar from './sidebar.js';
import { scrollIntoView, scroll2Top } from './scroll.js';
/** @typedef {import('../Docsify').Constructor} Constructor */

View File

@ -1,8 +1,8 @@
import Tweezer from 'tweezer.js';
import { isMobile } from '../util/env';
import * as dom from '../util/dom';
import { removeParams } from '../router/util';
import config from '../config';
import { isMobile } from '../util/env.js';
import * as dom from '../util/dom.js';
import { removeParams } from '../router/util.js';
import config from '../config.js';
const nav = {};
let hoverOver = false;

View File

@ -1,6 +1,6 @@
/* eslint-disable no-unused-vars */
import { isMobile } from '../util/env';
import * as dom from '../util/dom';
import { isMobile } from '../util/env.js';
import * as dom from '../util/dom.js';
const title = dom.$.title;
/**

View File

@ -1,6 +1,6 @@
/* eslint-disable no-unused-vars */
import progressbar from '../render/progressbar';
import { noop, hasOwn } from '../util/core';
import progressbar from '../render/progressbar.js';
import { noop, hasOwn } from '../util/core.js';
const cache = {};

View File

@ -1,8 +1,8 @@
/* eslint-disable no-unused-vars */
import { getParentPath, stringifyQuery } from '../router/util';
import { noop, isExternal } from '../util/core';
import { getAndActive } from '../event/sidebar';
import { get } from './ajax';
import { getParentPath, stringifyQuery } from '../router/util.js';
import { noop, isExternal } from '../util/core.js';
import { getAndActive } from '../event/sidebar.js';
import { get } from './ajax.js';
function loadNested(path, qs, file, next, vm, first) {
path = first ? path : path.replace(/\/$/, '');

View File

@ -1,10 +1,10 @@
import prism from 'prismjs';
import { marked } from 'marked';
import * as util from './util';
import * as dom from './util/dom';
import { Compiler } from './render/compiler';
import { slugify } from './render/slugify';
import { get } from './fetch/ajax';
import * as util from './util/index.js';
import * as dom from './util/dom.js';
import { Compiler } from './render/compiler.js';
import { slugify } from './render/slugify.js';
import { get } from './fetch/ajax.js';
// TODO This is deprecated, kept for backwards compatibility. Remove in next
// major release. We'll tell people to get everything from the DOCSIFY global

View File

@ -1,5 +1,5 @@
import { documentReady } from './util/dom';
import { Docsify } from './Docsify';
import { documentReady } from './util/dom.js';
import { Docsify } from './Docsify.js';
/**
* Run Docsify

View File

@ -1,4 +1,4 @@
import { noop } from '../util/core';
import { noop } from '../util/core.js';
/** @typedef {import('../Docsify').Constructor} Constructor */

View File

@ -1,21 +1,21 @@
import { marked } from 'marked';
import { isAbsolutePath, getPath, getParentPath } from '../router/util';
import { isFn, merge, cached, isPrimitive } from '../util/core';
import { tree as treeTpl } from './tpl';
import { genTree } from './gen-tree';
import { slugify } from './slugify';
import { emojify } from './emojify';
import { isAbsolutePath, getPath, getParentPath } from '../router/util.js';
import { isFn, merge, cached, isPrimitive } from '../util/core.js';
import { tree as treeTpl } from './tpl.js';
import { genTree } from './gen-tree.js';
import { slugify } from './slugify.js';
import { emojify } from './emojify.js';
import {
getAndRemoveConfig,
removeAtag,
getAndRemoveDocisfyIgnorConfig,
} from './utils';
import { imageCompiler } from './compiler/image';
import { highlightCodeCompiler } from './compiler/code';
import { paragraphCompiler } from './compiler/paragraph';
import { taskListCompiler } from './compiler/taskList';
import { taskListItemCompiler } from './compiler/taskListItem';
import { linkCompiler } from './compiler/link';
} from './utils.js';
import { imageCompiler } from './compiler/image.js';
import { highlightCodeCompiler } from './compiler/code.js';
import { paragraphCompiler } from './compiler/paragraph.js';
import { taskListCompiler } from './compiler/taskList.js';
import { taskListItemCompiler } from './compiler/taskListItem.js';
import { linkCompiler } from './compiler/link.js';
const cachedLinks = {};

View File

@ -1,6 +1,6 @@
import Prism from 'prismjs';
// See https://github.com/PrismJS/prism/pull/1367
import 'prismjs/components/prism-markup-templating';
import 'prismjs/components/prism-markup-templating.js';
export const highlightCodeCompiler = ({ renderer }) =>
(renderer.code = function (code, lang = 'markup') {

View File

@ -2,8 +2,8 @@ import {
getAndRemoveConfig,
removeAtag,
getAndRemoveDocisfyIgnorConfig,
} from '../utils';
import { slugify } from './slugify';
} from '../utils.js';
import { slugify } from './slugify.js';
export const headingCompiler = ({ renderer, router, _self }) =>
(renderer.code = (text, level) => {

View File

@ -1,5 +1,5 @@
import { getAndRemoveConfig } from '../utils';
import { isAbsolutePath, getPath, getParentPath } from '../../router/util';
import { getAndRemoveConfig } from '../utils.js';
import { isAbsolutePath, getPath, getParentPath } from '../../router/util.js';
export const imageCompiler = ({ renderer, contentBase, router }) =>
(renderer.image = (href, title, text) => {

View File

@ -1,5 +1,5 @@
import { getAndRemoveConfig } from '../utils';
import { isAbsolutePath } from '../../router/util';
import { getAndRemoveConfig } from '../utils.js';
import { isAbsolutePath } from '../../router/util.js';
export const linkCompiler = ({
renderer,

View File

@ -1,4 +1,4 @@
import { helper as helperTpl } from '../tpl';
import { helper as helperTpl } from '../tpl.js';
export const paragraphCompiler = ({ renderer }) =>
(renderer.paragraph = text => {

View File

@ -1,6 +1,6 @@
import stripIndent from 'strip-indent';
import { get } from '../fetch/ajax';
import { merge } from '../util/core';
import { get } from '../fetch/ajax.js';
import { merge } from '../util/core.js';
const cached = {};

View File

@ -1,15 +1,15 @@
/* eslint-disable no-unused-vars */
import tinydate from 'tinydate';
import * as dom from '../util/dom';
import cssVars from '../util/polyfill/css-vars';
import { getAndActive, sticky } from '../event/sidebar';
import { getPath, isAbsolutePath } from '../router/util';
import { isMobile, inBrowser } from '../util/env';
import { isPrimitive, merge } from '../util/core';
import { scrollActiveSidebar } from '../event/scroll';
import { Compiler } from './compiler';
import * as tpl from './tpl';
import { prerenderEmbed } from './embed';
import * as dom from '../util/dom.js';
import cssVars from '../util/polyfill/css-vars.js';
import { getAndActive, sticky } from '../event/sidebar.js';
import { getPath, isAbsolutePath } from '../router/util.js';
import { isMobile, inBrowser } from '../util/env.js';
import { isPrimitive, merge } from '../util/core.js';
import { scrollActiveSidebar } from '../event/scroll.js';
import { Compiler } from './compiler.js';
import * as tpl from './tpl.js';
import { prerenderEmbed } from './embed.js';
let vueGlobalData;

View File

@ -1,4 +1,4 @@
import * as dom from '../util/dom';
import * as dom from '../util/dom.js';
let barEl;
let timeId;

View File

@ -1,4 +1,4 @@
import { hasOwn } from '../util/core';
import { hasOwn } from '../util/core.js';
let cache = {};
const re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g;

View File

@ -5,8 +5,8 @@ import {
cleanPath,
replaceSlug,
resolvePath,
} from '../util';
import { noop, merge } from '../../util/core';
} from '../util.js';
import { noop, merge } from '../../util/core.js';
const cached = {};

View File

@ -1,8 +1,8 @@
import { isExternal, noop } from '../../util/core';
import { on } from '../../util/dom';
import { endsWith } from '../../util/str';
import { parseQuery, cleanPath, replaceSlug } from '../util';
import { History } from './base';
import { isExternal, noop } from '../../util/core.js';
import { on } from '../../util/dom.js';
import { endsWith } from '../../util/str.js';
import { parseQuery, cleanPath, replaceSlug } from '../util.js';
import { History } from './base.js';
function replaceHash(path) {
const i = location.href.indexOf('#');

View File

@ -1,7 +1,7 @@
import { isExternal, noop } from '../../util/core';
import { on } from '../../util/dom';
import { parseQuery, getPath } from '../util';
import { History } from './base';
import { isExternal, noop } from '../../util/core.js';
import { on } from '../../util/dom.js';
import { parseQuery, getPath } from '../util.js';
import { History } from './base.js';
export class HTML5History extends History {
constructor(config) {

View File

@ -1,8 +1,8 @@
import { supportsPushState } from '../util/env';
import * as dom from '../util/dom';
import { noop } from '../util/core';
import { HashHistory } from './history/hash';
import { HTML5History } from './history/html5';
import { supportsPushState } from '../util/env.js';
import * as dom from '../util/dom.js';
import { noop } from '../util/core.js';
import { HashHistory } from './history/hash.js';
import { HTML5History } from './history/html5.js';
/**
* @typedef {{

View File

@ -1,4 +1,4 @@
import { cached } from '../util/core';
import { cached } from '../util/core.js';
const decode = decodeURIComponent;
const encode = encodeURIComponent;

View File

@ -1,5 +1,5 @@
import { isFn } from '../util/core';
import { inBrowser } from './env';
import { isFn } from '../util/core.js';
import { inBrowser } from './env.js';
const cacheNode = {};

View File

@ -1,3 +1,3 @@
export * from './core';
export * from './env';
export * from '../router/util';
export * from './core.js';
export * from './env.js';
export * from '../router/util.js';

View File

@ -1,5 +1,5 @@
import * as dom from '../dom';
import { get } from '../../fetch/ajax';
import * as dom from '../dom.js';
import { get } from '../../fetch/ajax.js';
function replaceVar(block, color) {
block.innerHTML = block.innerHTML.replace(

View File

@ -1,4 +1,4 @@
import { startsWith, endsWith } from '../util/str';
import { startsWith, endsWith } from '../util/str.js';
/**
* Adds beginning of input (^) and end of input ($) assertions if needed into a regex string

View File

@ -1,5 +1,5 @@
import { makeExactMatcher } from './exact-match';
import { createNextFunction } from './next';
import { makeExactMatcher } from './exact-match.js';
import { createNextFunction } from './next.js';
/** @typedef {import('../Docsify').Constructor} Constructor */

View File

@ -1,4 +1,4 @@
import parser from './parser';
import parser from './parser.js';
const install = function (hook, vm) {
// Used to remove front matter from embedded pages if installed.

View File

@ -2,7 +2,7 @@
* Fork https://github.com/egoist/docute/blob/master/src/utils/front-matter.js
*/
/* eslint-disable */
import parser from './yaml'
import parser from './yaml.js'
var optionalByteOrderMark = '\\ufeff?'
var pattern =

View File

@ -1,5 +1,5 @@
/* eslint-disable no-unused-vars */
import { search } from './search';
import { search } from './search.js';
let NO_DATA_TEXT = '';
let options;

View File

@ -1,6 +1,9 @@
/* eslint-disable no-unused-vars */
import { init as initComponent, update as updateComponent } from './component';
import { init as initSearch } from './search';
import {
init as initComponent,
update as updateComponent,
} from './component.js';
import { init as initSearch } from './search.js';
const CONFIG = {
placeholder: 'Type to search',

View File

@ -1,6 +1,6 @@
/* eslint-disable no-unused-vars */
import { getAndRemoveConfig } from '../../core/render/utils';
import { removeDocsifyIgnoreTag } from '../../core/util/str';
import { getAndRemoveConfig } from '../../core/render/utils.js';
import { removeDocsifyIgnoreTag } from '../../core/util/str.js';
let INDEXS = {};

View File

@ -1,6 +1,8 @@
/* global afterEach, beforeAll, beforeEach */
import mock from 'xhr-mock';
import _mock from 'xhr-mock';
const mock = _mock.default;
const sideEffects = {
document: {

View File

@ -1,5 +1,5 @@
const server = require('./server.js');
import server from './server.js';
module.exports = async () => {
export default async () => {
await server.startAsync();
};

View File

@ -1,5 +1,5 @@
const server = require('./server.js');
import server from './server.js';
module.exports = async () => {
export default async () => {
server.stop();
};

View File

@ -1,5 +1,5 @@
const server = require('./server.js');
import server from './server.js';
module.exports = async config => {
export default async config => {
await server.startAsync();
};

View File

@ -1,5 +1,5 @@
const server = require('./server.js');
import server from './server.js';
module.exports = async config => {
export default async config => {
server.stop();
};

View File

@ -1,5 +1,8 @@
const browserSync = require('browser-sync').create();
const path = require('path');
import { create } from 'browser-sync';
import path from 'path';
import url from 'url';
const browserSync = create();
const hasStartArg = process.argv.includes('--start');
const serverConfig = {
@ -7,6 +10,11 @@ const serverConfig = {
port: hasStartArg ? 3002 : 3001,
};
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export const TEST_HOST = `http://${serverConfig.hostname}:${serverConfig.port}`;
function startServer(options = {}, cb = Function.prototype) {
const defaults = {
...serverConfig,
@ -73,7 +81,7 @@ function startServer(options = {}, cb = Function.prototype) {
console.log('\n');
// Set TEST_HOST environment variable
process.env.TEST_HOST = `http://${serverConfig.hostname}:${serverConfig.port}`;
process.env.TEST_HOST = TEST_HOST;
// Start server
browserSync.init(
@ -111,13 +119,20 @@ if (hasStartArg) {
});
}
// Display friendly message about manually starting a server instance
else if (require.main === module) {
else if (isMain(import.meta)) {
console.info('Use --start argument to manually start server instance');
}
module.exports = {
// Replacement for CommonJS `require.main === module`. https://2ality.com/2022/07/nodejs-esm-main.html
function isMain(meta) {
if (meta.url.startsWith('file:')) {
if (process.argv[1] === __filename) return true;
}
return false;
}
export default {
start: startServer,
startAsync: startServerAsync,
stop: stopServer,
TEST_HOST: `http://${serverConfig.hostname}:${serverConfig.port}`,
};

View File

@ -1,5 +1,5 @@
const docsifyInit = require('../helpers/docsify-init');
const { test, expect } = require('./fixtures/docsify-init-fixture');
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';
test.describe('Configuration options', () => {
test('catchPluginErrors:true (handles uncaught errors)', async ({ page }) => {

View File

@ -1,7 +1,7 @@
// Modules, constants, and variables
// -----------------------------------------------------------------------------
const docsifyInit = require('../helpers/docsify-init');
const { test, expect } = require('./fixtures/docsify-init-fixture');
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';
// Suite
// -----------------------------------------------------------------------------

View File

@ -1,6 +1,6 @@
const base = require('@playwright/test');
import { test as _test, expect as _expect } from '@playwright/test';
exports.test = base.test.extend({
export const test = _test.extend({
page: async ({ page }, use) => {
global.page = page;
@ -13,4 +13,5 @@ exports.test = base.test.extend({
await use(page);
},
});
exports.expect = base.expect;
export const expect = _expect;

View File

@ -1,5 +1,5 @@
const docsifyInit = require('../helpers/docsify-init');
const { test, expect } = require('./fixtures/docsify-init-fixture');
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';
test.describe('Index file hosting', () => {
const sharedOptions = {

View File

@ -1,5 +1,5 @@
const docsifyInit = require('../helpers/docsify-init');
const { test, expect } = require('./fixtures/docsify-init-fixture');
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';
test.describe('Plugins', () => {
test('Hook order', async ({ page }) => {

View File

@ -1,5 +1,5 @@
const docsifyInit = require('../helpers/docsify-init');
const { test, expect } = require('./fixtures/docsify-init-fixture');
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';
test.describe('Search Plugin Tests', () => {
test('search readme', async ({ page }) => {

View File

@ -1,5 +1,5 @@
const docsifyInit = require('../helpers/docsify-init');
const { test, expect } = require('./fixtures/docsify-init-fixture');
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';
test.describe('Security - Cross Site Scripting (XSS)', () => {
const sharedOptions = {

View File

@ -1,5 +1,5 @@
const docsifyInit = require('../helpers/docsify-init');
const { test, expect } = require('./fixtures/docsify-init-fixture');
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';
// Suite
// -----------------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
const docsifyInit = require('../helpers/docsify-init');
const { test, expect } = require('./fixtures/docsify-init-fixture');
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';
/**
* Navigate to a specific route in the site
@ -8,6 +8,8 @@ const { test, expect } = require('./fixtures/docsify-init-fixture');
*/
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');
}

View File

@ -1,6 +1,6 @@
const stripIndent = require('common-tags/lib/stripIndent');
const docsifyInit = require('../helpers/docsify-init');
const { test, expect } = require('./fixtures/docsify-init-fixture');
import stripIndent from 'common-tags/lib/stripIndent/index.js';
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';
const vueURLs = [
'/node_modules/vue2/dist/vue.js',
@ -179,6 +179,7 @@ test.describe('Vue.js Compatibility', () => {
await expect(page.locator('#vuefor')).toHaveText('12345');
await expect(page.locator('#vuecomponent')).toHaveText('0');
await expect(page.locator('#vuecomponent')).toHaveText('0');
// eslint-disable-next-line playwright/prefer-web-first-assertions
expect(await page.locator('#vueglobaloptions p').innerText()).toBe('');
await expect(page.locator('#vuemounts p')).toHaveText('vuemounts');
await expect(page.locator('#vuescript p')).toHaveText('vuescript');

View File

@ -1,12 +1,13 @@
/* globals page */
import _mock, { proxy } from 'xhr-mock';
const axios = require('axios');
const mock = require('xhr-mock').default;
const prettier = require('prettier');
const stripIndent = require('common-tags/lib/stripIndent');
const { proxy } = require('xhr-mock');
const { waitForSelector } = require('./wait-for');
import axios from 'axios';
import prettier from 'prettier';
import stripIndent from 'common-tags/lib/stripIndent/index.js';
// import { TEST_HOST } from '../config/server.js';
import { waitForSelector } from './wait-for.js';
const mock = _mock.default;
const docsifyPATH = '../../lib/docsify.js'; // JSDOM
const docsifyURL = '/lib/docsify.js'; // Playwright
@ -262,7 +263,8 @@ async function docsifyInit(options = {}) {
const isDocsifyLoaded = 'Docsify' in window;
if (!isDocsifyLoaded) {
require(docsifyPATH);
// require(docsifyPATH);
await import(docsifyPATH);
}
} else if (isPlaywright) {
for (const url of settings.scriptURLs) {
@ -358,4 +360,4 @@ async function docsifyInit(options = {}) {
return Promise.resolve();
}
module.exports = docsifyInit;
export default docsifyInit;

View File

@ -125,8 +125,4 @@ function waitForText(cssSelector, text, options = {}) {
});
}
module.exports = {
waitForFunction,
waitForSelector,
waitForText,
};
export { waitForFunction, waitForSelector, waitForText };

View File

@ -0,0 +1 @@
module.exports = require('../unit/.eslintrc.cjs');

View File

@ -1 +0,0 @@
module.exports = require('../unit/.eslintrc');

View File

@ -1,4 +1,5 @@
const docsifyInit = require('../helpers/docsify-init');
import { jest } from '@jest/globals';
import docsifyInit from '../helpers/docsify-init.js';
// Suite
// -----------------------------------------------------------------------------

View File

@ -1,4 +1,5 @@
const docsifyInit = require('../helpers/docsify-init');
import { jest } from '@jest/globals';
import docsifyInit from '../helpers/docsify-init.js';
// Suite
// -----------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
const docsifyInit = require('../helpers/docsify-init');
import docsifyInit from '../helpers/docsify-init.js';
// Suite
// -----------------------------------------------------------------------------

View File

@ -1,6 +1,5 @@
const { waitForFunction, waitForText } = require('../helpers/wait-for');
const docsifyInit = require('../helpers/docsify-init');
import { waitForFunction, waitForText } from '../helpers/wait-for.js';
import docsifyInit from '../helpers/docsify-init.js';
// Suite
// -----------------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
const stripIndent = require('common-tags/lib/stripIndent');
const docsifyInit = require('../helpers/docsify-init');
import stripIndent from 'common-tags/lib/stripIndent/index.js';
import docsifyInit from '../helpers/docsify-init.js';
// Suite
// -----------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
const { isExternal } = require('../../src/core/util');
import { isExternal } from '../../src/core/util/index.js';
// Core util
// -----------------------------------------------------------------------------

View File

@ -1,12 +1,8 @@
//
import { jest } from '@jest/globals';
import { greet } from './fixtures/greet.js';
import { getTimeOfDay } from './fixtures/get-time-of-day.js';
import * as getTimeOfDayModule from './fixtures/get-time-of-day.js';
// const greet = require('./fixtures/greet');
// const getTimeOfDay = require('./fixtures/get-time-of-day');
// const getTimeOfDayModule = { getTimeOfDay: getTimeOfDay };
// Suite
// -----------------------------------------------------------------------------
describe(`Example Tests`, function () {
@ -53,20 +49,26 @@ describe(`Example Tests`, function () {
});
});
// Test not working, but it is an example so no matter.
// "TypeError: setSystemTime is not available when not using modern timers"
describe('Fake Timers', function () {
// jest version issue
// test('data & time', () => {
// jest.useFakeTimers();
// jest.setSystemTime(fakeDate);
// const timeOfDay = getTimeOfDay();
// expect(timeOfDay).toContain;
// });
test.skip('data & time', () => {
const fakeDate = new Date().setHours(1);
jest.useFakeTimers('modern');
jest.setSystemTime(fakeDate);
const timeOfDay = getTimeOfDay();
expect(timeOfDay).toBe('morning');
});
});
describe('Mocks & Spies', function () {
test('mock import/require dependency using jest.fn()', () => {
const testModule = require('./fixtures/get-time-of-day.js');
const { greet: testGreet } = require('./fixtures/greet.js');
describe('Mocks & Spys', function () {
// TODO use jest.unstable_mockModule(), experimental support for mocking ES Modules, https://github.com/facebook/jest/issues/10025
test.skip('mock import dependency using jest.fn()', async () => {
const testModule = { ...(await import('./fixtures/get-time-of-day.js')) };
const { greet: testGreet } = { ...(await import('./fixtures/greet.js')) };
testModule.getTimeOfDay = jest.fn(() => 'day');
@ -77,7 +79,8 @@ describe(`Example Tests`, function () {
expect(greeting).toBe(`Good day, John!`);
});
test('mock import/require dependency using jest.doMock()', () => {
// TODO use jest.unstable_mockModule(), experimental support for mocking ES Modules, https://github.com/facebook/jest/issues/10025
test.skip('mock import dependency using jest.doMock()', async () => {
const mockModulePath = './fixtures/get-time-of-day.js';
jest.doMock(mockModulePath, () => ({
@ -85,8 +88,8 @@ describe(`Example Tests`, function () {
getTimeOfDay: jest.fn(() => 'night'),
}));
const mockGetTimeOfDay = require(mockModulePath).getTimeOfDay;
const { greet: testGreet } = require('./fixtures/greet.js');
const mockGetTimeOfDay = (await import(mockModulePath)).getTimeOfDay;
const { greet: testGreet } = await import('./fixtures/greet.js');
const timeOfDay = mockGetTimeOfDay();
const greeting = testGreet('John');
@ -104,7 +107,8 @@ describe(`Example Tests`, function () {
expect(Math.random()).toBe(0.1);
});
test('spy on import/require dependency using jest.spyOn()', () => {
// TODO use jest.unstable_mockModule(), experimental support for mocking ES Modules, https://github.com/facebook/jest/issues/10025
test.skip('spy on import dependency using jest.spyOn()', () => {
jest
.spyOn(getTimeOfDayModule, 'getTimeOfDay')
.mockImplementation(() => 'night');

View File

@ -1,4 +1,4 @@
import { getTimeOfDay } from './get-time-of-day';
import { getTimeOfDay } from './get-time-of-day.js';
export function greet(name = 'friend') {
const timeOfDay = getTimeOfDay();

View File

@ -1,12 +1,10 @@
const {
import {
removeAtag,
getAndRemoveConfig,
getAndRemoveDocisfyIgnorConfig,
} = require('../../src/core/render/utils');
const { tree } = require(`../../src/core/render/tpl`);
const { slugify } = require(`../../src/core/render/slugify`);
} from '../../src/core/render/utils.js';
import { tree } from '../../src/core/render/tpl.js';
import { slugify } from '../../src/core/render/slugify.js';
// Suite
// -----------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
const { History } = require('../../src/core/router/history/base');
import { History } from '../../src/core/router/history/base.js';
class MockHistory extends History {
parse(path) {

View File

@ -1,4 +1,4 @@
const { resolvePath } = require('../../src/core/util');
import { resolvePath } from '../../src/core/util/index.js';
// Suite
// -----------------------------------------------------------------------------