|
| 1 | +/** |
| 2 | + * Handle "sidecar" data - these are JSON files that sit next to rendered template previews, |
| 3 | + * containing metadata about the preview, so that we can re-render them later. |
| 4 | + * |
| 5 | + * @packageDocumentation |
| 6 | + */ |
| 7 | +import fs from "fs/promises"; |
| 8 | +import { join } from "path"; |
| 9 | + |
| 10 | +import { TEMPLATE_ENGINES } from "../engines"; |
| 11 | +import { TemplatePreview, TemplatePreviews } from "../engines/types"; |
| 12 | +import { stopIfCriticalFsError } from "../util/files"; |
| 13 | +import createLogger from "../util/logger"; |
| 14 | +import { CliOptions, CSVRecord } from "../util/types"; |
| 15 | +import { SidecardData } from "./types"; |
| 16 | + |
| 17 | +const PARTS_SEPARATOR = "__"; |
| 18 | +const logger = createLogger("docsoc.sidecar"); |
| 19 | + |
| 20 | +/** |
| 21 | + * Generate the first part of a filename for a record's previews - this part |
| 22 | + * identifies the record itself via the fileNamer function. |
| 23 | + * @param record The record to generate a prefix for |
| 24 | + * @param fileNamer A function that generates a filename for a record |
| 25 | + * @returns |
| 26 | + */ |
| 27 | +export const getRecordPreviewPrefix = (record: CSVRecord, fileNamer: (record: CSVRecord) => string) => |
| 28 | + `${fileNamer(record)}`; |
| 29 | + |
| 30 | +/** |
| 31 | + * Generate predicable prefixes for preview names (including record specific part |
| 32 | + * |
| 33 | + * @example |
| 34 | + * const record = { id: "1", name: "Test Record" }; |
| 35 | + * const fileNamer = (record: CSVRecord) => `file_${record["id"]}`; |
| 36 | + * getRecordPreviewPrefixForIndividual(record, fileNamer, "nunjucks", { name: "preview1.txt", content: "content1", metadata: { key: "value" } }) |
| 37 | + * // => "file_1__nunjucks__preview1.txt" |
| 38 | + */ |
| 39 | +export const getRecordPreviewPrefixForIndividual = ( |
| 40 | + record: CSVRecord, |
| 41 | + fileNamer: (record: CSVRecord) => string, |
| 42 | + templateEngine: string, |
| 43 | + preview: TemplatePreview, |
| 44 | +) => [getRecordPreviewPrefix(record, fileNamer), templateEngine, preview.name].join(PARTS_SEPARATOR); |
| 45 | + |
| 46 | +/** |
| 47 | + * Generate the filename for the metadata file for a record |
| 48 | + * @param record The record to generate a metadata filename for |
| 49 | + * @param fileNamer A function that generates a filename for a record |
| 50 | + * @example |
| 51 | + * const record = { id: "1", name: "Test Record" }; |
| 52 | + * const fileNamer = (record: CSVRecord) => `file_${record["id"]}`; |
| 53 | + * getRecordPreviewPrefixForMetadata(record, fileNamer) |
| 54 | + * // => "file_1-metadata.json" |
| 55 | + */ |
| 56 | +export const getRecordPreviewPrefixForMetadata = (record: CSVRecord, fileNamer: (record: CSVRecord) => string) => |
| 57 | + `${getRecordPreviewPrefix(record, fileNamer)}-metadata.json`; |
| 58 | + |
| 59 | +/** |
| 60 | + * Write the metadata for a record & its associated previews to a JSON file. |
| 61 | + * @param record The record to write metadata for |
| 62 | + * @param templateEngine The engine used to render the previews |
| 63 | + * @param templateOptions The options given to the engine |
| 64 | + * @param previews The previews rendered for the record |
| 65 | + * @param fileNamer A function that generates a filename for a record |
| 66 | + * @param previewsRoot The root directory to write the metadata to |
| 67 | + * @returns |
| 68 | + */ |
| 69 | +export async function writeMetadata( |
| 70 | + record: CSVRecord, |
| 71 | + templateEngine: TEMPLATE_ENGINES, |
| 72 | + templateOptions: CliOptions["templateOptions"], |
| 73 | + previews: TemplatePreviews, |
| 74 | + fileNamer: (record: CSVRecord) => string, |
| 75 | + previewsRoot: string, |
| 76 | +): Promise<void> { |
| 77 | + const sidecar: SidecardData = { |
| 78 | + record: record, |
| 79 | + engine: templateEngine, |
| 80 | + engineOptions: templateOptions, |
| 81 | + files: previews.map((preview) => ({ |
| 82 | + filename: getRecordPreviewPrefixForIndividual(record, fileNamer, templateEngine, preview), |
| 83 | + engineData: { |
| 84 | + ...preview, |
| 85 | + content: undefined, |
| 86 | + }, |
| 87 | + })), |
| 88 | + }; |
| 89 | + |
| 90 | + const metadataFile = getRecordPreviewPrefixForMetadata(record, fileNamer); |
| 91 | + logger.debug(`Writing metadata for ${fileNamer(record)} to ${metadataFile}`); |
| 92 | + await stopIfCriticalFsError(fs.writeFile(join(previewsRoot, metadataFile), JSON.stringify(sidecar, null, 4))); |
| 93 | + return Promise.resolve(); |
| 94 | +} |
0 commit comments