import { D as XML_DECLARATION, E as VIEWER_ROOT_PATH, T as TOC_TITLE, _ as EPUB_NS, b as MANIFEST_FILENAME, f as EPUB_CONTAINER_XML, h as EPUB_LANDMARKS_TOC_ENTRY, m as EPUB_LANDMARKS_TITLE, n as CONTAINER_LOCAL_HOSTNAME, p as EPUB_LANDMARKS_COVER_ENTRY, s as DEFAULT_BROWSER_VERSIONS, t as CMYK_RESERVE_MAP_FILENAME } from "./constants-DgZl8nng.js";
import { A as statFileSync, C as pathEquals, E as registerCleanupHandler, I as viewerRoot, N as touchTmpFile, O as runCleanupHandlers, P as useTmpDirectory, R as writeFileIfChanged, S as pathContains, T as readPackageJson, _ as isInContainer, a as cwd, b as openEpub, d as getDefaultBrowserTag, f as getDefaultEpubOpfPath, h as getOsLocale, j as toError, l as executeWithCleanupOnInterrupt, m as getFormattedError, n as assertPubManifestSchema, o as debounce, p as getEpubRootDir, r as cliVersion, s as detectBrowserPlatform, t as DetailError, u as getCacheDir, v as isRunningOnWSL, y as isValidUri, z as Logger } from "./util-CsmUMRtR.js";
import { a as _usingCtx, i as getWebPubResourceMatcher, o as loadVivliostyleConfig, r as getAssetMatcher, s as locateVivliostyleConfig, t as GlobMatcher, u as setupConfigFromFlags } from "./asset-DBj3nifv.js";
import { t as importNodeModule } from "./node-modules-CWZAzWTp.js";
import { VFM, readMetadata } from "@vivliostyle/vfm";
import upath from "upath";
import fs from "node:fs";
import { URL as URL$1, fileURLToPath, pathToFileURL } from "node:url";
import { createServer, preview } from "vite";
import { lookup } from "mime-types";
import npa from "npm-package-arg";
import { glob, globSync } from "tinyglobby";
import vfile from "vfile";
import escapeRe from "escape-string-regexp";
import sirv from "sirv";
import { copy, move } from "fs-extra/esm";
import serializeToXml from "w3c-xmlserializer";
import MIMEType from "whatwg-mimetype";
import jsdom, { JSDOM, ResourceLoader } from "@vivliostyle/jsdom";
import DOMPurify from "dompurify";
import { toHtml } from "hast-util-to-html";
import { jsx, jsxs } from "hastscript/jsx-runtime";
import archiver from "archiver";
import { lookup as lookup$1 } from "bcp-47-match";
import XMLBuilder from "fast-xml-builder";
import GithubSlugger from "github-slugger";
import { v4 } from "uuid";
import Arborist from "@npmcli/arborist";
//#region src/config/merge.ts
const pruneObject = (obj) => {
	return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== void 0 && value !== null));
};
function mergeConfig$1(base, override) {
	return {
		tasks: base.tasks.map((task) => ({
			...pruneObject(task),
			...pruneObject(override)
		})),
		inlineOptions: base.inlineOptions
	};
}
function mergeInlineConfig({ tasks, inlineOptions }, inlineConfig) {
	const { theme, size, pressReady, title, author, language, readingProgression, timeout, image, viewer, viewerParam, browser, output, renderMode, preflight, preflightOption, vite, viteConfigFile, host, port, ...overrideInlineOptions } = inlineConfig;
	return {
		tasks: tasks.map((task) => ({
			...pruneObject(task),
			...pruneObject({
				theme: theme === false ? void 0 : theme,
				size,
				pressReady,
				title,
				author,
				language,
				readingProgression,
				timeout,
				image,
				viewer,
				viewerParam,
				browser,
				vite,
				viteConfigFile
			}),
			output: (output?.length ? output : task.output)?.map((o) => Object.assign(pruneObject(o), pruneObject({
				renderMode,
				preflight,
				preflightOption
			}))),
			server: {
				...pruneObject(task.server ?? {}),
				...pruneObject({
					host,
					port
				})
			}
		})),
		inlineOptions: {
			...pruneObject(inlineOptions),
			...pruneObject({
				renderMode,
				preflight,
				preflightOption
			}),
			...pruneObject(overrideInlineOptions)
		}
	};
}
//#endregion
//#region src/processor/markdown.ts
async function processMarkdown(documentProcessorFactory, documentMetadataReader, filepath, options = {}) {
	const markdownString = fs.readFileSync(filepath, "utf8");
	return await documentProcessorFactory(options, documentMetadataReader(markdownString)).process(vfile({
		path: filepath,
		contents: markdownString
	}));
}
function readMarkdownMetadata(filepath, documentMetadataReader) {
	return documentMetadataReader(fs.readFileSync(filepath, "utf8"));
}
//#endregion
//#region src/config/resolve.ts
const manuscriptMediaTypes = [
	"text/markdown",
	"text/html",
	"text/plain",
	"application/xhtml+xml",
	"text/x-vivliostyle-custom"
];
function parseHexColor(hex) {
	let b, g, r;
	if (hex.length === 4 || hex.length === 5) {
		r = Number.parseInt(hex[1] + hex[1], 16);
		g = Number.parseInt(hex[2] + hex[2], 16);
		b = Number.parseInt(hex[3] + hex[3], 16);
	} else {
		r = Number.parseInt(hex.slice(1, 3), 16);
		g = Number.parseInt(hex.slice(3, 5), 16);
		b = Number.parseInt(hex.slice(5, 7), 16);
	}
	return {
		r: Math.round(r / 255 * 1e4),
		g: Math.round(g / 255 * 1e4),
		b: Math.round(b / 255 * 1e4)
	};
}
function resolveMapEntries(entries) {
	return entries.map(([rgb, cmyk]) => {
		return [typeof rgb === "string" ? parseHexColor(rgb) : rgb, cmyk];
	});
}
const UseTemporaryServerRoot = Symbol("UseTemporaryServerRoot");
const DEFAULT_ASSET_EXTENSIONS = [
	"css",
	"css.map",
	"png",
	"jpg",
	"jpeg",
	"svg",
	"gif",
	"webp",
	"apng",
	"ttf",
	"otf",
	"woff",
	"woff2"
];
function isManuscriptMediaType(mediaType) {
	return !!(mediaType && manuscriptMediaTypes.includes(mediaType));
}
const htmlExtensions = [
	".html",
	".htm",
	".xhtml",
	".xht"
];
/**
* Convert non-HTML file extensions to .html
* HTML/XHTML extensions are preserved as-is
*/
function toHtmlExtension(filename) {
	const ext = upath.extname(filename).toLowerCase();
	if (htmlExtensions.includes(ext)) return filename;
	return `${filename.slice(0, -ext.length)}.html`;
}
function isWebPubConfig(config) {
	return config.viewerInput.type === "webpub";
}
function isWebbookConfig(config) {
	return config.viewerInput.type === "webbook";
}
function parsePackageName(specifier, cwd) {
	try {
		let result = npa(specifier, cwd);
		if (result.type === "git" && result.saveSpec?.startsWith("github:")) result = npa(`file:${specifier}`, cwd);
		return result;
	} catch (error) {
		return null;
	}
}
function parseTheme({ theme, context, workspaceDir, themesDir }) {
	const { specifier, import: importPath } = typeof theme === "string" ? {
		specifier: theme,
		import: void 0
	} : theme;
	if (isValidUri(specifier)) return {
		type: "uri",
		name: upath.basename(specifier),
		location: specifier
	};
	const stylePath = upath.resolve(context, specifier);
	if (fs.existsSync(stylePath) && stylePath.endsWith(".css")) {
		const sourceRelPath = upath.relative(context, stylePath);
		return {
			type: "file",
			name: upath.basename(specifier),
			source: stylePath,
			location: upath.resolve(workspaceDir, sourceRelPath)
		};
	}
	const parsed = parsePackageName(specifier, context);
	if (!parsed) throw new Error(`Invalid package name: ${specifier}`);
	if (!parsed.registry && parsed.type !== "directory") throw new Error(`This package specifier is not allowed: ${specifier}`);
	let name = parsed.name;
	let resolvedSpecifier = specifier;
	if (parsed.type === "directory" && parsed.fetchSpec) {
		const pkgJsonPath = upath.join(parsed.fetchSpec, "package.json");
		if (fs.existsSync(pkgJsonPath)) {
			name = readPackageJson(pkgJsonPath).name ?? null;
			resolvedSpecifier = parsed.fetchSpec;
		}
	}
	if (!name) throw new Error(`Could not determine the package name: ${specifier}`);
	return {
		type: "package",
		name,
		specifier: resolvedSpecifier,
		location: upath.join(themesDir, "node_modules", name),
		registry: Boolean(parsed.registry),
		importPath
	};
}
function parsePageSize(size) {
	const [width, height, ...others] = size.split(",");
	if (!width || others.length > 0) throw new Error(`Cannot parse size: ${size}`);
	else if (width && height) return {
		width,
		height
	};
	else return { format: width };
}
function parseFileMetadata({ contentType, sourcePath, workspaceDir, themesDir, documentMetadataReader }) {
	const sourceDir = upath.dirname(sourcePath);
	let title;
	let themes;
	if (documentMetadataReader) {
		const metadata = readMarkdownMetadata(sourcePath, documentMetadataReader);
		title = metadata.title;
		if (metadata.vfm?.theme && themesDir) themes = [metadata.vfm.theme].flat().filter((entry) => !!entry && (typeof entry === "string" || typeof entry === "object")).map((theme) => parseTheme({
			theme,
			context: sourceDir,
			workspaceDir,
			themesDir
		}));
	} else if (contentType === "text/html" || contentType === "application/xhtml+xml") title = fs.readFileSync(sourcePath, "utf8").match(/<title>([^<]*)<\/title>/v)?.[1] || void 0;
	return {
		title,
		themes
	};
}
function parseCustomStyle({ customStyle, entryContextDir }) {
	if (isValidUri(customStyle)) return customStyle;
	const stylePath = upath.resolve(entryContextDir, customStyle);
	if (!pathContains(entryContextDir, stylePath)) throw new Error(`Custom style file ${customStyle} is not in ${entryContextDir}. Make sure the file is located in the context directory or a subdirectory.`);
	if (!fs.existsSync(stylePath)) throw new Error(`Custom style file not found: ${customStyle}`);
	return pathToFileURL(stylePath).href.slice(pathToFileURL(entryContextDir).href.replace(/\/$/v, "").length + 1);
}
function resolveTaskConfig(config, options) {
	const context = options.cwd ?? cwd;
	Logger.debug("resolveTaskConfig > context %s", context);
	const entryContextDir = config.entryContext ? upath.resolve(context, config.entryContext) : context;
	const language = config.language;
	const readingProgression = config.readingProgression;
	const size = config.size ? parsePageSize(config.size) : void 0;
	const cropMarks = options.cropMarks ?? false;
	const bleed = options.bleed;
	const cropOffset = options.cropOffset;
	const css = options.css;
	const singleDoc = options.singleDoc ?? false;
	const quick = options.quick ?? false;
	const temporaryFilePrefix = config.temporaryFilePrefix ?? `.vs-${Date.now()}.`;
	const vfmOptions = {
		...config?.vfm,
		hardLineBreaks: config?.vfm?.hardLineBreaks ?? false,
		disableFormatHtml: config?.vfm?.disableFormatHtml ?? false
	};
	const timeout = config.timeout ?? 3e5;
	const sandbox = options.sandbox ?? false;
	const browser = (() => {
		const type = config.browser?.type ?? "chrome";
		const platform = detectBrowserPlatform();
		return {
			type,
			tag: config.browser?.tag ?? (platform ? DEFAULT_BROWSER_VERSIONS[type][platform] : "latest"),
			executablePath: options.executableBrowser
		};
	})();
	const proxyServer = options.proxyServer ?? process.env.HTTP_PROXY ?? void 0;
	const proxy = proxyServer ? {
		server: proxyServer,
		bypass: options.proxyBypass ?? process.env.NOPROXY ?? void 0,
		username: options.proxyUser,
		password: options.proxyPass
	} : void 0;
	const image = config.image ?? `ghcr.io/vivliostyle/cli:${cliVersion}`;
	const viewer = config.viewer ?? void 0;
	const viewerParam = config.viewerParam ?? void 0;
	const logLevel = options.logLevel ?? "silent";
	const ignoreHttpsErrors = options.ignoreHttpsErrors ?? false;
	const base = config.base ?? "/vivliostyle";
	const staticRoutes = config.static ?? {};
	const viteConfig = config.vite;
	const viteConfigFile = config.viteConfigFile ?? true;
	const customStyle = options.style && parseCustomStyle({
		customStyle: options.style,
		entryContextDir
	}) || void 0;
	const customUserStyle = options.userStyle && parseCustomStyle({
		customStyle: options.userStyle,
		entryContextDir
	}) || void 0;
	const outputs = (() => {
		const resolveCmykConfig = (cmykOption) => {
			if (cmykOption && typeof cmykOption === "object") return {
				warnUnmapped: cmykOption.warnUnmapped ?? true,
				overrideMap: resolveMapEntries(cmykOption.overrideMap ?? []),
				reserveMap: resolveMapEntries(cmykOption.reserveMap ?? []),
				mapOutput: cmykOption.mapOutput ? upath.resolve(context, cmykOption.mapOutput) : void 0
			};
			if (options.cmyk || cmykOption === true) return {
				warnUnmapped: true,
				overrideMap: [],
				reserveMap: [],
				mapOutput: void 0
			};
			return false;
		};
		const resolveReplaceImageConfig = (replaceImageOption) => {
			if (!replaceImageOption) return [];
			const allFiles = globSync("**/*", {
				cwd: entryContextDir,
				onlyFiles: true
			});
			return replaceImageOption.flatMap(({ source, replacement }) => {
				if (source instanceof RegExp) return allFiles.filter((file) => source.test(file)).map((file) => ({
					source: upath.resolve(entryContextDir, file),
					replacement: upath.resolve(entryContextDir, file.replace(source, replacement))
				}));
				return {
					source: upath.resolve(entryContextDir, source),
					replacement: upath.resolve(entryContextDir, replacement)
				};
			});
		};
		const resolveDefaultPreflight = () => {
			if (options.preflight) return options.preflight;
			const pp = config.pdfPostprocess;
			if (pp && "preflight" in pp) return pp.preflight;
			if (config.pressReady) return "press-ready";
		};
		const resolveDefaultPreflightOption = () => {
			if (options.preflightOption) return options.preflightOption;
			return config.pdfPostprocess?.preflightOption ?? [];
		};
		const defaultPdfOptions = {
			format: "pdf",
			renderMode: options.renderMode ?? "local",
			preflight: resolveDefaultPreflight(),
			preflightOption: resolveDefaultPreflightOption(),
			cmyk: resolveCmykConfig(config.pdfPostprocess?.cmyk),
			replaceImage: resolveReplaceImageConfig(config.pdfPostprocess?.replaceImage)
		};
		if (config.output) return config.output.map((target) => {
			const outputPath = upath.resolve(context, target.path);
			const format = target.format;
			switch (format) {
				case "pdf": {
					const targetPp = target.pdfPostprocess;
					const { pdfPostprocess: _, ...targetRest } = target;
					const resolvedPreflight = (() => {
						if (options.preflight) return options.preflight;
						if (targetPp?.preflight) return targetPp.preflight;
						if (target.preflight) return target.preflight;
						return defaultPdfOptions.preflight;
					})();
					const resolvedPreflightOption = (() => {
						if (options.preflightOption) return options.preflightOption;
						if (targetPp?.preflightOption) return targetPp.preflightOption;
						if (target.preflightOption) return target.preflightOption;
						return defaultPdfOptions.preflightOption;
					})();
					const resolvedCmyk = targetPp?.cmyk === void 0 ? defaultPdfOptions.cmyk : resolveCmykConfig(targetPp.cmyk);
					const resolvedReplaceImage = targetPp?.replaceImage === void 0 ? defaultPdfOptions.replaceImage : resolveReplaceImageConfig(targetPp.replaceImage);
					return {
						...defaultPdfOptions,
						...targetRest,
						format,
						path: outputPath,
						preflight: resolvedPreflight,
						preflightOption: resolvedPreflightOption,
						cmyk: resolvedCmyk,
						replaceImage: resolvedReplaceImage
					};
				}
				case "epub": return {
					...target,
					format,
					path: outputPath,
					version: "3.0"
				};
				case "webpub": return {
					...target,
					format,
					path: outputPath
				};
				default: return format;
			}
		});
		const filename = config.title ? `${config.title}.pdf` : "output.pdf";
		return [{
			...defaultPdfOptions,
			path: upath.resolve(context, filename)
		}];
	})();
	const { server, rootUrl } = (() => {
		let host = config.server?.host ?? false;
		const allowedHosts = config.server?.allowedHosts || [];
		const port = config.server?.port ?? 13e3;
		if (outputs.some((target) => target.format === "pdf" && target.renderMode === "docker") && !isInContainer()) {
			host = true;
			if (Array.isArray(allowedHosts) && !allowedHosts.includes("host.docker.internal")) allowedHosts.push(CONTAINER_LOCAL_HOSTNAME);
		}
		const rootHostname = host ? host === true ? "0.0.0.0" : host : "localhost";
		return {
			server: {
				host,
				port,
				proxy: config.server?.proxy ?? {},
				allowedHosts
			},
			rootUrl: `http://${rootHostname}:${port}`
		};
	})();
	const cover = config.cover && {
		src: upath.resolve(entryContextDir, config.cover.src),
		name: config.cover.name || "Cover image"
	};
	const copyAsset = {
		includes: config.copyAsset?.includes ?? config.includeAssets ?? [],
		excludes: config.copyAsset?.excludes ?? [],
		fileExtensions: [.../* @__PURE__ */ new Set([...DEFAULT_ASSET_EXTENSIONS, ...config.copyAsset?.includeFileExtensions ?? []])].filter((ext) => !(config.copyAsset?.excludeFileExtensions ?? []).includes(ext))
	};
	const themeIndexes = /* @__PURE__ */ new Set();
	const { input } = options;
	const projectConfig = !options.config && input ? resolveSingleInputConfig({
		config,
		input,
		context,
		temporaryFilePrefix,
		themeIndexes,
		base
	}) : resolveComposedProjectConfig({
		config,
		context,
		entryContextDir,
		outputs,
		temporaryFilePrefix,
		themeIndexes,
		cover
	});
	for (const output of outputs) {
		const relPath = upath.relative(context, output.path);
		if (pathContains(output.path, entryContextDir) || pathEquals(output.path, entryContextDir)) throw new Error(`The output path is set to "${relPath}", but this will overwrite the original manuscript file. Please specify a different path.`);
		if (pathContains(output.path, projectConfig.workspaceDir) || pathEquals(output.path, projectConfig.workspaceDir)) throw new Error(`The output path is set to "${relPath}", but this will overwrite the working directory of Vivliostyle. Please specify a different path.`);
	}
	const { entries, workspaceDir } = projectConfig;
	const duplicatedTarget = entries.find((v1, i) => entries.findLastIndex((v2) => v1.target === v2.target) !== i)?.target;
	if (duplicatedTarget) {
		const sourceFile = entries.find((entry) => entry.target === duplicatedTarget && entry.source?.type === "file")?.source;
		throw new Error(`The output path "${upath.relative(workspaceDir, duplicatedTarget)}" will overwrite existing content.` + (sourceFile ? ` Please choose a different name for the source file: ${sourceFile.pathname}` : ""));
	}
	return {
		...projectConfig,
		entryContextDir,
		outputs,
		themeIndexes,
		copyAsset,
		temporaryFilePrefix,
		size,
		cropMarks,
		bleed,
		cropOffset,
		css,
		customStyle,
		customUserStyle,
		singleDoc,
		quick,
		language,
		readingProgression,
		vfmOptions,
		cover,
		timeout,
		sandbox,
		browser,
		proxy,
		image,
		viewer,
		viewerParam,
		logLevel,
		ignoreHttpsErrors,
		base,
		server,
		static: staticRoutes,
		rootUrl,
		viteConfig,
		viteConfigFile
	};
}
function resolveSingleInputConfig({ config, input, context, temporaryFilePrefix, themeIndexes, base }) {
	Logger.debug("entering single entry config mode");
	let serverRootDir;
	let sourcePath;
	let workspaceDir;
	const inputFormat = input.format;
	const title = config?.title;
	const author = config?.author;
	const entries = [];
	const exportAliases = [];
	let isLocalResource = true;
	if (isValidUri(input.entry)) {
		const url = new URL(input.entry);
		if (url.protocol === "file:") sourcePath = fileURLToPath(url);
		else {
			isLocalResource = false;
			sourcePath = input.entry;
		}
	} else sourcePath = upath.resolve(context, input.entry);
	if (isLocalResource) {
		statFileSync(sourcePath);
		switch (input.format) {
			case "webbook":
			case "markdown":
			case "pub-manifest":
			case "epub":
				workspaceDir = upath.dirname(sourcePath);
				break;
			case "epub-opf": {
				const rootDir = getEpubRootDir(sourcePath);
				if (!rootDir) throw new Error(`Could not determine the EPUB root directory for the OPF file: ${sourcePath}`);
				workspaceDir = rootDir;
				break;
			}
			default: return input.format;
		}
		serverRootDir = workspaceDir;
	} else {
		serverRootDir = UseTemporaryServerRoot;
		workspaceDir = context;
	}
	const themesDir = upath.resolve(workspaceDir, "themes");
	if (input.format === "markdown") {
		const contentType = "text/markdown";
		const documentProcessor = {
			processorFactory: config.documentProcessor ?? VFM,
			metadataReader: config.documentMetadataReader ?? readMetadata
		};
		const metadata = parseFileMetadata({
			contentType,
			sourcePath,
			workspaceDir,
			documentMetadataReader: documentProcessor.metadataReader
		});
		const target = toHtmlExtension(upath.resolve(workspaceDir, `${temporaryFilePrefix}${upath.basename(sourcePath)}`));
		touchTmpFile(target);
		const themes = metadata.themes ?? config.theme?.map((theme) => parseTheme({
			theme,
			context,
			workspaceDir,
			themesDir
		})) ?? [];
		themes.forEach((t) => {
			themeIndexes.add(t);
		});
		entries.push({
			contentType,
			source: {
				type: "file",
				pathname: sourcePath,
				contentType,
				documentProcessor
			},
			target,
			title: metadata.title,
			themes
		});
		exportAliases.push({
			source: target,
			target: upath.resolve(upath.dirname(target), toHtmlExtension(upath.basename(sourcePath)))
		});
	}
	let fallbackTitle;
	let viewerInput;
	if (inputFormat === "markdown") {
		const manifestPath = upath.resolve(workspaceDir, `${temporaryFilePrefix}${MANIFEST_FILENAME}`);
		touchTmpFile(manifestPath);
		exportAliases.push({
			source: manifestPath,
			target: upath.resolve(workspaceDir, MANIFEST_FILENAME)
		});
		fallbackTitle = entries.length === 1 && entries[0].title ? entries[0].title : upath.basename(sourcePath);
		viewerInput = {
			type: "webpub",
			manifestPath,
			needToGenerateManifest: true
		};
	} else if (inputFormat === "webbook") {
		let webbookEntryUrl;
		let webbookPath;
		if (isValidUri(sourcePath)) webbookEntryUrl = new URL(sourcePath).href;
		else {
			const rootFileUrl = pathToFileURL(workspaceDir).href;
			webbookEntryUrl = `${base}${pathToFileURL(sourcePath).href.slice(rootFileUrl.length)}`;
			webbookPath = sourcePath;
		}
		viewerInput = {
			type: "webbook",
			webbookEntryUrl,
			webbookPath
		};
	} else if (inputFormat === "pub-manifest") viewerInput = {
		type: "webpub",
		manifestPath: sourcePath,
		needToGenerateManifest: false
	};
	else if (inputFormat === "epub-opf") viewerInput = {
		type: "epub-opf",
		epubOpfPath: sourcePath
	};
	else if (inputFormat === "epub") viewerInput = {
		type: "epub",
		epubPath: sourcePath,
		epubTmpOutputDir: upath.join(sourcePath, `../${temporaryFilePrefix}${upath.basename(sourcePath)}`)
	};
	else return inputFormat;
	return {
		serverRootDir,
		workspaceDir,
		themesDir,
		entries,
		input: {
			format: inputFormat,
			entry: sourcePath
		},
		viewerInput,
		exportAliases,
		title: title || fallbackTitle,
		author
	};
}
function resolveComposedProjectConfig({ config, context, entryContextDir, outputs, temporaryFilePrefix, themeIndexes, cover }) {
	Logger.debug("entering composed project config mode");
	const workspaceDir = upath.resolve(context, config.workspaceDir ?? ".vivliostyle");
	const themesDir = upath.resolve(workspaceDir, "themes");
	const pkgJsonPath = upath.resolve(context, "package.json");
	const pkgJson = fs.existsSync(pkgJsonPath) ? readPackageJson(pkgJsonPath) : void 0;
	if (pkgJson) Logger.debug("located package.json path", pkgJsonPath);
	const exportAliases = [];
	const rootThemes = config.theme?.map((theme) => parseTheme({
		theme,
		context,
		workspaceDir,
		themesDir
	})) ?? [];
	rootThemes.forEach((t) => {
		themeIndexes.add(t);
	});
	const tocConfig = {
		tocTitle: config.toc?.title ?? config?.tocTitle ?? "Table of Contents",
		target: upath.resolve(workspaceDir, config.toc?.htmlPath ?? "index.html"),
		sectionDepth: config.toc?.sectionDepth ?? 0,
		transform: {
			transformDocumentList: config.toc?.transformDocumentList,
			transformSectionList: config.toc?.transformSectionList
		}
	};
	const coverHtml = config.cover && ("htmlPath" in config.cover && !config.cover.htmlPath ? void 0 : upath.resolve(workspaceDir, config.cover?.htmlPath || "cover.html"));
	const ensureCoverImage = (src) => {
		const absPath = src && upath.resolve(entryContextDir, src);
		if (absPath) statFileSync(absPath, { errorMessage: "Specified cover image does not exist" });
		return absPath;
	};
	const pkgAuthor = pkgJson?.author;
	const projectTitle = config?.title ?? pkgJson?.name;
	const projectAuthor = config?.author ?? (typeof pkgAuthor === "string" ? pkgAuthor : void 0);
	const rootDocumentProcessor = {
		processorFactory: config.documentProcessor ?? VFM,
		metadataReader: config.documentMetadataReader ?? readMetadata
	};
	const isContentsEntry = (entry) => entry.rel === "contents";
	const isCoverEntry = (entry) => entry.rel === "cover";
	const isArticleEntry = (entry) => !isContentsEntry(entry) && !isCoverEntry(entry);
	function parseEntry(entry) {
		const getInputInfo = (entryPath) => {
			if (/^https?:/v.test(entryPath)) return {
				type: "uri",
				href: entryPath,
				rootDir: upath.join(workspaceDir, new URL(entryPath).host)
			};
			else if (entryPath.startsWith("/")) return {
				type: "uri",
				href: entryPath,
				rootDir: upath.join(workspaceDir, "localhost")
			};
			const pathname = upath.resolve(entryContextDir, entryPath);
			statFileSync(pathname);
			const rawContentType = lookup(pathname);
			const documentProcessor = {
				processorFactory: "documentProcessor" in entry && entry.documentProcessor || rootDocumentProcessor.processorFactory,
				metadataReader: "documentMetadataReader" in entry && entry.documentMetadataReader || rootDocumentProcessor.metadataReader
			};
			const contentType = (documentProcessor.processorFactory !== VFM || documentProcessor.metadataReader !== readMetadata) && rawContentType !== "text/markdown" ? "text/x-vivliostyle-custom" : rawContentType;
			if (!isManuscriptMediaType(contentType) || contentType === "text/plain") throw new Error(`Invalid manuscript type ${rawContentType} detected: ${entryPath}`);
			const useDocumentProcessor = contentType === "text/markdown" || contentType === "text/x-vivliostyle-custom";
			return {
				type: "file",
				pathname,
				contentType,
				metadata: parseFileMetadata({
					contentType,
					sourcePath: pathname,
					workspaceDir,
					themesDir,
					documentMetadataReader: useDocumentProcessor ? documentProcessor.metadataReader : void 0
				}),
				...useDocumentProcessor && { documentProcessor }
			};
		};
		const getTargetPath = (source) => {
			switch (source.type) {
				case "file": return upath.resolve(workspaceDir, toHtmlExtension(upath.relative(entryContextDir, source.pathname)));
				case "uri": {
					let pathname = new URL(source.href, "a://dummy").pathname;
					if (!/\.\w+$/v.test(pathname)) pathname = `${pathname.replace(/\/$/v, "")}/index.html`;
					return upath.join(source.rootDir, pathname);
				}
				default: return source;
			}
		};
		if ((isContentsEntry(entry) || isCoverEntry(entry)) && entry.path) {
			const source = upath.resolve(entryContextDir, entry.path);
			try {
				statFileSync(source);
			} catch (error) {
				Logger.logWarn(`The "path" option is set but the file does not exist: ${source}\nMaybe you want to set the "output" field instead.`);
				entry.output = entry.path;
				entry.path = void 0;
			}
		}
		if (isContentsEntry(entry)) {
			const inputInfo = entry.path ? getInputInfo(entry.path) : void 0;
			const { metadata, ...template } = inputInfo ?? {};
			let target = entry.output ? upath.resolve(workspaceDir, entry.output) : inputInfo && getTargetPath(inputInfo);
			const themes = entry.theme ? [entry.theme].flat().map((theme) => parseTheme({
				theme,
				context,
				workspaceDir,
				themesDir
			})) : metadata?.themes ?? [...rootThemes];
			themes.forEach((t) => {
				themeIndexes.add(t);
			});
			target ??= tocConfig.target;
			if (inputInfo?.type === "file" && pathEquals(inputInfo.pathname, target)) {
				const tmpPath = upath.resolve(upath.dirname(target), `${temporaryFilePrefix}${upath.basename(target)}`);
				exportAliases.push({
					source: tmpPath,
					target
				});
				touchTmpFile(tmpPath);
				target = tmpPath;
			}
			return {
				rel: "contents",
				...tocConfig,
				target,
				title: entry.title ?? metadata?.title ?? projectTitle,
				themes,
				pageBreakBefore: entry.pageBreakBefore,
				pageCounterReset: entry.pageCounterReset,
				..."type" in template && { template }
			};
		}
		if (isCoverEntry(entry)) {
			const inputInfo = entry.path ? getInputInfo(entry.path) : void 0;
			const { metadata, ...template } = inputInfo ?? {};
			let target = entry.output ? upath.resolve(workspaceDir, entry.output) : inputInfo && getTargetPath(inputInfo);
			const themes = entry.theme ? [entry.theme].flat().map((theme) => parseTheme({
				theme,
				context,
				workspaceDir,
				themesDir
			})) : metadata?.themes ?? [];
			themes.forEach((t) => {
				themeIndexes.add(t);
			});
			const coverImageSrc = ensureCoverImage(entry.imageSrc || cover?.src);
			if (!coverImageSrc) throw new Error(`A CoverEntryConfig is set in the entry list but a location of cover file is not set. Please set 'cover' property in your config file.`);
			target ??= upath.resolve(workspaceDir, entry.path || coverHtml || "cover.html");
			if (inputInfo?.type === "file" && pathEquals(inputInfo.pathname, target)) {
				const tmpPath = upath.resolve(upath.dirname(target), `${temporaryFilePrefix}${upath.basename(target)}`);
				exportAliases.push({
					source: tmpPath,
					target
				});
				touchTmpFile(tmpPath);
				target = tmpPath;
			}
			return {
				rel: "cover",
				target,
				title: entry.title ?? metadata?.title ?? projectTitle,
				themes,
				coverImageSrc,
				coverImageAlt: entry.imageAlt || cover?.name || "Cover image",
				pageBreakBefore: entry.pageBreakBefore,
				..."type" in template && { template }
			};
		}
		if (isArticleEntry(entry)) {
			const inputInfo = getInputInfo(entry.path);
			const { metadata, ...source } = inputInfo;
			const target = entry.output ? upath.resolve(workspaceDir, entry.output) : getTargetPath(inputInfo);
			const themes = entry.theme ? [entry.theme].flat().map((theme) => parseTheme({
				theme,
				context,
				workspaceDir,
				themesDir
			})) : metadata?.themes ?? [...rootThemes];
			themes.forEach((t) => {
				themeIndexes.add(t);
			});
			return {
				contentType: inputInfo.type === "file" ? inputInfo.contentType : "text/html",
				source,
				target,
				title: entry.title ?? metadata?.title ?? projectTitle,
				themes,
				...entry.rel && { rel: entry.rel }
			};
		}
		/* v8 ignore next */
		return entry;
	}
	const entries = config.entry.map(parseEntry);
	let fallbackProjectTitle;
	if (!projectTitle) if (entries.length === 1 && entries[0].title) fallbackProjectTitle = entries[0].title;
	else fallbackProjectTitle = upath.basename(outputs[0].path);
	if (!!config?.toc && entries.every(({ rel }) => rel !== "contents")) entries.unshift({
		rel: "contents",
		...tocConfig,
		themes: [...rootThemes]
	});
	if (cover && coverHtml && entries.every(({ rel }) => rel !== "cover")) {
		const coverImageSrc = ensureCoverImage(cover.src);
		/* v8 ignore next 5 */
		if (!coverImageSrc) throw new Error(`A cover is set but a location of cover file is not set. Please set 'cover' property in your config file.`);
		entries.unshift({
			rel: "cover",
			target: coverHtml,
			title: projectTitle,
			themes: [],
			coverImageSrc,
			coverImageAlt: cover.name
		});
	}
	return {
		serverRootDir: context,
		workspaceDir,
		themesDir,
		entries,
		input: {
			format: "pub-manifest",
			entry: upath.join(workspaceDir, MANIFEST_FILENAME)
		},
		viewerInput: {
			type: "webpub",
			manifestPath: upath.join(workspaceDir, MANIFEST_FILENAME),
			needToGenerateManifest: true
		},
		exportAliases,
		title: projectTitle || fallbackProjectTitle,
		author: projectAuthor
	};
}
//#endregion
//#region src/browser.ts
const browserEnumMap = {
	chrome: "chrome",
	chromium: "chromium",
	firefox: "firefox"
};
function getAbortReason(signal) {
	try {
		signal.throwIfAborted();
	} catch (err) {
		return err instanceof Error ? err : new Error(String(err), { cause: err });
	}
	return toError(signal.reason);
}
async function runBrowserOperationWithAbort({ signal, closeBrowser, operation }) {
	if (!signal) return operation();
	signal.throwIfAborted();
	let abortClosePromise;
	let removeAbortListener;
	const abortPromise = new Promise((_, reject) => {
		const handleAbort = () => {
			abortClosePromise = closeBrowser();
			abortClosePromise.then(() => reject(getAbortReason(signal)), () => reject(getAbortReason(signal)));
		};
		signal.addEventListener("abort", handleAbort, { once: true });
		removeAbortListener = () => {
			signal.removeEventListener("abort", handleAbort);
		};
	});
	const operationPromise = operation().catch((err) => {
		if (signal.aborted) throw getAbortReason(signal);
		throw err;
	});
	try {
		const result = await Promise.race([operationPromise, abortPromise]);
		signal.throwIfAborted();
		return result;
	} catch (err) {
		if (signal.aborted) {
			try {
				await (abortClosePromise ?? closeBrowser());
			} catch (closeError) {
				Logger.debug("Failed to close browser after abort", closeError);
			}
			throw getAbortReason(signal);
		}
		throw err;
	} finally {
		removeAbortListener?.();
	}
}
async function launchBrowser({ browserType, proxy, executablePath, headless, noSandbox, disableDevShmUsage, ignoreHttpsErrors, protocolTimeout }) {
	const puppeteer = await importNodeModule("puppeteer-core");
	const args = [];
	if (browserType === "chrome" || browserType === "chromium") {
		args.push("--disable-field-trial-config", "--disable-back-forward-cache", "--disable-component-update", "--no-default-browser-check", "--disable-features=AcceptCHFrame,AvoidUnnecessaryBeforeUnloadCheckSync,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,Translate,AutoDeElevate,RenderDocument", "--enable-features=CDPScreenshotNewSurface", "--no-service-autorun", "--unsafely-disable-devtools-self-xss-warnings", "--edge-skip-compat-layer-relaunch");
		if (process.platform === "darwin") args.push("--enable-unsafe-swiftshader");
		if (noSandbox) args.push("--no-sandbox");
		if (headless) args.push("--hide-scrollbars", "--mute-audio", "--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4");
		if (proxy?.server) {
			const proxyURL = new URL(proxy.server);
			if (proxyURL.protocol === "socks5:") args.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`);
			args.push(`--proxy-server=${proxy.server}`);
			const proxyBypassRules = [];
			if (proxy.bypass) proxyBypassRules.push(...proxy.bypass.split(",").map((t) => t.trim()).map((t) => t.startsWith(".") ? "*" + t : t));
			proxyBypassRules.push("<-loopback>");
			args.push(`--proxy-bypass-list=${proxyBypassRules.join(";")}`);
		}
		args.push("--disable-web-security");
		if (disableDevShmUsage) args.push("--disable-dev-shm-usage");
		if (headless) args.push("--force-device-scale-factor=1");
		if (isRunningOnWSL()) args.push("--disable-gpu");
		args.push("--lang=en");
		if (!headless && process.platform === "darwin") args.push("-AppleLanguages", "(en)");
		args.push("--no-startup-window");
	}
	const launchOptions = {
		executablePath,
		args,
		browser: browserType === "chromium" ? "chrome" : browserType,
		headless,
		acceptInsecureCerts: ignoreHttpsErrors,
		waitForInitialPage: false,
		protocolTimeout
	};
	Logger.debug("launchOptions %O", launchOptions);
	const env = {
		...process.env,
		LANG: "en.UTF-8"
	};
	if (process.platform === "linux" && !isWritableDir(env.HOME)) [env.HOME] = await useTmpDirectory();
	const browserLaunch = puppeteer.launch({
		...launchOptions,
		env,
		handleSIGINT: false,
		handleSIGTERM: false,
		handleSIGHUP: false
	});
	let closeBrowserPromise;
	const closeBrowser = () => closeBrowserPromise ??= browserLaunch.then((browser) => browser.close());
	registerCleanupHandler("Closing browser", closeBrowser);
	const browser = await browserLaunch;
	const [browserContext] = browser.browserContexts();
	return {
		browser,
		browserContext,
		closeBrowser
	};
}
function isWritableDir(dir) {
	if (!dir) return false;
	try {
		if (!fs.statSync(dir).isDirectory()) return false;
		fs.accessSync(dir, fs.constants.W_OK | fs.constants.X_OK);
		return true;
	} catch {
		return false;
	}
}
function getPuppeteerCacheDir() {
	if (isInContainer()) return "/opt/puppeteer";
	return upath.join(getCacheDir(), "browsers");
}
async function resolveBuildId({ type, tag, browsers }) {
	const platform = detectBrowserPlatform();
	if (!platform) throw new Error("The current platform is not supported.");
	const cacheFilename = upath.join(getPuppeteerCacheDir(), "build-ids.json");
	const readCache = () => {
		try {
			return JSON.parse(fs.readFileSync(cacheFilename, "utf-8"));
		} catch {
			return { buildIds: {} };
		}
	};
	let buildId;
	try {
		buildId = await browsers.resolveBuildId(browserEnumMap[type], platform, tag);
	} catch (error) {
		const thrownError = toError(error);
		const cached = readCache().buildIds[type]?.[tag];
		if (!cached) throw error;
		Logger.logWarn(`Failed to resolve ${type}@${tag} from the browser registry; using the cached build ID ${cached}.\n${thrownError}`);
		return cached;
	}
	const cacheData = readCache();
	(cacheData.buildIds[type] ??= {})[tag] = buildId;
	try {
		fs.mkdirSync(upath.dirname(cacheFilename), { recursive: true });
		fs.writeFileSync(cacheFilename, JSON.stringify(cacheData));
	} catch (error) {
		const thrownError = toError(error);
		Logger.logWarn(`Failed to update the build-id cache (${cacheFilename}): ${thrownError}`);
	}
	return buildId;
}
async function cleanupOutdatedBrowsers() {
	for (const browser of Object.values(browserEnumMap)) {
		const browsersDir = upath.join(getPuppeteerCacheDir(), browser);
		if (!fs.existsSync(browsersDir)) continue;
		const entries = fs.readdirSync(browsersDir);
		for (const entry of entries) {
			const entryPath = upath.join(browsersDir, entry);
			const stat = fs.statSync(entryPath);
			if (!stat.isDirectory() || Date.now() - stat.mtimeMs > 10080 * 60 * 1e3) {
				Logger.debug(`Removing outdated browser at ${entryPath}`);
				await fs.promises.rm(entryPath, {
					recursive: true,
					force: true
				});
			}
		}
	}
}
async function getExecutableBrowserPath({ type, tag }) {
	const browsers = await importNodeModule("@puppeteer/browsers");
	const buildId = await resolveBuildId({
		type,
		tag,
		browsers
	});
	return browsers.computeExecutablePath({
		cacheDir: getPuppeteerCacheDir(),
		browser: browserEnumMap[type],
		buildId
	});
}
function checkBrowserAvailability(path) {
	return fs.existsSync(path);
}
async function downloadBrowser({ type, tag }) {
	const browsers = await importNodeModule("@puppeteer/browsers");
	const buildId = await resolveBuildId({
		type,
		tag,
		browsers
	});
	let installedBrowser;
	if (isInContainer()) {
		const defaultBrowserVersion = getDefaultBrowserTag("chrome");
		Logger.logWarn(`The container you are using already includes a browser (chrome@${defaultBrowserVersion}); however, the specified browser ${type}@${tag} was not found. Downloading the browser inside the container may take a long time. Consider using a container image that includes the required browser version.`);
	}
	try {
		var _usingCtx$1 = _usingCtx();
		_usingCtx$1.u(Logger.suspendLogging("Rendering browser is not installed yet. Downloading now."));
		installedBrowser = await browsers.install({
			cacheDir: getPuppeteerCacheDir(),
			browser: browserEnumMap[type],
			buildId,
			downloadProgressCallback: "default"
		});
	} catch (_) {
		_usingCtx$1.e = _;
	} finally {
		_usingCtx$1.d();
	}
	return installedBrowser.executablePath;
}
async function launchPreview({ mode, url, signal, onBrowserOpen, onPageOpen, config: { browser: browserConfig, proxy, sandbox, ignoreHttpsErrors, timeout } }) {
	let executableBrowser = browserConfig.executablePath;
	Logger.debug(`Specified browser path: ${executableBrowser}`);
	if (executableBrowser) {
		if (!checkBrowserAvailability(executableBrowser)) throw new Error(`Cannot find the browser. Please check the executable browser path: ${executableBrowser}`);
	} else if (detectBrowserPlatform() === "linux_arm" && (browserConfig.type === "chrome" || browserConfig.type === "chromium")) {
		Logger.logInfo("The official Chrome/Chromium binaries are not available for ARM64 Linux. Using the system-installed Chromium browser instead.");
		executableBrowser = "/usr/bin/chromium";
	} else {
		executableBrowser = await getExecutableBrowserPath(browserConfig);
		Logger.debug(`Using default browser: ${executableBrowser}`);
		if (!checkBrowserAvailability(executableBrowser)) {
			await cleanupOutdatedBrowsers();
			await downloadBrowser(browserConfig);
		}
	}
	signal?.throwIfAborted();
	const { browser, browserContext, closeBrowser } = await launchBrowser({
		browserType: browserConfig.type,
		proxy,
		executablePath: executableBrowser,
		headless: mode === "build",
		noSandbox: !sandbox,
		disableDevShmUsage: isInContainer(),
		ignoreHttpsErrors,
		protocolTimeout: timeout
	});
	await onBrowserOpen?.(browser);
	signal?.throwIfAborted();
	return {
		browser,
		page: await runBrowserOperationWithAbort({
			signal,
			closeBrowser,
			operation: async () => {
				const previewPage = (await browserContext.pages())[0] ?? await browserContext.newPage();
				await previewPage.setViewport(mode === "build" ? {
					width: 800,
					height: 600
				} : null);
				await onPageOpen?.(previewPage);
				previewPage.on("dialog", () => {});
				if (proxy?.username && proxy?.password) await previewPage.authenticate({
					username: proxy.username,
					password: proxy.password
				});
				await previewPage.goto(url, { signal });
				return previewPage;
			}
		}),
		closeBrowser
	};
}
//#endregion
//#region src/vite/plugin-util.ts
const headStartTagRe = /<head[^>]*>/iv;
const prependToHead = (html, content) => html.replace(headStartTagRe, (match) => `${match}\n${content}`);
async function reloadConfig(prevConfig, inlineConfig, resolvedViteConfig) {
	let config = await loadVivliostyleConfig(inlineConfig) ?? setupConfigFromFlags(inlineConfig);
	config = mergeInlineConfig(config, inlineConfig);
	config = mergeConfig$1(config, {
		temporaryFilePrefix: prevConfig.temporaryFilePrefix,
		server: resolvedViteConfig?.server
	});
	return resolveTaskConfig(config.tasks[0], config.inlineOptions);
}
//#endregion
//#region src/vite/vite-plugin-browser.ts
function vsBrowserPlugin({ config: _config, inlineConfig }) {
	let config = _config;
	let server;
	let closeBrowser;
	function handlePageClose() {
		(async () => {
			await server?.close();
			await runCleanupHandlers();
		})();
	}
	async function openPreviewPage() {
		const locale = getOsLocale();
		const { page, closeBrowser: closeLaunchedBrowser } = await launchPreview({
			mode: "preview",
			url: await getViewerFullUrl(config),
			signal: inlineConfig.signal,
			config,
			/* v8 ignore next 4 */
			onPageOpen: (openedPage) => {
				openedPage.on("close", handlePageClose);
			}
		});
		closeBrowser = () => {
			page.off("close", handlePageClose);
			return closeLaunchedBrowser();
		};
		await runBrowserOperationWithAbort({
			signal: inlineConfig.signal,
			closeBrowser,
			operation: async () => {
				if (!import.meta.env?.VITEST)
 /* v8 ignore next 4 */
				await page.evaluate((lng) => {
					window.localStorage.setItem("i18nextLng", lng);
				}, locale);
				await page.bringToFront();
				if (!import.meta.env?.VITEST)
 /* v8 ignore next 6 */
				await page.evaluate(() => {
					document.querySelector("#vivliostyle-input-url")?.focus();
				});
			}
		});
	}
	return {
		name: "vivliostyle:browser",
		apply: () => Boolean(inlineConfig.openViewer),
		configureServer(viteServer) {
			server = viteServer;
			const originalListen = viteServer.listen.bind(viteServer);
			viteServer.listen = async (...args) => {
				const startedServer = await originalListen(...args);
				config = await reloadConfig(config, inlineConfig, startedServer.config);
				await openPreviewPage();
				return startedServer;
			};
		},
		async closeBundle() {
			await closeBrowser?.();
		}
	};
}
//#endregion
//#region src/processor/html.tsx
const createVirtualConsole = (onError) => {
	const virtualConsole = new jsdom.VirtualConsole();
	/* v8 ignore start */
	virtualConsole.on("error", (message) => {
		Logger.debug("[JSDOM Console] error:", message);
	});
	virtualConsole.on("warn", (message) => {
		Logger.debug("[JSDOM Console] warn:", message);
	});
	virtualConsole.on("log", (message) => {
		Logger.debug("[JSDOM Console] log:", message);
	});
	virtualConsole.on("info", (message) => {
		Logger.debug("[JSDOM Console] info:", message);
	});
	virtualConsole.on("dir", (message) => {
		Logger.debug("[JSDOM Console] dir:", message);
	});
	virtualConsole.on("jsdomError", (error) => {
		if (error.message === "Could not parse CSS stylesheet") return;
		onError(new DetailError("Error occurred when loading content", error.stack ?? error.message));
	});
	/* v8 ignore stop */
	return virtualConsole;
};
const htmlPurify = DOMPurify(new JSDOM("").window);
var ResourceLoader$1 = class ResourceLoader$1 extends ResourceLoader {
	constructor(..._args) {
		super(..._args);
		this.fetcherMap = /* @__PURE__ */ new Map();
	}
	static {
		this.dataUrlOrigin = "http://localhost/";
	}
	fetch(url, options) {
		Logger.debug(`[JSDOM] Fetching resource: ${url}`);
		const fetcher = super.fetch(url, options);
		if (fetcher) this.fetcherMap.set(url, fetcher);
		return fetcher;
	}
	static async saveFetchedResources({ fetcherMap, rootUrl, outputDir, onError }) {
		const rootHref = rootUrl.startsWith("data:") ? ResourceLoader$1.dataUrlOrigin : /^https?:/iv.test(rootUrl) ? new URL("/", rootUrl).href : new URL(".", rootUrl).href;
		const normalizeToLocalPath = (urlString, mimeType) => {
			const url = new URL(urlString);
			url.hash = "";
			if (mimeType === "text/html" && !/\.html?$/v.test(url.pathname)) url.pathname = `${url.pathname.replace(/\/$/v, "")}/index.html`;
			const relTarget = upath.relative(rootHref, url.href);
			return decodeURI(relTarget);
		};
		const fetchedResources = [];
		await Promise.allSettled([...fetcherMap.entries()].flatMap(async ([url, fetcher]) => {
			if (!url.startsWith(rootHref)) return [];
			return fetcher.then((buffer) => {
				let encodingFormat;
				try {
					const contentType = fetcher.response?.headers["content-type"];
					if (contentType) encodingFormat = new MIMEType(contentType).essence;
				} catch (e) {}
				const relTarget = normalizeToLocalPath(url, encodingFormat);
				const target = upath.join(outputDir, relTarget);
				fetchedResources.push({
					url: relTarget,
					encodingFormat
				});
				writeFileIfChanged(target, buffer);
			}).catch(onError);
		}));
		return fetchedResources;
	}
};
async function getJsdomFromUrlOrFile({ src, resourceLoader, virtualConsole = createVirtualConsole((error) => {
	throw error;
}) }) {
	const url = isValidUri(src) ? new URL(src) : pathToFileURL(src);
	let dom;
	if (url.protocol === "http:" || url.protocol === "https:") dom = await JSDOM.fromURL(src, {
		virtualConsole,
		resources: resourceLoader
	});
	else if (url.protocol === "file:") {
		if (resourceLoader) {
			const file = resourceLoader._readFile(fileURLToPath(url));
			resourceLoader.fetcherMap.set(url.href, file);
		}
		dom = await JSDOM.fromFile(fileURLToPath(url), {
			virtualConsole,
			resources: resourceLoader,
			contentType: src.endsWith(".xhtml") || src.endsWith(".xml") ? "application/xhtml+xml; charset=UTF-8" : "text/html; charset=UTF-8"
		});
	} else if (url.protocol === "data:") {
		const [head, body] = url.href.split(",", 2);
		const data = decodeURIComponent(body);
		const buffer = Buffer.from(data, /;base64$/iv.test(head) ? "base64" : "utf8");
		const dummyUrl = `${ResourceLoader$1.dataUrlOrigin}index.html`;
		if (resourceLoader) {
			let timeoutId;
			const promise = new Promise((resolve) => {
				timeoutId = setTimeout(resolve, 0, buffer);
			});
			promise.abort = () => {
				if (timeoutId !== void 0) clearTimeout(timeoutId);
			};
			resourceLoader.fetcherMap.set(dummyUrl, promise);
		}
		dom = new JSDOM(buffer.toString(), {
			virtualConsole,
			resources: resourceLoader,
			contentType: "text/html; charset=UTF-8",
			url: dummyUrl
		});
	} else throw new Error(`Unsupported protocol: ${url.protocol}`);
	return dom;
}
function getJsdomFromString({ html, virtualConsole = createVirtualConsole((error) => {
	throw error;
}) }) {
	return new JSDOM(html, { virtualConsole });
}
async function getStructuredSectionFromHtml(htmlPath, href) {
	const { document } = (await getJsdomFromUrlOrFile({ src: htmlPath })).window;
	const allHeadings = [...document.querySelectorAll("h1, h2, h3, h4, h5, h6")].filter((el) => {
		return !el.matches("blockquote *");
	}).toSorted((a, b) => {
		const position = a.compareDocumentPosition(b);
		return position & 2 ? 1 : position & 4 ? -1 : 0;
	});
	function traverse(headers) {
		if (headers.length === 0) return [];
		const [head, ...tail] = headers;
		const section = head.parentElement;
		/* v8 ignore next 3 */
		if (!section) throw new Error("Heading element is not contained in any parent element");
		const id = head.id || section.id;
		const level = Number(head.tagName.slice(1));
		let i = tail.findIndex((s) => Number(s.tagName.slice(1)) <= level);
		i = i === -1 ? tail.length : i;
		return [{
			headingHtml: htmlPurify.sanitize(head.innerHTML),
			headingText: head.textContent?.trim().replaceAll(/\s+/gv, " ") || "",
			level,
			...href && id && { href: `${href}#${encodeURIComponent(id)}` },
			...id && { id },
			children: traverse(tail.slice(0, i))
		}, ...traverse(tail.slice(i))];
	}
	return traverse(allHeadings);
}
const getTocHtmlStyle = ({ pageBreakBefore, pageCounterReset }) => {
	if (!pageBreakBefore && typeof pageCounterReset !== "number") return null;
	return `
${pageBreakBefore ? `:root {
  break-before: ${pageBreakBefore};
}` : ""}
${typeof pageCounterReset === "number" ? `@page :nth(1) {
  --vs-document-first-page-counter-reset: page ${Math.floor(pageCounterReset - 1)};
  counter-reset: var(--vs-document-first-page-counter-reset);
}` : ""}
`;
};
const defaultTocTransform = {
	transformDocumentList: (nodeList) => (propsList) => {
		return /* @__PURE__ */ jsx("ol", { children: nodeList.map((a, i) => [a, propsList[i]]).flatMap(([{ href, title, sections }, { children, ...otherProps }]) => {
			if (sections?.length === 1 && sections[0].level === 1) return [children].flat().flatMap((e) => {
				if (e.type === "element" && e.tagName === "ol") return e.children;
				return e;
			});
			return /* @__PURE__ */ jsxs("li", {
				...otherProps,
				children: [/* @__PURE__ */ jsx("a", {
					href,
					children: title
				}), children]
			});
		}) });
	},
	transformSectionList: (nodeList) => (propsList) => {
		return /* @__PURE__ */ jsx("ol", { children: nodeList.map((a, i) => [a, propsList[i]]).map(([{ headingHtml, href, level }, { children, ...otherProps }]) => {
			const headingContent = {
				type: "raw",
				value: headingHtml
			};
			return /* @__PURE__ */ jsxs("li", {
				...otherProps,
				"data-section-level": level,
				children: [href ? /* @__PURE__ */ jsx("a", {
					href,
					children: headingContent
				}) : /* @__PURE__ */ jsx("span", { children: headingContent }), children]
			});
		}) });
	}
};
function generateDefaultTocHtml({ language, title }) {
	return toHtml(/* @__PURE__ */ jsxs("html", {
		lang: language,
		children: [/* @__PURE__ */ jsxs("head", { children: [
			/* @__PURE__ */ jsx("meta", { charset: "utf-8" }),
			/* @__PURE__ */ jsx("title", { children: title || "" }),
			/* @__PURE__ */ jsx("style", { "data-vv-style": true })
		] }), /* @__PURE__ */ jsxs("body", { children: [/* @__PURE__ */ jsx("h1", { children: title || "" }), /* @__PURE__ */ jsx("nav", {
			id: "toc",
			role: "doc-toc"
		})] })]
	}));
}
async function generateTocListSection({ entries, distDir, sectionDepth, transform = {} }) {
	const { transformDocumentList = defaultTocTransform.transformDocumentList, transformSectionList = defaultTocTransform.transformSectionList } = transform;
	const structure = await Promise.all(entries.map(async (entry) => {
		const href = encodeURI(upath.relative(distDir, entry.target));
		const sections = sectionDepth >= 1 ? await getStructuredSectionFromHtml(entry.target, href) : [];
		return {
			title: entry.title || upath.basename(entry.target, ".html"),
			href: encodeURI(upath.relative(distDir, entry.target)),
			sections,
			children: []
		};
	}));
	return toHtml(transformDocumentList(structure)(structure.map((doc) => {
		function renderSectionList(sections) {
			const nodeList = sections.flatMap((section) => {
				if (section.level > sectionDepth) return [];
				return section;
			});
			if (nodeList.length === 0) return [];
			return transformSectionList(nodeList)(nodeList.map((node) => ({ children: [renderSectionList(node.children || [])].flat() })));
		}
		return { children: [renderSectionList(doc.sections || [])].flat() };
	})), { allowDangerousHtml: true });
}
async function processTocHtml(dom, { manifestPath, tocTitle, styleOptions = {}, entries, distDir, sectionDepth, transform }) {
	const { document } = dom.window;
	if (!document.querySelector("link[rel=\"publication\"][type=\"application/ld+json\"]")) {
		const l = document.createElement("link");
		l.setAttribute("rel", "publication");
		l.setAttribute("type", "application/ld+json");
		l.setAttribute("href", encodeURI(upath.relative(distDir, manifestPath)));
		document.head.append(l);
	}
	const style = document.querySelector("style[data-vv-style]");
	if (style) {
		const textContent = getTocHtmlStyle(styleOptions);
		if (textContent) style.textContent = textContent;
		else style.remove();
	}
	const nav = document.querySelector("nav, [role=\"doc-toc\"]");
	if (nav && !nav.hasChildNodes()) {
		const h2 = document.createElement("h2");
		h2.textContent = tocTitle;
		nav.append(h2);
		nav.innerHTML += await generateTocListSection({
			entries,
			distDir,
			sectionDepth,
			transform
		});
	}
	return dom;
}
const getCoverHtmlStyle = ({ pageBreakBefore }) => `
${pageBreakBefore ? `:root {
  break-before: ${pageBreakBefore};
}` : ""}
body {
  margin: 0;
}
[role="doc-cover"] {
  display: block;
  width: 100vw;
  height: 100vh;
  object-fit: contain;
}
@page {
  margin: 0;
}
`;
function generateDefaultCoverHtml({ language, title }) {
	return toHtml(/* @__PURE__ */ jsxs("html", {
		lang: language,
		children: [/* @__PURE__ */ jsxs("head", { children: [
			/* @__PURE__ */ jsx("meta", { charset: "utf-8" }),
			/* @__PURE__ */ jsx("title", { children: title || "" }),
			/* @__PURE__ */ jsx("style", { "data-vv-style": true })
		] }), /* @__PURE__ */ jsx("body", { children: /* @__PURE__ */ jsx("section", {
			role: "region",
			"aria-label": "Cover",
			children: /* @__PURE__ */ jsx("img", { role: "doc-cover" })
		}) })]
	}));
}
async function processCoverHtml(dom, { imageSrc, imageAlt, styleOptions = {} }) {
	const { document } = dom.window;
	const style = document.querySelector("style[data-vv-style]");
	if (style) {
		const textContent = getCoverHtmlStyle(styleOptions);
		if (textContent) style.textContent = textContent;
		else style.remove();
	}
	const cover = document.querySelector("img[role=\"doc-cover\"]");
	if (cover && !cover.hasAttribute("src")) cover.setAttribute("src", encodeURI(imageSrc));
	if (cover && !cover.hasAttribute("alt")) cover.setAttribute("alt", imageAlt);
	return dom;
}
async function processManuscriptHtml(dom, { title, style, contentType, language }) {
	const { document } = dom.window;
	if (title) {
		if (!document.querySelector("title")) {
			const t = document.createElement("title");
			document.head.append(t);
		}
		document.title = title;
	}
	for (const s of style ?? []) {
		const l = document.createElement("link");
		l.setAttribute("rel", "stylesheet");
		l.setAttribute("type", "text/css");
		l.setAttribute("href", encodeURI(s));
		document.head.append(l);
	}
	if (language) {
		if (contentType === "application/xhtml+xml") {
			if (!document.documentElement.getAttribute("xml:lang")) {
				document.documentElement.setAttribute("lang", language);
				document.documentElement.setAttribute("xml:lang", language);
			}
		} else if (!document.documentElement.getAttribute("lang")) document.documentElement.setAttribute("lang", language);
	}
	return dom;
}
async function fetchLinkedPublicationManifest({ dom, resourceLoader, baseUrl }) {
	const { document } = dom.window;
	const linkEl = document.querySelector("link[href][rel=\"publication\"]");
	if (!linkEl) return null;
	const hrefAttr = linkEl.getAttribute("href");
	/* v8 ignore next 3 */
	if (hrefAttr === null) throw new Error("Publication manifest link has no href attribute");
	const href = hrefAttr.trim();
	let manifest;
	let manifestUrl = baseUrl;
	if (href.startsWith("#")) {
		const scriptEl = document.getElementById(href.slice(1));
		if (scriptEl?.getAttribute("type") !== "application/ld+json") return null;
		Logger.debug(`Found embedded publication manifest: ${href}`);
		try {
			manifest = JSON.parse(scriptEl.innerHTML);
		} catch (error) {
			throw new DetailError("Failed to parse manifest data", typeof toError(error).stack);
		}
	} else {
		Logger.debug(`Found linked publication manifest: ${href}`);
		const url = new URL(href, baseUrl);
		manifestUrl = url.href;
		const buffer = await resourceLoader.fetch(url.href);
		if (!buffer) throw new Error(`Failed to fetch manifest JSON file: ${url.href}`);
		const manifestJson = buffer.toString();
		try {
			manifest = JSON.parse(manifestJson);
		} catch (error) {
			throw new DetailError("Failed to parse manifest data", typeof toError(error).stack);
		}
	}
	try {
		assertPubManifestSchema(manifest);
	} catch (error) {
		Logger.logWarn(`Publication manifest validation failed. Processing continues, but some problems may occur.\n${String(error)}`);
	}
	return {
		manifest: decodePublicationManifest(manifest),
		manifestUrl
	};
}
function parseTocDocument(dom) {
	const { document } = dom.window;
	const docTocEl = document.querySelectorAll("[role=\"doc-toc\"]");
	if (docTocEl.length === 0) return null;
	const tocRoot = docTocEl.item(0);
	const parseTocItem = (element) => {
		if (element.tagName !== "LI") return null;
		const label = element.children.item(0);
		const ol = element.children.item(1);
		if (!label || label.tagName !== "A" && label.tagName !== "SPAN") return null;
		if (!ol || ol.tagName !== "OL") return {
			element,
			label
		};
		const children = Array.from(ol.children).reduce((acc, val) => {
			if (!acc) return acc;
			const res = parseTocItem(val);
			if (!res) return null;
			acc.push(res);
			return acc;
		}, []);
		return children && {
			element,
			label,
			children
		};
	};
	let heading;
	for (const child of Array.from(tocRoot.children)) if (child.tagName === "OL") {
		const children = Array.from(child.children).reduce((acc, val) => {
			if (!acc) return acc;
			const res = parseTocItem(val);
			if (!res) return null;
			acc.push(res);
			return acc;
		}, []);
		return children && {
			element: tocRoot,
			heading,
			children
		};
	} else if ([
		"H1",
		"H2",
		"H3",
		"H4",
		"H5",
		"H6",
		"HGROUP"
	].includes(child.tagName)) heading = child;
	else return null;
	return null;
}
function parsePageListDocument(dom) {
	const { document } = dom.window;
	const docPageListEl = document.querySelectorAll("[role=\"doc-pagelist\"]");
	if (docPageListEl.length === 0) return null;
	const pageListRoot = docPageListEl.item(0);
	let heading;
	for (const child of Array.from(pageListRoot.children)) if (child.tagName === "OL") {
		const children = Array.from(child.children).reduce((acc, element) => {
			if (!acc) return acc;
			if (element.tagName !== "LI") return null;
			acc.push({ element });
			return acc;
		}, []);
		return children && {
			element: pageListRoot,
			heading,
			children
		};
	} else if ([
		"H1",
		"H2",
		"H3",
		"H4",
		"H5",
		"H6",
		"HGROUP"
	].includes(child.tagName)) heading = child;
	else return null;
	return null;
}
//#endregion
//#region src/output/epub.ts
const TOC_ID = "toc";
const LANDMARKS_ID = "landmarks";
const PAGELIST_ID = "page-list";
const COVER_IMAGE_MIMETYPES = /* @__PURE__ */ new Set([
	"image/gif",
	"image/jpeg",
	"image/png",
	"image/svg+xml",
	"image/webp"
]);
const changeExtname = (filepath, newExt) => {
	const ext = upath.extname(filepath);
	return `${filepath.slice(0, -ext.length)}${newExt}`;
};
const getRelativeHref = (target, baseUrl, rootUrl) => {
	const absBasePath = upath.join("/", baseUrl);
	const absRootPath = upath.join("/", rootUrl);
	const hrefUrl = new URL(encodeURI(target), pathToFileURL(absBasePath));
	if (hrefUrl.protocol !== "file:") return target;
	if (/\.html?$/v.test(hrefUrl.pathname)) hrefUrl.pathname = changeExtname(hrefUrl.pathname, ".xhtml");
	return `${upath.posix.relative(pathToFileURL(upath.dirname(absRootPath)).pathname, hrefUrl.pathname)}${hrefUrl.search}${hrefUrl.hash}`;
};
const normalizeLocalizableString = (value, availableLanguages) => {
	if (!value) return;
	const values = [value].flat().map((item) => typeof item === "string" ? { value: item } : item);
	const localizedValues = values.filter((v) => !!v.language);
	const preferredLang = lookup$1(localizedValues.map((v) => v.language), availableLanguages);
	if (preferredLang) return localizedValues[localizedValues.findIndex((v) => v.language === preferredLang)].value;
	return values.find((v) => !v.language)?.value;
};
const appendManifestProperty = (entry, newProperty) => {
	entry.properties = entry.properties ? Array.from(/* @__PURE__ */ new Set([...entry.properties.split(" "), newProperty])).join(" ") : newProperty;
};
async function exportEpub({ webpubDir, entryHtmlFile, manifest, relManifestPath, target, epubVersion }) {
	Logger.debug("Export EPUB", {
		webpubDir,
		entryHtmlFile,
		relManifestPath,
		target,
		epubVersion
	});
	const [tmpDir] = await useTmpDirectory();
	fs.mkdirSync(upath.join(tmpDir, "META-INF"), { recursive: true });
	await copy(webpubDir, upath.join(tmpDir, "EPUB"));
	const uid = `urn:uuid:${v4()}`;
	const entryHtmlRelPath = entryHtmlFile && upath.relative(webpubDir, upath.resolve(webpubDir, entryHtmlFile));
	const findPublicationLink = (relType, list, filter) => [list].flat().find((e) => typeof e === "object" && e.rel === relType && (!filter || filter(e)));
	const tocResource = findPublicationLink("contents", [...[manifest.readingOrder || []].flat(), ...[manifest.resources || []].flat()]);
	const pageListResource = findPublicationLink("pagelist", [...[manifest.readingOrder || []].flat(), ...[manifest.resources || []].flat()]);
	const pictureCoverResource = findPublicationLink("cover", manifest.resources, (e) => COVER_IMAGE_MIMETYPES.has(e.encodingFormat || lookup(e.url) || ""));
	const htmlCoverResource = findPublicationLink("cover", [...[manifest.readingOrder || []].flat(), ...[manifest.resources || []].flat()], (e) => /\.html?$/v.test(e.url));
	const manifestItem = [
		...[manifest.links || []].flat(),
		...[manifest.readingOrder || []].flat(),
		...[manifest.resources || []].flat()
	].reduce((acc, val) => {
		const { url, encodingFormat } = typeof val === "string" ? { url: val } : val;
		if (URL.canParse(url)) return acc;
		if (!fs.existsSync(upath.join(tmpDir, "EPUB", url))) return acc;
		acc[url] = {
			href: url,
			mediaType: encodingFormat || lookup(url) || "text/plain"
		};
		if (/\.html?$/v.test(url)) {
			acc[url].href = changeExtname(url, ".xhtml");
			acc[url].mediaType = "application/xhtml+xml";
		}
		if (url === pictureCoverResource?.url) acc[url].properties = "cover-image";
		return acc;
	}, {});
	const htmlFiles = Object.keys(manifestItem).filter((url) => /\.html?$/v.test(url));
	let tocHtml = htmlFiles.find((f) => f === tocResource?.url);
	const readingOrder = [manifest.readingOrder || entryHtmlRelPath].flat().flatMap((v) => v ? typeof v === "string" ? { url: v } : v : []);
	if (!tocHtml) {
		Logger.logWarn("No table of contents document was found. for EPUB output, we recommend to enable `toc` option in your Vivliostyle config file to generate a table of contents document.");
		tocHtml = htmlFiles.find((f) => f === entryHtmlRelPath) || readingOrder[0].url;
	}
	const spineItems = readingOrder.map(({ url }) => ({ href: changeExtname(url, ".xhtml") }));
	if (!(tocHtml in manifestItem)) manifestItem[tocHtml] = {
		href: changeExtname(tocHtml, ".xhtml"),
		mediaType: "application/xhtml+xml"
	};
	appendManifestProperty(manifestItem[tocHtml], "nav");
	const landmarks = [{
		type: "toc",
		href: `${manifestItem[tocHtml].href}#${TOC_ID}`,
		text: EPUB_LANDMARKS_TOC_ENTRY
	}];
	if (htmlCoverResource) landmarks.push({
		type: "cover",
		href: changeExtname(htmlCoverResource.url, ".xhtml"),
		text: EPUB_LANDMARKS_COVER_ENTRY
	});
	const contextDir = upath.join(tmpDir, "EPUB");
	const processHtml = async (htmlFile) => {
		let parseResult;
		try {
			parseResult = await transpileHtmlToXhtml({
				target: htmlFile,
				contextDir
			});
		} catch (error) {
			const thrownError = toError(error);
			throw new DetailError(`Failed to transpile document to XHTML: ${htmlFile}`, thrownError.stack ?? thrownError.message);
		}
		if (parseResult.hasMathmlContent) appendManifestProperty(manifestItem[htmlFile], "mathml");
		if (parseResult.hasRemoteResources) appendManifestProperty(manifestItem[htmlFile], "remote-resources");
		if (parseResult.hasScriptedContent) appendManifestProperty(manifestItem[htmlFile], "scripted");
		if (parseResult.hasSvgContent) appendManifestProperty(manifestItem[htmlFile], "svg");
		return parseResult;
	};
	const processResult = {};
	Logger.debug(`Transpiling ToC HTML to XHTML: ${tocHtml}`);
	processResult[tocHtml] = await processHtml(tocHtml);
	for (const htmlFile of htmlFiles.filter((f) => f !== tocHtml)) {
		Logger.debug(`Transpiling HTML to XHTML: ${htmlFile}`);
		processResult[htmlFile] = await processHtml(htmlFile);
	}
	const { document: entryDocument } = processResult[tocHtml].dom.window;
	const docLanguages = [manifest.inLanguage].flat().filter((v) => Boolean(v));
	if (docLanguages.length === 0) docLanguages.push(entryDocument.documentElement.lang || "en");
	const docTitle = normalizeLocalizableString(manifest.name, docLanguages) || entryDocument.title;
	if (!docTitle) throw new Error("EPUB must have a title of one or more characters");
	await processTocDocument({
		dom: processResult[tocHtml].dom,
		target: tocHtml,
		contextDir,
		readingOrder,
		docLanguages,
		landmarks
	});
	const pageListHtml = pageListResource?.url || entryHtmlRelPath;
	if (pageListHtml && pageListHtml in processResult) await processPagelistDocument({
		dom: processResult[pageListHtml].dom,
		target: pageListHtml,
		contextDir
	});
	if (relManifestPath) {
		await fs.promises.rm(upath.join(tmpDir, "EPUB", relManifestPath), {
			force: true,
			recursive: true
		});
		Reflect.deleteProperty(manifestItem, relManifestPath);
	}
	fs.writeFileSync(upath.join(tmpDir, "META-INF/container.xml"), EPUB_CONTAINER_XML, "utf8");
	Logger.debug(`Generating content.opf`);
	fs.writeFileSync(upath.join(tmpDir, "EPUB/content.opf"), buildEpubPackageDocument({
		epubVersion,
		uid,
		docTitle,
		docLanguages,
		manifest,
		spineItems,
		manifestItems: Object.values(manifestItem)
	}), "utf8");
	await compressEpub({
		target,
		sourceDir: tmpDir
	});
}
async function writeAsXhtml(dom, absPath) {
	const xhtml = `${XML_DECLARATION}\n${serializeToXml(dom.window.document)}`;
	await fs.promises.writeFile(changeExtname(absPath, ".xhtml"), xhtml, "utf8");
}
async function transpileHtmlToXhtml({ target, contextDir }) {
	const absPath = upath.join(contextDir, target);
	const dom = await getJsdomFromUrlOrFile({ src: absPath });
	const { document } = dom.window;
	document.documentElement.removeAttribute("xmlns");
	document.documentElement.setAttribute("xmlns:epub", EPUB_NS);
	document.querySelectorAll("a[href]").forEach((el) => {
		const hrefAttr = el.getAttribute("href");
		if (hrefAttr === null) return;
		const href = decodeURI(hrefAttr);
		el.setAttribute("href", getRelativeHref(href, target, target));
	});
	await writeAsXhtml(dom, absPath);
	await fs.promises.unlink(absPath);
	return {
		dom,
		hasMathmlContent: !!document.querySelector("math"),
		hasRemoteResources: !!document.querySelector("[src^=\"http://\"], [src^=\"https://\"]"),
		hasScriptedContent: !!document.querySelector("script, form"),
		hasSvgContent: !!document.querySelector("svg")
	};
}
function replaceWithNavElement(dom, el) {
	const nav = dom.window.document.createElement("nav");
	while (el.firstChild) nav.append(el.firstChild);
	for (let i = 0; i < el.attributes.length; i++) nav.attributes.setNamedItem(el.attributes[i].cloneNode());
	el.parentNode?.replaceChild(nav, el);
	return nav;
}
async function processTocDocument({ dom, target, contextDir, readingOrder, docLanguages, landmarks }) {
	const { document } = dom.window;
	let tocResourceTree = null;
	if (!document.querySelector("nav[epub:type]")) {
		tocResourceTree = parseTocDocument(dom);
		if (tocResourceTree) {
			const nav = replaceWithNavElement(dom, tocResourceTree.element);
			nav.setAttribute("id", TOC_ID);
			nav.setAttribute("epub:type", "toc");
		} else {
			Logger.debug(`Generating toc nav element: ${target}`);
			const nav = document.createElement("nav");
			nav.setAttribute("id", TOC_ID);
			nav.setAttribute("role", "doc-toc");
			nav.setAttribute("epub:type", "toc");
			nav.setAttribute("hidden", "");
			const h2 = document.createElement("h2");
			h2.textContent = TOC_TITLE;
			nav.append(h2);
			const ol = document.createElement("ol");
			tocResourceTree = {
				element: nav,
				children: []
			};
			for (const content of readingOrder) {
				let name = normalizeLocalizableString(content.name, docLanguages);
				if (!name) name = (await getJsdomFromUrlOrFile({ src: upath.join(contextDir, changeExtname(content.url, ".xhtml")) })).window.document.title;
				const li = document.createElement("li");
				const a = document.createElement("a");
				a.textContent = name;
				a.href = getRelativeHref(content.url, "", target);
				li.append(a);
				ol.append(li);
				tocResourceTree.children.push({
					element: li,
					label: a
				});
			}
			nav.append(ol);
			document.body.append(nav);
			Logger.debug("Generated toc nav element", nav.outerHTML);
		}
		if (landmarks.length > 0) {
			Logger.debug(`Generating landmark nav element: ${target}`);
			const nav = document.createElement("nav");
			nav.setAttribute("epub:type", "landmarks");
			nav.setAttribute("id", LANDMARKS_ID);
			nav.setAttribute("hidden", "");
			const h2 = document.createElement("h2");
			h2.textContent = EPUB_LANDMARKS_TITLE;
			nav.append(h2);
			const ol = document.createElement("ol");
			for (const { type, href, text } of landmarks) {
				const li = document.createElement("li");
				const a = document.createElement("a");
				a.setAttribute("epub:type", type);
				a.setAttribute("href", getRelativeHref(href, "", target));
				a.text = text;
				li.append(a);
				ol.append(li);
			}
			nav.append(ol);
			document.body.append(nav);
			Logger.debug("Generated landmark nav element", nav.outerHTML);
		}
	}
	const publicationLinkEl = document.querySelector("link[href][rel=\"publication\"]");
	if (publicationLinkEl) {
		const hrefAttr = publicationLinkEl.getAttribute("href");
		/* v8 ignore next 3 */
		if (hrefAttr === null) throw new Error("Expected publication link element to have href");
		const href = hrefAttr.trim();
		if (href.startsWith("#")) {
			const scriptEl = document.getElementById(href.slice(1));
			if (scriptEl?.getAttribute("type") === "application/ld+json") scriptEl.remove();
		}
		publicationLinkEl.remove();
	}
	await writeAsXhtml(dom, upath.join(contextDir, target));
	return { tocResourceTree };
}
async function processPagelistDocument({ dom, target, contextDir }) {
	const pageListResourceTree = parsePageListDocument(dom);
	if (pageListResourceTree) {
		const nav = replaceWithNavElement(dom, pageListResourceTree.element);
		nav.setAttribute("id", PAGELIST_ID);
		nav.setAttribute("epub:type", "page-list");
	}
	await writeAsXhtml(dom, upath.join(contextDir, target));
	return { pageListResourceTree };
}
function buildEpubPackageDocument({ epubVersion, manifest, uid, docTitle, docLanguages, spineItems, manifestItems }) {
	const slugger = new GithubSlugger();
	slugger.reset();
	const bookIdentifier = slugger.slug("bookid");
	const normalizeDate = (value) => value && `${new Date(value).toISOString().split(".")[0]}Z`;
	const toText = (value) => typeof value === "string" || typeof value === "number" ? `${value}` : "";
	const transformToGenericTextNode = (value, attributes) => [value].flat().filter(Boolean).map(() => Object.assign({}, attributes, { "#text": String(value) }));
	const transformContributor = (contributorMap) => Object.entries(contributorMap).flatMap(([type, contributor]) => contributor ? [contributor].flat().map((entry, index) => ({
		_id: slugger.slug(`${type}-${index + 1}`),
		"#text": typeof entry === "string" ? entry : normalizeLocalizableString(entry.name, docLanguages)
	})) : []);
	const itemIdMap = /* @__PURE__ */ new Map();
	manifestItems.forEach(({ href }) => {
		itemIdMap.set(href, slugger.slug(href));
	});
	return new XMLBuilder({
		format: true,
		ignoreAttributes: false,
		attributeNamePrefix: "_"
	}).build({
		"?xml": {
			_version: "1.0",
			_encoding: "UTF-8"
		},
		package: {
			_xmlns: "http://www.idpf.org/2007/opf",
			_version: epubVersion,
			"_unique-identifier": bookIdentifier,
			"_xml:lang": docLanguages[0],
			metadata: {
				"_xmlns:dc": "http://purl.org/dc/elements/1.1/",
				"dc:identifier": {
					_id: bookIdentifier,
					"#text": uid
				},
				"dc:title": docTitle,
				"dc:language": docLanguages,
				"dc:creator": transformContributor({
					author: manifest.author,
					creator: manifest.creator,
					editor: manifest.editor,
					artist: manifest.artist,
					illustrator: manifest.illustrator,
					colorist: manifest.colorist,
					penciler: manifest.penciler,
					inker: manifest.inker,
					letterer: manifest.letterer,
					translator: manifest.translator,
					readBy: manifest.readBy
				}),
				"dc:publisher": transformContributor({ publisher: manifest.publisher }),
				"dc:contributor": transformContributor({ contributor: manifest.contributor }),
				"dc:date": transformToGenericTextNode(normalizeDate(manifest.datePublished)),
				"dc:rights": transformToGenericTextNode(manifest.copyrightHolder && `© ${manifest.copyrightYear ? `${toText(manifest.copyrightYear)} ` : ""}${toText(manifest.copyrightHolder)}`),
				"dc:subject": transformToGenericTextNode(manifest["dc:subject"] || manifest.subject),
				meta: [...transformToGenericTextNode(normalizeDate(manifest.dateModified || Date.now()), { _property: "dcterms:modified" }), ...(() => {
					const coverImage = manifestItems.find((it) => it.properties === "cover-image");
					return coverImage ? [{
						_name: "cover",
						_content: itemIdMap.get(coverImage.href)
					}] : [];
				})()]
			},
			manifest: { item: manifestItems.map(({ href, mediaType, properties }) => ({
				_id: itemIdMap.get(href),
				_href: encodeURI(href),
				"_media-type": mediaType,
				...properties ? { _properties: properties } : {}
			})) },
			spine: {
				...manifest.readingProgression ? { "_page-progression-direction": manifest.readingProgression } : {},
				itemref: spineItems.map(({ href }) => ({ _idref: itemIdMap.get(href) }))
			}
		}
	});
}
function compressEpub({ target, sourceDir }) {
	Logger.debug(`Compressing EPUB: ${target}`);
	const output = fs.createWriteStream(target);
	const archive = archiver("zip", { zlib: { level: 9 } });
	return new Promise((resolve, reject) => {
		output.on("close", () => {
			Logger.debug(`Compressed EPUB: ${target}`);
			resolve();
		});
		output.on("error", reject);
		archive.on("warning", reject);
		archive.on("error", reject);
		archive.pipe(output);
		archive.append("application/epub+zip", {
			name: "mimetype",
			store: true
		});
		archive.directory(upath.join(sourceDir, "META-INF"), "META-INF");
		archive.directory(upath.join(sourceDir, "EPUB"), "EPUB");
		archive.finalize();
	});
}
//#endregion
//#region src/output/webbook.ts
function sortManifestResources(manifest) {
	if (!Array.isArray(manifest.resources)) return;
	manifest.resources = manifest.resources.toSorted((a, b) => (typeof a === "string" ? a : a.url) > (typeof b === "string" ? b : b.url) ? 1 : -1);
}
async function prepareWebPublicationDirectory({ outputDir }) {
	if (fs.existsSync(outputDir)) {
		Logger.debug("going to remove existing webpub", outputDir);
		await fs.promises.rm(outputDir, {
			force: true,
			recursive: true
		});
	}
	fs.mkdirSync(outputDir, { recursive: true });
}
function transformPublicationManifest(entity, transformer) {
	const { url: transformUrl } = transformer;
	const transformUrlOrPublicationLinks = (e) => {
		if (typeof e === "string") return transformUrl(e);
		return {
			...e,
			url: transformUrl(e.url)
		};
	};
	const ret = { ...entity };
	for (const [key, tr] of Object.entries({
		conformsTo: transformUrl,
		url: transformUrl,
		readingOrder: transformUrlOrPublicationLinks,
		resources: transformUrlOrPublicationLinks,
		links: transformUrlOrPublicationLinks
	})) if (key in ret) ret[key] = Array.isArray(ret[key]) ? ret[key].map(tr) : tr(ret[key]);
	return ret;
}
function decodePublicationManifest(input) {
	return transformPublicationManifest(input, { url: decodeURI });
}
function encodePublicationManifest(input) {
	return transformPublicationManifest(input, { url: encodeURI });
}
function writePublicationManifest(output, options) {
	const entries = options.entries.map((entry) => ({
		url: entry.path,
		...entry.title && { name: entry.title },
		...entry.encodingFormat && { encodingFormat: entry.encodingFormat },
		...entry.rel && { rel: entry.rel },
		...(entry.rel === "contents" || entry.rel === "cover") && { type: "LinkedResource" }
	}));
	const links = [options.links ?? []].flat();
	const resources = [options.resources ?? []].flat();
	if (options.cover) {
		const mimeType = lookup(options.cover.url);
		if (mimeType) resources.push({
			rel: "cover",
			url: options.cover.url,
			name: options.cover.name,
			encodingFormat: mimeType
		});
		else Logger.logWarn(`Cover image "${options.cover.url}" was set in your configuration but couldn’t detect the image metadata. Please check a valid cover file is placed.`);
	}
	const publication = {
		"@context": ["https://schema.org", "https://www.w3.org/ns/pub-context"],
		type: "Book",
		conformsTo: "https://github.com/vivliostyle/vivliostyle-cli",
		...options.title && { name: options.title },
		...options.author && { author: options.author },
		...options.language && { inLanguage: options.language },
		...options.readingProgression && { readingProgression: options.readingProgression },
		dateModified: options.modified,
		readingOrder: entries,
		resources,
		links
	};
	const encodedManifest = encodePublicationManifest(publication);
	Logger.debug("writePublicationManifest path: %s content: %O", output, encodedManifest);
	try {
		assertPubManifestSchema(encodedManifest);
	} catch (error) {
		const thrownError = error;
		throw new DetailError(`Validation of publication manifest failed. Please check the schema: ${output}`, typeof thrownError === "string" ? thrownError : thrownError.stack ?? thrownError.message);
	}
	fs.mkdirSync(upath.dirname(output), { recursive: true });
	fs.writeFileSync(output, JSON.stringify(encodedManifest, null, 2));
	return publication;
}
async function retrieveWebbookEntry({ viewerInput, outputDir }) {
	const webbookEntryUrl = viewerInput.webbookPath ? pathToFileURL(viewerInput.webbookPath).href : viewerInput.webbookEntryUrl;
	if (/^https?:/iv.test(webbookEntryUrl)) Logger.logUpdate("Fetching remote contents");
	const resourceLoader = new ResourceLoader$1();
	const dom = await getJsdomFromUrlOrFile({
		src: webbookEntryUrl,
		resourceLoader
	});
	const entryHtml = viewerInput.webbookPath ? upath.basename(viewerInput.webbookPath) : decodeURI(dom.window.location.pathname);
	const { manifest, manifestUrl } = await fetchLinkedPublicationManifest({
		dom,
		resourceLoader,
		baseUrl: webbookEntryUrl
	}) ?? {};
	let pathContains;
	if (webbookEntryUrl.startsWith("data:")) pathContains = () => false;
	else {
		const rootUrl = /^https?:/iv.test(webbookEntryUrl) ? new URL("/", webbookEntryUrl).href : new URL(".", webbookEntryUrl).href;
		pathContains = (url) => !upath.relative(rootUrl, url).startsWith("..");
	}
	const retriever = new Map(resourceLoader.fetcherMap);
	if (manifest && manifestUrl) {
		[manifest.resources || []].flat().forEach((v) => {
			const url = typeof v === "string" ? v : v.url;
			const fullUrl = new URL(encodeURI(url), manifestUrl).href;
			if (!pathContains(fullUrl) || retriever.has(fullUrl)) return;
			const fetchPromise = resourceLoader.fetch(fullUrl);
			if (fetchPromise && !retriever.has(fullUrl)) retriever.set(fullUrl, fetchPromise);
		});
		for (const v of [manifest.readingOrder || []].flat()) {
			const url = typeof v === "string" ? v : v.url;
			if (!/\.html?$/v.test(url) && !(typeof v === "string" || v.encodingFormat === "text/html")) continue;
			const fullUrl = new URL(encodeURI(url), manifestUrl).href;
			if (!pathContains(fullUrl) || fullUrl === webbookEntryUrl) continue;
			const subpathResourceLoader = new ResourceLoader$1();
			await getJsdomFromUrlOrFile({
				src: fullUrl,
				resourceLoader: subpathResourceLoader,
				virtualConsole: createVirtualConsole((error) => {
					Logger.logError(`Failed to fetch webbook resources: ${error.detail}`);
				})
			});
			subpathResourceLoader.fetcherMap.forEach((fetcher, k) => {
				if (!retriever.has(k)) retriever.set(k, fetcher);
			});
		}
	}
	const fetchedResources = await ResourceLoader$1.saveFetchedResources({
		fetcherMap: retriever,
		rootUrl: webbookEntryUrl,
		outputDir,
		/* v8 ignore next 4 */
		onError: (error) => {
			Logger.debug(error);
			Logger.logError(`Failed to fetch webbook resources: ${error}`);
		}
	});
	if (manifest) {
		const referencedContents = new Set([...[manifest.readingOrder || []].flat(), ...[manifest.resources || []].flat()].map((v) => typeof v === "string" ? v : v.url));
		manifest.resources = [...[manifest.resources || []].flat(), ...fetchedResources.filter(({ url }) => !referencedContents.has(url))];
		sortManifestResources(manifest);
	}
	Logger.debug("Saved webbook resources", fetchedResources.map((v) => v.url));
	Logger.debug("Publication manifest from webbook", manifest && JSON.stringify(manifest, null, 2));
	return {
		entryHtmlFile: upath.join(outputDir, entryHtml, ...upath.extname(entryHtml) ? [] : ["index.html"]),
		manifest
	};
}
async function supplyWebPublicationManifestForWebbook({ entryHtmlFile, outputDir, ...config }) {
	Logger.debug(`Generating publication manifest from HTML: ${entryHtmlFile}`);
	const dom = await getJsdomFromUrlOrFile({ src: entryHtmlFile });
	const { document } = dom.window;
	const language = config.language || document.documentElement.lang || void 0;
	const title = config.title || document.title || "";
	const author = config.author || document.querySelector("meta[name=\"author\"]")?.getAttribute("content") || "";
	const entry = upath.relative(outputDir, entryHtmlFile);
	const allFiles = await glob("**", { cwd: outputDir });
	const manifest = writePublicationManifest(upath.join(outputDir, MANIFEST_FILENAME), {
		title,
		author,
		language,
		readingProgression: config.readingProgression,
		modified: (/* @__PURE__ */ new Date()).toISOString(),
		entries: [{ path: entry }],
		resources: allFiles.filter((f) => f !== entry)
	});
	sortManifestResources(manifest);
	const link = document.createElement("link");
	link.setAttribute("rel", "publication");
	link.setAttribute("type", "application/ld+json");
	link.setAttribute("href", upath.relative(upath.dirname(entryHtmlFile), upath.join(outputDir, MANIFEST_FILENAME)));
	document.head.append(link);
	await fs.promises.writeFile(entryHtmlFile, dom.serialize(), "utf8");
	Logger.debug("Generated publication manifest from HTML", JSON.stringify(manifest, null, 2));
	return manifest;
}
async function copyWebPublicationAssets({ exportAliases, outputs, copyAsset, themesDir, manifestPath, input, outputDir, entries }) {
	const relExportAliases = exportAliases.map(({ source, target }) => ({
		source: upath.relative(input, source),
		target: upath.relative(input, target)
	})).filter(({ source }) => !source.startsWith(".."));
	const assetMatcher = getAssetMatcher({
		copyAsset,
		cwd: input,
		outputs,
		themesDir,
		entries
	});
	const webResourceMatcher = getWebPubResourceMatcher({
		cwd: input,
		outputs,
		themesDir,
		entries,
		manifestPath,
		copyAsset
	});
	const allFiles = /* @__PURE__ */ new Set([...await assetMatcher.glob(), ...await webResourceMatcher.glob({ followSymbolicLinks: true })]);
	for (const alias of relExportAliases) allFiles.delete(alias.target);
	Logger.debug("webbook files", JSON.stringify([...allFiles].map((file) => {
		const alias = relExportAliases.find(({ source }) => source === file);
		return alias ? `${file} (alias: ${alias.target})` : file;
	}), null, 2));
	const resources = [];
	let actualManifestPath = upath.join(outputDir, upath.relative(input, manifestPath));
	for (const file of allFiles) {
		const alias = relExportAliases.find(({ source }) => source === file);
		const relTarget = alias?.target || file;
		resources.push(relTarget);
		const target = upath.join(outputDir, relTarget);
		fs.mkdirSync(upath.dirname(target), { recursive: true });
		await copy(upath.join(input, file), target);
		if (alias && pathEquals(upath.join(input, alias.source), manifestPath)) actualManifestPath = target;
	}
	Logger.debug("webbook publication.json", actualManifestPath);
	const manifest = decodePublicationManifest(JSON.parse(fs.readFileSync(actualManifestPath, "utf8")));
	for (const entry of relExportAliases) {
		const rewriteAliasPath = (e) => {
			if (typeof e === "string") return pathEquals(e, entry.source) ? entry.source : e;
			if (pathEquals(e.url, entry.source)) e.url = entry.target;
			return e;
		};
		if (manifest.links) manifest.links = Array.isArray(manifest.links) ? manifest.links.map(rewriteAliasPath) : rewriteAliasPath(manifest.links);
		if (manifest.readingOrder) manifest.readingOrder = Array.isArray(manifest.readingOrder) ? manifest.readingOrder.map(rewriteAliasPath) : rewriteAliasPath(manifest.readingOrder);
		if (manifest.resources) manifest.resources = Array.isArray(manifest.resources) ? manifest.resources.map(rewriteAliasPath) : rewriteAliasPath(manifest.resources);
	}
	const normalizeToUrl = (val) => [val || []].flat().map((e) => typeof e === "string" ? e : e.url);
	const preDefinedResources = /* @__PURE__ */ new Set([
		...normalizeToUrl(manifest.links),
		...normalizeToUrl(manifest.readingOrder),
		...normalizeToUrl(manifest.resources)
	]);
	manifest.resources = [...[manifest.resources || []].flat(), ...resources.flatMap((file) => {
		if (preDefinedResources.has(file) || pathEquals(file, upath.relative(outputDir, actualManifestPath))) return [];
		return file;
	})];
	sortManifestResources(manifest);
	fs.writeFileSync(actualManifestPath, JSON.stringify(encodePublicationManifest(manifest), null, 2));
	return {
		manifest,
		actualManifestPath
	};
}
async function buildWebPublication({ target, config }) {
	let outputDir;
	if (target.format === "webpub") {
		outputDir = target.path;
		await prepareWebPublicationDirectory({ outputDir });
	} else [outputDir] = await useTmpDirectory();
	let entryHtmlFile;
	let manifest;
	let actualManifestPath;
	if (config.viewerInput.type === "webpub") {
		const ret = await copyWebPublicationAssets({
			...config,
			input: config.workspaceDir,
			outputDir,
			manifestPath: config.viewerInput.manifestPath
		});
		manifest = ret.manifest;
		actualManifestPath = ret.actualManifestPath;
		if (config.input.format === "markdown") {
			const entry = [manifest.readingOrder].flat()[0];
			if (entry) entryHtmlFile = upath.join(outputDir, typeof entry === "string" ? entry : entry.url);
		}
	} else if (isWebbookConfig(config)) {
		const ret = await retrieveWebbookEntry({
			viewerInput: config.viewerInput,
			outputDir
		});
		entryHtmlFile = ret.entryHtmlFile;
		manifest = ret.manifest ?? await supplyWebPublicationManifestForWebbook({
			...config,
			entryHtmlFile: ret.entryHtmlFile,
			outputDir
		});
	} else throw new Error("No entry specified");
	if (target.format === "epub") await exportEpub({
		webpubDir: outputDir,
		entryHtmlFile,
		manifest,
		relManifestPath: actualManifestPath && upath.relative(outputDir, actualManifestPath),
		target: target.path,
		epubVersion: target.version
	});
	return target.path;
}
//#endregion
//#region src/processor/theme.ts
function getThemeInstallCacheDir(themesDir) {
	return upath.join(themesDir, ".npm", "_cacache");
}
function* iterateSymlinksInThemesNodeModules(themesDir) {
	if (!fs.existsSync(themesDir)) return;
	const nodeModulesDir = upath.join(themesDir, "node_modules");
	if (!fs.existsSync(nodeModulesDir)) return;
	for (const entry of fs.readdirSync(nodeModulesDir, { withFileTypes: true })) {
		if (!entry.isSymbolicLink()) continue;
		const linkPath = upath.join(nodeModulesDir, entry.name);
		const target = fs.readlinkSync(linkPath);
		yield upath.resolve(nodeModulesDir, target);
	}
}
/**
* Check for broken symlinks in node_modules directory and remove the entire
* themes directory if any are found. This is necessary because Arborist fails
* when encountering symlinks pointing to non-existent directories
* (e.g., when a local theme is moved or deleted).
*/
function removeThemesDirIfBrokenSymlinks(themesDir) {
	for (const target of iterateSymlinksInThemesNodeModules(themesDir)) if (!fs.existsSync(target)) {
		fs.rmSync(themesDir, { recursive: true });
		return;
	}
}
async function checkThemeInstallationNecessity({ themesDir, themeIndexes }) {
	removeThemesDirIfBrokenSymlinks(themesDir);
	if (!fs.existsSync(themesDir)) return [...themeIndexes].some((theme) => theme.type === "package");
	const tree = await new Arborist({
		path: themesDir,
		cache: getThemeInstallCacheDir(themesDir),
		lockfileVersion: 3,
		installLinks: true
	}).loadActual();
	const pkgs = new Set(Array.from(tree.children.keys()));
	return [...themeIndexes].some((theme) => theme.type === "package" && !pkgs.has(theme.name));
}
function getLocalThemePaths({ themesDir }) {
	return [...iterateSymlinksInThemesNodeModules(themesDir)];
}
async function installThemeDependencies({ themesDir, themeIndexes, signal }) {
	signal?.throwIfAborted();
	fs.mkdirSync(themesDir, { recursive: true });
	removeThemesDirIfBrokenSymlinks(themesDir);
	try {
		const commonOpt = {
			path: themesDir,
			cache: getThemeInstallCacheDir(themesDir),
			lockfileVersion: 3,
			installLinks: true
		};
		const tree = await new Arborist(commonOpt).buildIdealTree();
		signal?.throwIfAborted();
		const existing = Array.from(tree.children.keys());
		const add = [...new Set([...themeIndexes].flatMap((theme) => theme.type === "package" ? [theme.specifier] : []))];
		const rm = existing.filter((v) => !add.includes(v));
		const opt = {
			...commonOpt,
			rm,
			add
		};
		const arb = new Arborist(opt);
		const actualTree = await executeWithCleanupOnInterrupt("Waiting for theme installation rollback", () => arb.reify(opt));
		for (const child of actualTree.children.values()) if (child.resolved?.startsWith("file:")) {
			const sourcePath = upath.resolve(themesDir, child.resolved.slice(5));
			if (fs.existsSync(child.path)) fs.rmSync(child.path, { recursive: true });
			const relPath = upath.relative(upath.dirname(child.path), sourcePath);
			fs.symlinkSync(relPath, child.path, "junction");
		}
		signal?.throwIfAborted();
	} catch (error) {
		signal?.throwIfAborted();
		const thrownError = toError(error);
		throw new DetailError("An error occurred during the installation of the theme", thrownError.stack ?? thrownError.message);
	}
}
//#endregion
//#region src/processor/compile.ts
const isContentsEntry = (entry) => entry.rel === "contents";
const isCoverEntry = (entry) => entry.rel === "cover";
const isManuscriptEntry = (entry) => "source" in entry;
function locateThemePath(theme, from) {
	if (theme.type === "uri") return theme.location;
	if (theme.type === "file") return upath.relative(from, theme.location);
	if (theme.importPath) return [theme.importPath].flat().map((locator) => {
		const resolvedPath = upath.resolve(theme.location, locator);
		if (!pathContains(theme.location, resolvedPath) || !fs.existsSync(resolvedPath)) throw new Error(`Could not find a style path ${locator} for the theme: ${theme.name}.`);
		return upath.relative(from, resolvedPath);
	});
	const packageJson = readPackageJson(upath.join(theme.location, "package.json"));
	const maybeStyle = packageJson.vivliostyle?.theme?.style ?? packageJson.style ?? packageJson.main;
	if (!maybeStyle) throw new DetailError(`Could not find a style file for the theme: ${theme.name}.`, "Please ensure this package satisfies a `vivliostyle.theme.style` property.");
	return upath.relative(from, upath.join(theme.location, maybeStyle));
}
async function cleanupWorkspace({ entryContextDir, workspaceDir, themesDir, entries }) {
	if (pathEquals(workspaceDir, entryContextDir) || pathContains(workspaceDir, entryContextDir) || entries.some((entry) => entry.source?.type === "file" && pathContains(workspaceDir, entry.source.pathname))) return;
	Logger.debug("cleanup workspace files", workspaceDir);
	let movedWorkspacePath;
	await executeWithCleanupOnInterrupt("Waiting for workspace cleanup", async () => {
		if (pathContains(workspaceDir, themesDir) && fs.existsSync(themesDir)) {
			movedWorkspacePath = upath.join(upath.dirname(workspaceDir), `.vs-${Date.now()}`);
			const movedThemePath = upath.join(movedWorkspacePath, upath.relative(workspaceDir, themesDir));
			fs.mkdirSync(upath.dirname(movedThemePath), { recursive: true });
			await move(themesDir, movedThemePath);
		}
		await fs.promises.rm(workspaceDir, {
			recursive: true,
			force: true
		});
		if (movedWorkspacePath) await move(movedWorkspacePath, workspaceDir);
	}, () => {
		if (movedWorkspacePath && fs.existsSync(movedWorkspacePath)) fs.rmSync(movedWorkspacePath, {
			recursive: true,
			force: true
		});
	});
}
async function prepareThemeDirectory({ themesDir, themeIndexes }, signal) {
	if (fs.existsSync(upath.join(themesDir, "packages")) && !fs.existsSync(upath.join(themesDir, "node_modules"))) fs.renameSync(upath.join(themesDir, "packages"), upath.join(themesDir, "node_modules"));
	if (await checkThemeInstallationNecessity({
		themesDir,
		themeIndexes
	})) {
		Logger.startLogging("Installing theme files");
		await installThemeDependencies({
			themesDir,
			themeIndexes,
			signal
		});
	}
	for (const theme of themeIndexes) if (theme.type === "file" && !pathEquals(theme.source, theme.location)) {
		fs.mkdirSync(upath.dirname(theme.location), { recursive: true });
		await copy(theme.source, theme.location);
	}
	return getLocalThemePaths({ themesDir });
}
async function transformManuscript(entry, { entryContextDir, workspaceDir, viewerInput: { manifestPath }, title, entries, language, vfmOptions, rootUrl }) {
	const source = isContentsEntry(entry) || isCoverEntry(entry) ? entry.template : entry.source;
	let content;
	let resourceLoader;
	let resourceUrl;
	const style = entry.themes.flatMap((theme) => locateThemePath(theme, upath.dirname(entry.target)));
	if (source?.type === "file") {
		if (source.documentProcessor) {
			const vfile = await processMarkdown(source.documentProcessor.processorFactory, source.documentProcessor.metadataReader, source.pathname, {
				...vfmOptions,
				style,
				title: entry.title,
				language: language ?? void 0
			});
			content = getJsdomFromString({ html: String(vfile) });
		} else if (source.contentType === "text/html" || source.contentType === "application/xhtml+xml") {
			content = await getJsdomFromUrlOrFile({ src: source.pathname });
			content = await processManuscriptHtml(content, {
				style,
				title: entry.title,
				contentType: source.contentType,
				language
			});
		} else if (!pathEquals(source.pathname, entry.target)) await copy(source.pathname, entry.target);
	} else if (source?.type === "uri") {
		resourceUrl = /^https?:/v.test(source.href) ? source.href : `${rootUrl}${source.href}`;
		resourceLoader = new ResourceLoader$1();
		try {
			await getJsdomFromUrlOrFile({
				src: resourceUrl,
				resourceLoader,
				virtualConsole: createVirtualConsole((error) => {
					Logger.logError(`Failed to fetch resources: ${error.detail}`);
				})
			});
		} catch (error) {
			const thrownError = toError(error);
			throw new DetailError(`Failed to fetch the content from ${resourceUrl}`, thrownError.stack ?? thrownError.message);
		}
		const contentFetcher = resourceLoader.fetcherMap.get(resourceUrl);
		if (contentFetcher) {
			const buffer = await contentFetcher;
			const contentType = contentFetcher.response?.headers["content-type"];
			if (!contentType || new MIMEType(contentType).essence !== "text/html") throw new Error(`The content is not an HTML document: ${resourceUrl}`);
			content = getJsdomFromString({ html: buffer.toString("utf8") });
			content = await processManuscriptHtml(content, {
				style,
				title: entry.title,
				contentType: "text/html",
				language
			});
		}
	} else if (entry.rel === "contents") {
		content = getJsdomFromString({ html: generateDefaultTocHtml({
			language,
			title
		}) });
		content = await processManuscriptHtml(content, {
			style,
			title,
			contentType: "text/html",
			language
		});
	} else if (entry.rel === "cover") {
		content = getJsdomFromString({ html: generateDefaultCoverHtml({
			language,
			title: entry.title
		}) });
		content = await processManuscriptHtml(content, {
			style,
			title: entry.title,
			contentType: "text/html",
			language
		});
	}
	if (!content) return;
	if (isContentsEntry(entry)) {
		const contentsEntry = entry;
		const manuscriptEntries = entries.filter(isManuscriptEntry);
		content = await processTocHtml(content, {
			entries: manuscriptEntries,
			manifestPath,
			distDir: upath.dirname(contentsEntry.target),
			tocTitle: contentsEntry.tocTitle,
			sectionDepth: contentsEntry.sectionDepth,
			styleOptions: contentsEntry,
			transform: contentsEntry.transform
		});
	}
	if (isCoverEntry(entry)) {
		const coverEntry = entry;
		content = await processCoverHtml(content, {
			imageSrc: upath.relative(upath.join(entryContextDir, upath.relative(workspaceDir, coverEntry.target), ".."), coverEntry.coverImageSrc),
			imageAlt: coverEntry.coverImageAlt,
			styleOptions: coverEntry
		});
	}
	let html;
	if (content.window.document.contentType === "application/xhtml+xml") html = `${XML_DECLARATION}\n${serializeToXml(content.window.document)}`;
	else html = content.serialize();
	const htmlBuffer = Buffer.from(html, "utf8");
	if (!source || source.type === "file" && !pathEquals(source.pathname, entry.target)) writeFileIfChanged(entry.target, htmlBuffer);
	if (source?.type === "uri" && resourceLoader && resourceUrl) {
		const fetcher = resourceLoader.fetcherMap.get(resourceUrl);
		/* v8 ignore next 3 */
		if (!fetcher) throw new Error(`Failed to locate the fetched content: ${resourceUrl}`);
		const { response } = fetcher;
		const contentFetcher = Promise.resolve(htmlBuffer);
		contentFetcher.abort = () => {};
		contentFetcher.response = response;
		resourceLoader.fetcherMap.set(resourceUrl, contentFetcher);
		await ResourceLoader$1.saveFetchedResources({
			fetcherMap: resourceLoader.fetcherMap,
			rootUrl: resourceUrl,
			outputDir: source.rootDir
		});
	}
	return html;
}
function generateManifest({ entryContextDir, workspaceDir, viewerInput: { manifestPath }, title, author, entries, language, readingProgression, cover }) {
	const manifestEntries = entries.map((entry) => ({
		title: isContentsEntry(entry) && entry.tocTitle || entry.title,
		path: upath.relative(workspaceDir, entry.target),
		encodingFormat: !("contentType" in entry) || entry.contentType === "text/markdown" || entry.contentType === "text/x-vivliostyle-custom" || entry.contentType === "text/html" ? void 0 : entry.contentType,
		rel: entry.rel
	}));
	writePublicationManifest(manifestPath, {
		title,
		author,
		language,
		readingProgression,
		cover: cover && {
			url: upath.relative(entryContextDir, cover.src),
			name: cover.name
		},
		entries: manifestEntries,
		modified: (/* @__PURE__ */ new Date()).toISOString()
	});
}
async function compile(config) {
	const tocEntries = [];
	for (const entry of config.entries) {
		if (entry.rel === "contents") {
			tocEntries.push(entry);
			continue;
		}
		await transformManuscript(entry, config);
	}
	for (const entry of tocEntries) await transformManuscript(entry, config);
	if (config.viewerInput.needToGenerateManifest) generateManifest(config);
}
//#endregion
//#region src/vite/vite-plugin-dev-server.ts
function createEntriesRouteLookup(entries, cwd) {
	const extns = [
		"",
		"html",
		"htm"
	];
	const toAssume = (uri) => {
		const len = uri.length - 1;
		let i = 0, x;
		const path = uri.codePointAt(len) === 47 ? uri.slice(0, len) : uri;
		const arr = [], tmp = `${path}/index`;
		for (; i < extns.length; i++) {
			x = extns[i] ? `.${extns[i]}` : "";
			if (path) arr.push(path + x);
			arr.push(tmp + x);
		}
		return arr;
	};
	const cache = entries.reduce((acc, e) => {
		acc[`/${upath.relative(cwd, e.target).normalize().replaceAll(/\\+/gv, "/")}`] = e;
		return acc;
	}, {});
	return (uri) => {
		const arr = toAssume(uri);
		let data, i = 0;
		for (; i < arr.length; i++) if (data = cache[arr[i]]) return [data, arr[i]];
	};
}
function getWorkspaceMatcher({ workspaceDir, themesDir, viewerInput, entries, outputs, copyAsset }) {
	const hasCmykReserveMap = outputs.some((o) => o.format === "pdf" && o.cmyk && o.cmyk.reserveMap.length > 0);
	if (viewerInput.type === "webpub") return getWebPubResourceMatcher({
		outputs,
		themesDir,
		entries,
		cwd: workspaceDir,
		manifestPath: viewerInput.manifestPath,
		copyAsset,
		additionalPatterns: hasCmykReserveMap ? [CMYK_RESERVE_MAP_FILENAME] : []
	});
	let entryFiles = [];
	switch (viewerInput.type) {
		case "epub":
			entryFiles = [upath.join(upath.relative(workspaceDir, viewerInput.epubTmpOutputDir), "**")];
			break;
		case "epub-opf":
		case "webbook":
			entryFiles = ["**"];
			break;
		default: entryFiles = viewerInput;
	}
	return new GlobMatcher([{
		patterns: entryFiles,
		ignore: ["node_modules/**"],
		dot: true,
		cwd: workspaceDir
	}]);
}
function vsDevServerPlugin({ config: _config, inlineConfig }) {
	let config = _config;
	let server;
	let program;
	const transformCache = /* @__PURE__ */ new Map();
	let matchProjectDep;
	async function reload(forceUpdate = false) {
		const prevConfig = config;
		config = await reloadConfig(prevConfig, inlineConfig, server?.config);
		transformCache.clear();
		const needToUpdateManifest = forceUpdate || JSON.stringify(prevConfig) !== JSON.stringify(config);
		if (isWebPubConfig(config) && config.viewerInput.needToGenerateManifest && needToUpdateManifest) generateManifest(config);
		generateCmykReserveMap(config);
		const localThemePaths = await prepareThemeDirectory(config, inlineConfig.signal);
		const entriesLookup = createEntriesRouteLookup(config.entries, config.workspaceDir);
		const urlMatchRe = new RegExp(`^${escapeRe(config.base)}(/[^?#]*)([?#].*)?$`, "v");
		const serveWorkspace = sirv(config.workspaceDir, {
			dev: true,
			etag: false,
			dotfiles: true,
			extensions: []
		});
		const serveWorkspaceMatcher = getWorkspaceMatcher(config);
		const serveAssets = sirv(config.entryContextDir, {
			dev: true,
			etag: false,
			dotfiles: false,
			extensions: []
		});
		const serveAssetsMatcher = getAssetMatcher({
			...config,
			cwd: config.entryContextDir
		});
		program = {
			entriesLookup,
			urlMatchRe,
			serveWorkspace,
			serveWorkspaceMatcher,
			serveAssets,
			serveAssetsMatcher
		};
		if (needToUpdateManifest) {
			Logger.debug("dev-server > serveWorkspaceMatcher %O", serveWorkspaceMatcher.matcherConfig);
			Logger.debug("dev-server > serveAssetsMatcher %O", serveAssetsMatcher.matcherConfig);
		}
		const configPath = locateVivliostyleConfig(inlineConfig);
		const projectDeps = [];
		if (configPath) {
			projectDeps.push(configPath);
			server?.watcher.add(configPath);
		}
		if (config.viewerInput.type === "webpub") {
			projectDeps.push(config.viewerInput.manifestPath);
			server?.watcher.add(config.viewerInput.manifestPath);
		}
		const flattenWatchTarget = (themes) => [...themes].flatMap((theme) => {
			if (theme.type === "file") return [theme.source];
			if (theme.type === "package" && !theme.registry) return [theme.specifier];
			return [];
		});
		const prevThemeFiles = flattenWatchTarget(prevConfig.themeIndexes);
		const themeFiles = flattenWatchTarget(config.themeIndexes);
		server?.watcher.unwatch(prevThemeFiles.filter((target) => !themeFiles.includes(target)));
		server?.watcher.add(themeFiles);
		server?.watcher.add(localThemePaths);
		projectDeps.push(...themeFiles, ...localThemePaths);
		matchProjectDep = (pathname) => projectDeps.some((dep) => pathEquals(dep, pathname) || pathContains(dep, pathname));
	}
	function transform(entry, host) {
		if (!isWebPubConfig(config)) return;
		const rootUrl = host ? `${server?.config.server.https ? "https" : "http"}://${host}` : config.rootUrl;
		const promise = (async () => {
			try {
				const html = await transformManuscript(entry, {
					...config,
					rootUrl
				});
				if (!html) {
					transformCache.delete(entry.target);
					return;
				}
				const etag = `W/"${Date.now()}"`;
				if (entry.source?.type === "file") server?.watcher.add(entry.source.pathname);
				return {
					content: html,
					etag
				};
			} catch (error) {
				server?.config.logger.error(getFormattedError(error instanceof Error ? error : new Error(String(error))));
				transformCache.delete(entry.target);
			}
		})();
		transformCache.set(entry.target, promise);
		return promise;
	}
	async function transformAll(host) {
		const tocEntries = [];
		for (const entry of config.entries) {
			if (entry.rel === "contents") {
				tocEntries.push(entry);
				continue;
			}
			await transform(entry, host);
		}
		for (const entry of tocEntries) await transform(entry, host);
	}
	function invalidate(entry) {
		const cwd = pathToFileURL(config.workspaceDir);
		const target = pathToFileURL(entry.target);
		if (target.href.indexOf(cwd.href) !== 0) return;
		transformCache.delete(entry.target);
		config.entries.filter((contentsEntry) => contentsEntry.rel === "contents").forEach((contentsEntry) => {
			transformCache.delete(contentsEntry.target);
		});
		server?.ws.send({
			type: "full-reload",
			path: target.href.slice(cwd.href.length)
		});
	}
	const devServerMiddleware = async function vivliostyleDevServerMiddleware(req, res, next) {
		if (!program || req.url === void 0) {
			next();
			return;
		}
		const { entriesLookup, urlMatchRe } = program;
		const [_, pathname, qs] = decodeURI(req.url).match(urlMatchRe) ?? [];
		const match = pathname && entriesLookup(pathname);
		if (!match) {
			next();
			return;
		}
		const [entry, expected] = match;
		if (pathname !== expected) {
			res.statusCode = 301;
			res.setHeader("Location", `${expected}${qs || ""}`);
			return res.end();
		}
		Logger.debug("dev-server > request %s", pathname);
		const cachePromise = transformCache.get(entry.target);
		if (cachePromise) {
			const cached = await cachePromise;
			if (!cached) {
				next();
				return;
			}
			if (req.headers["if-none-match"] === cached.etag) {
				res.statusCode = 304;
				return res.end();
			}
			res.statusCode = 200;
			res.setHeader("Content-Type", "text/html;charset=utf-8");
			res.setHeader("Cache-Control", "no-cache");
			res.setHeader("Etag", cached.etag);
			return res.end(cached.content);
		}
		const { host } = req.headers;
		if (entry.rel === "contents") await transformAll(host);
		const result = await transform(entry, host);
		if (!result) {
			next();
			return;
		}
		res.statusCode = 200;
		res.setHeader("Content-Type", "text/html;charset=utf-8");
		res.setHeader("Cache-Control", "no-cache");
		res.setHeader("Etag", result.etag);
		return res.end(result.content);
	};
	const serveWorkspaceMiddleware = function vivliostyleServeWorkspaceMiddleware(req, res, next) {
		if (!config || !program || req.url === void 0) {
			next();
			return;
		}
		const requestUrl = req.url;
		const { urlMatchRe, serveWorkspace, serveWorkspaceMatcher, serveAssets, serveAssetsMatcher } = program;
		const [_, pathname] = decodeURI(requestUrl).match(urlMatchRe) ?? [];
		if (!pathname) {
			next();
			return;
		}
		const handleWorkspace = (proceed) => {
			if (!serveWorkspaceMatcher.match(pathname.slice(1))) {
				proceed();
				return;
			}
			Logger.debug("dev-server > serveWorkspace %s", pathname);
			const url = requestUrl;
			req.url = requestUrl.slice(config.base.length);
			serveWorkspace(req, res, () => {
				req.url = url;
				proceed();
			});
		};
		const handleAssets = (proceed) => {
			if (!serveAssetsMatcher.match(pathname.slice(1))) {
				proceed();
				return;
			}
			Logger.debug("dev-server > serveAssets %s", pathname);
			const url = requestUrl;
			req.url = url.slice(config.base.length);
			serveAssets(req, res, () => {
				req.url = url;
				proceed();
			});
		};
		handleWorkspace(() => {
			handleAssets(next);
		});
	};
	return {
		name: "vivliostyle:dev-server",
		enforce: "pre",
		configureServer(viteServer) {
			server = viteServer;
			const requestReload = debounce(async () => {
				await reload();
				viteServer.ws.send({
					type: "full-reload",
					path: "*"
				});
			}, 200);
			const handleUpdate = (pathname) => {
				if (!matchProjectDep?.(pathname)) return;
				requestReload();
			};
			viteServer.watcher.on("add", handleUpdate);
			viteServer.watcher.on("change", handleUpdate);
			viteServer.watcher.on("unlink", handleUpdate);
			return () => {
				viteServer.middlewares.use((req, res, next) => {
					devServerMiddleware(req, res, next);
				});
				viteServer.middlewares.use(serveWorkspaceMiddleware);
			};
		},
		configurePreviewServer(viteServer) {
			return () => {
				viteServer.middlewares.use(config.base, sirv(config.workspaceDir, {
					dev: true,
					etag: false,
					dotfiles: true,
					extensions: []
				}));
				viteServer.middlewares.use(config.base, sirv(config.entryContextDir, {
					dev: true,
					etag: false,
					dotfiles: false,
					extensions: []
				}));
			};
		},
		async buildStart() {
			await reload(true);
			await transformAll();
		},
		handleHotUpdate(ctx) {
			const entry = config?.entries.find((e) => e.source?.type === "file" && e.source.pathname === ctx.file || !e.source && e.target === ctx.file);
			if (config && entry) invalidate(entry);
		}
	};
}
//#endregion
//#region src/vite/vite-plugin-static-serve.ts
function vsStaticServePlugin({ config: _config, inlineConfig }) {
	const config = _config;
	const createMiddlewares = () => {
		if (typeof config.serverRootDir !== "string") return [];
		return Object.entries(config.static).flatMap(([base, dirs]) => dirs.map((dir) => [base, sirv(upath.resolve(config.serverRootDir, dir), {
			dev: true,
			etag: false
		})]));
	};
	return {
		name: "vivliostyle:static-serve",
		apply: () => Boolean(inlineConfig.enableStaticServe),
		configureServer(viteServer) {
			return () => {
				createMiddlewares().forEach(([base, middleware]) => {
					viteServer.middlewares.use(base, middleware);
				});
			};
		},
		configurePreviewServer(viteServer) {
			return () => {
				createMiddlewares().forEach(([base, middleware]) => {
					viteServer.middlewares.use(base, middleware);
				});
			};
		}
	};
}
//#endregion
//#region src/vite/vite-plugin-viewer.ts
const viewerClientRequestPath = `/@vivliostyle:viewer:client`;
const viewerClientContent = `
if (import.meta.hot) {
  import.meta.hot.on('vite:beforeFullReload', (e) => {
    location.reload();
  });
}`;
function vsViewerPlugin(_) {
	const serveRootDir = upath.join(viewerRoot, "lib");
	const serve = sirv(serveRootDir, {
		dev: false,
		etag: true
	});
	let cachedIndexHtml;
	const middleware = function vivliostyleViewerMiddleware(req, res, next) {
		if (req.url === "/" || req.url === "/index.html") {
			cachedIndexHtml ??= prependToHead(fs.readFileSync(upath.join(serveRootDir, "index.html"), "utf-8"), `<script type="module" src="${viewerClientRequestPath}"><\/script>`);
			res.statusCode = 200;
			res.setHeader("Content-Type", "text/html;charset=utf-8");
			res.setHeader("Cache-Control", "no-cache");
			res.end(cachedIndexHtml);
			return;
		}
		serve(req, res, next);
	};
	return {
		name: "vivliostyle:viewer",
		config() {
			return { optimizeDeps: { exclude: ["@vivliostyle/viewer"] } };
		},
		configureServer(viteServer) {
			viteServer.middlewares.use(VIEWER_ROOT_PATH, middleware);
		},
		configurePreviewServer(viteServer) {
			viteServer.middlewares.use(VIEWER_ROOT_PATH, serve);
		},
		load(id) {
			if (id === viewerClientRequestPath) return viewerClientContent;
		}
	};
}
//#endregion
//#region src/server.ts
function generateCmykReserveMap({ outputs, workspaceDir }) {
	const pdfOutput = outputs.find((o) => o.format === "pdf");
	if (pdfOutput && "cmyk" in pdfOutput && pdfOutput.cmyk && pdfOutput.cmyk.reserveMap.length > 0) fs.writeFileSync(upath.join(workspaceDir, CMYK_RESERVE_MAP_FILENAME), JSON.stringify(pdfOutput.cmyk.reserveMap));
}
function getViewerParams(src, { size, cropMarks, bleed, cropOffset, css, customStyle, customUserStyle, singleDoc, quick, viewerParam, base, rootUrl }, { cmykReserveMapUrl } = {}) {
	const pageSizeValue = size && ("format" in size ? size.format : `${size.width} ${size.height}`);
	function escapeParam(url) {
		return url.replaceAll("&", "%26");
	}
	let viewerParams = src ? `src=${escapeParam(src)}` : "";
	viewerParams += `&bookMode=${!singleDoc}&renderAllPages=${!quick}`;
	if (customStyle) {
		const param = isValidUri(customStyle) ? customStyle : new URL$1(upath.posix.join(base, customStyle), rootUrl).href;
		viewerParams += `&style=${escapeParam(param)}`;
	}
	if (customUserStyle) {
		const param = isValidUri(customUserStyle) ? customUserStyle : new URL$1(upath.posix.join(base, customUserStyle), rootUrl).href;
		viewerParams += `&userStyle=${escapeParam(param)}`;
	}
	if (pageSizeValue || cropMarks || bleed || cropOffset || css) {
		let pageStyle = "@page{";
		if (pageSizeValue) pageStyle += `size:${pageSizeValue};`;
		if (cropMarks) pageStyle += `marks:crop cross;`;
		if (bleed || cropMarks) pageStyle += `bleed:${bleed ?? "3mm"};`;
		if (cropOffset) pageStyle += `crop-offset:${cropOffset};`;
		pageStyle += "}";
		viewerParams += `&style=data:,/*<viewer>*/${encodeURIComponent(pageStyle)}/*</viewer>*/${encodeURIComponent(css ?? "")}`;
	}
	if (cmykReserveMapUrl) viewerParams += `&cmykReserveMap=${escapeParam(cmykReserveMapUrl)}`;
	if (viewerParam) viewerParams += `&${viewerParam}`;
	return viewerParams;
}
async function getSourceUrl({ viewerInput, base, workspaceDir, rootUrl }) {
	let input;
	switch (viewerInput.type) {
		case "webpub":
			input = viewerInput.manifestPath;
			break;
		case "webbook":
			input = viewerInput.webbookEntryUrl;
			break;
		case "epub-opf":
			input = viewerInput.epubOpfPath;
			break;
		case "epub":
			if (!fs.existsSync(viewerInput.epubTmpOutputDir)) await openEpub(viewerInput.epubPath, viewerInput.epubTmpOutputDir);
			input = getDefaultEpubOpfPath(viewerInput.epubTmpOutputDir);
			break;
		default: input = viewerInput;
	}
	return (isValidUri(input) ? new URL$1(input) : new URL$1(upath.posix.join(base, upath.relative(workspaceDir, input)), rootUrl)).href;
}
async function getViewerFullUrl({ viewerInput, base, workspaceDir, rootUrl, viewer, ...config }) {
	const viewerUrl = viewer ? new URL$1(viewer) : new URL$1(`${VIEWER_ROOT_PATH}/index.html`, rootUrl);
	const sourceUrl = await getSourceUrl({
		viewerInput,
		base,
		workspaceDir,
		rootUrl
	});
	const cmykReserveMapUrl = config.outputs.find((o) => o.format === "pdf" && o.cmyk && o.cmyk.reserveMap.length > 0) ? new URL$1(upath.posix.join(base, CMYK_RESERVE_MAP_FILENAME), rootUrl).href : void 0;
	const viewerParams = getViewerParams(sourceUrl === "data:," ? void 0 : sourceUrl, {
		base,
		rootUrl,
		...config
	}, { cmykReserveMapUrl });
	viewerUrl.hash = "";
	return `${viewerUrl.href}#${viewerParams}`;
}
async function createViteServer({ config, viteConfig, inlineConfig, mode }) {
	const viteInlineConfig = {
		clearScreen: false,
		configFile: false,
		appType: "custom",
		plugins: [
			vsDevServerPlugin({
				config,
				inlineConfig
			}),
			vsViewerPlugin({
				config,
				inlineConfig
			}),
			vsBrowserPlugin({
				config,
				inlineConfig
			}),
			vsStaticServePlugin({
				config,
				inlineConfig
			})
		],
		server: viteConfig.server,
		preview: viteConfig.preview,
		customLogger: viteConfig.customLogger,
		cacheDir: viteConfig.cacheDir,
		root: viteConfig.root
	};
	Logger.debug("createViteServer > viteInlineConfig %O", viteInlineConfig);
	if (config.serverRootDir === config.workspaceDir) {
		const { cacheDir } = viteInlineConfig;
		registerCleanupHandler("Removing the Vite cacheDir", () => {
			if (fs.existsSync(cacheDir)) fs.rmSync(cacheDir, { recursive: true });
		});
	}
	const serverLaunch = mode === "preview" ? createServer(viteInlineConfig) : preview(viteInlineConfig);
	let closeServerPromise;
	let closeLaunchedServer;
	const closeServer = () => closeServerPromise ??= serverLaunch.then((server) => {
		closeLaunchedServer ??= server.close.bind(server);
		return closeLaunchedServer();
	});
	registerCleanupHandler("Closing Vite server", closeServer, { prepend: true });
	const server = await serverLaunch;
	closeLaunchedServer = server.close.bind(server);
	server.close = closeServer;
	return server;
}
//#endregion
export { mergeConfig$1 as _, vsViewerPlugin as a, cleanupWorkspace as c, buildWebPublication as d, vsBrowserPlugin as f, resolveTaskConfig as g, isWebPubConfig as h, getViewerFullUrl as i, compile as l, runBrowserOperationWithAbort as m, generateCmykReserveMap as n, vsStaticServePlugin as o, launchPreview as p, getSourceUrl as r, vsDevServerPlugin as s, createViteServer as t, prepareThemeDirectory as u, mergeInlineConfig as v };

//# sourceMappingURL=server-CbkA4XCo.js.map