diff --git a/LICENSE.txt b/LICENSE.txt index 8338e43..d0a1fa1 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,5 +1,3 @@ -Copyright (c) 2023 Mike Bland - Mozilla Public License Version 2.0 ================================== diff --git a/README.md b/README.md index 4c8dff0..11c4786 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Source: [![CI status](https://github.com/mbland/jsdoc-cli-wrapper/actions/workflows/run-tests.yaml/badge.svg)](https://github.com/mbland/jsdoc-cli-wrapper/actions/workflows/run-tests.yaml?branch=main) [![Test results](https://github.com/mbland/jsdoc-cli-wrapper/actions/workflows/publish-test-results.yaml/badge.svg)](https://github.com/mbland/jsdoc-cli-wrapper/actions/workflows/publish-test-results.yaml?branch=main) [![Coverage Status](https://coveralls.io/repos/github/mbland/jsdoc-cli-wrapper/badge.svg?branch=main)][coveralls-jsdw] +[![npm version](https://badge.fury.io/js/jsdoc-cli-wrapper.svg)][npm-jsdw] ## Installation @@ -145,6 +146,7 @@ Node.js, JSDoc, and [npm packaging][] exercise as well. [JSDoc]: https://jsdoc.app/ [cli]: https://github.com/jsdoc/jsdoc [coveralls-jsdw]: https://coveralls.io/github/mbland/jsdoc-cli-wrapper?branch=main +[npm-jsdw]: https://www.npmjs.com/package/jsdoc-cli-wrapper [pnpm]: https://pnpm.io/ [mbland/tomcat-servlet-testing-example]: https://github.com/mbland/tomcat-servlet-testing-example [Gradle]: https://gradle.org/ diff --git a/index.js b/index.js index a2f805c..20668f7 100755 --- a/index.js +++ b/index.js @@ -11,6 +11,7 @@ * Removes the existing destination directory if it exists, runs JSDoc, and * emits the relative path to the generated index.html file. * @author Mike Bland + * @license MPL-2.0 */ import { runJsdoc } from './lib/index.js' diff --git a/lib/index.js b/lib/index.js index 915b122..b067cdf 100644 --- a/lib/index.js +++ b/lib/index.js @@ -20,7 +20,8 @@ import path from 'node: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 + * @returns {Promise} result of `jsdoc` execution + * @throws if `jsdoc` isn't found or can't execute */ export async function runJsdoc(argv, env, platform) { let jsdocPath @@ -56,17 +57,18 @@ export async function runJsdoc(argv, env, platform) { /** * Determines the key for the command search path within process.env. * @param {string} platform - the process.platform string - * @returns {string} - On every platform other than 'win32', this will be "PATH" + * @returns {string} On every platform other than 'win32', this will be "PATH" * On 'win32', this will be "Path". */ -export const pathKey = (platform) => platform !== 'win32' ? 'PATH' : 'Path' +export const pathKey = platform => platform !== 'win32' ? 'PATH' : 'Path' /** * Returns the full path to the specified command * @param {string} cmdName - command to find in env[pathKey(platform)] * @param {object} env - environment variables, presumably process.env * @param {string} platform - the process.platform string - * @returns {Promise} - path to the command + * @returns {Promise} path to the command + * @throws if `jsdoc` isn't found */ export async function getPath(cmdName, env, platform) { const pk = pathKey(platform) @@ -95,17 +97,14 @@ export async function getPath(cmdName, env, platform) { /** * 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 + * @returns {Promise} analysis results */ export async function analyzeArgv(argv) { + const validArg = nextArg => nextArg !== undefined && !nextArg.startsWith('-') let destination = undefined let willGenerate = true let cmdLineDest = false - const validArg = (nextArg) => ( - nextArg !== undefined && !nextArg.startsWith('-') - ) - for (let i = 0; i !== argv.length; ++i) { const arg = argv[i] const nextArg = argv[i+1] @@ -148,7 +147,8 @@ export async function analyzeArgv(argv) { * 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 + * @returns {Promise} path to filename within dirname + * @throws if filename not found */ export async function findFile(dirname, filename) { const childDirs = [dirname] diff --git a/package.json b/package.json index c28f85b..aa9ec2f 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "test:ci": "eslint --color --max-warnings 0 . && vitest run -c ci/vitest.config.js", "jsdoc": "node index.js -c jsdoc.json ." }, + "files": [ "lib/**" ], "keywords": [ "jsdoc", "JavaScript" @@ -17,6 +18,9 @@ "author": "Mike Bland (https://mike-bland.com/)", "license": "MPL-2.0", "type": "module", + "engines": { + "node": ">= 18.0.0" + }, "homepage": "https://github.com/mbland/jsdoc-cli-wrapper", "repository": "https://github.com/mbland/jsdoc-cli-wrapper", "bugs": "https://github.com/mbland/jsdoc-cli-wrapperr/issues", diff --git a/test/fixtures/jsdocStub/jsdoc b/test/fixtures/jsdocStub/jsdoc index e114d3f..0eccb21 100755 --- a/test/fixtures/jsdocStub/jsdoc +++ b/test/fixtures/jsdocStub/jsdoc @@ -1,69 +1,31 @@ -#!/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/. - */ - -/** - * Fake jsdoc implementation for testing - */ - -import { mkdir, writeFile } from 'node:fs/promises' -import path from 'node:path' -import { exit } from 'node:process' - -try { - const {willGenerate, destination, exitCode} = parseArgs(process.argv.slice(2)) - - if (willGenerate && exitCode === 0) { - const newSubDir = path.join(destination, 'new-subdir') - await mkdir(newSubDir, {recursive: true}) - await writeFile(path.join(newSubDir, 'index.html'), 'New Hotness') - } - exit(exitCode) - -} catch (err) { - console.error(err) - exit(1) -} - -/** - * The parameters parsed from process.argv by parseArgs() - * @typedef {object} ArgsResult - * @property {string} destination - the JSDoc destination directory - * @property {boolean} willGenerate - true unless -h or --no-input-files present - * @property {number} exitCode - the value of --exit-code or 0 by default - */ - -/** - * Parses fake jsdoc arguments - * @param {string[]} argv - command line arguments - * @returns {ArgsResult} - parameters determining fake jsdoc behavior - */ -function parseArgs(argv) { - let destination = null - let willGenerate = true - let exitCode = 0 - - for (let i = 0; i !== argv.length; ++i) { - const arg = argv[i] - const nextArg = argv[i+1] - - switch (arg) { - case '-d': - destination = nextArg - break - - case '-h': - case '--no-input-files': - willGenerate = false - break - - case '--exit-code': - exitCode = nextArg - break - } - } - return {willGenerate, destination, exitCode} -} +#!/bin/sh +# +# Wraps the jsdoc.js command on non-Windows platforms. +# +# This is necessary to support older Node versions as package.json engines +# without removing the `"type": "module"` specifier. Without this shim, running +# `pnpm test` under many older verions caused runJsdoc.test.js and main.test.js +# to fail on the spawn(jsdocPath) call within runJsdoc(). +# +# These older versions couldn't grok that the previous `jsdoc` stub was really +# written in ECMAScript Module style without a file extension: +# +# TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for +# .../jsdoc-cli-wrapper/test/fixtures/jsdocStub/jsdoc. Loading extensionless +# files is not supported inside of "type":"module" package.json contexts. The +# package.json file .../jsdoc-cli-wrapper/package.json caused this +# "type":"module" context. Try changing +# .../jsdoc-cli-wrapper/test/fixtures/jsdocStub/jsdoc to have a file +# extension. Note the "bin" field of package.json can point to a file with an +# extension, for example +# {"type":"module","bin":{"jsdoc":"./test/fixtures/jsdocStub/jsdoc.js"}} +# +# I tried adding the `--experimental-default-type=module` flag to the shebang +# line of the former `jsdoc` stub. Only after trying that did I realize that +# `#!/usr/bin/env` style shebangs don't support passing command line arguments +# to the specified interpreter. +# +# Hence moving the original `jsdoc` stub to `jsdoc.js` and invoking it via this +# one-line wrapper. + +exec node "${0}.js" "$@" diff --git a/test/fixtures/jsdocStub/jsdoc.CMD b/test/fixtures/jsdocStub/jsdoc.CMD index d5e0a72..31ac7f7 100644 --- a/test/fixtures/jsdocStub/jsdoc.CMD +++ b/test/fixtures/jsdocStub/jsdoc.CMD @@ -7,4 +7,4 @@ :: https://ss64.com/nt/ :: https://htipe.wordpress.com/2008/10/09/the-dp0-variable/ @echo off -node "%~dp0\jsdoc" %* +node "%~dp0\jsdoc.js" %* diff --git a/test/fixtures/jsdocStub/jsdoc.js b/test/fixtures/jsdocStub/jsdoc.js new file mode 100755 index 0000000..e114d3f --- /dev/null +++ b/test/fixtures/jsdocStub/jsdoc.js @@ -0,0 +1,69 @@ +#!/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/. + */ + +/** + * Fake jsdoc implementation for testing + */ + +import { mkdir, writeFile } from 'node:fs/promises' +import path from 'node:path' +import { exit } from 'node:process' + +try { + const {willGenerate, destination, exitCode} = parseArgs(process.argv.slice(2)) + + if (willGenerate && exitCode === 0) { + const newSubDir = path.join(destination, 'new-subdir') + await mkdir(newSubDir, {recursive: true}) + await writeFile(path.join(newSubDir, 'index.html'), 'New Hotness') + } + exit(exitCode) + +} catch (err) { + console.error(err) + exit(1) +} + +/** + * The parameters parsed from process.argv by parseArgs() + * @typedef {object} ArgsResult + * @property {string} destination - the JSDoc destination directory + * @property {boolean} willGenerate - true unless -h or --no-input-files present + * @property {number} exitCode - the value of --exit-code or 0 by default + */ + +/** + * Parses fake jsdoc arguments + * @param {string[]} argv - command line arguments + * @returns {ArgsResult} - parameters determining fake jsdoc behavior + */ +function parseArgs(argv) { + let destination = null + let willGenerate = true + let exitCode = 0 + + for (let i = 0; i !== argv.length; ++i) { + const arg = argv[i] + const nextArg = argv[i+1] + + switch (arg) { + case '-d': + destination = nextArg + break + + case '-h': + case '--no-input-files': + willGenerate = false + break + + case '--exit-code': + exitCode = nextArg + break + } + } + return {willGenerate, destination, exitCode} +} diff --git a/test/getPath.test.js b/test/getPath.test.js index f1140b8..810746e 100644 --- a/test/getPath.test.js +++ b/test/getPath.test.js @@ -15,7 +15,7 @@ describe('getPath', () => { const envPath = ['usr/local/bin', 'usr/bin', 'bin'] .map(p => path.join(root, p)) .join(path.delimiter) - const makeEnv = (platform) => ({[pathKey(platform)]: envPath}) + const makeEnv = platform => ({[pathKey(platform)]: envPath}) test('finds command on POSIX system', async() => { await expect(getPath('testcmd', makeEnv('linux'), 'linux')).resolves diff --git a/test/main.test.js b/test/main.test.js index 12f9ceb..6f9fd0f 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -37,7 +37,7 @@ describe('jsdoc-cli-wrapper', () => { if (stderr) result.stderr = stderr resolve(result) }) - wrapper.on('error', (err) => reject(err)) + wrapper.on('error', err => reject(err)) }) const runMain = (...argv) => spawnMain(envPath, ...argv)