diff options
Diffstat (limited to 'src/write')
-rw-r--r-- | src/write/bind-utilities.js | 168 | ||||
-rw-r--r-- | src/write/build-modes/live-dev-server.js | 169 | ||||
-rw-r--r-- | src/write/page-template.js | 150 |
3 files changed, 163 insertions, 324 deletions
diff --git a/src/write/bind-utilities.js b/src/write/bind-utilities.js index ffaaa7a7..d6053353 100644 --- a/src/write/bind-utilities.js +++ b/src/write/bind-utilities.js @@ -5,59 +5,28 @@ import chroma from 'chroma-js'; import { - fancifyFlashURL, - fancifyURL, - getAlbumGridHTML, - getAlbumStylesheet, - getArtistString, - getCarouselHTML, - getFlashGridHTML, - getGridHTML, - getRevealStringFromArtTags, - getRevealStringFromContentWarningMessage, - getThemeString, - generateAdditionalFilesList, - generateAdditionalFilesShortcut, - generateChronologyLinks, - generateContentHeading, - generateCoverLink, - generateInfoGalleryLinks, - generateTrackListDividedByGroups, - generateNavigationLinks, - generateStickyHeadingContainer, - iconifyURL, - img, -} from '../misc-templates.js'; - -import { replacerSpec, transformInline, - transformLyrics, - transformMultiline, + // transformLyrics, + // transformMultiline, } from '../util/transform-content.js'; import * as html from '../util/html.js'; -import {bindOpts, withEntries} from '../util/sugar.js'; +import {bindOpts} from '../util/sugar.js'; import {getColors} from '../util/colors.js'; import {bindFind} from '../util/find.js'; - -import link, {getLinkThemeString} from '../util/link.js'; - -import { - getAlbumCover, - getArtistAvatar, - getFlashCover, - getTrackCover, -} from '../util/wiki-data.js'; +import {thumb} from '../util/urls.js'; export function bindUtilities({ absoluteTo, + cachebust, defaultLanguage, getSizeOfAdditionalFile, getSizeOfImageFile, language, languages, + pagePath, to, urls, wikiData, @@ -69,42 +38,22 @@ export function bindUtilities({ Object.assign(bound, { absoluteTo, + cachebust, defaultLanguage, getSizeOfAdditionalFile, getSizeOfImageFile, html, language, languages, + pagePath, + thumb, to, urls, wikiData, - }) - - bound.img = bindOpts(img, { - [bindOpts.bindIndex]: 0, - getSizeOfImageFile, - html, - to, - }); - - bound.getColors = bindOpts(getColors, { - chroma, - }); - - bound.getLinkThemeString = bindOpts(getLinkThemeString, { - getColors: bound.getColors, + wikiInfo: wikiData.wikiInfo, }); - bound.getThemeString = bindOpts(getThemeString, { - getColors: bound.getColors, - }); - - bound.link = withEntries(link, (entries) => - entries - .map(([key, fn]) => [key, bindOpts(fn, { - getLinkThemeString: bound.getLinkThemeString, - to, - })])); + bound.getColors = bindOpts(getColors, {chroma}); bound.find = bindFind(wikiData, {mode: 'warn'}); @@ -117,6 +66,7 @@ export function bindUtilities({ wikiData, }); + /* bound.transformMultiline = bindOpts(transformMultiline, { img: bound.img, to, @@ -127,81 +77,14 @@ export function bindUtilities({ transformInline: bound.transformInline, transformMultiline: bound.transformMultiline, }); + */ - bound.iconifyURL = bindOpts(iconifyURL, { - html, - language, - to, - }); - - bound.fancifyURL = bindOpts(fancifyURL, { - html, - language, - }); - - bound.fancifyFlashURL = bindOpts(fancifyFlashURL, { - [bindOpts.bindIndex]: 2, - html, - language, - - fancifyURL: bound.fancifyURL, - }); - - bound.getRevealStringFromContentWarningMessage = bindOpts(getRevealStringFromContentWarningMessage, { - html, - language, - }); - - bound.getRevealStringFromArtTags = bindOpts(getRevealStringFromArtTags, { - language, - - getRevealStringFromContentWarningMessage: bound.getRevealStringFromContentWarningMessage, - }); - - bound.getArtistString = bindOpts(getArtistString, { - html, - link: bound.link, - language, - - iconifyURL: bound.iconifyURL, - }); - - bound.getAlbumCover = bindOpts(getAlbumCover, { - to, - }); - - bound.getTrackCover = bindOpts(getTrackCover, { - to, - }); - - bound.getFlashCover = bindOpts(getFlashCover, { - to, - }); - - bound.getArtistAvatar = bindOpts(getArtistAvatar, { - to, - }); - - bound.generateAdditionalFilesShortcut = bindOpts(generateAdditionalFilesShortcut, { - html, - language, - }); - - bound.generateAdditionalFilesList = bindOpts(generateAdditionalFilesList, { - html, - language, - }); - + /* bound.generateNavigationLinks = bindOpts(generateNavigationLinks, { link: bound.link, language, }); - bound.generateContentHeading = bindOpts(generateContentHeading, { - [bindOpts.bindIndex]: 0, - html, - }); - bound.generateStickyHeadingContainer = bindOpts(generateStickyHeadingContainer, { [bindOpts.bindIndex]: 0, html, @@ -217,30 +100,12 @@ export function bindUtilities({ generateNavigationLinks: bound.generateNavigationLinks, }); - bound.generateCoverLink = bindOpts(generateCoverLink, { - [bindOpts.bindIndex]: 0, - html, - img: bound.img, - link: bound.link, - language, - to, - wikiData, - - getRevealStringFromArtTags: bound.getRevealStringFromArtTags, - }); - bound.generateInfoGalleryLinks = bindOpts(generateInfoGalleryLinks, { [bindOpts.bindIndex]: 2, link: bound.link, language, }); - bound.generateTrackListDividedByGroups = bindOpts(generateTrackListDividedByGroups, { - html, - language, - wikiData, - }); - bound.getGridHTML = bindOpts(getGridHTML, { [bindOpts.bindIndex]: 0, img: bound.img, @@ -271,11 +136,8 @@ export function bindUtilities({ [bindOpts.bindIndex]: 0, img: bound.img, html, - }) - - bound.getAlbumStylesheet = bindOpts(getAlbumStylesheet, { - to, }); + */ return bound; } diff --git a/src/write/build-modes/live-dev-server.js b/src/write/build-modes/live-dev-server.js index 6dfa7d71..10b40cf0 100644 --- a/src/write/build-modes/live-dev-server.js +++ b/src/write/build-modes/live-dev-server.js @@ -11,7 +11,7 @@ import {serializeThings} from '../../data/serialize.js'; import * as pageSpecs from '../../page/index.js'; import {logInfo, logWarn, progressCallAll} from '../../util/cli.js'; - +import {empty} from '../../util/sugar.js'; import { getPagePathname, getPagePathnameAcrossLanguages, @@ -20,11 +20,21 @@ import { } from '../../util/urls.js'; import { - generateDocumentHTML, generateGlobalWikiDataJSON, generateRedirectHTML, } from '../page-template.js'; +import { + watchContentDependencies, +} from '../../content/dependencies/index.js'; + +import { + fillRelationsLayoutFromSlotResults, + flattenRelationsTree, + getRelationsTree, + getNeededContentDependencyNames, +} from '../../content-function.js'; + const defaultHost = '0.0.0.0'; const defaultPort = 8002; @@ -64,20 +74,35 @@ export async function go({ developersComment, getSizeOfAdditionalFile, getSizeOfImageFile, + niceShowAggregate, }) { + const showError = (error) => { + if (error instanceof AggregateError && niceShowAggregate) { + niceShowAggregate(error); + } else { + console.error(error); + } + }; + const host = cliOptions['host'] ?? defaultHost; const port = parseInt(cliOptions['port'] ?? defaultPort); + const contentDependenciesWatcher = await watchContentDependencies(); + const {contentDependencies: allContentDependencies} = contentDependenciesWatcher; + + contentDependenciesWatcher.on('error', () => {}); + await new Promise(resolve => contentDependenciesWatcher.once('ready', resolve)); + let targetSpecPairs = getPageSpecsWithTargets({wikiData}); const pages = progressCallAll(`Computing page data & paths for ${targetSpecPairs.length} targets.`, - targetSpecPairs.map(({ + targetSpecPairs.flatMap(({ pageSpec, target, targetless, }) => () => targetless - ? pageSpec.writeTargetless({wikiData}) - : pageSpec.write(target, {wikiData}))).flat(); + ? [pageSpec.writeTargetless({wikiData})] + : pageSpec.pathsForTarget(target))).flat(); logInfo`Will be serving a total of ${pages.length} pages.`; @@ -143,7 +168,7 @@ export async function go({ response.writeHead(500, contentTypeJSON); response.end({error: `Internal error serializing wiki JSON`}); console.error(`${requestHead} [500] /data.json`); - console.error(error); + showError(error); } return; } @@ -186,7 +211,7 @@ export async function go({ response.writeHead(500, contentTypePlain); response.end(`Internal error accessing ${localFileArea} file for: ${safePath}`); console.error(`${requestHead} [500] ${pathname}`); - console.error(error); + showError(error); } return; } @@ -239,7 +264,7 @@ export async function go({ response.writeHead(500, contentTypePlain); response.end(`Failed during file-to-response pipeline`); console.error(`${requestHead} [500] ${pathname}`); - console.error(error); + showError(error); } return; } @@ -305,8 +330,6 @@ export async function go({ return; } - response.writeHead(200, contentTypeHTML); - const localizedPathnames = getPagePathnameAcrossLanguages({ defaultLanguage, languages, @@ -314,37 +337,139 @@ export async function go({ urls, }); + const {name, args} = page.contentFunction; + const bound = bindUtilities({ absoluteTo, + cachebust, defaultLanguage, getSizeOfAdditionalFile, getSizeOfImageFile, language, languages, + pagePath: servePath, to, urls, wikiData, }); - const pageInfo = page.page(bound); - - const pageHTML = generateDocumentHTML(pageInfo, { + const allExtraDependencies = { ...bound, - cachebust, - developersComment, - localizedPathnames, - oEmbedJSONHref: null, // No oEmbed support for live dev server - pagePath: servePath, - pathname, - }); + + appendIndexHTML: false, + transformMultiline: text => `<p>${text}</p>`, + }; + + // NOTE: ALL THIS STUFF IS PASTED, REVIEW AND INTEGRATE SOON(TM) + + const treeInfo = getRelationsTree(allContentDependencies, name, wikiData, ...args); + const flatTreeInfo = flattenRelationsTree(treeInfo); + const {root, relationIdentifier, flatRelationSlots} = flatTreeInfo; + + const neededContentDependencyNames = + getNeededContentDependencyNames(allContentDependencies, name); + + // Content functions aren't recursive, so by following the set above + // sequentually, we will always provide fulfilled content functions as the + // dependencies for later content functions. + const fulfilledContentDependencies = {}; + for (const name of neededContentDependencyNames) { + const unfulfilledContentFunction = allContentDependencies[name]; + if (!unfulfilledContentFunction) continue; + + const {contentDependencies, extraDependencies} = unfulfilledContentFunction; + + if (empty(contentDependencies) && empty(extraDependencies)) { + fulfilledContentDependencies[name] = unfulfilledContentFunction; + continue; + } + + const fulfillments = {}; + + for (const dependencyName of contentDependencies ?? []) { + if (dependencyName in fulfilledContentDependencies) { + fulfillments[dependencyName] = + fulfilledContentDependencies[dependencyName]; + } + } + + for (const dependencyName of extraDependencies ?? []) { + if (dependencyName in allExtraDependencies) { + fulfillments[dependencyName] = + allExtraDependencies[dependencyName]; + } + } + + fulfilledContentDependencies[name] = + unfulfilledContentFunction.fulfill(fulfillments); + } + + // There might still be unfulfilled content functions if dependencies weren't + // provided as part of allContentDependencies or allExtraDependencies. + // Catch and report these early, together in an aggregate error. + const unfulfilledErrors = []; + const unfulfilledNames = []; + for (const name of neededContentDependencyNames) { + const contentFunction = fulfilledContentDependencies[name]; + if (!contentFunction) continue; + if (!contentFunction.fulfilled) { + try { + contentFunction(); + } catch (error) { + error.message = `(${name}) ${error.message}`; + unfulfilledErrors.push(error); + unfulfilledNames.push(name); + } + } + } + + if (!empty(unfulfilledErrors)) { + throw new AggregateError(unfulfilledErrors, `Content functions unfulfilled (${unfulfilledNames.join(', ')})`); + } + + const slotResults = {}; + + function runContentFunction({name, args, relations: flatRelations}) { + const contentFunction = fulfilledContentDependencies[name]; + + if (!contentFunction) { + throw new Error(`Content function ${name} unfulfilled or not listed`); + } + + const sprawl = + contentFunction.sprawl?.(allExtraDependencies.wikiData, ...args); + + const relations = + fillRelationsLayoutFromSlotResults(relationIdentifier, slotResults, flatRelations); + + const data = + (sprawl + ? contentFunction.data?.(sprawl, ...args) + : contentFunction.data?.(...args)); + + const generateArgs = [data, relations].filter(Boolean); + + return contentFunction(...generateArgs); + } + + for (const slot of Object.getOwnPropertySymbols(flatRelationSlots)) { + slotResults[slot] = runContentFunction(flatRelationSlots[slot]); + } + + const topLevelResult = runContentFunction(root); + + // END PASTE + + const pageHTML = topLevelResult.toString(); console.log(`${requestHead} [200] ${pathname}`); + response.writeHead(200, contentTypeHTML); response.end(pageHTML); } catch (error) { response.writeHead(500, contentTypePlain); response.end(`Error generating page, view server log for details\n`); console.error(`${requestHead} [500] ${pathname}`); - console.error(error); + showError(error); } }); @@ -360,7 +485,7 @@ export async function go({ }, 10_000); } else { console.error(`Server error detected (code: ${error.code})`); - console.error(error); + showError(error); } }); diff --git a/src/write/page-template.js b/src/write/page-template.js index 8a3b44e8..d3d7b098 100644 --- a/src/write/page-template.js +++ b/src/write/page-template.js @@ -3,11 +3,6 @@ import chroma from 'chroma-js'; import * as html from '../util/html.js'; import {getColors} from '../util/colors.js'; -import { - getFooterLocalizationLinks, - getRevealStringFromContentWarningMessage, -} from '../misc-templates.js'; - export function generateDevelopersCommentHTML({ buildTime, commit, @@ -153,40 +148,7 @@ export function generateDocumentHTML(pageInfo, { const collapseSidebars = sidebarLeft.collapse !== false && sidebarRight.collapse !== false; - const mainHTML = - html.tag('main', { - id: 'content', - class: main.classes, - }, [ - ...html.fragment( - !title ? - null - : main.headingMode === 'sticky' ? - generateStickyHeadingContainer({ - coverSrc: cover.src, - coverAlt: cover.alt, - coverArtTags: cover.artTags, - title, - }) - : main.headingMode === 'static' ? - html.tag('h1', title) - : null), - - ...html.fragment( - cover.src && - generateCoverLink({ - src: cover.src, - alt: cover.alt, - tags: cover.artTags, - })), - html.tag('div', - { - [html.onlyIfContent]: true, - class: 'main-content-container', - }, - main.content), - ]); const footerHTML = html.tag('footer', @@ -378,31 +340,6 @@ export function generateDocumentHTML(pageInfo, { height: banner.dimensions[1] || 200, })); - const layoutHTML = [ - navHTML, - banner.position === 'top' && bannerHTML, - secondaryNavHTML, - html.tag('div', - { - class: [ - 'layout-columns', - !collapseSidebars && 'vertical-when-thin', - (sidebarLeftHTML || sidebarRightHTML) && 'has-one-sidebar', - (sidebarLeftHTML && sidebarRightHTML) && 'has-two-sidebars', - !(sidebarLeftHTML || sidebarRightHTML) && 'has-zero-sidebars', - sidebarLeftHTML && 'has-sidebar-left', - sidebarRightHTML && 'has-sidebar-right', - ], - }, - [ - sidebarLeftHTML, - mainHTML, - sidebarRightHTML, - ]), - banner.position === 'bottom' && bannerHTML, - footerHTML, - ].filter(Boolean).join('\n'); - const processSkippers = skipperList => skipperList .filter(Boolean) @@ -612,92 +549,7 @@ export function generateDocumentHTML(pageInfo, { }), ].filter(Boolean).join('\n'); - return `<!DOCTYPE html>\n` + html.tag('html', - { - lang: language.intlCode, - 'data-language-code': language.code, - 'data-url-key': 'localized.' + pagePath[0], - ...Object.fromEntries( - pagePath.slice(1).map((v, i) => [['data-url-value' + i], v])), - 'data-rebase-localized': to('localized.root'), - 'data-rebase-shared': to('shared.root'), - 'data-rebase-media': to('media.root'), - 'data-rebase-data': to('data.root'), - }, - [ - developersComment, - - html.tag('head', [ - html.tag('title', - showWikiNameInTitle - ? language.formatString('misc.pageTitle.withWikiName', { - title, - wikiName: wikiInfo.nameShort, - }) - : language.formatString('misc.pageTitle', {title})), - - html.tag('meta', {charset: 'utf-8'}), - html.tag('meta', { - name: 'viewport', - content: 'width=device-width, initial-scale=1', - }), - - ...( - Object.entries(meta) - .filter(([key, value]) => value) - .map(([key, value]) => html.tag('meta', {[key]: value}))), - - canonical && - html.tag('link', { - rel: 'canonical', - href: canonical, - }), - - ...( - localizedCanonical - .map(({lang, href}) => html.tag('link', { - rel: 'alternate', - hreflang: lang, - href, - }))), - - socialEmbedHTML, - - html.tag('link', { - rel: 'stylesheet', - href: to('shared.staticFile', `site3.css?${cachebust}`), - }), - - html.tag('style', - {[html.onlyIfContent]: true}, - [ - theme, - stylesheet, - ]), - - html.tag('script', { - src: to('shared.staticFile', `lazy-loading.js?${cachebust}`), - }), - ]), - - html.tag('body', - {style: body.style || ''}, - [ - html.tag('div', {id: 'page-container'}, [ - mainHTML && - skippersHTML, - layoutHTML, - ]), - - infoCardHTML, - imageOverlayHTML, - - html.tag('script', { - type: 'module', - src: to('shared.staticFile', `client.js?${cachebust}`), - }), - ]), - ]); + return `<!DOCTYPE html>\n` } export function generateOEmbedJSON(pageInfo, {language, wikiData}) { |