From acecaa1ecd52af8b028a671b8933c47c8018d4ec Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 16 Jan 2023 18:14:13 -0400 Subject: Error on unknown CLI arguments --- src/upd8.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/upd8.js') diff --git a/src/upd8.js b/src/upd8.js index 21413a17..cb6c559a 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -101,10 +101,12 @@ async function main() { const selectedBuildModeFlags = Object.keys( await parseOptions(process.argv.slice(2), { + // Ignore unknown options for now - we'll handle and error them later. [parseOptions.handleUnknown]: () => {}, - ...Object.fromEntries(Object.keys(buildModes) - .map((key) => [key, {type: 'flag'}])), + ...Object.fromEntries( + Object.keys(buildModes) + .map((key) => [key, {type: 'flag'}])), })); let selectedBuildModeFlag; @@ -130,6 +132,13 @@ async function main() { }; const cliOptions = await parseOptions(process.argv.slice(2), { + // We don't want to error when we receive these options, so specify them + // here, even though we won't be doing anything with them later. + // (This is a bit of a hack.) + ...Object.fromEntries( + Object.keys(buildModes) + .map((key) => [key, {type: 'flag'}])), + ...selectedBuildMode.getCLIOptions(), // Data files for the site, including flash, artist, and al8um data, @@ -217,8 +226,6 @@ async function main() { 'precache-data': { type: 'flag', }, - - [parseOptions.handleUnknown]: () => {}, }); const dataPath = cliOptions['data-path'] || process.env.HSMUSIC_DATA; -- cgit 1.3.0-6-gf8a5 From 8fa238e49bf4db2ddd72de4960bbaec7f4a72824 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 16 Jan 2023 19:44:33 -0400 Subject: stub --help option --- src/upd8.js | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 13 deletions(-) (limited to 'src/upd8.js') diff --git a/src/upd8.js b/src/upd8.js index cb6c559a..5668542c 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -34,6 +34,7 @@ import {execSync} from 'child_process'; import * as path from 'path'; import {fileURLToPath} from 'url'; +import wrap from 'word-wrap'; import genThumbs from './gen-thumbs.js'; import {listingSpec, listingTargetSpec} from './listing-spec.js'; @@ -60,6 +61,7 @@ import {validateReplacerSpec} from './util/replacer.js'; import {empty, showAggregate} from './util/sugar.js'; import {replacerSpec} from './util/transform-content.js'; import {generateURLs} from './util/urls.js'; +import {sortByName} from './util/wiki-data.js'; import {generateDevelopersCommentHTML} from './write/page-template.js'; import * as buildModes from './write/build-modes/index.js'; @@ -99,14 +101,15 @@ if (!validateReplacerSpec(replacerSpec, {find, link})) { async function main() { Error.stackTraceLimit = Infinity; + const buildModeFlagOptions = ( + Object.fromEntries( + Object.keys(buildModes) + .map(key => [key, {type: 'flag'}]))); + const selectedBuildModeFlags = Object.keys( await parseOptions(process.argv.slice(2), { - // Ignore unknown options for now - we'll handle and error them later. [parseOptions.handleUnknown]: () => {}, - - ...Object.fromEntries( - Object.keys(buildModes) - .map((key) => [key, {type: 'flag'}])), + ...buildModeFlagOptions, })); let selectedBuildModeFlag; @@ -131,15 +134,12 @@ async function main() { listingTargetSpec, }; - const cliOptions = await parseOptions(process.argv.slice(2), { - // We don't want to error when we receive these options, so specify them - // here, even though we won't be doing anything with them later. - // (This is a bit of a hack.) - ...Object.fromEntries( - Object.keys(buildModes) - .map((key) => [key, {type: 'flag'}])), + const buildOptions = selectedBuildMode.getCLIOptions(); - ...selectedBuildMode.getCLIOptions(), + const commonOptions = { + 'help': { + type: 'flag', + }, // Data files for the site, including flash, artist, and al8um data, // and like a jillion other things too. Pretty much everything which @@ -226,8 +226,83 @@ async function main() { 'precache-data': { type: 'flag', }, + }; + + const cliOptions = await parseOptions(process.argv.slice(2), { + // We don't want to error when we receive these options, so specify them + // here, even though we won't be doing anything with them later. + // (This is a bit of a hack.) + ...buildModeFlagOptions, + + ...commonOptions, + ...buildOptions, }); + if (cliOptions['help']) { + const indentWrap = (spaces, str) => wrap(str, {width: 60 - spaces, indent: ' '.repeat(spaces)}); + + const showOptions = (msg, options) => { + console.log(color.bright(msg)); + + const entries = Object.entries(options); + const sortedOptions = sortByName(entries + .map(([name, descriptor]) => ({name, descriptor}))); + + if (!sortedOptions.length) { + console.log(`(No options available)`) + } + + for (const {name, descriptor} of sortedOptions) { + if (descriptor.alias) { + continue; + } + + const aliases = entries + .filter(([_name, {alias}]) => alias === name) + .map(([name]) => name); + + console.log(color.bright(` --` + name) + + (aliases.length + ? ` (or: ${aliases.map(alias => color.bright(`--` + alias)).join(', ')})` + : '') + + (descriptor.help + ? '' + : color.dim(' (no help provided)'))); + + if (descriptor.help) { + console.log(indentWrap(4, descriptor.help)); + } + } + + console.log(``); + }; + + console.log( + color.bright(`hsmusic (aka. Homestuck Music Wiki)\n`) + + `static wiki software cataloguing collaborative creation\n`); + + console.log(indentWrap(0, + `The \`hsmusic\` command provides basic control over all parts of generating user-visible HTML pages and website content/structure from provided data, media, and language directories.\n` + + `\n` + + `CLI options are divided into three groups:\n`)); + console.log(` 1) ` + indentWrap(4, + `Common options: These are shared by all build modes and always have the same essential behavior`).trim()); + console.log(` 2) ` + indentWrap(4, + `Build mode selection: One build mode may be selected (or else the default, --static-build, is used), and it decides which entire set of behavior to use for providing site content to the user`).trim()); + console.log(` 3) ` + indentWrap(4, + `Build options: Each build mode has a set of unique options which customize behavior for that build mode`).trim()); + console.log(``); + + showOptions(`Common options`, commonOptions); + showOptions(`Build mode selection`, buildModeFlagOptions); + + if (buildOptions) { + showOptions(`Build options for --${selectedBuildModeFlag}`, buildOptions); + } + + return; + } + const dataPath = cliOptions['data-path'] || process.env.HSMUSIC_DATA; const mediaPath = cliOptions['media-path'] || process.env.HSMUSIC_MEDIA; const langPath = cliOptions['lang-path'] || process.env.HSMUSIC_LANG; // Can 8e left unset! -- cgit 1.3.0-6-gf8a5 From f6c5df932dfba1567ef8b521796ecaf73317638d Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 16 Jan 2023 20:13:49 -0400 Subject: padding lines between long option help --- src/upd8.js | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'src/upd8.js') diff --git a/src/upd8.js b/src/upd8.js index 5668542c..42f0df46 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -252,6 +252,8 @@ async function main() { console.log(`(No options available)`) } + let justInsertedPaddingLine = false; + for (const {name, descriptor} of sortedOptions) { if (descriptor.alias) { continue; @@ -261,6 +263,16 @@ async function main() { .filter(([_name, {alias}]) => alias === name) .map(([name]) => name); + let wrappedHelp, wrappedHelpLines = 0; + if (descriptor.help) { + wrappedHelp = indentWrap(4, descriptor.help); + wrappedHelpLines = wrappedHelp.split('\n').length; + } + + if (wrappedHelpLines > 1 && !justInsertedPaddingLine) { + console.log(''); + } + console.log(color.bright(` --` + name) + (aliases.length ? ` (or: ${aliases.map(alias => color.bright(`--` + alias)).join(', ')})` @@ -269,12 +281,21 @@ async function main() { ? '' : color.dim(' (no help provided)'))); - if (descriptor.help) { - console.log(indentWrap(4, descriptor.help)); + if (wrappedHelp) { + console.log(wrappedHelp); + } + + if (wrappedHelpLines > 1) { + console.log(''); + justInsertedPaddingLine = true; + } else { + justInsertedPaddingLine = false; } } - console.log(``); + if (!justInsertedPaddingLine) { + console.log(``); + } }; console.log( -- cgit 1.3.0-6-gf8a5 From 93b510293b8cd7209d0ddc23f5e800aed5a25577 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 16 Jan 2023 20:14:07 -0400 Subject: --help info for common options --- src/upd8.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/upd8.js') diff --git a/src/upd8.js b/src/upd8.js index 42f0df46..a1a499dd 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -101,6 +101,8 @@ if (!validateReplacerSpec(replacerSpec, {find, link})) { async function main() { Error.stackTraceLimit = Infinity; + const defaultQueueSize = 500; + const buildModeFlagOptions = ( Object.fromEntries( Object.keys(buildModes) @@ -138,6 +140,7 @@ async function main() { const commonOptions = { 'help': { + help: `Display usage info and basic information for the \`hsmusic\` command`, type: 'flag', }, @@ -145,6 +148,7 @@ async function main() { // and like a jillion other things too. Pretty much everything which // makes an individual wiki what it is goes here! 'data-path': { + help: `Specify path to data directory, including YAML files that cover all info about wiki content, layout, and structure; always required for wiki building, but may be provided by the HSMUSIC_DATA environment variable instead`, type: 'value', }, @@ -152,6 +156,7 @@ async function main() { // categorized; check out MEDIA_ALBUM_ART_DIRECTORY and other constants // near the top of this file (upd8.js). 'media-path': { + help: `Specify path to media directory, including album artwork and additional files, as well as custom site layout media and other media files for reference or linking in wiki content; always required for wiki building, but may be provided by the HSMUSIC_MEDIA environment variable instead`, type: 'value', }, @@ -166,6 +171,7 @@ async function main() { // 8uild with the default (English) strings if this path is left // unspecified. 'lang-path': { + help: `Specify path to language directory, including JSON files that mapping internal string keys to localized language content, and various language metadata`, type: 'value', }, @@ -173,12 +179,14 @@ async function main() { // kinda a pain to run every time, since it does necessit8te reading // every media file at run time. Pass this to skip it. 'skip-thumbs': { + help: `Skip processing and generating thumbnails in media directory (speeds up subsequent builds, but remove this option [or use --thumbs-only] and re-run once when you add or modify media files to ensure thumbnails stay up-to-date!)`, type: 'flag', }, // Or, if you *only* want to gener8te newly upd8ted thum8nails, you can // pass this flag! It exits 8efore 8uilding the rest of the site. 'thumbs-only': { + help: `Skip everything besides processing media directory and generating up-to-date thumbnails (useful when using --skip-thumbs for most runs)`, type: 'flag', }, @@ -186,6 +194,7 @@ async function main() { // generating site HTML yet? This flag will cut execution off right // 8efore any site 8uilding actually happens. 'no-build': { + help: `Don't run a build of the site at all; only process data/media and report any errors detected`, type: 'flag', }, @@ -194,10 +203,12 @@ async function main() { // line) right to your output, 8ut also pro8a8ly give you a headache // 8ecause wow that is a lot of visual noise. 'show-traces': { + help: `Show JavaScript source code paths for reported errors in "aggregate" error displays\n\n(Debugging use only, but please enable this if you're reporting bugs for our issue tracker!)`, type: 'flag', }, 'queue-size': { + help: `Process more or fewer disk files at once to optimize performance or avoid I/O errors, unlimited if set to 0 (between 500 and 700 is usually a safe range for building HSMusic on Windows machines)\nDefaults to ${defaultQueueSize}`, type: 'value', validate(size) { if (parseInt(size) !== parseFloat(size)) return 'an integer'; @@ -211,6 +222,7 @@ async function main() { // CacheableObject in a mode where every instance is a Proxy which will // keep track of invalid property accesses. 'show-invalid-property-accesses': { + help: `Report accesses at runtime to nonexistant properties on wiki data objects, at a dramatic performance cost\n(Internal/development use only)`, type: 'flag', }, @@ -224,6 +236,7 @@ async function main() { // efficiency of data calculation or write generation separately instead of // mixed together). 'precache-data': { + help: `Compute all runtime-cached values for wiki data objects before proceeding to site build (optimizes rate of content generation/serving, but waits a lot longer before build actually starts, and may compute data which is never required for this build)`, type: 'flag', }, }; @@ -340,7 +353,7 @@ async function main() { // Makes writing nicer on the CPU and file I/O parts of the OS, with a // marginal performance deficit while waiting for file writes to finish // before proceeding to more page processing. - const queueSize = +(cliOptions['queue-size'] ?? 500); + const queueSize = +(cliOptions['queue-size'] ?? defaultQueueSize); { let errored = false; -- cgit 1.3.0-6-gf8a5 From 5631be8ac1d77aab9a63dd4e5e72f668dddf0de6 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 30 Jan 2023 20:32:23 -0400 Subject: remaining CLI help messages & tweaks --- src/upd8.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src/upd8.js') diff --git a/src/upd8.js b/src/upd8.js index a1a499dd..3bcdf884 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -58,7 +58,7 @@ import {findFiles} from './util/io.js'; import link from './util/link.js'; import {isMain} from './util/node-utils.js'; import {validateReplacerSpec} from './util/replacer.js'; -import {empty, showAggregate} from './util/sugar.js'; +import {empty, showAggregate, withEntries} from './util/sugar.js'; import {replacerSpec} from './util/transform-content.js'; import {generateURLs} from './util/urls.js'; import {sortByName} from './util/wiki-data.js'; @@ -104,9 +104,11 @@ async function main() { const defaultQueueSize = 500; const buildModeFlagOptions = ( - Object.fromEntries( - Object.keys(buildModes) - .map(key => [key, {type: 'flag'}]))); + withEntries(buildModes, entries => + entries.map(([key, mode]) => [key, { + help: mode.description, + type: 'flag', + }]))); const selectedBuildModeFlags = Object.keys( await parseOptions(process.argv.slice(2), { @@ -115,9 +117,11 @@ async function main() { })); let selectedBuildModeFlag; + let usingDefaultBuildMode; if (empty(selectedBuildModeFlags)) { selectedBuildModeFlag = 'static-build'; + usingDefaultBuildMode = true; logInfo`No build mode specified, using default: ${selectedBuildModeFlag}`; } else if (selectedBuildModeFlags.length > 1) { logError`Building multiple modes (${selectedBuildModeFlags.join(', ')}) at once not supported.`; @@ -125,6 +129,7 @@ async function main() { return; } else { selectedBuildModeFlag = selectedBuildModeFlags[0]; + usingDefaultBuildMode = false; logInfo`Using specified build mode: ${selectedBuildModeFlag}`; } @@ -148,7 +153,7 @@ async function main() { // and like a jillion other things too. Pretty much everything which // makes an individual wiki what it is goes here! 'data-path': { - help: `Specify path to data directory, including YAML files that cover all info about wiki content, layout, and structure; always required for wiki building, but may be provided by the HSMUSIC_DATA environment variable instead`, + help: `Specify path to data directory, including YAML files that cover all info about wiki content, layout, and structure\n\nAlways required for wiki building, but may be provided via the HSMUSIC_DATA environment variable instead`, type: 'value', }, @@ -156,7 +161,7 @@ async function main() { // categorized; check out MEDIA_ALBUM_ART_DIRECTORY and other constants // near the top of this file (upd8.js). 'media-path': { - help: `Specify path to media directory, including album artwork and additional files, as well as custom site layout media and other media files for reference or linking in wiki content; always required for wiki building, but may be provided by the HSMUSIC_MEDIA environment variable instead`, + help: `Specify path to media directory, including album artwork and additional files, as well as custom site layout media and other media files for reference or linking in wiki content\n\nAlways required for wiki building, but may be provided via the HSMUSIC_MEDIA environment variable instead`, type: 'value', }, @@ -282,7 +287,7 @@ async function main() { wrappedHelpLines = wrappedHelp.split('\n').length; } - if (wrappedHelpLines > 1 && !justInsertedPaddingLine) { + if (wrappedHelpLines > 0 && !justInsertedPaddingLine) { console.log(''); } @@ -331,7 +336,9 @@ async function main() { showOptions(`Build mode selection`, buildModeFlagOptions); if (buildOptions) { - showOptions(`Build options for --${selectedBuildModeFlag}`, buildOptions); + showOptions(`Build options for --${selectedBuildModeFlag} (${ + usingDefaultBuildMode ? 'default' : 'selected' + })`, buildOptions); } return; -- cgit 1.3.0-6-gf8a5