diff options
Diffstat (limited to 'src/write')
-rw-r--r-- | src/write/bind-utilities.js | 2 | ||||
-rw-r--r-- | src/write/build-modes/live-dev-server.js | 6 | ||||
-rw-r--r-- | src/write/build-modes/repl.js | 6 | ||||
-rw-r--r-- | src/write/build-modes/static-build.js | 126 |
4 files changed, 131 insertions, 9 deletions
diff --git a/src/write/bind-utilities.js b/src/write/bind-utilities.js index 3d4ecc7a..8dd08dba 100644 --- a/src/write/bind-utilities.js +++ b/src/write/bind-utilities.js @@ -19,7 +19,6 @@ import { export function bindUtilities({ absoluteTo, - cachebust, defaultLanguage, getSizeOfAdditionalFile, getSizeOfImagePath, @@ -36,7 +35,6 @@ export function bindUtilities({ Object.assign(bound, { absoluteTo, - cachebust, defaultLanguage, getSizeOfAdditionalFile, getSizeOfImagePath, diff --git a/src/write/build-modes/live-dev-server.js b/src/write/build-modes/live-dev-server.js index 03ef6049..b018bc1c 100644 --- a/src/write/build-modes/live-dev-server.js +++ b/src/write/build-modes/live-dev-server.js @@ -24,7 +24,7 @@ import {generateRandomLinkDataJSON, generateRedirectHTML} from '../common-templa const defaultHost = '0.0.0.0'; const defaultPort = 8002; -export const description = `Hosts a local HTTP server which generates page content as it is requested, instead of all at once; reacts to changes in data files, so new reloads will be up-to-date with on-disk YAML data (<- not implemented yet, check back soon!)\n\nIntended for local development ONLY; this custom HTTP server is NOT rigorously tested and almost certainly has security flaws`; +export const description = `Hosts a local HTTP server which generates page content as it is requested, instead of all at once\n\nIntended for local development ONLY; this custom HTTP server is NOT rigorously tested and almost certainly has security flaws`; export const config = { fileSizes: { @@ -103,7 +103,6 @@ export async function go({ webRoutes, wikiData, - cachebust, developersComment: _developersComment, getSizeOfAdditionalFile, getSizeOfImagePath, @@ -338,7 +337,7 @@ export async function go({ if (!Object.hasOwn(urlToPageMap, pathnameKey)) { response.writeHead(404, contentTypePlain); response.end(`No page found for: ${pathnameKey}\n`); - if (loudResponses) console.log(`${requestHead} [404] ${pathname}`); + if (loudResponses) console.log(`${requestHead} [404] ${pathname} (no page)`); return; } @@ -399,7 +398,6 @@ export async function go({ const bound = bindUtilities({ absoluteTo, - cachebust, defaultLanguage, getSizeOfAdditionalFile, getSizeOfImagePath, diff --git a/src/write/build-modes/repl.js b/src/write/build-modes/repl.js index b300e8e8..faba8a34 100644 --- a/src/write/build-modes/repl.js +++ b/src/write/build-modes/repl.js @@ -13,6 +13,10 @@ export const config = { default: 'skip', }, + search: { + default: 'skip', + }, + thumbs: { applicable: false, }, @@ -51,6 +55,7 @@ export async function getContextAssignments({ missingImagePaths, thumbsCache, urls, + webRoutes, wikiData, getSizeOfAdditionalFile, @@ -78,6 +83,7 @@ export async function getContextAssignments({ missingImagePaths, thumbsCache, urls, + webRoutes, wikiData, ...wikiData, diff --git a/src/write/build-modes/static-build.js b/src/write/build-modes/static-build.js index 68cf0949..86e3da0f 100644 --- a/src/write/build-modes/static-build.js +++ b/src/write/build-modes/static-build.js @@ -2,6 +2,7 @@ import * as path from 'node:path'; import { copyFile, + cp, mkdir, stat, symlink, @@ -9,6 +10,8 @@ import { unlink, } from 'node:fs/promises'; +import {rimraf} from 'rimraf'; + import {quickLoadContentDependencies} from '#content-dependencies'; import {quickEvaluate} from '#content-function'; import * as html from '#html'; @@ -49,6 +52,10 @@ export const config = { default: 'perform', }, + search: { + default: 'perform', + }, + thumbs: { default: 'perform', }, @@ -115,7 +122,6 @@ export async function go({ webRoutes, wikiData, - cachebust, developersComment: _developersComment, getSizeOfAdditionalFile, getSizeOfImagePath, @@ -156,6 +162,11 @@ export async function go({ webRoutes, }); + await writeWebRouteCopies({ + outputPath, + webRoutes, + }); + if (writeAll) { await writeFavicon({ mediaPath, @@ -306,7 +317,6 @@ export async function go({ const bound = bindUtilities({ absoluteTo, - cachebust, defaultLanguage, getSizeOfAdditionalFile, getSizeOfImagePath, @@ -436,8 +446,11 @@ function writeWebRouteSymlinks({ outputPath, webRoutes, }) { + const symlinkRoutes = + webRoutes.filter(route => route.statically === 'symlink'); + const promises = - webRoutes.map(async route => { + symlinkRoutes.map(async route => { const parts = route.to.split('/'); const parentDirectoryParts = parts.slice(0, -1); const symlinkNamePart = parts.at(-1); @@ -469,6 +482,113 @@ function writeWebRouteSymlinks({ return progressPromiseAll(`Writing web route symlinks.`, promises); } +async function writeWebRouteCopies({ + outputPath, + webRoutes, +}) { + const copyRoutes = + webRoutes.filter(route => route.statically === 'copy'); + + const promises = + copyRoutes.map(async route => { + const permissionName = '__hsmusic-ok-for-deletion.txt'; + + const parts = route.to.split('/'); + const parentDirectoryParts = parts.slice(0, -1); + const copyNamePart = parts.at(-1); + + const parentDirectory = path.join(outputPath, ...parentDirectoryParts); + const copyPath = path.join(parentDirectory, copyNamePart); + + // We're going to do a rimraf call! This is freaking terrifying, + // so nope out on a couple important conditions. + + let needsDelete; + try { + await stat(copyPath); + needsDelete = true; + } catch (error) { + if (error.code === 'ENOENT') { + needsDelete = false; + } else { + throw error; + } + } + + if (needsDelete) { + // First remove it directly, in case it's a symlink. + try { + await unlink(copyPath); + needsDelete = false; + } catch (error) { + // EPERM is POSIX, but libuv may or may not flat-out just raise + // the system error (which is ostensibly EISDIR on Linux). + // https://github.com/nodejs/node-v0.x-archive/issues/5791 + // https://man7.org/linux/man-pages/man2/unlink.2.html + // + // Both of these indidcate "a directory, probably" and we'll + // still check for the deletion permission file where we expect + // it before actually touching anything. + if (error.code !== 'EPERM' && error.code !== 'EISDIR') { + throw error; + } + } + } + + if (needsDelete) { + // Then check that the deletion permission file exists + // where we expect it. + try { + await stat(path.join(copyPath, permissionName)); + } catch (error) { + if (error.code === 'ENOENT') { + throw new Error(`Couldn't find ${permissionName} in ${copyPath} - please delete or move away this folder manually`); + } else { + throw error; + } + } + + // And *then* actually delete that directory. + await rimraf(copyPath); + } + + // Actually copy the source path where it's wanted. + await cp(route.from, copyPath, {recursive: true}); + + // And certify that it's OK to delete this path, next time around. + await writeFile(path.join(copyPath, permissionName), + `The presence of this file (by its name, not its contents)\n` + + `indicates hsmusic may delete everything contained in this\n` + + `directory (the one which directly contains this file, *not*\n` + + `any further-up parent directories).\n` + + `\n` + + `If you make edits, or add any files, they will be deleted or\n` + + `overwritten the next time you run the build.\n` + + `\n` + + `If you delete *this* file, hsmusic will error during the next\n` + + `build, and will ask that you delete the containing directory\n` + + `yourself.\n`); + }); + + const results = + await Promise.allSettled(promises); + + const errors = + results + .filter(({status}) => status === 'rejected') + .map(({reason}) => reason) + .map(err => + (err.message.startsWith(`Couldn't find`) + ? err.message + : err)); + + if (empty(errors)) { + logInfo`Wrote web route copies.`; + } else { + throw new AggregateError(errors, `Errors copying internal files ("web routes")`); + } +} + async function writeFavicon({ mediaPath, outputPath, |