diff --git a/strcalc/src/main/frontend/bin/jsdoc b/strcalc/src/main/frontend/bin/jsdoc deleted file mode 100755 index 15756de..0000000 --- a/strcalc/src/main/frontend/bin/jsdoc +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -# jsdoc wrapper used by the "jsdoc" package.json script -# -# Removes the existing destination directory if it exists, runs JSDoc, and emits -# the relative path to the generated index.html file. -# -# Prompts the user to install JSDoc if not present. It's not a package.json -# devDependency because it seems that many folks browse JSDoc in their IDE -# without needing to generate the HTML version. - -if ! command -v jsdoc > /dev/null; then - echo \"Run 'pnpm add -g jsdoc' to install JSDoc: https://jsdoc.app\" - exit 1 -fi - -ARGS=() -DESTINATION="" - -# Discover the output directory, since JSDoc doesn't have a setting to emit it. -while [[ "$#" -ne 0 ]]; do - curArg="$1" - shift - ARGS+=("$curArg") - - case "$curArg" in - -c|--configure) - if [[ -z "$DESTINATION" ]]; then - # All bets are off if the destination property contains an escaped '"' or - # there's more than one "destination" property defined. - DESTINATION="$(sed -ne 's/.*"destination": *"\([^"]*\)".*/\1/p' < "$1")" - fi - ARGS+=("$1") - shift - ;; - -d|--destination) - DESTINATION="$1" - ARGS+=("$1") - shift - ;; - *) - ;; - esac -done - -# "out" is the JSDoc default directory. -DESTINATION="${DESTINATION:-out}" -rm -rf "$DESTINATION" - -if jsdoc "${ARGS[@]}"; then - exec find "$DESTINATION" -name index.html -print -quit -fi diff --git a/strcalc/src/main/frontend/bin/jsdoc-cli-wrapper.js b/strcalc/src/main/frontend/bin/jsdoc-cli-wrapper.js deleted file mode 100755 index 630056a..0000000 --- a/strcalc/src/main/frontend/bin/jsdoc-cli-wrapper.js +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env node -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -/** - * @file JSDoc command line interface wrapper. - * - * Removes the existing destination directory if it exists, runs JSDoc, and - * emits the relative path to the generated index.html file. - * @author Mike Bland - */ - -import { spawn } from 'node:child_process' -import { access, readdir, readFile, rm } from 'node:fs/promises' -import path from 'node:path' -import { exit, stdout } from 'node:process' - -try { - const {exitCode, indexHtml} = await runJsdoc( - process.argv.slice(2), process.env, process.platform - ) - if (indexHtml !== undefined) stdout.write(`${indexHtml}\n`) - exit(exitCode) - -} catch (err) { - console.error(err) - exit(1) -} - -/** - * Result of the `jsdoc` execution - * @typedef {object} RunJsdocResults - * @property {number} exitCode - 0 on success, nonzero on failure - * @property {string} indexHtml - path to the generated index.html file - */ - -/** - * Removes the existing JSDoc directory, runs `jsdoc`, and emits the result path - * @param {string[]} argv - JSDoc command line interface arguments - * @param {object} env - environment variables, presumably process.env - * @param {string} platform - the process.platform string - * @returns {Promise} - result of `jsdoc` execution - */ -async function runJsdoc(argv, env, platform) { - let jsdocPath - - try { - jsdocPath = await getPath('jsdoc', env, platform) - } catch { - return Promise.reject( - 'Run \'pnpm add -g jsdoc\' to install JSDoc: https://jsdoc.app\n' - ) - } - - const {destination, willGenerate} = await analyzeArgv(argv) - - if (willGenerate) await rm(destination, {force: true, recursive: true}) - - const exitCode = await new Promise(resolve => { - spawn(jsdocPath, argv, {stdio: 'inherit'}) - .on('close', code => resolve(code)) - }) - - try { - if (exitCode === 0 && willGenerate) { - return {exitCode, indexHtml: await findFile(destination, 'index.html')} - } - } catch { - // If jsdoc finds no input files, it won't create the destination directory. - // It will print "There are no input files to process." and exit with 0. - } - return {exitCode} -} - -/** - * Returns the full path to the specified command - * @param {string} cmdName - command to find in env.PATH - * @param {object} env - environment variables, presumably process.env - * @param {string} env.PATH - the PATH environment variable - * @param {string} platform - the process.platform string - * @returns {Promise} - path to the command - */ -async function getPath(cmdName, env, platform) { - for (const p of env.PATH.split(path.delimiter)) { - // pnpm will install both the original script and versions ending with .CMD - // and .ps1. We'll just default to .CMD. - const extension = (platform === 'win32') ? '.CMD' : '' - const candidate = path.join(p, cmdName) + extension - - try { - await access(candidate) - return candidate - } catch { /* try next candidate */ } - } - return Promise.reject(`${cmdName} not found in PATH`) -} - -/** - * Results from analyzing JSDoc command line arguments - * @typedef {object} ArgvResults - * @property {string} destination - the JSDoc destination directory - * @property {boolean} willGenerate - true unless --help or --version present - */ - -/** - * Analyzes JSDoc CLI args to determine if JSDoc will generate docs and where - * @param {string[]} argv - JSDoc command line interface arguments - * @returns {Promise} - analysis results - */ -async function analyzeArgv(argv) { - let destination = undefined - let willGenerate = true - let cmdLineDest = false - - for (let i = 0; i !== argv.length; ++i) { - const arg = argv[i] - const nextArg = argv[i+1] - let config = null - - switch (arg) { - case '-c': - case '--configure': - if (!cmdLineDest && nextArg !== undefined) { - config = JSON.parse(await readFile(nextArg)) - if (config.opts !== undefined) { - destination = config.opts.destination - } - } - break - - case '-d': - case '--destination': - if (nextArg !== undefined && !nextArg.startsWith('-')) { - destination = nextArg - cmdLineDest = true - } - break - - case '-h': - case '--help': - case '-v': - case '--version': - willGenerate = false - break - } - } - - // "out" is the JSDoc default directory. - destination ??= 'out' - return {willGenerate, destination} -} - -/** - * Searches for filename within a directory tree via breadth-first search - * @param {string} dirname - current directory to search - * @param {string} filename - name of file to find - * @returns {Promise} - path to filename within dirname - */ -async function findFile(dirname, filename) { - const childDirs = [dirname] - let curDir - - while ((curDir = childDirs.shift()) !== undefined) { - // This should be `for await (const entry of readdir(...))`: - // - // - https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/for-await...of - // - // But Node 20.10.0 errors with: - // - // TypeError: readdir(...) is not a function or its return value is not - // async iterable - const entries = await readdir(curDir, {withFileTypes: true}) - for (const entry of entries) { - const childPath = path.join(curDir, entry.name) - if (entry.name === filename) return childPath - if (entry.isDirectory()) childDirs.push(childPath) - } - } - return Promise.reject(`failed to find ${filename} in ${dirname}`) -} diff --git a/strcalc/src/main/frontend/package.json b/strcalc/src/main/frontend/package.json index c96be32..9b9bede 100644 --- a/strcalc/src/main/frontend/package.json +++ b/strcalc/src/main/frontend/package.json @@ -29,7 +29,7 @@ "test-ui": "vitest --ui --coverage", "test-ci": "eslint --color --max-warnings 0 . && vitest run --config ci/vitest.config.js && vitest run --config ci/vitest.config.browser.js", "coverage": "vitest run --coverage", - "jsdoc": "node bin/jsdoc-cli-wrapper.js -c ./jsdoc.json ." + "jsdoc": "jsdoc-cli-wrapper -c ./jsdoc.json ." }, "devDependencies": { "@rollup/pluginutils": "^5.1.0", @@ -42,6 +42,7 @@ "eslint-plugin-jsdoc": "^46.9.1", "eslint-plugin-vitest": "^0.3.20", "handlebars": "^4.7.8", + "jsdoc-cli-wrapper": "^1.0.0", "jsdom": "^23.0.1", "vite": "^5.0.10", "vitest": "^1.1.0", diff --git a/strcalc/src/main/frontend/pnpm-lock.yaml b/strcalc/src/main/frontend/pnpm-lock.yaml index 2e6bb2b..f23d03b 100644 --- a/strcalc/src/main/frontend/pnpm-lock.yaml +++ b/strcalc/src/main/frontend/pnpm-lock.yaml @@ -35,6 +35,9 @@ devDependencies: handlebars: specifier: ^4.7.8 version: 4.7.8 + jsdoc-cli-wrapper: + specifier: ^1.0.0 + version: 1.0.0 jsdom: specifier: ^23.0.1 version: 23.0.1 @@ -2496,6 +2499,12 @@ packages: argparse: 2.0.1 dev: true + /jsdoc-cli-wrapper@1.0.0: + resolution: {integrity: sha512-WYwKHGL4NT2RTn2B5QtvZydKTK4P73bKPy/CrnNjSiU/CNTYUF4jsK5emwxPARKsmA2mD8qY2gWAa/DAlzZrfg==} + engines: {node: '>= 18.0.0'} + hasBin: true + dev: true + /jsdoc-type-pratt-parser@4.0.0: resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} engines: {node: '>=12.0.0'}