diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 7dc915d..43245b9 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -15,4 +15,5 @@ jobs: - name: Build run: | npm ci + npm test npx gulp bundle diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index e431000..37914ad 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -17,6 +17,7 @@ jobs: - name: Build run: | npm ci + npm test npx gulp bundle - name: Release uses: marvinpinto/action-automatic-releases@v1.2.1 diff --git a/gulpfile.js b/gulpfile.js index 25ce769..64d3bc1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -19,6 +19,7 @@ const glob = { all: [srcDir, previewSrcDir], css: `${srcDir}/css/**/*.css`, js: ['gulpfile.js', 'gulp.d/**/*.js', `${srcDir}/{helpers,js}/**/*.js`], + test: ['test/**/*.js'], } const cleanTask = createTask({ @@ -39,16 +40,34 @@ const lintJsTask = createTask({ call: task.lintJs(glob.js), }) +const lintTestJsTask = createTask({ + name: 'lint:testjs', + desc: 'Lint the JavaScript source files using eslint (JavaScript Standard Style)', + call: task.lintJs(glob.test), +}) + const lintTask = createTask({ name: 'lint', desc: 'Lint the CSS and JavaScript source files', - call: parallel(lintCssTask, lintJsTask), + call: parallel(lintCssTask, lintJsTask, lintTestJsTask), +}) + +const formatJsTask = createTask({ + name: 'format:js', + desc: 'Format the JavaScript source files using prettify (JavaScript Standard Style)', + call: task.format(glob.test), +}) + +const formatTestJsTask = createTask({ + name: 'format:testjs', + desc: 'Format the JavaScript source files using prettify (JavaScript Standard Style)', + call: task.format(glob.test), }) const formatTask = createTask({ name: 'format', - desc: 'Format the JavaScript source files using prettify (JavaScript Standard Style)', - call: task.format(glob.js), + desc: 'Lint the CSS and JavaScript source files', + call: parallel(formatJsTask, formatTestJsTask), }) const buildTask = createTask({ diff --git a/package-lock.json b/package-lock.json index 7e752a8..fc5baf5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,8 +18,12 @@ "autoprefixer": "~10.4", "browser-pack-flat": "~3.5", "browserify": "~17.0", + "chai": "~4.3", + "chai-fs": "~2.0", + "chai-spies": "~1.0", "core-js": "~3.26", "cssnano": "~5.1", + "dirty-chai": "~2.0", "eslint": "~6.8", "eslint-config-standard": "~14.1", "eslint-plugin-import": "~2.22", @@ -42,6 +46,7 @@ "js-yaml": "~4.1", "merge-stream": "~2.0", "micromodal": "^0.4.10", + "mocha": "~10.2", "postcss": "~8.4", "postcss-calc": "~8.2", "postcss-custom-properties": "~12.1", @@ -1535,6 +1540,19 @@ "node": ">=0.10.0" } }, + "node_modules/array-events": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/array-events/-/array-events-0.2.0.tgz", + "integrity": "sha512-Js6+JM/MxB72WeODWcUOOD/BWRqx6QTff8FWvweERQ0MdzViScUJV4XwRFnXvyvbfhuwWNrwhid7IJe2ux3r4Q==", + "dev": true, + "dependencies": { + "async-arrays": "*", + "extended-emitter": "*" + }, + "engines": { + "node": "*" + } + }, "node_modules/array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -1777,6 +1795,15 @@ "inherits": "2.0.1" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -1795,6 +1822,18 @@ "node": ">=4" } }, + "node_modules/async-arrays": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/async-arrays/-/async-arrays-2.0.0.tgz", + "integrity": "sha512-lMm6njQEX7gHbdX/b+PGBDXD/Vwg40BKSatlOaWNxrW/O5wYzARmoh+50h58s3hsyzGPU5+xYndwtc+m91yLiw==", + "dev": true, + "dependencies": { + "sift": "*" + }, + "engines": { + "node": "*" + } + }, "node_modules/async-done": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", @@ -2308,6 +2347,18 @@ "file-uri-to-path": "1.0.0" } }, + "node_modules/bit-mask": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bit-mask/-/bit-mask-1.0.2.tgz", + "integrity": "sha512-UGtq08LSiazxL4zVmBzrhdCWnT4RWx3JhhD/3crhfv8xxjnVHxf/WoVjEstjSUaZeZRP7kZrWNqup1VvUClCaQ==", + "dev": true, + "dependencies": { + "array-events": "^0.2.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/bl": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", @@ -2448,6 +2499,12 @@ "resolve": "^1.17.0" } }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "node_modules/browserify": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", @@ -2822,6 +2879,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "dev": true + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2909,6 +2972,52 @@ "node": ">=4" } }, + "node_modules/chai": { + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-fs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chai-fs/-/chai-fs-2.0.0.tgz", + "integrity": "sha512-PGfINFH/7XrQBnbp5/MnbFtzBL1//erKs+uoUdyo7KnW0mUX13L6bTO3Jm8OIexSVSh0Y+aaFhhbxyDtb679DA==", + "dev": true, + "dependencies": { + "bit-mask": "^1.0.1", + "readdir-enhanced": "^1.4.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "chai": ">= 1.6.1 < 5" + } + }, + "node_modules/chai-spies": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-1.0.0.tgz", + "integrity": "sha512-elF2ZUczBsFoP07qCfMO/zeggs8pqCf3fZGyK5+2X4AndS8jycZYID91ztD9oQ7d/0tnS963dPkd0frQEThDsg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + }, + "peerDependencies": { + "chai": "*" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -2959,6 +3068,18 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -4153,6 +4274,18 @@ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4294,6 +4427,15 @@ "node": ">=0.8.0" } }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -4323,6 +4465,15 @@ "node": ">=8" } }, + "node_modules/dirty-chai": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/dirty-chai/-/dirty-chai-2.0.1.tgz", + "integrity": "sha512-ys79pWKvDMowIDEPC6Fig8d5THiC0DJ2gmTeGzVAoEH18J8OzLud0Jh7I9IWg3NSk8x2UocznUuFmfHCXYZx9w==", + "dev": true, + "peerDependencies": { + "chai": ">=2.2.1 <5" + } + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -4773,6 +4924,12 @@ "event-emitter": "~0.3.5" } }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, "node_modules/es6-set": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.6.tgz", @@ -5620,6 +5777,18 @@ "node": ">=0.10.0" } }, + "node_modules/extended-emitter": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/extended-emitter/-/extended-emitter-1.6.0.tgz", + "integrity": "sha512-TNF4xMKL9aKYTR2cTNkKYMUnKzzjfV5Nl6TX45smJ/796CmaFt+KCyidgGdod0Kgj5VSL+ctNIGVf+i1l3e+UA==", + "dev": true, + "dependencies": { + "sift": "*" + }, + "engines": { + "node": "*" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -6030,6 +6199,15 @@ "node": ">= 0.10" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -6250,6 +6428,15 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", @@ -6458,6 +6645,12 @@ "node": ">=0.10.0" } }, + "node_modules/glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==", + "dev": true + }, "node_modules/glob-watcher": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", @@ -7371,6 +7564,15 @@ "minimalistic-assert": "^1.0.1" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, "node_modules/highlight.js": { "version": "9.18.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.3.tgz", @@ -9196,6 +9398,15 @@ "node": ">=0.10.0" } }, + "node_modules/loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, "node_modules/lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -9905,142 +10116,627 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, - "node_modules/module-deps": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", - "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", - "dev": true, - "dependencies": { - "browser-resolve": "^2.0.0", - "cached-path-relative": "^1.0.2", - "concat-stream": "~1.6.0", - "defined": "^1.0.0", - "detective": "^5.2.0", - "duplexer2": "^0.1.2", - "inherits": "^2.0.1", - "JSONStream": "^1.0.3", - "parents": "^1.0.0", - "readable-stream": "^2.0.2", - "resolve": "^1.4.0", - "stream-combiner2": "^1.1.1", - "subarg": "^1.0.0", - "through2": "^2.0.0", - "xtend": "^4.0.0" + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" }, "bin": { - "module-deps": "bin/cmd.js" + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" } }, - "node_modules/module-deps/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "node_modules/mocha/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "engines": { + "node": ">=6" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mute-stdout": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", - "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "node_modules/mutexify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/mutexify/-/mutexify-1.4.0.tgz", - "integrity": "sha512-pbYSsOrSB/AKN5h/WzzLRMFgZhClWccf2XIB4RSMC8JbquiB0e0/SH5AIfdQMdyHmYtv4seU7yV/TvAwPLJ1Yg==", + "node_modules/mocha/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "queue-tick": "^1.0.0" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", - "dev": true, - "optional": true - }, - "node_modules/nanobench": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nanobench/-/nanobench-2.1.1.tgz", - "integrity": "sha512-z+Vv7zElcjN+OpzAxAquUayFLGK3JI/ubCl0Oh64YQqsTGG09CGqieJVQw4ui8huDnnAgrvTv93qi5UaOoNj8A==", + "node_modules/mocha/node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { - "browser-process-hrtime": "^0.1.2", - "chalk": "^1.1.3", - "mutexify": "^1.1.0", - "pretty-hrtime": "^1.0.2" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, - "bin": { - "nanobench": "run.js", - "nanobench-compare": "compare.js" - } - }, - "node_modules/nanobench/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/nanobench/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "node_modules/mocha/node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/nanobench/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "node_modules/mocha/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "fill-range": "^7.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/nanobench/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "node_modules/mocha/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { - "ansi-regex": "^2.0.0" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/mocha/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/mocha/node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/module-deps": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", + "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", + "dev": true, + "dependencies": { + "browser-resolve": "^2.0.0", + "cached-path-relative": "^1.0.2", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.2.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "JSONStream": "^1.0.3", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + }, + "bin": { + "module-deps": "bin/cmd.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/module-deps/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/mutexify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/mutexify/-/mutexify-1.4.0.tgz", + "integrity": "sha512-pbYSsOrSB/AKN5h/WzzLRMFgZhClWccf2XIB4RSMC8JbquiB0e0/SH5AIfdQMdyHmYtv4seU7yV/TvAwPLJ1Yg==", + "dev": true, + "dependencies": { + "queue-tick": "^1.0.0" + } + }, + "node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "dev": true, + "optional": true + }, + "node_modules/nanobench": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nanobench/-/nanobench-2.1.1.tgz", + "integrity": "sha512-z+Vv7zElcjN+OpzAxAquUayFLGK3JI/ubCl0Oh64YQqsTGG09CGqieJVQw4ui8huDnnAgrvTv93qi5UaOoNj8A==", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^0.1.2", + "chalk": "^1.1.3", + "mutexify": "^1.1.0", + "pretty-hrtime": "^1.0.2" + }, + "bin": { + "nanobench": "run.js", + "nanobench-compare": "compare.js" + } + }, + "node_modules/nanobench/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanobench/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanobench/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanobench/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, "node_modules/nanobench/node_modules/supports-color": { @@ -10994,6 +11690,15 @@ "node": ">=8" } }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -13002,6 +13707,17 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/readdir-enhanced": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/readdir-enhanced/-/readdir-enhanced-1.5.2.tgz", + "integrity": "sha512-oncAoS9LLjy/+DeZfSAdZBI/iFJGcPCOp44RPFI6FIMHuxt5CC5P0cUZ9mET+EZB9ONhcEvAids/lVRkj0sTHw==", + "dev": true, + "dependencies": { + "call-me-maybe": "^1.0.1", + "es6-promise": "^4.1.0", + "glob-to-regexp": "^0.3.0" + } + }, "node_modules/readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", @@ -13706,6 +14422,15 @@ "node": ">= 0.6" } }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -13978,6 +14703,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.0.1.tgz", + "integrity": "sha512-10rmPF5nuz5UdKuhhxgfS7Vz1aIRGmb+kn5Zy6bntCgNwkbZc0a7Z2dUw2Y9wSoRrBzf7Oim81SUsYdOkVnI8Q==", + "dev": true + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -16384,6 +17115,15 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -17265,6 +18005,12 @@ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, "node_modules/wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", @@ -17440,6 +18186,54 @@ "node": ">=0.10.0" } }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/yargs/node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -17618,6 +18412,18 @@ "buffer-crc32": "~0.2.3" } }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", diff --git a/package.json b/package.json index f96e8e3..193234a 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "url": "https://github.com/spring-io/antora-ui-spring" }, "scripts": { - "start": "gulp preview" + "start": "gulp preview", + "test": "_mocha" }, "engines": { "node": ">=16.0.0" @@ -25,6 +26,10 @@ "autoprefixer": "~10.4", "browser-pack-flat": "~3.5", "browserify": "~17.0", + "chai": "~4.3", + "chai-fs": "~2.0", + "chai-spies": "~1.0", + "dirty-chai": "~2.0", "core-js": "~3.26", "cssnano": "~5.1", "eslint": "~6.8", @@ -47,6 +52,7 @@ "highlight.js": "9.18.3", "instantsearch.js": "^4.54.1", "js-yaml": "~4.1", + "mocha": "~10.2", "merge-stream": "~2.0", "micromodal": "^0.4.10", "postcss": "~8.4", diff --git a/src/helpers/related_projects.js b/src/helpers/related_projects.js index 2dde4ab..f4d3ee4 100644 --- a/src/helpers/related_projects.js +++ b/src/helpers/related_projects.js @@ -1,257 +1,26 @@ 'use strict' -module.exports = (categories, projectIds) => relatedProjects(categories, projectIds) +module.exports = (categories, projectIds, projects) => relatedProjects(categories, projectIds, projects) -function relatedProjects (categories = [], projectIds = []) { - const projectDocumentation = [ - { - href: 'https://docs.spring.io/spring-framework/reference/', - id: 'framework', - text: 'Spring Framework', - categories: ['core'], - }, - { - text: 'Spring Cloud', - id: 'cloud', - categories: ['cloud'], - children: [ - { - href: 'https://docs.spring.io/spring-cloud-build/reference/', - id: 'cloud-build', - text: 'Spring Cloud Build', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-bus/reference/', - id: 'cloud-bus', - text: 'Spring Cloud Bus', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-circuitbreaker/reference/', - id: 'cloud-circuitbreaker', - text: 'Spring Cloud Circuit Breaker', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-commons/reference/', - id: 'cloud-commons', - text: 'Spring Cloud Commons', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-config/reference/', - id: 'cloud-config', - text: 'Spring Cloud Config', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-consul/reference/', - id: 'cloud-consul', - text: 'Spring Cloud Consul', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-contract/reference/', - id: 'cloud-contract', - text: 'Spring Cloud Contract', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-function/reference/', - id: 'cloud-function', - text: 'Spring Cloud Function', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-gateway/reference/', - id: 'cloud-gateway', - text: 'Spring Cloud Gateway', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-kubernetes/reference/', - id: 'cloud-kubernetes', - text: 'Spring Cloud Kubernetes', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-netflix/reference/', - id: 'cloud-netflix', - text: 'Spring Cloud Netflix', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-openfeign/reference/', - id: 'cloud-openfeign', - text: 'Spring Cloud OpenFeign', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-stream/reference/', - id: 'cloud-stream', - text: 'Spring Cloud Stream', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-task/reference/', - id: 'cloud-task', - text: 'Spring Cloud Task', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-vault/reference/', - id: 'cloud-vault', - text: 'Spring Cloud Vault', - categories: ['cloud'], - }, - { - href: 'https://docs.spring.io/spring-cloud-zookeeper/reference/', - id: 'cloud-zookeeper', - text: 'Spring Cloud Zookeeper', - categories: ['cloud'], - }, - ], - }, - { - text: 'Spring Data', - id: 'data', - categories: ['data'], - children: [ - { - href: 'https://docs.spring.io/spring-data/cassandra/reference/', - id: 'data/cassandra', - text: 'Spring Data Cassandra', - categories: ['data'], - }, - { - href: 'https://docs.spring.io/spring-data/commons/reference/', - id: 'data/commons', - text: 'Spring Data Commons', - categories: ['data'], - }, - { - href: 'https://docs.spring.io/spring-data/couchbase/reference/', - id: 'data/couchbase', - text: 'Spring Data Couchbase', - categories: ['data'], - }, - { - href: 'https://docs.spring.io/spring-data/elasticsearch/reference/', - id: 'data/elasticsearch', - text: 'Spring Data Elasticsearch', - categories: ['data'], - }, - { - href: 'https://docs.spring.io/spring-data/jpa/reference/', - id: 'data/jpa', - text: 'Spring Data JPA', - categories: ['data'], - }, - { - href: 'https://docs.spring.io/spring-data/keyvalue/reference/', - id: 'data/keyvalue', - text: 'Spring Data KeyValue', - categories: ['data'], - }, - { - href: 'https://docs.spring.io/spring-data/ldap/reference/', - id: 'data/ldap', - text: 'Spring Data LDAP', - categories: ['data'], - }, - { - href: 'https://docs.spring.io/spring-data/mongodb/reference/', - id: 'data/mongodb', - text: 'Spring Data MongoDB', - categories: ['data'], - }, - { - href: 'https://docs.spring.io/spring-data/neo4j/reference/', - id: 'data/neo4j', - text: 'Spring Data Neo4j', - categories: ['data'], - }, - { - href: 'https://docs.spring.io/spring-data/redis/reference/', - id: 'data/redis', - text: 'Spring Data Redis', - categories: ['data'], - }, - { - href: 'https://docs.spring.io/spring-data/relational/reference/', - id: 'data/relational', - text: 'Spring Data JDBC & R2DBC', - categories: ['data'], - }, - { - href: 'https://docs.spring.io/spring-data/rest/reference/', - id: 'data/rest', - text: 'Spring Data REST', - categories: ['data'], - }, - ], - }, - { href: 'https://docs.spring.io/spring-integration/reference/', id: 'integration', text: 'Spring Integration' }, - { href: 'https://docs.spring.io/spring-batch/reference/', id: 'batch', text: 'Spring Batch' }, - { - href: 'https://docs.spring.io/spring-security/reference/', - id: 'security', - text: 'Spring Security', - categories: ['security', 'core'], - children: [ - { - href: 'https://docs.spring.io/spring-authorization-server/reference/', - id: 'authorization-server', - text: 'Spring Authorization Server', - categories: ['security'], - }, - { - href: 'https://docs.spring.io/spring-ldap/reference/', - id: 'ldap', - text: 'Spring LDAP', - categories: ['security'], - }, - { - href: 'https://docs.spring.io/spring-security-kerberos/reference/', - id: 'security-kerberos', - text: 'Spring Security Kerberos', - categories: ['security'], - }, - { - href: 'https://docs.spring.io/spring-session/reference/', - id: 'session', - text: 'Spring Session', - categories: ['security'], - }, - { - href: 'https://docs.spring.io/spring-vault/reference/', - id: 'vault', - text: 'Spring Vault', - categories: ['security'], - }, - ], - }, - { href: 'https://docs.spring.io/spring-ai/reference/', id: 'ai', text: 'Spring AI' }, - { href: 'https://docs.spring.io/spring-cli/reference/', id: 'cli', text: 'Spring CLI' }, - { href: 'https://docs.spring.io/spring-graphql/reference/', id: 'graphql', text: 'Spring GraphQL' }, - { href: 'https://docs.spring.io/spring-kafka/reference/', id: 'kafka', text: 'Spring for Apache Kafka' }, - { href: 'https://docs.spring.io/spring-modulith/reference/', id: 'modulith', text: 'Spring Modulith' }, - { href: 'https://docs.spring.io/spring-pulsar/reference/', id: 'pulsar', text: 'Spring for Apache Pulsar' }, - { href: 'https://docs.spring.io/spring-shell/reference/', id: 'shell', text: 'Spring Shell' }, - ] - return projectDocumentation.filter( - (p) => filterProjectByCategories(p, categories) || filterProjectByProjectIds(p, projectIds) +function relatedProjects (categories, projectIds, projects) { + if (categories.length === 0 && projectIds.length === 0) { + return projects + } + const result = projects.filter( + (project) => filterProjectByCategories(project, categories) || filterProjectByProjectIds(project, projectIds) ) + for (const project of result) { + if (project.children) { + project.children = relatedProjects(categories, projectIds, project.children) + } + } + return result } function filterProjectByCategories (project, categories) { - return ( - categories.length === 0 || - categories.some((category) => project.categories && project.categories.includes(category)) - ) + return project.categories && categories.some((category) => project.categories.includes(category)) } function filterProjectByProjectIds (project, projectIds) { - return projectIds.length === 0 || projectIds.some((projectId) => project.id === projectId) + return projectIds.some((projectId) => project.id === projectId) } diff --git a/src/helpers/spring-projects.js b/src/helpers/spring-projects.js new file mode 100644 index 0000000..8e2fe74 --- /dev/null +++ b/src/helpers/spring-projects.js @@ -0,0 +1,239 @@ +'use strict' + +module.exports = () => [ + { + href: 'https://docs.spring.io/spring-framework/reference/', + id: 'framework', + text: 'Spring Framework', + categories: ['core'], + }, + { + text: 'Spring Cloud', + id: 'cloud', + categories: ['cloud'], + children: [ + { + href: 'https://docs.spring.io/spring-cloud-build/reference/', + id: 'cloud-build', + text: 'Spring Cloud Build', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-bus/reference/', + id: 'cloud-bus', + text: 'Spring Cloud Bus', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-circuitbreaker/reference/', + id: 'cloud-circuitbreaker', + text: 'Spring Cloud Circuit Breaker', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-commons/reference/', + id: 'cloud-commons', + text: 'Spring Cloud Commons', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-config/reference/', + id: 'cloud-config', + text: 'Spring Cloud Config', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-consul/reference/', + id: 'cloud-consul', + text: 'Spring Cloud Consul', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-contract/reference/', + id: 'cloud-contract', + text: 'Spring Cloud Contract', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-function/reference/', + id: 'cloud-function', + text: 'Spring Cloud Function', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-gateway/reference/', + id: 'cloud-gateway', + text: 'Spring Cloud Gateway', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-kubernetes/reference/', + id: 'cloud-kubernetes', + text: 'Spring Cloud Kubernetes', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-netflix/reference/', + id: 'cloud-netflix', + text: 'Spring Cloud Netflix', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-openfeign/reference/', + id: 'cloud-openfeign', + text: 'Spring Cloud OpenFeign', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-stream/reference/', + id: 'cloud-stream', + text: 'Spring Cloud Stream', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-task/reference/', + id: 'cloud-task', + text: 'Spring Cloud Task', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-vault/reference/', + id: 'cloud-vault', + text: 'Spring Cloud Vault', + categories: ['cloud'], + }, + { + href: 'https://docs.spring.io/spring-cloud-zookeeper/reference/', + id: 'cloud-zookeeper', + text: 'Spring Cloud Zookeeper', + categories: ['cloud'], + }, + ], + }, + { + text: 'Spring Data', + id: 'data', + categories: ['data'], + children: [ + { + href: 'https://docs.spring.io/spring-data/cassandra/reference/', + id: 'data/cassandra', + text: 'Spring Data Cassandra', + categories: ['data'], + }, + { + href: 'https://docs.spring.io/spring-data/commons/reference/', + id: 'data/commons', + text: 'Spring Data Commons', + categories: ['data'], + }, + { + href: 'https://docs.spring.io/spring-data/couchbase/reference/', + id: 'data/couchbase', + text: 'Spring Data Couchbase', + categories: ['data'], + }, + { + href: 'https://docs.spring.io/spring-data/elasticsearch/reference/', + id: 'data/elasticsearch', + text: 'Spring Data Elasticsearch', + categories: ['data'], + }, + { + href: 'https://docs.spring.io/spring-data/jpa/reference/', + id: 'data/jpa', + text: 'Spring Data JPA', + categories: ['data'], + }, + { + href: 'https://docs.spring.io/spring-data/keyvalue/reference/', + id: 'data/keyvalue', + text: 'Spring Data KeyValue', + categories: ['data'], + }, + { + href: 'https://docs.spring.io/spring-data/ldap/reference/', + id: 'data/ldap', + text: 'Spring Data LDAP', + categories: ['data'], + }, + { + href: 'https://docs.spring.io/spring-data/mongodb/reference/', + id: 'data/mongodb', + text: 'Spring Data MongoDB', + categories: ['data'], + }, + { + href: 'https://docs.spring.io/spring-data/neo4j/reference/', + id: 'data/neo4j', + text: 'Spring Data Neo4j', + categories: ['data'], + }, + { + href: 'https://docs.spring.io/spring-data/redis/reference/', + id: 'data/redis', + text: 'Spring Data Redis', + categories: ['data'], + }, + { + href: 'https://docs.spring.io/spring-data/relational/reference/', + id: 'data/relational', + text: 'Spring Data JDBC & R2DBC', + categories: ['data'], + }, + { + href: 'https://docs.spring.io/spring-data/rest/reference/', + id: 'data/rest', + text: 'Spring Data REST', + categories: ['data'], + }, + ], + }, + { href: 'https://docs.spring.io/spring-integration/reference/', id: 'integration', text: 'Spring Integration' }, + { href: 'https://docs.spring.io/spring-batch/reference/', id: 'batch', text: 'Spring Batch' }, + { + href: 'https://docs.spring.io/spring-security/reference/', + id: 'security', + text: 'Spring Security', + categories: ['security', 'core'], + children: [ + { + href: 'https://docs.spring.io/spring-authorization-server/reference/', + id: 'authorization-server', + text: 'Spring Authorization Server', + categories: ['security'], + }, + { + href: 'https://docs.spring.io/spring-ldap/reference/', + id: 'ldap', + text: 'Spring LDAP', + categories: ['security'], + }, + { + href: 'https://docs.spring.io/spring-security-kerberos/reference/', + id: 'security-kerberos', + text: 'Spring Security Kerberos', + categories: ['security'], + }, + { + href: 'https://docs.spring.io/spring-session/reference/', + id: 'session', + text: 'Spring Session', + categories: ['security'], + }, + { + href: 'https://docs.spring.io/spring-vault/reference/', + id: 'vault', + text: 'Spring Vault', + categories: ['security'], + }, + ], + }, + { href: 'https://docs.spring.io/spring-ai/reference/', id: 'ai', text: 'Spring AI' }, + { href: 'https://docs.spring.io/spring-cli/reference/', id: 'cli', text: 'Spring CLI' }, + { href: 'https://docs.spring.io/spring-graphql/reference/', id: 'graphql', text: 'Spring GraphQL' }, + { href: 'https://docs.spring.io/spring-kafka/reference/', id: 'kafka', text: 'Spring for Apache Kafka' }, + { href: 'https://docs.spring.io/spring-modulith/reference/', id: 'modulith', text: 'Spring Modulith' }, + { href: 'https://docs.spring.io/spring-pulsar/reference/', id: 'pulsar', text: 'Spring for Apache Pulsar' }, + { href: 'https://docs.spring.io/spring-shell/reference/', id: 'shell', text: 'Spring Shell' }, +] diff --git a/src/partials/article-spring-projects.hbs b/src/partials/article-spring-projects.hbs index 9996e55..e9408e1 100644 --- a/src/partials/article-spring-projects.hbs +++ b/src/partials/article-spring-projects.hbs @@ -5,7 +5,7 @@

Spring Projects

diff --git a/src/partials/related-projects.hbs b/src/partials/related-projects.hbs index d2c8ccc..71192ed 100644 --- a/src/partials/related-projects.hbs +++ b/src/partials/related-projects.hbs @@ -2,7 +2,7 @@
  • Related Spring Documentation diff --git a/test/harness/index.js b/test/harness/index.js new file mode 100644 index 0000000..1e0105c --- /dev/null +++ b/test/harness/index.js @@ -0,0 +1,89 @@ +/* eslint-env mocha */ +'use strict' + +process.env.NODE_ENV = 'test' + +const chai = require('chai') +const fsp = require('node:fs/promises') +const http = require('http') +const ospath = require('path') +const { once } = require('events') + +chai.use(require('chai-fs')) +chai.use(require('chai-spies')) +// dirty-chai must be loaded after the other plugins +// see https://github.com/prodatakey/dirty-chai#plugin-assertions +chai.use(require('dirty-chai')) + +const cleanDir = (dir, { create } = {}) => + fsp.rm(dir, { recursive: true, force: true }).then(() => (create ? fsp.mkdir(dir, { recursive: true }) : undefined)) + +const filterLines = (str, predicate) => str.split('\n').filter(predicate).join('\n') + +const heredoc = (literals, ...vals) => { + const str = literals + .reduce((accum, chunk, idx) => { + if (!idx) return [chunk] + let val = vals[idx - 1] + let match + const last = accum[accum.length - 1] + if (last.charAt(last.length - 1) === ' ' && (match = /\n( +)$/.exec(last))) { + const indent = match[1] + const valLines = val.split(/^/m) + if (valLines.length > 1) val = valLines.map((l, i) => (i ? indent + l : l)).join('') + } + return accum.concat(val, chunk) + }, undefined) + .join('') + .trimEnd() + let lines = str.split(/^/m) + if (lines[0] === '\n') lines = lines.slice(1) + if (lines.length < 2) return str // discourage use of heredoc in this case + const last = lines.pop() + if (last != null) { + lines.push(last[last.length - 1] === '\\' && last[last.length - 2] === ' ' ? last.slice(0, -2) + '\n' : last) + } + const indentRx = /^ +/ + const indentSize = Math.min(...lines.filter((l) => l.charAt() === ' ').map((l) => l.match(indentRx)[0].length)) + return (indentSize ? lines.map((l) => (l.charAt() === ' ' ? l.slice(indentSize) : l)) : lines).join('') +} + +const startWebServer = (hostname, rootDir) => { + const contentTypes = {} + const handler = { + delegate: (request, response) => { + fsp.readFile(ospath.join(rootDir, request.url)).then( + (content) => { + const ext = ospath.extname(request.url) + response.writeHead(200, { 'Content-Type': contentTypes[ext] || `application/${ext.slice(1)}` }) + response.end(content) + }, + () => { + response.writeHead(404, { 'Content-Type': 'text/html' }) + response.end('Not Found', 'utf8') + } + ) + }, + } + const httpServer = http.createServer((request, response) => handler.delegate(request, response)) + return once(httpServer.listen(0), 'listening').then(() => { + httpServer.shutdown = function () { + return once(this.close() || this, 'close') + }.bind(httpServer) + httpServer.handler = function (h) { + handler.delegate = h + } + return [httpServer, new URL(`http://${hostname}:${httpServer.address().port}`).toString().slice(0, -1)] + }) +} + +// NOTE async keyword only needed on fn declaration if the function it calls does not always return a Promise +const trapAsyncError = (fn, ...args) => + fn(...args).then( + (returnValue) => () => returnValue, + (err) => () => { + throw err + } + ) + +module.exports = { cleanDir, expect: chai.expect, filterLines, heredoc, spy: chai.spy, startWebServer, trapAsyncError } diff --git a/test/harness/mocha/ci-reporter.js b/test/harness/mocha/ci-reporter.js new file mode 100644 index 0000000..c8bcf58 --- /dev/null +++ b/test/harness/mocha/ci-reporter.js @@ -0,0 +1,14 @@ +'use strict' + +const { Base, Dot, XUnit } = require('mocha').reporters + +// A Mocha reporter that combines the dot and xunit reporters. +class CI extends Base { + constructor (runner, options) { + super(runner, options) + new Dot(runner, options) // eslint-disable-line no-new + new XUnit(runner, options) // eslint-disable-line no-new + } +} + +module.exports = CI diff --git a/test/harness/mocha/config.js b/test/harness/mocha/config.js new file mode 100644 index 0000000..2a37ff2 --- /dev/null +++ b/test/harness/mocha/config.js @@ -0,0 +1,39 @@ +'use strict' + +const config = { + checkLeaks: true, + globals: ['__coverage__'], // we know __coverage__ is global, so no need to check + mochaGlobalTeardown () { + if (!this.failures) logCoverageReportPath() + }, + require: __filename, + spec: resolveSpec(), + timeout: 10 * 60 * 1000, +} + +if (process.env.npm_config_watch) config.watch = true +if (process.env.CI) { + Object.assign(config, { + forbidOnly: true, + reporter: './test/harness/mocha/ci-reporter', + 'reporter-option': ['output=reports/tests-xunit.xml'], + }) +} + +function logCoverageReportPath () { + if (!process.env.NYC_PROCESS_ID) return + const { CI_PROJECT_PATH, CI_JOB_ID } = process.env + const coverageReportRelpath = 'reports/lcov-report/index.html' + const coverageReportURL = CI_JOB_ID + ? `https://gitlab.com/${CI_PROJECT_PATH}/-/jobs/${CI_JOB_ID}/artifacts/file/${coverageReportRelpath}` + : require('node:url').pathToFileURL(coverageReportRelpath) + console.log(`Coverage report: ${coverageReportURL}`) +} + +function resolveSpec () { + const spec = process.argv[2] + if (spec && !spec.startsWith('-')) return spec + return 'test/**/*-test.js' +} + +module.exports = config diff --git a/test/related_projects-test.js b/test/related_projects-test.js new file mode 100644 index 0000000..0bc7e5c --- /dev/null +++ b/test/related_projects-test.js @@ -0,0 +1,103 @@ +/* eslint-env mocha */ +'use strict' + +const { expect } = require('./harness') +const relatedProjects = require('../src/helpers/related_projects.js') + +describe('related_projects', () => { + const projects = [ + project('framework', { + categories: ['core'], + }), + project('security', { + categories: ['security'], + children: [ + project('session', { + categories: ['security'], + }), + ], + }), + ] + + it('projects categories and projectIds empty returns all results', () => { + const projectIds = relatedProjectIds([], [], projects) + expect(projectIds).is.eql(['framework', 'security', 'session']) + }) + + it('projectsIds empty and categories specified returns only categories', () => { + const projectIds = relatedProjectIds(['security'], [''], projects) + expect(projectIds).is.eql(['security', 'session']) + }) + + it('projectsIds specified and categories empty returns only ids', () => { + const projectIds = relatedProjectIds([], ['framework'], projects) + expect(projectIds).is.eql(['framework']) + }) + + it('project id is substring', () => { + const projects = [ + project('cloud', { + categories: ['cloud'], + children: [ + project('cloud-stream', { + categories: ['cloud'], + }), + project('cloud-gateway', { + categories: ['cloud'], + }), + ], + }), + ] + const projectIds = relatedProjectIds([], ['cloud', 'cloud-stream'], projects) + expect(projectIds).is.eql(['cloud', 'cloud-stream']) + }) + + it('children not included if parent is not', () => { + const projects = [ + project('cloud', { + categories: ['cloud'], + children: [ + project('cloud-stream', { + categories: ['cloud'], + }), + project('cloud-gateway', { + categories: ['cloud'], + }), + ], + }), + ] + const projectIds = relatedProjectIds([], ['cloud-stream'], projects) + expect(projectIds).is.eql([]) + }) + + it('categories defined but project.categories is null', () => { + const projects = [ + project('cloud', { categories: ['cloud'] }), + project('spring-integration'), + ] + const projectIds = relatedProjectIds(['cloud'], [''], projects) + expect(projectIds).is.eql(['cloud']) + }) +}) + +function project (id, attrs) { + return { href: `https://docs.spring.io/spring-${id}/reference/`, ...attrs, id: id, text: `Spring ${id}` } +} + +function relatedProjectIds (categories, projectIds, projects) { + return collectIdsForProjects(relatedProjects(categories, projectIds, projects)) +} + +function collectIdsForProjects (projects, results = []) { + for (const p of projects) { + collectIds(p, results) + } + return results +} + +function collectIds (p, results) { + results.push(p.id) + if (p.children) { + collectIdsForProjects(p.children, results) + } +}