Template
mirror of
https://github.com/r-ca/md2pdf-meow.git
synced 2026-11-08 19:37:36 +00:00
feat: support yaml-based template meta
This commit is contained in:
1 parent
bfe2e92245
commit
8a65b03780
8 files changed
+186
-13
No files matched your search
+3
-1
@@ -8,6 +8,7 @@ import { prepareWorkspace } from './pipeline/prepareWorkspace.js';
|
||||
import { AssetManager } from './pipeline/assetManager.js';
|
||||
import { renderMarkdownToHtml } from './pipeline/renderMarkdown.js';
|
||||
import { renderTemplate } from './pipeline/renderTemplate.js';
|
||||
import { loadMeta } from './pipeline/loadMeta.js';
|
||||
|
||||
async function runPipeline() {
|
||||
logger.info('md2pdf-meow のビルドを開始します。');
|
||||
@@ -16,7 +17,8 @@ async function runPipeline() {
|
||||
const { outputPath: markdownPath, orderedFiles } = await concatMarkdown(assetManager);
|
||||
await injectToc(markdownPath);
|
||||
await renderMarkdownToHtml(markdownPath, WORK_MARKDOWN_HTML, assetManager, orderedFiles);
|
||||
await renderTemplate(TEMPLATE_FILE, WORK_TEMPLATE_HTML);
|
||||
const meta = await loadMeta();
|
||||
await renderTemplate(TEMPLATE_FILE, WORK_TEMPLATE_HTML, meta);
|
||||
await inlineAssets(WORK_TEMPLATE_HTML, DIST_INLINE_HTML);
|
||||
await generatePdf(DIST_INLINE_HTML, DIST_PDF);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import path from 'node:path';
|
||||
import { parse as parseYaml } from 'yaml';
|
||||
|
||||
import { DOCUMENTS_DIR } from '../config.js';
|
||||
import { logger } from '../logger.js';
|
||||
import { pathExists, readText } from '../utils/fs.js';
|
||||
|
||||
export interface DocumentMetaSection {
|
||||
title?: string;
|
||||
author?: string;
|
||||
published?: string;
|
||||
pubDate?: string;
|
||||
copyright?: string;
|
||||
}
|
||||
|
||||
export interface DocumentMeta {
|
||||
title: string;
|
||||
author: string;
|
||||
published: string;
|
||||
description?: string;
|
||||
copyright: string;
|
||||
frontCover?: DocumentMetaSection;
|
||||
backCover?: DocumentMetaSection;
|
||||
}
|
||||
|
||||
const META_FILES = ['_meta.yaml', '_meta.yml'];
|
||||
|
||||
const DEFAULT_META: DocumentMeta = {
|
||||
title: 'Template',
|
||||
author: 'ろむねこ',
|
||||
published: '0000/000/00',
|
||||
copyright: 'template',
|
||||
description: ''
|
||||
};
|
||||
|
||||
function toRecord(value: unknown): Record<string, unknown> | undefined {
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function pickString(value: unknown): string | undefined {
|
||||
if (typeof value === 'string') {
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length > 0 ? trimmed : undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function buildSection(value: unknown): DocumentMetaSection | undefined {
|
||||
const record = toRecord(value);
|
||||
if (!record) {
|
||||
return undefined;
|
||||
}
|
||||
const section: DocumentMetaSection = {};
|
||||
const title = pickString(record.title);
|
||||
const author = pickString(record.author);
|
||||
const published = pickString(record.published);
|
||||
const pubDate = pickString(record.pubDate);
|
||||
const copyright = pickString(record.copyright);
|
||||
|
||||
if (title) section.title = title;
|
||||
if (author) section.author = author;
|
||||
if (published) section.published = published;
|
||||
if (pubDate) section.pubDate = pubDate;
|
||||
if (copyright) section.copyright = copyright;
|
||||
|
||||
return Object.keys(section).length > 0 ? section : undefined;
|
||||
}
|
||||
|
||||
function mergeMeta(partial: Record<string, unknown>): DocumentMeta {
|
||||
return {
|
||||
title: pickString(partial.title) ?? DEFAULT_META.title,
|
||||
author: pickString(partial.author) ?? DEFAULT_META.author,
|
||||
published: pickString(partial.published) ?? DEFAULT_META.published,
|
||||
description: pickString(partial.description) ?? DEFAULT_META.description,
|
||||
copyright: pickString(partial.copyright) ?? DEFAULT_META.copyright,
|
||||
frontCover: buildSection(partial.frontCover),
|
||||
backCover: buildSection(partial.backCover)
|
||||
};
|
||||
}
|
||||
|
||||
async function loadYamlMeta(filePath: string) {
|
||||
const raw = await readText(filePath);
|
||||
try {
|
||||
const parsed = parseYaml(raw);
|
||||
const record = toRecord(parsed);
|
||||
if (!record) {
|
||||
throw new Error('YAML の内容がオブジェクトではありません。');
|
||||
}
|
||||
return mergeMeta(record);
|
||||
} catch (error) {
|
||||
throw new Error(`YAML を解析できませんでした (${path.basename(filePath)}): ${(error as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadMeta(): Promise<DocumentMeta> {
|
||||
for (const filename of META_FILES) {
|
||||
const fullPath = path.join(DOCUMENTS_DIR, filename);
|
||||
if (await pathExists(fullPath)) {
|
||||
logger.info(`メタ情報ファイル ${filename} を読み込みます`);
|
||||
return loadYamlMeta(fullPath);
|
||||
}
|
||||
}
|
||||
logger.info('メタ情報ファイルが見つからなかったため、テンプレートのデフォルト値を使用します');
|
||||
return DEFAULT_META;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import tidy from 'htmltidy2';
|
||||
|
||||
import { logger } from '../logger.js';
|
||||
import { writeText } from '../utils/fs.js';
|
||||
import { DocumentMeta } from './loadMeta.js';
|
||||
|
||||
const renderFile = promisify(ejs.renderFile);
|
||||
const tidyHtml = promisify(tidy.tidy);
|
||||
@@ -13,9 +14,9 @@ const tidyHtml = promisify(tidy.tidy);
|
||||
* scripts/ejs.js (2SC1815J/md2pdf, MIT License) を TypeScript 化し、
|
||||
* HTML Tidy 設定もフォーク元に合わせている。
|
||||
*/
|
||||
export async function renderTemplate(templatePath: string, outputPath: string) {
|
||||
export async function renderTemplate(templatePath: string, outputPath: string, meta: DocumentMeta) {
|
||||
logger.info('テンプレート HTML を描画しています…');
|
||||
const rendered = await renderFile(templatePath);
|
||||
const rendered = await renderFile(templatePath, { meta });
|
||||
const tidied = await tidyHtml(rendered, {
|
||||
doctype: 'html5',
|
||||
indent: 'auto',
|
||||
|
||||
Reference in new issue
Block a user