From 7fa4f92c8a41754e198ade96a7d5d0dd5b0aa59e Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 7 Nov 2023 09:12:47 -0400 Subject: upd8: add --no-language-reloading option, default for static-build --- src/data/language.js | 2 +- src/upd8.js | 318 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 204 insertions(+), 116 deletions(-) diff --git a/src/data/language.js b/src/data/language.js index 99eaa58..15c1193 100644 --- a/src/data/language.js +++ b/src/data/language.js @@ -17,7 +17,7 @@ import { const {Language} = T; -export function processLanguageSpec(spec, {existingCode = null}) { +export function processLanguageSpec(spec, {existingCode = null} = {}) { const { 'meta.languageCode': code, 'meta.languageName': name, diff --git a/src/upd8.js b/src/upd8.js index 4068374..764ee0c 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -39,7 +39,7 @@ import {fileURLToPath} from 'node:url'; import wrap from 'word-wrap'; import {displayCompositeCacheAnalysis} from '#composite'; -import {watchLanguageFile} from '#language'; +import {processLanguageFile, watchLanguageFile} from '#language'; import {isMain, traverse} from '#node-utils'; import bootRepl from '#repl'; import {empty, showAggregate, withEntries} from '#sugar'; @@ -295,6 +295,13 @@ async function main() { type: 'flag', }, + 'no-language-reloading': { + help: `Don't reload language files while the build is running\n\nApplied by default for --static-build`, + type: 'flag', + }, + + 'no-language-reload': {alias: 'no-language-reloading'}, + // Want sweet, sweet trace8ack info in aggreg8te error messages? This // will print all the juicy details (or at least the first relevant // line) right to your output, 8ut also pro8a8ly give you a headache @@ -462,6 +469,7 @@ async function main() { const skipReferenceValidation = cliOptions['skip-reference-validation'] ?? false; const noBuild = cliOptions['no-build'] ?? false; const noInput = cliOptions['no-input'] ?? false; + let noLanguageReloading = cliOptions['no-language-reloading'] ?? null; // Will get default later. showStepStatusSummary = cliOptions['show-step-summary'] ?? false; @@ -572,12 +580,24 @@ async function main() { } if (noBuild) { + logInfo`Won't generate any site or page files this run (--no-build passed).`; + Object.assign(stepStatusSummary.performBuild, { status: STATUS_NOT_APPLICABLE, annotation: `--no-build provided`, }); + } else if (usingDefaultBuildMode) { + logInfo`No build mode specified, will use default: ${selectedBuildModeFlag}`; + } else { + logInfo`Will use specified build mode: ${selectedBuildModeFlag}`; } + noLanguageReloading ??= + ({ + 'static-build': true, + 'live-dev-server': false, + })[selectedBuildModeFlag]; + if (skipThumbs && thumbsOnly) { logInfo`Well, you've put yourself rather between a roc and a hard place, hmmmm?`; return false; @@ -771,14 +791,6 @@ async function main() { thumbsCache = result.cache; } - if (noBuild) { - logInfo`Not generating any site or page files this run (--no-build passed).`; - } else if (usingDefaultBuildMode) { - logInfo`No build mode specified, using default: ${selectedBuildModeFlag}`; - } else { - logInfo`Using specified build mode: ${selectedBuildModeFlag}`; - } - if (showInvalidPropertyAccesses) { CacheableObject.DEBUG_SLOW_TRACK_INVALID_PROPERTIES = true; } @@ -1090,35 +1102,54 @@ async function main() { }); let internalDefaultLanguage; + let internalDefaultLanguageWatcher; - const internalDefaultLanguageWatcher = - watchLanguageFile(path.join(__dirname, DEFAULT_STRINGS_FILE)); + const internalDefaultStringsFile = path.join(__dirname, DEFAULT_STRINGS_FILE); - try { - await new Promise((resolve, reject) => { - const watcher = internalDefaultLanguageWatcher; - - const onReady = () => { - watcher.removeListener('ready', onReady); - watcher.removeListener('error', onError); - resolve(); - }; - - const onError = error => { - watcher.removeListener('ready', onReady); - watcher.removeListener('error', onError); - watcher.close(); - reject(error); - }; - - watcher.on('ready', onReady); - watcher.on('error', onError); - }); + let errorLoadingInternalDefaultLanguage = false; - internalDefaultLanguage = internalDefaultLanguageWatcher.language; - } catch (_error) { - // No need to display the error here - it's already printed by - // watchLanguageFile. + if (noLanguageReloading) { + internalDefaultLanguageWatcher = null; + + try { + internalDefaultLanguage = await processLanguageFile(internalDefaultStringsFile); + } catch (error) { + niceShowAggregate(error); + errorLoadingInternalDefaultLanguage = true; + } + } else { + internalDefaultLanguageWatcher = watchLanguageFile(internalDefaultStringsFile); + + try { + await new Promise((resolve, reject) => { + const watcher = internalDefaultLanguageWatcher; + + const onReady = () => { + watcher.removeListener('ready', onReady); + watcher.removeListener('error', onError); + resolve(); + }; + + const onError = error => { + watcher.removeListener('ready', onReady); + watcher.removeListener('error', onError); + watcher.close(); + reject(error); + }; + + watcher.on('ready', onReady); + watcher.on('error', onError); + }); + + internalDefaultLanguage = internalDefaultLanguageWatcher.language; + } catch (_error) { + // No need to display the error here - it's already printed by + // watchLanguageFile. + errorLoadingInternalDefaultLanguage = true; + } + } + + if (errorLoadingInternalDefaultLanguage) { logError`There was an error reading the internal language file.`; fileIssue(); @@ -1131,8 +1162,10 @@ async function main() { return false; } - // Bypass node.js special-case handling for uncaught error events - internalDefaultLanguageWatcher.on('error', () => {}); + if (!noLanguageReloading) { + // Bypass node.js special-case handling for uncaught error events + internalDefaultLanguageWatcher.on('error', () => {}); + } Object.assign(stepStatusSummary.loadInternalDefaultLanguage, { status: STATUS_DONE_CLEAN, @@ -1153,81 +1186,118 @@ async function main() { pathStyle: 'device', }); - customLanguageWatchers = - languageDataFiles.map(file => { - const watcher = watchLanguageFile(file); + let errorLoadingCustomLanguages = false; - // Bypass node.js special-case handling for uncaught error events - watcher.on('error', () => {}); + if (noLanguageReloading) { + languages = {}; - return watcher; - }); + const results = + await Promise.allSettled( + languageDataFiles + .map(file => processLanguageFile(file))); - const waitingOnWatchers = new Set(customLanguageWatchers); - - const initialResults = - await Promise.allSettled( - customLanguageWatchers.map(watcher => - new Promise((resolve, reject) => { - const onReady = () => { - watcher.removeListener('ready', onReady); - watcher.removeListener('error', onError); - waitingOnWatchers.delete(watcher); - resolve(); - }; - - const onError = error => { - watcher.removeListener('ready', onReady); - watcher.removeListener('error', onError); - reject(error); - }; - - watcher.on('ready', onReady); - watcher.on('error', onError); - }))); - - if (initialResults.some(({status}) => status === 'rejected')) { - logWarn`There were errors loading custom languages from the language path`; - logWarn`provided: ${langPath}`; - - if (noInput) { - logError`Failed to load language files. Please investigate these, or don't provide`; - logError`--lang-path (or HSMUSIC_LANG) and build again.`; - - Object.assign(stepStatusSummary.loadLanguageFiles, { - status: STATUS_FATAL_ERROR, - annotation: `see log for details`, - timeEnd: Date.now(), + for (const {status, value: language, reason: error} of results) { + if (status === 'rejected') { + errorLoadingCustomLanguages = true; + niceShowAggregate(error); + } else { + languages[language.code] = language; + } + } + } else watchCustomLanguages: { + customLanguageWatchers = + languageDataFiles.map(file => { + const watcher = watchLanguageFile(file); + + // Bypass node.js special-case handling for uncaught error events + watcher.on('error', () => {}); + + return watcher; }); - return false; - } + const waitingOnWatchers = new Set(customLanguageWatchers); + + const initialResults = + await Promise.allSettled( + customLanguageWatchers + .map(watcher => new Promise((resolve, reject) => { + const onReady = () => { + watcher.removeListener('ready', onReady); + watcher.removeListener('error', onError); + waitingOnWatchers.delete(watcher); + resolve(); + }; + + const onError = error => { + watcher.removeListener('ready', onReady); + watcher.removeListener('error', onError); + reject(error); + }; + + watcher.on('ready', onReady); + watcher.on('error', onError); + }))); + + if (initialResults.some(({status}) => status === 'rejected')) { + logWarn`There were errors loading custom languages from the language path`; + logWarn`provided: ${langPath}`; + + if (noInput) { + internalDefaultLanguageWatcher.close(); + + for (const watcher of Object.values(customLanguageWatchers)) { + watcher.close(); + } - logWarn`The build should start automatically if you investigate these.`; - logWarn`Or, exit by pressing ^C here (control+C) and run again without`; - logWarn`providing ${'--lang-path'} (or ${'HSMUSIC_LANG'}) to build without custom`; - logWarn`languages.`; - - await new Promise(resolve => { - for (const watcher of waitingOnWatchers) { - watcher.once('ready', () => { - waitingOnWatchers.remove(watcher); - if (empty(waitingOnWatchers)) { - resolve(); - } - }); + errorLoadingCustomLanguages = true; + break watchCustomLanguages; } - }); + + logWarn`The build should start automatically if you investigate these.`; + logWarn`Or, exit by pressing ^C here (control+C) and run again without`; + logWarn`providing ${'--lang-path'} (or ${'HSMUSIC_LANG'}) to build without custom`; + logWarn`languages.`; + + await new Promise(resolve => { + for (const watcher of waitingOnWatchers) { + watcher.once('ready', () => { + waitingOnWatchers.remove(watcher); + if (empty(waitingOnWatchers)) { + resolve(); + } + }); + } + }); + } + + languages = + Object.fromEntries( + customLanguageWatchers + .map(({language}) => [language.code, language])); } - languages = - Object.fromEntries( - customLanguageWatchers - .map(watcher => [watcher.language.code, watcher.language])); + if (errorLoadingCustomLanguages) { + logError`Failed to load language files. Please investigate these, or don't provide`; + logError`--lang-path (or HSMUSIC_LANG) and build again.`; + + Object.assign(stepStatusSummary.loadLanguageFiles, { + status: STATUS_FATAL_ERROR, + annotation: `see log for details`, + timeEnd: Date.now(), + }); + + return false; + } Object.assign(stepStatusSummary.loadLanguageFiles, { status: STATUS_DONE_CLEAN, timeEnd: Date.now(), + annotation: + (noLanguageReloading + ? (selectedBuildModeFlag === 'static-build' + ? `loaded statically, default for --static-build` + : `loaded statically, --no-language-reloading provided`) + : `watching for changes`), }); } else { languages = {}; @@ -1265,24 +1335,40 @@ async function main() { logInfo`Applying new default strings from custom ${customDefaultLanguage.code} language file.`; finalDefaultLanguage = customDefaultLanguage; - finalDefaultLanguageWatcher = - customLanguageWatchers.find(({language}) => language === customDefaultLanguage); finalDefaultLanguageAnnotation = `using wiki-specified custom default language`; + + if (!noLanguageReloading) { + finalDefaultLanguageWatcher = + customLanguageWatchers + .find(({language}) => language === customDefaultLanguage); + } } else if (languages[internalDefaultLanguage.code]) { const customDefaultLanguage = languages[internalDefaultLanguage.code]; + finalDefaultLanguage = customDefaultLanguage; - finalDefaultLanguageWatcher = - customLanguageWatchers.find(({language}) => language === customDefaultLanguage); finalDefaultLanguageAnnotation = `using inferred custom default language`; + + if (!noLanguageReloading) { + finalDefaultLanguageWatcher = + customLanguageWatchers + .find(({language}) => language === customDefaultLanguage); + } } else { languages[internalDefaultLanguage.code] = internalDefaultLanguage; finalDefaultLanguage = internalDefaultLanguage; - finalDefaultLanguageWatcher = internalDefaultLanguageWatcher; finalDefaultLanguageAnnotation = `no custom default language specified`; + + if (!noLanguageReloading) { + finalDefaultLanguageWatcher = internalDefaultLanguageWatcher; + } } const inheritStringsFromInternalLanguage = () => { + // The custom default language, if set, will be the new one providing fallback + // strings for other languages. But on its own, it still might not be a complete + // list of strings - so it falls back to the internal default language, which + // won't otherwise be presented in the build. if (finalDefaultLanguage === internalDefaultLanguage) return; const {strings: inheritedStrings} = internalDefaultLanguage; Object.assign(finalDefaultLanguage, {inheritedStrings}); @@ -1296,22 +1382,24 @@ async function main() { } }; - // The custom default language, if set, will be the new one providing fallback - // strings for other languages. But on its own, it still might not be a complete - // list of strings - so it falls back to the internal default language, which - // won't otherwise be presented in the build. if (finalDefaultLanguage !== internalDefaultLanguage) { inheritStringsFromInternalLanguage(); - internalDefaultLanguageWatcher.on('update', () => { - inheritStringsFromInternalLanguage(); - inheritStringsFromDefaultLanguage(); - }); } inheritStringsFromDefaultLanguage(); - finalDefaultLanguageWatcher.on('update', () => { - inheritStringsFromDefaultLanguage(); - }); + + if (!noLanguageReloading) { + if (finalDefaultLanguage !== internalDefaultLanguage) { + internalDefaultLanguageWatcher.on('update', () => { + inheritStringsFromInternalLanguage(); + inheritStringsFromDefaultLanguage(); + }); + } + + finalDefaultLanguageWatcher.on('update', () => { + inheritStringsFromDefaultLanguage(); + }); + } logInfo`Loaded language strings: ${Object.keys(languages).join(', ')}`; -- cgit 1.3.0-6-gf8a5