From 3a205843397b6b4ef86d3e1ab07d93251c90e81f Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 9 Jan 2024 11:32:43 -0400 Subject: write: implement repl as proper build mode --- README.md | 2 +- package.json | 2 +- src/repl.js | 176 ----------------------------------------- src/upd8.js | 24 ------ src/write/build-modes/index.js | 1 + src/write/build-modes/repl.js | 163 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 166 insertions(+), 202 deletions(-) delete mode 100644 src/repl.js create mode 100644 src/write/build-modes/repl.js diff --git a/README.md b/README.md index 2c99e3d..0e4ed90 100644 --- a/README.md +++ b/README.md @@ -149,11 +149,11 @@ The source code for HSMusic is divided across a number of source files, loosely - `write/`: Common utilities and output methods for controlling what hsmusic does to turn data and media into something you actually visit; these each define a variety of command-line arguments and are basically the interchangeable "second half" of upd8.js - `live-dev-server.js`: Gets the site available for viewing as quickly as possible, generating and serving pages as they are requested from a local HTTP server; reacts live to code changes in the `content` directory - `static-build.js`: Builds the entire site at once, writing all the output to one self-contained folder which can be uploaded to a static file server + - `repl.js`: Provides a convenient REPL to run filters and transformations on data objects right from the Node.js command line - `static/`: Purely client-side files are kept here, e.g. site CSS, icon SVGs, and client-side JS - `util/`: Common utilities which generally may be accessed from both Node.js or the client (web browser) - `upd8.js`: Main entry point which controls and directs site generation from start to finish - `gen-thumbs.js`: Standalone utility also called every time HSMusic is run (unless `--skip-thumbs` is provided) which keeps a persistent cache of media MD5s and (re)generates thumbnails for new or updated image files - - `repl.js`: Standalone utility for loading all wiki data and providing a convenient REPL to run filters and transformations on data objects right from the Node.js command line - `listing-spec.js`: Descriptors for computations and HTML templates used for the Listings part of the site - `url-spec.js`: Index of output paths where generated HTML ends up; also controls where ``, ``, etc tags link - `file-size-preloader.js`: Simple utility for calculating size of files in media directory diff --git a/package.json b/package.json index a1c165b..0374e9c 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "#language": "./src/data/language.js", "#page-specs": "./src/page/index.js", "#node-utils": "./src/util/node-utils.js", - "#repl": "./src/repl.js", + "#repl": "./src/write/build-modes/repl.js", "#replacer": "./src/util/replacer.js", "#serialize": "./src/data/serialize.js", "#sugar": "./src/util/sugar.js", diff --git a/src/repl.js b/src/repl.js deleted file mode 100644 index d7f4286..0000000 --- a/src/repl.js +++ /dev/null @@ -1,176 +0,0 @@ -import * as os from 'node:os'; -import * as path from 'node:path'; -import * as repl from 'node:repl'; -import {fileURLToPath} from 'node:url'; - -import {logError, logWarn, parseOptions} from '#cli'; -import {debugComposite} from '#composite'; -import {internalDefaultStringsFile, processLanguageFile} from '#language'; -import {isMain} from '#node-utils'; -import {bindOpts, showAggregate} from '#sugar'; -import {generateURLs, urlSpec} from '#urls'; -import {quickLoadAllFromYAML} from '#yaml'; - -import _find, {bindFind} from '#find'; -import CacheableObject from '#cacheable-object'; -import thingConstructors from '#things'; -import * as serialize from '#serialize'; -import * as sugar from '#sugar'; -import * as wikiDataUtils from '#wiki-data'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -export async function getContextAssignments({ - dataPath, - mediaPath, - wikiData, -}) { - let urls; - try { - urls = generateURLs(urlSpec); - } catch (error) { - console.error(error); - logWarn`Failed to generate URL mappings for built-in urlSpec`; - logWarn`\`urls\` variable will be missing`; - } - - let find; - try { - find = bindFind(wikiData); - } catch (error) { - console.error(error); - logWarn`Failed to prepare wikiData-bound find() functions`; - logWarn`\`find\` variable will be missing`; - } - - let language; - try { - language = await processLanguageFile(internalDefaultStringsFile); - } catch (error) { - console.error(error); - logWarn`Failed to create Language object`; - logWarn`\`language\` variable will be missing`; - language = undefined; - } - - return { - dataPath, - mediaPath, - - wikiData, - ...wikiData, - WD: wikiData, - - ...thingConstructors, - CacheableObject, - language, - debugComposite, - - ...sugar, - ...wikiDataUtils, - - serialize, - S: serialize, - - urls, - - _find, - find, - bindFind, - }; -} - -export default async function bootRepl({ - dataPath = process.env.HSMUSIC_DATA, - mediaPath = process.env.HSMUSIC_MEDIA, - disableHistory = false, - showTraces = false, -}) { - if (!dataPath) { - logError`Expected --data-path option or HSMUSIC_DATA to be set`; - return; - } - - if (!mediaPath) { - logError`Expected --media-path option or HSMUSIC_MEDIA to be set`; - return; - } - - console.log('HSMusic data REPL'); - - const wikiData = await quickLoadAllFromYAML(dataPath, { - showAggregate: bindOpts(showAggregate, { - showTraces, - pathToFileURL: (f) => path.relative(__dirname, fileURLToPath(f)), - }), - }); - - const replServer = repl.start(); - - Object.assign(replServer.context, await getContextAssignments({ - dataPath, - mediaPath, - wikiData, - })); - - if (disableHistory) { - console.log(`\rInput history disabled (--no-repl-history provided)`); - replServer.displayPrompt(true); - } else { - const historyFile = path.join(os.homedir(), '.hsmusic_repl_history'); - replServer.setupHistory(historyFile, (err) => { - if (err) { - console.error( - `\rFailed to begin locally logging input history to ${historyFile} (provide --no-repl-history to disable)` - ); - } else { - console.log( - `\rLogging input history to ${historyFile} (provide --no-repl-history to disable)` - ); - } - replServer.displayPrompt(true); - }); - } - - // Is this called breaking a promise? - await new Promise(() => {}); - - return true; -} - -async function main() { - const miscOptions = await parseOptions(process.argv.slice(2), { - 'data-path': { - type: 'value', - }, - - 'media-path': { - type: 'value', - }, - - 'no-repl-history': { - type: 'flag', - }, - - 'show-traces': { - type: 'flag', - }, - }); - - return bootRepl({ - dataPath: miscOptions['data-path'], - mediaPath: miscOptions['media-path'], - disableHistory: miscOptions['no-repl-history'], - showTraces: miscOptions['show-traces'], - }); -} - -if (isMain(import.meta.url)) { - main().catch((error) => { - if (error instanceof AggregateError) { - showAggregate(error); - } else { - console.error(error); - } - }); -} diff --git a/src/upd8.js b/src/upd8.js index 5b6b440..17c4d36 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -43,7 +43,6 @@ import {displayCompositeCacheAnalysis} from '#composite'; import {processLanguageFile, watchLanguageFile, internalDefaultStringsFile} from '#language'; import {isMain, traverse} from '#node-utils'; -import bootRepl from '#repl'; import {empty, showAggregate, withEntries} from '#sugar'; import {generateURLs, urlSpec} from '#urls'; import {sortByName} from '#wiki-data'; @@ -250,16 +249,6 @@ async function main() { type: 'value', }, - 'repl': { - help: `Boot into the HSMusic REPL for command-line interactive access to data objects`, - type: 'flag', - }, - - 'no-repl-history': { - help: `Disable locally logging commands entered into the REPL in your home directory`, - type: 'flag', - }, - 'skip-reference-validation': { help: `Skips checking and reporting reference errors, which speeds up the build but may silently allow erroneous data to pass through`, type: 'flag', @@ -481,9 +470,6 @@ async function main() { showStepStatusSummary = cliOptions['show-step-summary'] ?? false; - const replFlag = cliOptions['repl'] ?? false; - const disableReplHistory = cliOptions['no-repl-history'] ?? false; - const showAggregateTraces = cliOptions['show-traces'] ?? false; const precacheMode = cliOptions['precache-mode'] ?? 'common'; @@ -508,16 +494,6 @@ async function main() { return false; } - if (replFlag) { - return bootRepl({ - dataPath, - mediaPath, - - disableHistory: disableReplHistory, - showTraces: showAggregateTraces, - }); - } - if (cliOptions['no-build']) { logInfo`Won't generate any site or page files this run (--no-build passed).`; diff --git a/src/write/build-modes/index.js b/src/write/build-modes/index.js index 91e3900..3ae2cfc 100644 --- a/src/write/build-modes/index.js +++ b/src/write/build-modes/index.js @@ -1,2 +1,3 @@ export * as 'live-dev-server' from './live-dev-server.js'; +export * as 'repl' from './repl.js'; export * as 'static-build' from './static-build.js'; diff --git a/src/write/build-modes/repl.js b/src/write/build-modes/repl.js new file mode 100644 index 0000000..1a9fbd3 --- /dev/null +++ b/src/write/build-modes/repl.js @@ -0,0 +1,163 @@ +export const description = `Provide command-line interactive access to wiki data objects`; + +export const config = { + fileSizes: { + default: 'skip', + }, + + languageReloading: { + default: true, + }, + + mediaValidation: { + default: 'skip', + }, + + thumbs: { + applicable: false, + }, +}; + +export function getCLIOptions() { + return { + 'no-repl-history': { + help: `Disable locally logging commands entered into the REPL in your home directory`, + type: 'flag', + }, + }; +} + +import * as os from 'node:os'; +import * as path from 'node:path'; +import * as repl from 'node:repl'; +import {fileURLToPath} from 'node:url'; + +import {logError, logWarn, parseOptions} from '#cli'; +import {debugComposite} from '#composite'; +import {internalDefaultStringsFile, processLanguageFile} from '#language'; +import {isMain} from '#node-utils'; +import {bindOpts, showAggregate} from '#sugar'; +import {generateURLs, urlSpec} from '#urls'; +import {quickLoadAllFromYAML} from '#yaml'; + +import _find, {bindFind} from '#find'; +import CacheableObject from '#cacheable-object'; +import thingConstructors from '#things'; +import * as serialize from '#serialize'; +import * as sugar from '#sugar'; +import * as wikiDataUtils from '#wiki-data'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +export async function getContextAssignments({ + dataPath, + mediaPath, + mediaCachePath, + + defaultLanguage, + languages, + missingImagePaths, + thumbsCache, + urls, + wikiData, + + getSizeOfAdditionalFile, + getSizeOfImagePath, + niceShowAggregate, +}) { + let find; + try { + find = bindFind(wikiData); + } catch (error) { + console.error(error); + logWarn`Failed to prepare wikiData-bound find() functions`; + logWarn`\`find\` variable will be missing`; + } + + const replContext = { + dataPath, + mediaPath, + mediaCachePath, + + languages, + defaultLanguage, + language: defaultLanguage, + + missingImagePaths, + thumbsCache, + urls, + + wikiData, + ...wikiData, + WD: wikiData, + + ...thingConstructors, + CacheableObject, + debugComposite, + + ...sugar, + ...wikiDataUtils, + + serialize, + S: serialize, + + _find, + find, + bindFind, + + getSizeOfAdditionalFile, + getSizeOfImagePath, + showAggregate: niceShowAggregate, + }; + + replContext.replContext = replContext; + + return replContext; +} + +export async function go(buildOptions) { + const { + cliOptions, + closeLanguageWatchers, + } = buildOptions; + + const disableHistory = cliOptions['no-repl-history'] ?? false; + + console.log('HSMusic data REPL'); + + const replServer = repl.start(); + + Object.assign(replServer.context, + await getContextAssignments(buildOptions)); + + if (disableHistory) { + console.log(`\rInput history disabled (--no-repl-history provided)`); + } else { + const historyFile = path.join(os.homedir(), '.hsmusic_repl_history'); + await new Promise(resolve => { + replServer.setupHistory(historyFile, (err) => { + if (err) { + console.error(`\rFailed to begin locally logging input history to ${historyFile} (provide --no-repl-history to disable)`); + } else { + console.log(`\rLogging input history to ${historyFile} (provide --no-repl-history to disable)`); + } + resolve(); + }); + }); + } + + replServer.displayPrompt(true); + + let resolveDone; + + replServer.on('exit', () => { + closeLanguageWatchers(); + resolveDone(); + }); + + await new Promise(resolve => { + resolveDone = resolve; + }); + + return true; +} -- cgit 1.3.0-6-gf8a5