From 61c0ef52d531e1c76dfd556c3b0b34485fc4825b Mon Sep 17 00:00:00 2001 From: spring-raining Date: Mon, 26 Jun 2023 23:58:56 +0900 Subject: [PATCH 01/42] refactor: Remove epub.ts --- src/config.ts | 10 +++++----- src/epub.ts | 27 --------------------------- src/util.ts | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 32 deletions(-) delete mode 100644 src/epub.ts diff --git a/src/config.ts b/src/config.ts index 879421b1..7facfbb9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -9,20 +9,19 @@ import { pathToFileURL } from 'url'; import { getExecutableBrowserPath } from './browser.js'; import { MANIFEST_FILENAME, TOC_FILENAME, TOC_TITLE } from './const.js'; import { CONTAINER_IMAGE } from './container.js'; -import { openEpubToTmpDirectory } from './epub.js'; import { - detectInputFormat, - detectManuscriptMediaType, InputFormat, ManuscriptMediaType, + detectInputFormat, + detectManuscriptMediaType, } from './input.js'; import { readMarkdownMetadata } from './markdown.js'; import { + OutputFormat, checkOutputFormat, checkPreflightMode, checkRenderMode, detectOutputFormat, - OutputFormat, } from './output.js'; import { vivliostyleConfigSchema } from './schema/vivliostyle.js'; import type { @@ -35,13 +34,14 @@ import type { import { PageSize } from './server.js'; import { parsePackageName } from './theme.js'; import { + DetailError, cwd, debug, - DetailError, filterRelevantAjvErrors, isUrlString, log, logWarn, + openEpubToTmpDirectory, readJSON, statFileSync, touchTmpFile, diff --git a/src/epub.ts b/src/epub.ts deleted file mode 100644 index 26a4dc45..00000000 --- a/src/epub.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { XMLParser } from 'fast-xml-parser'; -import fs from 'node:fs'; -import path from 'upath'; -import { inflateZip, useTmpDirectory } from './util.js'; - -const pickFirstOne = (arg: T | T[]): T => - Array.isArray(arg) ? arg[0] : arg; - -export async function openEpubToTmpDirectory(filePath: string): Promise<{ - dest: string; - epubOpfPath: string; - deleteEpub: () => void; -}> { - const [tmpDir, deleteEpub] = await useTmpDirectory(); - await inflateZip(filePath, tmpDir); - - const containerXmlPath = path.join(tmpDir, 'META-INF/container.xml'); - const xmlParser = new XMLParser({ - ignoreAttributes: false, - }); - const { container } = xmlParser.parse( - fs.readFileSync(containerXmlPath, 'utf8'), - ); - const rootfile = pickFirstOne(container.rootfiles.rootfile); // Only supports a default rendition - const epubOpfPath = path.join(tmpDir, rootfile['@_full-path']); - return { dest: tmpDir, epubOpfPath, deleteEpub }; -} diff --git a/src/util.ts b/src/util.ts index d3654e41..2bb73a57 100644 --- a/src/util.ts +++ b/src/util.ts @@ -2,6 +2,7 @@ import { ErrorObject } from 'ajv'; import chalk from 'chalk'; import debugConstructor from 'debug'; import fastGlob from 'fast-glob'; +import { XMLParser } from 'fast-xml-parser'; import { globby, Options as GlobbyOptions } from 'globby'; import gitIgnore, { Ignore } from 'ignore'; import StreamZip from 'node-stream-zip'; @@ -396,3 +397,23 @@ export async function safeGlob( ]); return result.filter(filter); } + +export async function openEpubToTmpDirectory(filePath: string): Promise<{ + dest: string; + epubOpfPath: string; + deleteEpub: () => void; +}> { + const [tmpDir, deleteEpub] = await useTmpDirectory(); + await inflateZip(filePath, tmpDir); + + const containerXmlPath = upath.join(tmpDir, 'META-INF/container.xml'); + const xmlParser = new XMLParser({ + ignoreAttributes: false, + }); + const { container } = xmlParser.parse( + fs.readFileSync(containerXmlPath, 'utf8'), + ); + const rootfile = [container.rootfiles.rootfile].flat()[0]; // Only supports a default rendition + const epubOpfPath = upath.join(tmpDir, rootfile['@_full-path']); + return { dest: tmpDir, epubOpfPath, deleteEpub }; +} From 19b1dab11128881893d3388dc57455137a74fb18 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Sun, 2 Jul 2023 11:00:59 +0900 Subject: [PATCH 02/42] feat: Add EPUB for output option --- package.json | 14 +- src/build.ts | 16 +- src/config.ts | 13 +- src/const.ts | 11 + src/epub-output.ts | 444 ++++++++++++++++++++++++++++++++++++ src/html.ts | 2 +- src/output.ts | 12 +- src/webbook.ts | 25 ++- yarn.lock | 544 +++++++++++++++++++++++++++++---------------- 9 files changed, 876 insertions(+), 205 deletions(-) create mode 100644 src/epub-output.ts diff --git a/package.json b/package.json index 506cc614..2582592e 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@vivliostyle/viewer": "2.25.2", "ajv": "^8.11.2", "ajv-formats": "^2.1.1", + "archiver": "^5.3.1", "better-ajv-errors": "^1.2.0", "chalk": "^4.1.2", "cheerio": "^1.0.0-rc.10", @@ -36,12 +37,15 @@ "execa": "^5.1.1", "fast-glob": "3.2.12", "fast-xml-parser": "^4.2.4", + "github-slugger": "^1.4.0", "globby": "13.1.2", "hast-util-to-html": "^7.1.3", "hastscript": "^6.0.0", "ignore": "5.2.4", "image-size": "^1.0.0", "is-interactive": "1.0.0", + "jsdom": "^22.1.0", + "language-tags": "^1.0.8", "mime-types": "^2.1.32", "node-stream-zip": "^1.14.0", "ora": "^5.4.1", @@ -58,14 +62,18 @@ "tmp": "^0.2.1", "upath": "^2.0.1", "uuid": "^8.3.2", - "vfile": "^4.2.1" + "vfile": "^4.2.1", + "w3c-xmlserializer": "^4.0.0" }, "devDependencies": { "@release-it/conventional-changelog": "^5.1.1", + "@types/archiver": "^5.3.2", "@types/command-exists": "1.2.0", "@types/debug": "^4.1.7", + "@types/github-slugger": "^1.3.0", "@types/jest": "^29.2.4", - "@types/jsdom": "^16.2.13", + "@types/jsdom": "^21.1.1", + "@types/language-tags": "^1.0.1", "@types/mime-types": "^2.1.1", "@types/node": "^16.7.2", "@types/npm-package-arg": "^6.1.1", @@ -73,10 +81,10 @@ "@types/serve-handler": "^6.1.1", "@types/tmp": "^0.2.1", "@types/uuid": "^8.3.1", + "@types/w3c-xmlserializer": "^2.0.2", "file-type": "^16.5.3", "husky": "^4.3.8", "jest": "^29.3.1", - "jsdom": "^17.0.0", "json-schema-to-typescript": "^10.1.4", "lint-staged": "^11.1.2", "nodemon": "^2.0.12", diff --git a/src/build.ts b/src/build.ts index 24e2a7d6..786ffd38 100644 --- a/src/build.ts +++ b/src/build.ts @@ -11,10 +11,11 @@ import { } from './builder.js'; import { CliFlags, + MergedConfig, collectVivliostyleConfig, mergeConfig, - MergedConfig, } from './config.js'; +import { exportEpub } from './epub-output.js'; import { buildPDF, buildPDFWithContainer } from './pdf.js'; import { teardownServer } from './server.js'; import { @@ -122,6 +123,19 @@ export async function build(cliFlags: BuildCliFlags) { input: config.workspaceDir, outputDir: target.path, }); + } else if (target.format === 'epub') { + const { exportAliases, outputs, manifestPath } = config; + if (!manifestPath) { + continue; + } + await exportEpub({ + exportAliases, + outputs, + input: config.workspaceDir, + manifestPath, + target: target.path, + epubVersion: target.version, + }); } if (output) { const formattedOutput = chalk.bold.green(path.relative(cwd, output)); diff --git a/src/config.ts b/src/config.ts index 7facfbb9..c93d1b23 100644 --- a/src/config.ts +++ b/src/config.ts @@ -7,7 +7,12 @@ import fs from 'fs'; import path from 'upath'; import { pathToFileURL } from 'url'; import { getExecutableBrowserPath } from './browser.js'; -import { MANIFEST_FILENAME, TOC_FILENAME, TOC_TITLE } from './const.js'; +import { + EPUB_OUTPUT_VERSION, + MANIFEST_FILENAME, + TOC_FILENAME, + TOC_TITLE, +} from './const.js'; import { CONTAINER_IMAGE } from './container.js'; import { InputFormat, @@ -559,6 +564,12 @@ export async function mergeConfig( preflight, preflightOption, }; + } else if (format === 'epub') { + return { + path: path.resolve(outputPath), + format, + version: EPUB_OUTPUT_VERSION, + }; } else { return { path: path.resolve(outputPath), diff --git a/src/const.ts b/src/const.ts index fc891462..519756ef 100644 --- a/src/const.ts +++ b/src/const.ts @@ -8,6 +8,17 @@ export const MANIFEST_FILENAME = 'publication.json'; export const TOC_FILENAME = 'index.html'; export const TOC_TITLE = 'Table of Contents'; +export const XML_DECLARATION = ''; +export const EPUB_OUTPUT_VERSION = '3.0'; +export const EPUB_MIMETYPE = 'application/epub+zip'; +export const EPUB_NS = 'http://www.idpf.org/2007/ops'; +export const EPUB_CONTAINER_XML = `${XML_DECLARATION} + + + + +`; + export const cliRoot = path.join(fileURLToPath(import.meta.url), '../..'); export const { version: cliVersion }: { version: string } = readJSON( path.join(cliRoot, 'package.json'), diff --git a/src/epub-output.ts b/src/epub-output.ts new file mode 100644 index 00000000..3526bea6 --- /dev/null +++ b/src/epub-output.ts @@ -0,0 +1,444 @@ +import archiver from 'archiver'; +import { XMLBuilder } from 'fast-xml-parser'; +import GithubSlugger from 'github-slugger'; +import { JSDOM } from 'jsdom'; +import languageTags from 'language-tags'; +import { lookup as mime } from 'mime-types'; +import fs from 'node:fs'; +import url from 'node:url'; +import path from 'upath'; +import { v4 as uuid } from 'uuid'; +import serializeToXml from 'w3c-xmlserializer'; +import { MergedConfig } from './config.js'; +import { + EPUB_CONTAINER_XML, + EPUB_NS, + XML_DECLARATION, + cliRoot, +} from './const.js'; +import { + Contributor, + PublicationLinks, + PublicationManifest, + ResourceCategorization, +} from './schema/publication.schema.js'; +import { debug, safeGlob } from './util.js'; +import { copyWebPublicationAssets } from './webbook.js'; + +interface ManifestEntry { + href: string; + mediaType: string; + properties?: string; +} + +interface LandmarkEntry { + type: string; + href: string; +} + +const changeExtname = (filepath: string, newExt: string) => { + let ext = path.extname(filepath); + return `${filepath.slice(0, -ext.length)}${newExt}`; +}; + +export async function exportEpub({ + exportAliases, + outputs, + input, + manifestPath, + target, + epubVersion, +}: Pick & { + input: string; + manifestPath: string; + target: string; + epubVersion: '3.0'; +}) { + debug('Export EPUB'); + + // const [tmpDir, clearTmpDir] = await useTmpDirectory(); + const tmpDir = path.join(cliRoot, 'tmp'); + fs.mkdirSync(tmpDir, { recursive: true }); + const clearTmpDir = () => {}; + fs.mkdirSync(path.join(tmpDir, 'META-INF'), { recursive: true }); + fs.mkdirSync(path.join(tmpDir, 'EPUB'), { recursive: true }); + + await copyWebPublicationAssets({ + exportAliases, + outputs, + input, + outputDir: path.join(tmpDir, 'EPUB'), + manifestPath, + }); + const manifest = JSON.parse( + fs.readFileSync(path.join(tmpDir, 'EPUB/publication.json'), 'utf8'), + ) as PublicationManifest; + + const htmlFiles = await safeGlob(['**/*.html', '**/*.htm'], { + cwd: path.join(tmpDir, 'EPUB'), + ignore: ['publication.json'], + followSymbolicLinks: false, + gitignore: false, + }); + + const findPublicationLink = ( + relType: string, + list?: ResourceCategorization, + ) => + [list] + .flat() + .find( + (e): e is PublicationLinks => + typeof e === 'object' && e.rel === relType, + ); + const tocResource = + findPublicationLink('contents', manifest.readingOrder) || + findPublicationLink('contents', manifest.resources); + if (!tocResource) { + // TODO: Generate ToC + throw new Error(); + } + const pageListResource = + findPublicationLink('pagelist', manifest.readingOrder) || + findPublicationLink('pagelist', manifest.resources); + const coverResource = findPublicationLink('cover', manifest.resources); + const landmarks: { type: string; href: string }[] = []; + if (coverResource) { + const href = path.extname(coverResource.url).match(/^\.html?$/) + ? changeExtname(coverResource.url, '.xhtml') + : coverResource.url; + landmarks.push({ type: 'cover', href }); + } + + for (const target of htmlFiles) { + debug(`Transpiling HTML to XHTML: ${target}`); + try { + await transpileHtmlToXhtml({ + target, + htmlFiles, + contextDir: path.join(tmpDir, 'EPUB'), + landmarks, + tocResource, + pageListResource, + }); + } catch (error) { + throw new Error(`Failed to transpile document to XHTML: ${target}`); + } + } + + // TODO: Use `resources` property of webpub + const manifestItems = await safeGlob('**', { + cwd: path.join(tmpDir, 'EPUB'), + ignore: ['*.opf'], + followSymbolicLinks: false, + gitignore: false, + }).then((files) => + files.map((href) => { + const mediaType = mime(href); + if (!mediaType) { + throw new Error(`Unknown mediaType: ${href}`); + } + return { + href, + mediaType, + // TODO: Determine `cover-image` item + properties: + href === changeExtname(tocResource.url, '.xhtml') ? 'nav' : undefined, + }; + }), + ); + const readingOrder = [manifest.readingOrder] + .flat() + .filter(Boolean) + .map((e) => (typeof e === 'string' ? e : e!.url)) + .map((p) => (htmlFiles.includes(p) ? changeExtname(p, '.xhtml') : p)); + + // META-INF/container.xml + fs.writeFileSync( + path.join(tmpDir, 'META-INF/container.xml'), + EPUB_CONTAINER_XML, + 'utf8', + ); + + // EPUB/content.opf + fs.writeFileSync( + path.join(tmpDir, 'EPUB/content.opf'), + buildEpubPackageDocument({ + epubVersion, + manifest, + readingOrder, + manifestItems, + landmarks, + }), + 'utf8', + ); + + await compressEpub({ target, sourceDir: tmpDir }); + clearTmpDir(); +} + +async function transpileHtmlToXhtml({ + target, + htmlFiles, + contextDir, + landmarks, + tocResource, + pageListResource, +}: { + target: string; + htmlFiles: string[]; + contextDir: string; + landmarks: LandmarkEntry[]; + tocResource: PublicationLinks; + pageListResource?: PublicationLinks; +}): Promise { + const absPath = path.join(contextDir, target); + const htmlFileUrls = htmlFiles.map((p) => + url.pathToFileURL(path.join(contextDir, p)), + ); + const html = await fs.promises.readFile(absPath, 'utf8'); + const dom = new JSDOM(html); + const { document } = dom.window; + const htmlElement = document.body.parentElement!; + htmlElement.setAttribute('xmlns:epub', EPUB_NS); + + document.querySelectorAll('a[href]').forEach((el) => { + const href = el.getAttribute('href')!; + const hrefUrl = new URL(href, url.pathToFileURL(absPath)); + if ( + htmlFileUrls.some( + (url) => + url.pathname === hrefUrl.pathname || + changeExtname(url.pathname, '') === hrefUrl.pathname, + ) + ) { + hrefUrl.pathname = changeExtname(hrefUrl.pathname, '.xhtml'); + } + const pathname = path.posix.relative( + url.pathToFileURL(path.dirname(absPath)).pathname, + hrefUrl.pathname, + ); + el.setAttribute('href', `${pathname}${hrefUrl.search}${hrefUrl.hash}`); + }); + + const replaceWithNavElement = (el: Element) => { + const nav = document.createElement('nav'); + while (el.firstChild) { + nav.appendChild(el.firstChild); + } + for (let i = 0; i < el.attributes.length; i++) { + nav.attributes.setNamedItem(el.attributes[i].cloneNode() as Attr); + } + el.parentNode?.replaceChild(nav, el); + return nav; + }; + + if (target === tocResource.url) { + if (!document.querySelector('[epub:type="toc"]')) { + const tocRoot = document.querySelectorAll('[role="doc-toc"]'); + if (tocRoot.length !== 1) { + throw new Error('Navigation document must have one "toc" nav element'); + } + tocRoot.forEach((el) => { + const nav = replaceWithNavElement(el); + nav.setAttribute('id', 'toc'); + nav.setAttribute('epub:type', 'toc'); + }); + } + + if ( + landmarks.length > 0 && + !document.querySelector('[epub:type="landmarks"]') + ) { + const nav = document.createElement('nav'); + nav.setAttribute('epub:type', 'landmarks'); + nav.setAttribute('id', 'landmarks'); + nav.setAttribute('hidden', ''); + const ol = document.createElement('ol'); + for (const { type, href } of landmarks) { + const li = document.createElement('li'); + const a = document.createElement('a'); + a.setAttribute('epub:type', type); + a.setAttribute('href', href); + li.appendChild(a); + ol.appendChild(li); + } + nav.appendChild(ol); + document.body.appendChild(nav); + } + } + + if (target === pageListResource?.url) { + document.querySelectorAll('[role="doc-pagelist"]').forEach((el) => { + const nav = replaceWithNavElement(el); + nav.setAttribute('id', 'page-list'); + nav.setAttribute('epub:type', 'page-list'); + }); + } + + const xhtml = `${XML_DECLARATION}\n${serializeToXml(document)}`; + await fs.promises.writeFile(changeExtname(absPath, '.xhtml'), xhtml, 'utf8'); + await fs.promises.unlink(absPath); + return changeExtname(absPath, '.xhtml'); +} + +function buildEpubPackageDocument({ + epubVersion, + manifest, + readingOrder, + manifestItems, + landmarks, +}: Pick[0], 'epubVersion'> & { + manifest: PublicationManifest; + readingOrder: string[]; + manifestItems: ManifestEntry[]; + landmarks: LandmarkEntry[]; +}): string { + const slugger = new GithubSlugger(); + slugger.reset(); + + const bookIdentifier = slugger.slug('bookid'); + const formattedLang = languageTags( + [manifest.inLanguage ?? 'en'].flat()[0], + ).format(); + if (!languageTags(formattedLang).valid()) { + throw languageTags(formattedLang).errors()[0]; + } + const normalizeDate = (value: string | number | undefined) => + value && `${new Date(value).toISOString().split('.')[0]}Z`; + + const transformToGenericTextNode = (value: unknown, attributes?: T) => + [value] + .flat() + .filter(Boolean) + .map((v) => ({ ...(attributes || {}), '#text': `${value}` })); + const transformContributor = ( + contributorMap: Record, + ) => + Object.entries(contributorMap).flatMap(([type, contributor]) => + contributor + ? [contributor].flat().map((entry, index) => ({ + _id: slugger.slug(`${type}-${index + 1}`), + '#text': typeof entry === 'string' ? entry : entry.name, + })) + : [], + ); + + const itemIdMap = new Map(); + manifestItems.forEach(({ href }) => { + itemIdMap.set(href, slugger.slug(href)); + }); + + const builder = new XMLBuilder({ + format: true, + ignoreAttributes: false, + attributeNamePrefix: '_', + }); + return builder.build({ + '?xml': { + _version: '1.0', + _encoding: 'UTF-8', + }, + package: { + _xmlns: 'http://www.idpf.org/2007/opf', + _version: epubVersion, + '_unique-identifier': bookIdentifier, + '_xml:lang': formattedLang, + metadata: { + '_xmlns:dc': 'http://purl.org/dc/elements/1.1/', + 'dc:identifier': { + _id: bookIdentifier, + '#text': `urn:uuid:${uuid()}`, + }, + 'dc:title': manifest.name, + 'dc:language': formattedLang, + 'dc:creator': transformContributor({ + // TODO: Define proper order + 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 ? `${manifest.copyrightYear} ` : ''}${ + manifest.copyrightHolder + }`, + ), + 'dc:subject': transformToGenericTextNode( + manifest['dc:subject'] || manifest.subject, + ), + meta: transformToGenericTextNode( + normalizeDate(manifest.dateModified || Date.now()), + { + _property: 'dcterms:modified', + }, + ), + }, + manifest: { + item: manifestItems.map(({ href, mediaType, properties }) => ({ + _id: itemIdMap.get(href), + _href: href, + '_media-type': mediaType, + ...(properties ? { _properties: properties } : {}), + })), + }, + spine: { + itemref: readingOrder.map((href) => ({ + _idref: itemIdMap.get(href), + })), + }, + guide: { + reference: [ + { + _type: 'toc', + _href: manifestItems.find(({ properties }) => properties === 'nav')! + .href, + }, + ...landmarks.map(({ type, href }) => ({ _type: type, _href: href })), + ], + }, + }, + }); +} + +async function compressEpub({ + target, + sourceDir, +}: { + target: string; + sourceDir: string; +}): Promise { + const output = fs.createWriteStream(target); + const archive = archiver('zip', { + store: true, + }); + return new Promise((resolve, reject) => { + output.on('close', resolve); + output.on('error', reject); + archive.on('warning', reject); + archive.on('error', reject); + archive.pipe(output); + + archive.append('application/epub+zip', { name: 'mimetype' }); + archive.directory(path.join(sourceDir, 'META-INF'), 'META-INF'); + archive.directory(path.join(sourceDir, 'EPUB'), 'EPUB'); + archive.finalize(); + }); +} diff --git a/src/html.ts b/src/html.ts index 4c2f2eee..10202c55 100644 --- a/src/html.ts +++ b/src/html.ts @@ -1,7 +1,7 @@ import cheerio from 'cheerio'; -import fs from 'node:fs'; import toHTML from 'hast-util-to-html'; import h from 'hastscript'; +import fs from 'node:fs'; import prettier from 'prettier'; import path from 'upath'; import { ManuscriptEntry } from './config.js'; diff --git a/src/output.ts b/src/output.ts index 287deba4..3dd56c8b 100644 --- a/src/output.ts +++ b/src/output.ts @@ -17,9 +17,15 @@ export interface WebPublicationOutput extends OutputFormatTrait<'webpub'> { path: string; } -export type OutputFormat = PdfOutput | WebPublicationOutput; +/** A single file of EPUB */ +export interface EpubOutput extends OutputFormatTrait<'epub'> { + path: string; + version: '3.0'; // Reserved for future updates +} + +export type OutputFormat = PdfOutput | WebPublicationOutput | EpubOutput; export const checkOutputFormat = (v: unknown): v is OutputFormat['format'] => { - return ['pdf', 'webpub'].includes(v as string); + return ['pdf', 'webpub', 'epub'].includes(v as string); }; export const checkRenderMode = (v: unknown): v is PdfOutput['renderMode'] => { @@ -34,6 +40,8 @@ export function detectOutputFormat(outputPath: string): OutputFormat['format'] { const lowerCasedExt = path.extname(outputPath); if (lowerCasedExt === '.pdf') { return 'pdf'; + } else if (lowerCasedExt === '.epub') { + return 'epub'; } else { return 'webpub'; } diff --git a/src/webbook.ts b/src/webbook.ts index da93ef19..ff2fdf78 100644 --- a/src/webbook.ts +++ b/src/webbook.ts @@ -8,7 +8,7 @@ import type { } from './schema/publication.schema.js'; import { debug, pathContains, pathEquals, safeGlob } from './util.js'; -export async function exportWebPublication({ +export async function copyWebPublicationAssets({ exportAliases, outputs, manifestPath, @@ -18,11 +18,7 @@ export async function exportWebPublication({ input: string; outputDir: string; manifestPath: string; -}): Promise { - if (fs.existsSync(outputDir)) { - debug('going to remove existing webpub', outputDir); - shelljs.rm('-rf', outputDir); - } +}) { const silentMode = shelljs.config.silent; shelljs.config.silent = true; try { @@ -111,10 +107,25 @@ export async function exportWebPublication({ } fs.writeFileSync(actualManifestPath, JSON.stringify(manifest, null, 2)); } catch (err) { - shelljs.rm('-rf', outputDir); throw err; } finally { shelljs.config.silent = silentMode; } +} + +export async function exportWebPublication( + params: Parameters[0], +): Promise { + const { outputDir } = params; + if (fs.existsSync(outputDir)) { + debug('going to remove existing webpub', outputDir); + shelljs.rm('-rf', outputDir); + } + try { + await copyWebPublicationAssets(params); + } catch (err) { + shelljs.rm('-rf', outputDir); + throw err; + } return outputDir; } diff --git a/yarn.lock b/yarn.lock index 5ed5f51b..12232a6b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1118,6 +1118,13 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== +"@types/archiver@^5.3.2": + version "5.3.2" + resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-5.3.2.tgz#a9f0bcb0f0b991400e7766d35f6e19d163bdadcc" + integrity sha512-IctHreBuWE5dvBDz/0WeKtyVKVRs4h75IblxOACL92wU66v+HGAfEYAOyXkOFphvRJMhuXdI9huDXpX0FC6lCw== + dependencies: + "@types/readdir-glob" "*" + "@types/babel__core@^7.1.14": version "7.1.15" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024" @@ -1175,6 +1182,11 @@ dependencies: "@types/ms" "*" +"@types/github-slugger@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/github-slugger/-/github-slugger-1.3.0.tgz#16ab393b30d8ae2a111ac748a015ac05a1fc5524" + integrity sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g== + "@types/glob@*": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" @@ -1229,20 +1241,25 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/jsdom@^16.2.13": - version "16.2.13" - resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.13.tgz#126c8b7441b159d6234610a48de77b6066f1823f" - integrity sha512-8JQCjdeAidptSsOcRWk2iTm9wCcwn9l+kRG6k5bzUacrnm1ezV4forq0kWjUih/tumAeoG+OspOvQEbbRucBTw== +"@types/jsdom@^21.1.1": + version "21.1.1" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-21.1.1.tgz#e59e26352071267b507bf04d51841a1d7d3e8617" + integrity sha512-cZFuoVLtzKP3gmq9eNosUL1R50U+USkbLtUQ1bYVgl/lKp0FZM7Cq4aIHAL8oIvQ17uSHi7jXPtfDOdjPwBE7A== dependencies: "@types/node" "*" - "@types/parse5" "*" "@types/tough-cookie" "*" + parse5 "^7.0.0" "@types/json-schema@^7.0.6": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== +"@types/language-tags@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/language-tags/-/language-tags-1.0.1.tgz#58e04ca5a27031b8cb7c0c91eec653f245b6bde9" + integrity sha512-rTtRNIewaBrkMUfsCe7ES3xsTRQcEVgic2yoDY9hM3D/nwmABcG2du4l4+dTbWvfO8pUYwL4/2TbWFJa/AGc2g== + "@types/lodash@^4.14.168": version "4.14.168" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008" @@ -1354,7 +1371,7 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/parse5@*", "@types/parse5@^5.0.0": +"@types/parse5@^5.0.0": version "5.0.3" resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== @@ -1372,6 +1389,13 @@ "@types/node" "*" safe-buffer "*" +"@types/readdir-glob@*": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/readdir-glob/-/readdir-glob-1.1.1.tgz#27ac2db283e6aa3d110b14ff9da44fcd1a5c38b1" + integrity sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ== + dependencies: + "@types/node" "*" + "@types/serve-handler@^6.1.1": version "6.1.1" resolved "https://registry.yarnpkg.com/@types/serve-handler/-/serve-handler-6.1.1.tgz#629dc9a62b201ab79a216e1e46e162aa4c8d1455" @@ -1424,6 +1448,11 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f" integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== +"@types/w3c-xmlserializer@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@types/w3c-xmlserializer/-/w3c-xmlserializer-2.0.2.tgz#79ab833574bdfac3d03eadb4179508ae89d6bf89" + integrity sha512-lSw+ZP+r+SQLuQpY1BIAyYUIOd5YBOVg5VY8psTCcHOoDBxVbUPSZZOkGQfssrrWeTJs0tKi5cWhYCKj8gmM5w== + "@types/yargs-parser@*": version "15.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" @@ -1506,10 +1535,10 @@ JSONStream@^1.0.4: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.3, abab@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" - integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== abbrev@1, abbrev@^1.0.0: version "1.1.1" @@ -1528,34 +1557,11 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - -acorn-walk@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" - integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== - acorn-walk@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.4.1: - version "8.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" - integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== - acorn@^8.7.0: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" @@ -1705,6 +1711,35 @@ anymatch@~3.1.2: resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== +archiver-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" + integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== + dependencies: + glob "^7.1.4" + graceful-fs "^4.2.0" + lazystream "^1.0.0" + lodash.defaults "^4.2.0" + lodash.difference "^4.5.0" + lodash.flatten "^4.4.0" + lodash.isplainobject "^4.0.6" + lodash.union "^4.6.0" + normalize-path "^3.0.0" + readable-stream "^2.0.0" + +archiver@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.1.tgz#21e92811d6f09ecfce649fbefefe8c79e57cbbb6" + integrity sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w== + dependencies: + archiver-utils "^2.1.0" + async "^3.2.3" + buffer-crc32 "^0.2.1" + readable-stream "^3.6.0" + readdir-glob "^1.0.0" + tar-stream "^2.2.0" + zip-stream "^4.1.0" + are-we-there-yet@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" @@ -1795,6 +1830,11 @@ async@^2.6.2: dependencies: lodash "^4.17.14" +async@^3.2.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1916,7 +1956,7 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bl@^4.1.0: +bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== @@ -1975,11 +2015,6 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - browserslist@^4.21.3: version "4.21.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" @@ -2004,6 +2039,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -2488,6 +2528,16 @@ compare-versions@^3.6.0: resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== +compress-commons@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" + integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ== + dependencies: + buffer-crc32 "^0.2.13" + crc32-stream "^4.0.2" + normalize-path "^3.0.0" + readable-stream "^3.6.0" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2728,6 +2778,19 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +crc32-stream@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007" + integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w== + dependencies: + crc-32 "^1.2.0" + readable-stream "^3.4.0" + cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -2781,22 +2844,12 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssom@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" - integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== +cssstyle@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" + integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg== dependencies: - cssom "~0.3.6" + rrweb-cssom "^0.6.0" d@1, d@^1.0.1: version "1.0.1" @@ -2821,14 +2874,14 @@ data-uri-to-buffer@^4.0.0: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== -data-urls@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.0.tgz#3ff551c986d7c6234a0ac4bbf20a269e1cd6b378" - integrity sha512-4AefxbTTdFtxDUdh0BuMBs2qJVL25Mow2zlcuuePegQwgD6GEmQao42LLEeksOui8nL4RcNEugIpFP7eRd33xg== +data-urls@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" + integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g== dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^9.0.0" + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.0" dateformat@^3.0.0: version "3.0.3" @@ -2881,10 +2934,10 @@ decamelize@^1.1.0, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -decimal.js@^10.3.1: - version "10.3.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== +decimal.js@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== decompress-response@^6.0.0: version "6.0.0" @@ -3035,12 +3088,12 @@ domelementtype@^2.2.0: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== dependencies: - webidl-conversions "^5.0.0" + webidl-conversions "^7.0.0" domhandler@^4.0.0: version "4.0.0" @@ -3120,7 +3173,7 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.1.0: +end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -3139,6 +3192,11 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + env-paths@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -3329,18 +3387,6 @@ escodegen@^1.8.1: optionalDependencies: source-map "~0.6.1" -escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -3351,11 +3397,6 @@ estraverse@^4.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -3651,6 +3692,11 @@ formdata-polyfill@^4.0.10: dependencies: fetch-blob "^3.1.2" +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -4241,12 +4287,12 @@ html-element-attributes@^2.0.0: resolved "https://registry.yarnpkg.com/html-element-attributes/-/html-element-attributes-2.3.0.tgz#a192ac90a457be9f1e2cc9ab69000ee89be74aa6" integrity sha512-RJv2v3BBaYSc0ODHwT0sqWI+2lFs6DATBvCRnW20BDmULxoAWvfT6r28uL8LcW1a9/eqUl+1DccUOJzw00qVXQ== -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== dependencies: - whatwg-encoding "^1.0.5" + whatwg-encoding "^2.0.0" html-escaper@^2.0.0: version "2.0.2" @@ -4315,7 +4361,7 @@ http2-wrapper@^2.1.10: quick-lru "^5.1.1" resolve-alpn "^1.2.0" -https-proxy-agent@5: +https-proxy-agent@5, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -4388,7 +4434,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2: +iconv-lite@0.6.3, iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -5361,38 +5407,34 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsdom@^17.0.0: - version "17.0.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-17.0.0.tgz#3ec82d1d30030649c8defedc45fff6aa3e5d06ae" - integrity sha512-MUq4XdqwtNurZDVeKScENMPHnkgmdIvMzZ1r1NSwHkDuaqI6BouPjr+17COo4/19oLNnmdpFDPOHVpgIZmZ+VA== - dependencies: - abab "^2.0.5" - acorn "^8.4.1" - acorn-globals "^6.0.0" - cssom "^0.5.0" - cssstyle "^2.3.0" - data-urls "^3.0.0" - decimal.js "^10.3.1" - domexception "^2.0.1" - escodegen "^2.0.0" +jsdom@^22.1.0: + version "22.1.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.1.0.tgz#0fca6d1a37fbeb7f4aac93d1090d782c56b611c8" + integrity sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw== + dependencies: + abab "^2.0.6" + cssstyle "^3.0.0" + data-urls "^4.0.0" + decimal.js "^10.4.3" + domexception "^4.0.0" form-data "^4.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" + nwsapi "^2.2.4" + parse5 "^7.1.2" + rrweb-cssom "^0.6.0" + saxes "^6.0.0" symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^9.0.0" - ws "^8.0.0" - xml-name-validator "^3.0.0" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.1" + ws "^8.13.0" + xml-name-validator "^4.0.0" jsesc@^2.5.1: version "2.5.2" @@ -5516,6 +5558,18 @@ knockout@^3.5.0: resolved "https://registry.yarnpkg.com/knockout/-/knockout-3.5.1.tgz#62c81e81843bea2008fd23c575edd9ca978e75cf" integrity sha512-wRJ9I4az0QcsH7A4v4l0enUpkS++MBx0BnL/68KaLzJg7x1qmbjSlwEoCNol7KTYZ+pmtI7Eh2J0Nu6/2Z5J/Q== +language-subtag-registry@^0.3.20: + version "0.3.22" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" + integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== + +language-tags@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.8.tgz#042b4bdb0d4e771a9f8cc2fdc9bb26a52a367312" + integrity sha512-aWAZwgPLS8hJ20lNPm9HNVs4inexz6S2sQa3wx/+ycuutMNE5/IfYxiWYBbi+9UWCQVaXYCOPUl6gFrPR7+jGg== + dependencies: + language-subtag-registry "^0.3.20" + latest-version@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-7.0.0.tgz#843201591ea81a4d404932eeb61240fe04e9e5da" @@ -5523,6 +5577,13 @@ latest-version@^7.0.0: dependencies: package-json "^8.1.0" +lazystream@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" + integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== + dependencies: + readable-stream "^2.0.5" + leven@^3.1.0, "leven@^3.1.0 < 4": version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -5606,16 +5667,41 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== + +lodash.difference@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" + integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA== + +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== + lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g== +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + lodash.memoize@4.x: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== +lodash.union@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" + integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== + lodash@4.17.21, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -6398,10 +6484,10 @@ nth-check@^2.0.0: dependencies: boolbase "^1.0.0" -nwsapi@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== +nwsapi@^2.2.4: + version "2.2.5" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.5.tgz#a52744c61b3889dd44b0a158687add39b8d935e2" + integrity sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ== object-assign@^4.0.1: version "4.1.1" @@ -6726,11 +6812,18 @@ parse5-htmlparser2-tree-adapter@^6.0.1: dependencies: parse5 "^6.0.1" -parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: +parse5@^6.0.0, parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parse5@^7.0.0, parse5@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -7069,6 +7162,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +punycode@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + pupa@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/pupa/-/pupa-3.1.0.tgz#f15610274376bbcc70c9a3aa8b505ea23f41c579" @@ -7081,6 +7179,11 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -7211,6 +7314,28 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^2.0.0, readable-stream@^2.0.5: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readable-stream@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.2.0.tgz#a7ef523d3b39e4962b0db1a1af22777b10eeca46" @@ -7242,6 +7367,13 @@ readable-web-to-node-stream@^3.0.0: "@types/readable-stream" "^2.3.9" readable-stream "^3.6.0" +readdir-glob@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" + integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== + dependencies: + minimatch "^5.1.0" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -7452,6 +7584,11 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + resolve-alpn@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" @@ -7548,6 +7685,11 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rrweb-cssom@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" + integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== + run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -7598,10 +7740,10 @@ safe-regex-test@^1.0.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== dependencies: xmlchars "^2.2.0" @@ -8131,6 +8273,17 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +tar-stream@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + tar@^6.1.11, tar@^6.1.2: version "6.1.12" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.12.tgz#3b742fb05669b55671fb769ab67a7791ea1a62e6" @@ -8266,21 +8419,22 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== +tough-cookie@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== dependencies: psl "^1.1.33" punycode "^2.1.1" - universalify "^0.1.2" + universalify "^0.2.0" + url-parse "^1.5.3" -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== +tr46@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" + integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw== dependencies: - punycode "^2.1.1" + punycode "^2.3.0" tr46@~0.0.3: version "0.0.3" @@ -8597,11 +8751,16 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== -universalify@^0.1.0, universalify@^0.1.2: +universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -8657,6 +8816,14 @@ url-join@5.0.0: resolved "https://registry.yarnpkg.com/url-join/-/url-join-5.0.0.tgz#c2f1e5cbd95fa91082a93b58a1f42fecb4bdbcf1" integrity sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA== +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -8722,19 +8889,12 @@ vm2@^3.9.8: acorn "^8.7.0" acorn-walk "^8.2.0" -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== dependencies: - xml-name-validator "^3.0.0" + xml-name-validator "^4.0.0" walk-up-path@^1.0.0: version "1.0.0" @@ -8770,27 +8930,30 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== dependencies: - iconv-lite "0.4.24" + iconv-lite "0.6.3" -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^12.0.0, whatwg-url@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" + integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ== + dependencies: + tr46 "^4.1.1" + webidl-conversions "^7.0.0" whatwg-url@^5.0.0: version "5.0.0" @@ -8800,14 +8963,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-9.1.0.tgz#1b112cf237d72cd64fa7882b9c3f6234a1c3050d" - integrity sha512-CQ0UcrPHyomtlOCot1TL77WyMIm/bCwrJ2D6AOKGwEczU9EpyoqAokfqrf/MioU9kHcMsmJZcg1egXix2KYEsA== - dependencies: - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -8956,20 +9111,20 @@ write-file-atomic@^5.0.0: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@^8.0.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.0.tgz#0b738cd484bfc9303421914b11bb4011e07615bb" - integrity sha512-uYhVJ/m9oXwEI04iIVmgLmugh2qrZihkywG9y5FfZV2ATeLIzHf93qs+tUNqlttbQK957/VX3mtwAS+UfIwA4g== +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== xdg-basedir@^5.0.1, xdg-basedir@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-5.1.0.tgz#1efba19425e73be1bc6f2a6ceb52a3d2c884c0c9" integrity sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ== -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== xmlchars@^2.2.0: version "2.2.0" @@ -9077,6 +9232,15 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +zip-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" + integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A== + dependencies: + archiver-utils "^2.1.0" + compress-commons "^4.1.0" + readable-stream "^3.6.0" + zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" From e8b5982d774c22a3812622e27f104f306041a773 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Tue, 4 Jul 2023 02:26:33 +0900 Subject: [PATCH 03/42] chore: Implement toc.ncx output --- src/epub-output.ts | 140 +++++++++++++++++++++++++++++++++++++++------ src/html.ts | 122 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+), 16 deletions(-) diff --git a/src/epub-output.ts b/src/epub-output.ts index 3526bea6..8f9ca1d8 100644 --- a/src/epub-output.ts +++ b/src/epub-output.ts @@ -16,13 +16,20 @@ import { XML_DECLARATION, cliRoot, } from './const.js'; +import { + PageListResourceTreeRoot, + TocResourceTreeItem, + TocResourceTreeRoot, + parsePageListDocument, + parseTocDocument, +} from './html.js'; import { Contributor, PublicationLinks, PublicationManifest, ResourceCategorization, } from './schema/publication.schema.js'; -import { debug, safeGlob } from './util.js'; +import { DetailError, debug, safeGlob } from './util.js'; import { copyWebPublicationAssets } from './webbook.js'; interface ManifestEntry { @@ -73,6 +80,7 @@ export async function exportEpub({ const manifest = JSON.parse( fs.readFileSync(path.join(tmpDir, 'EPUB/publication.json'), 'utf8'), ) as PublicationManifest; + const uid = `urn:uuid:${uuid()}`; const htmlFiles = await safeGlob(['**/*.html', '**/*.htm'], { cwd: path.join(tmpDir, 'EPUB'), @@ -112,8 +120,9 @@ export async function exportEpub({ for (const target of htmlFiles) { debug(`Transpiling HTML to XHTML: ${target}`); + let parseResult: Resolved>; try { - await transpileHtmlToXhtml({ + parseResult = await transpileHtmlToXhtml({ target, htmlFiles, contextDir: path.join(tmpDir, 'EPUB'), @@ -122,7 +131,20 @@ export async function exportEpub({ pageListResource, }); } catch (error) { - throw new Error(`Failed to transpile document to XHTML: ${target}`); + const thrownError = error as Error; + throw new DetailError( + `Failed to transpile document to XHTML: ${target}`, + thrownError.stack ?? thrownError.message, + ); + } + if (parseResult.tocParseTree) { + debug(`Generating toc.ncx`); + + fs.writeFileSync( + path.join(tmpDir, 'EPUB/toc.ncx'), + buildNcx({ toc: parseResult.tocParseTree, manifest, uid }), + 'utf8', + ); } } @@ -161,10 +183,12 @@ export async function exportEpub({ ); // EPUB/content.opf + debug(`Generating content.opf`); fs.writeFileSync( path.join(tmpDir, 'EPUB/content.opf'), buildEpubPackageDocument({ epubVersion, + uid, manifest, readingOrder, manifestItems, @@ -191,7 +215,10 @@ async function transpileHtmlToXhtml({ landmarks: LandmarkEntry[]; tocResource: PublicationLinks; pageListResource?: PublicationLinks; -}): Promise { +}): Promise<{ + tocParseTree?: TocResourceTreeRoot; + pageListParseTree?: PageListResourceTreeRoot; +}> { const absPath = path.join(contextDir, target); const htmlFileUrls = htmlFiles.map((p) => url.pathToFileURL(path.join(contextDir, p)), @@ -233,17 +260,19 @@ async function transpileHtmlToXhtml({ return nav; }; + let tocParseTree: TocResourceTreeRoot | undefined; + let pageListParseTree: PageListResourceTreeRoot | undefined; + if (target === tocResource.url) { if (!document.querySelector('[epub:type="toc"]')) { - const tocRoot = document.querySelectorAll('[role="doc-toc"]'); - if (tocRoot.length !== 1) { + const parsed = parseTocDocument(dom); + if (!parsed) { throw new Error('Navigation document must have one "toc" nav element'); } - tocRoot.forEach((el) => { - const nav = replaceWithNavElement(el); - nav.setAttribute('id', 'toc'); - nav.setAttribute('epub:type', 'toc'); - }); + tocParseTree = parsed; + const nav = replaceWithNavElement(parsed.element); + nav.setAttribute('id', 'toc'); + nav.setAttribute('epub:type', 'toc'); } if ( @@ -269,27 +298,34 @@ async function transpileHtmlToXhtml({ } if (target === pageListResource?.url) { - document.querySelectorAll('[role="doc-pagelist"]').forEach((el) => { - const nav = replaceWithNavElement(el); + const parsed = parsePageListDocument(dom); + if (parsed) { + pageListParseTree = parsed; + const nav = replaceWithNavElement(parsed.element); nav.setAttribute('id', 'page-list'); nav.setAttribute('epub:type', 'page-list'); - }); + } } const xhtml = `${XML_DECLARATION}\n${serializeToXml(document)}`; await fs.promises.writeFile(changeExtname(absPath, '.xhtml'), xhtml, 'utf8'); await fs.promises.unlink(absPath); - return changeExtname(absPath, '.xhtml'); + return { + tocParseTree, + pageListParseTree, + }; } function buildEpubPackageDocument({ epubVersion, manifest, + uid, readingOrder, manifestItems, landmarks, }: Pick[0], 'epubVersion'> & { manifest: PublicationManifest; + uid: string; readingOrder: string[]; manifestItems: ManifestEntry[]; landmarks: LandmarkEntry[]; @@ -348,7 +384,7 @@ function buildEpubPackageDocument({ '_xmlns:dc': 'http://purl.org/dc/elements/1.1/', 'dc:identifier': { _id: bookIdentifier, - '#text': `urn:uuid:${uuid()}`, + '#text': uid, }, 'dc:title': manifest.name, 'dc:language': formattedLang, @@ -400,6 +436,10 @@ function buildEpubPackageDocument({ })), }, spine: { + ...(() => { + const toc = manifestItems.find(({ href }) => href === 'toc.ncx'); + return toc ? { _toc: itemIdMap.get(toc.href) } : {}; + })(), itemref: readingOrder.map((href) => ({ _idref: itemIdMap.get(href), })), @@ -418,6 +458,74 @@ function buildEpubPackageDocument({ }); } +function buildNcx({ + toc, + manifest, + uid, +}: { + toc: TocResourceTreeRoot; + manifest: PublicationManifest; + uid: string; +}): string { + const slugger = new GithubSlugger(); + slugger.reset(); + // Dummy incremental to increase sequential counts + slugger.slug('navPoint'); + + const transformNavItem = ( + item: TocResourceTreeItem, + ): Record => { + return { + _id: slugger.slug('navPoint'), + navLabel: { + text: (item.label.textContent ?? '').trim(), + }, + ...(item.label.tagName === 'A' && item.label.getAttribute('href') + ? { + content: { + _src: item.label.getAttribute('href'), + }, + } + : {}), + ...(item.children && item.children.length > 0 + ? { + navPoint: item.children.map(transformNavItem), + } + : {}), + }; + }; + + const builder = new XMLBuilder({ + format: true, + ignoreAttributes: false, + attributeNamePrefix: '_', + }); + return builder.build({ + '?xml': { + _version: '1.0', + _encoding: 'UTF-8', + }, + ncx: { + _xmlns: 'http://www.daisy.org/z3986/2005/ncx/', + _version: '2005-1', + head: { + meta: [ + { _name: 'dtb:uid', _content: uid }, + { _name: 'dtb:depth', _content: '1' }, + { _name: 'dtb:totalPageCount', _content: '0' }, + { _name: 'dtb:maxPageNumber', _content: '0' }, + ], + }, + docTitle: { + text: manifest.name, + }, + navMap: { + navPoint: toc.children.map(transformNavItem), + }, + }, + }); +} + async function compressEpub({ target, sourceDir, diff --git a/src/html.ts b/src/html.ts index 10202c55..0d0f3354 100644 --- a/src/html.ts +++ b/src/html.ts @@ -1,6 +1,7 @@ import cheerio from 'cheerio'; import toHTML from 'hast-util-to-html'; import h from 'hastscript'; +import { JSDOM } from 'jsdom'; import fs from 'node:fs'; import prettier from 'prettier'; import path from 'upath'; @@ -108,3 +109,124 @@ export function isTocHtml(filepath: string): boolean { return false; } } + +export type TocResourceTreeItem = { + element: HTMLElement; + label: HTMLElement; + children?: TocResourceTreeItem[]; +}; +export type TocResourceTreeRoot = { + element: HTMLElement; + heading?: HTMLElement; + children: TocResourceTreeItem[]; +}; + +export function parseTocDocument(dom: JSDOM): TocResourceTreeRoot | null { + 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: Element): TocResourceTreeItem | null => { + 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: element as HTMLElement, label: label as HTMLElement }; + } + const children = Array.from(ol.children).reduce< + TocResourceTreeItem[] | null + >((acc, val) => { + if (!acc) { + return acc; + } + const res = parseTocItem(val); + return res && [...acc, res]; + }, []); + return ( + children && { + element: element as HTMLElement, + label: label as HTMLElement, + children, + } + ); + }; + + let heading: HTMLElement | undefined; + for (let child of Array.from(tocRoot.children)) { + if (child.tagName === 'OL') { + const children = Array.from(child.children).reduce< + TocResourceTreeItem[] | null + >((acc, val) => { + if (!acc) { + return acc; + } + const res = parseTocItem(val); + return res && [...acc, res]; + }, []); + return children && { element: tocRoot as HTMLElement, heading, children }; + } else if ( + ['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HGROUP'].includes(child.tagName) + ) { + heading = child as HTMLElement; + } else { + return null; + } + } + return null; +} + +export type PageListResourceTreeItem = { + element: HTMLElement; +}; +export type PageListResourceTreeRoot = { + element: HTMLElement; + heading?: HTMLElement; + children: PageListResourceTreeItem[]; +}; + +export function parsePageListDocument( + dom: JSDOM, +): PageListResourceTreeRoot | null { + const { document } = dom.window; + + const docPageListEl = document.querySelectorAll('[role="doc-pagelist"]'); + if (docPageListEl.length === 0) { + return null; + } + const pageListRoot = docPageListEl.item(0); + + let heading: HTMLElement | undefined; + for (let child of Array.from(pageListRoot.children)) { + if (child.tagName === 'OL') { + const children = Array.from(child.children).reduce< + PageListResourceTreeItem[] | null + >((acc, element) => { + return ( + acc && + (element.tagName === 'LI' + ? [...acc, { element: element as HTMLElement }] + : null) + ); + }, []); + return ( + children && { element: pageListRoot as HTMLElement, heading, children } + ); + } else if ( + ['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HGROUP'].includes(child.tagName) + ) { + heading = child as HTMLElement; + } else { + return null; + } + } + return null; +} From 3d6fa6a2e1dbf09ae8da9f66d8830836f0553f51 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Wed, 5 Jul 2023 01:20:15 +0900 Subject: [PATCH 04/42] fix: Set resources property for web publication manifest --- src/epub-output.ts | 111 +++++++++++++++++++++++++++++---------------- src/webbook.ts | 59 ++++++++++++++++-------- 2 files changed, 112 insertions(+), 58 deletions(-) diff --git a/src/epub-output.ts b/src/epub-output.ts index 8f9ca1d8..2b35d9d8 100644 --- a/src/epub-output.ts +++ b/src/epub-output.ts @@ -29,7 +29,7 @@ import { PublicationManifest, ResourceCategorization, } from './schema/publication.schema.js'; -import { DetailError, debug, safeGlob } from './util.js'; +import { DetailError, debug } from './util.js'; import { copyWebPublicationAssets } from './webbook.js'; interface ManifestEntry { @@ -82,22 +82,16 @@ export async function exportEpub({ ) as PublicationManifest; const uid = `urn:uuid:${uuid()}`; - const htmlFiles = await safeGlob(['**/*.html', '**/*.htm'], { - cwd: path.join(tmpDir, 'EPUB'), - ignore: ['publication.json'], - followSymbolicLinks: false, - gitignore: false, - }); - const findPublicationLink = ( relType: string, list?: ResourceCategorization, + filter?: (e: PublicationLinks) => boolean, ) => [list] .flat() .find( (e): e is PublicationLinks => - typeof e === 'object' && e.rel === relType, + typeof e === 'object' && e.rel === relType && (!filter || filter(e)), ); const tocResource = findPublicationLink('contents', manifest.readingOrder) || @@ -109,15 +103,70 @@ export async function exportEpub({ const pageListResource = findPublicationLink('pagelist', manifest.readingOrder) || findPublicationLink('pagelist', manifest.resources); - const coverResource = findPublicationLink('cover', manifest.resources); + // NOTE: EPUB allows one cover-image item unlike web publication + // vivliostyle-cli takes the first cover resource. + const pictureCoverResource = findPublicationLink( + 'cover', + manifest.resources, + (e) => + ['image/gif', 'image/jpeg', 'image/png', 'image/svg+xml'].includes( + e.encodingFormat || mime(e.url) || '', + ), + ); + const htmlCoverResource = findPublicationLink( + 'cover', + manifest.resources, + (e) => /\.html?$/.test(e.url), + ); + const landmarks: { type: string; href: string }[] = []; - if (coverResource) { - const href = path.extname(coverResource.url).match(/^\.html?$/) - ? changeExtname(coverResource.url, '.xhtml') - : coverResource.url; - landmarks.push({ type: 'cover', href }); + if (htmlCoverResource) { + landmarks.push({ + type: 'cover', + href: changeExtname(htmlCoverResource.url, '.xhtml'), + }); } + const manifestItem = [ + ...[manifest.links || []].flat(), + ...[manifest.readingOrder || []].flat(), + ...[manifest.resources || []].flat(), + ].reduce((acc, val) => { + const { url, encodingFormat } = + typeof val === 'string' ? ({ url: val } as PublicationLinks) : val; + // Only accepts path-like url + try { + new URL(url); + return acc; + } catch (e) { + /* NOOP */ + } + if (!fs.existsSync(path.join(tmpDir, 'EPUB', url))) { + return acc; + } + const mediaType = encodingFormat || mime(url); + if (!mediaType) { + throw new Error(`Unknown mediaType: ${url}`); + } + acc[url] = { + href: url, + mediaType, + }; + if (/\.html?$/.test(url)) { + acc[url].href = changeExtname(url, '.xhtml'); + acc[url].mediaType = 'application/xhtml+xml'; + } + if (url === tocResource.url) { + acc[url].properties = 'nav'; + } else if (url === pictureCoverResource?.url) { + acc[url].properties = 'cover-image'; + } + return acc; + }, {} as Record); + const htmlFiles = Object.keys(manifestItem).filter((url) => + /\.html?$/.test(url), + ); + for (const target of htmlFiles) { debug(`Transpiling HTML to XHTML: ${target}`); let parseResult: Resolved>; @@ -145,34 +194,16 @@ export async function exportEpub({ buildNcx({ toc: parseResult.tocParseTree, manifest, uid }), 'utf8', ); + manifestItem['toc.ncx'] = { + href: 'toc.ncx', + mediaType: 'application/x-dtbncx+xml', + }; } } - // TODO: Use `resources` property of webpub - const manifestItems = await safeGlob('**', { - cwd: path.join(tmpDir, 'EPUB'), - ignore: ['*.opf'], - followSymbolicLinks: false, - gitignore: false, - }).then((files) => - files.map((href) => { - const mediaType = mime(href); - if (!mediaType) { - throw new Error(`Unknown mediaType: ${href}`); - } - return { - href, - mediaType, - // TODO: Determine `cover-image` item - properties: - href === changeExtname(tocResource.url, '.xhtml') ? 'nav' : undefined, - }; - }), - ); - const readingOrder = [manifest.readingOrder] + const readingOrder = [manifest.readingOrder || []] .flat() - .filter(Boolean) - .map((e) => (typeof e === 'string' ? e : e!.url)) + .map((e) => (typeof e === 'string' ? e : e.url)) .map((p) => (htmlFiles.includes(p) ? changeExtname(p, '.xhtml') : p)); // META-INF/container.xml @@ -191,7 +222,7 @@ export async function exportEpub({ uid, manifest, readingOrder, - manifestItems, + manifestItems: Object.values(manifestItem), landmarks, }), 'utf8', diff --git a/src/webbook.ts b/src/webbook.ts index ff2fdf78..c52d042c 100644 --- a/src/webbook.ts +++ b/src/webbook.ts @@ -5,6 +5,7 @@ import { MergedConfig } from './config.js'; import type { PublicationLinks, PublicationManifest, + ResourceCategorization, } from './schema/publication.schema.js'; import { debug, pathContains, pathEquals, safeGlob } from './util.js'; @@ -28,7 +29,7 @@ export async function copyWebPublicationAssets({ target: path.relative(input, target), })) .filter(({ source }) => !source.startsWith('..')); - const files = await safeGlob('**', { + const allFiles = await safeGlob('**', { cwd: input, ignore: [ // don't copy auto-generated assets @@ -39,37 +40,41 @@ export async function copyWebPublicationAssets({ ? path.join(path.relative(input, p), '**') : path.relative(input, p), ), - // copy files included on exportAlias in last - ...relExportAliases.map(({ source }) => source), // including node_modules possibly occurs cyclic reference of symlink '**/node_modules', + // only include dotfiles starting with `.vs-` + '**/.!(vs-*)', ], // follow symbolic links to copy local theme packages followSymbolicLinks: true, gitignore: false, + dot: true, }); - debug('webbook files', files); - for (const file of files) { - const target = path.join(outputDir, file); + debug( + 'webbook files', + allFiles.map((file) => { + const alias = relExportAliases.find(({ source }) => source === file); + return alias ? `${file} (alias: ${alias.target})` : file; + }), + ); + const resources: string[] = []; + let actualManifestPath = path.join( + outputDir, + path.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 = path.join(outputDir, relTarget); const stderr = shelljs.mkdir('-p', path.dirname(target)).stderr || shelljs.cp('-r', path.join(input, file), target).stderr; if (stderr) { throw new Error(stderr); } - } - debug('webbook files (alias)', relExportAliases); - let actualManifestPath = manifestPath; - for (const entry of relExportAliases) { - const target = path.join(outputDir, entry.target); - const stderr = - shelljs.mkdir('-p', path.dirname(target)).stderr || - shelljs.cp('-r', path.join(input, entry.source), target).stderr; - if (stderr) { - throw new Error(stderr); - } - if (pathEquals(path.join(input, entry.source), manifestPath)) { + if (alias && pathEquals(path.join(input, alias.source), manifestPath)) { actualManifestPath = target; } } @@ -105,6 +110,24 @@ export async function copyWebPublicationAssets({ : rewriteAliasPath(manifest.resources); } } + + // List copied files to resources field + const normalizeToUrl = (val?: ResourceCategorization) => + [val || []].flat().map((e) => (typeof e === 'string' ? e : e.url)); + const preDefinedResources = [ + ...normalizeToUrl(manifest.links), + ...normalizeToUrl(manifest.readingOrder), + ...normalizeToUrl(manifest.resources), + ]; + manifest.resources = [ + ...[manifest.resources || []].flat(), + ...resources.flatMap((file) => { + if (preDefinedResources.includes(file)) { + return []; + } + return file; + }), + ]; fs.writeFileSync(actualManifestPath, JSON.stringify(manifest, null, 2)); } catch (err) { throw err; From 4ad8787582bd67204846ef708a13fbbc918609b4 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Wed, 5 Jul 2023 02:19:02 +0900 Subject: [PATCH 05/42] chore: Set valid properties for XHTML --- src/epub-output.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/epub-output.ts b/src/epub-output.ts index 2b35d9d8..35db0953 100644 --- a/src/epub-output.ts +++ b/src/epub-output.ts @@ -186,6 +186,22 @@ export async function exportEpub({ thrownError.stack ?? thrownError.message, ); } + const appendProperty = (name: string) => { + const obj = manifestItem[target]; + obj.properties = [obj.properties, name].filter(Boolean).join(' '); + }; + if (parseResult.hasMathmlContent) { + appendProperty('mathml'); + } + if (parseResult.hasRemoteResources) { + appendProperty('remote-resources'); + } + if (parseResult.hasScriptedContent) { + appendProperty('scripted'); + } + if (parseResult.hasSvgContent) { + appendProperty('svg'); + } if (parseResult.tocParseTree) { debug(`Generating toc.ncx`); @@ -249,6 +265,10 @@ async function transpileHtmlToXhtml({ }): Promise<{ tocParseTree?: TocResourceTreeRoot; pageListParseTree?: PageListResourceTreeRoot; + hasMathmlContent: boolean; + hasRemoteResources: boolean; + hasScriptedContent: boolean; + hasSvgContent: boolean; }> { const absPath = path.join(contextDir, target); const htmlFileUrls = htmlFiles.map((p) => @@ -344,6 +364,13 @@ async function transpileHtmlToXhtml({ return { tocParseTree, pageListParseTree, + // FIXME: Yes, I recognize this implementation is inadequate. + hasMathmlContent: !!document.querySelector('math'), + hasRemoteResources: !!document.querySelector( + '[src^="http://"], [src^="https://"]', + ), + hasScriptedContent: !!document.querySelector('script, form'), + hasSvgContent: !!document.querySelector('svg'), }; } From 8b2a87c2b332fbac69a873cb214aec6c8b2e8077 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Sat, 8 Jul 2023 01:41:06 +0900 Subject: [PATCH 06/42] =?UTF-8?q?feat:=20Support=20single=20HTML=20?= =?UTF-8?q?=E2=86=92=20webpub=20convertion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/build.ts | 88 ++++++++++++++-------- src/builder.ts | 77 +++++++++----------- src/config.ts | 83 +++++++++------------ src/const.ts | 5 +- src/epub-output.ts | 40 +++------- src/html.ts | 157 ++++++++++++++++++++++++++++++++++++++- src/preview.ts | 2 +- src/util.ts | 47 +++++++++++- src/webbook.ts | 178 ++++++++++++++++++++++++++++++++++++++++----- types/jsdom.d.ts | 7 ++ 10 files changed, 505 insertions(+), 179 deletions(-) create mode 100644 types/jsdom.d.ts diff --git a/src/build.ts b/src/build.ts index 786ffd38..b8a912e0 100644 --- a/src/build.ts +++ b/src/build.ts @@ -17,6 +17,7 @@ import { } from './config.js'; import { exportEpub } from './epub-output.js'; import { buildPDF, buildPDFWithContainer } from './pdf.js'; +import type { PublicationManifest } from './schema/publication.schema.js'; import { teardownServer } from './server.js'; import { checkContainerEnvironment, @@ -25,8 +26,14 @@ import { log, startLogging, stopLogging, + useTmpDirectory, } from './util.js'; -import { exportWebPublication } from './webbook.js'; +import { + copyWebPublicationAssets, + prepareWebPublicationDirectory, + retrieveWebbookEntry, + supplyWebPublicationManifestForWebbook, +} from './webbook.js'; export interface BuildCliFlags extends CliFlags { output?: { @@ -93,7 +100,8 @@ export async function build(cliFlags: BuildCliFlags) { // generate files for (const target of config.outputs) { let output: string | null = null; - if (target.format === 'pdf') { + const { format } = target; + if (format === 'pdf') { if (!isInContainer && target.renderMode === 'docker') { output = await buildPDFWithContainer({ ...config, @@ -111,31 +119,50 @@ export async function build(cliFlags: BuildCliFlags) { target, }); } - } else if (target.format === 'webpub') { - const { exportAliases, outputs, manifestPath } = config; - if (!manifestPath) { + } else if (format === 'webpub' || format === 'epub') { + const { manifestPath, webbookEntryPath } = config; + let outputDir: string; + if (format === 'webpub') { + outputDir = target.path; + prepareWebPublicationDirectory({ outputDir }); + } else if (format === 'epub') { + [outputDir] = await useTmpDirectory(); + } else { continue; } - output = await exportWebPublication({ - exportAliases, - outputs, - manifestPath, - input: config.workspaceDir, - outputDir: target.path, - }); - } else if (target.format === 'epub') { - const { exportAliases, outputs, manifestPath } = config; - if (!manifestPath) { + + let manifest: PublicationManifest; + if (manifestPath) { + manifest = await copyWebPublicationAssets({ + ...config, + input: config.workspaceDir, + outputDir, + manifestPath, + }); + } else if (webbookEntryPath) { + const ret = await retrieveWebbookEntry({ + webbookEntryPath, + outputDir, + }); + manifest = + ret.manifest || + (await supplyWebPublicationManifestForWebbook({ + ...config, + entryHtmlFile: ret.entryHtmlFile, + })); + } else { continue; } - await exportEpub({ - exportAliases, - outputs, - input: config.workspaceDir, - manifestPath, - target: target.path, - epubVersion: target.version, - }); + + if (format === 'epub') { + await exportEpub({ + webpubDir: outputDir, + manifest, + target: target.path, + epubVersion: target.version, + }); + } + output = target.path; } if (output) { const formattedOutput = chalk.bold.green(path.relative(cwd, output)); @@ -153,18 +180,15 @@ export async function build(cliFlags: BuildCliFlags) { stopLogging('Built successfully.', '🎉'); } -function checkUnsupportedOutputs({ - webbookEntryPath, - epubOpfPath, - outputs, -}: MergedConfig) { - if (webbookEntryPath && outputs.some((t) => t.format === 'webpub')) { +function checkUnsupportedOutputs({ epubOpfPath, outputs }: MergedConfig) { + if (epubOpfPath && outputs.some((t) => t.format === 'webpub')) { throw new Error( - 'Exporting webpub format from single HTML input is not supported.', + 'Exporting webpub format from EPUB or OPF file is not supported.', ); - } else if (epubOpfPath && outputs.some((t) => t.format === 'webpub')) { + } + if (epubOpfPath && outputs.some((t) => t.format === 'epub')) { throw new Error( - 'Exporting webpub format from EPUB or OPF file is not supported.', + 'Exporting EPUB format from EPUB or OPF file is not supported.', ); } } diff --git a/src/builder.ts b/src/builder.ts index 2357857c..15b6b619 100644 --- a/src/builder.ts +++ b/src/builder.ts @@ -1,6 +1,3 @@ -import AjvModule from 'ajv'; -import AjvFormatsModule from 'ajv-formats'; -import betterAjvErrors from 'better-ajv-errors'; import chalk from 'chalk'; import { imageSize } from 'image-size'; import { lookup as mime } from 'mime-types'; @@ -19,17 +16,17 @@ import { processMarkdown } from './markdown.js'; import type { PublicationLinks, PublicationManifest, + URL as PublicationURL, } from './schema/publication.schema.js'; -import { publicationSchema, publicationSchemas } from './schema/pubManifest.js'; import type { ArticleEntryObject } from './schema/vivliostyleConfig.schema.js'; import { checkThemeInstallationNecessity, installThemeDependencies, } from './theme.js'; import { - debug, DetailError, - filterRelevantAjvErrors, + assertPubManifestSchema, + debug, log, pathContains, pathEquals, @@ -38,10 +35,6 @@ import { useTmpDirectory, } from './util.js'; -// FIXME: https://github.com/ajv-validator/ajv/issues/2047 -const Ajv = AjvModule.default; -const addFormats = AjvFormatsModule.default; - function locateThemePath(theme: ParsedTheme, from: string): string | string[] { if (theme.type === 'uri') { return theme.location; @@ -132,24 +125,30 @@ export function generateManifest( outputPath: string, entryContextDir: string, options: { - title?: string; - author?: string; + title: string; + author: string; language?: string | null; readingProgression?: 'ltr' | 'rtl'; modified: string; entries: ArticleEntryObject[]; cover?: string; + links?: (PublicationURL | PublicationLinks)[]; + resources?: (PublicationURL | PublicationLinks)[]; }, -) { +): PublicationManifest { const entries: PublicationLinks[] = options.entries.map((entry) => ({ url: encodeURI(entry.path), - name: entry.title, + ...(entry.title && { name: entry.title }), ...(entry.encodingFormat && { encodingFormat: entry.encodingFormat }), ...(entry.rel && { rel: entry.rel }), ...(entry.rel === 'contents' && { type: 'LinkedResource' }), })); - const links: PublicationLinks[] = []; - const resources: PublicationLinks[] = []; + const links: (PublicationURL | PublicationLinks)[] = [ + options.links || [], + ].flat(); + const resources: (PublicationURL | PublicationLinks)[] = [ + options.resources || [], + ].flat(); if (options.cover) { const { width, height, type } = imageSize( @@ -196,35 +195,30 @@ export function generateManifest( }; const publicationJson = JSON.stringify(publication, null, 2); - fs.writeFileSync(outputPath, publicationJson); - const ajv = new Ajv({ strict: false }); - addFormats(ajv); - ajv.addSchema(publicationSchemas); - const validate = ajv.compile(publicationSchema); - const valid = validate(publication); - if (!valid) { - const message = `Validation of pubManifest failed. Please check the schema: ${outputPath}`; - const detailMessage = - validate.errors && - betterAjvErrors( - publicationSchemas, - publication, - filterRelevantAjvErrors(validate.errors), - { - json: publicationJson, - }, - ); - throw detailMessage - ? new DetailError(message, detailMessage) - : new Error(message); + try { + assertPubManifestSchema(publication, { + json: publicationJson, + }); + } catch (error) { + const thrownError = error as Error | string; + throw new DetailError( + `Validation of pubManifest failed. Please check the schema: ${outputPath}`, + typeof thrownError === 'string' + ? thrownError + : thrownError.stack ?? thrownError.message, + ); } + fs.writeFileSync(outputPath, publicationJson); + return publication; } export async function compile({ entryContextDir, workspaceDir, manifestPath, - manifestAutoGenerate, + needToGenerateManifest, + title, + author, entries, language, readingProgression, @@ -293,7 +287,7 @@ export async function compile({ entries: contentEntries, manifestPath, distDir: path.dirname(generativeContentsEntry.target), - title: manifestAutoGenerate?.title, + title, tocTitle: generativeContentsEntry.title ?? TOC_TITLE, style, }); @@ -301,9 +295,10 @@ export async function compile({ } // generate manifest - if (manifestAutoGenerate) { + if (needToGenerateManifest) { generateManifest(manifestPath, entryContextDir, { - ...manifestAutoGenerate, + title, + author, language, readingProgression, cover: cover && path.relative(entryContextDir, cover), diff --git a/src/config.ts b/src/config.ts index c93d1b23..36e99beb 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,3 @@ -import AjvModule from 'ajv'; -import AjvFormatsModule from 'ajv-formats'; -import betterAjvErrors from 'better-ajv-errors'; import chalk from 'chalk'; import cheerio from 'cheerio'; import fs from 'fs'; @@ -28,7 +25,6 @@ import { checkRenderMode, detectOutputFormat, } from './output.js'; -import { vivliostyleConfigSchema } from './schema/vivliostyle.js'; import type { BrowserType, EntryObject, @@ -40,9 +36,9 @@ import { PageSize } from './server.js'; import { parsePackageName } from './theme.js'; import { DetailError, + assertVivliostyleConfigSchema, cwd, debug, - filterRelevantAjvErrors, isUrlString, log, logWarn, @@ -52,10 +48,6 @@ import { touchTmpFile, } from './util.js'; -// FIXME: https://github.com/ajv-validator/ajv/issues/2047 -const Ajv = AjvModule.default; -const addFormats = AjvFormatsModule.default; - export type ParsedTheme = UriTheme | FileTheme | PackageTheme; export interface UriTheme { @@ -132,10 +124,7 @@ export interface CliFlags { export interface WebPublicationManifestConfig { manifestPath: string; - manifestAutoGenerate: { - title: string; - author: string; - } | null; + needToGenerateManifest?: boolean; } export interface EpubManifestConfig { epubOpfPath: string; @@ -170,6 +159,8 @@ export type MergedConfig = { customUserStyle: string | undefined; singleDoc: boolean; quick: boolean; + title: string; + author: string; language: string | null; readingProgression: 'ltr' | 'rtl' | undefined; vfmOptions: { @@ -381,23 +372,19 @@ export async function collectVivliostyleConfig( ); } - const ajv = new Ajv({ strict: false }); - addFormats(ajv); - const validate = ajv.compile(vivliostyleConfigSchema); - const valid = validate(config); - if (!valid) { - const message = `Validation of vivliostyle.config failed. Please check the schema: ${configPath}`; - const detailMessage = - validate.errors && - betterAjvErrors( - vivliostyleConfigSchema, - config, - filterRelevantAjvErrors(validate.errors), - typeof jsonRaw === 'string' ? { json: jsonRaw } : { indent: 2 }, - ); - throw detailMessage - ? new DetailError(message, detailMessage) - : new Error(message); + try { + assertVivliostyleConfigSchema( + config, + typeof jsonRaw === 'string' ? { json: jsonRaw } : { indent: 2 }, + ); + } catch (error) { + const thrownError = error as Error | string; + throw new DetailError( + `Validation of vivliostyle.config failed. Please check the schema: ${configPath}`, + typeof thrownError === 'string' + ? thrownError + : thrownError.stack ?? thrownError.message, + ); } return config; }; @@ -677,11 +664,11 @@ type CommonOpts = Omit< | 'entries' | 'exportAliases' | 'manifestPath' - | 'manifestAutoGenerate' + | 'needToGenerateManifest' | 'epubOpfPath' | 'webbookEntryPath' - | 'projectTitle' - | 'projectAuthor' + | 'title' + | 'author' >; async function composeSingleInputConfig( @@ -693,6 +680,8 @@ async function composeSingleInputConfig( let sourcePath: string; let input: InputFormat; + const title = cliFlags.title ?? config?.title; + const author = cliFlags.author ?? config?.author; const workspaceDir = otherConfig.workspaceDir; const entries: ParsedEntry[] = []; const exportAliases: { source: string; target: string }[] = []; @@ -738,6 +727,7 @@ async function composeSingleInputConfig( }); } + let fallbackTitle: string = ''; const manifestDeclaration = await (async (): Promise => { if (input.format === 'markdown') { // create temporary manifest file @@ -750,22 +740,15 @@ async function composeSingleInputConfig( source: manifestPath, target: path.resolve(workspaceDir, MANIFEST_FILENAME), }); - return { - manifestPath, - manifestAutoGenerate: { - title: - cliFlags.title ?? - config?.title ?? - (entries.length === 1 && entries[0].title - ? (entries[0].title as string) - : path.basename(sourcePath)), - author: cliFlags.author ?? config?.author ?? '', - }, - }; + fallbackTitle = + entries.length === 1 && entries[0].title + ? (entries[0].title as string) + : path.basename(sourcePath); + return { manifestPath, needToGenerateManifest: true }; } else if (input.format === 'html' || input.format === 'webbook') { return { webbookEntryPath: input.entry }; } else if (input.format === 'pub-manifest') { - return { manifestPath: input.entry, manifestAutoGenerate: null }; + return { manifestPath: input.entry }; } else if (input.format === 'epub-opf') { return { epubOpfPath: input.entry }; } else if (input.format === 'epub') { @@ -782,6 +765,8 @@ async function composeSingleInputConfig( entries, input, exportAliases, + title: title || fallbackTitle, + author: author || '', }; } @@ -920,10 +905,8 @@ async function composeProjectConfig( }, exportAliases: [], manifestPath: path.join(workspaceDir, MANIFEST_FILENAME), - manifestAutoGenerate: { - title: projectTitle || fallbackProjectTitle, - author: projectAuthor || '', - }, + title: projectTitle || fallbackProjectTitle, + author: projectAuthor || '', }; } diff --git a/src/const.ts b/src/const.ts index 519756ef..d8c68063 100644 --- a/src/const.ts +++ b/src/const.ts @@ -2,7 +2,6 @@ import fs from 'node:fs'; import { fileURLToPath } from 'node:url'; import resolvePkg from 'resolve-pkg'; import path from 'upath'; -import { readJSON } from './util.js'; export const MANIFEST_FILENAME = 'publication.json'; export const TOC_FILENAME = 'index.html'; @@ -20,8 +19,8 @@ export const EPUB_CONTAINER_XML = `${XML_DECLARATION} `; export const cliRoot = path.join(fileURLToPath(import.meta.url), '../..'); -export const { version: cliVersion }: { version: string } = readJSON( - path.join(cliRoot, 'package.json'), +export const { version: cliVersion }: { version: string } = JSON.parse( + fs.readFileSync(path.join(cliRoot, 'package.json'), 'utf8'), ); export const viewerRoot = resolvePkg('@vivliostyle/viewer', { cwd: cliRoot })!; diff --git a/src/epub-output.ts b/src/epub-output.ts index 35db0953..2fd50fb0 100644 --- a/src/epub-output.ts +++ b/src/epub-output.ts @@ -1,15 +1,14 @@ import archiver from 'archiver'; import { XMLBuilder } from 'fast-xml-parser'; import GithubSlugger from 'github-slugger'; -import { JSDOM } from 'jsdom'; import languageTags from 'language-tags'; import { lookup as mime } from 'mime-types'; import fs from 'node:fs'; import url from 'node:url'; +import shelljs from 'shelljs'; import path from 'upath'; import { v4 as uuid } from 'uuid'; import serializeToXml from 'w3c-xmlserializer'; -import { MergedConfig } from './config.js'; import { EPUB_CONTAINER_XML, EPUB_NS, @@ -20,6 +19,7 @@ import { PageListResourceTreeRoot, TocResourceTreeItem, TocResourceTreeRoot, + getJsdomFromUrlOrFile, parsePageListDocument, parseTocDocument, } from './html.js'; @@ -30,7 +30,6 @@ import { ResourceCategorization, } from './schema/publication.schema.js'; import { DetailError, debug } from './util.js'; -import { copyWebPublicationAssets } from './webbook.js'; interface ManifestEntry { href: string; @@ -49,15 +48,13 @@ const changeExtname = (filepath: string, newExt: string) => { }; export async function exportEpub({ - exportAliases, - outputs, - input, - manifestPath, + webpubDir, + manifest, target, epubVersion, -}: Pick & { - input: string; - manifestPath: string; +}: { + webpubDir: string; + manifest: PublicationManifest; target: string; epubVersion: '3.0'; }) { @@ -65,21 +62,9 @@ export async function exportEpub({ // const [tmpDir, clearTmpDir] = await useTmpDirectory(); const tmpDir = path.join(cliRoot, 'tmp'); - fs.mkdirSync(tmpDir, { recursive: true }); - const clearTmpDir = () => {}; fs.mkdirSync(path.join(tmpDir, 'META-INF'), { recursive: true }); - fs.mkdirSync(path.join(tmpDir, 'EPUB'), { recursive: true }); - - await copyWebPublicationAssets({ - exportAliases, - outputs, - input, - outputDir: path.join(tmpDir, 'EPUB'), - manifestPath, - }); - const manifest = JSON.parse( - fs.readFileSync(path.join(tmpDir, 'EPUB/publication.json'), 'utf8'), - ) as PublicationManifest; + shelljs.cp('-rf', webpubDir, path.join(tmpDir, 'EPUB')); + const uid = `urn:uuid:${uuid()}`; const findPublicationLink = ( @@ -245,7 +230,6 @@ export async function exportEpub({ ); await compressEpub({ target, sourceDir: tmpDir }); - clearTmpDir(); } async function transpileHtmlToXhtml({ @@ -274,11 +258,9 @@ async function transpileHtmlToXhtml({ const htmlFileUrls = htmlFiles.map((p) => url.pathToFileURL(path.join(contextDir, p)), ); - const html = await fs.promises.readFile(absPath, 'utf8'); - const dom = new JSDOM(html); + const { dom } = await getJsdomFromUrlOrFile(absPath); const { document } = dom.window; - const htmlElement = document.body.parentElement!; - htmlElement.setAttribute('xmlns:epub', EPUB_NS); + document.documentElement.setAttribute('xmlns:epub', EPUB_NS); document.querySelectorAll('a[href]').forEach((el) => { const href = el.getAttribute('href')!; diff --git a/src/html.ts b/src/html.ts index 0d0f3354..47b90fa7 100644 --- a/src/html.ts +++ b/src/html.ts @@ -1,11 +1,94 @@ import cheerio from 'cheerio'; import toHTML from 'hast-util-to-html'; import h from 'hastscript'; -import { JSDOM } from 'jsdom'; +import jsdom, { ResourceLoader as BaseResourceLoader, JSDOM } from 'jsdom'; import fs from 'node:fs'; +import url from 'node:url'; import prettier from 'prettier'; import path from 'upath'; import { ManuscriptEntry } from './config.js'; +import type { PublicationManifest } from './schema/publication.schema.js'; +import { + DetailError, + assertPubManifestSchema, + debug, + log, + logWarn, +} from './util.js'; + +const virtualConsole = new jsdom.VirtualConsole(); +virtualConsole.on('error', (message) => { + debug('[JSDOM Console] error:', message); +}); +virtualConsole.on('warn', (message) => { + debug('[JSDOM Console] warn:', message); +}); +virtualConsole.on('log', (message) => { + debug('[JSDOM Console] log:', message); +}); +virtualConsole.on('info', (message) => { + debug('[JSDOM Console] info:', message); +}); +virtualConsole.on('dir', (message) => { + debug('[JSDOM Console] dir:', message); +}); +virtualConsole.on('jsdomError', (error) => { + // Most of CSS using Paged media will be failed to run CSS parser of JSDOM. + // We just ignore it because we don't use CSS parse results. + // https://github.com/jsdom/jsdom/blob/a39e0ec4ce9a8806692d986a7ed0cd565ec7498a/lib/jsdom/living/helpers/stylesheets.js#L33-L44 + // see also: https://github.com/jsdom/jsdom/issues/2005 + if (error.message === 'Could not parse CSS stylesheet') { + return; + } + throw new DetailError( + 'Error occurred when loading HTML', + error.stack ?? error.message, + ); +}); + +class ResourceLoader extends BaseResourceLoader { + fetcherMap = new Map>(); + + fetch(url: string, options?: jsdom.FetchOptions) { + debug(`[JSDOM] Fetching resource: ${url}`); + const fetcher = super.fetch(url, options); + if (fetcher) { + this.fetcherMap.set(url, fetcher); + } + return fetcher; + } +} + +export async function getJsdomFromUrlOrFile(src: string): Promise<{ + dom: JSDOM; + resourceLoader: ResourceLoader; + baseUrl: string; +}> { + const resourceLoader = new ResourceLoader(); + let baseUrl = src; + let dom: JSDOM; + if (/^https?:\/\//.test(src)) { + const url = new URL(src); + // Ensures trailing slash or explicit HTML extensions + if (!url.pathname.endsWith('/') && !/\.html?$/.test(url.pathname)) { + url.pathname = `${url.pathname}/`; + } + baseUrl = url.href; + dom = await JSDOM.fromURL(src, { + virtualConsole, + resources: resourceLoader, + }); + } else { + baseUrl = /^file:\/\//.test(src) ? src : url.pathToFileURL(src).href; + const file = await resourceLoader._readFile(url.fileURLToPath(baseUrl)); + resourceLoader.fetcherMap.set(baseUrl, Promise.resolve(file)); + dom = await JSDOM.fromFile(url.fileURLToPath(baseUrl), { + virtualConsole, + resources: resourceLoader, + }); + } + return { dom, resourceLoader, baseUrl }; +} export function generateTocHtml({ entries, @@ -110,6 +193,78 @@ export function isTocHtml(filepath: string): boolean { } } +export async function fetchLinkedPublicationManifest({ + dom, + resourceLoader, + baseUrl, +}: { + dom: JSDOM; + resourceLoader: ResourceLoader; + baseUrl: string; +}): Promise { + const { document } = dom.window; + + const linkEl = document.querySelector('link[href][rel="publication"]'); + if (!linkEl) { + return null; + } + const href = linkEl.getAttribute('href')!.trim(); + let manifest: PublicationManifest; + if (href.startsWith('#')) { + const scriptEl = document.getElementById(href.slice(1)); + if (scriptEl?.getAttribute('type') !== 'application/ld+json') { + return null; + } + debug(`Found embedded publication manifest: ${href}`); + let manifest: PublicationManifest; + try { + manifest = JSON.parse(scriptEl.innerHTML); + } catch (error) { + const thrownError = error as Error; + throw new DetailError( + 'Failed to parse manifest data', + typeof thrownError.stack ?? thrownError.message, + ); + } + try { + assertPubManifestSchema(manifest, { indent: 2 }); + } catch (error) { + logWarn( + 'Publication manifest validation failed. Processing continues, but some problems may occur.', + ); + log(error); + } + return manifest; + } else { + debug(`Found linked publication manifest: ${href}`); + const url = new URL(href, baseUrl); + const buffer = await resourceLoader.fetch(url.href); + if (!buffer) { + throw new Error(`Failed to fetch manifest JSON file: ${url.href}`); + } + let manifestJson: string; + try { + manifestJson = buffer.toString(); + manifest = JSON.parse(manifestJson); + } catch (error) { + const thrownError = error as Error; + throw new DetailError( + 'Failed to parse manifest data', + typeof thrownError.stack ?? thrownError.message, + ); + } + try { + assertPubManifestSchema(manifest, { json: manifestJson }); + } catch (error) { + logWarn( + 'Publication manifest validation failed. Processing continues, but some problems may occur.', + ); + log(error); + } + return manifest; + } +} + export type TocResourceTreeItem = { element: HTMLElement; label: HTMLElement; diff --git a/src/preview.ts b/src/preview.ts index 6a98ce7c..d6045828 100644 --- a/src/preview.ts +++ b/src/preview.ts @@ -177,7 +177,7 @@ export async function preview(cliFlags: PreviewCliFlags) { return true; // ignore saved intermediate files } if ( - config.manifestAutoGenerate && + config.needToGenerateManifest && pathEquals(path, config.manifestPath) ) { return true; // ignore generated pub-manifest diff --git a/src/util.ts b/src/util.ts index 2bb73a57..1a29c12f 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,9 +1,11 @@ -import { ErrorObject } from 'ajv'; +import AjvModule, { ErrorObject, Schema } from 'ajv'; +import AjvFormatsModule from 'ajv-formats'; +import betterAjvErrors, { IInputOptions } from 'better-ajv-errors'; import chalk from 'chalk'; import debugConstructor from 'debug'; import fastGlob from 'fast-glob'; import { XMLParser } from 'fast-xml-parser'; -import { globby, Options as GlobbyOptions } from 'globby'; +import { Options as GlobbyOptions, globby } from 'globby'; import gitIgnore, { Ignore } from 'ignore'; import StreamZip from 'node-stream-zip'; import fs from 'node:fs'; @@ -15,6 +17,10 @@ import shelljs from 'shelljs'; import slash from 'slash'; import tmp from 'tmp'; import upath from 'upath'; +import { publicationSchema, publicationSchemas } from './schema/pubManifest.js'; +import type { PublicationManifest } from './schema/publication.schema.js'; +import { vivliostyleConfigSchema } from './schema/vivliostyle.js'; +import type { VivliostyleConfigSchema } from './schema/vivliostyleConfig.schema.js'; export const debug = debugConstructor('vs-cli'); export const cwd = upath.normalize(process.cwd()); @@ -417,3 +423,40 @@ export async function openEpubToTmpDirectory(filePath: string): Promise<{ const epubOpfPath = upath.join(tmpDir, rootfile['@_full-path']); return { dest: tmpDir, epubOpfPath, deleteEpub }; } + +// FIXME: https://github.com/ajv-validator/ajv/issues/2047 +const Ajv = AjvModule.default; +const addFormats = AjvFormatsModule.default; + +const getValidatorFunction = + (schema: T, refSchemas?: Schema | Schema[]) => + (obj: unknown, errorFormatOption?: IInputOptions): obj is T => { + const ajv = new Ajv({ strict: false }); + addFormats(ajv); + if (refSchemas) { + ajv.addSchema(refSchemas); + } + const validate = ajv.compile(schema); + const valid = validate(obj); + if (!valid) { + const detailMessage = + validate.errors && + betterAjvErrors( + schema, + obj, + filterRelevantAjvErrors(validate.errors), + errorFormatOption, + ); + throw detailMessage || new Error(); + } + return true; + }; + +export const assertVivliostyleConfigSchema = + getValidatorFunction(vivliostyleConfigSchema); + +export const assertPubManifestSchema = + getValidatorFunction( + publicationSchema, + publicationSchemas, + ); diff --git a/src/webbook.ts b/src/webbook.ts index c52d042c..c8eb2fec 100644 --- a/src/webbook.ts +++ b/src/webbook.ts @@ -1,13 +1,167 @@ import fs from 'node:fs'; import shelljs from 'shelljs'; import path from 'upath'; -import { MergedConfig } from './config.js'; +import { generateManifest } from './builder.js'; +import { MergedConfig, WebbookEntryConfig } from './config.js'; +import { MANIFEST_FILENAME } from './const.js'; +import { + fetchLinkedPublicationManifest, + getJsdomFromUrlOrFile, +} from './html.js'; import type { PublicationLinks, PublicationManifest, ResourceCategorization, } from './schema/publication.schema.js'; -import { debug, pathContains, pathEquals, safeGlob } from './util.js'; +import { debug, logError, pathContains, pathEquals, safeGlob } from './util.js'; + +export function prepareWebPublicationDirectory({ + outputDir, +}: { + outputDir: string; +}) { + if (fs.existsSync(outputDir)) { + debug('going to remove existing webpub', outputDir); + shelljs.rm('-rf', outputDir); + } + fs.mkdirSync(outputDir, { recursive: true }); +} + +export async function retrieveWebbookEntry({ + webbookEntryPath, + outputDir, +}: WebbookEntryConfig & { + outputDir: string; +}): Promise<{ + entryHtmlFile: string; + manifest: PublicationManifest | null; +}> { + const { dom, resourceLoader, baseUrl } = await getJsdomFromUrlOrFile( + webbookEntryPath, + ); + const manifest = await fetchLinkedPublicationManifest({ + dom, + resourceLoader, + baseUrl, + }); + const rootUrl = new URL('.', baseUrl).href; + const pathContains = (url: string) => + !path.posix.relative(rootUrl, url).startsWith('..'); + const retriever = new Map(resourceLoader.fetcherMap); + + if (manifest) { + [manifest.resources || []].flat().forEach((v) => { + const url = typeof v === 'string' ? v : v.url; + const fullUrl = new URL(url, baseUrl).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?$/.test(url) && + !(typeof v === 'string' || v.encodingFormat === 'text/html') + ) { + continue; + } + const fullUrl = new URL(url, baseUrl).href; + if (!pathContains(fullUrl) || fullUrl === baseUrl) { + continue; + } + const { resourceLoader } = await getJsdomFromUrlOrFile(fullUrl); + resourceLoader.fetcherMap.forEach( + (v, k) => !retriever.has(k) && retriever.set(k, v), + ); + } + } + + const resources: string[] = []; + await Promise.allSettled( + Array.from(retriever.entries()).flatMap(([url, fetchPromise]) => { + if (!pathContains(url)) { + return []; + } + return fetchPromise + .then(async (buffer) => { + let relTarget = path.relative(rootUrl, url); + if (!relTarget || !path.extname(relTarget)) { + relTarget = path.join(relTarget, 'index.html'); + } + const target = path.join(outputDir, relTarget); + resources.push(relTarget); + await fs.promises.mkdir(path.dirname(target), { recursive: true }); + await fs.promises.writeFile(target, buffer); + }) + .catch(() => { + logError(`Failed to fetch webbook resources: ${url}`); + }); + }), + ); + + debug('Saved webbook resources', resources); + + return { + entryHtmlFile: path.join( + outputDir, + new URL(baseUrl).pathname.split('/').at(-1) || 'index.html', + ), + manifest, + }; +} + +export async function supplyWebPublicationManifestForWebbook({ + entryHtmlFile, + ...config +}: Pick< + MergedConfig, + 'language' | 'title' | 'author' | 'readingProgression' +> & { + entryHtmlFile: string; +}): Promise { + debug(`Generating publication manifest from HTML: ${entryHtmlFile}`); + const { dom } = await getJsdomFromUrlOrFile(entryHtmlFile); + const { document } = dom.window; + const language = + config.language || document.documentElement.lang || undefined; + const title = config.title || document.title || ''; + const author = + config.author || + document.querySelector('meta[name="author"]')?.getAttribute('content') || + ''; + + const rootDir = path.dirname(entryHtmlFile); + const entry = path.basename(entryHtmlFile); + const allFiles = await safeGlob('**', { + cwd: rootDir, + gitignore: false, + }); + + const manifest = generateManifest( + path.join(rootDir, MANIFEST_FILENAME), + rootDir, + { + title, + author, + language, + readingProgression: config.readingProgression, + modified: new Date().toISOString(), + entries: [{ path: entry }], + resources: allFiles.filter((f) => f !== entry), + }, + ); + const link = document.createElement('link'); + link.setAttribute('rel', 'publication'); + link.setAttribute('type', 'application/ld+json'); + link.setAttribute('href', MANIFEST_FILENAME); + document.head.appendChild(link); + await fs.promises.writeFile(entryHtmlFile, dom.serialize(), 'utf8'); + return manifest; +} export async function copyWebPublicationAssets({ exportAliases, @@ -19,7 +173,7 @@ export async function copyWebPublicationAssets({ input: string; outputDir: string; manifestPath: string; -}) { +}): Promise { const silentMode = shelljs.config.silent; shelljs.config.silent = true; try { @@ -129,26 +283,10 @@ export async function copyWebPublicationAssets({ }), ]; fs.writeFileSync(actualManifestPath, JSON.stringify(manifest, null, 2)); + return manifest; } catch (err) { throw err; } finally { shelljs.config.silent = silentMode; } } - -export async function exportWebPublication( - params: Parameters[0], -): Promise { - const { outputDir } = params; - if (fs.existsSync(outputDir)) { - debug('going to remove existing webpub', outputDir); - shelljs.rm('-rf', outputDir); - } - try { - await copyWebPublicationAssets(params); - } catch (err) { - shelljs.rm('-rf', outputDir); - throw err; - } - return outputDir; -} diff --git a/types/jsdom.d.ts b/types/jsdom.d.ts new file mode 100644 index 00000000..7a0c14d0 --- /dev/null +++ b/types/jsdom.d.ts @@ -0,0 +1,7 @@ +declare module 'jsdom' { + export class ResourceLoader { + _readFile(filePath: string): AbortablePromise; + fetch(url: string, options?: FetchOptions): AbortablePromise | null; + constructor(obj?: ResourceLoaderConstructorOptions); + } +} From e88281522269087d059bd3ae26c9988d4d6d00e9 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Sat, 8 Jul 2023 05:06:53 +0900 Subject: [PATCH 07/42] chore: Provide toc for single document of EPUB --- src/build.ts | 12 +++++++ src/epub-output.ts | 89 +++++++++++++++++++++++++++++++++------------- 2 files changed, 76 insertions(+), 25 deletions(-) diff --git a/src/build.ts b/src/build.ts index b8a912e0..619946b5 100644 --- a/src/build.ts +++ b/src/build.ts @@ -131,6 +131,7 @@ export async function build(cliFlags: BuildCliFlags) { continue; } + let entryHtmlFile: string | undefined; let manifest: PublicationManifest; if (manifestPath) { manifest = await copyWebPublicationAssets({ @@ -139,11 +140,21 @@ export async function build(cliFlags: BuildCliFlags) { outputDir, manifestPath, }); + if (config.input.format === 'markdown') { + const entry = [manifest.readingOrder].flat()[0]; + if (entry) { + entryHtmlFile = path.join( + outputDir, + typeof entry === 'string' ? entry : entry.url, + ); + } + } } else if (webbookEntryPath) { const ret = await retrieveWebbookEntry({ webbookEntryPath, outputDir, }); + entryHtmlFile = ret.entryHtmlFile; manifest = ret.manifest || (await supplyWebPublicationManifestForWebbook({ @@ -157,6 +168,7 @@ export async function build(cliFlags: BuildCliFlags) { if (format === 'epub') { await exportEpub({ webpubDir: outputDir, + entryHtmlFile, manifest, target: target.path, epubVersion: target.version, diff --git a/src/epub-output.ts b/src/epub-output.ts index 2fd50fb0..e272bd1b 100644 --- a/src/epub-output.ts +++ b/src/epub-output.ts @@ -49,11 +49,13 @@ const changeExtname = (filepath: string, newExt: string) => { export async function exportEpub({ webpubDir, + entryHtmlFile, manifest, target, epubVersion, }: { webpubDir: string; + entryHtmlFile?: string; manifest: PublicationManifest; target: string; epubVersion: '3.0'; @@ -62,10 +64,14 @@ export async function exportEpub({ // const [tmpDir, clearTmpDir] = await useTmpDirectory(); const tmpDir = path.join(cliRoot, 'tmp'); + shelljs.rm('-rf', tmpDir); fs.mkdirSync(path.join(tmpDir, 'META-INF'), { recursive: true }); shelljs.cp('-rf', webpubDir, path.join(tmpDir, 'EPUB')); const uid = `urn:uuid:${uuid()}`; + const entryHtmlRelPath = + entryHtmlFile && + path.relative(webpubDir, path.resolve(webpubDir, entryHtmlFile)); const findPublicationLink = ( relType: string, @@ -81,10 +87,6 @@ export async function exportEpub({ const tocResource = findPublicationLink('contents', manifest.readingOrder) || findPublicationLink('contents', manifest.resources); - if (!tocResource) { - // TODO: Generate ToC - throw new Error(); - } const pageListResource = findPublicationLink('pagelist', manifest.readingOrder) || findPublicationLink('pagelist', manifest.resources); @@ -141,13 +143,26 @@ export async function exportEpub({ acc[url].href = changeExtname(url, '.xhtml'); acc[url].mediaType = 'application/xhtml+xml'; } - if (url === tocResource.url) { + if (url === (tocResource?.url || entryHtmlRelPath)) { acc[url].properties = 'nav'; } else if (url === pictureCoverResource?.url) { acc[url].properties = 'cover-image'; } return acc; }, {} as Record); + if ( + entryHtmlRelPath && + Object.values(manifestItem).every( + ({ properties }) => !properties?.split(' ').includes('nav'), + ) + ) { + manifestItem[entryHtmlRelPath] = { + href: changeExtname(entryHtmlRelPath, '.xhtml'), + mediaType: 'application/xhtml+xml', + properties: 'nav', + }; + } + const htmlFiles = Object.keys(manifestItem).filter((url) => /\.html?$/.test(url), ); @@ -161,8 +176,8 @@ export async function exportEpub({ htmlFiles, contextDir: path.join(tmpDir, 'EPUB'), landmarks, - tocResource, - pageListResource, + isTocHtml: target === (tocResource?.url || entryHtmlRelPath), + isPagelistHtml: target === (pageListResource?.url || entryHtmlRelPath), }); } catch (error) { const thrownError = error as Error; @@ -202,8 +217,9 @@ export async function exportEpub({ } } - const readingOrder = [manifest.readingOrder || []] + const readingOrder = [manifest.readingOrder || entryHtmlRelPath] .flat() + .filter((v): v is string | PublicationLinks => Boolean(v)) .map((e) => (typeof e === 'string' ? e : e.url)) .map((p) => (htmlFiles.includes(p) ? changeExtname(p, '.xhtml') : p)); @@ -237,15 +253,15 @@ async function transpileHtmlToXhtml({ htmlFiles, contextDir, landmarks, - tocResource, - pageListResource, + isTocHtml, + isPagelistHtml, }: { target: string; htmlFiles: string[]; contextDir: string; landmarks: LandmarkEntry[]; - tocResource: PublicationLinks; - pageListResource?: PublicationLinks; + isTocHtml: boolean; + isPagelistHtml: boolean; }): Promise<{ tocParseTree?: TocResourceTreeRoot; pageListParseTree?: PageListResourceTreeRoot; @@ -296,16 +312,36 @@ async function transpileHtmlToXhtml({ let tocParseTree: TocResourceTreeRoot | undefined; let pageListParseTree: PageListResourceTreeRoot | undefined; - if (target === tocResource.url) { + if (isTocHtml) { if (!document.querySelector('[epub:type="toc"]')) { const parsed = parseTocDocument(dom); - if (!parsed) { - throw new Error('Navigation document must have one "toc" nav element'); + if (parsed) { + tocParseTree = parsed; + const nav = replaceWithNavElement(parsed.element); + nav.setAttribute('id', 'toc'); + nav.setAttribute('epub:type', 'toc'); + } else { + // Insert single document toc + const nav = document.createElement('nav'); + nav.setAttribute('id', 'toc'); + nav.setAttribute('role', 'doc-toc'); + nav.setAttribute('epub:type', 'toc'); + nav.setAttribute('hidden', ''); + nav.innerHTML = '
'; + const span = document.createElement('span'); + span.textContent = document.title; + nav.querySelector('li')!.appendChild(span); + document.body.appendChild(nav); + tocParseTree = { + element: nav, + children: [ + { + element: nav.querySelector('li')!, + label: span, + }, + ], + }; } - tocParseTree = parsed; - const nav = replaceWithNavElement(parsed.element); - nav.setAttribute('id', 'toc'); - nav.setAttribute('epub:type', 'toc'); } if ( @@ -330,7 +366,7 @@ async function transpileHtmlToXhtml({ } } - if (target === pageListResource?.url) { + if (isPagelistHtml) { const parsed = parsePageListDocument(dom); if (parsed) { pageListParseTree = parsed; @@ -486,11 +522,14 @@ function buildEpubPackageDocument({ }, guide: { reference: [ - { - _type: 'toc', - _href: manifestItems.find(({ properties }) => properties === 'nav')! - .href, - }, + ...[ + (() => { + const tocItem = manifestItems.find(({ properties }) => + properties?.split(' ').includes('nav'), + ); + return tocItem ? { _type: 'toc', _href: tocItem.href } : []; + })(), + ].flat(), ...landmarks.map(({ type, href }) => ({ _type: type, _href: href })), ], }, From 40624441337ad22564537f323e5f24a0308965fb Mon Sep 17 00:00:00 2001 From: spring-raining Date: Sat, 8 Jul 2023 15:28:19 +0900 Subject: [PATCH 08/42] chore: Reduce redundant fetch --- src/html.ts | 17 ++++++++++------- src/webbook.ts | 10 +++++++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/html.ts b/src/html.ts index 47b90fa7..b8632293 100644 --- a/src/html.ts +++ b/src/html.ts @@ -46,7 +46,7 @@ virtualConsole.on('jsdomError', (error) => { ); }); -class ResourceLoader extends BaseResourceLoader { +export class ResourceLoader extends BaseResourceLoader { fetcherMap = new Map>(); fetch(url: string, options?: jsdom.FetchOptions) { @@ -59,12 +59,13 @@ class ResourceLoader extends BaseResourceLoader { } } -export async function getJsdomFromUrlOrFile(src: string): Promise<{ +export async function getJsdomFromUrlOrFile( + src: string, + resourceLoader?: ResourceLoader, +): Promise<{ dom: JSDOM; - resourceLoader: ResourceLoader; baseUrl: string; }> { - const resourceLoader = new ResourceLoader(); let baseUrl = src; let dom: JSDOM; if (/^https?:\/\//.test(src)) { @@ -80,14 +81,16 @@ export async function getJsdomFromUrlOrFile(src: string): Promise<{ }); } else { baseUrl = /^file:\/\//.test(src) ? src : url.pathToFileURL(src).href; - const file = await resourceLoader._readFile(url.fileURLToPath(baseUrl)); - resourceLoader.fetcherMap.set(baseUrl, Promise.resolve(file)); + if (resourceLoader) { + const file = await resourceLoader._readFile(url.fileURLToPath(baseUrl)); + resourceLoader.fetcherMap.set(baseUrl, Promise.resolve(file)); + } dom = await JSDOM.fromFile(url.fileURLToPath(baseUrl), { virtualConsole, resources: resourceLoader, }); } - return { dom, resourceLoader, baseUrl }; + return { dom, baseUrl }; } export function generateTocHtml({ diff --git a/src/webbook.ts b/src/webbook.ts index c8eb2fec..5a3e77a9 100644 --- a/src/webbook.ts +++ b/src/webbook.ts @@ -5,6 +5,7 @@ import { generateManifest } from './builder.js'; import { MergedConfig, WebbookEntryConfig } from './config.js'; import { MANIFEST_FILENAME } from './const.js'; import { + ResourceLoader, fetchLinkedPublicationManifest, getJsdomFromUrlOrFile, } from './html.js'; @@ -36,8 +37,10 @@ export async function retrieveWebbookEntry({ entryHtmlFile: string; manifest: PublicationManifest | null; }> { - const { dom, resourceLoader, baseUrl } = await getJsdomFromUrlOrFile( + const resourceLoader = new ResourceLoader(); + const { dom, baseUrl } = await getJsdomFromUrlOrFile( webbookEntryPath, + resourceLoader, ); const manifest = await fetchLinkedPublicationManifest({ dom, @@ -73,8 +76,9 @@ export async function retrieveWebbookEntry({ if (!pathContains(fullUrl) || fullUrl === baseUrl) { continue; } - const { resourceLoader } = await getJsdomFromUrlOrFile(fullUrl); - resourceLoader.fetcherMap.forEach( + const subpathResourceLoader = new ResourceLoader(); + await getJsdomFromUrlOrFile(fullUrl, subpathResourceLoader); + subpathResourceLoader.fetcherMap.forEach( (v, k) => !retriever.has(k) && retriever.set(k, v), ); } From 0dc9571f67dbda8b1b3209253a6a506bd644ccc1 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Sat, 8 Jul 2023 18:05:16 +0900 Subject: [PATCH 09/42] chore: Improve languages/title/readingProgression --- package.json | 3 +- src/epub-output.ts | 139 +++++++++++++++++++++++++++++++++------------ src/webbook.ts | 6 ++ yarn.lock | 22 ++----- 4 files changed, 114 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index 2582592e..f4877bed 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "ajv": "^8.11.2", "ajv-formats": "^2.1.1", "archiver": "^5.3.1", + "bcp-47-match": "^2.0.3", "better-ajv-errors": "^1.2.0", "chalk": "^4.1.2", "cheerio": "^1.0.0-rc.10", @@ -45,7 +46,6 @@ "image-size": "^1.0.0", "is-interactive": "1.0.0", "jsdom": "^22.1.0", - "language-tags": "^1.0.8", "mime-types": "^2.1.32", "node-stream-zip": "^1.14.0", "ora": "^5.4.1", @@ -73,7 +73,6 @@ "@types/github-slugger": "^1.3.0", "@types/jest": "^29.2.4", "@types/jsdom": "^21.1.1", - "@types/language-tags": "^1.0.1", "@types/mime-types": "^2.1.1", "@types/node": "^16.7.2", "@types/npm-package-arg": "^6.1.1", diff --git a/src/epub-output.ts b/src/epub-output.ts index e272bd1b..f677068b 100644 --- a/src/epub-output.ts +++ b/src/epub-output.ts @@ -1,7 +1,8 @@ import archiver from 'archiver'; +import { lookup as lookupLanguage } from 'bcp-47-match'; import { XMLBuilder } from 'fast-xml-parser'; import GithubSlugger from 'github-slugger'; -import languageTags from 'language-tags'; +import type { JSDOM } from 'jsdom'; import { lookup as mime } from 'mime-types'; import fs from 'node:fs'; import url from 'node:url'; @@ -25,6 +26,8 @@ import { } from './html.js'; import { Contributor, + LocalizableStringObject, + LocalizableStringOrObject, PublicationLinks, PublicationManifest, ResourceCategorization, @@ -47,6 +50,31 @@ const changeExtname = (filepath: string, newExt: string) => { return `${filepath.slice(0, -ext.length)}${newExt}`; }; +const normalizeLocalizableString = ( + value: LocalizableStringOrObject | undefined, + availableLanguages: string[], +): string | undefined => { + if (!value) { + return; + } + const values = [value] + .flat() + .map((value) => (typeof value === 'string' ? { value } : value)); + const localizedValues = values.filter( + (v): v is LocalizableStringObject & { language: string } => !!v.language, + ); + const preferredLang = lookupLanguage( + localizedValues.map((v) => v.language), + availableLanguages, + ); + if (preferredLang) { + return localizedValues[ + localizedValues.findIndex((v) => v.language === preferredLang) + ].value; + } + return values.find((v) => !v.language)?.value; +}; + export async function exportEpub({ webpubDir, entryHtmlFile, @@ -166,9 +194,14 @@ export async function exportEpub({ const htmlFiles = Object.keys(manifestItem).filter((url) => /\.html?$/.test(url), ); + const tocHtml = htmlFiles.find( + (f) => f === (tocResource?.url || entryHtmlRelPath), + ); + if (!tocHtml) { + throw new Error('EPUB must have one ToC document or entry HTML'); + } - for (const target of htmlFiles) { - debug(`Transpiling HTML to XHTML: ${target}`); + const processHtml = async (target: string, isTocHtml: boolean) => { let parseResult: Resolved>; try { parseResult = await transpileHtmlToXhtml({ @@ -176,7 +209,7 @@ export async function exportEpub({ htmlFiles, contextDir: path.join(tmpDir, 'EPUB'), landmarks, - isTocHtml: target === (tocResource?.url || entryHtmlRelPath), + isTocHtml, isPagelistHtml: target === (pageListResource?.url || entryHtmlRelPath), }); } catch (error) { @@ -202,19 +235,44 @@ export async function exportEpub({ if (parseResult.hasSvgContent) { appendProperty('svg'); } - if (parseResult.tocParseTree) { - debug(`Generating toc.ncx`); + return parseResult; + }; - fs.writeFileSync( - path.join(tmpDir, 'EPUB/toc.ncx'), - buildNcx({ toc: parseResult.tocParseTree, manifest, uid }), - 'utf8', - ); - manifestItem['toc.ncx'] = { - href: 'toc.ncx', - mediaType: 'application/x-dtbncx+xml', - }; - } + debug(`Transpiling ToC HTML to XHTML: ${tocHtml}`); + const { dom, tocParseTree } = await processHtml(tocHtml, true); + const { document: entryDocument } = dom.window; + const docLanguages = [manifest.inLanguage] + .flat() + .filter((v): v is string => 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'); + } + if (tocParseTree) { + debug(`Generating toc.ncx`); + fs.writeFileSync( + path.join(tmpDir, 'EPUB/toc.ncx'), + buildNcx({ + toc: tocParseTree, + docTitle, + uid, + }), + 'utf8', + ); + manifestItem['toc.ncx'] = { + href: 'toc.ncx', + mediaType: 'application/x-dtbncx+xml', + }; + } + + for (const target of htmlFiles.filter((f) => f !== tocHtml)) { + debug(`Transpiling HTML to XHTML: ${target}`); + await processHtml(target, false); } const readingOrder = [manifest.readingOrder || entryHtmlRelPath] @@ -237,6 +295,9 @@ export async function exportEpub({ buildEpubPackageDocument({ epubVersion, uid, + docTitle, + docLanguages, + tocXhtml: manifestItem[tocHtml].href, manifest, readingOrder, manifestItems: Object.values(manifestItem), @@ -263,6 +324,7 @@ async function transpileHtmlToXhtml({ isTocHtml: boolean; isPagelistHtml: boolean; }): Promise<{ + dom: JSDOM; tocParseTree?: TocResourceTreeRoot; pageListParseTree?: PageListResourceTreeRoot; hasMathmlContent: boolean; @@ -380,6 +442,7 @@ async function transpileHtmlToXhtml({ await fs.promises.writeFile(changeExtname(absPath, '.xhtml'), xhtml, 'utf8'); await fs.promises.unlink(absPath); return { + dom, tocParseTree, pageListParseTree, // FIXME: Yes, I recognize this implementation is inadequate. @@ -396,12 +459,18 @@ function buildEpubPackageDocument({ epubVersion, manifest, uid, + docTitle, + docLanguages, + tocXhtml, readingOrder, manifestItems, landmarks, }: Pick[0], 'epubVersion'> & { manifest: PublicationManifest; uid: string; + docTitle: string; + docLanguages: string[]; + tocXhtml: string; readingOrder: string[]; manifestItems: ManifestEntry[]; landmarks: LandmarkEntry[]; @@ -410,12 +479,6 @@ function buildEpubPackageDocument({ slugger.reset(); const bookIdentifier = slugger.slug('bookid'); - const formattedLang = languageTags( - [manifest.inLanguage ?? 'en'].flat()[0], - ).format(); - if (!languageTags(formattedLang).valid()) { - throw languageTags(formattedLang).errors()[0]; - } const normalizeDate = (value: string | number | undefined) => value && `${new Date(value).toISOString().split('.')[0]}Z`; @@ -431,7 +494,10 @@ function buildEpubPackageDocument({ contributor ? [contributor].flat().map((entry, index) => ({ _id: slugger.slug(`${type}-${index + 1}`), - '#text': typeof entry === 'string' ? entry : entry.name, + '#text': + typeof entry === 'string' + ? entry + : normalizeLocalizableString(entry.name, docLanguages), })) : [], ); @@ -455,15 +521,15 @@ function buildEpubPackageDocument({ _xmlns: 'http://www.idpf.org/2007/opf', _version: epubVersion, '_unique-identifier': bookIdentifier, - '_xml:lang': formattedLang, + '_xml:lang': docLanguages[0], metadata: { '_xmlns:dc': 'http://purl.org/dc/elements/1.1/', 'dc:identifier': { _id: bookIdentifier, '#text': uid, }, - 'dc:title': manifest.name, - 'dc:language': formattedLang, + 'dc:title': docTitle, + 'dc:language': docLanguages, 'dc:creator': transformContributor({ // TODO: Define proper order author: manifest.author, @@ -516,20 +582,19 @@ function buildEpubPackageDocument({ const toc = manifestItems.find(({ href }) => href === 'toc.ncx'); return toc ? { _toc: itemIdMap.get(toc.href) } : {}; })(), + ...(manifest.readingProgression + ? { '_page-progression-direction': manifest.readingProgression } + : {}), itemref: readingOrder.map((href) => ({ _idref: itemIdMap.get(href), })), }, guide: { reference: [ - ...[ - (() => { - const tocItem = manifestItems.find(({ properties }) => - properties?.split(' ').includes('nav'), - ); - return tocItem ? { _type: 'toc', _href: tocItem.href } : []; - })(), - ].flat(), + { + _type: 'toc', + _href: manifestItems.find((v) => v.href === tocXhtml)!.href, + }, ...landmarks.map(({ type, href }) => ({ _type: type, _href: href })), ], }, @@ -539,11 +604,11 @@ function buildEpubPackageDocument({ function buildNcx({ toc, - manifest, + docTitle, uid, }: { toc: TocResourceTreeRoot; - manifest: PublicationManifest; + docTitle: string; uid: string; }): string { const slugger = new GithubSlugger(); @@ -596,7 +661,7 @@ function buildNcx({ ], }, docTitle: { - text: manifest.name, + text: docTitle, }, navMap: { navPoint: toc.children.map(transformNavItem), diff --git a/src/webbook.ts b/src/webbook.ts index 5a3e77a9..87b0f1ff 100644 --- a/src/webbook.ts +++ b/src/webbook.ts @@ -108,6 +108,10 @@ export async function retrieveWebbookEntry({ ); debug('Saved webbook resources', resources); + debug( + 'Publication manifest from webbook', + manifest && JSON.stringify(manifest), + ); return { entryHtmlFile: path.join( @@ -164,6 +168,8 @@ export async function supplyWebPublicationManifestForWebbook({ link.setAttribute('href', MANIFEST_FILENAME); document.head.appendChild(link); await fs.promises.writeFile(entryHtmlFile, dom.serialize(), 'utf8'); + + debug('Generated publication manifest from HTML', JSON.stringify(manifest)); return manifest; } diff --git a/yarn.lock b/yarn.lock index 12232a6b..50eccbdc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1255,11 +1255,6 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== -"@types/language-tags@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/language-tags/-/language-tags-1.0.1.tgz#58e04ca5a27031b8cb7c0c91eec653f245b6bde9" - integrity sha512-rTtRNIewaBrkMUfsCe7ES3xsTRQcEVgic2yoDY9hM3D/nwmABcG2du4l4+dTbWvfO8pUYwL4/2TbWFJa/AGc2g== - "@types/lodash@^4.14.168": version "4.14.168" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008" @@ -1925,6 +1920,11 @@ bcp-47-match@^1.0.0: resolved "https://registry.yarnpkg.com/bcp-47-match/-/bcp-47-match-1.0.3.tgz#cb8d03071389a10aff2062b862d6575ffd7cd7ef" integrity sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w== +bcp-47-match@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/bcp-47-match/-/bcp-47-match-2.0.3.tgz#603226f6e5d3914a581408be33b28a53144b09d0" + integrity sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ== + before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -5558,18 +5558,6 @@ knockout@^3.5.0: resolved "https://registry.yarnpkg.com/knockout/-/knockout-3.5.1.tgz#62c81e81843bea2008fd23c575edd9ca978e75cf" integrity sha512-wRJ9I4az0QcsH7A4v4l0enUpkS++MBx0BnL/68KaLzJg7x1qmbjSlwEoCNol7KTYZ+pmtI7Eh2J0Nu6/2Z5J/Q== -language-subtag-registry@^0.3.20: - version "0.3.22" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" - integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== - -language-tags@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.8.tgz#042b4bdb0d4e771a9f8cc2fdc9bb26a52a367312" - integrity sha512-aWAZwgPLS8hJ20lNPm9HNVs4inexz6S2sQa3wx/+ycuutMNE5/IfYxiWYBbi+9UWCQVaXYCOPUl6gFrPR7+jGg== - dependencies: - language-subtag-registry "^0.3.20" - latest-version@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-7.0.0.tgz#843201591ea81a4d404932eeb61240fe04e9e5da" From 8c4353c357cd26960709fad286f144b42f57b788 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Sat, 8 Jul 2023 20:46:47 +0900 Subject: [PATCH 10/42] feat: Add --reading-progression CLI option This option is a workaround for the problem that reading progression cannot be inferred when a single HTML input is specified. If no publication manifest is given, the only way to infer reading progression is to parse the CSS, but it is difficult to provide a complete CSS parser. --- src/commands/build.parser.ts | 8 +++++++- src/commands/build.ts | 1 + src/commands/preview.parser.ts | 6 ++++++ src/commands/preview.ts | 1 + src/config.ts | 4 +++- 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/commands/build.parser.ts b/src/commands/build.parser.ts index f734f1fc..c2761068 100644 --- a/src/commands/build.parser.ts +++ b/src/commands/build.parser.ts @@ -2,9 +2,9 @@ import { Command, Option } from 'commander'; import { BuildCliFlags } from '../build.js'; import { validateTimeoutFlag } from '../config.js'; import { + OutputFormat, checkOutputFormat, detectOutputFormat, - OutputFormat, } from '../output.js'; export function setupBuildParserProgram(): Command { @@ -98,6 +98,12 @@ This option is equivalent with "--preflight press-ready"`, .option('--title ', 'title') .option('--author <author>', 'author') .option('-l, --language <language>', 'language') + .addOption( + new Option( + '--reading-progression <direction>', + 'Direction of reading progression', + ).choices(['ltr', 'rtl']), + ) .addOption( new Option( '--render-mode <mode>', diff --git a/src/commands/build.ts b/src/commands/build.ts index fa9e41bd..4ae31d3d 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -24,6 +24,7 @@ try { author: options.author, language: options.language, pressReady: options.pressReady, + readingProgression: options.readingProgression, renderMode: options.renderMode || 'local', preflight: options.preflight, preflightOption: options.preflightOption, diff --git a/src/commands/preview.parser.ts b/src/commands/preview.parser.ts index b68df2fa..62286fa5 100644 --- a/src/commands/preview.parser.ts +++ b/src/commands/preview.parser.ts @@ -34,6 +34,12 @@ custom(comma separated): 182mm,257mm or 8.5in,11in`, .option('--title <title>', 'title') .option('--author <author>', 'author') .option('-l, --language <language>', 'language') + .addOption( + new Option( + '--reading-progression <direction>', + 'Direction of reading progression', + ).choices(['ltr', 'rtl']), + ) .option('--verbose', 'verbose log output') .option( '--no-sandbox', diff --git a/src/commands/preview.ts b/src/commands/preview.ts index ee1036d3..415ef69b 100644 --- a/src/commands/preview.ts +++ b/src/commands/preview.ts @@ -22,6 +22,7 @@ try { title: options.title, author: options.author, language: options.language, + readingProgression: options.readingProgression, verbose: options.verbose, timeout: options.timeout, sandbox: options.sandbox, diff --git a/src/config.ts b/src/config.ts index 36e99beb..640d0056 100644 --- a/src/config.ts +++ b/src/config.ts @@ -119,6 +119,7 @@ export interface CliFlags { viewer?: string; viewerParam?: string; browser?: 'chromium' | 'firefox' | 'webkit'; + readingProgression?: 'ltr' | 'rtl'; /** @deprecated */ executableChromium?: string; } @@ -477,7 +478,8 @@ export async function mergeConfig<T extends CliFlags>( : DEFAULT_ASSETS; const language = cliFlags.language ?? config?.language ?? null; - const readingProgression = config?.readingProgression ?? undefined; + const readingProgression = + cliFlags.readingProgression ?? config?.readingProgression ?? undefined; const sizeFlag = cliFlags.size ?? config?.size; const size = sizeFlag ? parsePageSize(sizeFlag) : undefined; const cropMarks = cliFlags.cropMarks ?? false; From 1cb6ae504591ea71a3fca00e971ad293a3b4f112 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sat, 8 Jul 2023 21:22:37 +0900 Subject: [PATCH 11/42] chore: Improve logging --- src/html.ts | 35 ++++++++++++++--------------------- src/util.ts | 16 ++++++++++++++++ src/webbook.ts | 12 +++++++++++- 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/html.ts b/src/html.ts index b8632293..c6eb5cc2 100644 --- a/src/html.ts +++ b/src/html.ts @@ -1,3 +1,4 @@ +import chalk from 'chalk'; import cheerio from 'cheerio'; import toHTML from 'hast-util-to-html'; import h from 'hastscript'; @@ -12,7 +13,6 @@ import { DetailError, assertPubManifestSchema, debug, - log, logWarn, } from './util.js'; @@ -213,13 +213,13 @@ export async function fetchLinkedPublicationManifest({ } const href = linkEl.getAttribute('href')!.trim(); let manifest: PublicationManifest; + let manifestJson: string; if (href.startsWith('#')) { const scriptEl = document.getElementById(href.slice(1)); if (scriptEl?.getAttribute('type') !== 'application/ld+json') { return null; } debug(`Found embedded publication manifest: ${href}`); - let manifest: PublicationManifest; try { manifest = JSON.parse(scriptEl.innerHTML); } catch (error) { @@ -229,15 +229,7 @@ export async function fetchLinkedPublicationManifest({ typeof thrownError.stack ?? thrownError.message, ); } - try { - assertPubManifestSchema(manifest, { indent: 2 }); - } catch (error) { - logWarn( - 'Publication manifest validation failed. Processing continues, but some problems may occur.', - ); - log(error); - } - return manifest; + manifestJson = JSON.stringify(manifest, null, 2); } else { debug(`Found linked publication manifest: ${href}`); const url = new URL(href, baseUrl); @@ -245,9 +237,8 @@ export async function fetchLinkedPublicationManifest({ if (!buffer) { throw new Error(`Failed to fetch manifest JSON file: ${url.href}`); } - let manifestJson: string; + manifestJson = buffer.toString(); try { - manifestJson = buffer.toString(); manifest = JSON.parse(manifestJson); } catch (error) { const thrownError = error as Error; @@ -256,16 +247,18 @@ export async function fetchLinkedPublicationManifest({ typeof thrownError.stack ?? thrownError.message, ); } - try { - assertPubManifestSchema(manifest, { json: manifestJson }); - } catch (error) { - logWarn( + } + + try { + assertPubManifestSchema(manifest, { json: manifestJson }); + } catch (error) { + logWarn( + `${chalk.yellowBright( 'Publication manifest validation failed. Processing continues, but some problems may occur.', - ); - log(error); - } - return manifest; + )}\n${error}`, + ); } + return manifest; } export type TocResourceTreeItem = { diff --git a/src/util.ts b/src/util.ts index 1a29c12f..965e1aee 100644 --- a/src/util.ts +++ b/src/util.ts @@ -83,19 +83,35 @@ export function logUpdate(...obj: string[]) { } export function logSuccess(...obj: string[]) { + const { isSpinning, text } = ora; ora.succeed(obj.join(' ')); + if (isSpinning) { + startLogging(text); + } } export function logError(...obj: string[]) { + const { isSpinning, text } = ora; ora.fail(obj.join(' ')); + if (isSpinning) { + startLogging(text); + } } export function logWarn(...obj: string[]) { + const { isSpinning, text } = ora; ora.warn(obj.join(' ')); + if (isSpinning) { + startLogging(text); + } } export function logInfo(...obj: string[]) { + const { isSpinning, text } = ora; ora.info(obj.join(' ')); + if (isSpinning) { + startLogging(text); + } } export class DetailError extends Error { diff --git a/src/webbook.ts b/src/webbook.ts index 87b0f1ff..81eec561 100644 --- a/src/webbook.ts +++ b/src/webbook.ts @@ -14,7 +14,14 @@ import type { PublicationManifest, ResourceCategorization, } from './schema/publication.schema.js'; -import { debug, logError, pathContains, pathEquals, safeGlob } from './util.js'; +import { + debug, + logError, + logUpdate, + pathContains, + pathEquals, + safeGlob, +} from './util.js'; export function prepareWebPublicationDirectory({ outputDir, @@ -37,6 +44,9 @@ export async function retrieveWebbookEntry({ entryHtmlFile: string; manifest: PublicationManifest | null; }> { + if (/^https?:\/\//.test(webbookEntryPath)) { + logUpdate('Fetching remote contents'); + } const resourceLoader = new ResourceLoader(); const { dom, baseUrl } = await getJsdomFromUrlOrFile( webbookEntryPath, From 29b7ab907aaa53f04993c3cebdcefaa15347be6d Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sat, 8 Jul 2023 21:59:32 +0900 Subject: [PATCH 12/42] refactor: Arrange file structure --- src/build.ts | 30 +++++++++---------- src/commands/build.parser.ts | 4 +-- src/{ => input}/config.ts | 20 ++++++------- src/{input.ts => input/input-types.ts} | 0 src/{epub-output.ts => output/epub.ts} | 8 ++--- src/{output.ts => output/output-types.ts} | 0 .../pdf-postprocess.ts} | 12 ++++---- src/{ => output}/pdf.ts | 16 +++++----- src/{ => output}/webbook.ts | 12 ++++---- src/preview.ts | 14 ++++----- src/{builder.ts => processor/compile.ts} | 22 +++++++------- src/{ => processor}/html.ts | 6 ++-- src/{ => processor}/markdown.ts | 0 src/{ => processor}/theme.ts | 4 +-- src/server.ts | 3 +- tests/builder.test.ts | 8 ++--- tests/commandUtil.ts | 2 +- tests/server.test.ts | 4 +-- tests/toc.test.ts | 8 ++--- tests/util.test.ts | 2 +- 20 files changed, 87 insertions(+), 88 deletions(-) rename src/{ => input}/config.ts (98%) rename src/{input.ts => input/input-types.ts} (100%) rename src/{epub-output.ts => output/epub.ts} (99%) rename src/{output.ts => output/output-types.ts} (100%) rename src/{postprocess.ts => output/pdf-postprocess.ts} (97%) rename src/{ => output}/pdf.ts (96%) rename src/{ => output}/webbook.ts (97%) rename src/{builder.ts => processor/compile.ts} (98%) rename src/{ => processor}/html.ts (98%) rename src/{ => processor}/markdown.ts (100%) rename src/{ => processor}/theme.ts (96%) diff --git a/src/build.ts b/src/build.ts index 619946b5..a6b4721e 100644 --- a/src/build.ts +++ b/src/build.ts @@ -2,21 +2,27 @@ import chalk from 'chalk'; import terminalLink from 'terminal-link'; import path from 'upath'; import { getExecutableBrowserPath } from './browser.js'; +import { + CliFlags, + MergedConfig, + collectVivliostyleConfig, + mergeConfig, +} from './input/config.js'; +import { exportEpub } from './output/epub.js'; +import { buildPDF, buildPDFWithContainer } from './output/pdf.js'; +import { + copyWebPublicationAssets, + prepareWebPublicationDirectory, + retrieveWebbookEntry, + supplyWebPublicationManifestForWebbook, +} from './output/webbook.js'; import { checkOverwriteViolation, cleanupWorkspace, compile, copyAssets, prepareThemeDirectory, -} from './builder.js'; -import { - CliFlags, - MergedConfig, - collectVivliostyleConfig, - mergeConfig, -} from './config.js'; -import { exportEpub } from './epub-output.js'; -import { buildPDF, buildPDFWithContainer } from './pdf.js'; +} from './processor/compile.js'; import type { PublicationManifest } from './schema/publication.schema.js'; import { teardownServer } from './server.js'; import { @@ -28,12 +34,6 @@ import { stopLogging, useTmpDirectory, } from './util.js'; -import { - copyWebPublicationAssets, - prepareWebPublicationDirectory, - retrieveWebbookEntry, - supplyWebPublicationManifestForWebbook, -} from './webbook.js'; export interface BuildCliFlags extends CliFlags { output?: { diff --git a/src/commands/build.parser.ts b/src/commands/build.parser.ts index c2761068..bc9d7758 100644 --- a/src/commands/build.parser.ts +++ b/src/commands/build.parser.ts @@ -1,11 +1,11 @@ import { Command, Option } from 'commander'; import { BuildCliFlags } from '../build.js'; -import { validateTimeoutFlag } from '../config.js'; +import { validateTimeoutFlag } from '../input/config.js'; import { OutputFormat, checkOutputFormat, detectOutputFormat, -} from '../output.js'; +} from '../output/output-types.js'; export function setupBuildParserProgram(): Command { // Provide an order-sensitive command parser diff --git a/src/config.ts b/src/input/config.ts similarity index 98% rename from src/config.ts rename to src/input/config.ts index 640d0056..3a7e4b1c 100644 --- a/src/config.ts +++ b/src/input/config.ts @@ -3,37 +3,37 @@ import cheerio from 'cheerio'; import fs from 'fs'; import path from 'upath'; import { pathToFileURL } from 'url'; -import { getExecutableBrowserPath } from './browser.js'; +import { getExecutableBrowserPath } from '../browser.js'; import { EPUB_OUTPUT_VERSION, MANIFEST_FILENAME, TOC_FILENAME, TOC_TITLE, -} from './const.js'; -import { CONTAINER_IMAGE } from './container.js'; +} from '../const.js'; +import { CONTAINER_IMAGE } from '../container.js'; import { InputFormat, ManuscriptMediaType, detectInputFormat, detectManuscriptMediaType, -} from './input.js'; -import { readMarkdownMetadata } from './markdown.js'; +} from '../input/input-types.js'; import { OutputFormat, checkOutputFormat, checkPreflightMode, checkRenderMode, detectOutputFormat, -} from './output.js'; +} from '../output/output-types.js'; +import { readMarkdownMetadata } from '../processor/markdown.js'; +import { parsePackageName } from '../processor/theme.js'; import type { BrowserType, EntryObject, ThemeObject, VivliostyleConfigEntry, VivliostyleConfigSchema, -} from './schema/vivliostyleConfig.schema.js'; -import { PageSize } from './server.js'; -import { parsePackageName } from './theme.js'; +} from '../schema/vivliostyleConfig.schema.js'; +import { PageSize } from '../server.js'; import { DetailError, assertVivliostyleConfigSchema, @@ -46,7 +46,7 @@ import { readJSON, statFileSync, touchTmpFile, -} from './util.js'; +} from '../util.js'; export type ParsedTheme = UriTheme | FileTheme | PackageTheme; diff --git a/src/input.ts b/src/input/input-types.ts similarity index 100% rename from src/input.ts rename to src/input/input-types.ts diff --git a/src/epub-output.ts b/src/output/epub.ts similarity index 99% rename from src/epub-output.ts rename to src/output/epub.ts index f677068b..c8bb94af 100644 --- a/src/epub-output.ts +++ b/src/output/epub.ts @@ -15,7 +15,7 @@ import { EPUB_NS, XML_DECLARATION, cliRoot, -} from './const.js'; +} from '../const.js'; import { PageListResourceTreeRoot, TocResourceTreeItem, @@ -23,7 +23,7 @@ import { getJsdomFromUrlOrFile, parsePageListDocument, parseTocDocument, -} from './html.js'; +} from '../processor/html.js'; import { Contributor, LocalizableStringObject, @@ -31,8 +31,8 @@ import { PublicationLinks, PublicationManifest, ResourceCategorization, -} from './schema/publication.schema.js'; -import { DetailError, debug } from './util.js'; +} from '../schema/publication.schema.js'; +import { DetailError, debug } from '../util.js'; interface ManifestEntry { href: string; diff --git a/src/output.ts b/src/output/output-types.ts similarity index 100% rename from src/output.ts rename to src/output/output-types.ts diff --git a/src/postprocess.ts b/src/output/pdf-postprocess.ts similarity index 97% rename from src/postprocess.ts rename to src/output/pdf-postprocess.ts index 7d53f58b..7f39e010 100644 --- a/src/postprocess.ts +++ b/src/output/pdf-postprocess.ts @@ -13,20 +13,20 @@ import { import * as pressReadyModule from 'press-ready'; import path from 'upath'; import { v1 as uuid } from 'uuid'; -import { MergedConfig } from './config.js'; -import { coreVersion } from './const.js'; +import { coreVersion } from '../const.js'; import { collectVolumeArgs, runContainer, toContainerPath, -} from './container.js'; -import type { Meta, TOCItem } from './global-viewer.js'; -import { PdfOutput } from './output.js'; +} from '../container.js'; +import type { Meta, TOCItem } from '../global-viewer.js'; +import { MergedConfig } from '../input/config.js'; import { checkContainerEnvironment, startLogging, stopLogging, -} from './util.js'; +} from '../util.js'; +import type { PdfOutput } from './output-types.js'; export type SaveOption = Pick<PdfOutput, 'preflight' | 'preflightOption'> & Pick<MergedConfig, 'image'>; diff --git a/src/pdf.ts b/src/output/pdf.ts similarity index 96% rename from src/pdf.ts rename to src/output/pdf.ts index 69c2e916..0d2fd88f 100644 --- a/src/pdf.ts +++ b/src/output/pdf.ts @@ -10,17 +10,15 @@ import { getFullBrowserName, isPlaywrightExecutable, launchBrowser, -} from './browser.js'; -import { ManuscriptEntry, MergedConfig } from './config.js'; +} from '../browser.js'; import { collectVolumeArgs, runContainer, toContainerPath, -} from './container.js'; -import { Meta, Payload, TOCItem } from './global-viewer.js'; -import { PdfOutput } from './output.js'; -import { PageSizeData, PostProcess } from './postprocess.js'; -import { prepareServer } from './server.js'; +} from '../container.js'; +import { Meta, Payload, TOCItem } from '../global-viewer.js'; +import { ManuscriptEntry, MergedConfig } from '../input/config.js'; +import { prepareServer } from '../server.js'; import { checkContainerEnvironment, debug, @@ -30,7 +28,9 @@ import { logUpdate, pathEquals, startLogging, -} from './util.js'; +} from '../util.js'; +import type { PdfOutput } from './output-types.js'; +import { PageSizeData, PostProcess } from './pdf-postprocess.js'; export type BuildPdfOptions = Omit<MergedConfig, 'outputs' | 'input'> & { input: string; diff --git a/src/webbook.ts b/src/output/webbook.ts similarity index 97% rename from src/webbook.ts rename to src/output/webbook.ts index 81eec561..a72f5d84 100644 --- a/src/webbook.ts +++ b/src/output/webbook.ts @@ -1,19 +1,19 @@ import fs from 'node:fs'; import shelljs from 'shelljs'; import path from 'upath'; -import { generateManifest } from './builder.js'; -import { MergedConfig, WebbookEntryConfig } from './config.js'; -import { MANIFEST_FILENAME } from './const.js'; +import { MANIFEST_FILENAME } from '../const.js'; +import { MergedConfig, WebbookEntryConfig } from '../input/config.js'; +import { generateManifest } from '../processor/compile.js'; import { ResourceLoader, fetchLinkedPublicationManifest, getJsdomFromUrlOrFile, -} from './html.js'; +} from '../processor/html.js'; import type { PublicationLinks, PublicationManifest, ResourceCategorization, -} from './schema/publication.schema.js'; +} from '../schema/publication.schema.js'; import { debug, logError, @@ -21,7 +21,7 @@ import { pathContains, pathEquals, safeGlob, -} from './util.js'; +} from '../util.js'; export function prepareWebPublicationDirectory({ outputDir, diff --git a/src/preview.ts b/src/preview.ts index d6045828..4768e325 100644 --- a/src/preview.ts +++ b/src/preview.ts @@ -6,18 +6,18 @@ import { isPlaywrightExecutable, launchBrowser, } from './browser.js'; +import { + CliFlags, + ManuscriptEntry, + collectVivliostyleConfig, + mergeConfig, +} from './input/config.js'; import { cleanupWorkspace, compile, copyAssets, prepareThemeDirectory, -} from './builder.js'; -import { - CliFlags, - collectVivliostyleConfig, - mergeConfig, - ManuscriptEntry, -} from './config.js'; +} from './processor/compile.js'; import { prepareServer } from './server.js'; import { cwd, diff --git a/src/builder.ts b/src/processor/compile.ts similarity index 98% rename from src/builder.ts rename to src/processor/compile.ts index 15b6b619..6eabc5b6 100644 --- a/src/builder.ts +++ b/src/processor/compile.ts @@ -4,25 +4,19 @@ import { lookup as mime } from 'mime-types'; import fs from 'node:fs'; import shelljs from 'shelljs'; import path from 'upath'; +import { TOC_TITLE } from '../const.js'; import { ManuscriptEntry, MergedConfig, ParsedTheme, WebPublicationManifestConfig, -} from './config.js'; -import { TOC_TITLE } from './const.js'; -import { generateTocHtml, isTocHtml, processManuscriptHtml } from './html.js'; -import { processMarkdown } from './markdown.js'; +} from '../input/config.js'; import type { PublicationLinks, PublicationManifest, URL as PublicationURL, -} from './schema/publication.schema.js'; -import type { ArticleEntryObject } from './schema/vivliostyleConfig.schema.js'; -import { - checkThemeInstallationNecessity, - installThemeDependencies, -} from './theme.js'; +} from '../schema/publication.schema.js'; +import type { ArticleEntryObject } from '../schema/vivliostyleConfig.schema.js'; import { DetailError, assertPubManifestSchema, @@ -33,7 +27,13 @@ import { safeGlob, startLogging, useTmpDirectory, -} from './util.js'; +} from '../util.js'; +import { generateTocHtml, isTocHtml, processManuscriptHtml } from './html.js'; +import { processMarkdown } from './markdown.js'; +import { + checkThemeInstallationNecessity, + installThemeDependencies, +} from './theme.js'; function locateThemePath(theme: ParsedTheme, from: string): string | string[] { if (theme.type === 'uri') { diff --git a/src/html.ts b/src/processor/html.ts similarity index 98% rename from src/html.ts rename to src/processor/html.ts index c6eb5cc2..690deabd 100644 --- a/src/html.ts +++ b/src/processor/html.ts @@ -7,14 +7,14 @@ import fs from 'node:fs'; import url from 'node:url'; import prettier from 'prettier'; import path from 'upath'; -import { ManuscriptEntry } from './config.js'; -import type { PublicationManifest } from './schema/publication.schema.js'; +import { ManuscriptEntry } from '../input/config.js'; +import type { PublicationManifest } from '../schema/publication.schema.js'; import { DetailError, assertPubManifestSchema, debug, logWarn, -} from './util.js'; +} from '../util.js'; const virtualConsole = new jsdom.VirtualConsole(); virtualConsole.on('error', (message) => { diff --git a/src/markdown.ts b/src/processor/markdown.ts similarity index 100% rename from src/markdown.ts rename to src/processor/markdown.ts diff --git a/src/theme.ts b/src/processor/theme.ts similarity index 96% rename from src/theme.ts rename to src/processor/theme.ts index fdaa860f..3a09d9bc 100644 --- a/src/theme.ts +++ b/src/processor/theme.ts @@ -3,8 +3,8 @@ import fs from 'node:fs'; import npa from 'npm-package-arg'; import shelljs from 'shelljs'; import path from 'upath'; -import { MergedConfig } from './config.js'; -import { beforeExitHandlers, DetailError } from './util.js'; +import type { MergedConfig } from '../input/config.js'; +import { beforeExitHandlers, DetailError } from '../util.js'; // Rename `packages` directory into `node_modules` while Arborist works const temporaryMovePackagesDirectrory = async <T = unknown>( diff --git a/src/server.ts b/src/server.ts index 6ea27445..f4b67dc4 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,8 +1,7 @@ import http from 'node:http'; -import resolvePkg from 'resolve-pkg'; +import { pathToFileURL, URL } from 'node:url'; import handler from 'serve-handler'; import upath from 'upath'; -import { pathToFileURL, URL } from 'node:url'; import { viewerRoot } from './const.js'; import { beforeExitHandlers, diff --git a/tests/builder.test.ts b/tests/builder.test.ts index 4c5d1787..7f4dcb30 100644 --- a/tests/builder.test.ts +++ b/tests/builder.test.ts @@ -1,16 +1,16 @@ +import { JSDOM } from 'jsdom'; import assert from 'node:assert'; import fs from 'node:fs'; -import { JSDOM } from 'jsdom'; import shelljs from 'shelljs'; +import { MergedConfig } from '../src/input/config.js'; import { checkOverwriteViolation, cleanupWorkspace, compile, copyAssets, prepareThemeDirectory, -} from '../src/builder.js'; -import { MergedConfig } from '../src/config.js'; -import { checkThemeInstallationNecessity } from '../src/theme.js'; +} from '../src/processor/compile.js'; +import { checkThemeInstallationNecessity } from '../src/processor/theme.js'; import { assertArray, assertSingleItem, diff --git a/tests/commandUtil.ts b/tests/commandUtil.ts index 121ea806..9a7e86bc 100644 --- a/tests/commandUtil.ts +++ b/tests/commandUtil.ts @@ -6,7 +6,7 @@ import { collectVivliostyleConfig, mergeConfig, MergedConfig, -} from '../src/config.js'; +} from '../src/input/config.js'; export const rootPath = path.join(URL.fileURLToPath(import.meta.url), '../..'); diff --git a/tests/server.test.ts b/tests/server.test.ts index 3d6984a0..6c12bc43 100644 --- a/tests/server.test.ts +++ b/tests/server.test.ts @@ -1,6 +1,6 @@ -import path from 'upath'; import { pathToFileURL } from 'node:url'; -import { getViewerFullUrl } from '../src/server'; +import path from 'upath'; +import { getViewerFullUrl } from '../src/server.js'; import { maskConfig, rootPath } from './commandUtil.js'; // Giving up run tests using ESM mocks due to lack of Jest’s support diff --git a/tests/toc.test.ts b/tests/toc.test.ts index 57d68a48..fd195fd0 100644 --- a/tests/toc.test.ts +++ b/tests/toc.test.ts @@ -1,10 +1,10 @@ +import { JSDOM } from 'jsdom'; import assert from 'node:assert'; import fs from 'node:fs'; -import { JSDOM } from 'jsdom'; import shelljs from 'shelljs'; -import { compile, prepareThemeDirectory } from '../src/builder'; -import { MergedConfig } from '../src/config'; -import { generateTocHtml } from '../src/html'; +import { MergedConfig } from '../src/input/config.js'; +import { compile, prepareThemeDirectory } from '../src/processor/compile.js'; +import { generateTocHtml } from '../src/processor/html.js'; import { assertSingleItem, getMergedConfig, diff --git a/tests/util.test.ts b/tests/util.test.ts index ac3b554c..83cbf0c1 100644 --- a/tests/util.test.ts +++ b/tests/util.test.ts @@ -1,4 +1,4 @@ -import { safeGlob } from '../src/util'; +import { safeGlob } from '../src/util.js'; import { resolveFixture } from './commandUtil.js'; it('safeGlob follows symbolic link: ../..', async () => { From 2229185867ede6547a0f770a3d63440061e44762 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sat, 8 Jul 2023 22:16:06 +0900 Subject: [PATCH 13/42] chore: Allow undefined title/author --- src/input/config.ts | 16 ++++++++-------- src/processor/compile.ts | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/input/config.ts b/src/input/config.ts index 3a7e4b1c..862165c6 100644 --- a/src/input/config.ts +++ b/src/input/config.ts @@ -160,9 +160,9 @@ export type MergedConfig = { customUserStyle: string | undefined; singleDoc: boolean; quick: boolean; - title: string; - author: string; - language: string | null; + title: string | undefined; + author: string | undefined; + language: string | undefined; readingProgression: 'ltr' | 'rtl' | undefined; vfmOptions: { hardLineBreaks: boolean; @@ -477,7 +477,7 @@ export async function mergeConfig<T extends CliFlags>( : [config.includeAssets] : DEFAULT_ASSETS; - const language = cliFlags.language ?? config?.language ?? null; + const language = cliFlags.language ?? config?.language ?? undefined; const readingProgression = cliFlags.readingProgression ?? config?.readingProgression ?? undefined; const sizeFlag = cliFlags.size ?? config?.size; @@ -729,7 +729,7 @@ async function composeSingleInputConfig<T extends CliFlags>( }); } - let fallbackTitle: string = ''; + let fallbackTitle: string | undefined; const manifestDeclaration = await (async (): Promise<ManifestConfig> => { if (input.format === 'markdown') { // create temporary manifest file @@ -768,7 +768,7 @@ async function composeSingleInputConfig<T extends CliFlags>( input, exportAliases, title: title || fallbackTitle, - author: author || '', + author: author, }; } @@ -874,7 +874,7 @@ async function composeProjectConfig<T extends CliFlags>( ); } - let fallbackProjectTitle: string = ''; + let fallbackProjectTitle: string | undefined; if (!projectTitle) { if (entries.length === 1 && entries[0].title) { fallbackProjectTitle = entries[0].title; @@ -908,7 +908,7 @@ async function composeProjectConfig<T extends CliFlags>( exportAliases: [], manifestPath: path.join(workspaceDir, MANIFEST_FILENAME), title: projectTitle || fallbackProjectTitle, - author: projectAuthor || '', + author: projectAuthor, }; } diff --git a/src/processor/compile.ts b/src/processor/compile.ts index 6eabc5b6..4940680b 100644 --- a/src/processor/compile.ts +++ b/src/processor/compile.ts @@ -125,9 +125,9 @@ export function generateManifest( outputPath: string, entryContextDir: string, options: { - title: string; - author: string; - language?: string | null; + title?: string; + author?: string; + language?: string; readingProgression?: 'ltr' | 'rtl'; modified: string; entries: ArticleEntryObject[]; @@ -182,13 +182,13 @@ export function generateManifest( '@context': ['https://schema.org', 'https://www.w3.org/ns/pub-context'], type: 'Book', conformsTo: 'https://github.com/vivliostyle/vivliostyle-cli', - author: options.author, + ...(options.title && { name: options.title }), + ...(options.author && { author: options.author }), ...(options.language && { inLanguage: options.language }), ...(options.readingProgression && { readingProgression: options.readingProgression, }), dateModified: options.modified, - name: options.title, readingOrder: entries, resources, links, From 52f12860b2127271f8923e2162a12ca24d263e8d Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sat, 8 Jul 2023 22:16:30 +0900 Subject: [PATCH 14/42] test: Update snapshots --- tests/__snapshots__/config.test.ts.snap | 100 +++++++++++------------- 1 file changed, 46 insertions(+), 54 deletions(-) diff --git a/tests/__snapshots__/config.test.ts.snap b/tests/__snapshots__/config.test.ts.snap index 4c1f7967..02b07882 100644 --- a/tests/__snapshots__/config.test.ts.snap +++ b/tests/__snapshots__/config.test.ts.snap @@ -2,6 +2,7 @@ exports[`allow a loose specifier of a theme direcory: valid.5.config.js 1`] = ` Object { + "author": "pkgAuthor", "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -49,11 +50,7 @@ Object { "entry": "__WORKSPACE__/tests/fixtures/config/publication.json", "format": "pub-manifest", }, - "language": null, - "manifestAutoGenerate": Object { - "author": "pkgAuthor", - "title": "pkgName", - }, + "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", "outputs": Array [ Object { @@ -89,6 +86,7 @@ Object { }, "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, + "title": "pkgName", "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, @@ -102,6 +100,7 @@ Object { exports[`imports a EPUB OPF file 1`] = ` Object { + "author": undefined, "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -134,7 +133,7 @@ Object { "entry": "__WORKSPACE__/tests/fixtures/epubs/adaptive/OPS/content.opf", "format": "epub-opf", }, - "language": null, + "language": undefined, "outputs": Array [ Object { "format": "pdf", @@ -153,6 +152,7 @@ Object { "themeIndexes": Set {}, "themesDir": "__WORKSPACE__/tests/fixtures/epubs/adaptive/OPS/themes", "timeout": 120000, + "title": undefined, "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, @@ -166,6 +166,7 @@ Object { exports[`imports a EPUB file 1`] = ` Object { + "author": undefined, "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -198,7 +199,7 @@ Object { "entry": "__WORKSPACE__/tests/fixtures/epubs/adaptive.epub", "format": "epub", }, - "language": null, + "language": undefined, "outputs": Array [ Object { "format": "pdf", @@ -217,6 +218,7 @@ Object { "themeIndexes": Set {}, "themesDir": "__WORKSPACE__/tests/fixtures/epubs/themes", "timeout": 120000, + "title": undefined, "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, @@ -230,6 +232,7 @@ Object { exports[`imports a https URL 1`] = ` Object { + "author": undefined, "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -261,7 +264,7 @@ Object { "entry": "https://vivliostyle.github.io/vivliostyle_doc/ja/vivliostyle-user-group-vol1/", "format": "webbook", }, - "language": null, + "language": undefined, "outputs": Array [ Object { "format": "pdf", @@ -280,6 +283,7 @@ Object { "themeIndexes": Set {}, "themesDir": "__WORKSPACE__/themes", "timeout": 120000, + "title": undefined, "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, @@ -294,6 +298,7 @@ Object { exports[`imports a webbook compliant to Readium Web publication 1`] = ` Object { + "author": undefined, "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -325,8 +330,7 @@ Object { "entry": "__WORKSPACE__/tests/fixtures/webbooks/readium-webpub/manifest.jsonld", "format": "pub-manifest", }, - "language": null, - "manifestAutoGenerate": null, + "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/webbooks/readium-webpub/manifest.jsonld", "outputs": Array [ Object { @@ -343,6 +347,7 @@ Object { "themeIndexes": Set {}, "themesDir": "__WORKSPACE__/tests/fixtures/webbooks/readium-webpub/themes", "timeout": 120000, + "title": undefined, "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, @@ -356,6 +361,7 @@ Object { exports[`imports a webbook compliant to W3C Web publication 1`] = ` Object { + "author": undefined, "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -387,8 +393,7 @@ Object { "entry": "__WORKSPACE__/tests/fixtures/webbooks/w3c-webpub/publication.json", "format": "pub-manifest", }, - "language": null, - "manifestAutoGenerate": null, + "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/webbooks/w3c-webpub/publication.json", "outputs": Array [ Object { @@ -405,6 +410,7 @@ Object { "themeIndexes": Set {}, "themesDir": "__WORKSPACE__/tests/fixtures/webbooks/w3c-webpub/themes", "timeout": 120000, + "title": undefined, "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, @@ -418,6 +424,7 @@ Object { exports[`imports single html file 1`] = ` Object { + "author": undefined, "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -449,7 +456,7 @@ Object { "entry": "__WORKSPACE__/tests/fixtures/config/sample.html", "format": "webbook", }, - "language": null, + "language": undefined, "outputs": Array [ Object { "format": "pdf", @@ -468,6 +475,7 @@ Object { "themeIndexes": Set {}, "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, + "title": undefined, "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, @@ -482,6 +490,7 @@ Object { exports[`override option by CLI command: valid.1.config.js 1`] = ` Object { + "author": "myAuthor", "bleed": undefined, "browserType": "firefox", "cover": "__WORKSPACE__/tests/fixtures/config/cover.png", @@ -554,10 +563,6 @@ Object { "format": "pub-manifest", }, "language": "myLanguage", - "manifestAutoGenerate": Object { - "author": "myAuthor", - "title": "myTitle", - }, "manifestPath": "__WORKSPACE__/tests/fixtures/config/workspaceDir/publication.json", "outputs": Array [ Object { @@ -601,6 +606,7 @@ Object { }, "themesDir": "__WORKSPACE__/tests/fixtures/config/workspaceDir/themes", "timeout": 42000, + "title": "myTitle", "verbose": false, "vfmOptions": Object { "disableFormatHtml": true, @@ -614,6 +620,7 @@ Object { exports[`parse array of config: valid.1.config.js 1`] = ` Object { + "author": "author", "bleed": undefined, "browserType": "firefox", "cover": "__WORKSPACE__/tests/fixtures/config/cover.png", @@ -690,10 +697,6 @@ Object { "format": "pub-manifest", }, "language": "language", - "manifestAutoGenerate": Object { - "author": "author", - "title": "title", - }, "manifestPath": "__WORKSPACE__/tests/fixtures/config/workspaceDir/publication.json", "outputs": Array [ Object { @@ -744,6 +747,7 @@ Object { }, "themesDir": "__WORKSPACE__/tests/fixtures/config/workspaceDir/themes", "timeout": 1, + "title": "title", "verbose": false, "vfmOptions": Object { "disableFormatHtml": true, @@ -757,6 +761,7 @@ Object { exports[`parse array of config: valid.2.config.js 1`] = ` Object { + "author": "pkgAuthor", "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -796,11 +801,7 @@ Object { "entry": "__WORKSPACE__/tests/fixtures/config/publication.json", "format": "pub-manifest", }, - "language": null, - "manifestAutoGenerate": Object { - "author": "pkgAuthor", - "title": "pkgName", - }, + "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", "outputs": Array [ Object { @@ -820,6 +821,7 @@ Object { "themeIndexes": Set {}, "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, + "title": "pkgName", "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, @@ -833,6 +835,7 @@ Object { exports[`parse array of config: valid.3.config.js 1`] = ` Object { + "author": "pkgAuthor", "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -872,11 +875,7 @@ Object { "entry": "__WORKSPACE__/tests/fixtures/config/publication.json", "format": "pub-manifest", }, - "language": null, - "manifestAutoGenerate": Object { - "author": "pkgAuthor", - "title": "example", - }, + "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", "outputs": Array [ Object { @@ -896,6 +895,7 @@ Object { "themeIndexes": Set {}, "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, + "title": "example", "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, @@ -909,6 +909,7 @@ Object { exports[`parse vivliostyle config: valid.1.config.js 1`] = ` Object { + "author": "author", "bleed": undefined, "browserType": "firefox", "cover": "__WORKSPACE__/tests/fixtures/config/cover.png", @@ -985,10 +986,6 @@ Object { "format": "pub-manifest", }, "language": "language", - "manifestAutoGenerate": Object { - "author": "author", - "title": "title", - }, "manifestPath": "__WORKSPACE__/tests/fixtures/config/workspaceDir/publication.json", "outputs": Array [ Object { @@ -1039,6 +1036,7 @@ Object { }, "themesDir": "__WORKSPACE__/tests/fixtures/config/workspaceDir/themes", "timeout": 1, + "title": "title", "verbose": false, "vfmOptions": Object { "disableFormatHtml": true, @@ -1052,6 +1050,7 @@ Object { exports[`parse vivliostyle config: valid.2.config.js 1`] = ` Object { + "author": "pkgAuthor", "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -1091,11 +1090,7 @@ Object { "entry": "__WORKSPACE__/tests/fixtures/config/publication.json", "format": "pub-manifest", }, - "language": null, - "manifestAutoGenerate": Object { - "author": "pkgAuthor", - "title": "pkgName", - }, + "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", "outputs": Array [ Object { @@ -1115,6 +1110,7 @@ Object { "themeIndexes": Set {}, "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, + "title": "pkgName", "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, @@ -1128,6 +1124,7 @@ Object { exports[`parse vivliostyle config: valid.3.config.js 1`] = ` Object { + "author": "pkgAuthor", "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -1167,11 +1164,7 @@ Object { "entry": "__WORKSPACE__/tests/fixtures/config/publication.json", "format": "pub-manifest", }, - "language": null, - "manifestAutoGenerate": Object { - "author": "pkgAuthor", - "title": "example", - }, + "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", "outputs": Array [ Object { @@ -1191,6 +1184,7 @@ Object { "themeIndexes": Set {}, "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, + "title": "example", "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, @@ -1204,6 +1198,7 @@ Object { exports[`yields a config with single input and vivliostyle config 1`] = ` Object { + "author": "author", "bleed": undefined, "browserType": "firefox", "cover": "__WORKSPACE__/tests/fixtures/config/cover.png", @@ -1261,11 +1256,8 @@ Object { "format": "markdown", }, "language": "language", - "manifestAutoGenerate": Object { - "author": "author", - "title": "title", - }, "manifestPath": "__SNIP__", + "needToGenerateManifest": true, "outputs": Array [ Object { "format": "pdf", @@ -1309,6 +1301,7 @@ Object { }, "themesDir": "__WORKSPACE__/tests/fixtures/config/workspaceDir/themes", "timeout": 1, + "title": "title", "verbose": false, "vfmOptions": Object { "disableFormatHtml": true, @@ -1322,6 +1315,7 @@ Object { exports[`yields a config with single markdown 1`] = ` Object { + "author": undefined, "bleed": undefined, "browserType": "chromium", "cover": undefined, @@ -1361,12 +1355,9 @@ Object { "entry": "__WORKSPACE__/tests/fixtures/config/sample.md", "format": "markdown", }, - "language": null, - "manifestAutoGenerate": Object { - "author": "", - "title": "Yuno", - }, + "language": undefined, "manifestPath": "__SNIP__", + "needToGenerateManifest": true, "outputs": Array [ Object { "format": "pdf", @@ -1385,6 +1376,7 @@ Object { "themeIndexes": Set {}, "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, + "title": "Yuno", "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, From f0eb2a881f9917eb0381368ae66f96a9eecbb087 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sat, 8 Jul 2023 22:31:03 +0900 Subject: [PATCH 15/42] chore: Fix degrade --- src/input/config.ts | 1 + tests/__snapshots__/config.test.ts.snap | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/input/config.ts b/src/input/config.ts index 862165c6..147d29c9 100644 --- a/src/input/config.ts +++ b/src/input/config.ts @@ -909,6 +909,7 @@ async function composeProjectConfig<T extends CliFlags>( manifestPath: path.join(workspaceDir, MANIFEST_FILENAME), title: projectTitle || fallbackProjectTitle, author: projectAuthor, + needToGenerateManifest: true, }; } diff --git a/tests/__snapshots__/config.test.ts.snap b/tests/__snapshots__/config.test.ts.snap index 02b07882..f6e40995 100644 --- a/tests/__snapshots__/config.test.ts.snap +++ b/tests/__snapshots__/config.test.ts.snap @@ -52,6 +52,7 @@ Object { }, "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", + "needToGenerateManifest": true, "outputs": Array [ Object { "format": "pdf", @@ -564,6 +565,7 @@ Object { }, "language": "myLanguage", "manifestPath": "__WORKSPACE__/tests/fixtures/config/workspaceDir/publication.json", + "needToGenerateManifest": true, "outputs": Array [ Object { "format": "pdf", @@ -698,6 +700,7 @@ Object { }, "language": "language", "manifestPath": "__WORKSPACE__/tests/fixtures/config/workspaceDir/publication.json", + "needToGenerateManifest": true, "outputs": Array [ Object { "format": "pdf", @@ -803,6 +806,7 @@ Object { }, "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", + "needToGenerateManifest": true, "outputs": Array [ Object { "format": "pdf", @@ -877,6 +881,7 @@ Object { }, "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", + "needToGenerateManifest": true, "outputs": Array [ Object { "format": "pdf", @@ -987,6 +992,7 @@ Object { }, "language": "language", "manifestPath": "__WORKSPACE__/tests/fixtures/config/workspaceDir/publication.json", + "needToGenerateManifest": true, "outputs": Array [ Object { "format": "pdf", @@ -1092,6 +1098,7 @@ Object { }, "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", + "needToGenerateManifest": true, "outputs": Array [ Object { "format": "pdf", @@ -1166,6 +1173,7 @@ Object { }, "language": undefined, "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", + "needToGenerateManifest": true, "outputs": Array [ Object { "format": "pdf", From 2931037ac02715c85cab06263d7816af4eb88a03 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sat, 8 Jul 2023 22:44:33 +0900 Subject: [PATCH 16/42] chore: Install canvas This is necessary for JSDOM canvas support https://github.com/jsdom/jsdom/tree/master#canvas-support --- package.json | 1 + yarn.lock | 112 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 107 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index f4877bed..7fb02996 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "archiver": "^5.3.1", "bcp-47-match": "^2.0.3", "better-ajv-errors": "^1.2.0", + "canvas": "^2.11.2", "chalk": "^4.1.2", "cheerio": "^1.0.0-rc.10", "chokidar": "^3.5.2", diff --git a/yarn.lock b/yarn.lock index 50eccbdc..d75a1116 100644 --- a/yarn.lock +++ b/yarn.lock @@ -747,6 +747,21 @@ resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== +"@mapbox/node-pre-gyp@^1.0.0": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" + integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1735,6 +1750,14 @@ archiver@^5.3.1: tar-stream "^2.2.0" zip-stream "^4.1.0" +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + are-we-there-yet@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" @@ -2198,6 +2221,15 @@ caniuse-lite@^1.0.30001400: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz#ab7371faeb4adff4b74dad1718a6fd122e45d9cb" integrity sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A== +canvas@^2.11.2: + version "2.11.2" + resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.11.2.tgz#553d87b1e0228c7ac0fc72887c3adbac4abbd860" + integrity sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.0" + nan "^2.17.0" + simple-get "^3.0.3" + ccount@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" @@ -2473,7 +2505,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-support@^1.1.3: +color-support@^1.1.2, color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== @@ -2572,7 +2604,7 @@ configstore@^6.0.0: write-file-atomic "^3.0.3" xdg-basedir "^5.0.1" -console-control-strings@^1.1.0: +console-control-strings@^1.0.0, console-control-strings@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== @@ -2939,6 +2971,13 @@ decimal.js@^10.4.3: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" @@ -3033,6 +3072,11 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== +detect-libc@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" + integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -3751,6 +3795,21 @@ functions-have-names@^1.2.2: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + gauge@^4.0.3: version "4.0.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" @@ -5757,7 +5816,7 @@ macos-release@^3.0.1: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-3.1.0.tgz#6165bb0736ae567ed6649e36ce6a24d87cbb7aca" integrity sha512-/M/R0gCDgM+Cv1IuBG1XGdfTFnMEG6PZeT+KGWHO/OG+imqmaD9CH5vHBTycEM3+Kc4uG2Il+tFAuUWLqQOeUA== -make-dir@^3.0.0: +make-dir@^3.0.0, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -5998,6 +6057,11 @@ mimic-fn@^4.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== +mimic-response@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + mimic-response@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" @@ -6181,6 +6245,11 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" +nan@^2.17.0: + version "2.17.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" + integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -6291,6 +6360,13 @@ nodemon@^2.0.12: touch "^3.1.0" undefsafe "^2.0.5" +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + nopt@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" @@ -6445,6 +6521,16 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + npmlog@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" @@ -6477,7 +6563,7 @@ nwsapi@^2.2.4: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.5.tgz#a52744c61b3889dd44b0a158687add39b8d935e2" integrity sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ== -object-assign@^4.0.1: +object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -7858,11 +7944,25 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55" + integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-update-notifier@^1.0.7: version "1.1.0" resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82" @@ -9005,7 +9105,7 @@ which@^3.0.0: dependencies: isexe "^2.0.0" -wide-align@^1.1.5: +wide-align@^1.1.2, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== From 940d04e39fdb73580ab05539a90ad645b83de372 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sat, 8 Jul 2023 22:44:55 +0900 Subject: [PATCH 17/42] chore: Fix single document toc --- src/output/epub.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/output/epub.ts b/src/output/epub.ts index c8bb94af..09c8cc9e 100644 --- a/src/output/epub.ts +++ b/src/output/epub.ts @@ -390,16 +390,17 @@ async function transpileHtmlToXhtml({ nav.setAttribute('epub:type', 'toc'); nav.setAttribute('hidden', ''); nav.innerHTML = '<ol><li></li></ol>'; - const span = document.createElement('span'); - span.textContent = document.title; - nav.querySelector('li')!.appendChild(span); + const a = document.createElement('a'); + a.textContent = document.title; + a.href = changeExtname(target, '.xhtml'); + nav.querySelector('li')!.appendChild(a); document.body.appendChild(nav); tocParseTree = { element: nav, children: [ { element: nav.querySelector('li')!, - label: span, + label: a, }, ], }; From d2a28058e3a9ec81f567fda59deb95269c4b5648 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sun, 9 Jul 2023 02:36:58 +0900 Subject: [PATCH 18/42] chore: Improve remote resource fetcher --- package.json | 4 ++- src/build.ts | 1 + src/output/epub.ts | 52 +++++++++++++++++-------------- src/output/webbook.ts | 72 +++++++++++++++++++++++++++++++++---------- src/processor/html.ts | 6 ++-- types/jsdom.d.ts | 5 +++ yarn.lock | 5 +++ 7 files changed, 101 insertions(+), 44 deletions(-) diff --git a/package.json b/package.json index 7fb02996..35194982 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,8 @@ "upath": "^2.0.1", "uuid": "^8.3.2", "vfile": "^4.2.1", - "w3c-xmlserializer": "^4.0.0" + "w3c-xmlserializer": "^4.0.0", + "whatwg-mimetype": "^3.0.0" }, "devDependencies": { "@release-it/conventional-changelog": "^5.1.1", @@ -82,6 +83,7 @@ "@types/tmp": "^0.2.1", "@types/uuid": "^8.3.1", "@types/w3c-xmlserializer": "^2.0.2", + "@types/whatwg-mimetype": "^3.0.0", "file-type": "^16.5.3", "husky": "^4.3.8", "jest": "^29.3.1", diff --git a/src/build.ts b/src/build.ts index a6b4721e..fc7a5c28 100644 --- a/src/build.ts +++ b/src/build.ts @@ -160,6 +160,7 @@ export async function build(cliFlags: BuildCliFlags) { (await supplyWebPublicationManifestForWebbook({ ...config, entryHtmlFile: ret.entryHtmlFile, + outputDir, })); } else { continue; diff --git a/src/output/epub.ts b/src/output/epub.ts index 09c8cc9e..e9c060cc 100644 --- a/src/output/epub.ts +++ b/src/output/epub.ts @@ -50,6 +50,23 @@ const changeExtname = (filepath: string, newExt: string) => { return `${filepath.slice(0, -ext.length)}${newExt}`; }; +const getRelativeHref = (target: string, baseUrl: string, rootUrl: string) => { + const absBasePath = path.join('/', baseUrl); + const absRootPath = path.join('/', rootUrl); + const hrefUrl = new URL(target, url.pathToFileURL(absBasePath)); + if (hrefUrl.protocol !== 'file:') { + return target; + } + if (/\.html?$/.test(hrefUrl.pathname)) { + hrefUrl.pathname = changeExtname(hrefUrl.pathname, '.xhtml'); + } + const pathname = path.posix.relative( + url.pathToFileURL(path.dirname(absRootPath)).pathname, + hrefUrl.pathname, + ); + return `${pathname}${hrefUrl.search}${hrefUrl.hash}`; +}; + const normalizeLocalizableString = ( value: LocalizableStringOrObject | undefined, availableLanguages: string[], @@ -206,7 +223,6 @@ export async function exportEpub({ try { parseResult = await transpileHtmlToXhtml({ target, - htmlFiles, contextDir: path.join(tmpDir, 'EPUB'), landmarks, isTocHtml, @@ -261,6 +277,7 @@ export async function exportEpub({ toc: tocParseTree, docTitle, uid, + tocHtml, }), 'utf8', ); @@ -311,14 +328,12 @@ export async function exportEpub({ async function transpileHtmlToXhtml({ target, - htmlFiles, contextDir, landmarks, isTocHtml, isPagelistHtml, }: { target: string; - htmlFiles: string[]; contextDir: string; landmarks: LandmarkEntry[]; isTocHtml: boolean; @@ -333,30 +348,15 @@ async function transpileHtmlToXhtml({ hasSvgContent: boolean; }> { const absPath = path.join(contextDir, target); - const htmlFileUrls = htmlFiles.map((p) => - url.pathToFileURL(path.join(contextDir, p)), - ); const { dom } = await getJsdomFromUrlOrFile(absPath); const { document } = dom.window; + // `xmlns` will be supplied in later serialization process + document.documentElement.removeAttribute('xmlns'); document.documentElement.setAttribute('xmlns:epub', EPUB_NS); document.querySelectorAll('a[href]').forEach((el) => { const href = el.getAttribute('href')!; - const hrefUrl = new URL(href, url.pathToFileURL(absPath)); - if ( - htmlFileUrls.some( - (url) => - url.pathname === hrefUrl.pathname || - changeExtname(url.pathname, '') === hrefUrl.pathname, - ) - ) { - hrefUrl.pathname = changeExtname(hrefUrl.pathname, '.xhtml'); - } - const pathname = path.posix.relative( - url.pathToFileURL(path.dirname(absPath)).pathname, - hrefUrl.pathname, - ); - el.setAttribute('href', `${pathname}${hrefUrl.search}${hrefUrl.hash}`); + el.setAttribute('href', getRelativeHref(href, target, target)); }); const replaceWithNavElement = (el: Element) => { @@ -392,7 +392,7 @@ async function transpileHtmlToXhtml({ nav.innerHTML = '<ol><li></li></ol>'; const a = document.createElement('a'); a.textContent = document.title; - a.href = changeExtname(target, '.xhtml'); + a.href = changeExtname(path.basename(target), '.xhtml'); nav.querySelector('li')!.appendChild(a); document.body.appendChild(nav); tocParseTree = { @@ -607,10 +607,12 @@ function buildNcx({ toc, docTitle, uid, + tocHtml, }: { toc: TocResourceTreeRoot; docTitle: string; uid: string; + tocHtml: string; }): string { const slugger = new GithubSlugger(); slugger.reset(); @@ -628,7 +630,11 @@ function buildNcx({ ...(item.label.tagName === 'A' && item.label.getAttribute('href') ? { content: { - _src: item.label.getAttribute('href'), + _src: getRelativeHref( + item.label.getAttribute('href')!, + tocHtml, + 'toc.ncx', + ), }, } : {}), diff --git a/src/output/webbook.ts b/src/output/webbook.ts index a72f5d84..70bb724a 100644 --- a/src/output/webbook.ts +++ b/src/output/webbook.ts @@ -1,6 +1,7 @@ import fs from 'node:fs'; import shelljs from 'shelljs'; import path from 'upath'; +import MIMEType from 'whatwg-mimetype'; import { MANIFEST_FILENAME } from '../const.js'; import { MergedConfig, WebbookEntryConfig } from '../input/config.js'; import { generateManifest } from '../processor/compile.js'; @@ -57,7 +58,9 @@ export async function retrieveWebbookEntry({ resourceLoader, baseUrl, }); - const rootUrl = new URL('.', baseUrl).href; + const rootUrl = /^https?:\/\//.test(baseUrl) + ? new URL('/', baseUrl).href + : new URL('.', baseUrl).href; const pathContains = (url: string) => !path.posix.relative(rootUrl, url).startsWith('..'); const retriever = new Map(resourceLoader.fetcherMap); @@ -94,20 +97,35 @@ export async function retrieveWebbookEntry({ } } - const resources: string[] = []; + const normalizeToLocalPath = (urlString: string, mimeType?: string) => { + const url = new URL(urlString); + url.hash = ''; + let relTarget = path.posix.relative(rootUrl, url.href); + if (!relTarget || (mimeType === 'text/html' && !path.extname(relTarget))) { + relTarget = path.join(relTarget, 'index.html'); + } + return relTarget; + }; + const fetchedResources: { url: string; encodingFormat?: string }[] = []; await Promise.allSettled( - Array.from(retriever.entries()).flatMap(([url, fetchPromise]) => { + Array.from(retriever.entries()).flatMap(([url, fetcher]) => { if (!pathContains(url)) { return []; } - return fetchPromise + return fetcher .then(async (buffer) => { - let relTarget = path.relative(rootUrl, url); - if (!relTarget || !path.extname(relTarget)) { - relTarget = path.join(relTarget, 'index.html'); + let encodingFormat: string | undefined; + try { + const contentType = fetcher.response?.headers['content-type']; + if (contentType) { + encodingFormat = new MIMEType(contentType).essence; + } + } catch (e) { + /* NOOP */ } + const relTarget = normalizeToLocalPath(url, encodingFormat); const target = path.join(outputDir, relTarget); - resources.push(relTarget); + fetchedResources.push({ url: relTarget, encodingFormat }); await fs.promises.mkdir(path.dirname(target), { recursive: true }); await fs.promises.writeFile(target, buffer); }) @@ -117,16 +135,32 @@ export async function retrieveWebbookEntry({ }), ); - debug('Saved webbook resources', resources); + if (manifest) { + const referencedContents = [ + ...[manifest.readingOrder || []].flat(), + ...[manifest.resources || []].flat(), + ].map((v) => (typeof v === 'string' ? v : v.url)); + manifest.resources = [ + ...[manifest.resources || []].flat(), + ...fetchedResources.filter( + ({ url }) => !referencedContents.includes(url), + ), + ]; + } + + debug( + 'Saved webbook resources', + fetchedResources.map((v) => v.url), + ); debug( 'Publication manifest from webbook', - manifest && JSON.stringify(manifest), + manifest && JSON.stringify(manifest, null, 2), ); return { entryHtmlFile: path.join( outputDir, - new URL(baseUrl).pathname.split('/').at(-1) || 'index.html', + normalizeToLocalPath(baseUrl, 'text/html'), ), manifest, }; @@ -134,12 +168,14 @@ export async function retrieveWebbookEntry({ export async function supplyWebPublicationManifestForWebbook({ entryHtmlFile, + outputDir, ...config }: Pick< MergedConfig, 'language' | 'title' | 'author' | 'readingProgression' > & { entryHtmlFile: string; + outputDir: string; }): Promise<PublicationManifest> { debug(`Generating publication manifest from HTML: ${entryHtmlFile}`); const { dom } = await getJsdomFromUrlOrFile(entryHtmlFile); @@ -152,16 +188,15 @@ export async function supplyWebPublicationManifestForWebbook({ document.querySelector('meta[name="author"]')?.getAttribute('content') || ''; - const rootDir = path.dirname(entryHtmlFile); - const entry = path.basename(entryHtmlFile); + const entry = path.relative(outputDir, entryHtmlFile); const allFiles = await safeGlob('**', { - cwd: rootDir, + cwd: outputDir, gitignore: false, }); const manifest = generateManifest( - path.join(rootDir, MANIFEST_FILENAME), - rootDir, + path.join(outputDir, MANIFEST_FILENAME), + outputDir, { title, author, @@ -179,7 +214,10 @@ export async function supplyWebPublicationManifestForWebbook({ document.head.appendChild(link); await fs.promises.writeFile(entryHtmlFile, dom.serialize(), 'utf8'); - debug('Generated publication manifest from HTML', JSON.stringify(manifest)); + debug( + 'Generated publication manifest from HTML', + JSON.stringify(manifest, null, 2), + ); return manifest; } diff --git a/src/processor/html.ts b/src/processor/html.ts index 690deabd..becb35da 100644 --- a/src/processor/html.ts +++ b/src/processor/html.ts @@ -47,7 +47,7 @@ virtualConsole.on('jsdomError', (error) => { }); export class ResourceLoader extends BaseResourceLoader { - fetcherMap = new Map<string, Promise<Buffer>>(); + fetcherMap = new Map<string, jsdom.AbortablePromise<Buffer>>(); fetch(url: string, options?: jsdom.FetchOptions) { debug(`[JSDOM] Fetching resource: ${url}`); @@ -82,8 +82,8 @@ export async function getJsdomFromUrlOrFile( } else { baseUrl = /^file:\/\//.test(src) ? src : url.pathToFileURL(src).href; if (resourceLoader) { - const file = await resourceLoader._readFile(url.fileURLToPath(baseUrl)); - resourceLoader.fetcherMap.set(baseUrl, Promise.resolve(file)); + const file = resourceLoader._readFile(url.fileURLToPath(baseUrl)); + resourceLoader.fetcherMap.set(baseUrl, file); } dom = await JSDOM.fromFile(url.fileURLToPath(baseUrl), { virtualConsole, diff --git a/types/jsdom.d.ts b/types/jsdom.d.ts index 7a0c14d0..b6f4c319 100644 --- a/types/jsdom.d.ts +++ b/types/jsdom.d.ts @@ -1,4 +1,9 @@ declare module 'jsdom' { + export interface AbortablePromise<T> extends Promise<T> { + abort(): void; + response?: import('http').IncomingMessage; + } + export class ResourceLoader { _readFile(filePath: string): AbortablePromise<Buffer>; fetch(url: string, options?: FetchOptions): AbortablePromise<Buffer> | null; diff --git a/yarn.lock b/yarn.lock index d75a1116..3675dc85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1463,6 +1463,11 @@ resolved "https://registry.yarnpkg.com/@types/w3c-xmlserializer/-/w3c-xmlserializer-2.0.2.tgz#79ab833574bdfac3d03eadb4179508ae89d6bf89" integrity sha512-lSw+ZP+r+SQLuQpY1BIAyYUIOd5YBOVg5VY8psTCcHOoDBxVbUPSZZOkGQfssrrWeTJs0tKi5cWhYCKj8gmM5w== +"@types/whatwg-mimetype@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#4ff45f787a085e7d22b7e01f3724f28a97d84c67" + integrity sha512-xHFOhd41VpUR6Y0k8ZinlyFv5cyhC/r2zghJgWWN8oNxqNo45Nf0qCBInJsFeifLeoHcIF4voEfap4A2GYHWkw== + "@types/yargs-parser@*": version "15.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" From 17f14efe3077ed2aebe3787eb18a768a7355630a Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sun, 9 Jul 2023 13:29:19 +0900 Subject: [PATCH 19/42] chore: Improve genrated toc nav element --- src/output/epub.ts | 193 ++++++++++++++++++++++++++------------------- 1 file changed, 113 insertions(+), 80 deletions(-) diff --git a/src/output/epub.ts b/src/output/epub.ts index e9c060cc..c2905014 100644 --- a/src/output/epub.ts +++ b/src/output/epub.ts @@ -1,5 +1,6 @@ import archiver from 'archiver'; import { lookup as lookupLanguage } from 'bcp-47-match'; +import chalk from 'chalk'; import { XMLBuilder } from 'fast-xml-parser'; import GithubSlugger from 'github-slugger'; import type { JSDOM } from 'jsdom'; @@ -32,7 +33,7 @@ import { PublicationManifest, ResourceCategorization, } from '../schema/publication.schema.js'; -import { DetailError, debug } from '../util.js'; +import { DetailError, debug, logWarn } from '../util.js'; interface ManifestEntry { href: string; @@ -92,6 +93,14 @@ const normalizeLocalizableString = ( return values.find((v) => !v.language)?.value; }; +const appendManifestProperty = (entry: ManifestEntry, newProperty: string) => { + entry.properties = entry.properties + ? Array.from(new Set([...entry.properties.split(' '), newProperty])).join( + ' ', + ) + : newProperty; +}; + export async function exportEpub({ webpubDir, entryHtmlFile, @@ -176,10 +185,7 @@ export async function exportEpub({ if (!fs.existsSync(path.join(tmpDir, 'EPUB', url))) { return acc; } - const mediaType = encodingFormat || mime(url); - if (!mediaType) { - throw new Error(`Unknown mediaType: ${url}`); - } + const mediaType = encodingFormat || mime(url) || 'text/plain'; acc[url] = { href: url, mediaType, @@ -188,35 +194,30 @@ export async function exportEpub({ acc[url].href = changeExtname(url, '.xhtml'); acc[url].mediaType = 'application/xhtml+xml'; } - if (url === (tocResource?.url || entryHtmlRelPath)) { - acc[url].properties = 'nav'; - } else if (url === pictureCoverResource?.url) { + if (url === pictureCoverResource?.url) { acc[url].properties = 'cover-image'; } return acc; }, {} as Record<string, ManifestEntry>); - if ( - entryHtmlRelPath && - Object.values(manifestItem).every( - ({ properties }) => !properties?.split(' ').includes('nav'), - ) - ) { - manifestItem[entryHtmlRelPath] = { - href: changeExtname(entryHtmlRelPath, '.xhtml'), - mediaType: 'application/xhtml+xml', - properties: 'nav', - }; - } const htmlFiles = Object.keys(manifestItem).filter((url) => /\.html?$/.test(url), ); - const tocHtml = htmlFiles.find( + let tocHtml = htmlFiles.find( (f) => f === (tocResource?.url || entryHtmlRelPath), ); + const readingOrder = [manifest.readingOrder || entryHtmlRelPath] + .flat() + .flatMap((v) => (v ? (typeof v === 'string' ? { url: v } : v) : [])); if (!tocHtml) { - throw new Error('EPUB must have one ToC document or entry HTML'); + logWarn( + chalk.yellowBright( + '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 = readingOrder[0].url; } + appendManifestProperty(manifestItem[tocHtml], 'nav'); const processHtml = async (target: string, isTocHtml: boolean) => { let parseResult: Resolved<ReturnType<typeof transpileHtmlToXhtml>>; @@ -235,28 +236,24 @@ export async function exportEpub({ thrownError.stack ?? thrownError.message, ); } - const appendProperty = (name: string) => { - const obj = manifestItem[target]; - obj.properties = [obj.properties, name].filter(Boolean).join(' '); - }; if (parseResult.hasMathmlContent) { - appendProperty('mathml'); + appendManifestProperty(manifestItem[target], 'mathml'); } if (parseResult.hasRemoteResources) { - appendProperty('remote-resources'); + appendManifestProperty(manifestItem[target], 'remote-resources'); } if (parseResult.hasScriptedContent) { - appendProperty('scripted'); + appendManifestProperty(manifestItem[target], 'scripted'); } if (parseResult.hasSvgContent) { - appendProperty('svg'); + appendManifestProperty(manifestItem[target], 'svg'); } return parseResult; }; debug(`Transpiling ToC HTML to XHTML: ${tocHtml}`); - const { dom, tocParseTree } = await processHtml(tocHtml, true); - const { document: entryDocument } = dom.window; + const tocProcessResult = await processHtml(tocHtml, true); + const { document: entryDocument } = tocProcessResult.dom.window; const docLanguages = [manifest.inLanguage] .flat() .filter((v): v is string => Boolean(v)); @@ -269,34 +266,38 @@ export async function exportEpub({ if (!docTitle) { throw new Error('EPUB must have a title of one or more characters'); } - if (tocParseTree) { - debug(`Generating toc.ncx`); - fs.writeFileSync( - path.join(tmpDir, 'EPUB/toc.ncx'), - buildNcx({ - toc: tocParseTree, - docTitle, - uid, - tocHtml, - }), - 'utf8', - ); - manifestItem['toc.ncx'] = { - href: 'toc.ncx', - mediaType: 'application/x-dtbncx+xml', - }; - } for (const target of htmlFiles.filter((f) => f !== tocHtml)) { debug(`Transpiling HTML to XHTML: ${target}`); await processHtml(target, false); } - const readingOrder = [manifest.readingOrder || entryHtmlRelPath] - .flat() - .filter((v): v is string | PublicationLinks => Boolean(v)) - .map((e) => (typeof e === 'string' ? e : e.url)) - .map((p) => (htmlFiles.includes(p) ? changeExtname(p, '.xhtml') : p)); + let { tocParseTree } = tocProcessResult; + if (!tocParseTree) { + tocParseTree = await supplyTocNavElement({ + tocHtml, + tocDom: tocProcessResult.dom, + contextDir: path.join(tmpDir, 'EPUB'), + readingOrder, + docLanguages, + }); + } + + // EPUB/toc.ncx + fs.writeFileSync( + path.join(tmpDir, 'EPUB/toc.ncx'), + buildNcx({ + toc: tocParseTree, + docTitle, + uid, + tocHtml, + }), + 'utf8', + ); + manifestItem['toc.ncx'] = { + href: 'toc.ncx', + mediaType: 'application/x-dtbncx+xml', + }; // META-INF/container.xml fs.writeFileSync( @@ -382,28 +383,6 @@ async function transpileHtmlToXhtml({ const nav = replaceWithNavElement(parsed.element); nav.setAttribute('id', 'toc'); nav.setAttribute('epub:type', 'toc'); - } else { - // Insert single document toc - const nav = document.createElement('nav'); - nav.setAttribute('id', 'toc'); - nav.setAttribute('role', 'doc-toc'); - nav.setAttribute('epub:type', 'toc'); - nav.setAttribute('hidden', ''); - nav.innerHTML = '<ol><li></li></ol>'; - const a = document.createElement('a'); - a.textContent = document.title; - a.href = changeExtname(path.basename(target), '.xhtml'); - nav.querySelector('li')!.appendChild(a); - document.body.appendChild(nav); - tocParseTree = { - element: nav, - children: [ - { - element: nav.querySelector('li')!, - label: a, - }, - ], - }; } } @@ -456,6 +435,60 @@ async function transpileHtmlToXhtml({ }; } +export async function supplyTocNavElement({ + tocHtml, + tocDom, + contextDir, + readingOrder, + docLanguages, +}: { + tocHtml: string; + tocDom: JSDOM; + contextDir: string; + readingOrder: PublicationLinks[]; + docLanguages: string[]; +}): Promise<TocResourceTreeRoot> { + debug(`Generating toc nav element: ${tocHtml}`); + const absPath = path.join(contextDir, tocHtml); + const { document } = tocDom.window; + + const nav = document.createElement('nav'); + nav.setAttribute('id', 'toc'); + nav.setAttribute('role', 'doc-toc'); + nav.setAttribute('epub:type', 'toc'); + nav.setAttribute('hidden', ''); + const ol = document.createElement('ol'); + const tocParseTree: TocResourceTreeRoot = { + element: nav, + children: [], + }; + + for (const content of readingOrder) { + let name = normalizeLocalizableString(content.name, docLanguages); + if (!name) { + const { dom } = await getJsdomFromUrlOrFile( + path.join(contextDir, changeExtname(content.url, '.xhtml')), + ); + name = dom.window.document.title; + } + const li = document.createElement('li'); + const a = document.createElement('a'); + a.textContent = name; + a.href = getRelativeHref(content.url, '', tocHtml); + li.appendChild(a); + ol.appendChild(li); + tocParseTree.children.push({ element: li, label: a }); + } + + nav.appendChild(ol); + document.body.appendChild(nav); + const xhtml = `${XML_DECLARATION}\n${serializeToXml(document)}`; + await fs.promises.writeFile(changeExtname(absPath, '.xhtml'), xhtml, 'utf8'); + + debug('Generated toc nav element', nav.outerHTML); + return tocParseTree; +} + function buildEpubPackageDocument({ epubVersion, manifest, @@ -472,7 +505,7 @@ function buildEpubPackageDocument({ docTitle: string; docLanguages: string[]; tocXhtml: string; - readingOrder: string[]; + readingOrder: PublicationLinks[]; manifestItems: ManifestEntry[]; landmarks: LandmarkEntry[]; }): string { @@ -586,8 +619,8 @@ function buildEpubPackageDocument({ ...(manifest.readingProgression ? { '_page-progression-direction': manifest.readingProgression } : {}), - itemref: readingOrder.map((href) => ({ - _idref: itemIdMap.get(href), + itemref: readingOrder.map(({ url }) => ({ + _idref: itemIdMap.get(changeExtname(url, '.xhtml')), })), }, guide: { @@ -633,7 +666,7 @@ function buildNcx({ _src: getRelativeHref( item.label.getAttribute('href')!, tocHtml, - 'toc.ncx', + '', ), }, } From 6a9d1fed9bd89de79d575fd61f16b4bbd3abf64e Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sun, 9 Jul 2023 13:37:33 +0900 Subject: [PATCH 20/42] chore: Remove debug code --- src/output/epub.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/output/epub.ts b/src/output/epub.ts index c2905014..6dd7c883 100644 --- a/src/output/epub.ts +++ b/src/output/epub.ts @@ -11,12 +11,7 @@ import shelljs from 'shelljs'; import path from 'upath'; import { v4 as uuid } from 'uuid'; import serializeToXml from 'w3c-xmlserializer'; -import { - EPUB_CONTAINER_XML, - EPUB_NS, - XML_DECLARATION, - cliRoot, -} from '../const.js'; +import { EPUB_CONTAINER_XML, EPUB_NS, XML_DECLARATION } from '../const.js'; import { PageListResourceTreeRoot, TocResourceTreeItem, @@ -25,7 +20,7 @@ import { parsePageListDocument, parseTocDocument, } from '../processor/html.js'; -import { +import type { Contributor, LocalizableStringObject, LocalizableStringOrObject, @@ -33,7 +28,7 @@ import { PublicationManifest, ResourceCategorization, } from '../schema/publication.schema.js'; -import { DetailError, debug, logWarn } from '../util.js'; +import { DetailError, debug, logWarn, useTmpDirectory } from '../util.js'; interface ManifestEntry { href: string; @@ -116,9 +111,7 @@ export async function exportEpub({ }) { debug('Export EPUB'); - // const [tmpDir, clearTmpDir] = await useTmpDirectory(); - const tmpDir = path.join(cliRoot, 'tmp'); - shelljs.rm('-rf', tmpDir); + const [tmpDir] = await useTmpDirectory(); fs.mkdirSync(path.join(tmpDir, 'META-INF'), { recursive: true }); shelljs.cp('-rf', webpubDir, path.join(tmpDir, 'EPUB')); From e20d817f6513c6b1805909da783574a76f57f1d7 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Thu, 13 Jul 2023 23:14:35 +0900 Subject: [PATCH 21/42] fix: Migrate Jest to Vitest --- .github/workflows/test.yml | 1 + .prettierignore | 1 + jest.config.js | 24 - package.json | 25 +- src/build.ts | 2 +- src/const.ts | 24 +- src/output/epub.ts | 5 +- src/output/pdf.ts | 4 +- src/output/webbook.ts | 211 +- src/processor/compile.ts | 29 +- src/processor/theme.ts | 13 +- src/schema/pubManifest.ts | 66 +- src/schema/vivliostyle.ts | 11 +- src/util.ts | 22 +- tests/__snapshots__/config.test.ts.snap | 18 +- tests/api.test.ts | 1 + tests/builder.test.ts | 171 +- tests/cli.test.ts | 1 + tests/config.test.ts | 6 +- .../fixtures/builder/multipleEntry.config.cjs | 2 +- tests/fixtures/builder/vfm.config.cjs | 2 +- .../config/vivliostyle.config.valid.4.cjs | 6 +- tests/init.test.ts | 11 +- tests/server.test.ts | 39 +- tests/toc.test.ts | 76 +- tests/util.test.ts | 1 + tsconfig.json | 5 +- tsconfig.test.json | 6 - types/custom.d.ts | 4 + vendors/.gitignore | 1 + vendors/README.md | 10 + vendors/build.js | 27 + vendors/index.d.ts | 4 + vendors/index.src.js | 9 + vite.config.ts | 15 + yarn.lock | 2111 +++++------------ 36 files changed, 1006 insertions(+), 1958 deletions(-) delete mode 100644 jest.config.js delete mode 100644 tsconfig.test.json create mode 100644 vendors/.gitignore create mode 100644 vendors/README.md create mode 100644 vendors/build.js create mode 100644 vendors/index.d.ts create mode 100644 vendors/index.src.js create mode 100644 vite.config.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 63b32b67..2aa9dd67 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,6 +23,7 @@ jobs: node-version: ${{ matrix.node }} - run: yarn install - run: yarn playwright-core install chromium + - run: yarn build - run: yarn test build-and-push-image: runs-on: ubuntu-latest diff --git a/.prettierignore b/.prettierignore index 46b411bf..ef9e45af 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ /examples +/vendors/index.js diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 390c5b26..00000000 --- a/jest.config.js +++ /dev/null @@ -1,24 +0,0 @@ -/** @type {import('jest').Config} */ -export default { - preset: 'ts-jest/presets/default-esm', - testMatch: ['**/src/__tests__/*.+(ts|tsx|js)', '**/tests/*.test.(ts|tsx|js)'], - testPathIgnorePatterns: ['/dist/'], - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1', - }, - transform: { - '^.+\\.tsx?$': [ - 'ts-jest', - { - useESM: true, - tsconfig: 'tsconfig.test.json', - }, - ], - }, - coverageProvider: 'v8', - coveragePathIgnorePatterns: ['/node_modules/', '/tests/', '/tmp/'], - snapshotFormat: { - escapeString: true, - printBasicPrototype: true, - }, -}; diff --git a/package.json b/package.json index 35194982..87bde094 100644 --- a/package.json +++ b/package.json @@ -5,19 +5,21 @@ "author": "spring_raining <harusamex.com@gmail.com>", "type": "module", "scripts": { - "build": "yarn clean && run-p build:*", + "build": "yarn clean && run-s build:*", + "build:vendors": "node vendors/build.js", "build:cli": "tsc && shx chmod +x dist/cli.js", "generate:schema": "run-p generate:schema:*", "generate:schema:pubManifest": "json2ts -i schemas/pubManifest/publication.schema.json -o src/schema/publication.schema.ts --cwd schemas/pubManifest", "generate:schema:vivliostyleConfig": "json2ts -i schemas/vivliostyle/vivliostyleConfig.schema.json -o src/schema/vivliostyleConfig.schema.ts --cwd schemas/vivliostyle", "clean": "shx rm -rf dist tmp", "dev": "run-p dev:*", + "dev:vendors": "node vendors/build.js -w", "dev:cli": "tsc -w --preserveWatchOutput", "example": "yarn --cwd example build", - "pretest": "yarn build && playwright-core install chromium", + "pretest": "playwright-core install chromium", "release": "release-it", "release:pre": "release-it --preRelease --npm.tag=next", - "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage" + "test": "vitest run --coverage" }, "dependencies": { "@npmcli/arborist": "^6.1.3", @@ -57,10 +59,8 @@ "prettier": "^2.3.2", "resolve-pkg": "^2.0.0", "serve-handler": "^6.1.3", - "shelljs": "^0.8.5", "slash": "4.0.0", "terminal-link": "^2.1.1", - "tmp": "^0.2.1", "upath": "^2.0.1", "uuid": "^8.3.2", "vfile": "^4.2.1", @@ -68,12 +68,13 @@ "whatwg-mimetype": "^3.0.0" }, "devDependencies": { + "@hyrious/esbuild-plugin-commonjs": "^0.2.2", "@release-it/conventional-changelog": "^5.1.1", "@types/archiver": "^5.3.2", "@types/command-exists": "1.2.0", "@types/debug": "^4.1.7", + "@types/fs-extra": "^11.0.1", "@types/github-slugger": "^1.3.0", - "@types/jest": "^29.2.4", "@types/jsdom": "^21.1.1", "@types/mime-types": "^2.1.1", "@types/node": "^16.7.2", @@ -84,9 +85,11 @@ "@types/uuid": "^8.3.1", "@types/w3c-xmlserializer": "^2.0.2", "@types/whatwg-mimetype": "^3.0.0", + "@vitest/coverage-v8": "^0.33.0", + "esbuild": "^0.18.11", "file-type": "^16.5.3", + "fs-extra": "^11.1.1", "husky": "^4.3.8", - "jest": "^29.3.1", "json-schema-to-typescript": "^10.1.4", "lint-staged": "^11.1.2", "nodemon": "^2.0.12", @@ -95,8 +98,9 @@ "pretty-quick": "^3.1.1", "release-it": "^15.6.0", "shx": "^0.3.3", - "ts-jest": "^29.0.3", - "typescript": "^4.9.3" + "tmp": "^0.2.1", + "typescript": "^4.9.3", + "vitest": "^0.33.0" }, "main": "dist/index.js", "module": "dist/index.js", @@ -117,7 +121,8 @@ "files": [ "dist", "schemas", - "types" + "types", + "vendors" ], "husky": { "hooks": { diff --git a/src/build.ts b/src/build.ts index fc7a5c28..f84566a2 100644 --- a/src/build.ts +++ b/src/build.ts @@ -124,7 +124,7 @@ export async function build(cliFlags: BuildCliFlags) { let outputDir: string; if (format === 'webpub') { outputDir = target.path; - prepareWebPublicationDirectory({ outputDir }); + await prepareWebPublicationDirectory({ outputDir }); } else if (format === 'epub') { [outputDir] = await useTmpDirectory(); } else { diff --git a/src/const.ts b/src/const.ts index d8c68063..86db1ad2 100644 --- a/src/const.ts +++ b/src/const.ts @@ -19,11 +19,23 @@ export const EPUB_CONTAINER_XML = `${XML_DECLARATION} </container>`; export const cliRoot = path.join(fileURLToPath(import.meta.url), '../..'); -export const { version: cliVersion }: { version: string } = JSON.parse( - fs.readFileSync(path.join(cliRoot, 'package.json'), 'utf8'), -); +export const cliVersion = (() => { + if (import.meta.env?.VITEST) { + return '0.0.1'; + } + const pkg = JSON.parse( + fs.readFileSync(path.join(cliRoot, 'package.json'), 'utf8'), + ); + return pkg.version; +})(); export const viewerRoot = resolvePkg('@vivliostyle/viewer', { cwd: cliRoot })!; -export const { version: coreVersion }: { version: string } = JSON.parse( - fs.readFileSync(path.join(viewerRoot, 'package.json'), 'utf8'), -); +export const coreVersion = (() => { + if (import.meta.env?.VITEST) { + return '0.0.1'; + } + const pkg = JSON.parse( + fs.readFileSync(path.join(viewerRoot, 'package.json'), 'utf8'), + ); + return pkg.version; +})(); diff --git a/src/output/epub.ts b/src/output/epub.ts index 6dd7c883..ad1477fe 100644 --- a/src/output/epub.ts +++ b/src/output/epub.ts @@ -7,7 +7,6 @@ import type { JSDOM } from 'jsdom'; import { lookup as mime } from 'mime-types'; import fs from 'node:fs'; import url from 'node:url'; -import shelljs from 'shelljs'; import path from 'upath'; import { v4 as uuid } from 'uuid'; import serializeToXml from 'w3c-xmlserializer'; @@ -28,7 +27,7 @@ import type { PublicationManifest, ResourceCategorization, } from '../schema/publication.schema.js'; -import { DetailError, debug, logWarn, useTmpDirectory } from '../util.js'; +import { DetailError, copy, debug, logWarn, useTmpDirectory } from '../util.js'; interface ManifestEntry { href: string; @@ -113,7 +112,7 @@ export async function exportEpub({ const [tmpDir] = await useTmpDirectory(); fs.mkdirSync(path.join(tmpDir, 'META-INF'), { recursive: true }); - shelljs.cp('-rf', webpubDir, path.join(tmpDir, 'EPUB')); + await copy(webpubDir, path.join(tmpDir, 'EPUB')); const uid = `urn:uuid:${uuid()}`; const entryHtmlRelPath = diff --git a/src/output/pdf.ts b/src/output/pdf.ts index 0d2fd88f..2dd996e0 100644 --- a/src/output/pdf.ts +++ b/src/output/pdf.ts @@ -1,7 +1,7 @@ import chalk from 'chalk'; +import fs from 'node:fs'; import { URL } from 'node:url'; import { Page } from 'playwright-core'; -import shelljs from 'shelljs'; import terminalLink from 'terminal-link'; import path from 'upath'; import { @@ -294,7 +294,7 @@ export async function buildPDF({ await browser.close(); logUpdate('Processing PDF'); - shelljs.mkdir('-p', path.dirname(target.path)); + fs.mkdirSync(path.dirname(target.path), { recursive: true }); const post = await PostProcess.load(pdf); await post.metadata(metadata, { diff --git a/src/output/webbook.ts b/src/output/webbook.ts index 70bb724a..169e51f6 100644 --- a/src/output/webbook.ts +++ b/src/output/webbook.ts @@ -1,5 +1,4 @@ import fs from 'node:fs'; -import shelljs from 'shelljs'; import path from 'upath'; import MIMEType from 'whatwg-mimetype'; import { MANIFEST_FILENAME } from '../const.js'; @@ -16,22 +15,24 @@ import type { ResourceCategorization, } from '../schema/publication.schema.js'; import { + copy, debug, logError, logUpdate, pathContains, pathEquals, + remove, safeGlob, } from '../util.js'; -export function prepareWebPublicationDirectory({ +export async function prepareWebPublicationDirectory({ outputDir, }: { outputDir: string; -}) { +}): Promise<void> { if (fs.existsSync(outputDir)) { debug('going to remove existing webpub', outputDir); - shelljs.rm('-rf', outputDir); + await remove(outputDir); } fs.mkdirSync(outputDir, { recursive: true }); } @@ -232,119 +233,107 @@ export async function copyWebPublicationAssets({ outputDir: string; manifestPath: string; }): Promise<PublicationManifest> { - const silentMode = shelljs.config.silent; - shelljs.config.silent = true; - try { - const relExportAliases = exportAliases - .map(({ source, target }) => ({ - source: path.relative(input, source), - target: path.relative(input, target), - })) - .filter(({ source }) => !source.startsWith('..')); - const allFiles = await safeGlob('**', { - cwd: input, - ignore: [ - // don't copy auto-generated assets - ...outputs.flatMap(({ format, path: p }) => - !pathContains(input, p) - ? [] - : format === 'webpub' - ? path.join(path.relative(input, p), '**') - : path.relative(input, p), - ), - // including node_modules possibly occurs cyclic reference of symlink - '**/node_modules', - // only include dotfiles starting with `.vs-` - '**/.!(vs-*)', - ], - // follow symbolic links to copy local theme packages - followSymbolicLinks: true, - gitignore: false, - dot: true, - }); + const relExportAliases = exportAliases + .map(({ source, target }) => ({ + source: path.relative(input, source), + target: path.relative(input, target), + })) + .filter(({ source }) => !source.startsWith('..')); + const allFiles = await safeGlob('**', { + cwd: input, + ignore: [ + // don't copy auto-generated assets + ...outputs.flatMap(({ format, path: p }) => + !pathContains(input, p) + ? [] + : format === 'webpub' + ? path.join(path.relative(input, p), '**') + : path.relative(input, p), + ), + // including node_modules possibly occurs cyclic reference of symlink + '**/node_modules', + // only include dotfiles starting with `.vs-` + '**/.!(vs-*)', + ], + // follow symbolic links to copy local theme packages + followSymbolicLinks: true, + gitignore: false, + dot: true, + }); - debug( - 'webbook files', - allFiles.map((file) => { - const alias = relExportAliases.find(({ source }) => source === file); - return alias ? `${file} (alias: ${alias.target})` : file; - }), - ); - const resources: string[] = []; - let actualManifestPath = path.join( - outputDir, - path.relative(input, manifestPath), - ); - for (const file of allFiles) { + debug( + 'webbook files', + allFiles.map((file) => { const alias = relExportAliases.find(({ source }) => source === file); - const relTarget = alias?.target || file; - resources.push(relTarget); - const target = path.join(outputDir, relTarget); - const stderr = - shelljs.mkdir('-p', path.dirname(target)).stderr || - shelljs.cp('-r', path.join(input, file), target).stderr; - if (stderr) { - throw new Error(stderr); - } - if (alias && pathEquals(path.join(input, alias.source), manifestPath)) { - actualManifestPath = target; - } + return alias ? `${file} (alias: ${alias.target})` : file; + }), + ); + const resources: string[] = []; + let actualManifestPath = path.join( + outputDir, + path.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 = path.join(outputDir, relTarget); + fs.mkdirSync(path.dirname(target), { recursive: true }); + await copy(path.join(input, file), target); + if (alias && pathEquals(path.join(input, alias.source), manifestPath)) { + actualManifestPath = target; } + } - debug('webbook publication.json', actualManifestPath); - // Overwrite copied publication.json - const manifest = JSON.parse( - fs.readFileSync(actualManifestPath, 'utf8'), - ) as PublicationManifest; - for (const entry of relExportAliases) { - const rewriteAliasPath = (e: PublicationLinks | string) => { - 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); + debug('webbook publication.json', actualManifestPath); + // Overwrite copied publication.json + const manifest = JSON.parse( + fs.readFileSync(actualManifestPath, 'utf8'), + ) as PublicationManifest; + for (const entry of relExportAliases) { + const rewriteAliasPath = (e: PublicationLinks | string) => { + if (typeof e === 'string') { + return pathEquals(e, entry.source) ? entry.source : e; } - 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); + 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); } - - // List copied files to resources field - const normalizeToUrl = (val?: ResourceCategorization) => - [val || []].flat().map((e) => (typeof e === 'string' ? e : e.url)); - const preDefinedResources = [ - ...normalizeToUrl(manifest.links), - ...normalizeToUrl(manifest.readingOrder), - ...normalizeToUrl(manifest.resources), - ]; - manifest.resources = [ - ...[manifest.resources || []].flat(), - ...resources.flatMap((file) => { - if (preDefinedResources.includes(file)) { - return []; - } - return file; - }), - ]; - fs.writeFileSync(actualManifestPath, JSON.stringify(manifest, null, 2)); - return manifest; - } catch (err) { - throw err; - } finally { - shelljs.config.silent = silentMode; } + + // List copied files to resources field + const normalizeToUrl = (val?: ResourceCategorization) => + [val || []].flat().map((e) => (typeof e === 'string' ? e : e.url)); + const preDefinedResources = [ + ...normalizeToUrl(manifest.links), + ...normalizeToUrl(manifest.readingOrder), + ...normalizeToUrl(manifest.resources), + ]; + manifest.resources = [ + ...[manifest.resources || []].flat(), + ...resources.flatMap((file) => { + if (preDefinedResources.includes(file)) { + return []; + } + return file; + }), + ]; + fs.writeFileSync(actualManifestPath, JSON.stringify(manifest, null, 2)); + return manifest; } diff --git a/src/processor/compile.ts b/src/processor/compile.ts index 4940680b..8a764c35 100644 --- a/src/processor/compile.ts +++ b/src/processor/compile.ts @@ -2,8 +2,7 @@ import chalk from 'chalk'; import { imageSize } from 'image-size'; import { lookup as mime } from 'mime-types'; import fs from 'node:fs'; -import shelljs from 'shelljs'; -import path from 'upath'; +import path from 'node:path'; import { TOC_TITLE } from '../const.js'; import { ManuscriptEntry, @@ -20,10 +19,12 @@ import type { ArticleEntryObject } from '../schema/vivliostyleConfig.schema.js'; import { DetailError, assertPubManifestSchema, + copy, debug, log, pathContains, pathEquals, + remove, safeGlob, startLogging, useTmpDirectory, @@ -88,16 +89,12 @@ export async function cleanupWorkspace({ let movedThemePath: string | undefined; if (pathContains(workspaceDir, themesDir) && fs.existsSync(themesDir)) { [movedThemePath] = await useTmpDirectory(); - shelljs.cp('-rf', themesDir, movedThemePath); + await copy(themesDir, movedThemePath); } - shelljs.rm('-rf', workspaceDir); + await remove(workspaceDir); if (movedThemePath) { - shelljs.mkdir('-p', workspaceDir); - shelljs.cp( - '-rf', - path.join(movedThemePath, path.basename(themesDir)), - workspaceDir, - ); + fs.mkdirSync(path.dirname(themesDir), { recursive: true }); + await copy(movedThemePath, themesDir); } } @@ -114,8 +111,8 @@ export async function prepareThemeDirectory({ // copy theme files for (const theme of themeIndexes) { if (theme.type === 'file' && !pathEquals(theme.source, theme.location)) { - shelljs.mkdir('-p', path.dirname(theme.location)); - shelljs.cp(theme.source, theme.location); + fs.mkdirSync(path.dirname(theme.location), { recursive: true }); + await copy(theme.source, theme.location); } } } @@ -242,7 +239,7 @@ export async function compile({ (e): e is ManuscriptEntry => 'source' in e, ); for (const entry of contentEntries) { - shelljs.mkdir('-p', path.dirname(entry.target)); + fs.mkdirSync(path.dirname(entry.target), { recursive: true }); // calculate style path const style = entry.themes.flatMap((theme) => @@ -273,7 +270,7 @@ export async function compile({ } } else { if (!pathEquals(entry.source, entry.target)) { - shelljs.cp(entry.source, entry.target); + await copy(entry.source, entry.target); } } } @@ -349,8 +346,8 @@ export async function copyAssets({ debug('assets', assets); for (const asset of assets) { const target = path.join(workspaceDir, asset); - shelljs.mkdir('-p', path.dirname(target)); - shelljs.cp(path.resolve(entryContextDir, asset), target); + fs.mkdirSync(path.dirname(target), { recursive: true }); + await copy(path.resolve(entryContextDir, asset), target); } } diff --git a/src/processor/theme.ts b/src/processor/theme.ts index 3a09d9bc..42dcb255 100644 --- a/src/processor/theme.ts +++ b/src/processor/theme.ts @@ -1,10 +1,9 @@ import Arborist from '@npmcli/arborist'; import fs from 'node:fs'; import npa from 'npm-package-arg'; -import shelljs from 'shelljs'; import path from 'upath'; import type { MergedConfig } from '../input/config.js'; -import { beforeExitHandlers, DetailError } from '../util.js'; +import { beforeExitHandlers, DetailError, moveSync } from '../util.js'; // Rename `packages` directory into `node_modules` while Arborist works const temporaryMovePackagesDirectrory = async <T = unknown>( @@ -13,19 +12,19 @@ const temporaryMovePackagesDirectrory = async <T = unknown>( ) => { const exitHandler = () => { if (fs.existsSync(path.join(themesDir, 'node_modules'))) { - shelljs.mv( - '-f', + moveSync( path.join(themesDir, 'node_modules'), path.join(themesDir, 'packages'), + { overwrite: true }, ); } }; beforeExitHandlers.push(exitHandler); if (fs.existsSync(path.join(themesDir, 'packages'))) { - shelljs.mv( - '-f', + moveSync( path.join(themesDir, 'packages'), path.join(themesDir, 'node_modules'), + { overwrite: true }, ); } try { @@ -61,7 +60,7 @@ export async function installThemeDependencies({ themesDir, themeIndexes, }: Pick<MergedConfig, 'themesDir' | 'themeIndexes'>): Promise<void> { - shelljs.mkdir('-p', themesDir); + fs.mkdirSync(themesDir, { recursive: true }); await temporaryMovePackagesDirectrory(themesDir, async () => { try { diff --git a/src/schema/pubManifest.ts b/src/schema/pubManifest.ts index 990e4455..9215998d 100644 --- a/src/schema/pubManifest.ts +++ b/src/schema/pubManifest.ts @@ -1,51 +1,25 @@ -import fs from 'node:fs'; -import path from 'upath'; -import { cliRoot } from '../const.js'; +import { createRequire } from 'node:module'; // TODO: Change to static import after JSON module becomes stable -const importJSON = (p: string) => - JSON.parse(fs.readFileSync(path.join(cliRoot, p), 'utf8')); -const bcpSchema = importJSON('schemas/pubManifest/module/bcp.schema.json'); -const contextSchema = importJSON( - 'schemas/pubManifest/module/context.schema.json', -); -const contributorObjectSchema = importJSON( - 'schemas/pubManifest/module/contributor-object.schema.json', -); -const contributorSchema = importJSON( - 'schemas/pubManifest/module/contributor.schema.json', -); -const dateSchema = importJSON('schemas/pubManifest/module/date.schema.json'); -const durationSchema = importJSON( - 'schemas/pubManifest/module/duration.schema.json', -); -const itemListsSchema = importJSON( - 'schemas/pubManifest/module/item-lists.schema.json', -); -const itemListSchema = importJSON( - 'schemas/pubManifest/module/ItemList.schema.json', -); -const languageSchema = importJSON( - 'schemas/pubManifest/module/language.schema.json', -); -const linkSchema = importJSON('schemas/pubManifest/module/link.schema.json'); -const localizableObjectSchema = importJSON( - 'schemas/pubManifest/module/localizable-object.schema.json', -); -const localizableSchema = importJSON( - 'schemas/pubManifest/module/localizable.schema.json', -); -const resourceCategorizationSchema = importJSON( - 'schemas/pubManifest/module/resource.categorization.schema.json', -); -const stringsSchema = importJSON( - 'schemas/pubManifest/module/strings.schema.json', -); -const urlSchema = importJSON('schemas/pubManifest/module/url.schema.json'); -const urlsSchema = importJSON('schemas/pubManifest/module/urls.schema.json'); -const publicationSchema = importJSON( - 'schemas/pubManifest/publication.schema.json', -); +const require = createRequire(import.meta.url); + +const bcpSchema = require('../../schemas/pubManifest/module/bcp.schema.json'); +const contextSchema = require('../../schemas/pubManifest/module/context.schema.json'); +const contributorObjectSchema = require('../../schemas/pubManifest/module/contributor-object.schema.json'); +const contributorSchema = require('../../schemas/pubManifest/module/contributor.schema.json'); +const dateSchema = require('../../schemas/pubManifest/module/date.schema.json'); +const durationSchema = require('../../schemas/pubManifest/module/duration.schema.json'); +const itemListsSchema = require('../../schemas/pubManifest/module/item-lists.schema.json'); +const itemListSchema = require('../../schemas/pubManifest/module/ItemList.schema.json'); +const languageSchema = require('../../schemas/pubManifest/module/language.schema.json'); +const linkSchema = require('../../schemas/pubManifest/module/link.schema.json'); +const localizableObjectSchema = require('../../schemas/pubManifest/module/localizable-object.schema.json'); +const localizableSchema = require('../../schemas/pubManifest/module/localizable.schema.json'); +const resourceCategorizationSchema = require('../../schemas/pubManifest/module/resource.categorization.schema.json'); +const stringsSchema = require('../../schemas/pubManifest/module/strings.schema.json'); +const urlSchema = require('../../schemas/pubManifest/module/url.schema.json'); +const urlsSchema = require('../../schemas/pubManifest/module/urls.schema.json'); +const publicationSchema = require('../../schemas/pubManifest/publication.schema.json'); export const publicationSchemas = [ bcpSchema, diff --git a/src/schema/vivliostyle.ts b/src/schema/vivliostyle.ts index f063eb61..f8910db0 100644 --- a/src/schema/vivliostyle.ts +++ b/src/schema/vivliostyle.ts @@ -1,13 +1,8 @@ -import fs from 'node:fs'; -import path from 'upath'; -import { cliRoot } from '../const.js'; +import { createRequire } from 'node:module'; // TODO: Change to static import after JSON module becomes stable -const importJSON = (p: string) => - JSON.parse(fs.readFileSync(path.join(cliRoot, p), 'utf8')); +const require = createRequire(import.meta.url); -const vivliostyleConfigSchema = importJSON( - 'schemas/vivliostyle/vivliostyleConfig.schema.json', -); +const vivliostyleConfigSchema = require('../../schemas/vivliostyle/vivliostyleConfig.schema.json'); export { vivliostyleConfigSchema }; diff --git a/src/util.ts b/src/util.ts index 965e1aee..8a8cb8b6 100644 --- a/src/util.ts +++ b/src/util.ts @@ -13,15 +13,24 @@ import { fileURLToPath } from 'node:url'; import util from 'node:util'; import oraConstructor from 'ora'; import portfinder from 'portfinder'; -import shelljs from 'shelljs'; import slash from 'slash'; -import tmp from 'tmp'; import upath from 'upath'; +import { + copy, + copySync, + move, + moveSync, + remove, + removeSync, + tmp, +} from '../vendors/index.js'; import { publicationSchema, publicationSchemas } from './schema/pubManifest.js'; import type { PublicationManifest } from './schema/publication.schema.js'; import { vivliostyleConfigSchema } from './schema/vivliostyle.js'; import type { VivliostyleConfigSchema } from './schema/vivliostyleConfig.schema.js'; +export { copy, copySync, move, moveSync, remove, removeSync, tmp }; + export const debug = debugConstructor('vs-cli'); export const cwd = upath.normalize(process.cwd()); @@ -270,7 +279,7 @@ export function useTmpDirectory(): Promise<[string, () => void]> { const callback = () => { // clear function doesn't work well? // clear(); - shelljs.rm('-rf', path); + removeSync(path); debug(`Removed the temporary directory: ${path}`); }; beforeExitHandlers.push(callback); @@ -280,11 +289,12 @@ export function useTmpDirectory(): Promise<[string, () => void]> { } export async function touchTmpFile(path: string): Promise<() => void> { - shelljs.mkdir('-p', upath.dirname(path)); - shelljs.touch(path); + fs.mkdirSync(upath.dirname(path), { recursive: true }); + // Create file if not exist + fs.closeSync(fs.openSync(path, 'a')); debug(`Created the temporary file: ${path}`); const callback = () => { - shelljs.rm('-f', path); + removeSync(path); debug(`Removed the temporary file: ${path}`); }; beforeExitHandlers.push(callback); diff --git a/tests/__snapshots__/config.test.ts.snap b/tests/__snapshots__/config.test.ts.snap index f6e40995..9f14fb71 100644 --- a/tests/__snapshots__/config.test.ts.snap +++ b/tests/__snapshots__/config.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`allow a loose specifier of a theme direcory: valid.5.config.js 1`] = ` +exports[`allow a loose specifier of a theme direcory > valid.5.config.js 1`] = ` Object { "author": "pkgAuthor", "bleed": undefined, @@ -489,7 +489,7 @@ Object { } `; -exports[`override option by CLI command: valid.1.config.js 1`] = ` +exports[`override option by CLI command > valid.1.config.js 1`] = ` Object { "author": "myAuthor", "bleed": undefined, @@ -620,7 +620,7 @@ Object { } `; -exports[`parse array of config: valid.1.config.js 1`] = ` +exports[`parse array of config > valid.1.config.js 1`] = ` Object { "author": "author", "bleed": undefined, @@ -762,7 +762,7 @@ Object { } `; -exports[`parse array of config: valid.2.config.js 1`] = ` +exports[`parse array of config > valid.2.config.js 1`] = ` Object { "author": "pkgAuthor", "bleed": undefined, @@ -837,7 +837,7 @@ Object { } `; -exports[`parse array of config: valid.3.config.js 1`] = ` +exports[`parse array of config > valid.3.config.js 1`] = ` Object { "author": "pkgAuthor", "bleed": undefined, @@ -912,7 +912,7 @@ Object { } `; -exports[`parse vivliostyle config: valid.1.config.js 1`] = ` +exports[`parse vivliostyle config > valid.1.config.js 1`] = ` Object { "author": "author", "bleed": undefined, @@ -1054,7 +1054,7 @@ Object { } `; -exports[`parse vivliostyle config: valid.2.config.js 1`] = ` +exports[`parse vivliostyle config > valid.2.config.js 1`] = ` Object { "author": "pkgAuthor", "bleed": undefined, @@ -1129,7 +1129,7 @@ Object { } `; -exports[`parse vivliostyle config: valid.3.config.js 1`] = ` +exports[`parse vivliostyle config > valid.3.config.js 1`] = ` Object { "author": "pkgAuthor", "bleed": undefined, diff --git a/tests/api.test.ts b/tests/api.test.ts index bf26777e..6d3ddbbf 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -1,6 +1,7 @@ import fileType from 'file-type'; import fs from 'node:fs'; import path from 'upath'; +import { expect, test } from 'vitest'; import { build } from '../src/index.js'; import { rootPath } from './commandUtil.js'; diff --git a/tests/builder.test.ts b/tests/builder.test.ts index 7f4dcb30..bf4a53ac 100644 --- a/tests/builder.test.ts +++ b/tests/builder.test.ts @@ -1,7 +1,8 @@ +import { globby } from 'globby'; import { JSDOM } from 'jsdom'; import assert from 'node:assert'; import fs from 'node:fs'; -import shelljs from 'shelljs'; +import { afterAll, expect, it } from 'vitest'; import { MergedConfig } from '../src/input/config.js'; import { checkOverwriteViolation, @@ -11,6 +12,7 @@ import { prepareThemeDirectory, } from '../src/processor/compile.js'; import { checkThemeInstallationNecessity } from '../src/processor/theme.js'; +import { removeSync } from '../src/util.js'; import { assertArray, assertSingleItem, @@ -25,7 +27,7 @@ function assertManifestPath( } afterAll(() => { - shelljs.rm('-rf', [ + [ resolveFixture('builder/.vs-workspace'), resolveFixture('builder/.vs-entryContext'), resolveFixture('builder/.vs-variousManuscriptFormat'), @@ -37,7 +39,7 @@ afterAll(() => { resolveFixture('builder/.vs-nonExistTheme'), resolveFixture('builder/.vs-invalidTheme'), resolveFixture('builder/.vs-nonExistImport'), - ]); + ].map((f) => removeSync(f)); }); it('generate workspace directory', async () => { @@ -54,20 +56,23 @@ it('generate workspace directory', async () => { await prepareThemeDirectory(config); await compile(config); await copyAssets(config); - const fileList = shelljs.ls('-R', resolveFixture('builder/.vs-workspace')); - expect([...fileList]).toEqual([ - 'index.html', - 'manuscript', - 'manuscript/cover.png', - 'manuscript/sample-theme.css', - 'manuscript/soda.html', - 'publication.json', - 'themes', - 'themes/package-lock.json', - 'themes/package.json', - 'themes/packages', - 'themes/packages/debug-theme', - ]); + const fileList = await globby('**', { + cwd: resolveFixture('builder/.vs-workspace'), + }); + expect(new Set(fileList)).toEqual( + new Set([ + 'index.html', + 'manuscript/cover.png', + 'manuscript/sample-theme.css', + 'manuscript/soda.html', + 'publication.json', + 'themes/package-lock.json', + 'themes/package.json', + 'themes/packages/debug-theme/additional-theme.css', + 'themes/packages/debug-theme/package.json', + 'themes/packages/debug-theme/theme.css', + ]), + ); const { default: manifest } = await import( resolveFixture('builder/.vs-workspace/publication.json') ); @@ -110,8 +115,10 @@ it('generate workspace directory', async () => { await prepareThemeDirectory(config); await compile(config); await copyAssets(config); - const fileList2 = shelljs.ls('-R', resolveFixture('builder/.vs-workspace')); - expect([...fileList2]).toEqual([...fileList]); + const fileList2 = await globby('**', { + cwd: resolveFixture('builder/.vs-workspace'), + }); + expect(new Set(fileList2)).toEqual(new Set(fileList)); }); it('generate files with entryContext', async () => { @@ -128,15 +135,18 @@ it('generate files with entryContext', async () => { await prepareThemeDirectory(config); await compile(config); await copyAssets(config); - const fileList = shelljs.ls('-R', resolveFixture('builder/.vs-entryContext')); - expect([...fileList]).toEqual([ - 'cover.png', - 'manuscript', - 'manuscript/sample-theme.css', - 'publication.json', - 'soda.html', - 't-o-c.html', - ]); + const fileList = await globby('**', { + cwd: resolveFixture('builder/.vs-entryContext'), + }); + expect(new Set(fileList)).toEqual( + new Set([ + 'cover.png', + 'manuscript/sample-theme.css', + 'publication.json', + 'soda.html', + 't-o-c.html', + ]), + ); const { default: manifest } = await import( resolveFixture('builder/.vs-entryContext/publication.json') ); @@ -177,11 +187,10 @@ it('generate files with entryContext', async () => { await prepareThemeDirectory(config); await compile(config); await copyAssets(config); - const fileList2 = shelljs.ls( - '-R', - resolveFixture('builder/.vs-entryContext'), - ); - expect([...fileList2]).toEqual([...fileList]); + const fileList2 = await globby('**', { + cwd: resolveFixture('builder/.vs-entryContext'), + }); + expect(new Set(fileList2)).toEqual(new Set(fileList)); }); it('generate from various manuscript formats', async () => { @@ -198,23 +207,23 @@ it('generate from various manuscript formats', async () => { await prepareThemeDirectory(config); await compile(config); await copyAssets(config); - const fileList = shelljs.ls( - '-R', - resolveFixture('builder/.vs-variousManuscriptFormat'), + const fileList = await globby('**', { + cwd: resolveFixture('builder/.vs-variousManuscriptFormat'), + }); + expect(new Set(fileList)).toEqual( + new Set([ + 'manuscript/cover.png', + 'manuscript/sample-html.html', + 'manuscript/sample-xhtml.xhtml', + 'manuscript/soda.html', + 'publication.json', + 'themes/package-lock.json', + 'themes/package.json', + 'themes/packages/debug-theme/additional-theme.css', + 'themes/packages/debug-theme/package.json', + 'themes/packages/debug-theme/theme.css', + ]), ); - expect([...fileList]).toEqual([ - 'manuscript', - 'manuscript/cover.png', - 'manuscript/sample-html.html', - 'manuscript/sample-xhtml.xhtml', - 'manuscript/soda.html', - 'publication.json', - 'themes', - 'themes/package-lock.json', - 'themes/package.json', - 'themes/packages', - 'themes/packages/debug-theme', - ]); const { default: manifest } = await import( resolveFixture('builder/.vs-variousManuscriptFormat/publication.json') ); @@ -440,30 +449,30 @@ it('install local themes', async () => { await prepareThemeDirectory(config); await compile(config); await copyAssets(config); - const fileList = shelljs.ls('-R', resolveFixture('builder/.vs-localTheme')); - expect([...fileList]).toEqual([ - 'manuscript', - 'manuscript/cover.png', - 'manuscript/soda.html', - 'manuscript/theme-reference.html', - 'publication.json', - 'sample-theme.css', - 'themes', - 'themes/package-lock.json', - 'themes/package.json', - 'themes/packages', - 'themes/packages/debug-theme', - ]); + const fileList = await globby('**', { + cwd: resolveFixture('builder/.vs-localTheme'), + }); + expect(new Set(fileList)).toEqual( + new Set([ + 'manuscript/cover.png', + 'manuscript/soda.html', + 'manuscript/theme-reference.html', + 'publication.json', + 'sample-theme.css', + 'themes/package-lock.json', + 'themes/package.json', + 'themes/packages/debug-theme/additional-theme.css', + 'themes/packages/debug-theme/package.json', + 'themes/packages/debug-theme/theme.css', + ]), + ); // checking symlink-referenced directory - const themePackageFileList = shelljs.ls( - '-R', - resolveFixture('builder/.vs-localTheme/themes/packages/debug-theme'), + const themePackageFileList = await globby('**', { + cwd: resolveFixture('builder/.vs-localTheme/themes/packages/debug-theme'), + }); + expect(new Set(themePackageFileList)).toEqual( + new Set(['additional-theme.css', 'package.json', 'theme.css']), ); - expect([...themePackageFileList]).toEqual([ - 'additional-theme.css', - 'package.json', - 'theme.css', - ]); const doc1 = new JSDOM( fs.readFileSync( @@ -492,8 +501,10 @@ it('install local themes', async () => { await prepareThemeDirectory(config); await compile(config); await copyAssets(config); - const fileList2 = shelljs.ls('-R', resolveFixture('builder/.vs-localTheme')); - expect([...fileList2]).toEqual([...fileList]); + const fileList2 = await globby('**', { + cwd: resolveFixture('builder/.vs-localTheme'), + }); + expect(new Set(fileList2)).toEqual(new Set(fileList)); }); it('install remote themes', async () => { @@ -507,8 +518,10 @@ it('install remote themes', async () => { await prepareThemeDirectory(config); await compile(config); await copyAssets(config); - const fileList = shelljs.ls('-R', resolveFixture('builder/.vs-remoteTheme')); - expect([...fileList]).toContain( + const fileList = await globby('**', { + cwd: resolveFixture('builder/.vs-remoteTheme'), + }); + expect(fileList).toContain( 'themes/packages/@vivliostyle/theme-academic/package.json', ); @@ -529,9 +542,11 @@ it('install remote themes', async () => { await prepareThemeDirectory(config); await compile(config); await copyAssets(config); - const fileList2 = shelljs.ls('-R', resolveFixture('builder/.vs-remoteTheme')); - expect([...fileList2]).toEqual([...fileList]); -}); + const fileList2 = await globby('**', { + cwd: resolveFixture('builder/.vs-remoteTheme'), + }); + expect(new Set(fileList2)).toEqual(new Set(fileList)); +}, 300000); it('use multiple themes', async () => { const config = await getMergedConfig([ diff --git a/tests/cli.test.ts b/tests/cli.test.ts index efd28073..e8a9d527 100644 --- a/tests/cli.test.ts +++ b/tests/cli.test.ts @@ -10,6 +10,7 @@ import { PDFNumber, } from 'pdf-lib'; import path from 'upath'; +import { expect, it } from 'vitest'; import packageJSON from '../package.json'; import { rootPath } from './commandUtil.js'; diff --git a/tests/config.test.ts b/tests/config.test.ts index be513a7e..7798a591 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -1,4 +1,5 @@ -import shelljs from 'shelljs'; +import { afterAll, expect, it } from 'vitest'; +import { removeSync } from '../src/util.js'; import { assertArray, assertSingleItem, @@ -25,7 +26,7 @@ const configFilePath = configFiles.reduce( ); afterAll(() => { - shelljs.rm('-f', resolveFixture('config/.vs-*')); + removeSync(resolveFixture('config/.vs-*')); }); it('parse vivliostyle config', async () => { @@ -225,6 +226,7 @@ it('yields a config from frontmatter', async () => { }); it('parse array of config', async () => { + console.log(configFilePath['valid.4']); const validConfig = await getMergedConfig(['-c', configFilePath['valid.4']]); maskConfig(validConfig); assertArray(validConfig); diff --git a/tests/fixtures/builder/multipleEntry.config.cjs b/tests/fixtures/builder/multipleEntry.config.cjs index b24060f7..31edfccd 100644 --- a/tests/fixtures/builder/multipleEntry.config.cjs +++ b/tests/fixtures/builder/multipleEntry.config.cjs @@ -1,4 +1,4 @@ -const baseConfig = require('./workspace.config'); +const baseConfig = require('./workspace.config.cjs'); module.exports = [ { diff --git a/tests/fixtures/builder/vfm.config.cjs b/tests/fixtures/builder/vfm.config.cjs index 3b2eb36c..c30cf441 100644 --- a/tests/fixtures/builder/vfm.config.cjs +++ b/tests/fixtures/builder/vfm.config.cjs @@ -1,4 +1,4 @@ -const baseConfig = require('./workspace.config'); +const baseConfig = require('./workspace.config.cjs'); module.exports = { ...baseConfig, diff --git a/tests/fixtures/config/vivliostyle.config.valid.4.cjs b/tests/fixtures/config/vivliostyle.config.valid.4.cjs index 24078a3e..d64908dc 100644 --- a/tests/fixtures/config/vivliostyle.config.valid.4.cjs +++ b/tests/fixtures/config/vivliostyle.config.valid.4.cjs @@ -1,5 +1,5 @@ -const config1 = require('./vivliostyle.config.valid.1'); -const config2 = require('./vivliostyle.config.valid.2'); -const config3 = require('./vivliostyle.config.valid.3'); +const config1 = require('./vivliostyle.config.valid.1.cjs'); +const config2 = require('./vivliostyle.config.valid.2.cjs'); +const config3 = require('./vivliostyle.config.valid.3.cjs'); module.exports = [config1, config2, config3]; diff --git a/tests/init.test.ts b/tests/init.test.ts index 8f6eb852..f67c3f06 100644 --- a/tests/init.test.ts +++ b/tests/init.test.ts @@ -1,9 +1,10 @@ import execa from 'execa'; import fs from 'node:fs'; -import shelljs from 'shelljs'; import path from 'upath'; -import { rootPath } from './commandUtil.js'; +import { expect, it } from 'vitest'; import packageJSON from '../package.json'; +import { moveSync } from '../src/util.js'; +import { rootPath } from './commandUtil.js'; const cliPath = path.join(rootPath, packageJSON.bin.vivliostyle); @@ -76,9 +77,10 @@ it('test the init command with long option', async () => { ); // Change file extension and load Common JS - shelljs.mv( + moveSync( path.join(outputDir, 'vivliostyle.config.js'), path.join(outputDir, 'vivliostyle.config.cjs'), + { overwrite: true }, ); const { default: config } = await import( path.join(outputDir, 'vivliostyle.config.cjs') @@ -117,9 +119,10 @@ it('test the init command with short option', async () => { ); // Change file extension and load Common JS - shelljs.mv( + moveSync( path.join(outputDir, 'vivliostyle.config.js'), path.join(outputDir, 'vivliostyle.config.cjs'), + { overwrite: true }, ); const { default: config } = await import( path.join(outputDir, 'vivliostyle.config.cjs') diff --git a/tests/server.test.ts b/tests/server.test.ts index 6c12bc43..ee62131a 100644 --- a/tests/server.test.ts +++ b/tests/server.test.ts @@ -1,18 +1,20 @@ +import http from 'node:http'; import { pathToFileURL } from 'node:url'; +import portfinder from 'portfinder'; import path from 'upath'; -import { getViewerFullUrl } from '../src/server.js'; +import { afterEach, beforeEach, expect, it, vi } from 'vitest'; +import { + getViewerFullUrl, + prepareServer, + teardownServer, +} from '../src/server.js'; import { maskConfig, rootPath } from './commandUtil.js'; -// Giving up run tests using ESM mocks due to lack of Jest’s support -// I'd really like to switch to Vitest.. -// https://jestjs.io/ja/docs/ecmascript-modules#module-mocking-in-esm -/* -jest.mock('http', () => { - const { Agent } = jest.requireActual('http') as typeof http; +vi.mock('http', async () => { + const { Agent } = await vi.importActual<typeof import('http')>('http'); return { - __esModule: true, default: { - createServer: jest.fn(() => ({ + createServer: vi.fn(() => ({ listen: (port: number, host: string, callback: () => void) => { callback(); }, @@ -23,21 +25,16 @@ jest.mock('http', () => { Agent, }; }); -const mockedCreateServer = http.createServer as jest.MockedFunction< - typeof http.createServer ->; +const mockedCreateServer = vi.mocked(http).createServer; -jest.mock('portfinder', () => ({ - __esModule: true, +vi.mock('portfinder', () => ({ default: { - getPortPromise: jest.fn(async () => { + getPortPromise: vi.fn(async () => { return 33333; }), }, })); -const mockedGetPortPromise = portfinder.getPortPromise as jest.MockedFunction< - typeof portfinder.getPortPromise ->; +const mockedGetPortPromise = vi.mocked(portfinder).getPortPromise; beforeEach(() => { mockedCreateServer.mockClear(); @@ -47,7 +44,6 @@ beforeEach(() => { afterEach(() => { teardownServer(); }); -*/ it('converts to valid broker url', async () => { const sourceUrl1 = pathToFileURL('/absolute/path/to/manifest/file.json'); @@ -152,7 +148,6 @@ it('converts to valid broker url', async () => { ); }); -/* it('starts up broker and source servers', async () => { const validOut1 = await prepareServer({ input: '/absolute/path/to/manifest/file.json', @@ -164,6 +159,7 @@ it('starts up broker and source servers', async () => { expect(validOut1.viewerFullUrl).toBe( 'http://localhost:33333/lib/index.html#src=http://localhost:33333/to/manifest/file.json&bookMode=true&renderAllPages=true', ); + vi.mocked(http).createServer.mock.calls; expect(mockedCreateServer.mock.calls.length).toBe(2); expect(mockedGetPortPromise.mock.calls.length).toBe(2); }); @@ -208,9 +204,8 @@ it('starts up with no http server', async () => { }); maskConfig(validOut1); expect(validOut1.viewerFullUrl).toBe( - 'file:///something/viewer#src=file:///absolute/path/to/manifest/file.json&bookMode=true&renderAllPages=true', + 'file://something/viewer#src=file:///absolute/path/to/manifest/file.json&bookMode=true&renderAllPages=true', ); expect(mockedCreateServer.mock.calls.length).toBe(0); expect(mockedGetPortPromise.mock.calls.length).toBe(0); }); -*/ diff --git a/tests/toc.test.ts b/tests/toc.test.ts index fd195fd0..7e33bb27 100644 --- a/tests/toc.test.ts +++ b/tests/toc.test.ts @@ -1,10 +1,12 @@ +import { globby } from 'globby'; import { JSDOM } from 'jsdom'; import assert from 'node:assert'; import fs from 'node:fs'; -import shelljs from 'shelljs'; +import { afterAll, expect, it } from 'vitest'; import { MergedConfig } from '../src/input/config.js'; import { compile, prepareThemeDirectory } from '../src/processor/compile.js'; import { generateTocHtml } from '../src/processor/html.js'; +import { removeSync } from '../src/util.js'; import { assertSingleItem, getMergedConfig, @@ -18,11 +20,11 @@ function assertManifestPath( } afterAll(() => { - shelljs.rm('-rf', [ + [ resolveFixture('toc/.vs-valid.1'), resolveFixture('toc/.vs-valid.2'), resolveFixture('toc/.vs-valid.3'), - ]); + ].forEach((f) => removeSync(f)); }); it('generateTocHtml', () => { @@ -66,14 +68,12 @@ it('toc: true', async () => { assertManifestPath(config); await prepareThemeDirectory(config); await compile(config); - const fileList = shelljs.ls('-R', resolveFixture('toc/.vs-valid.1')); - expect([...fileList]).toEqual([ - 'a.html', - 'b.html', - 'c.html', - 'index.html', - 'publication.json', - ]); + const fileList = await globby('**', { + cwd: resolveFixture('toc/.vs-valid.1'), + }); + expect(new Set(fileList)).toEqual( + new Set(['a.html', 'b.html', 'c.html', 'index.html', 'publication.json']), + ); const { default: manifest } = await import( resolveFixture('toc/.vs-valid.1/publication.json') ); @@ -111,15 +111,18 @@ it("toc: 'manuscript/contents.html'", async () => { assertManifestPath(config); await prepareThemeDirectory(config); await compile(config); - const fileList = shelljs.ls('-R', resolveFixture('toc/.vs-valid.2')); - expect([...fileList]).toEqual([ - 'manuscript', - 'manuscript/a.html', - 'manuscript/b.html', - 'manuscript/c.html', - 'manuscript/contents.html', - 'publication.json', - ]); + const fileList = await globby('**', { + cwd: resolveFixture('toc/.vs-valid.2'), + }); + expect(new Set(fileList)).toMatchObject( + new Set([ + 'manuscript/a.html', + 'manuscript/b.html', + 'manuscript/c.html', + 'manuscript/contents.html', + 'publication.json', + ]), + ); const { default: manifest } = await import( resolveFixture('toc/.vs-valid.2/publication.json') ); @@ -160,21 +163,24 @@ it('Write ToC by myself', async () => { assertManifestPath(config); await prepareThemeDirectory(config); await compile(config); - const fileList = shelljs.ls('-R', resolveFixture('toc/.vs-valid.3')); - expect([...fileList]).toEqual([ - 'manuscript', - 'manuscript/a.html', - 'manuscript/b.html', - 'manuscript/c.html', - 'manuscript/ToC.html', - 'publication.json', - 'sample-theme.css', - 'themes', - 'themes/package-lock.json', - 'themes/package.json', - 'themes/packages', - 'themes/packages/debug-theme', - ]); + const fileList = await globby('**', { + cwd: resolveFixture('toc/.vs-valid.3'), + }); + expect(new Set(fileList)).toMatchObject( + new Set([ + 'manuscript/a.html', + 'manuscript/b.html', + 'manuscript/c.html', + 'manuscript/ToC.html', + 'publication.json', + 'sample-theme.css', + 'themes/package-lock.json', + 'themes/package.json', + 'themes/packages/debug-theme/additional-theme.css', + 'themes/packages/debug-theme/package.json', + 'themes/packages/debug-theme/theme.css', + ]), + ); const { default: manifest } = await import( resolveFixture('toc/.vs-valid.3/publication.json') ); diff --git a/tests/util.test.ts b/tests/util.test.ts index 83cbf0c1..2073c5b1 100644 --- a/tests/util.test.ts +++ b/tests/util.test.ts @@ -1,3 +1,4 @@ +import { expect, it } from 'vitest'; import { safeGlob } from '../src/util.js'; import { resolveFixture } from './commandUtil.js'; diff --git a/tsconfig.json b/tsconfig.json index a825a6c1..9379698c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,5 @@ { + "include": ["src/**/*", "types/**/*", "vendors"], "exclude": ["dist", "tests"], "compilerOptions": { "skipLibCheck": true, @@ -50,7 +51,9 @@ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ + "types": [ + "vitest/importMeta" + ], // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tsconfig.test.json b/tsconfig.test.json deleted file mode 100644 index f4c451fd..00000000 --- a/tsconfig.test.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./tsconfig", - "compilerOptions": { - "rootDir": "." - } -} diff --git a/types/custom.d.ts b/types/custom.d.ts index 0a0ef7e8..9bc0df76 100644 --- a/types/custom.d.ts +++ b/types/custom.d.ts @@ -7,3 +7,7 @@ declare type XOR<T extends any[], U extends any[] = []> = T extends [] ? never : (T[0] & Without<Union<[...U, ...Tail<T>]>>) | XOR<Tail<T>, [T[0], ...U]>; declare type Resolved<T> = T extends Promise<infer U> ? U : never; + +interface ImportMeta { + env?: Record<string, unknown>; +} diff --git a/vendors/.gitignore b/vendors/.gitignore new file mode 100644 index 00000000..012a3cd6 --- /dev/null +++ b/vendors/.gitignore @@ -0,0 +1 @@ +index.js diff --git a/vendors/README.md b/vendors/README.md new file mode 100644 index 00000000..5589c157 --- /dev/null +++ b/vendors/README.md @@ -0,0 +1,10 @@ +Unfortunately, some libraries do not support pure ESM and we have to bundle them in pure ESM format beforehand for the specific environment such as Vitest. + +## Usage + +``` +# build mode +npm run build:vendors +# watch mode +npm run dev:vendors +``` diff --git a/vendors/build.js b/vendors/build.js new file mode 100644 index 00000000..935dc9ec --- /dev/null +++ b/vendors/build.js @@ -0,0 +1,27 @@ +import { commonjs } from '@hyrious/esbuild-plugin-commonjs'; +import chokidar from 'chokidar'; +import { context } from 'esbuild'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const dirname = path.dirname(fileURLToPath(import.meta.url)); + +const ctx = await context({ + entryPoints: [path.join(dirname, 'index.src.js')], + bundle: true, + format: 'esm', + outfile: path.join(dirname, 'index.js'), + platform: 'node', + plugins: [commonjs()], +}).catch(() => process.exit(1)); +await ctx.rebuild(); + +if (process.argv.slice(2).includes('-w')) { + chokidar.watch(path.join(dirname, '**')).on('all', async () => { + await ctx.rebuild().catch(() => { + /* NOOP */ + }); + }); +} else { + await ctx.dispose(); +} diff --git a/vendors/index.d.ts b/vendors/index.d.ts new file mode 100644 index 00000000..24c0746a --- /dev/null +++ b/vendors/index.d.ts @@ -0,0 +1,4 @@ +import { copy, copySync, move, moveSync, remove, removeSync } from 'fs-extra'; +import tmp from 'tmp'; + +export { copy, copySync, move, moveSync, remove, removeSync, tmp }; diff --git a/vendors/index.src.js b/vendors/index.src.js new file mode 100644 index 00000000..cabb040c --- /dev/null +++ b/vendors/index.src.js @@ -0,0 +1,9 @@ +import tmp from 'tmp'; +import { copy, copySync } from '../node_modules/fs-extra/lib/copy/index.js'; +import { move, moveSync } from '../node_modules/fs-extra/lib/move/index.js'; +import { + remove, + removeSync, +} from '../node_modules/fs-extra/lib/remove/index.js'; + +export { copy, copySync, move, moveSync, remove, removeSync, tmp }; diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 00000000..782f18c8 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['**/src/__tests__/*.+(ts|tsx|js)', '**/tests/*.test.(ts|tsx|js)'], + coverage: { + provider: 'v8', + exclude: ['node_modules/', 'tests/', 'tmp/', 'vendors/'], + }, + snapshotFormat: { + escapeString: true, + printBasicPrototype: true, + }, + }, +}); diff --git a/yarn.lock b/yarn.lock index 3675dc85..42c25f11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,12 +2,12 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@ampproject/remapping@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" "@apidevtools/json-schema-ref-parser@9.0.7": @@ -19,27 +19,13 @@ call-me-maybe "^1.0.1" js-yaml "^3.13.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6": +"@babel/code-frame@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: "@babel/highlight" "^7.18.6" -"@babel/code-frame@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" - integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== - dependencies: - "@babel/highlight" "^7.10.1" - -"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== - dependencies: - "@babel/highlight" "^7.14.5" - "@babel/code-frame@^7.16.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" @@ -47,216 +33,15 @@ dependencies: "@babel/highlight" "^7.16.7" -"@babel/compat-data@^7.20.0": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.5.tgz#86f172690b093373a933223b4745deeb6049e733" - integrity sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g== - -"@babel/core@^7.11.6", "@babel/core@^7.12.3": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.5.tgz#45e2114dc6cd4ab167f81daf7820e8fa1250d113" - integrity sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.0" - "@babel/helper-module-transforms" "^7.20.2" - "@babel/helpers" "^7.20.5" - "@babel/parser" "^7.20.5" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.5" - "@babel/types" "^7.20.5" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" - -"@babel/generator@^7.15.0", "@babel/generator@^7.7.2": - version "7.15.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.0.tgz#a7d0c172e0d814974bad5aa77ace543b97917f15" - integrity sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ== - dependencies: - "@babel/types" "^7.15.0" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/generator@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95" - integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA== - dependencies: - "@babel/types" "^7.20.5" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/helper-compilation-targets@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" - integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== - dependencies: - "@babel/compat-data" "^7.20.0" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.21.3" - semver "^6.3.0" - -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - -"@babel/helper-function-name@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" - integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== - dependencies: - "@babel/helper-get-function-arity" "^7.14.5" - "@babel/template" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" - -"@babel/helper-get-function-arity@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" - integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-hoist-variables@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" - integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-transforms@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" - integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.2" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.1", "@babel/helper-plugin-utils@^7.8.0": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz#ec5a5cf0eec925b66c60580328b122c01230a127" - integrity sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA== - -"@babel/helper-plugin-utils@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz#174254d0f2424d8aefb4dd48057511247b0a9eeb" - integrity sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA== - -"@babel/helper-plugin-utils@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" - integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== - -"@babel/helper-plugin-utils@^7.18.6": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== - -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== - dependencies: - "@babel/types" "^7.20.2" - -"@babel/helper-split-export-declaration@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" - integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - -"@babel/helper-validator-identifier@^7.10.1", "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9": - version "7.14.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48" - integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g== - "@babel/helper-validator-identifier@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== - -"@babel/helpers@^7.20.5": - version "7.20.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.6.tgz#e64778046b70e04779dfbdf924e7ebb45992c763" - integrity sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.5" - "@babel/types" "^7.20.5" - -"@babel/highlight@^7.10.1", "@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" - js-tokens "^4.0.0" +"@babel/helper-validator-identifier@^7.18.6": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== "@babel/highlight@^7.16.7": version "7.17.9" @@ -267,208 +52,130 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.10.1": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" - integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== - -"@babel/parser@^7.14.5", "@babel/parser@^7.15.0": - version "7.15.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.3.tgz#3416d9bea748052cfcb63dbcc27368105b1ed862" - integrity sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA== - -"@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" - integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-bigint@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz#d5bc0645913df5b17ad7eda0fa2308330bde34c5" - integrity sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - -"@babel/plugin-syntax-import-meta@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.1.tgz#3e59120ed8b3c2ccc5abb1cfc7aaa3ea01cd36b6" - integrity sha512-ypC4jwfIVF72og0dgvEcFRdOM2V9Qm1tu7RGmdZOlhsccyK0wisXmMObGuWEOd5jQ+K9wcIgSNftCpk2vkjUfQ== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.7.2": +"@babel/highlight@^7.18.6": version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" - integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.1.tgz#fffee77b4934ce77f3b427649ecdddbec1958550" - integrity sha512-XyHIFa9kdrgJS91CUH+ccPVTnJShr8nLGc5bG2IhGXv5p1Rd+8BleGE5yzIg2Nc1QZAdHDa0Qp4m6066OL96Iw== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz#25761ee7410bc8cf97327ba741ee94e4a61b7d99" - integrity sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" - integrity sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-typescript@^7.7.2": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716" - integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/template@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" - integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/parser" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/template@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" - integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" - -"@babel/template@^7.3.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" - integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== - dependencies: - "@babel/code-frame" "^7.10.1" - "@babel/parser" "^7.10.1" - "@babel/types" "^7.10.1" - -"@babel/traverse@^7.20.1", "@babel/traverse@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133" - integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.5" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.5" - "@babel/types" "^7.20.5" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.7.2": - version "7.15.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.0.tgz#4cca838fd1b2a03283c1f38e141f639d60b3fc98" - integrity sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.0" - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-hoist-variables" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/parser" "^7.15.0" - "@babel/types" "^7.15.0" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.10.1", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" - integrity sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng== - dependencies: - "@babel/helper-validator-identifier" "^7.10.1" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - -"@babel/types@^7.14.5", "@babel/types@^7.15.0": - version "7.15.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.0.tgz#61af11f2286c4e9c69ca8deb5f4375a73c72dcbd" - integrity sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ== - dependencies: - "@babel/helper-validator-identifier" "^7.14.9" - to-fast-properties "^2.0.0" - -"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84" - integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg== + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@esbuild/android-arm64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.11.tgz#fa6f0cc7105367cb79cc0a8bf32bf50cb1673e45" + integrity sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw== + +"@esbuild/android-arm@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.11.tgz#ae84a410696c9f549a15be94eaececb860bacacb" + integrity sha512-q4qlUf5ucwbUJZXF5tEQ8LF7y0Nk4P58hOsGk3ucY0oCwgQqAnqXVbUuahCddVHfrxmpyewRpiTHwVHIETYu7Q== + +"@esbuild/android-x64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.11.tgz#0e58360bbc789ad0d68174d32ba20e678c2a16b6" + integrity sha512-iPuoxQEV34+hTF6FT7om+Qwziv1U519lEOvekXO9zaMMlT9+XneAhKL32DW3H7okrCOBQ44BMihE8dclbZtTuw== + +"@esbuild/darwin-arm64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.11.tgz#fcdcd2ef76ca656540208afdd84f284072f0d1f9" + integrity sha512-Gm0QkI3k402OpfMKyQEEMG0RuW2LQsSmI6OeO4El2ojJMoF5NLYb3qMIjvbG/lbMeLOGiW6ooU8xqc+S0fgz2w== + +"@esbuild/darwin-x64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.11.tgz#c5ac602ec0504a8ff81e876bc8a9811e94d69d37" + integrity sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw== + +"@esbuild/freebsd-arm64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.11.tgz#7012fb06ee3e6e0d5560664a65f3fefbcc46db2e" + integrity sha512-atEyuq6a3omEY5qAh5jIORWk8MzFnCpSTUruBgeyN9jZq1K/QI9uke0ATi3MHu4L8c59CnIi4+1jDKMuqmR71A== + +"@esbuild/freebsd-x64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.11.tgz#c5de1199f70e1f97d5c8fca51afa9bf9a2af5969" + integrity sha512-XtuPrEfBj/YYYnAAB7KcorzzpGTvOr/dTtXPGesRfmflqhA4LMF0Gh/n5+a9JBzPuJ+CGk17CA++Hmr1F/gI0Q== + +"@esbuild/linux-arm64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.11.tgz#2a6d3a74e0b8b5f294e22b4515b29f76ebd42660" + integrity sha512-c6Vh2WS9VFKxKZ2TvJdA7gdy0n6eSy+yunBvv4aqNCEhSWVor1TU43wNRp2YLO9Vng2G+W94aRz+ILDSwAiYog== + +"@esbuild/linux-arm@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.11.tgz#5175bd61b793b436e4aece6328aa0d9be07751e1" + integrity sha512-Idipz+Taso/toi2ETugShXjQ3S59b6m62KmLHkJlSq/cBejixmIydqrtM2XTvNCywFl3VC7SreSf6NV0i6sRyg== + +"@esbuild/linux-ia32@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.11.tgz#20ee6cfd65a398875f321a485e7b2278e5f6f67b" + integrity sha512-S3hkIF6KUqRh9n1Q0dSyYcWmcVa9Cg+mSoZEfFuzoYXXsk6196qndrM+ZiHNwpZKi3XOXpShZZ+9dfN5ykqjjw== + +"@esbuild/linux-loong64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.11.tgz#8e7b251dede75083bf44508dab5edce3f49d052b" + integrity sha512-MRESANOoObQINBA+RMZW+Z0TJWpibtE7cPFnahzyQHDCA9X9LOmGh68MVimZlM9J8n5Ia8lU773te6O3ILW8kw== + +"@esbuild/linux-mips64el@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.11.tgz#a3125eb48538ac4932a9d05089b157f94e443165" + integrity sha512-qVyPIZrXNMOLYegtD1u8EBccCrBVshxMrn5MkuFc3mEVsw7CCQHaqZ4jm9hbn4gWY95XFnb7i4SsT3eflxZsUg== + +"@esbuild/linux-ppc64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.11.tgz#842abadb7a0995bd539adee2be4d681b68279499" + integrity sha512-T3yd8vJXfPirZaUOoA9D2ZjxZX4Gr3QuC3GztBJA6PklLotc/7sXTOuuRkhE9W/5JvJP/K9b99ayPNAD+R+4qQ== + +"@esbuild/linux-riscv64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.11.tgz#7ce6e6cee1c72d5b4d2f4f8b6fcccf4a9bea0e28" + integrity sha512-evUoRPWiwuFk++snjH9e2cAjF5VVSTj+Dnf+rkO/Q20tRqv+644279TZlPK8nUGunjPAtQRCj1jQkDAvL6rm2w== + +"@esbuild/linux-s390x@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.11.tgz#98fbc794363d02ded07d300df2e535650b297b96" + integrity sha512-/SlRJ15XR6i93gRWquRxYCfhTeC5PdqEapKoLbX63PLCmAkXZHY2uQm2l9bN0oPHBsOw2IswRZctMYS0MijFcg== + +"@esbuild/linux-x64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.11.tgz#f8458ec8cf74c8274e4cacd00744d8446cac52eb" + integrity sha512-xcncej+wF16WEmIwPtCHi0qmx1FweBqgsRtEL1mSHLFR6/mb3GEZfLQnx+pUDfRDEM4DQF8dpXIW7eDOZl1IbA== + +"@esbuild/netbsd-x64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.11.tgz#a7b2f991b8293748a7be42eac1c4325faf0c7cca" + integrity sha512-aSjMHj/F7BuS1CptSXNg6S3M4F3bLp5wfFPIJM+Km2NfIVfFKhdmfHF9frhiCLIGVzDziggqWll0B+9AUbud/Q== + +"@esbuild/openbsd-x64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.11.tgz#3e50923de84c54008f834221130fd23646072b2f" + integrity sha512-tNBq+6XIBZtht0xJGv7IBB5XaSyvYPCm1PxJ33zLQONdZoLVM0bgGqUrXnJyiEguD9LU4AHiu+GCXy/Hm9LsdQ== + +"@esbuild/sunos-x64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.11.tgz#ae47a550b0cd395de03606ecfba03cc96c7c19e2" + integrity sha512-kxfbDOrH4dHuAAOhr7D7EqaYf+W45LsAOOhAet99EyuxxQmjbk8M9N4ezHcEiCYPaiW8Dj3K26Z2V17Gt6p3ng== + +"@esbuild/win32-arm64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.11.tgz#05d364582b7862d7fbf4698ef43644f7346dcfcc" + integrity sha512-Sh0dDRyk1Xi348idbal7lZyfSkjhJsdFeuC13zqdipsvMetlGiFQNdO+Yfp6f6B4FbyQm7qsk16yaZk25LChzg== + +"@esbuild/win32-ia32@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.11.tgz#a3372095a4a1939da672156a3c104f8ce85ee616" + integrity sha512-o9JUIKF1j0rqJTFbIoF4bXj6rvrTZYOrfRcGyL0Vm5uJ/j5CkBD/51tpdxe9lXEDouhRgdr/BYzUrDOvrWwJpg== + +"@esbuild/win32-x64@0.18.11": + version "0.18.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.11.tgz#6526c7e1b40d5b9f0a222c6b767c22f6fb97aa57" + integrity sha512-rQI4cjLHd2hGsM1LqgDI7oOCYbQ6IBOVsX9ejuRMSze0GqXUG2ekwiKkiBU1pRGSeCqFFHxTrcEydB2Hyoz9CA== + "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" @@ -484,6 +191,11 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== +"@hyrious/esbuild-plugin-commonjs@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@hyrious/esbuild-plugin-commonjs/-/esbuild-plugin-commonjs-0.2.2.tgz#1505dc904ead8628fee73fd0caa458bea2b20c91" + integrity sha512-08RxncQ0S3vgVtj8bxx/TcG+4b6bLbWCiXQtRpkuO7U3dLi1pVvSOnIacVphWLCHudCvJL+05b/r1km+r3osWQ== + "@iarna/toml@2.2.5": version "2.2.5" resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" @@ -494,226 +206,22 @@ resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== - dependencies: - camelcase "^5.3.1" - find-up "^4.1.0" - get-package-type "^0.1.0" - js-yaml "^3.13.1" - resolve-from "^5.0.0" - "@istanbuljs/schema@^0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jest/console@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.3.1.tgz#3e3f876e4e47616ea3b1464b9fbda981872e9583" - integrity sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg== - dependencies: - "@jest/types" "^29.3.1" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^29.3.1" - jest-util "^29.3.1" - slash "^3.0.0" - -"@jest/core@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.3.1.tgz#bff00f413ff0128f4debec1099ba7dcd649774a1" - integrity sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw== +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" + integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== dependencies: - "@jest/console" "^29.3.1" - "@jest/reporters" "^29.3.1" - "@jest/test-result" "^29.3.1" - "@jest/transform" "^29.3.1" - "@jest/types" "^29.3.1" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^29.2.0" - jest-config "^29.3.1" - jest-haste-map "^29.3.1" - jest-message-util "^29.3.1" - jest-regex-util "^29.2.0" - jest-resolve "^29.3.1" - jest-resolve-dependencies "^29.3.1" - jest-runner "^29.3.1" - jest-runtime "^29.3.1" - jest-snapshot "^29.3.1" - jest-util "^29.3.1" - jest-validate "^29.3.1" - jest-watcher "^29.3.1" - micromatch "^4.0.4" - pretty-format "^29.3.1" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/environment@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.3.1.tgz#eb039f726d5fcd14698acd072ac6576d41cfcaa6" - integrity sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag== - dependencies: - "@jest/fake-timers" "^29.3.1" - "@jest/types" "^29.3.1" - "@types/node" "*" - jest-mock "^29.3.1" - -"@jest/expect-utils@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.3.1.tgz#531f737039e9b9e27c42449798acb5bba01935b6" - integrity sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g== - dependencies: - jest-get-type "^29.2.0" - -"@jest/expect@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.3.1.tgz#456385b62894349c1d196f2d183e3716d4c6a6cd" - integrity sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg== - dependencies: - expect "^29.3.1" - jest-snapshot "^29.3.1" - -"@jest/fake-timers@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.3.1.tgz#b140625095b60a44de820876d4c14da1aa963f67" - integrity sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A== - dependencies: - "@jest/types" "^29.3.1" - "@sinonjs/fake-timers" "^9.1.2" - "@types/node" "*" - jest-message-util "^29.3.1" - jest-mock "^29.3.1" - jest-util "^29.3.1" + "@sinclair/typebox" "^0.27.8" -"@jest/globals@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.3.1.tgz#92be078228e82d629df40c3656d45328f134a0c6" - integrity sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q== - dependencies: - "@jest/environment" "^29.3.1" - "@jest/expect" "^29.3.1" - "@jest/types" "^29.3.1" - jest-mock "^29.3.1" - -"@jest/reporters@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.3.1.tgz#9a6d78c109608e677c25ddb34f907b90e07b4310" - integrity sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.3.1" - "@jest/test-result" "^29.3.1" - "@jest/transform" "^29.3.1" - "@jest/types" "^29.3.1" - "@jridgewell/trace-mapping" "^0.3.15" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^29.3.1" - jest-util "^29.3.1" - jest-worker "^29.3.1" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - v8-to-istanbul "^9.0.1" - -"@jest/schemas@^29.0.0": - version "29.0.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a" - integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA== - dependencies: - "@sinclair/typebox" "^0.24.1" - -"@jest/source-map@^29.2.0": - version "29.2.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.2.0.tgz#ab3420c46d42508dcc3dc1c6deee0b613c235744" - integrity sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ== - dependencies: - "@jridgewell/trace-mapping" "^0.3.15" - callsites "^3.0.0" - graceful-fs "^4.2.9" - -"@jest/test-result@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.3.1.tgz#92cd5099aa94be947560a24610aa76606de78f50" - integrity sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw== - dependencies: - "@jest/console" "^29.3.1" - "@jest/types" "^29.3.1" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz#fa24b3b050f7a59d48f7ef9e0b782ab65123090d" - integrity sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA== - dependencies: - "@jest/test-result" "^29.3.1" - graceful-fs "^4.2.9" - jest-haste-map "^29.3.1" - slash "^3.0.0" - -"@jest/transform@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.3.1.tgz#1e6bd3da4af50b5c82a539b7b1f3770568d6e36d" - integrity sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^29.3.1" - "@jridgewell/trace-mapping" "^0.3.15" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^2.0.0" - fast-json-stable-stringify "^2.1.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.3.1" - jest-regex-util "^29.2.0" - jest-util "^29.3.1" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.1" - -"@jest/types@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.3.1.tgz#7c5a80777cb13e703aeec6788d044150341147e3" - integrity sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA== - dependencies: - "@jest/schemas" "^29.0.0" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -724,7 +232,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": +"@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== @@ -734,7 +242,12 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== @@ -1087,30 +600,16 @@ conventional-recommended-bump "^6.1.0" semver "7.3.8" -"@sinclair/typebox@^0.24.1": - version "0.24.51" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" - integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@sindresorhus/is@^5.2.0": version "5.3.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.3.0.tgz#0ec9264cf54a527671d990eb874e030b55b70dcc" integrity sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw== -"@sinonjs/commons@^1.7.0": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" - integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@szmarczak/http-timer@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" @@ -1140,39 +639,6 @@ dependencies: "@types/readdir-glob" "*" -"@types/babel__core@^7.1.14": - version "7.1.15" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024" - integrity sha512-bxlMKPDbY8x5h6HBwVzEOk2C8fb6SLfYQ5Jw3uBYuYF1lfWk/kbLd81la82vrIkBb0l+JdmrZaDikPrNxpS/Ew== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.6.2" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" - integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.0" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.0.tgz#0c888dd70b3ee9eebb6e4f200e809da0076262be" - integrity sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.0.tgz#b9a1efa635201ba9bc850323a8793ee2d36c04a0" - integrity sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg== - dependencies: - "@babel/types" "^7.3.0" - "@types/cacache@*": version "15.0.1" resolved "https://registry.yarnpkg.com/@types/cacache/-/cacache-15.0.1.tgz#3d1943cc80ade160c9ae98bd5c1ebcc538f9cd57" @@ -1180,6 +646,18 @@ dependencies: "@types/node" "*" +"@types/chai-subset@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@types/chai-subset/-/chai-subset-1.3.3.tgz#97893814e92abd2c534de422cb377e0e0bdaac94" + integrity sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw== + dependencies: + "@types/chai" "*" + +"@types/chai@*", "@types/chai@^4.3.5": + version "4.3.5" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b" + integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng== + "@types/cli-table@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@types/cli-table/-/cli-table-0.3.0.tgz#f1857156bf5fd115c6a2db260ba0be1f8fc5671c" @@ -1197,6 +675,14 @@ dependencies: "@types/ms" "*" +"@types/fs-extra@^11.0.1": + version "11.0.1" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.1.tgz#f542ec47810532a8a252127e6e105f487e0a6ea5" + integrity sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA== + dependencies: + "@types/jsonfile" "*" + "@types/node" "*" + "@types/github-slugger@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@types/github-slugger/-/github-slugger-1.3.0.tgz#16ab393b30d8ae2a111ac748a015ac05a1fc5524" @@ -1210,13 +696,6 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/graceful-fs@^4.1.3": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== - dependencies: - "@types/node" "*" - "@types/hast@^2.0.0": version "2.3.4" resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" @@ -1229,33 +708,11 @@ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": +"@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== -"@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" - integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/jest@^29.2.4": - version "29.2.4" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.2.4.tgz#9c155c4b81c9570dbd183eb8604aa0ae80ba5a5b" - integrity sha512-PipFB04k2qTRPePduVLTRiPzQfvMeLwUN3Z21hsAKaB/W9IIzgB2pizCL466ftJlcyZqnHoC9ZHpxLGl3fS86A== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - "@types/jsdom@^21.1.1": version "21.1.1" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-21.1.1.tgz#e59e26352071267b507bf04d51841a1d7d3e8617" @@ -1270,6 +727,13 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== +"@types/jsonfile@*": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.1.tgz#ac84e9aefa74a2425a0fb3012bdea44f58970f1b" + integrity sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png== + dependencies: + "@types/node" "*" + "@types/lodash@^4.14.168": version "4.14.168" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008" @@ -1428,11 +892,6 @@ dependencies: "@types/node" "*" -"@types/stack-utils@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" - integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== - "@types/tmp@^0.2.1": version "0.2.1" resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.2.1.tgz#83ecf4ec22a8c218c71db25f316619fe5b986011" @@ -1480,12 +939,65 @@ dependencies: "@types/yargs-parser" "*" -"@types/yargs@^17.0.8": - version "17.0.17" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.17.tgz#5672e5621f8e0fca13f433a8017aae4b7a2a03e7" - integrity sha512-72bWxFKTK6uwWJAVT+3rF6Jo6RTojiJ27FQo8Rf60AL+VZbzoVPnMFhKsUnbjR8A3BTCYQ7Mv3hnl8T0A+CX9g== +"@vitest/coverage-v8@^0.33.0": + version "0.33.0" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-0.33.0.tgz#dfcb36cf51624a89d33ab0962a6eec8a41346ef2" + integrity sha512-Rj5IzoLF7FLj6yR7TmqsfRDSeaFki6NAJ/cQexqhbWkHEV2htlVGrmuOde3xzvFsCbLCagf4omhcIaVmfU8Okg== dependencies: - "@types/yargs-parser" "*" + "@ampproject/remapping" "^2.2.1" + "@bcoe/v8-coverage" "^0.2.3" + istanbul-lib-coverage "^3.2.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.1" + istanbul-reports "^3.1.5" + magic-string "^0.30.1" + picocolors "^1.0.0" + std-env "^3.3.3" + test-exclude "^6.0.0" + v8-to-istanbul "^9.1.0" + +"@vitest/expect@0.33.0": + version "0.33.0" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-0.33.0.tgz#f48652591f3573ad6c2db828ad358d5c078845d3" + integrity sha512-sVNf+Gla3mhTCxNJx+wJLDPp/WcstOe0Ksqz4Vec51MmgMth/ia0MGFEkIZmVGeTL5HtjYR4Wl/ZxBxBXZJTzQ== + dependencies: + "@vitest/spy" "0.33.0" + "@vitest/utils" "0.33.0" + chai "^4.3.7" + +"@vitest/runner@0.33.0": + version "0.33.0" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.33.0.tgz#0b1a4d04ff8bc5cdad73920eac019d99550edf9d" + integrity sha512-UPfACnmCB6HKRHTlcgCoBh6ppl6fDn+J/xR8dTufWiKt/74Y9bHci5CKB8tESSV82zKYtkBJo9whU3mNvfaisg== + dependencies: + "@vitest/utils" "0.33.0" + p-limit "^4.0.0" + pathe "^1.1.1" + +"@vitest/snapshot@0.33.0": + version "0.33.0" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-0.33.0.tgz#4400a90c48907808122b573053a2112a832b3698" + integrity sha512-tJjrl//qAHbyHajpFvr8Wsk8DIOODEebTu7pgBrP07iOepR5jYkLFiqLq2Ltxv+r0uptUb4izv1J8XBOwKkVYA== + dependencies: + magic-string "^0.30.1" + pathe "^1.1.1" + pretty-format "^29.5.0" + +"@vitest/spy@0.33.0": + version "0.33.0" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.33.0.tgz#366074d3cf9cf1ed8aeaa76e50e78c799fb242eb" + integrity sha512-Kv+yZ4hnH1WdiAkPUQTpRxW8kGtH8VRTnus7ZTGovFYM1ZezJpvGtb9nPIjPnptHbsyIAxYZsEpVPYgtpjGnrg== + dependencies: + tinyspy "^2.1.1" + +"@vitest/utils@0.33.0": + version "0.33.0" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.33.0.tgz#6b9820cb8f128d649da6f78ecaa9b73d6222b277" + integrity sha512-pF1w22ic965sv+EN6uoePkAOTkAPWM03Ri/jXNyMIKBb/XHLDPfhLvf/Fa9g0YECevAIz56oVYXhodLvLQ/awA== + dependencies: + diff-sequences "^29.4.3" + loupe "^2.3.6" + pretty-format "^29.5.0" "@vivliostyle/core@^2.25.2": version "2.25.2" @@ -1582,6 +1094,11 @@ acorn@^8.7.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +acorn@^8.8.2, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" @@ -1705,14 +1222,6 @@ any-promise@^1.0.0: resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= -anymatch@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -1827,6 +1336,11 @@ arrify@^2.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + ast-types@^0.13.2: version "0.13.4" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" @@ -1868,66 +1382,6 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -babel-jest@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.3.1.tgz#05c83e0d128cd48c453eea851482a38782249f44" - integrity sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA== - dependencies: - "@jest/transform" "^29.3.1" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.2.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - -babel-plugin-istanbul@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" - integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^5.0.4" - test-exclude "^6.0.0" - -babel-plugin-jest-hoist@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz#23ee99c37390a98cfddf3ef4a78674180d823094" - integrity sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - -babel-preset-current-node-syntax@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" - integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== - dependencies: - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - -babel-preset-jest@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz#3048bea3a1af222e3505e4a767a974c95a7620dc" - integrity sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA== - dependencies: - babel-plugin-jest-hoist "^29.2.0" - babel-preset-current-node-syntax "^1.0.0" - bail@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" @@ -2043,30 +1497,6 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.21.3: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== - dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" - -bs-logger@0.x: - version "0.2.6" - resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" - integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== - dependencies: - fast-json-stable-stringify "2.x" - -bser@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" - integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== - dependencies: - node-int64 "^0.4.0" - buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -2110,6 +1540,11 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + cacache@^16.1.0: version "16.1.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" @@ -2211,21 +1646,11 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - camelcase@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048" integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== -caniuse-lite@^1.0.30001400: - version "1.0.30001439" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz#ab7371faeb4adff4b74dad1718a6fd122e45d9cb" - integrity sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A== - canvas@^2.11.2: version "2.11.2" resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.11.2.tgz#553d87b1e0228c7ac0fc72887c3adbac4abbd860" @@ -2240,6 +1665,19 @@ ccount@^1.0.0: resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== +chai@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" + integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^4.1.2" + get-func-name "^2.0.0" + loupe "^2.3.1" + pathval "^1.1.1" + type-detect "^4.0.5" + chalk@5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.1.2.tgz#d957f370038b75ac572471e83be4c5ca9f8e8c45" @@ -2283,11 +1721,6 @@ chalk@^5.0.0, chalk@^5.0.1, chalk@^5.1.2: resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3" integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== -char-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== - character-entities-html4@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125" @@ -2313,6 +1746,11 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== + cheerio-select@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823" @@ -2367,11 +1805,6 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.0.tgz#6d01b3696c59915b6ce057e4aa4adfc2fa25f5ef" integrity sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog== -cjs-module-lexer@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" - integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -2452,15 +1885,6 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -2471,21 +1895,11 @@ cmd-shim@^6.0.0: resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.0.tgz#6c4eb6437defacf0ae8e9d281d5b06749730a65b" integrity sha512-wx+RWLgiSU6SCDzMtxG0Dv1lsuOcEfqq5SbqAViezaJIkR5sbveKzFU31YnWhqrJx3o3Iu3H0Rq8R00OS3oI+Q== -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - collapse-white-space@^1.0.2: version "1.0.6" resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== -collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -2777,18 +2191,13 @@ conventional-recommended-bump@^6.1.0: meow "^8.0.0" q "^1.5.1" -convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== dependencies: safe-buffer "~5.1.1" -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -2939,7 +2348,7 @@ debug@^3.1.1, debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.3.1, debug@^4.3.3: +debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -2990,10 +2399,12 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +deep-eql@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== + dependencies: + type-detect "^4.0.0" deep-extend@^0.6.0: version "0.6.0" @@ -3005,11 +2416,6 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - defaults@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" @@ -3082,15 +2488,10 @@ detect-libc@^2.0.0: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - -diff-sequences@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e" - integrity sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== dir-glob@^3.0.1: version "3.0.1" @@ -3195,16 +2596,6 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -electron-to-chromium@^1.4.251: - version "1.4.284" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" - integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== - -emittery@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" - integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -3394,6 +2785,34 @@ es6-weak-map@^2.0.3: es6-iterator "^2.0.3" es6-symbol "^3.1.1" +esbuild@^0.18.10, esbuild@^0.18.11: + version "0.18.11" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.11.tgz#cbf94dc3359d57f600a0dbf281df9b1d1b4a156e" + integrity sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA== + optionalDependencies: + "@esbuild/android-arm" "0.18.11" + "@esbuild/android-arm64" "0.18.11" + "@esbuild/android-x64" "0.18.11" + "@esbuild/darwin-arm64" "0.18.11" + "@esbuild/darwin-x64" "0.18.11" + "@esbuild/freebsd-arm64" "0.18.11" + "@esbuild/freebsd-x64" "0.18.11" + "@esbuild/linux-arm" "0.18.11" + "@esbuild/linux-arm64" "0.18.11" + "@esbuild/linux-ia32" "0.18.11" + "@esbuild/linux-loong64" "0.18.11" + "@esbuild/linux-mips64el" "0.18.11" + "@esbuild/linux-ppc64" "0.18.11" + "@esbuild/linux-riscv64" "0.18.11" + "@esbuild/linux-s390x" "0.18.11" + "@esbuild/linux-x64" "0.18.11" + "@esbuild/netbsd-x64" "0.18.11" + "@esbuild/openbsd-x64" "0.18.11" + "@esbuild/sunos-x64" "0.18.11" + "@esbuild/win32-arm64" "0.18.11" + "@esbuild/win32-ia32" "0.18.11" + "@esbuild/win32-x64" "0.18.11" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -3409,11 +2828,6 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -3529,22 +2943,6 @@ execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= - -expect@^29.0.0, expect@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.3.1.tgz#92877aad3f7deefc2e3f6430dd195b92295554a6" - integrity sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA== - dependencies: - "@jest/expect-utils" "^29.3.1" - jest-get-type "^29.2.0" - jest-matcher-utils "^29.3.1" - jest-message-util "^29.3.1" - jest-util "^29.3.1" - ext@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" @@ -3587,11 +2985,6 @@ fast-glob@3.2.12, fast-glob@^3.2.11: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -3625,13 +3018,6 @@ fault@^1.0.1: dependencies: format "^0.2.0" -fb-watchman@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" - integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== - dependencies: - bser "2.1.1" - fetch-blob@^3.1.2, fetch-blob@^3.1.4: version "3.2.0" resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" @@ -3676,7 +3062,7 @@ find-up@^2.0.0: dependencies: locate-path "^2.0.0" -find-up@^4.0.0, find-up@^4.1.0: +find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -3746,6 +3132,15 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -3767,7 +3162,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: +fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -3843,16 +3238,16 @@ gauge@^5.0.0: strip-ansi "^6.0.1" wide-align "^1.1.5" -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== + get-intrinsic@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49" @@ -3876,11 +3271,6 @@ get-own-enumerable-property-symbols@^3.0.0: resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== -get-package-type@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" - integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== - get-pkg-repo@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" @@ -4038,11 +3428,6 @@ global-dirs@^3.0.0: dependencies: ini "2.0.0" -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - globalthis@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" @@ -4085,7 +3470,7 @@ got@12.5.3, got@^12.1.0: p-cancelable "^3.0.0" responselike "^3.0.0" -graceful-fs@4.2.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@4.2.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -4547,14 +3932,6 @@ import-lazy@^4.0.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== -import-local@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" - integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -4727,13 +4104,6 @@ is-ci@3.0.1, is-ci@^3.0.1: dependencies: ci-info "^3.2.0" -is-core-module@^2.2.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.6.0.tgz#d7553b2526fe59b92ba3e40c8df757ec8a709e19" - integrity sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ== - dependencies: - has "^1.0.3" - is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" @@ -4766,11 +4136,6 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -5040,17 +4405,6 @@ istanbul-lib-coverage@^3.2.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" - integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^6.3.0" - istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -5060,16 +4414,16 @@ istanbul-lib-report@^3.0.0: make-dir "^3.0.0" supports-color "^7.1.0" -istanbul-lib-source-maps@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" - integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== +istanbul-lib-source-maps@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^3.1.3: +istanbul-reports@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== @@ -5090,367 +4444,6 @@ iterate-value@^1.0.2: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" -jest-changed-files@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.2.0.tgz#b6598daa9803ea6a4dce7968e20ab380ddbee289" - integrity sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA== - dependencies: - execa "^5.0.0" - p-limit "^3.1.0" - -jest-circus@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.3.1.tgz#177d07c5c0beae8ef2937a67de68f1e17bbf1b4a" - integrity sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg== - dependencies: - "@jest/environment" "^29.3.1" - "@jest/expect" "^29.3.1" - "@jest/test-result" "^29.3.1" - "@jest/types" "^29.3.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - is-generator-fn "^2.0.0" - jest-each "^29.3.1" - jest-matcher-utils "^29.3.1" - jest-message-util "^29.3.1" - jest-runtime "^29.3.1" - jest-snapshot "^29.3.1" - jest-util "^29.3.1" - p-limit "^3.1.0" - pretty-format "^29.3.1" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-cli@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.3.1.tgz#e89dff427db3b1df50cea9a393ebd8640790416d" - integrity sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ== - dependencies: - "@jest/core" "^29.3.1" - "@jest/test-result" "^29.3.1" - "@jest/types" "^29.3.1" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^29.3.1" - jest-util "^29.3.1" - jest-validate "^29.3.1" - prompts "^2.0.1" - yargs "^17.3.1" - -jest-config@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.3.1.tgz#0bc3dcb0959ff8662957f1259947aedaefb7f3c6" - integrity sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.3.1" - "@jest/types" "^29.3.1" - babel-jest "^29.3.1" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^29.3.1" - jest-environment-node "^29.3.1" - jest-get-type "^29.2.0" - jest-regex-util "^29.2.0" - jest-resolve "^29.3.1" - jest-runner "^29.3.1" - jest-util "^29.3.1" - jest-validate "^29.3.1" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^29.3.1" - slash "^3.0.0" - strip-json-comments "^3.1.1" - -jest-diff@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.3.1.tgz#d8215b72fed8f1e647aed2cae6c752a89e757527" - integrity sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.3.1" - jest-get-type "^29.2.0" - pretty-format "^29.3.1" - -jest-docblock@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.2.0.tgz#307203e20b637d97cee04809efc1d43afc641e82" - integrity sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A== - dependencies: - detect-newline "^3.0.0" - -jest-each@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.3.1.tgz#bc375c8734f1bb96625d83d1ca03ef508379e132" - integrity sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA== - dependencies: - "@jest/types" "^29.3.1" - chalk "^4.0.0" - jest-get-type "^29.2.0" - jest-util "^29.3.1" - pretty-format "^29.3.1" - -jest-environment-node@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.3.1.tgz#5023b32472b3fba91db5c799a0d5624ad4803e74" - integrity sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag== - dependencies: - "@jest/environment" "^29.3.1" - "@jest/fake-timers" "^29.3.1" - "@jest/types" "^29.3.1" - "@types/node" "*" - jest-mock "^29.3.1" - jest-util "^29.3.1" - -jest-get-type@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" - integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== - -jest-haste-map@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.3.1.tgz#af83b4347f1dae5ee8c2fb57368dc0bb3e5af843" - integrity sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A== - dependencies: - "@jest/types" "^29.3.1" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^29.2.0" - jest-util "^29.3.1" - jest-worker "^29.3.1" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - -jest-leak-detector@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz#95336d020170671db0ee166b75cd8ef647265518" - integrity sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA== - dependencies: - jest-get-type "^29.2.0" - pretty-format "^29.3.1" - -jest-matcher-utils@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz#6e7f53512f80e817dfa148672bd2d5d04914a572" - integrity sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ== - dependencies: - chalk "^4.0.0" - jest-diff "^29.3.1" - jest-get-type "^29.2.0" - pretty-format "^29.3.1" - -jest-message-util@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.3.1.tgz#37bc5c468dfe5120712053dd03faf0f053bd6adb" - integrity sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.3.1" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.3.1" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-mock@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.3.1.tgz#60287d92e5010979d01f218c6b215b688e0f313e" - integrity sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA== - dependencies: - "@jest/types" "^29.3.1" - "@types/node" "*" - jest-util "^29.3.1" - -jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== - -jest-regex-util@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.2.0.tgz#82ef3b587e8c303357728d0322d48bbfd2971f7b" - integrity sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA== - -jest-resolve-dependencies@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz#a6a329708a128e68d67c49f38678a4a4a914c3bf" - integrity sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA== - dependencies: - jest-regex-util "^29.2.0" - jest-snapshot "^29.3.1" - -jest-resolve@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.3.1.tgz#9a4b6b65387a3141e4a40815535c7f196f1a68a7" - integrity sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.3.1" - jest-pnp-resolver "^1.2.2" - jest-util "^29.3.1" - jest-validate "^29.3.1" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - -jest-runner@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.3.1.tgz#a92a879a47dd096fea46bb1517b0a99418ee9e2d" - integrity sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA== - dependencies: - "@jest/console" "^29.3.1" - "@jest/environment" "^29.3.1" - "@jest/test-result" "^29.3.1" - "@jest/transform" "^29.3.1" - "@jest/types" "^29.3.1" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.13.1" - graceful-fs "^4.2.9" - jest-docblock "^29.2.0" - jest-environment-node "^29.3.1" - jest-haste-map "^29.3.1" - jest-leak-detector "^29.3.1" - jest-message-util "^29.3.1" - jest-resolve "^29.3.1" - jest-runtime "^29.3.1" - jest-util "^29.3.1" - jest-watcher "^29.3.1" - jest-worker "^29.3.1" - p-limit "^3.1.0" - source-map-support "0.5.13" - -jest-runtime@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.3.1.tgz#21efccb1a66911d6d8591276a6182f520b86737a" - integrity sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A== - dependencies: - "@jest/environment" "^29.3.1" - "@jest/fake-timers" "^29.3.1" - "@jest/globals" "^29.3.1" - "@jest/source-map" "^29.2.0" - "@jest/test-result" "^29.3.1" - "@jest/transform" "^29.3.1" - "@jest/types" "^29.3.1" - "@types/node" "*" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^29.3.1" - jest-message-util "^29.3.1" - jest-mock "^29.3.1" - jest-regex-util "^29.2.0" - jest-resolve "^29.3.1" - jest-snapshot "^29.3.1" - jest-util "^29.3.1" - slash "^3.0.0" - strip-bom "^4.0.0" - -jest-snapshot@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.3.1.tgz#17bcef71a453adc059a18a32ccbd594b8cc4e45e" - integrity sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-jsx" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.3.1" - "@jest/transform" "^29.3.1" - "@jest/types" "^29.3.1" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^29.3.1" - graceful-fs "^4.2.9" - jest-diff "^29.3.1" - jest-get-type "^29.2.0" - jest-haste-map "^29.3.1" - jest-matcher-utils "^29.3.1" - jest-message-util "^29.3.1" - jest-util "^29.3.1" - natural-compare "^1.4.0" - pretty-format "^29.3.1" - semver "^7.3.5" - -jest-util@^29.0.0, jest-util@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.3.1.tgz#1dda51e378bbcb7e3bc9d8ab651445591ed373e1" - integrity sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ== - dependencies: - "@jest/types" "^29.3.1" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-validate@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.3.1.tgz#d56fefaa2e7d1fde3ecdc973c7f7f8f25eea704a" - integrity sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g== - dependencies: - "@jest/types" "^29.3.1" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^29.2.0" - leven "^3.1.0" - pretty-format "^29.3.1" - -jest-watcher@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.3.1.tgz#3341547e14fe3c0f79f9c3a4c62dbc3fc977fd4a" - integrity sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg== - dependencies: - "@jest/test-result" "^29.3.1" - "@jest/types" "^29.3.1" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.13.1" - jest-util "^29.3.1" - string-length "^4.0.1" - -jest-worker@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.3.1.tgz#e9462161017a9bb176380d721cab022661da3d6b" - integrity sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw== - dependencies: - "@types/node" "*" - jest-util "^29.3.1" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.3.1.tgz#c130c0d551ae6b5459b8963747fed392ddbde122" - integrity sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA== - dependencies: - "@jest/core" "^29.3.1" - "@jest/types" "^29.3.1" - import-local "^3.0.2" - jest-cli "^29.3.1" - js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -5500,11 +4493,6 @@ jsdom@^22.1.0: ws "^8.13.0" xml-name-validator "^4.0.0" -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -5568,10 +4556,10 @@ json-stringify-safe@^5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== -json5@^2.2.1: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +jsonc-parser@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== jsonfile@^4.0.0: version "4.0.0" @@ -5580,6 +4568,15 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -5612,11 +4609,6 @@ kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - knockout@^3.5.0: version "3.5.1" resolved "https://registry.yarnpkg.com/knockout/-/knockout-3.5.1.tgz#62c81e81843bea2008fd23c575edd9ca978e75cf" @@ -5636,7 +4628,7 @@ lazystream@^1.0.0: dependencies: readable-stream "^2.0.5" -leven@^3.1.0, "leven@^3.1.0 < 4": +"leven@^3.1.0 < 4": version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== @@ -5697,6 +4689,11 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" +local-pkg@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.3.tgz#0ff361ab3ae7f1c19113d9bb97b98b905dbc4963" + integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g== + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -5744,17 +4741,12 @@ lodash.isplainobject@^4.0.6: resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== -lodash.memoize@4.x: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== - lodash.union@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== -lodash@4.17.21, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: +lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5785,6 +4777,13 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" +loupe@^2.3.1, loupe@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== + dependencies: + get-func-name "^2.0.0" + lowercase-keys@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" @@ -5821,6 +4820,13 @@ macos-release@^3.0.1: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-3.1.0.tgz#6165bb0736ae567ed6649e36ce6a24d87cbb7aca" integrity sha512-/M/R0gCDgM+Cv1IuBG1XGdfTFnMEG6PZeT+KGWHO/OG+imqmaD9CH5vHBTycEM3+Kc4uG2Il+tFAuUWLqQOeUA== +magic-string@^0.30.1: + version "0.30.1" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.1.tgz#ce5cd4b0a81a5d032bd69aab4522299b2166284d" + integrity sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + make-dir@^3.0.0, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -5828,11 +4834,6 @@ make-dir@^3.0.0, make-dir@^3.1.0: dependencies: semver "^6.0.0" -make-error@1.x: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - make-fetch-happen@^10.0.3: version "10.2.1" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" @@ -5877,13 +4878,6 @@ make-fetch-happen@^11.0.0: socks-proxy-agent "^7.0.0" ssri "^10.0.0" -makeerror@1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" - integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== - dependencies: - tmpl "1.0.5" - map-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -6200,6 +5194,16 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mlly@^1.2.0, mlly@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.4.0.tgz#830c10d63f1f97bd8785377b24dc2a15d972832b" + integrity sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg== + dependencies: + acorn "^8.9.0" + pathe "^1.1.1" + pkg-types "^1.0.3" + ufo "^1.1.2" + modify-values@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" @@ -6255,10 +5259,10 @@ nan@^2.17.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== negotiator@^0.6.3: version "0.6.3" @@ -6334,16 +5338,6 @@ node-gyp@^9.0.0: tar "^6.1.2" which "^2.0.2" -node-int64@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= - -node-releases@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== - node-stream-zip@^1.14.0: version "1.14.0" resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.14.0.tgz#fdf9b86d10d55c1e50aa1be4fea88bae256c4eba" @@ -6717,13 +5711,20 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2, p-limit@^3.1.0: +p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== + dependencies: + yocto-queue "^1.0.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -6860,7 +5861,7 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0, parse-json@^5.2.0: +parse-json@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -6938,7 +5939,7 @@ path-key@^4.0.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== -path-parse@^1.0.6, path-parse@^1.0.7: +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -6960,6 +5961,16 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathe@^1.1.0, pathe@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a" + integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q== + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + pdf-lib@^1.16.0: version "1.16.0" resolved "https://registry.yarnpkg.com/pdf-lib/-/pdf-lib-1.16.0.tgz#63cacec0d6139bf7a670dea8bad747ebf4c1fb42" @@ -7005,18 +6016,6 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== -pirates@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - pkg-dir@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" @@ -7024,6 +6023,15 @@ pkg-dir@^5.0.0: dependencies: find-up "^5.0.0" +pkg-types@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868" + integrity sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A== + dependencies: + jsonc-parser "^3.2.0" + mlly "^1.2.0" + pathe "^1.1.0" + playwright-core@1.35.0: version "1.35.0" resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.35.0.tgz#b7871b742b4a5c8714b7fa2f570c280a061cb414" @@ -7053,6 +6061,15 @@ postcss-selector-parser@^6.0.10: cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss@^8.4.24: + version "8.4.25" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.25.tgz#4a133f5e379eda7f61e906c3b1aaa9b81292726f" + integrity sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -7093,12 +6110,12 @@ prettier@^2.3.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== -pretty-format@^29.0.0, pretty-format@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.3.1.tgz#1841cac822b02b4da8971dacb03e8a871b4722da" - integrity sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg== +pretty-format@^29.5.0: + version "29.6.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.1.tgz#ec838c288850b7c4f9090b867c2d4f4edbfb0f3e" + integrity sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog== dependencies: - "@jest/schemas" "^29.0.0" + "@jest/schemas" "^29.6.0" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -7169,14 +6186,6 @@ promise.allsettled@1.0.6: get-intrinsic "^1.1.3" iterate-value "^1.0.2" -prompts@^2.0.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7" - integrity sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - property-information@^5.0.0, property-information@^5.3.0: version "5.6.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" @@ -7673,13 +6682,6 @@ resolve-alpn@^1.2.0: resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -7697,11 +6699,6 @@ resolve-pkg@^2.0.0: dependencies: resolve-from "^5.0.0" -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== - resolve@^1.1.6, resolve@^1.10.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -7711,14 +6708,6 @@ resolve@^1.1.6, resolve@^1.10.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - responselike@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" @@ -7764,6 +6753,13 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rollup@^3.25.2: + version "3.26.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.26.2.tgz#2e76a37606cb523fc9fef43e6f59c93f86d95e7c" + integrity sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA== + optionalDependencies: + fsevents "~2.3.2" + rrweb-cssom@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" @@ -7855,12 +6851,7 @@ semver@7.3.8, semver@^7.0.0, semver@^7.1.1, semver@^7.3.4, semver@^7.3.5, semver dependencies: lru-cache "^6.0.0" -semver@7.x: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - -semver@^6.0.0, semver@^6.3.0: +semver@^6.0.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -7923,7 +6914,7 @@ shell-quote@^1.6.1: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== -shelljs@0.8.5, shelljs@^0.8.3, shelljs@^0.8.4, shelljs@^0.8.5: +shelljs@0.8.5, shelljs@^0.8.3, shelljs@^0.8.4: version "0.8.5" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== @@ -7949,6 +6940,11 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -7975,11 +6971,6 @@ simple-update-notifier@^1.0.7: dependencies: semver "~7.0.0" -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== - slash@4.0.0, slash@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" @@ -8039,20 +7030,12 @@ socks@^2.3.3, socks@^2.6.2: ip "^2.0.0" smart-buffer "^4.2.0" -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -8121,12 +7104,10 @@ ssri@^9.0.0: dependencies: minipass "^3.1.1" -stack-utils@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" - integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== - dependencies: - escape-string-regexp "^2.0.0" +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== state-toggle@^1.0.0: version "1.0.3" @@ -8138,6 +7119,11 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +std-env@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.3.tgz#a54f06eb245fdcfef53d56f3c0251f1d5c3d01fe" + integrity sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg== + stop-iteration-iterator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" @@ -8150,14 +7136,6 @@ string-argv@0.3.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== -string-length@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1" - integrity sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -8275,11 +7253,6 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" @@ -8297,16 +7270,18 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== +strip-literal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-1.0.1.tgz#0115a332710c849b4e46497891fb8d585e404bd2" + integrity sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q== + dependencies: + acorn "^8.8.2" + strnum@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" @@ -8341,13 +7316,6 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - supports-hyperlinks@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz#f663df252af5f37c5d49bbd7eeefa9e0b9e59e47" @@ -8453,6 +7421,21 @@ timers-ext@^0.1.7: es5-ext "~0.10.46" next-tick "1" +tinybench@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.5.0.tgz#4711c99bbf6f3e986f67eb722fed9cddb3a68ba5" + integrity sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA== + +tinypool@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.6.0.tgz#c3640b851940abe2168497bb6e43b49fafb3ba7b" + integrity sha512-FdswUUo5SxRizcBc6b1GSuLpLjisa8N8qMyYoP3rl+bym+QauhtJP5bvZY1ytt8krKGmMLYIRl36HBZfeAoqhQ== + +tinyspy@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.1.1.tgz#9e6371b00c259e5c5b301917ca18c01d40ae558c" + integrity sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -8467,16 +7450,6 @@ tmp@^0.2.1: dependencies: rimraf "^3.0.0" -tmpl@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" - integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -8559,20 +7532,6 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== -ts-jest@^29.0.3: - version "29.0.3" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.0.3.tgz#63ea93c5401ab73595440733cefdba31fcf9cb77" - integrity sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ== - dependencies: - bs-logger "0.x" - fast-json-stable-stringify "2.x" - jest-util "^29.0.0" - json5 "^2.2.1" - lodash.memoize "4.x" - make-error "1.x" - semver "7.x" - yargs-parser "^21.0.1" - tslib@^1.11.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -8595,7 +7554,7 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@4.0.8: +type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -8676,6 +7635,11 @@ typescript@^4.9.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db" integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA== +ufo@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.2.tgz#d0d9e0fa09dece0c31ffd57bd363f030a35cfe76" + integrity sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ== + uglify-js@^3.1.4: version "3.17.4" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" @@ -8854,6 +7818,11 @@ universalify@^0.2.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -8869,14 +7838,6 @@ upath@^2.0.1: resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== -update-browserslist-db@^1.0.9: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - update-notifier@6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-6.0.2.tgz#a6990253dfe6d5a02bd04fbb6a61543f55026b60" @@ -8927,10 +7888,10 @@ uuid@^8.1.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-to-istanbul@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" - integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== +v8-to-istanbul@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== dependencies: "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" @@ -8974,6 +7935,59 @@ vfile@^4.0.0, vfile@^4.2.1: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" +vite-node@0.33.0: + version "0.33.0" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.33.0.tgz#c6a3a527e0b8090da7436241bc875760ae0eef28" + integrity sha512-19FpHYbwWWxDr73ruNahC+vtEdza52kA90Qb3La98yZ0xULqV8A5JLNPUff0f5zID4984tW7l3DH2przTJUZSw== + dependencies: + cac "^6.7.14" + debug "^4.3.4" + mlly "^1.4.0" + pathe "^1.1.1" + picocolors "^1.0.0" + vite "^3.0.0 || ^4.0.0" + +"vite@^3.0.0 || ^4.0.0": + version "4.4.2" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.2.tgz#acd47de771c498aec80e4900f30133d9529b278a" + integrity sha512-zUcsJN+UvdSyHhYa277UHhiJ3iq4hUBwHavOpsNUGsTgjBeoBlK8eDt+iT09pBq0h9/knhG/SPrZiM7cGmg7NA== + dependencies: + esbuild "^0.18.10" + postcss "^8.4.24" + rollup "^3.25.2" + optionalDependencies: + fsevents "~2.3.2" + +vitest@^0.33.0: + version "0.33.0" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.33.0.tgz#e2be6153aec1d30e3460ac6d64265bf72da2551c" + integrity sha512-1CxaugJ50xskkQ0e969R/hW47za4YXDUfWJDxip1hwbnhUjYolpfUn2AMOulqG/Dtd9WYAtkHmM/m3yKVrEejQ== + dependencies: + "@types/chai" "^4.3.5" + "@types/chai-subset" "^1.3.3" + "@types/node" "*" + "@vitest/expect" "0.33.0" + "@vitest/runner" "0.33.0" + "@vitest/snapshot" "0.33.0" + "@vitest/spy" "0.33.0" + "@vitest/utils" "0.33.0" + acorn "^8.9.0" + acorn-walk "^8.2.0" + cac "^6.7.14" + chai "^4.3.7" + debug "^4.3.4" + local-pkg "^0.4.3" + magic-string "^0.30.1" + pathe "^1.1.1" + picocolors "^1.0.0" + std-env "^3.3.3" + strip-literal "^1.0.1" + tinybench "^2.5.0" + tinypool "^0.6.0" + vite "^3.0.0 || ^4.0.0" + vite-node "0.33.0" + why-is-node-running "^2.2.2" + vm2@^3.9.8: version "3.9.18" resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.18.tgz#d919848bee191a0410c5cc1c5aac58adfd03ce9a" @@ -8994,13 +8008,6 @@ walk-up-path@^1.0.0: resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== -walker@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" - integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== - dependencies: - makeerror "1.0.12" - wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -9110,6 +8117,14 @@ which@^3.0.0: dependencies: isexe "^2.0.0" +why-is-node-running@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e" + integrity sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + wide-align@^1.1.2, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" @@ -9188,14 +8203,6 @@ write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" - integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^3.0.7" - write-file-atomic@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.0.tgz#54303f117e109bf3d540261125c8ea5a7320fab0" @@ -9259,7 +8266,7 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@21.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.1: +yargs-parser@21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== @@ -9307,24 +8314,16 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.3.1: - version "17.6.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" - integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +yocto-queue@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" + integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + zip-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" From e61e45e5524f62a78f459e2aa56ae7f35952cc42 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Thu, 13 Jul 2023 23:17:39 +0900 Subject: [PATCH 22/42] test: Remove obsoleted tests --- tests/cli.test.ts | 73 +---------------------------------------------- 1 file changed, 1 insertion(+), 72 deletions(-) diff --git a/tests/cli.test.ts b/tests/cli.test.ts index e8a9d527..0d53a399 100644 --- a/tests/cli.test.ts +++ b/tests/cli.test.ts @@ -1,14 +1,6 @@ import execa from 'execa'; import fileType from 'file-type'; import fs from 'node:fs'; -import { - PDFCatalog, - PDFDict, - PDFDocument, - PDFHexString, - PDFName, - PDFNumber, -} from 'pdf-lib'; import path from 'upath'; import { expect, it } from 'vitest'; import packageJSON from '../package.json'; @@ -40,7 +32,7 @@ it('show version', async () => { expect(stdout).toContain(packageJSON.version); }); -it.only('generate pdf without errors', async () => { +it('generate pdf without errors', async () => { const outputPath = path.join(localTmpDir, 'test.pdf'); cleanUp(outputPath); @@ -62,66 +54,3 @@ it.only('generate pdf without errors', async () => { const type = await fileType.fromFile(outputPath); expect(type!.mime).toEqual('application/pdf'); }, 120000); - -it('generate press-ready pdf without errors', async () => { - const outputPath = path.join(localTmpDir, 'test-press-ready.pdf'); - cleanUp(outputPath); - - try { - const response = await vivliostyleCLI([ - 'build', - '-s', - 'A4', - '-o', - outputPath, - '--press-ready', - fixtureFile, - ]); - } catch (err: any) { - throw err.stderr; - } - - // mimetype test - const type = await fileType.fromFile(outputPath); - expect(type!.mime).toEqual('application/pdf'); -}, 120000); - -it('generates a PDF with metadata', async () => { - const outputPath = path.join(localTmpDir, 'test-metadata.pdf'); - - try { - const response = await vivliostyleCLI([ - 'build', - '-s', - 'Letter', - '--title', - 'Wood Engraving', - '-o', - outputPath, - fixtureFile, - ]); - expect(response.stdout).toContain('has been created'); - } catch (err: any) { - throw err.stderr; - } - - const bytes = fs.readFileSync(outputPath); - const document = await PDFDocument.load(bytes); - - expect(document.getTitle()).toBe('Wood Engraving'); - - const catalog = document.context.lookup( - document.context.trailerInfo.Root, - PDFCatalog, - ); - - // Outlines - const outlines = catalog.lookup(PDFName.of('Outlines'), PDFDict); - - const count = outlines.lookup(PDFName.of('Count'), PDFNumber); - expect(count.value()).toBe(1); - - const intro = outlines.lookup(PDFName.of('First'), PDFDict); - const introTitle = intro.lookup(PDFName.of('Title'), PDFHexString); - expect(introTitle.sizeInBytes()).toBe(62); -}, 120000); From e5f8d1e3b5ba7ace041f9eb158079e929bf85eeb Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Thu, 13 Jul 2023 23:38:31 +0900 Subject: [PATCH 23/42] chore: Fix importing module --- src/processor/compile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/processor/compile.ts b/src/processor/compile.ts index 8a764c35..63dec7ef 100644 --- a/src/processor/compile.ts +++ b/src/processor/compile.ts @@ -2,7 +2,7 @@ import chalk from 'chalk'; import { imageSize } from 'image-size'; import { lookup as mime } from 'mime-types'; import fs from 'node:fs'; -import path from 'node:path'; +import path from 'upath'; import { TOC_TITLE } from '../const.js'; import { ManuscriptEntry, From 1246e04b70f43f50deedb58aa2f6f64bc098c251 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Fri, 14 Jul 2023 00:01:28 +0900 Subject: [PATCH 24/42] chore: Bundle upath library --- package.json | 2 +- src/build.ts | 10 ++-- src/const.ts | 8 ++-- src/container.ts | 14 +++--- src/init.ts | 5 +- src/input/config.ts | 88 ++++++++++++++++++----------------- src/input/input-types.ts | 4 +- src/output/epub.ts | 41 ++++++++-------- src/output/output-types.ts | 4 +- src/output/pdf-postprocess.ts | 8 ++-- src/output/pdf.ts | 10 ++-- src/output/webbook.ts | 40 ++++++++-------- src/preview.ts | 2 +- src/processor/compile.ts | 42 ++++++++--------- src/processor/html.ts | 8 ++-- src/processor/theme.ts | 15 +++--- src/server.ts | 2 +- src/util.ts | 4 +- tests/api.test.ts | 10 ++-- tests/cli.test.ts | 12 ++--- tests/commandUtil.ts | 12 ++--- tests/init.test.ts | 29 ++++++------ tests/server.test.ts | 10 ++-- vendors/index.d.ts | 3 +- vendors/index.src.js | 3 +- 25 files changed, 195 insertions(+), 191 deletions(-) diff --git a/package.json b/package.json index 87bde094..2ca31036 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,6 @@ "serve-handler": "^6.1.3", "slash": "4.0.0", "terminal-link": "^2.1.1", - "upath": "^2.0.1", "uuid": "^8.3.2", "vfile": "^4.2.1", "w3c-xmlserializer": "^4.0.0", @@ -100,6 +99,7 @@ "shx": "^0.3.3", "tmp": "^0.2.1", "typescript": "^4.9.3", + "upath": "^2.0.1", "vitest": "^0.33.0" }, "main": "dist/index.js", diff --git a/src/build.ts b/src/build.ts index f84566a2..64275e39 100644 --- a/src/build.ts +++ b/src/build.ts @@ -1,6 +1,5 @@ import chalk from 'chalk'; import terminalLink from 'terminal-link'; -import path from 'upath'; import { getExecutableBrowserPath } from './browser.js'; import { CliFlags, @@ -32,6 +31,7 @@ import { log, startLogging, stopLogging, + upath, useTmpDirectory, } from './util.js'; @@ -50,7 +50,9 @@ export async function getFullConfig( const { vivliostyleConfig, vivliostyleConfigPath } = loadedConf; const loadedCliFlags = loadedConf.cliFlags; - const context = vivliostyleConfig ? path.dirname(vivliostyleConfigPath) : cwd; + const context = vivliostyleConfig + ? upath.dirname(vivliostyleConfigPath) + : cwd; const configEntries: MergedConfig[] = []; for (const entry of vivliostyleConfig ?? [vivliostyleConfig]) { @@ -143,7 +145,7 @@ export async function build(cliFlags: BuildCliFlags) { if (config.input.format === 'markdown') { const entry = [manifest.readingOrder].flat()[0]; if (entry) { - entryHtmlFile = path.join( + entryHtmlFile = upath.join( outputDir, typeof entry === 'string' ? entry : entry.url, ); @@ -178,7 +180,7 @@ export async function build(cliFlags: BuildCliFlags) { output = target.path; } if (output) { - const formattedOutput = chalk.bold.green(path.relative(cwd, output)); + const formattedOutput = chalk.bold.green(upath.relative(cwd, output)); log( `\n${terminalLink(formattedOutput, 'file://' + output, { fallback: () => formattedOutput, diff --git a/src/const.ts b/src/const.ts index 86db1ad2..822bba1e 100644 --- a/src/const.ts +++ b/src/const.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import { fileURLToPath } from 'node:url'; import resolvePkg from 'resolve-pkg'; -import path from 'upath'; +import { upath } from '../vendors/index.js'; export const MANIFEST_FILENAME = 'publication.json'; export const TOC_FILENAME = 'index.html'; @@ -18,13 +18,13 @@ export const EPUB_CONTAINER_XML = `${XML_DECLARATION} </rootfiles> </container>`; -export const cliRoot = path.join(fileURLToPath(import.meta.url), '../..'); +export const cliRoot = upath.join(fileURLToPath(import.meta.url), '../..'); export const cliVersion = (() => { if (import.meta.env?.VITEST) { return '0.0.1'; } const pkg = JSON.parse( - fs.readFileSync(path.join(cliRoot, 'package.json'), 'utf8'), + fs.readFileSync(upath.join(cliRoot, 'package.json'), 'utf8'), ); return pkg.version; })(); @@ -35,7 +35,7 @@ export const coreVersion = (() => { return '0.0.1'; } const pkg = JSON.parse( - fs.readFileSync(path.join(viewerRoot, 'package.json'), 'utf8'), + fs.readFileSync(upath.join(viewerRoot, 'package.json'), 'utf8'), ); return pkg.version; })(); diff --git a/src/container.ts b/src/container.ts index 47a238db..2f47ba11 100644 --- a/src/container.ts +++ b/src/container.ts @@ -3,7 +3,6 @@ import commandExists from 'command-exists'; import execa from 'execa'; import isInteractive from 'is-interactive'; import process from 'node:process'; -import path from 'upath'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { cliVersion } from './const.js'; import { @@ -13,6 +12,7 @@ import { pathEquals, startLogging, stopLogging, + upath, } from './util.js'; export const CONTAINER_IMAGE = `ghcr.io/vivliostyle/cli:${cliVersion}`; @@ -22,18 +22,18 @@ export function toContainerPath(urlOrAbsPath: string): string { if (isUrlString(urlOrAbsPath)) { if (urlOrAbsPath.toLowerCase().startsWith('file')) { return pathToFileURL( - path.posix.join( + upath.posix.join( CONTAINER_ROOT_DIR, - path.toUnix(fileURLToPath(urlOrAbsPath)).replace(/^\w:/, ''), + upath.toUnix(fileURLToPath(urlOrAbsPath)).replace(/^\w:/, ''), ), ).href; } else { return urlOrAbsPath; } } - return path.posix.join( + return upath.posix.join( CONTAINER_ROOT_DIR, - path.toUnix(urlOrAbsPath).replace(/^\w:/, ''), + upath.toUnix(urlOrAbsPath).replace(/^\w:/, ''), ); } @@ -45,8 +45,8 @@ export function collectVolumeArgs(mountPoints: string[]): string[] { return false; } let parent = p; - while (!pathEquals(parent, path.dirname(parent))) { - parent = path.dirname(parent); + while (!pathEquals(parent, upath.dirname(parent))) { + parent = upath.dirname(parent); if (array.includes(parent)) { // other mount point contains its directory return false; diff --git a/src/init.ts b/src/init.ts index 543140d1..802b3e7d 100644 --- a/src/init.ts +++ b/src/init.ts @@ -1,8 +1,7 @@ import chalk from 'chalk'; import fs from 'node:fs'; -import path from 'upath'; import { CONTAINER_IMAGE } from './container.js'; -import { cwd, log } from './util.js'; +import { cwd, log, upath } from './util.js'; export interface InitCliFlags { title?: string; @@ -13,7 +12,7 @@ export interface InitCliFlags { } export async function init(cliFlags: InitCliFlags) { - const vivliostyleConfigPath = path.join(cwd, 'vivliostyle.config.js'); + const vivliostyleConfigPath = upath.join(cwd, 'vivliostyle.config.js'); if (fs.existsSync(vivliostyleConfigPath)) { return log( diff --git a/src/input/config.ts b/src/input/config.ts index 147d29c9..e1d116b1 100644 --- a/src/input/config.ts +++ b/src/input/config.ts @@ -1,7 +1,6 @@ import chalk from 'chalk'; import cheerio from 'cheerio'; import fs from 'fs'; -import path from 'upath'; import { pathToFileURL } from 'url'; import { getExecutableBrowserPath } from '../browser.js'; import { @@ -46,6 +45,7 @@ import { readJSON, statFileSync, touchTmpFile, + upath, } from '../util.js'; export type ParsedTheme = UriTheme | FileTheme | PackageTheme; @@ -204,7 +204,7 @@ export function contextResolve( context: string, loc: string | undefined, ): string | undefined { - return loc && path.resolve(context, loc); + return loc && upath.resolve(context, loc); } function normalizeEntry(e: string | EntryObject): EntryObject { @@ -233,20 +233,20 @@ export function parseTheme({ if (isUrlString(specifier)) { return { type: 'uri', - name: path.basename(specifier), + name: upath.basename(specifier), location: specifier, }; } // bare .css file - const stylePath = path.resolve(context, specifier); + const stylePath = upath.resolve(context, specifier); if (fs.existsSync(stylePath) && stylePath.endsWith('.css')) { - const sourceRelPath = path.relative(context, stylePath); + const sourceRelPath = upath.relative(context, stylePath); return { type: 'file', - name: path.basename(specifier), + name: upath.basename(specifier), source: stylePath, - location: path.resolve(workspaceDir, sourceRelPath), + location: upath.resolve(workspaceDir, sourceRelPath), }; } @@ -264,7 +264,7 @@ export function parseTheme({ let name = parsed.name; let resolvedSpecifier = specifier; if (parsed.type === 'directory' && parsed.fetchSpec) { - const pkgJsonPath = path.join(parsed.fetchSpec, 'package.json'); + const pkgJsonPath = upath.join(parsed.fetchSpec, 'package.json'); if (fs.existsSync(pkgJsonPath)) { const packageJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')); name = packageJson.name; @@ -278,7 +278,7 @@ export function parseTheme({ type: 'package', name, specifier: resolvedSpecifier, - location: path.join(themesDir, 'packages', name), + location: upath.join(themesDir, 'packages', name), importPath, }; } @@ -310,7 +310,7 @@ function parseFileMetadata({ workspaceDir: string; themesDir?: string; }): { title?: string; themes?: ParsedTheme[] } { - const sourceDir = path.dirname(sourcePath); + const sourceDir = upath.dirname(sourcePath); let title: string | undefined; let themes: ParsedTheme[] | undefined; if (type === 'text/markdown') { @@ -359,7 +359,7 @@ export async function collectVivliostyleConfig<T extends CliFlags>( let config: VivliostyleConfigSchema; let jsonRaw: string | undefined; try { - if (path.extname(configPath) === '.json') { + if (upath.extname(configPath) === '.json') { jsonRaw = fs.readFileSync(configPath, 'utf8'); config = JSON.parse(jsonRaw); } else { @@ -401,10 +401,10 @@ export async function collectVivliostyleConfig<T extends CliFlags>( } = {}; let vivliostyleConfigPath: string | undefined; if (cliFlags.configPath) { - vivliostyleConfigPath = path.resolve(cwd, cliFlags.configPath); + vivliostyleConfigPath = upath.resolve(cwd, cliFlags.configPath); } else { vivliostyleConfigPath = ['.js', '.mjs', '.cjs'] - .map((ext) => path.join(cwd, `vivliostyle.config${ext}`)) + .map((ext) => upath.join(cwd, `vivliostyle.config${ext}`)) .find((p) => fs.existsSync(p)); } // let vivliostyleConfig: VivliostyleConfigSchema | undefined; @@ -415,11 +415,11 @@ export async function collectVivliostyleConfig<T extends CliFlags>( }; } else if ( cliFlags.input && - path.basename(cliFlags.input).startsWith('vivliostyle.config') + upath.basename(cliFlags.input).startsWith('vivliostyle.config') ) { // Load an input argument as a Vivliostyle config try { - const inputPath = path.resolve(cwd, cliFlags.input); + const inputPath = upath.resolve(cwd, cliFlags.input); const inputConfig = await load(inputPath); cliFlags = { ...cliFlags, @@ -461,15 +461,15 @@ export async function mergeConfig<T extends CliFlags>( if (cliFlags.input && !config && isUrlString(cliFlags.input)) { workspaceDir = entryContextDir = cwd; } else { - entryContextDir = path.resolve( + entryContextDir = upath.resolve( cliFlags.input && !config - ? path.dirname(path.resolve(context, cliFlags.input)) + ? upath.dirname(upath.resolve(context, cliFlags.input)) : contextResolve(context, config?.entryContext) ?? context, ); workspaceDir = contextResolve(context, config?.workspaceDir) ?? entryContextDir; } - const themesDir = path.join(workspaceDir, 'themes'); + const themesDir = upath.join(workspaceDir, 'themes'); const includeAssets = config?.includeAssets ? Array.isArray(config.includeAssets) @@ -547,7 +547,7 @@ export async function mergeConfig<T extends CliFlags>( return cliFlags.targets.map(({ path: outputPath, format }) => { if (format === 'pdf') { return { - path: path.resolve(outputPath), + path: upath.resolve(outputPath), format, renderMode, preflight, @@ -555,13 +555,13 @@ export async function mergeConfig<T extends CliFlags>( }; } else if (format === 'epub') { return { - path: path.resolve(outputPath), + path: upath.resolve(outputPath), format, version: EPUB_OUTPUT_VERSION, }; } else { return { - path: path.resolve(outputPath), + path: upath.resolve(outputPath), format, }; } @@ -573,7 +573,7 @@ export async function mergeConfig<T extends CliFlags>( ).map((target) => { const targetObj = typeof target === 'string' ? { path: target } : target; - const outputPath = path.resolve(context, targetObj.path); + const outputPath = upath.resolve(context, targetObj.path); const format = targetObj.format ?? detectOutputFormat(outputPath); if (!checkOutputFormat(format)) { throw new Error(`Unknown format: ${format}`); @@ -607,7 +607,7 @@ export async function mergeConfig<T extends CliFlags>( const filename = config?.title ? `${config.title}.pdf` : 'output.pdf'; return [ { - path: path.resolve(context, filename), + path: upath.resolve(context, filename), format: 'pdf', renderMode, preflight, @@ -693,7 +693,7 @@ async function composeSingleInputConfig<T extends CliFlags>( sourcePath = cliFlags.input; input = { format: 'webbook', entry: sourcePath }; } else { - sourcePath = path.resolve(cliFlags.input); + sourcePath = upath.resolve(cliFlags.input); input = detectInputFormat(sourcePath); // Check file exists statFileSync(sourcePath); @@ -703,12 +703,16 @@ async function composeSingleInputConfig<T extends CliFlags>( // Single input file; create temporary file const type = detectManuscriptMediaType(sourcePath); const metadata = parseFileMetadata({ type, sourcePath, workspaceDir }); - const relDir = path.relative( + const relDir = upath.relative( otherConfig.entryContextDir, - path.dirname(sourcePath), + upath.dirname(sourcePath), ); - const target = path - .resolve(workspaceDir, relDir, `${tmpPrefix}${path.basename(sourcePath)}`) + const target = upath + .resolve( + workspaceDir, + relDir, + `${tmpPrefix}${upath.basename(sourcePath)}`, + ) .replace(/\.md$/, '.html'); await touchTmpFile(target); const themes = metadata.themes ?? [...otherConfig.rootThemes]; @@ -722,9 +726,9 @@ async function composeSingleInputConfig<T extends CliFlags>( }); exportAliases.push({ source: target, - target: path.resolve( - path.dirname(target), - path.basename(sourcePath).replace(/\.md$/, '.html'), + target: upath.resolve( + upath.dirname(target), + upath.basename(sourcePath).replace(/\.md$/, '.html'), ), }); } @@ -733,19 +737,19 @@ async function composeSingleInputConfig<T extends CliFlags>( const manifestDeclaration = await (async (): Promise<ManifestConfig> => { if (input.format === 'markdown') { // create temporary manifest file - const manifestPath = path.resolve( + const manifestPath = upath.resolve( workspaceDir, `${tmpPrefix}${MANIFEST_FILENAME}`, ); await touchTmpFile(manifestPath); exportAliases.push({ source: manifestPath, - target: path.resolve(workspaceDir, MANIFEST_FILENAME), + target: upath.resolve(workspaceDir, MANIFEST_FILENAME), }); fallbackTitle = entries.length === 1 && entries[0].title ? (entries[0].title as string) - : path.basename(sourcePath); + : upath.basename(sourcePath); return { manifestPath, needToGenerateManifest: true }; } else if (input.format === 'html' || input.format === 'webbook') { return { webbookEntryPath: input.entry }; @@ -788,7 +792,7 @@ async function composeProjectConfig<T extends CliFlags>( rootThemes, outputs, } = otherConfig; - const pkgJsonPath = path.resolve(entryContextDir, 'package.json'); + const pkgJsonPath = upath.resolve(entryContextDir, 'package.json'); const pkgJson = fs.existsSync(pkgJsonPath) ? readJSON(pkgJsonPath) : undefined; @@ -796,7 +800,7 @@ async function composeProjectConfig<T extends CliFlags>( debug('located package.json path', pkgJsonPath); } - const autoGeneratedTocPath = path.resolve( + const autoGeneratedTocPath = upath.resolve( workspaceDir, typeof config?.toc === 'string' ? config.toc : TOC_FILENAME, ); @@ -826,9 +830,9 @@ async function composeProjectConfig<T extends CliFlags>( themes, } as ContentsEntry; } - const sourcePath = path.resolve(entryContextDir, entry.path); // abs - const contextEntryPath = path.relative(entryContextDir, sourcePath); // rel - const targetPath = path + const sourcePath = upath.resolve(entryContextDir, entry.path); // abs + const contextEntryPath = upath.relative(entryContextDir, sourcePath); // rel + const targetPath = upath .resolve(workspaceDir, contextEntryPath) .replace(/\.md$/, '.html'); if (!isUrlString(sourcePath)) { @@ -879,7 +883,7 @@ async function composeProjectConfig<T extends CliFlags>( if (entries.length === 1 && entries[0].title) { fallbackProjectTitle = entries[0].title; } else { - fallbackProjectTitle = path.basename(outputs[0].path); + fallbackProjectTitle = upath.basename(outputs[0].path); log( `\n${chalk.yellow( 'Could not find any appropriate publication title. We set ', @@ -903,10 +907,10 @@ async function composeProjectConfig<T extends CliFlags>( entries, input: { format: 'pub-manifest', - entry: path.join(workspaceDir, MANIFEST_FILENAME), + entry: upath.join(workspaceDir, MANIFEST_FILENAME), }, exportAliases: [], - manifestPath: path.join(workspaceDir, MANIFEST_FILENAME), + manifestPath: upath.join(workspaceDir, MANIFEST_FILENAME), title: projectTitle || fallbackProjectTitle, author: projectAuthor, needToGenerateManifest: true, diff --git a/src/input/input-types.ts b/src/input/input-types.ts index 8abaddc2..998d83c5 100644 --- a/src/input/input-types.ts +++ b/src/input/input-types.ts @@ -1,5 +1,5 @@ import { lookup as mime } from 'mime-types'; -import path from 'upath'; +import { upath } from '../util.js'; interface InputFormatTrait<T extends string = string> { format: T; @@ -44,7 +44,7 @@ export type InputFormat = | EpubOpfInput; export function detectInputFormat(entry: string): InputFormat { - const lowerCasedExt = path.extname(entry).toLowerCase(); + const lowerCasedExt = upath.extname(entry).toLowerCase(); if (lowerCasedExt === '.md' || lowerCasedExt === '.markdown') { return { format: 'markdown', entry }; } else if (lowerCasedExt === '.json' || lowerCasedExt === '.jsonld') { diff --git a/src/output/epub.ts b/src/output/epub.ts index ad1477fe..68839ca5 100644 --- a/src/output/epub.ts +++ b/src/output/epub.ts @@ -7,7 +7,6 @@ import type { JSDOM } from 'jsdom'; import { lookup as mime } from 'mime-types'; import fs from 'node:fs'; import url from 'node:url'; -import path from 'upath'; import { v4 as uuid } from 'uuid'; import serializeToXml from 'w3c-xmlserializer'; import { EPUB_CONTAINER_XML, EPUB_NS, XML_DECLARATION } from '../const.js'; @@ -27,7 +26,7 @@ import type { PublicationManifest, ResourceCategorization, } from '../schema/publication.schema.js'; -import { DetailError, copy, debug, logWarn, useTmpDirectory } from '../util.js'; +import { DetailError, copy, debug, logWarn, upath, useTmpDirectory } from '../util.js'; interface ManifestEntry { href: string; @@ -41,13 +40,13 @@ interface LandmarkEntry { } const changeExtname = (filepath: string, newExt: string) => { - let ext = path.extname(filepath); + let ext = upath.extname(filepath); return `${filepath.slice(0, -ext.length)}${newExt}`; }; const getRelativeHref = (target: string, baseUrl: string, rootUrl: string) => { - const absBasePath = path.join('/', baseUrl); - const absRootPath = path.join('/', rootUrl); + const absBasePath = upath.join('/', baseUrl); + const absRootPath = upath.join('/', rootUrl); const hrefUrl = new URL(target, url.pathToFileURL(absBasePath)); if (hrefUrl.protocol !== 'file:') { return target; @@ -55,8 +54,8 @@ const getRelativeHref = (target: string, baseUrl: string, rootUrl: string) => { if (/\.html?$/.test(hrefUrl.pathname)) { hrefUrl.pathname = changeExtname(hrefUrl.pathname, '.xhtml'); } - const pathname = path.posix.relative( - url.pathToFileURL(path.dirname(absRootPath)).pathname, + const pathname = upath.posix.relative( + url.pathToFileURL(upath.dirname(absRootPath)).pathname, hrefUrl.pathname, ); return `${pathname}${hrefUrl.search}${hrefUrl.hash}`; @@ -111,13 +110,13 @@ export async function exportEpub({ debug('Export EPUB'); const [tmpDir] = await useTmpDirectory(); - fs.mkdirSync(path.join(tmpDir, 'META-INF'), { recursive: true }); - await copy(webpubDir, path.join(tmpDir, 'EPUB')); + fs.mkdirSync(upath.join(tmpDir, 'META-INF'), { recursive: true }); + await copy(webpubDir, upath.join(tmpDir, 'EPUB')); const uid = `urn:uuid:${uuid()}`; const entryHtmlRelPath = entryHtmlFile && - path.relative(webpubDir, path.resolve(webpubDir, entryHtmlFile)); + upath.relative(webpubDir, upath.resolve(webpubDir, entryHtmlFile)); const findPublicationLink = ( relType: string, @@ -174,7 +173,7 @@ export async function exportEpub({ } catch (e) { /* NOOP */ } - if (!fs.existsSync(path.join(tmpDir, 'EPUB', url))) { + if (!fs.existsSync(upath.join(tmpDir, 'EPUB', url))) { return acc; } const mediaType = encodingFormat || mime(url) || 'text/plain'; @@ -216,7 +215,7 @@ export async function exportEpub({ try { parseResult = await transpileHtmlToXhtml({ target, - contextDir: path.join(tmpDir, 'EPUB'), + contextDir: upath.join(tmpDir, 'EPUB'), landmarks, isTocHtml, isPagelistHtml: target === (pageListResource?.url || entryHtmlRelPath), @@ -269,7 +268,7 @@ export async function exportEpub({ tocParseTree = await supplyTocNavElement({ tocHtml, tocDom: tocProcessResult.dom, - contextDir: path.join(tmpDir, 'EPUB'), + contextDir: upath.join(tmpDir, 'EPUB'), readingOrder, docLanguages, }); @@ -277,7 +276,7 @@ export async function exportEpub({ // EPUB/toc.ncx fs.writeFileSync( - path.join(tmpDir, 'EPUB/toc.ncx'), + upath.join(tmpDir, 'EPUB/toc.ncx'), buildNcx({ toc: tocParseTree, docTitle, @@ -293,7 +292,7 @@ export async function exportEpub({ // META-INF/container.xml fs.writeFileSync( - path.join(tmpDir, 'META-INF/container.xml'), + upath.join(tmpDir, 'META-INF/container.xml'), EPUB_CONTAINER_XML, 'utf8', ); @@ -301,7 +300,7 @@ export async function exportEpub({ // EPUB/content.opf debug(`Generating content.opf`); fs.writeFileSync( - path.join(tmpDir, 'EPUB/content.opf'), + upath.join(tmpDir, 'EPUB/content.opf'), buildEpubPackageDocument({ epubVersion, uid, @@ -340,7 +339,7 @@ async function transpileHtmlToXhtml({ hasScriptedContent: boolean; hasSvgContent: boolean; }> { - const absPath = path.join(contextDir, target); + const absPath = upath.join(contextDir, target); const { dom } = await getJsdomFromUrlOrFile(absPath); const { document } = dom.window; // `xmlns` will be supplied in later serialization process @@ -441,7 +440,7 @@ export async function supplyTocNavElement({ docLanguages: string[]; }): Promise<TocResourceTreeRoot> { debug(`Generating toc nav element: ${tocHtml}`); - const absPath = path.join(contextDir, tocHtml); + const absPath = upath.join(contextDir, tocHtml); const { document } = tocDom.window; const nav = document.createElement('nav'); @@ -459,7 +458,7 @@ export async function supplyTocNavElement({ let name = normalizeLocalizableString(content.name, docLanguages); if (!name) { const { dom } = await getJsdomFromUrlOrFile( - path.join(contextDir, changeExtname(content.url, '.xhtml')), + upath.join(contextDir, changeExtname(content.url, '.xhtml')), ); name = dom.window.document.title; } @@ -721,8 +720,8 @@ async function compressEpub({ archive.pipe(output); archive.append('application/epub+zip', { name: 'mimetype' }); - archive.directory(path.join(sourceDir, 'META-INF'), 'META-INF'); - archive.directory(path.join(sourceDir, 'EPUB'), 'EPUB'); + archive.directory(upath.join(sourceDir, 'META-INF'), 'META-INF'); + archive.directory(upath.join(sourceDir, 'EPUB'), 'EPUB'); archive.finalize(); }); } diff --git a/src/output/output-types.ts b/src/output/output-types.ts index 3dd56c8b..8e08134b 100644 --- a/src/output/output-types.ts +++ b/src/output/output-types.ts @@ -1,4 +1,4 @@ -import path from 'upath'; +import { upath } from '../util.js'; interface OutputFormatTrait<T extends string = string> { format: T; @@ -37,7 +37,7 @@ export const checkPreflightMode = (v: unknown): v is PdfOutput['preflight'] => { }; export function detectOutputFormat(outputPath: string): OutputFormat['format'] { - const lowerCasedExt = path.extname(outputPath); + const lowerCasedExt = upath.extname(outputPath); if (lowerCasedExt === '.pdf') { return 'pdf'; } else if (lowerCasedExt === '.epub') { diff --git a/src/output/pdf-postprocess.ts b/src/output/pdf-postprocess.ts index 7f39e010..1de52098 100644 --- a/src/output/pdf-postprocess.ts +++ b/src/output/pdf-postprocess.ts @@ -11,7 +11,6 @@ import { ReadingDirection, } from 'pdf-lib'; import * as pressReadyModule from 'press-ready'; -import path from 'upath'; import { v1 as uuid } from 'uuid'; import { coreVersion } from '../const.js'; import { @@ -25,6 +24,7 @@ import { checkContainerEnvironment, startLogging, stopLogging, + upath, } from '../util.js'; import type { PdfOutput } from './output-types.js'; @@ -76,8 +76,8 @@ export async function pressReadyWithContainer({ image, entrypoint: 'press-ready', userVolumeArgs: collectVolumeArgs([ - path.dirname(input), - path.dirname(output), + upath.dirname(input), + upath.dirname(output), ]), commandArgs: [ 'build', @@ -106,7 +106,7 @@ export class PostProcess { ) { const isInContainer = checkContainerEnvironment(); const input = preflight - ? path.join(os.tmpdir(), `vivliostyle-cli-${uuid()}.pdf`) + ? upath.join(os.tmpdir(), `vivliostyle-cli-${uuid()}.pdf`) : output; const pdf = await this.document.save(); diff --git a/src/output/pdf.ts b/src/output/pdf.ts index 2dd996e0..0e962820 100644 --- a/src/output/pdf.ts +++ b/src/output/pdf.ts @@ -3,7 +3,6 @@ import fs from 'node:fs'; import { URL } from 'node:url'; import { Page } from 'playwright-core'; import terminalLink from 'terminal-link'; -import path from 'upath'; import { checkBrowserAvailability, downloadBrowser, @@ -28,6 +27,7 @@ import { logUpdate, pathEquals, startLogging, + upath, } from '../util.js'; import type { PdfOutput } from './output-types.js'; import { PageSizeData, PostProcess } from './pdf-postprocess.js'; @@ -59,7 +59,7 @@ export async function buildPDFWithContainer( image: option.image, userVolumeArgs: collectVolumeArgs([ option.workspaceDir, - path.dirname(option.target.path), + upath.dirname(option.target.path), ]), commandArgs: [ 'build', @@ -183,7 +183,7 @@ export async function buildPDF({ function stringifyEntry(entry: ManuscriptEntry) { const formattedSourcePath = chalk.bold.cyan( - path.relative(entryContextDir, entry.source), + upath.relative(entryContextDir, entry.source), ); return `${terminalLink(formattedSourcePath, 'file://' + entry.source, { fallback: () => formattedSourcePath, @@ -199,7 +199,7 @@ export async function buildPDF({ return url.protocol === 'file:' ? pathEquals(entry.target, url.pathname) : pathEquals( - path.relative(workspaceDir, entry.target), + upath.relative(workspaceDir, entry.target), url.pathname.substring(1), ); }); @@ -294,7 +294,7 @@ export async function buildPDF({ await browser.close(); logUpdate('Processing PDF'); - fs.mkdirSync(path.dirname(target.path), { recursive: true }); + fs.mkdirSync(upath.dirname(target.path), { recursive: true }); const post = await PostProcess.load(pdf); await post.metadata(metadata, { diff --git a/src/output/webbook.ts b/src/output/webbook.ts index 169e51f6..67dbd533 100644 --- a/src/output/webbook.ts +++ b/src/output/webbook.ts @@ -1,5 +1,4 @@ import fs from 'node:fs'; -import path from 'upath'; import MIMEType from 'whatwg-mimetype'; import { MANIFEST_FILENAME } from '../const.js'; import { MergedConfig, WebbookEntryConfig } from '../input/config.js'; @@ -23,6 +22,7 @@ import { pathEquals, remove, safeGlob, + upath, } from '../util.js'; export async function prepareWebPublicationDirectory({ @@ -63,7 +63,7 @@ export async function retrieveWebbookEntry({ ? new URL('/', baseUrl).href : new URL('.', baseUrl).href; const pathContains = (url: string) => - !path.posix.relative(rootUrl, url).startsWith('..'); + !upath.relative(rootUrl, url).startsWith('..'); const retriever = new Map(resourceLoader.fetcherMap); if (manifest) { @@ -101,9 +101,9 @@ export async function retrieveWebbookEntry({ const normalizeToLocalPath = (urlString: string, mimeType?: string) => { const url = new URL(urlString); url.hash = ''; - let relTarget = path.posix.relative(rootUrl, url.href); - if (!relTarget || (mimeType === 'text/html' && !path.extname(relTarget))) { - relTarget = path.join(relTarget, 'index.html'); + let relTarget = upath.relative(rootUrl, url.href); + if (!relTarget || (mimeType === 'text/html' && !upath.extname(relTarget))) { + relTarget = upath.join(relTarget, 'index.html'); } return relTarget; }; @@ -125,9 +125,9 @@ export async function retrieveWebbookEntry({ /* NOOP */ } const relTarget = normalizeToLocalPath(url, encodingFormat); - const target = path.join(outputDir, relTarget); + const target = upath.join(outputDir, relTarget); fetchedResources.push({ url: relTarget, encodingFormat }); - await fs.promises.mkdir(path.dirname(target), { recursive: true }); + await fs.promises.mkdir(upath.dirname(target), { recursive: true }); await fs.promises.writeFile(target, buffer); }) .catch(() => { @@ -159,7 +159,7 @@ export async function retrieveWebbookEntry({ ); return { - entryHtmlFile: path.join( + entryHtmlFile: upath.join( outputDir, normalizeToLocalPath(baseUrl, 'text/html'), ), @@ -189,14 +189,14 @@ export async function supplyWebPublicationManifestForWebbook({ document.querySelector('meta[name="author"]')?.getAttribute('content') || ''; - const entry = path.relative(outputDir, entryHtmlFile); + const entry = upath.relative(outputDir, entryHtmlFile); const allFiles = await safeGlob('**', { cwd: outputDir, gitignore: false, }); const manifest = generateManifest( - path.join(outputDir, MANIFEST_FILENAME), + upath.join(outputDir, MANIFEST_FILENAME), outputDir, { title, @@ -235,8 +235,8 @@ export async function copyWebPublicationAssets({ }): Promise<PublicationManifest> { const relExportAliases = exportAliases .map(({ source, target }) => ({ - source: path.relative(input, source), - target: path.relative(input, target), + source: upath.relative(input, source), + target: upath.relative(input, target), })) .filter(({ source }) => !source.startsWith('..')); const allFiles = await safeGlob('**', { @@ -247,8 +247,8 @@ export async function copyWebPublicationAssets({ !pathContains(input, p) ? [] : format === 'webpub' - ? path.join(path.relative(input, p), '**') - : path.relative(input, p), + ? upath.join(upath.relative(input, p), '**') + : upath.relative(input, p), ), // including node_modules possibly occurs cyclic reference of symlink '**/node_modules', @@ -269,18 +269,18 @@ export async function copyWebPublicationAssets({ }), ); const resources: string[] = []; - let actualManifestPath = path.join( + let actualManifestPath = upath.join( outputDir, - path.relative(input, manifestPath), + 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 = path.join(outputDir, relTarget); - fs.mkdirSync(path.dirname(target), { recursive: true }); - await copy(path.join(input, file), target); - if (alias && pathEquals(path.join(input, alias.source), manifestPath)) { + 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; } } diff --git a/src/preview.ts b/src/preview.ts index 4768e325..8f8baecd 100644 --- a/src/preview.ts +++ b/src/preview.ts @@ -1,5 +1,4 @@ import chokidar from 'chokidar'; -import upath from 'upath'; import { checkBrowserAvailability, downloadBrowser, @@ -28,6 +27,7 @@ import { pathEquals, startLogging, stopLogging, + upath, } from './util.js'; let timer: NodeJS.Timeout; diff --git a/src/processor/compile.ts b/src/processor/compile.ts index 63dec7ef..5ceb58ff 100644 --- a/src/processor/compile.ts +++ b/src/processor/compile.ts @@ -2,7 +2,6 @@ import chalk from 'chalk'; import { imageSize } from 'image-size'; import { lookup as mime } from 'mime-types'; import fs from 'node:fs'; -import path from 'upath'; import { TOC_TITLE } from '../const.js'; import { ManuscriptEntry, @@ -27,6 +26,7 @@ import { remove, safeGlob, startLogging, + upath, useTmpDirectory, } from '../util.js'; import { generateTocHtml, isTocHtml, processManuscriptHtml } from './html.js'; @@ -41,11 +41,11 @@ function locateThemePath(theme: ParsedTheme, from: string): string | string[] { return theme.location; } if (theme.type === 'file') { - return path.relative(from, theme.location); + return upath.relative(from, theme.location); } if (theme.importPath) { return [theme.importPath].flat().map((locator) => { - const resolvedPath = path.resolve(theme.location, locator); + const resolvedPath = upath.resolve(theme.location, locator); if ( !pathContains(theme.location, resolvedPath) || !fs.existsSync(resolvedPath) @@ -54,10 +54,10 @@ function locateThemePath(theme: ParsedTheme, from: string): string | string[] { `Could not find a style path ${theme.importPath} for the theme: ${theme.name}.`, ); } - return path.relative(from, resolvedPath); + return upath.relative(from, resolvedPath); }); } else { - const pkgJsonPath = path.join(theme.location, 'package.json'); + const pkgJsonPath = upath.join(theme.location, 'package.json'); const packageJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')); const maybeStyle = packageJson?.vivliostyle?.theme?.style ?? @@ -69,7 +69,7 @@ function locateThemePath(theme: ParsedTheme, from: string): string | string[] { 'Please ensure this package satisfies a `vivliostyle.theme.style` propertiy.', ); } - return path.relative(from, path.join(theme.location, maybeStyle)); + return upath.relative(from, upath.join(theme.location, maybeStyle)); } } @@ -93,7 +93,7 @@ export async function cleanupWorkspace({ } await remove(workspaceDir); if (movedThemePath) { - fs.mkdirSync(path.dirname(themesDir), { recursive: true }); + fs.mkdirSync(upath.dirname(themesDir), { recursive: true }); await copy(movedThemePath, themesDir); } } @@ -111,7 +111,7 @@ export async function prepareThemeDirectory({ // copy theme files for (const theme of themeIndexes) { if (theme.type === 'file' && !pathEquals(theme.source, theme.location)) { - fs.mkdirSync(path.dirname(theme.location), { recursive: true }); + fs.mkdirSync(upath.dirname(theme.location), { recursive: true }); await copy(theme.source, theme.location); } } @@ -149,7 +149,7 @@ export function generateManifest( if (options.cover) { const { width, height, type } = imageSize( - path.resolve(entryContextDir, options.cover), + upath.resolve(entryContextDir, options.cover), ); let mimeType: string | false = false; if (type) { @@ -239,11 +239,11 @@ export async function compile({ (e): e is ManuscriptEntry => 'source' in e, ); for (const entry of contentEntries) { - fs.mkdirSync(path.dirname(entry.target), { recursive: true }); + fs.mkdirSync(upath.dirname(entry.target), { recursive: true }); // calculate style path const style = entry.themes.flatMap((theme) => - locateThemePath(theme, path.dirname(entry.target)), + locateThemePath(theme, upath.dirname(entry.target)), ); if (entry.type === 'text/markdown') { // compile markdown @@ -283,7 +283,7 @@ export async function compile({ const tocString = generateTocHtml({ entries: contentEntries, manifestPath, - distDir: path.dirname(generativeContentsEntry.target), + distDir: upath.dirname(generativeContentsEntry.target), title, tocTitle: generativeContentsEntry.title ?? TOC_TITLE, style, @@ -298,10 +298,10 @@ export async function compile({ author, language, readingProgression, - cover: cover && path.relative(entryContextDir, cover), + cover: cover && upath.relative(entryContextDir, cover), entries: entries.map((entry) => ({ title: entry.title, - path: path.relative(workspaceDir, entry.target), + path: upath.relative(workspaceDir, entry.target), encodingFormat: !('type' in entry) || entry.type === 'text/markdown' || @@ -324,7 +324,7 @@ export async function copyAssets({ if (pathEquals(entryContextDir, workspaceDir)) { return; } - const relWorkspaceDir = path.relative(entryContextDir, workspaceDir); + const relWorkspaceDir = upath.relative(entryContextDir, workspaceDir); const assets = await safeGlob(includeAssets, { cwd: entryContextDir, ignore: [ @@ -333,11 +333,11 @@ export async function copyAssets({ !pathContains(entryContextDir, p) ? [] : format === 'webpub' - ? path.join(path.relative(entryContextDir, p), '**') - : path.relative(entryContextDir, p), + ? upath.join(upath.relative(entryContextDir, p), '**') + : upath.relative(entryContextDir, p), ), // don't copy workspace itself - ...(relWorkspaceDir ? [path.join(relWorkspaceDir, '**')] : []), + ...(relWorkspaceDir ? [upath.join(relWorkspaceDir, '**')] : []), ], caseSensitiveMatch: false, followSymbolicLinks: false, @@ -345,9 +345,9 @@ export async function copyAssets({ }); debug('assets', assets); for (const asset of assets) { - const target = path.join(workspaceDir, asset); - fs.mkdirSync(path.dirname(target), { recursive: true }); - await copy(path.resolve(entryContextDir, asset), target); + const target = upath.join(workspaceDir, asset); + fs.mkdirSync(upath.dirname(target), { recursive: true }); + await copy(upath.resolve(entryContextDir, asset), target); } } diff --git a/src/processor/html.ts b/src/processor/html.ts index becb35da..3e052b8e 100644 --- a/src/processor/html.ts +++ b/src/processor/html.ts @@ -6,7 +6,6 @@ import jsdom, { ResourceLoader as BaseResourceLoader, JSDOM } from 'jsdom'; import fs from 'node:fs'; import url from 'node:url'; import prettier from 'prettier'; -import path from 'upath'; import { ManuscriptEntry } from '../input/config.js'; import type { PublicationManifest } from '../schema/publication.schema.js'; import { @@ -14,6 +13,7 @@ import { assertPubManifestSchema, debug, logWarn, + upath, } from '../util.js'; const virtualConsole = new jsdom.VirtualConsole(); @@ -113,8 +113,8 @@ export function generateTocHtml({ 'li', h( 'a', - { href: encodeURI(path.relative(distDir, entry.target)) }, - entry.title || path.basename(entry.target, '.html'), + { href: encodeURI(upath.relative(distDir, entry.target)) }, + entry.title || upath.basename(entry.target, '.html'), ), ), ); @@ -125,7 +125,7 @@ export function generateTocHtml({ ...[ h('title', title ?? ''), h('link', { - href: encodeURI(path.relative(distDir, manifestPath)), + href: encodeURI(upath.relative(distDir, manifestPath)), rel: 'publication', type: 'application/ld+json', }), diff --git a/src/processor/theme.ts b/src/processor/theme.ts index 42dcb255..78a1956f 100644 --- a/src/processor/theme.ts +++ b/src/processor/theme.ts @@ -1,9 +1,8 @@ import Arborist from '@npmcli/arborist'; import fs from 'node:fs'; import npa from 'npm-package-arg'; -import path from 'upath'; import type { MergedConfig } from '../input/config.js'; -import { beforeExitHandlers, DetailError, moveSync } from '../util.js'; +import { beforeExitHandlers, DetailError, moveSync, upath } from '../util.js'; // Rename `packages` directory into `node_modules` while Arborist works const temporaryMovePackagesDirectrory = async <T = unknown>( @@ -11,19 +10,19 @@ const temporaryMovePackagesDirectrory = async <T = unknown>( cb: () => Promise<T>, ) => { const exitHandler = () => { - if (fs.existsSync(path.join(themesDir, 'node_modules'))) { + if (fs.existsSync(upath.join(themesDir, 'node_modules'))) { moveSync( - path.join(themesDir, 'node_modules'), - path.join(themesDir, 'packages'), + upath.join(themesDir, 'node_modules'), + upath.join(themesDir, 'packages'), { overwrite: true }, ); } }; beforeExitHandlers.push(exitHandler); - if (fs.existsSync(path.join(themesDir, 'packages'))) { + if (fs.existsSync(upath.join(themesDir, 'packages'))) { moveSync( - path.join(themesDir, 'packages'), - path.join(themesDir, 'node_modules'), + upath.join(themesDir, 'packages'), + upath.join(themesDir, 'node_modules'), { overwrite: true }, ); } diff --git a/src/server.ts b/src/server.ts index f4b67dc4..47d68b12 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,13 +1,13 @@ import http from 'node:http'; import { pathToFileURL, URL } from 'node:url'; import handler from 'serve-handler'; -import upath from 'upath'; import { viewerRoot } from './const.js'; import { beforeExitHandlers, debug, findAvailablePort, isUrlString, + upath, } from './util.js'; export type PageSize = { format: string } | { width: string; height: string }; diff --git a/src/util.ts b/src/util.ts index 8a8cb8b6..207fdbc9 100644 --- a/src/util.ts +++ b/src/util.ts @@ -14,7 +14,6 @@ import util from 'node:util'; import oraConstructor from 'ora'; import portfinder from 'portfinder'; import slash from 'slash'; -import upath from 'upath'; import { copy, copySync, @@ -23,13 +22,14 @@ import { remove, removeSync, tmp, + upath, } from '../vendors/index.js'; import { publicationSchema, publicationSchemas } from './schema/pubManifest.js'; import type { PublicationManifest } from './schema/publication.schema.js'; import { vivliostyleConfigSchema } from './schema/vivliostyle.js'; import type { VivliostyleConfigSchema } from './schema/vivliostyleConfig.schema.js'; -export { copy, copySync, move, moveSync, remove, removeSync, tmp }; +export { copy, copySync, move, moveSync, remove, removeSync, tmp, upath }; export const debug = debugConstructor('vs-cli'); export const cwd = upath.normalize(process.cwd()); diff --git a/tests/api.test.ts b/tests/api.test.ts index 6d3ddbbf..4a4b355f 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -1,14 +1,14 @@ import fileType from 'file-type'; import fs from 'node:fs'; -import path from 'upath'; import { expect, test } from 'vitest'; import { build } from '../src/index.js'; +import { upath } from '../src/util.js'; import { rootPath } from './commandUtil.js'; -const fixtureRoot = path.resolve(rootPath, 'tests/fixtures/wood'); -const fixtureFile = path.join(fixtureRoot, 'index.html'); +const fixtureRoot = upath.resolve(rootPath, 'tests/fixtures/wood'); +const fixtureFile = upath.join(fixtureRoot, 'index.html'); -const localTmpDir = path.join(rootPath, 'tmp'); +const localTmpDir = upath.join(rootPath, 'tmp'); fs.mkdirSync(localTmpDir, { recursive: true }); function cleanUp(filePath: string) { @@ -22,7 +22,7 @@ function cleanUp(filePath: string) { } test('api generates pdf without errors', async () => { - const outputPath = path.join(localTmpDir, 'test-api.pdf'); + const outputPath = upath.join(localTmpDir, 'test-api.pdf'); cleanUp(outputPath); await build({ diff --git a/tests/cli.test.ts b/tests/cli.test.ts index 0d53a399..f4b4d605 100644 --- a/tests/cli.test.ts +++ b/tests/cli.test.ts @@ -1,16 +1,16 @@ import execa from 'execa'; import fileType from 'file-type'; import fs from 'node:fs'; -import path from 'upath'; import { expect, it } from 'vitest'; import packageJSON from '../package.json'; +import { upath } from '../src/util.js'; import { rootPath } from './commandUtil.js'; -const cliPath = path.join(rootPath, packageJSON.bin.vivliostyle); -const fixtureRoot = path.resolve(rootPath, 'tests/fixtures/wood'); -const fixtureFile = path.join(fixtureRoot, 'index.html'); +const cliPath = upath.join(rootPath, packageJSON.bin.vivliostyle); +const fixtureRoot = upath.resolve(rootPath, 'tests/fixtures/wood'); +const fixtureFile = upath.join(fixtureRoot, 'index.html'); -const localTmpDir = path.join(rootPath, 'tmp'); +const localTmpDir = upath.join(rootPath, 'tmp'); fs.mkdirSync(localTmpDir, { recursive: true }); function cleanUp(filePath: string) { @@ -33,7 +33,7 @@ it('show version', async () => { }); it('generate pdf without errors', async () => { - const outputPath = path.join(localTmpDir, 'test.pdf'); + const outputPath = upath.join(localTmpDir, 'test.pdf'); cleanUp(outputPath); try { diff --git a/tests/commandUtil.ts b/tests/commandUtil.ts index 9a7e86bc..df1ec119 100644 --- a/tests/commandUtil.ts +++ b/tests/commandUtil.ts @@ -1,14 +1,14 @@ import assert from 'node:assert'; import URL from 'node:url'; -import path from 'upath'; import { setupBuildParserProgram } from '../src/commands/build.parser.js'; import { collectVivliostyleConfig, mergeConfig, MergedConfig, } from '../src/input/config.js'; +import { upath } from '../src/util.js'; -export const rootPath = path.join(URL.fileURLToPath(import.meta.url), '../..'); +export const rootPath = upath.join(URL.fileURLToPath(import.meta.url), '../..'); export const getMergedConfig = async ( args: string[], @@ -27,8 +27,8 @@ export const getMergedConfig = async ( targets: options.targets, }); const context = vivliostyleConfig - ? path.dirname(vivliostyleConfigPath) - : path.join(rootPath, 'tests'); + ? upath.dirname(vivliostyleConfigPath) + : upath.join(rootPath, 'tests'); const config = await Promise.all( (vivliostyleConfig ?? [vivliostyleConfig]).map((entry) => mergeConfig(cliFlags, entry, context), @@ -46,7 +46,7 @@ export const maskConfig = (obj: any) => { } else if (k === 'image') { obj[k] = '__IMAGE__'; } else if (typeof v === 'string') { - const normalized = v.match(/^(https?|file):\/{2}/) ? v : path.toUnix(v); + const normalized = v.match(/^(https?|file):\/{2}/) ? v : upath.toUnix(v); obj[k] = normalized .replace(rootPath, '__WORKSPACE__') .replace(/^(https?|file):\/+/, '$1://'); @@ -55,7 +55,7 @@ export const maskConfig = (obj: any) => { }; export const resolveFixture = (p: string) => - path.resolve(rootPath, 'tests/fixtures', p); + upath.resolve(rootPath, 'tests/fixtures', p); export function assertSingleItem<T = unknown>( value: T | T[], diff --git a/tests/init.test.ts b/tests/init.test.ts index f67c3f06..8c0ba9c5 100644 --- a/tests/init.test.ts +++ b/tests/init.test.ts @@ -1,14 +1,13 @@ import execa from 'execa'; import fs from 'node:fs'; -import path from 'upath'; import { expect, it } from 'vitest'; import packageJSON from '../package.json'; -import { moveSync } from '../src/util.js'; +import { moveSync, upath } from '../src/util.js'; import { rootPath } from './commandUtil.js'; -const cliPath = path.join(rootPath, packageJSON.bin.vivliostyle); +const cliPath = upath.join(rootPath, packageJSON.bin.vivliostyle); -const localTmpDir = path.join(rootPath, 'tmp'); +const localTmpDir = upath.join(rootPath, 'tmp'); fs.mkdirSync(localTmpDir, { recursive: true }); function cleanUp(filePath: string) { @@ -38,7 +37,7 @@ function unChalk(str: string) { } it('test the init command', async () => { - cleanUp(path.join(localTmpDir, 'vivliostyle.config.js')); + cleanUp(upath.join(localTmpDir, 'vivliostyle.config.js')); const response = await vivliostyleCLI(['init', localTmpDir], localTmpDir); expect(unChalk(response.stdout)).toBe( 'Successfully created vivliostyle.config.js', @@ -51,11 +50,11 @@ it('test the init command', async () => { }); it('test the init command with long option', async () => { - const outputDir = path.join(localTmpDir, 'long'); + const outputDir = upath.join(localTmpDir, 'long'); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir); } - cleanUp(path.join(outputDir, 'vivliostyle.config.js')); + cleanUp(upath.join(outputDir, 'vivliostyle.config.js')); const response = await vivliostyleCLI( [ 'init', @@ -78,12 +77,12 @@ it('test the init command with long option', async () => { // Change file extension and load Common JS moveSync( - path.join(outputDir, 'vivliostyle.config.js'), - path.join(outputDir, 'vivliostyle.config.cjs'), + upath.join(outputDir, 'vivliostyle.config.js'), + upath.join(outputDir, 'vivliostyle.config.cjs'), { overwrite: true }, ); const { default: config } = await import( - path.join(outputDir, 'vivliostyle.config.cjs') + upath.join(outputDir, 'vivliostyle.config.cjs') ); expect(config.title).toBe('Sample Document'); expect(config.author).toBe('Author Name <author@example.com>'); @@ -93,11 +92,11 @@ it('test the init command with long option', async () => { }); it('test the init command with short option', async () => { - const outputDir = path.join(localTmpDir, 'short'); + const outputDir = upath.join(localTmpDir, 'short'); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir); } - cleanUp(path.join(outputDir, 'vivliostyle.config.js')); + cleanUp(upath.join(outputDir, 'vivliostyle.config.js')); const response = await vivliostyleCLI( [ 'init', @@ -120,12 +119,12 @@ it('test the init command with short option', async () => { // Change file extension and load Common JS moveSync( - path.join(outputDir, 'vivliostyle.config.js'), - path.join(outputDir, 'vivliostyle.config.cjs'), + upath.join(outputDir, 'vivliostyle.config.js'), + upath.join(outputDir, 'vivliostyle.config.cjs'), { overwrite: true }, ); const { default: config } = await import( - path.join(outputDir, 'vivliostyle.config.cjs') + upath.join(outputDir, 'vivliostyle.config.cjs') ); expect(config.title).toBe('Sample Document2'); expect(config.author).toBe('Author Name2 <author@example.com>'); diff --git a/tests/server.test.ts b/tests/server.test.ts index ee62131a..349f8f24 100644 --- a/tests/server.test.ts +++ b/tests/server.test.ts @@ -1,13 +1,13 @@ import http from 'node:http'; import { pathToFileURL } from 'node:url'; import portfinder from 'portfinder'; -import path from 'upath'; import { afterEach, beforeEach, expect, it, vi } from 'vitest'; import { getViewerFullUrl, prepareServer, teardownServer, } from '../src/server.js'; +import { upath } from '../src/util.js'; import { maskConfig, rootPath } from './commandUtil.js'; vi.mock('http', async () => { @@ -52,7 +52,7 @@ it('converts to valid broker url', async () => { {}, { viewerUrl: pathToFileURL( - path.resolve( + upath.resolve( rootPath, 'node_modules/@vivliostyle/viewer/lib/index.html', ), @@ -84,7 +84,7 @@ it('converts to valid broker url', async () => { }, { viewerUrl: pathToFileURL( - path.resolve( + upath.resolve( rootPath, 'node_modules/@vivliostyle/viewer/lib/index.html', ), @@ -109,7 +109,7 @@ it('converts to valid broker url', async () => { }, { viewerUrl: pathToFileURL( - path.resolve( + upath.resolve( rootPath, 'node_modules/@vivliostyle/viewer/lib/index.html', ), @@ -133,7 +133,7 @@ it('converts to valid broker url', async () => { }, { viewerUrl: pathToFileURL( - path.resolve( + upath.resolve( rootPath, 'node_modules/@vivliostyle/viewer/lib/index.html', ), diff --git a/vendors/index.d.ts b/vendors/index.d.ts index 24c0746a..abcea001 100644 --- a/vendors/index.d.ts +++ b/vendors/index.d.ts @@ -1,4 +1,5 @@ import { copy, copySync, move, moveSync, remove, removeSync } from 'fs-extra'; import tmp from 'tmp'; +import upath from 'upath'; -export { copy, copySync, move, moveSync, remove, removeSync, tmp }; +export { copy, copySync, move, moveSync, remove, removeSync, tmp, upath }; diff --git a/vendors/index.src.js b/vendors/index.src.js index cabb040c..66af6550 100644 --- a/vendors/index.src.js +++ b/vendors/index.src.js @@ -1,4 +1,5 @@ import tmp from 'tmp'; +import upath from 'upath'; import { copy, copySync } from '../node_modules/fs-extra/lib/copy/index.js'; import { move, moveSync } from '../node_modules/fs-extra/lib/move/index.js'; import { @@ -6,4 +7,4 @@ import { removeSync, } from '../node_modules/fs-extra/lib/remove/index.js'; -export { copy, copySync, move, moveSync, remove, removeSync, tmp }; +export { copy, copySync, move, moveSync, remove, removeSync, tmp, upath }; From 842196301bee9381cdd2d325138aaecefdd98a05 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Fri, 14 Jul 2023 00:32:29 +0900 Subject: [PATCH 25/42] chore: Fix tests on Windows --- tests/server.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/server.test.ts b/tests/server.test.ts index 349f8f24..2bc0258c 100644 --- a/tests/server.test.ts +++ b/tests/server.test.ts @@ -196,15 +196,18 @@ it('starts up a source server with custom viewer', async () => { }); it('starts up with no http server', async () => { + const input = '/absolute/path/to/manifest/file.json'; const validOut1 = await prepareServer({ - input: '/absolute/path/to/manifest/file.json', + input, workspaceDir: '/absolute/path', httpServer: false, viewer: 'file:///something/viewer', }); maskConfig(validOut1); expect(validOut1.viewerFullUrl).toBe( - 'file://something/viewer#src=file:///absolute/path/to/manifest/file.json&bookMode=true&renderAllPages=true', + `file://something/viewer#src=${pathToFileURL( + input, + )}&bookMode=true&renderAllPages=true`, ); expect(mockedCreateServer.mock.calls.length).toBe(0); expect(mockedGetPortPromise.mock.calls.length).toBe(0); From ef4e2a82a265c71500c2a9bba6b9d4bce9cb2100 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Fri, 14 Jul 2023 21:40:24 +0900 Subject: [PATCH 26/42] chore: Remove unused property --- src/build.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/build.ts b/src/build.ts index 64275e39..21750d0f 100644 --- a/src/build.ts +++ b/src/build.ts @@ -36,10 +36,6 @@ import { } from './util.js'; export interface BuildCliFlags extends CliFlags { - output?: { - output?: string; - format?: string; - }[]; bypassedPdfBuilderOption?: string; } From f3b9b6ae288b428f54b93ffdc3677dbe07a140e5 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Fri, 14 Jul 2023 22:35:20 +0900 Subject: [PATCH 27/42] feat: Add a log level option for CLI and JavaScript API --- src/build.ts | 3 +++ src/commands/build.parser.ts | 8 ++++++++ src/commands/build.ts | 1 + src/commands/init.parser.ts | 12 +++++++++-- src/commands/init.ts | 1 + src/commands/preview.parser.ts | 8 ++++++++ src/commands/preview.ts | 1 + src/init.ts | 5 ++++- src/input/config.ts | 1 + src/preview.ts | 3 +++ src/util.ts | 37 +++++++++++++++++++++++++++++++--- 11 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/build.ts b/src/build.ts index 21750d0f..db92abf6 100644 --- a/src/build.ts +++ b/src/build.ts @@ -29,6 +29,7 @@ import { cwd, debug, log, + setLogLevel, startLogging, stopLogging, upath, @@ -65,6 +66,8 @@ export async function getFullConfig( } export async function build(cliFlags: BuildCliFlags) { + setLogLevel(cliFlags.logLevel); + if (cliFlags.bypassedPdfBuilderOption) { const option = JSON.parse(cliFlags.bypassedPdfBuilderOption); // Host doesn't know browser path inside of container diff --git a/src/commands/build.parser.ts b/src/commands/build.parser.ts index bc9d7758..56ca3d9b 100644 --- a/src/commands/build.parser.ts +++ b/src/commands/build.parser.ts @@ -154,6 +154,14 @@ It is useful that using own viewer that has staging features. (ex: https://vivli // Currently, Firefox and Webkit support preview command only!`, // ).choices(['chromium', 'firefox', 'webkit']), // ) + .addOption( + new Option( + '--log-level <level>', + 'specify a log level of console outputs', + ) + .choices(['silent', 'info', 'debug']) + .default('info'), + ) .addOption(new Option('--bypassed-pdf-builder-option <json>').hideHelp()) // TODO: Remove it in the next major version up .addOption(new Option('--executable-chromium <path>').hideHelp()) diff --git a/src/commands/build.ts b/src/commands/build.ts index 4ae31d3d..4eb686ee 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -37,6 +37,7 @@ try { viewer: options.viewer, viewerParam: options.viewerParam, // browser: options.browser, + logLevel: options.logLevel, bypassedPdfBuilderOption: options.bypassedPdfBuilderOption, executableChromium: options.executableChromium, // TODO: Remove it in the next major version up }).catch(gracefulError); diff --git a/src/commands/init.parser.ts b/src/commands/init.parser.ts index 7c427d43..22c846c7 100644 --- a/src/commands/init.parser.ts +++ b/src/commands/init.parser.ts @@ -1,4 +1,4 @@ -import { Command } from 'commander'; +import { Command, Option } from 'commander'; export function setupInitParserProgram(): Command { const program = new Command(); @@ -9,6 +9,14 @@ export function setupInitParserProgram(): Command { .option('--author <author>', 'author') .option('-l, --language <language>', 'language') .option('-s, --size <size>', 'paper size') - .option('-T, --theme <theme>', 'theme'); + .option('-T, --theme <theme>', 'theme') + .addOption( + new Option( + '--log-level <level>', + 'specify a log level of console outputs', + ) + .choices(['silent', 'info', 'debug']) + .default('info'), + ); return program; } diff --git a/src/commands/init.ts b/src/commands/init.ts index d9cd1f45..40d57475 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -12,6 +12,7 @@ try { language: options.language, size: options.size, theme: options.theme, + logLevel: options.logLevel, }).catch(gracefulError); } catch (err) { if (err instanceof Error) { diff --git a/src/commands/preview.parser.ts b/src/commands/preview.parser.ts index 62286fa5..93a65a19 100644 --- a/src/commands/preview.parser.ts +++ b/src/commands/preview.parser.ts @@ -70,6 +70,14 @@ It is useful that using own viewer that has staging features. (ex: https://vivli Currently, Firefox and Webkit support preview command only!`, ).choices(['chromium', 'firefox', 'webkit']), ) + .addOption( + new Option( + '--log-level <level>', + 'specify a log level of console outputs', + ) + .choices(['silent', 'info', 'debug']) + .default('info'), + ) // TODO: Remove it in the next major version up .addOption(new Option('--executable-chromium <path>').hideHelp()); return program; diff --git a/src/commands/preview.ts b/src/commands/preview.ts index 415ef69b..120af90e 100644 --- a/src/commands/preview.ts +++ b/src/commands/preview.ts @@ -31,6 +31,7 @@ try { viewer: options.viewer, viewerParam: options.viewerParam, browser: options.browser, + logLevel: options.logLevel, executableChromium: options.executableChromium, // TODO: Remove it in the next major version up }).catch(gracefulError); } catch (err) { diff --git a/src/init.ts b/src/init.ts index 802b3e7d..58597674 100644 --- a/src/init.ts +++ b/src/init.ts @@ -1,7 +1,7 @@ import chalk from 'chalk'; import fs from 'node:fs'; import { CONTAINER_IMAGE } from './container.js'; -import { cwd, log, upath } from './util.js'; +import { cwd, log, setLogLevel, upath } from './util.js'; export interface InitCliFlags { title?: string; @@ -9,9 +9,12 @@ export interface InitCliFlags { language?: string; theme?: string; size?: string; + logLevel?: 'silent' | 'info' | 'debug'; } export async function init(cliFlags: InitCliFlags) { + setLogLevel(cliFlags.logLevel); + const vivliostyleConfigPath = upath.join(cwd, 'vivliostyle.config.js'); if (fs.existsSync(vivliostyleConfigPath)) { diff --git a/src/input/config.ts b/src/input/config.ts index e1d116b1..19b6e585 100644 --- a/src/input/config.ts +++ b/src/input/config.ts @@ -120,6 +120,7 @@ export interface CliFlags { viewerParam?: string; browser?: 'chromium' | 'firefox' | 'webkit'; readingProgression?: 'ltr' | 'rtl'; + logLevel?: 'silent' | 'info' | 'debug'; /** @deprecated */ executableChromium?: string; } diff --git a/src/preview.ts b/src/preview.ts index 8f8baecd..76900e21 100644 --- a/src/preview.ts +++ b/src/preview.ts @@ -25,6 +25,7 @@ import { logSuccess, pathContains, pathEquals, + setLogLevel, startLogging, stopLogging, upath, @@ -35,6 +36,8 @@ let timer: NodeJS.Timeout; export interface PreviewCliFlags extends CliFlags {} export async function preview(cliFlags: PreviewCliFlags) { + setLogLevel(cliFlags.logLevel); + startLogging('Collecting preview config'); const loadedConf = await collectVivliostyleConfig(cliFlags); diff --git a/src/util.ts b/src/util.ts index 207fdbc9..336d4c37 100644 --- a/src/util.ts +++ b/src/util.ts @@ -57,8 +57,24 @@ exitSignals.forEach((sig) => { }); }); +/** + * 0: silent + * 1: info + * 2: debug + */ +let logLevel: 0 | 1 | 2 = 0; +export function setLogLevel(level?: 'silent' | 'info' | 'debug') { + if (!level) { + return; + } + logLevel = { silent: 0 as const, info: 1 as const, debug: 2 as const }[level]; + if (logLevel >= 2) { + debugConstructor.enable('vs-cli'); + } +} + export function startLogging(text?: string) { - if (process.env.NODE_ENV === 'test') { + if (logLevel < 1) { return; } // If text is not set, erase previous log with space character @@ -66,7 +82,7 @@ export function startLogging(text?: string) { } export function stopLogging(text?: string, symbol?: string) { - if (process.env.NODE_ENV === 'test') { + if (logLevel < 1) { return; } if (!text) { @@ -77,11 +93,14 @@ export function stopLogging(text?: string, symbol?: string) { } export function log(...obj: any) { + if (logLevel < 1) { + return; + } console.log(...obj); } export function logUpdate(...obj: string[]) { - if (process.env.NODE_ENV === 'test') { + if (logLevel < 1) { return; } if (ora.isSpinning) { @@ -92,6 +111,9 @@ export function logUpdate(...obj: string[]) { } export function logSuccess(...obj: string[]) { + if (logLevel < 1) { + return; + } const { isSpinning, text } = ora; ora.succeed(obj.join(' ')); if (isSpinning) { @@ -100,6 +122,9 @@ export function logSuccess(...obj: string[]) { } export function logError(...obj: string[]) { + if (logLevel < 1) { + return; + } const { isSpinning, text } = ora; ora.fail(obj.join(' ')); if (isSpinning) { @@ -108,6 +133,9 @@ export function logError(...obj: string[]) { } export function logWarn(...obj: string[]) { + if (logLevel < 1) { + return; + } const { isSpinning, text } = ora; ora.warn(obj.join(' ')); if (isSpinning) { @@ -116,6 +144,9 @@ export function logWarn(...obj: string[]) { } export function logInfo(...obj: string[]) { + if (logLevel < 1) { + return; + } const { isSpinning, text } = ora; ora.info(obj.join(' ')); if (isSpinning) { From ff4ba9133695b083a22005328f39fd195d2bb515 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Fri, 14 Jul 2023 22:35:43 +0900 Subject: [PATCH 28/42] chore: Remove unnecessary console --- tests/config.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/config.test.ts b/tests/config.test.ts index 7798a591..9857eb6c 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -226,7 +226,6 @@ it('yields a config from frontmatter', async () => { }); it('parse array of config', async () => { - console.log(configFilePath['valid.4']); const validConfig = await getMergedConfig(['-c', configFilePath['valid.4']]); maskConfig(validConfig); assertArray(validConfig); From ab0e3a41bbc826c5be43e422aa1f33e046aa5a1f Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sat, 15 Jul 2023 12:34:56 +0900 Subject: [PATCH 29/42] test: Add webbook output tests --- package.json | 1 + src/build.ts | 2 + src/init.ts | 4 +- src/output/webbook.ts | 50 ++-- src/preview.ts | 1 + src/processor/html.ts | 2 + src/util.ts | 18 +- tests/__snapshots__/webbook.test.ts.snap | 276 +++++++++++++++++++++++ tests/webbook.test.ts | 268 ++++++++++++++++++++++ yarn.lock | 31 +++ 10 files changed, 625 insertions(+), 28 deletions(-) create mode 100644 tests/__snapshots__/webbook.test.ts.snap create mode 100644 tests/webbook.test.ts diff --git a/package.json b/package.json index 2ca31036..755fa1ef 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "husky": "^4.3.8", "json-schema-to-typescript": "^10.1.4", "lint-staged": "^11.1.2", + "memfs": "^4.2.0", "nodemon": "^2.0.12", "npm-run-all": "^4.1.5", "prettier-plugin-organize-imports": "^2.3.3", diff --git a/src/build.ts b/src/build.ts index db92abf6..10409336 100644 --- a/src/build.ts +++ b/src/build.ts @@ -29,6 +29,7 @@ import { cwd, debug, log, + runExitHandlers, setLogLevel, startLogging, stopLogging, @@ -191,6 +192,7 @@ export async function build(cliFlags: BuildCliFlags) { teardownServer(); } + runExitHandlers(); stopLogging('Built successfully.', '🎉'); } diff --git a/src/init.ts b/src/init.ts index 58597674..16ad922e 100644 --- a/src/init.ts +++ b/src/init.ts @@ -1,7 +1,7 @@ import chalk from 'chalk'; import fs from 'node:fs'; import { CONTAINER_IMAGE } from './container.js'; -import { cwd, log, setLogLevel, upath } from './util.js'; +import { cwd, log, runExitHandlers, setLogLevel, upath } from './util.js'; export interface InitCliFlags { title?: string; @@ -74,5 +74,7 @@ module.exports = vivliostyleConfig; `; fs.writeFileSync(vivliostyleConfigPath, vivliostyleConfig); + + runExitHandlers(); log(`Successfully created ${chalk.cyan('vivliostyle.config.js')}`); } diff --git a/src/output/webbook.ts b/src/output/webbook.ts index 67dbd533..74be2498 100644 --- a/src/output/webbook.ts +++ b/src/output/webbook.ts @@ -113,26 +113,30 @@ export async function retrieveWebbookEntry({ if (!pathContains(url)) { return []; } - return fetcher - .then(async (buffer) => { - let encodingFormat: string | undefined; - try { - const contentType = fetcher.response?.headers['content-type']; - if (contentType) { - encodingFormat = new MIMEType(contentType).essence; + return ( + fetcher + .then(async (buffer) => { + let encodingFormat: string | undefined; + try { + const contentType = fetcher.response?.headers['content-type']; + if (contentType) { + encodingFormat = new MIMEType(contentType).essence; + } + /* c8 ignore next 3 */ + } catch (e) { + /* NOOP */ } - } catch (e) { - /* NOOP */ - } - const relTarget = normalizeToLocalPath(url, encodingFormat); - const target = upath.join(outputDir, relTarget); - fetchedResources.push({ url: relTarget, encodingFormat }); - await fs.promises.mkdir(upath.dirname(target), { recursive: true }); - await fs.promises.writeFile(target, buffer); - }) - .catch(() => { - logError(`Failed to fetch webbook resources: ${url}`); - }); + const relTarget = normalizeToLocalPath(url, encodingFormat); + const target = upath.join(outputDir, relTarget); + fetchedResources.push({ url: relTarget, encodingFormat }); + await fs.promises.mkdir(upath.dirname(target), { recursive: true }); + await fs.promises.writeFile(target, buffer); + }) + /* c8 ignore next 3 */ + .catch(() => { + logError(`Failed to fetch webbook resources: ${url}`); + }) + ); }), ); @@ -211,7 +215,13 @@ export async function supplyWebPublicationManifestForWebbook({ const link = document.createElement('link'); link.setAttribute('rel', 'publication'); link.setAttribute('type', 'application/ld+json'); - link.setAttribute('href', MANIFEST_FILENAME); + link.setAttribute( + 'href', + upath.relative( + upath.dirname(entryHtmlFile), + upath.join(outputDir, MANIFEST_FILENAME), + ), + ); document.head.appendChild(link); await fs.promises.writeFile(entryHtmlFile, dom.serialize(), 'utf8'); diff --git a/src/preview.ts b/src/preview.ts index 76900e21..edb1f02f 100644 --- a/src/preview.ts +++ b/src/preview.ts @@ -127,6 +127,7 @@ export async function preview(cliFlags: PreviewCliFlags) { // Focus to the URL input box if available await page.locator('#vivliostyle-input-url').focus(); + // note: runExitHandlers() is not necessary here stopLogging('Up and running ([ctrl+c] to quit)', '🚀'); function reloadConfig(path: string) { diff --git a/src/processor/html.ts b/src/processor/html.ts index 3e052b8e..12d6327f 100644 --- a/src/processor/html.ts +++ b/src/processor/html.ts @@ -17,6 +17,7 @@ import { } from '../util.js'; const virtualConsole = new jsdom.VirtualConsole(); +/* c8 ignore start */ virtualConsole.on('error', (message) => { debug('[JSDOM Console] error:', message); }); @@ -45,6 +46,7 @@ virtualConsole.on('jsdomError', (error) => { error.stack ?? error.message, ); }); +/* c8 ignore end */ export class ResourceLoader extends BaseResourceLoader { fetcherMap = new Map<string, jsdom.AbortablePromise<Buffer>>(); diff --git a/src/util.ts b/src/util.ts index 336d4c37..537bd077 100644 --- a/src/util.ts +++ b/src/util.ts @@ -43,16 +43,20 @@ const ora = oraConstructor({ }); export let beforeExitHandlers: (() => void)[] = []; +export function runExitHandlers() { + while (beforeExitHandlers.length) { + try { + beforeExitHandlers.shift()?.(); + } catch (e) { + // NOOP + } + } +} + const exitSignals = ['exit', 'SIGINT', 'SIGTERM', 'SIGHUP']; exitSignals.forEach((sig) => { process.on(sig, (code: number) => { - while (beforeExitHandlers.length) { - try { - beforeExitHandlers.shift()?.(); - } catch (e) { - // NOOP - } - } + runExitHandlers(); process.exit(code); }); }); diff --git a/tests/__snapshots__/webbook.test.ts.snap b/tests/__snapshots__/webbook.test.ts.snap new file mode 100644 index 00000000..f7549aaf --- /dev/null +++ b/tests/__snapshots__/webbook.test.ts.snap @@ -0,0 +1,276 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`generate webpub from a plain HTML 1`] = ` +"/ +└─ work/ + ├─ input/ + │ ├─ style.css + │ └─ webbook.html + └─ output/ + ├─ publication.json + ├─ style.css + └─ webbook.html" +`; + +exports[`generate webpub from a plain HTML 2`] = ` +Object { + "@context": Array [ + "https://schema.org", + "https://www.w3.org/ns/pub-context", + ], + "conformsTo": "https://github.com/vivliostyle/vivliostyle-cli", + "inLanguage": "ja", + "links": Array [], + "name": "Doc title", + "readingOrder": Array [ + Object { + "url": "webbook.html", + }, + ], + "resources": Array [ + "style.css", + ], + "type": "Book", +} +`; + +exports[`generate webpub from a plain HTML 3`] = ` +"<!DOCTYPE html> +<html lang=\\"ja\\"> + <head> + <link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"style.css\\" /> + <title>Doc title + + + + +" +`; + +exports[`generate webpub from a remote HTML document 1`] = ` +"/ +└─ work/ + ├─ assets/ + │ └─ style.css + ├─ input/ + │ └─ index.html + └─ output/ + ├─ publication.json + └─ work/ + ├─ assets/ + │ └─ style.css + └─ input/ + └─ index.html" +`; + +exports[`generate webpub from a remote HTML document 2`] = ` +Object { + "@context": Array [ + "https://schema.org", + "https://www.w3.org/ns/pub-context", + ], + "conformsTo": "https://github.com/vivliostyle/vivliostyle-cli", + "inLanguage": "en", + "links": Array [], + "name": "Document", + "readingOrder": Array [ + Object { + "url": "work/input/index.html", + }, + ], + "resources": Array [ + "work/assets/style.css", + ], + "type": "Book", +} +`; + +exports[`generate webpub from a remote HTML document 3`] = ` +" + + Document + + + + + +" +`; + +exports[`generate webpub from a single-document publication 1`] = ` +"/ +└─ work/ + ├─ input/ + │ ├─ assets/ + │ │ ├─ figure.svg + │ │ └─ subdir.css + │ ├─ subdir/ + │ │ └─ index.html + │ └─ webbook.html + └─ output/ + ├─ assets/ + │ ├─ figure.svg + │ └─ subdir.css + ├─ subdir/ + │ └─ index.html + └─ webbook.html" +`; + +exports[`generate webpub from a single-document publication 2`] = ` +" + + Document with toc + + + + + +" +`; + +exports[`generate webpub from single markdown file 1`] = ` +"/ +└─ work/ + ├─ input/ + │ └─ foo.md + └─ output/ + ├─ foo.html + ├─ foo.md + └─ publication.json" +`; + +exports[`generate webpub from single markdown file 2`] = ` +Object { + "@context": Array [ + "https://schema.org", + "https://www.w3.org/ns/pub-context", + ], + "conformsTo": "https://github.com/vivliostyle/vivliostyle-cli", + "links": Array [], + "name": "Hi", + "readingOrder": Array [ + Object { + "name": "Hi", + "url": "foo.html", + }, + ], + "resources": Array [ + "publication.json", + "foo.md", + ], + "type": "Book", +} +`; + +exports[`generate webpub from vivliostyle.config.js 1`] = ` +"/ +└─ work/ + ├─ input/ + │ ├─ doc/ + │ │ ├─ one.html + │ │ ├─ one.md + │ │ ├─ two.html + │ │ └─ two.md + │ ├─ index.html + │ ├─ output/ + │ │ ├─ doc/ + │ │ │ ├─ one.html + │ │ │ ├─ one.md + │ │ │ ├─ two.html + │ │ │ └─ two.md + │ │ ├─ index.html + │ │ ├─ publication.json + │ │ └─ vivliostyle.config.json + │ ├─ publication.json + │ └─ vivliostyle.config.json + └─ output/ + ├─ doc/ + │ ├─ one.html + │ ├─ one.md + │ ├─ two.html + │ └─ two.md + ├─ index.html + ├─ publication.json + └─ vivliostyle.config.json" +`; + +exports[`generate webpub from vivliostyle.config.js 2`] = ` +Object { + "@context": Array [ + "https://schema.org", + "https://www.w3.org/ns/pub-context", + ], + "conformsTo": "https://github.com/vivliostyle/vivliostyle-cli", + "links": Array [ + Object { + "encodingFormat": "image/png", + "height": 100, + "rel": "cover", + "url": "cover.png", + "width": 100, + }, + ], + "name": "output", + "readingOrder": Array [ + Object { + "name": "Table of Contents", + "rel": "contents", + "type": "LinkedResource", + "url": "index.html", + }, + Object { + "url": "doc/one.html", + }, + Object { + "url": "doc/two.html", + }, + ], + "readingProgression": "rtl", + "resources": Array [ + "publication.json", + "vivliostyle.config.json", + "doc/one.md", + "doc/two.md", + ], + "type": "Book", +} +`; + +exports[`generate webpub from vivliostyle.config.js 3`] = ` +" + + output + + + +

output

+ + + +" +`; diff --git a/tests/webbook.test.ts b/tests/webbook.test.ts new file mode 100644 index 00000000..549e9f87 --- /dev/null +++ b/tests/webbook.test.ts @@ -0,0 +1,268 @@ +/// +import { + AbortablePromise, + BaseOptions, + ConstructorOptions, + FetchOptions, + FileOptions, + SupportedContentTypes, +} from 'jsdom'; +import { fs as memfs, vol } from 'memfs'; +import { lookup as mime } from 'mime-types'; +import path from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { format } from 'prettier'; +import { afterEach, expect, it, vi } from 'vitest'; +import { build } from '../src/index.js'; +import { VivliostyleConfigSchema } from '../src/schema/vivliostyleConfig.schema.js'; + +vi.mock('node:fs', () => ({ + ...memfs, + default: memfs, +})); +vi.mock('fs', () => ({ + ...memfs, + default: memfs, +})); + +vi.mock('image-size', () => ({ + imageSize: () => ({ width: 100, height: 100, type: 'png' }), +})); + +vi.mock('jsdom', async () => { + const jsdom = await vi.importActual('jsdom'); + const { JSDOM: JSDOMBase, ResourceLoader: ResourceLoaderBase } = jsdom; + + // https://github.com/jsdom/jsdom/blob/a39e0ec4ce9a8806692d986a7ed0cd565ec7498a/lib/api.js#L183 + function normalizeFromFileOptions( + filename: string, + options: FileOptions, + ): ConstructorOptions { + const normalized = { ...options } as ConstructorOptions; + if (normalized.contentType === undefined) { + const extname = path.extname(filename); + if (extname === '.xhtml' || extname === '.xht' || extname === '.xml') { + normalized.contentType = 'application/xhtml+xml'; + } + } + if (normalized.url === undefined) { + normalized.url = pathToFileURL(filename) as any; + } + return normalized; + } + + function mapToLocalPath(urlString: string): string { + const url = new URL(urlString); + let pathname = url.pathname; + if (!path.extname(pathname)) { + pathname = path.posix.join(pathname, 'index.html'); + } + return pathname; + } + + class JSDOM extends JSDOMBase { + static async fromURL(url: string, options: BaseOptions = {}) { + const resourceLoader = + options.resources instanceof ResourceLoader + ? options.resources + : new ResourceLoader(); + const fetcher = resourceLoader.fetch(url) as AbortablePromise; + const buffer = await fetcher; + if (!buffer) { + throw new Error(); + } + return new JSDOMBase(buffer, { + ...options, + url, + contentType: fetcher?.response?.headers[ + 'content-type' + ] as SupportedContentTypes, + }); + } + + static async fromFile(url: string, options: FileOptions = {}) { + const buffer = await memfs.promises.readFile(url); + return new JSDOMBase(buffer, normalizeFromFileOptions(url, options)); + } + } + + class ResourceLoader extends ResourceLoaderBase { + _readFile(filePath) { + return memfs.promises.readFile(filePath) as AbortablePromise; + } + fetch(urlString: string, options: FetchOptions = {}) { + if (/^https?:/.test(urlString)) { + const url = new URL(urlString); + const fetcher = this._readFile( + mapToLocalPath(urlString), + ) as AbortablePromise; + fetcher.response = { + headers: { + 'content-type': mime(url.pathname) || 'text/html', + }, + } as any; + return fetcher; + } + return super.fetch(urlString, options); + } + } + return { ...jsdom, JSDOM, ResourceLoader }; +}); + +afterEach(() => vol.reset()); + +it('generate webpub from single markdown file', async () => { + vol.fromJSON({ + '/work/input/foo.md': '# Hi', + }); + await build({ + input: '/work/input/foo.md', + targets: [{ path: '/work/output', format: 'webpub' }], + }); + + expect(vol.toTree()).toMatchSnapshot(); + const file = vol.toJSON(); + const manifest = JSON.parse(file['/work/output/publication.json'] as string); + delete manifest.dateModified; + expect(manifest).toMatchSnapshot(); +}); + +it('generate webpub from vivliostyle.config.js', async () => { + const config: VivliostyleConfigSchema = { + entry: ['doc/one.md', 'doc/two.md'], + output: ['/work/input/output', '/work/output'], + toc: true, + cover: 'cover.png', + readingProgression: 'rtl', + }; + vol.fromJSON({ + '/work/input/vivliostyle.config.json': JSON.stringify(config), + '/work/input/doc/one.md': 'yuno', + '/work/input/doc/two.md': 'yunocchi', + }); + await build({ + configPath: '/work/input/vivliostyle.config.json', + }); + + expect(vol.toTree()).toMatchSnapshot(); + const file = vol.toJSON(); + const manifest = JSON.parse(file['/work/output/publication.json'] as string); + delete manifest.dateModified; + expect(manifest).toMatchSnapshot(); + const toc = file['/work/output/index.html']; + expect(format(toc as string, { parser: 'html' })).toMatchSnapshot(); + + const manifest2 = JSON.parse( + file['/work/input/output/publication.json'] as string, + ); + delete manifest2.dateModified; + expect(manifest2).toEqual(manifest); + const toc2 = file['/work/input/output/index.html']; + expect(toc2).toEqual(toc); +}); + +it('generate webpub from a plain HTML', async () => { + vol.fromJSON({ + '/work/input/webbook.html': /* html */ ` + + + + + Doc title + + + + + `, + '/work/input/style.css': '', + }); + await build({ + input: '/work/input/webbook.html', + targets: [{ path: '/work/output', format: 'webpub' }], + }); + + expect(vol.toTree()).toMatchSnapshot(); + const file = vol.toJSON(); + const manifest = JSON.parse(file['/work/output/publication.json'] as string); + delete manifest.dateModified; + expect(manifest).toMatchSnapshot(); + const entry = file['/work/output/webbook.html']; + expect(format(entry as string, { parser: 'html' })).toMatchSnapshot(); +}); + +it('generate webpub from a single-document publication', async () => { + vol.fromJSON({ + '/work/input/webbook.html': /* html */ ` + + + Document with toc + + + + + + + + `, + '/work/input/subdir/index.html': /* html */ ` + + + + Doc title + + + + + + `, + '/work/input/assets/figure.svg': '', + '/work/input/assets/subdir.css': '', + }); + await build({ + input: '/work/input/webbook.html', + targets: [{ path: '/work/output', format: 'webpub' }], + }); + + expect(vol.toTree()).toMatchSnapshot(); + const file = vol.toJSON(); + const entry = file['/work/output/webbook.html']; + expect(format(entry as string, { parser: 'html' })).toMatchSnapshot(); +}); + +it('generate webpub from a remote HTML document', async () => { + vol.fromJSON({ + '/work/input/index.html': /* html */ ` + + + Document + + + + + + `, + '/work/assets/style.css': '', + }); + await build({ + input: 'https://example.com/work/input', + targets: [{ path: '/work/output', format: 'webpub' }], + }); + + expect(vol.toTree()).toMatchSnapshot(); + const file = vol.toJSON(); + const manifest = JSON.parse(file['/work/output/publication.json'] as string); + delete manifest.dateModified; + expect(manifest).toMatchSnapshot(); + const entry = file['/work/output/work/input/index.html']; + expect(format(entry as string, { parser: 'html' })).toMatchSnapshot(); +}); diff --git a/yarn.lock b/yarn.lock index 42c25f11..c6a60c39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1288,6 +1288,11 @@ are-we-there-yet@^4.0.0: delegates "^1.0.0" readable-stream "^4.1.0" +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -3864,6 +3869,11 @@ husky@^4.3.8: slash "^3.0.0" which-pm-runs "^1.0.0" +hyperdyperid@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" + integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== + i18next-ko@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/i18next-ko/-/i18next-ko-3.0.1.tgz#b5ee2a4fc6767064e3b27d7b5683a527bf2bbc5d" @@ -4498,6 +4508,14 @@ json-buffer@3.0.1: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== +json-joy@^9.2.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/json-joy/-/json-joy-9.4.0.tgz#4e9aa2cd0378974132ec6c097d2c1a2cbc0282e7" + integrity sha512-qSWB6VlyQGOdzhjP5eKABYTqAzNlzFaR+uYPYzYijfbhcOSuqWP9Q6bfU7AVvNMFPnaU79vqFqezHeqFtCPXDA== + dependencies: + arg "^5.0.2" + hyperdyperid "^1.2.0" + json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -4938,6 +4956,14 @@ mdurl@^1.0.0: resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== +memfs@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.2.0.tgz#b30d4b70626b1015f4c35548eab34087a903a5f5" + integrity sha512-V5/xE+zl6+soWxlBjiVTQSkfXybTwhEBj2I8sK9LaS5lcZsTuhRftakrcRpDY7Ycac2NTK/VzEtpKMp+gpymrQ== + dependencies: + json-joy "^9.2.0" + thingies "^1.11.1" + memoizee@^0.4.14: version "0.4.15" resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" @@ -7393,6 +7419,11 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +thingies@^1.11.1: + version "1.12.0" + resolved "https://registry.yarnpkg.com/thingies/-/thingies-1.12.0.tgz#a815c224482d607aa70f563d3cbb351a338e4710" + integrity sha512-AiGqfYC1jLmJagbzQGuoZRM48JPsr9yB734a7K6wzr34NMhjUPrWSQrkF7ZBybf3yCerCL2Gcr02kMv4NmaZfA== + through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" From 02f551deb3fbf4fa50acc7385ac16cfb6a810975 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Sat, 15 Jul 2023 17:27:58 +0900 Subject: [PATCH 30/42] test: Add EPUB output tests --- package.json | 2 +- src/output/epub.ts | 71 ++++--- src/util.ts | 4 +- tests/__snapshots__/epub.test.ts.snap | 287 ++++++++++++++++++++++++++ tests/commandUtil.ts | 113 +++++++++- tests/epub.test.ts | 192 +++++++++++++++++ tests/webbook.test.ts | 103 +-------- vendors/index.d.ts | 3 +- vendors/index.src.js | 3 +- 9 files changed, 644 insertions(+), 134 deletions(-) create mode 100644 tests/__snapshots__/epub.test.ts.snap create mode 100644 tests/epub.test.ts diff --git a/package.json b/package.json index 755fa1ef..ae550eeb 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "serve-handler": "^6.1.3", "slash": "4.0.0", "terminal-link": "^2.1.1", + "tmp": "^0.2.1", "uuid": "^8.3.2", "vfile": "^4.2.1", "w3c-xmlserializer": "^4.0.0", @@ -98,7 +99,6 @@ "pretty-quick": "^3.1.1", "release-it": "^15.6.0", "shx": "^0.3.3", - "tmp": "^0.2.1", "typescript": "^4.9.3", "upath": "^2.0.1", "vitest": "^0.33.0" diff --git a/src/output/epub.ts b/src/output/epub.ts index 68839ca5..bab31aff 100644 --- a/src/output/epub.ts +++ b/src/output/epub.ts @@ -26,7 +26,14 @@ import type { PublicationManifest, ResourceCategorization, } from '../schema/publication.schema.js'; -import { DetailError, copy, debug, logWarn, upath, useTmpDirectory } from '../util.js'; +import { + DetailError, + copy, + debug, + logWarn, + upath, + useTmpDirectory, +} from '../util.js'; interface ManifestEntry { href: string; @@ -37,8 +44,13 @@ interface ManifestEntry { interface LandmarkEntry { type: string; href: string; + text: string; } +const TOC_ID = 'toc'; +const LANDMARKS_ID = 'landmarks'; +const PAGELIST_ID = 'page-list'; + const changeExtname = (filepath: string, newExt: string) => { let ext = upath.extname(filepath); return `${filepath.slice(0, -ext.length)}${newExt}`; @@ -151,14 +163,6 @@ export async function exportEpub({ (e) => /\.html?$/.test(e.url), ); - const landmarks: { type: string; href: string }[] = []; - if (htmlCoverResource) { - landmarks.push({ - type: 'cover', - href: changeExtname(htmlCoverResource.url, '.xhtml'), - }); - } - const manifestItem = [ ...[manifest.links || []].flat(), ...[manifest.readingOrder || []].flat(), @@ -208,8 +212,29 @@ export async function exportEpub({ ); tocHtml = readingOrder[0].url; } + if (!(tocHtml in manifestItem)) { + manifestItem[tocHtml] = { + href: changeExtname(tocHtml, '.xhtml'), + mediaType: 'application/xhtml+xml', + }; + } appendManifestProperty(manifestItem[tocHtml], 'nav'); + const landmarks: LandmarkEntry[] = [ + { + type: 'toc', + href: `${manifestItem[tocHtml].href}#${TOC_ID}`, + text: 'Table of Contents', + }, + ]; + if (htmlCoverResource) { + landmarks.push({ + type: 'cover', + href: changeExtname(htmlCoverResource.url, '.xhtml'), + text: 'Cover Page', + }); + } + const processHtml = async (target: string, isTocHtml: boolean) => { let parseResult: Resolved>; try { @@ -306,7 +331,6 @@ export async function exportEpub({ uid, docTitle, docLanguages, - tocXhtml: manifestItem[tocHtml].href, manifest, readingOrder, manifestItems: Object.values(manifestItem), @@ -372,7 +396,7 @@ async function transpileHtmlToXhtml({ if (parsed) { tocParseTree = parsed; const nav = replaceWithNavElement(parsed.element); - nav.setAttribute('id', 'toc'); + nav.setAttribute('id', TOC_ID); nav.setAttribute('epub:type', 'toc'); } } @@ -383,14 +407,15 @@ async function transpileHtmlToXhtml({ ) { const nav = document.createElement('nav'); nav.setAttribute('epub:type', 'landmarks'); - nav.setAttribute('id', 'landmarks'); + nav.setAttribute('id', LANDMARKS_ID); nav.setAttribute('hidden', ''); const ol = document.createElement('ol'); - for (const { type, href } of landmarks) { + 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', href); + a.setAttribute('href', getRelativeHref(href, '', target)); + a.text = text; li.appendChild(a); ol.appendChild(li); } @@ -404,7 +429,7 @@ async function transpileHtmlToXhtml({ if (parsed) { pageListParseTree = parsed; const nav = replaceWithNavElement(parsed.element); - nav.setAttribute('id', 'page-list'); + nav.setAttribute('id', PAGELIST_ID); nav.setAttribute('epub:type', 'page-list'); } } @@ -444,7 +469,7 @@ export async function supplyTocNavElement({ const { document } = tocDom.window; const nav = document.createElement('nav'); - nav.setAttribute('id', 'toc'); + nav.setAttribute('id', TOC_ID); nav.setAttribute('role', 'doc-toc'); nav.setAttribute('epub:type', 'toc'); nav.setAttribute('hidden', ''); @@ -486,7 +511,6 @@ function buildEpubPackageDocument({ uid, docTitle, docLanguages, - tocXhtml, readingOrder, manifestItems, landmarks, @@ -495,7 +519,6 @@ function buildEpubPackageDocument({ uid: string; docTitle: string; docLanguages: string[]; - tocXhtml: string; readingOrder: PublicationLinks[]; manifestItems: ManifestEntry[]; landmarks: LandmarkEntry[]; @@ -615,13 +638,11 @@ function buildEpubPackageDocument({ })), }, guide: { - reference: [ - { - _type: 'toc', - _href: manifestItems.find((v) => v.href === tocXhtml)!.href, - }, - ...landmarks.map(({ type, href }) => ({ _type: type, _href: href })), - ], + reference: landmarks.map(({ type, href, text }) => ({ + _type: type, + _href: href, + _title: text, + })), }, }, }); diff --git a/src/util.ts b/src/util.ts index 537bd077..730ead84 100644 --- a/src/util.ts +++ b/src/util.ts @@ -14,6 +14,7 @@ import util from 'node:util'; import oraConstructor from 'ora'; import portfinder from 'portfinder'; import slash from 'slash'; +import tmp from 'tmp'; import { copy, copySync, @@ -21,7 +22,6 @@ import { moveSync, remove, removeSync, - tmp, upath, } from '../vendors/index.js'; import { publicationSchema, publicationSchemas } from './schema/pubManifest.js'; @@ -29,7 +29,7 @@ import type { PublicationManifest } from './schema/publication.schema.js'; import { vivliostyleConfigSchema } from './schema/vivliostyle.js'; import type { VivliostyleConfigSchema } from './schema/vivliostyleConfig.schema.js'; -export { copy, copySync, move, moveSync, remove, removeSync, tmp, upath }; +export { copy, copySync, move, moveSync, remove, removeSync, upath }; export const debug = debugConstructor('vs-cli'); export const cwd = upath.normalize(process.cwd()); diff --git a/tests/__snapshots__/epub.test.ts.snap b/tests/__snapshots__/epub.test.ts.snap new file mode 100644 index 00000000..8f6d5a73 --- /dev/null +++ b/tests/__snapshots__/epub.test.ts.snap @@ -0,0 +1,287 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`generate EPUB from series of HTML files > content.opf 1`] = ` +" + + + + My book + en + + + + + + + + + + + + + + + + + +" +`; + +exports[`generate EPUB from series of HTML files > src/index.xhtml 1`] = ` +" + + + My book + + + 1 + 2 + 3 + + + + + +" +`; + +exports[`generate EPUB from series of HTML files > toc.ncx 1`] = ` +" + + + + + + + + + My book + + + + + My book + + + + + + yuno + + + + + + yunocchi + + + + + +" +`; + +exports[`generate EPUB from series of HTML files > tree 1`] = ` +"/ +├─ tmp/ +│ └─ 1/ +│ ├─ EPUB/ +│ │ ├─ content.opf +│ │ ├─ src/ +│ │ │ ├─ a/ +│ │ │ │ └─ index.xhtml +│ │ │ ├─ b/ +│ │ │ │ └─ c/ +│ │ │ │ └─ d.xhtml +│ │ │ └─ index.xhtml +│ │ └─ toc.ncx +│ └─ META-INF/ +│ └─ container.xml +└─ work/ + ├─ input/ + │ └─ src/ + │ ├─ a/ + │ │ └─ index.html + │ ├─ b/ + │ │ └─ c/ + │ │ └─ d.html + │ └─ index.html + └─ output.epub" +`; + +exports[`generate EPUB from single HTML with pub manifest > container.xml 1`] = ` +" + + + + +" +`; + +exports[`generate EPUB from single HTML with pub manifest > content.opf 1`] = ` +" + + + + Document + ja-JP + 宮沢賢治 + foo + bar + baz + a + i + x + q + c + b + l + o + Publisher + Contributor + © 2023 Acme Corporation + Subject + + + + + + + + + + + + + + +" +`; + +exports[`generate EPUB from single HTML with pub manifest > index.xhtml 1`] = ` +" + + + Document + + + + + + + + + + + + + +" +`; + +exports[`generate EPUB from single HTML with pub manifest > toc.ncx 1`] = ` +" + + + + + + + + + Document + + + + + Intro + + + + + + Main + + + + + Main 1 + + + + + + Main 2 + + + + + + +" +`; + +exports[`generate EPUB from single HTML with pub manifest > tree 1`] = ` +"/ +├─ tmp/ +│ └─ 1/ +│ ├─ EPUB/ +│ │ ├─ content.opf +│ │ ├─ cover.png +│ │ ├─ index.xhtml +│ │ ├─ publication.json +│ │ └─ toc.ncx +│ └─ META-INF/ +│ └─ container.xml +└─ work/ + ├─ input/ + │ ├─ cover.png + │ ├─ index.html + │ └─ publication.json + └─ output.epub" +`; diff --git a/tests/commandUtil.ts b/tests/commandUtil.ts index df1ec119..f185fe19 100644 --- a/tests/commandUtil.ts +++ b/tests/commandUtil.ts @@ -1,14 +1,27 @@ +/// +import { + AbortablePromise, + BaseOptions, + ConstructorOptions, + FetchOptions, + FileOptions, + SupportedContentTypes, +} from 'jsdom'; +import { fs as memfs } from 'memfs'; +import { lookup as mime } from 'mime-types'; import assert from 'node:assert'; -import URL from 'node:url'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { vi } from 'vitest'; import { setupBuildParserProgram } from '../src/commands/build.parser.js'; import { + MergedConfig, collectVivliostyleConfig, mergeConfig, - MergedConfig, } from '../src/input/config.js'; import { upath } from '../src/util.js'; -export const rootPath = upath.join(URL.fileURLToPath(import.meta.url), '../..'); +export const rootPath = upath.join(fileURLToPath(import.meta.url), '../..'); export const getMergedConfig = async ( args: string[], @@ -66,3 +79,97 @@ export function assertSingleItem( export function assertArray(value: T | T[]): asserts value is T[] { return assert(Array.isArray(value)); } + +export async function getMockedJSDOM(): Promise { + const jsdom = await vi.importActual('jsdom'); + const { JSDOM: JSDOMBase, ResourceLoader: ResourceLoaderBase } = jsdom; + + // https://github.com/jsdom/jsdom/blob/a39e0ec4ce9a8806692d986a7ed0cd565ec7498a/lib/api.js#L183 + function normalizeFromFileOptions( + filename: string, + options: FileOptions, + ): ConstructorOptions { + const normalized = { ...options } as ConstructorOptions; + if (normalized.contentType === undefined) { + const extname = path.extname(filename); + if (extname === '.xhtml' || extname === '.xht' || extname === '.xml') { + normalized.contentType = 'application/xhtml+xml'; + } + } + if (normalized.url === undefined) { + normalized.url = pathToFileURL(filename) as any; + } + return normalized; + } + + function mapToLocalPath(urlString: string): string { + const url = new URL(urlString); + let pathname = url.pathname; + if (!path.extname(pathname)) { + pathname = path.posix.join(pathname, 'index.html'); + } + return pathname; + } + + class JSDOM extends JSDOMBase { + static async fromURL(url: string, options: BaseOptions = {}) { + const resourceLoader = + options.resources instanceof ResourceLoader + ? options.resources + : new ResourceLoader(); + const fetcher = resourceLoader.fetch(url) as AbortablePromise; + const buffer = await fetcher; + if (!buffer) { + throw new Error(); + } + return new JSDOMBase(buffer, { + ...options, + url, + contentType: fetcher?.response?.headers[ + 'content-type' + ] as SupportedContentTypes, + }); + } + + static async fromFile(url: string, options: FileOptions = {}) { + const buffer = await memfs.promises.readFile(url); + return new JSDOMBase(buffer, normalizeFromFileOptions(url, options)); + } + } + + class ResourceLoader extends ResourceLoaderBase { + _readFile(filePath) { + return memfs.promises.readFile(filePath) as AbortablePromise; + } + fetch(urlString: string, options: FetchOptions = {}) { + if (/^https?:/.test(urlString)) { + const url = new URL(urlString); + const fetcher = this._readFile( + mapToLocalPath(urlString), + ) as AbortablePromise; + fetcher.response = { + headers: { + 'content-type': mime(url.pathname) || 'text/html', + }, + } as any; + return fetcher; + } + return super.fetch(urlString, options); + } + } + return { ...jsdom, JSDOM, ResourceLoader }; +} + +export function getMockedTmp() { + const mod = { + __callCount: 0, + dir: (_, cb) => { + const target = `/tmp/${++mod.__callCount}`; + memfs.mkdirSync(target, { recursive: true }); + cb(null, target); + }, + }; + return { default: mod } as unknown as typeof import('tmp') & { + __callCount: number; + }; +} diff --git a/tests/epub.test.ts b/tests/epub.test.ts new file mode 100644 index 00000000..93ec1d25 --- /dev/null +++ b/tests/epub.test.ts @@ -0,0 +1,192 @@ +import { fs as memfs, vol } from 'memfs'; +import { format } from 'prettier'; +import tmp from 'tmp'; +import { afterEach, expect, it, vi } from 'vitest'; +import { exportEpub } from '../src/output/epub.js'; +import { PublicationManifest } from '../src/schema/publication.schema.js'; + +vi.mock('node:fs', () => ({ ...memfs, default: memfs })); + +vi.mock('jsdom', () => + import('./commandUtil.js').then(({ getMockedJSDOM }) => getMockedJSDOM()), +); + +vi.mock('tmp', () => { + const mod = { + __count: 0, + dir: (_, cb) => { + const target = `/tmp/${++mod.__count}`; + memfs.mkdirSync(target, { recursive: true }); + cb(null, target); + }, + }; + return { default: mod }; +}); + +afterEach(() => { + vol.reset(); + (tmp as any).__count = 0; +}); + +it('generate EPUB from single HTML with pub manifest', async () => { + const manifest: PublicationManifest = { + '@context': ['https://schema.org', 'https://www.w3.org/ns/pub-context'], + conformsTo: 'yuno', + readingProgression: 'rtl', + resources: [ + { url: 'cover.png', rel: 'cover' }, + { url: 'index.html', rel: 'pagelist' }, + ], + author: { + name: [ + { value: 'Kenji Miyazawa', language: 'en' }, + { value: '宮沢賢治', language: 'ja' }, + ], + }, + creator: ['foo', 'bar'], + editor: 'baz', + artist: 'a', + illustrator: 'i', + colorist: 'x', + penciler: 'q', + inker: 'c', + letterer: 'b', + translator: 'l', + readBy: 'o', + publisher: { name: 'Publisher', type: 'Organization' }, + contributor: 'Contributor', + copyrightHolder: 'Acme Corporation', + copyrightYear: '2023', + subject: 'Subject', + }; + vol.fromJSON({ + '/work/input/publication.json': JSON.stringify(manifest), + '/work/input/index.html': /* html */ ` + + + Document + + + + +
+

Table of Contents

+
    +
  1. Intro
  2. +
  3. + Main +
      +
    1. Main 1
    2. +
    3. Main 2
    4. +
    +
  4. +
+
+
+
    +
  1. Intro
  2. +
  3. Main
  4. +
  5. Main 1
  6. +
  7. Main 2
  8. +
+
+ + + + + + `, + '/work/input/cover.png': '', + }); + await exportEpub({ + webpubDir: '/work/input', + entryHtmlFile: 'index.html', + manifest, + target: '/work/output.epub', + epubVersion: '3.0', + }); + + expect(vol.toTree()).toMatchSnapshot('tree'); + const file = vol.toJSON(); + expect(file['/tmp/1/META-INF/container.xml']).toMatchSnapshot( + 'container.xml', + ); + expect( + file['/tmp/1/EPUB/content.opf'] + ?.replace(/.+<\/dc:identifier>/g, '') + .replace(/.+<\/meta>/g, ''), + ).toMatchSnapshot('content.opf'); + expect( + file['/tmp/1/EPUB/toc.ncx']?.replace( + /<\/meta>/g, + '', + ), + ).toMatchSnapshot('toc.ncx'); + const entry = file['/tmp/1/EPUB/index.xhtml']; + expect(format(entry as string, { parser: 'html' })).toMatchSnapshot( + 'index.xhtml', + ); +}); + +it('generate EPUB from series of HTML files', async () => { + const manifest: PublicationManifest = { + '@context': ['https://schema.org', 'https://www.w3.org/ns/pub-context'], + conformsTo: 'yuno', + readingOrder: ['src/index.html', 'src/a/index.html', 'src/b/c/d.html'], + }; + vol.fromJSON({ + '/work/input/src/index.html': /* html */ ` + + + My book + + + 1 + 2 + 3 + + + `, + '/work/input/src/a/index.html': /* html */ ` + + + yuno + + + + + `, + '/work/input/src/b/c/d.html': /* html */ ` + + + yunocchi + + + + `, + }); + await exportEpub({ + webpubDir: '/work/input', + manifest, + target: '/work/output.epub', + epubVersion: '3.0', + }); + + expect(vol.toTree()).toMatchSnapshot('tree'); + const file = vol.toJSON(); + expect( + file['/tmp/1/EPUB/content.opf'] + ?.replace(/.+<\/dc:identifier>/g, '') + .replace(/.+<\/meta>/g, ''), + ).toMatchSnapshot('content.opf'); + expect( + file['/tmp/1/EPUB/toc.ncx']?.replace( + /<\/meta>/g, + '', + ), + ).toMatchSnapshot('toc.ncx'); + const first = file['/tmp/1/EPUB/src/index.xhtml']; + expect(format(first as string, { parser: 'html' })).toMatchSnapshot( + 'src/index.xhtml', + ); +}); diff --git a/tests/webbook.test.ts b/tests/webbook.test.ts index 549e9f87..eb5cb6a0 100644 --- a/tests/webbook.test.ts +++ b/tests/webbook.test.ts @@ -1,113 +1,18 @@ -/// -import { - AbortablePromise, - BaseOptions, - ConstructorOptions, - FetchOptions, - FileOptions, - SupportedContentTypes, -} from 'jsdom'; import { fs as memfs, vol } from 'memfs'; -import { lookup as mime } from 'mime-types'; -import path from 'node:path'; -import { pathToFileURL } from 'node:url'; import { format } from 'prettier'; import { afterEach, expect, it, vi } from 'vitest'; import { build } from '../src/index.js'; import { VivliostyleConfigSchema } from '../src/schema/vivliostyleConfig.schema.js'; -vi.mock('node:fs', () => ({ - ...memfs, - default: memfs, -})); -vi.mock('fs', () => ({ - ...memfs, - default: memfs, -})); +vi.mock('node:fs', () => ({ ...memfs, default: memfs })); vi.mock('image-size', () => ({ imageSize: () => ({ width: 100, height: 100, type: 'png' }), })); -vi.mock('jsdom', async () => { - const jsdom = await vi.importActual('jsdom'); - const { JSDOM: JSDOMBase, ResourceLoader: ResourceLoaderBase } = jsdom; - - // https://github.com/jsdom/jsdom/blob/a39e0ec4ce9a8806692d986a7ed0cd565ec7498a/lib/api.js#L183 - function normalizeFromFileOptions( - filename: string, - options: FileOptions, - ): ConstructorOptions { - const normalized = { ...options } as ConstructorOptions; - if (normalized.contentType === undefined) { - const extname = path.extname(filename); - if (extname === '.xhtml' || extname === '.xht' || extname === '.xml') { - normalized.contentType = 'application/xhtml+xml'; - } - } - if (normalized.url === undefined) { - normalized.url = pathToFileURL(filename) as any; - } - return normalized; - } - - function mapToLocalPath(urlString: string): string { - const url = new URL(urlString); - let pathname = url.pathname; - if (!path.extname(pathname)) { - pathname = path.posix.join(pathname, 'index.html'); - } - return pathname; - } - - class JSDOM extends JSDOMBase { - static async fromURL(url: string, options: BaseOptions = {}) { - const resourceLoader = - options.resources instanceof ResourceLoader - ? options.resources - : new ResourceLoader(); - const fetcher = resourceLoader.fetch(url) as AbortablePromise; - const buffer = await fetcher; - if (!buffer) { - throw new Error(); - } - return new JSDOMBase(buffer, { - ...options, - url, - contentType: fetcher?.response?.headers[ - 'content-type' - ] as SupportedContentTypes, - }); - } - - static async fromFile(url: string, options: FileOptions = {}) { - const buffer = await memfs.promises.readFile(url); - return new JSDOMBase(buffer, normalizeFromFileOptions(url, options)); - } - } - - class ResourceLoader extends ResourceLoaderBase { - _readFile(filePath) { - return memfs.promises.readFile(filePath) as AbortablePromise; - } - fetch(urlString: string, options: FetchOptions = {}) { - if (/^https?:/.test(urlString)) { - const url = new URL(urlString); - const fetcher = this._readFile( - mapToLocalPath(urlString), - ) as AbortablePromise; - fetcher.response = { - headers: { - 'content-type': mime(url.pathname) || 'text/html', - }, - } as any; - return fetcher; - } - return super.fetch(urlString, options); - } - } - return { ...jsdom, JSDOM, ResourceLoader }; -}); +vi.mock('jsdom', () => + import('./commandUtil.js').then(({ getMockedJSDOM }) => getMockedJSDOM()), +); afterEach(() => vol.reset()); diff --git a/vendors/index.d.ts b/vendors/index.d.ts index abcea001..567eec4e 100644 --- a/vendors/index.d.ts +++ b/vendors/index.d.ts @@ -1,5 +1,4 @@ import { copy, copySync, move, moveSync, remove, removeSync } from 'fs-extra'; -import tmp from 'tmp'; import upath from 'upath'; -export { copy, copySync, move, moveSync, remove, removeSync, tmp, upath }; +export { archiver, copy, copySync, move, moveSync, remove, removeSync, upath }; diff --git a/vendors/index.src.js b/vendors/index.src.js index 66af6550..307cbae5 100644 --- a/vendors/index.src.js +++ b/vendors/index.src.js @@ -1,4 +1,3 @@ -import tmp from 'tmp'; import upath from 'upath'; import { copy, copySync } from '../node_modules/fs-extra/lib/copy/index.js'; import { move, moveSync } from '../node_modules/fs-extra/lib/move/index.js'; @@ -7,4 +6,4 @@ import { removeSync, } from '../node_modules/fs-extra/lib/remove/index.js'; -export { copy, copySync, move, moveSync, remove, removeSync, tmp, upath }; +export { copy, copySync, move, moveSync, remove, removeSync, upath }; From 78e53104bb350f6669cfcd6fa8ccd9a6bc779ab1 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Sat, 15 Jul 2023 21:02:10 +0900 Subject: [PATCH 31/42] test: Sort items in tree results --- tests/commandUtil.ts | 39 +++++++++++++++++++++++++++++++++++++++ tests/epub.test.ts | 5 +++-- tests/webbook.test.ts | 12 ++++++------ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/tests/commandUtil.ts b/tests/commandUtil.ts index f185fe19..5fe69b49 100644 --- a/tests/commandUtil.ts +++ b/tests/commandUtil.ts @@ -7,7 +7,9 @@ import { FileOptions, SupportedContentTypes, } from 'jsdom'; +import { printTree } from 'json-joy/es6/util/print/printTree'; import { fs as memfs } from 'memfs'; +import { Volume } from 'memfs/lib/volume.js'; import { lookup as mime } from 'mime-types'; import assert from 'node:assert'; import path from 'node:path'; @@ -173,3 +175,40 @@ export function getMockedTmp() { __callCount: number; }; } + +/** + * Modified version of memfs's `toTreeSync` function that sorts by directory item names + * source: https://github.com/streamich/memfs/blob/cd6c25698536aab8845774c4a0036376a0fd599f/src/print/index.ts#L6-L30 + */ +export function toTree( + fs: Volume, + opts: { + dir?: string; + tab?: string; + depth?: number; + } = {}, +) { + const separator = '/'; + let dir = opts.dir || separator; + if (dir[dir.length - 1] !== separator) dir += separator; + const tab = opts.tab || ''; + const depth = 10; + let subtree = ' (...)'; + if (depth > 0) { + const list = fs.readdirSync(dir, { withFileTypes: true }); + list.sort((a, b) => (a.name > b.name ? 1 : -1)); + subtree = printTree( + tab, + list.map((entry) => (tab) => { + if (entry.isDirectory()) { + return toTree(fs, { dir: dir + entry.name, depth: depth - 1, tab }); + } else if (entry.isSymbolicLink()) { + return '' + entry.name + ' → ' + fs.readlinkSync(dir + entry.name); + } else { + return '' + entry.name; + } + }), + ); + } + return `${upath.basename(dir)}${separator}${subtree}`; +} diff --git a/tests/epub.test.ts b/tests/epub.test.ts index 93ec1d25..1d527b1a 100644 --- a/tests/epub.test.ts +++ b/tests/epub.test.ts @@ -4,6 +4,7 @@ import tmp from 'tmp'; import { afterEach, expect, it, vi } from 'vitest'; import { exportEpub } from '../src/output/epub.js'; import { PublicationManifest } from '../src/schema/publication.schema.js'; +import { toTree } from './commandUtil.js'; vi.mock('node:fs', () => ({ ...memfs, default: memfs })); @@ -106,7 +107,7 @@ it('generate EPUB from single HTML with pub manifest', async () => { epubVersion: '3.0', }); - expect(vol.toTree()).toMatchSnapshot('tree'); + expect(toTree(vol)).toMatchSnapshot('tree'); const file = vol.toJSON(); expect(file['/tmp/1/META-INF/container.xml']).toMatchSnapshot( 'container.xml', @@ -172,7 +173,7 @@ it('generate EPUB from series of HTML files', async () => { epubVersion: '3.0', }); - expect(vol.toTree()).toMatchSnapshot('tree'); + expect(toTree(vol)).toMatchSnapshot('tree'); const file = vol.toJSON(); expect( file['/tmp/1/EPUB/content.opf'] diff --git a/tests/webbook.test.ts b/tests/webbook.test.ts index eb5cb6a0..ce65a867 100644 --- a/tests/webbook.test.ts +++ b/tests/webbook.test.ts @@ -3,6 +3,7 @@ import { format } from 'prettier'; import { afterEach, expect, it, vi } from 'vitest'; import { build } from '../src/index.js'; import { VivliostyleConfigSchema } from '../src/schema/vivliostyleConfig.schema.js'; +import { toTree } from './commandUtil.js'; vi.mock('node:fs', () => ({ ...memfs, default: memfs })); @@ -25,7 +26,7 @@ it('generate webpub from single markdown file', async () => { targets: [{ path: '/work/output', format: 'webpub' }], }); - expect(vol.toTree()).toMatchSnapshot(); + expect(toTree(vol)).toMatchSnapshot(); const file = vol.toJSON(); const manifest = JSON.parse(file['/work/output/publication.json'] as string); delete manifest.dateModified; @@ -49,7 +50,7 @@ it('generate webpub from vivliostyle.config.js', async () => { configPath: '/work/input/vivliostyle.config.json', }); - expect(vol.toTree()).toMatchSnapshot(); + expect(toTree(vol)).toMatchSnapshot(); const file = vol.toJSON(); const manifest = JSON.parse(file['/work/output/publication.json'] as string); delete manifest.dateModified; @@ -86,7 +87,7 @@ it('generate webpub from a plain HTML', async () => { targets: [{ path: '/work/output', format: 'webpub' }], }); - expect(vol.toTree()).toMatchSnapshot(); + expect(toTree(vol)).toMatchSnapshot(); const file = vol.toJSON(); const manifest = JSON.parse(file['/work/output/publication.json'] as string); delete manifest.dateModified; @@ -138,7 +139,7 @@ it('generate webpub from a single-document publication', async () => { targets: [{ path: '/work/output', format: 'webpub' }], }); - expect(vol.toTree()).toMatchSnapshot(); + expect(toTree(vol)).toMatchSnapshot(); const file = vol.toJSON(); const entry = file['/work/output/webbook.html']; expect(format(entry as string, { parser: 'html' })).toMatchSnapshot(); @@ -162,8 +163,7 @@ it('generate webpub from a remote HTML document', async () => { input: 'https://example.com/work/input', targets: [{ path: '/work/output', format: 'webpub' }], }); - - expect(vol.toTree()).toMatchSnapshot(); + expect(toTree(vol)).toMatchSnapshot(); const file = vol.toJSON(); const manifest = JSON.parse(file['/work/output/publication.json'] as string); delete manifest.dateModified; From 9150547385a8ff96bab157f8795657a7004a50d6 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Sat, 15 Jul 2023 22:58:45 +0900 Subject: [PATCH 32/42] chore: Sort resources property in pub manifest --- src/output/webbook.ts | 14 ++++++++++++++ tests/__snapshots__/webbook.test.ts.snap | 6 +++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/output/webbook.ts b/src/output/webbook.ts index 74be2498..72a48b2c 100644 --- a/src/output/webbook.ts +++ b/src/output/webbook.ts @@ -25,6 +25,17 @@ import { upath, } from '../util.js'; +function sortManifestResources(manifest: PublicationManifest) { + if (!Array.isArray(manifest.resources)) { + return; + } + manifest.resources = [...manifest.resources].sort((a, b) => + (typeof a === 'string' ? a : a.url) > (typeof b === 'string' ? b : b.url) + ? 1 + : -1, + ); +} + export async function prepareWebPublicationDirectory({ outputDir, }: { @@ -151,6 +162,7 @@ export async function retrieveWebbookEntry({ ({ url }) => !referencedContents.includes(url), ), ]; + sortManifestResources(manifest); } debug( @@ -212,6 +224,7 @@ export async function supplyWebPublicationManifestForWebbook({ resources: allFiles.filter((f) => f !== entry), }, ); + sortManifestResources(manifest); const link = document.createElement('link'); link.setAttribute('rel', 'publication'); link.setAttribute('type', 'application/ld+json'); @@ -344,6 +357,7 @@ export async function copyWebPublicationAssets({ return file; }), ]; + sortManifestResources(manifest); fs.writeFileSync(actualManifestPath, JSON.stringify(manifest, null, 2)); return manifest; } diff --git a/tests/__snapshots__/webbook.test.ts.snap b/tests/__snapshots__/webbook.test.ts.snap index f7549aaf..6715f647 100644 --- a/tests/__snapshots__/webbook.test.ts.snap +++ b/tests/__snapshots__/webbook.test.ts.snap @@ -170,8 +170,8 @@ Object { }, ], "resources": Array [ - "publication.json", "foo.md", + "publication.json", ], "type": "Book", } @@ -242,10 +242,10 @@ Object { ], "readingProgression": "rtl", "resources": Array [ - "publication.json", - "vivliostyle.config.json", "doc/one.md", "doc/two.md", + "publication.json", + "vivliostyle.config.json", ], "type": "Book", } From dab272df1ec1c2a4d385eeb97c36d6b3e30f6065 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Sat, 15 Jul 2023 23:55:56 +0900 Subject: [PATCH 33/42] feat: Make --verbose option deprecated --- src/commands/build.parser.ts | 4 ++-- src/commands/build.ts | 2 +- src/commands/init.parser.ts | 2 +- src/commands/preview.parser.ts | 6 ++--- src/commands/preview.ts | 2 +- src/input/config.ts | 21 ++++++++++++---- src/output/pdf.ts | 4 ++-- src/util.ts | 16 +++++++++---- tests/__snapshots__/config.test.ts.snap | 32 ++++++++++++------------- 9 files changed, 53 insertions(+), 36 deletions(-) diff --git a/src/commands/build.parser.ts b/src/commands/build.parser.ts index 56ca3d9b..01ac1860 100644 --- a/src/commands/build.parser.ts +++ b/src/commands/build.parser.ts @@ -122,7 +122,6 @@ This option is equivalent with "--preflight press-ready"`, Please refer the document of press-ready for further information. https://github.com/vibranthq/press-ready`, ) - .option('--verbose', 'verbose log output') .option( '--no-sandbox', `launch chrome without sandbox. use this option when ECONNREFUSED error occurred.`, @@ -159,12 +158,13 @@ It is useful that using own viewer that has staging features. (ex: https://vivli '--log-level ', 'specify a log level of console outputs', ) - .choices(['silent', 'info', 'debug']) + .choices(['silent', 'info', 'verbose', 'debug']) .default('info'), ) .addOption(new Option('--bypassed-pdf-builder-option ').hideHelp()) // TODO: Remove it in the next major version up .addOption(new Option('--executable-chromium ').hideHelp()) + .addOption(new Option('--verbose').hideHelp()) .action((_arg: any, option: BuildCliFlags) => { option.targets = inferenceTargetsOption(targets); }); diff --git a/src/commands/build.ts b/src/commands/build.ts index 4eb686ee..a302cf82 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -28,7 +28,7 @@ try { renderMode: options.renderMode || 'local', preflight: options.preflight, preflightOption: options.preflightOption, - verbose: options.verbose, + verbose: options.verbose, // TODO: Remove it in the next major version up timeout: options.timeout, sandbox: options.sandbox, executableBrowser: options.executableBrowser, diff --git a/src/commands/init.parser.ts b/src/commands/init.parser.ts index 22c846c7..bac1a1fc 100644 --- a/src/commands/init.parser.ts +++ b/src/commands/init.parser.ts @@ -15,7 +15,7 @@ export function setupInitParserProgram(): Command { '--log-level ', 'specify a log level of console outputs', ) - .choices(['silent', 'info', 'debug']) + .choices(['silent', 'info', 'verbose', 'debug']) .default('info'), ); return program; diff --git a/src/commands/preview.parser.ts b/src/commands/preview.parser.ts index 93a65a19..3890b94f 100644 --- a/src/commands/preview.parser.ts +++ b/src/commands/preview.parser.ts @@ -40,7 +40,6 @@ custom(comma separated): 182mm,257mm or 8.5in,11in`, 'Direction of reading progression', ).choices(['ltr', 'rtl']), ) - .option('--verbose', 'verbose log output') .option( '--no-sandbox', `launch chrome without sandbox (use this option to avoid ECONNREFUSED error)`, @@ -75,10 +74,11 @@ Currently, Firefox and Webkit support preview command only!`, '--log-level ', 'specify a log level of console outputs', ) - .choices(['silent', 'info', 'debug']) + .choices(['silent', 'info', 'verbose', 'debug']) .default('info'), ) // TODO: Remove it in the next major version up - .addOption(new Option('--executable-chromium ').hideHelp()); + .addOption(new Option('--executable-chromium ').hideHelp()) + .addOption(new Option('--verbose').hideHelp()); return program; } diff --git a/src/commands/preview.ts b/src/commands/preview.ts index 120af90e..d716c331 100644 --- a/src/commands/preview.ts +++ b/src/commands/preview.ts @@ -23,7 +23,7 @@ try { author: options.author, language: options.language, readingProgression: options.readingProgression, - verbose: options.verbose, + verbose: options.verbose, // TODO: Remove it in the next major version up timeout: options.timeout, sandbox: options.sandbox, executableBrowser: options.executableBrowser, diff --git a/src/input/config.ts b/src/input/config.ts index 19b6e585..97502ff3 100644 --- a/src/input/config.ts +++ b/src/input/config.ts @@ -107,7 +107,7 @@ export interface CliFlags { title?: string; author?: string; language?: string; - verbose?: boolean; + /** @deprecated */ verbose?: boolean; timeout?: number; renderMode?: 'local' | 'docker'; preflight?: 'press-ready' | 'press-ready-local'; @@ -120,7 +120,7 @@ export interface CliFlags { viewerParam?: string; browser?: 'chromium' | 'firefox' | 'webkit'; readingProgression?: 'ltr' | 'rtl'; - logLevel?: 'silent' | 'info' | 'debug'; + logLevel?: 'silent' | 'info' | 'verbose' | 'debug'; /** @deprecated */ executableChromium?: string; } @@ -170,7 +170,6 @@ export type MergedConfig = { disableFormatHtml: boolean; }; cover: string | undefined; - verbose: boolean; timeout: number; sandbox: boolean; executableBrowser: string; @@ -179,6 +178,7 @@ export type MergedConfig = { httpServer: boolean; viewer: string | undefined; viewerParam: string | undefined; + logLevel: 'silent' | 'info' | 'verbose' | 'debug'; } & ManifestConfig; const DEFAULT_TIMEOUT = 2 * 60 * 1000; // 2 minutes @@ -442,6 +442,14 @@ export async function collectVivliostyleConfig( cliFlags.executableBrowser = cliFlags.executableChromium; } + if (cliFlags.verbose) { + logWarn( + chalk.yellowBright( + "'--verbose' option was deprecated and will be removed in a future release. Please replace with '--log-level verbose' option.", + ), + ); + } + return { cliFlags, ...configEntry, @@ -511,7 +519,6 @@ export async function mergeConfig( disableFormatHtml: config?.vfm?.disableFormatHtml ?? false, }; - const verbose = cliFlags.verbose ?? false; const timeout = cliFlags.timeout ?? config?.timeout ?? DEFAULT_TIMEOUT; const sandbox = cliFlags.sandbox ?? true; const browserType = cliFlags.browser ?? config?.browser ?? 'chromium'; @@ -521,6 +528,10 @@ export async function mergeConfig( const httpServer = cliFlags.http ?? config?.http ?? false; const viewer = cliFlags.viewer ?? config?.viewer ?? undefined; const viewerParam = cliFlags.viewerParam ?? config?.viewerParam ?? undefined; + const logLevel = + cliFlags.logLevel ?? + ((cliFlags.verbose && 'verbose') || undefined) ?? + 'silent'; const rootThemes = cliFlags.theme ? [ @@ -638,7 +649,6 @@ export async function mergeConfig( readingProgression, vfmOptions, cover, - verbose, timeout, sandbox, executableBrowser, @@ -647,6 +657,7 @@ export async function mergeConfig( httpServer, viewer, viewerParam, + logLevel, }; if (!cliFlags.input && !config) { throw new Error( diff --git a/src/output/pdf.ts b/src/output/pdf.ts index 0e962820..6b61a0ed 100644 --- a/src/output/pdf.ts +++ b/src/output/pdf.ts @@ -87,13 +87,13 @@ export async function buildPDF({ browserType, image, sandbox, - verbose, timeout, entryContextDir, entries, httpServer, viewer, viewerParam, + logLevel, }: BuildPdfOptions): Promise { const isInContainer = checkContainerEnvironment(); logUpdate(`Launching build environment`); @@ -169,7 +169,7 @@ export async function buildPDF({ } break; } - if (!verbose) { + if (logLevel === 'silent' || logLevel === 'info') { return; } if (msg.type() === 'error') { diff --git a/src/util.ts b/src/util.ts index 730ead84..b0ba687c 100644 --- a/src/util.ts +++ b/src/util.ts @@ -64,15 +64,21 @@ exitSignals.forEach((sig) => { /** * 0: silent * 1: info - * 2: debug + * 2: verbose + * 3: debug */ -let logLevel: 0 | 1 | 2 = 0; -export function setLogLevel(level?: 'silent' | 'info' | 'debug') { +let logLevel: 0 | 1 | 2 | 3 = 0; +export function setLogLevel(level?: 'silent' | 'info' | 'verbose' | 'debug') { if (!level) { return; } - logLevel = { silent: 0 as const, info: 1 as const, debug: 2 as const }[level]; - if (logLevel >= 2) { + logLevel = { + silent: 0 as const, + info: 1 as const, + verbose: 2 as const, + debug: 3 as const, + }[level]; + if (logLevel >= 3) { debugConstructor.enable('vs-cli'); } } diff --git a/tests/__snapshots__/config.test.ts.snap b/tests/__snapshots__/config.test.ts.snap index 9f14fb71..28e10ede 100644 --- a/tests/__snapshots__/config.test.ts.snap +++ b/tests/__snapshots__/config.test.ts.snap @@ -51,6 +51,7 @@ Object { "format": "pub-manifest", }, "language": undefined, + "logLevel": "info", "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", "needToGenerateManifest": true, "outputs": Array [ @@ -88,7 +89,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, "title": "pkgName", - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, @@ -135,6 +135,7 @@ Object { "format": "epub-opf", }, "language": undefined, + "logLevel": "info", "outputs": Array [ Object { "format": "pdf", @@ -154,7 +155,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/epubs/adaptive/OPS/themes", "timeout": 120000, "title": undefined, - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, @@ -201,6 +201,7 @@ Object { "format": "epub", }, "language": undefined, + "logLevel": "info", "outputs": Array [ Object { "format": "pdf", @@ -220,7 +221,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/epubs/themes", "timeout": 120000, "title": undefined, - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, @@ -266,6 +266,7 @@ Object { "format": "webbook", }, "language": undefined, + "logLevel": "info", "outputs": Array [ Object { "format": "pdf", @@ -285,7 +286,6 @@ Object { "themesDir": "__WORKSPACE__/themes", "timeout": 120000, "title": undefined, - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, @@ -332,6 +332,7 @@ Object { "format": "pub-manifest", }, "language": undefined, + "logLevel": "info", "manifestPath": "__WORKSPACE__/tests/fixtures/webbooks/readium-webpub/manifest.jsonld", "outputs": Array [ Object { @@ -349,7 +350,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/webbooks/readium-webpub/themes", "timeout": 120000, "title": undefined, - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, @@ -395,6 +395,7 @@ Object { "format": "pub-manifest", }, "language": undefined, + "logLevel": "info", "manifestPath": "__WORKSPACE__/tests/fixtures/webbooks/w3c-webpub/publication.json", "outputs": Array [ Object { @@ -412,7 +413,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/webbooks/w3c-webpub/themes", "timeout": 120000, "title": undefined, - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, @@ -458,6 +458,7 @@ Object { "format": "webbook", }, "language": undefined, + "logLevel": "info", "outputs": Array [ Object { "format": "pdf", @@ -477,7 +478,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, "title": undefined, - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, @@ -564,6 +564,7 @@ Object { "format": "pub-manifest", }, "language": "myLanguage", + "logLevel": "info", "manifestPath": "__WORKSPACE__/tests/fixtures/config/workspaceDir/publication.json", "needToGenerateManifest": true, "outputs": Array [ @@ -609,7 +610,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/config/workspaceDir/themes", "timeout": 42000, "title": "myTitle", - "verbose": false, "vfmOptions": Object { "disableFormatHtml": true, "hardLineBreaks": true, @@ -699,6 +699,7 @@ Object { "format": "pub-manifest", }, "language": "language", + "logLevel": "info", "manifestPath": "__WORKSPACE__/tests/fixtures/config/workspaceDir/publication.json", "needToGenerateManifest": true, "outputs": Array [ @@ -751,7 +752,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/config/workspaceDir/themes", "timeout": 1, "title": "title", - "verbose": false, "vfmOptions": Object { "disableFormatHtml": true, "hardLineBreaks": true, @@ -805,6 +805,7 @@ Object { "format": "pub-manifest", }, "language": undefined, + "logLevel": "info", "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", "needToGenerateManifest": true, "outputs": Array [ @@ -826,7 +827,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, "title": "pkgName", - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, @@ -880,6 +880,7 @@ Object { "format": "pub-manifest", }, "language": undefined, + "logLevel": "info", "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", "needToGenerateManifest": true, "outputs": Array [ @@ -901,7 +902,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, "title": "example", - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, @@ -991,6 +991,7 @@ Object { "format": "pub-manifest", }, "language": "language", + "logLevel": "info", "manifestPath": "__WORKSPACE__/tests/fixtures/config/workspaceDir/publication.json", "needToGenerateManifest": true, "outputs": Array [ @@ -1043,7 +1044,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/config/workspaceDir/themes", "timeout": 1, "title": "title", - "verbose": false, "vfmOptions": Object { "disableFormatHtml": true, "hardLineBreaks": true, @@ -1097,6 +1097,7 @@ Object { "format": "pub-manifest", }, "language": undefined, + "logLevel": "info", "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", "needToGenerateManifest": true, "outputs": Array [ @@ -1118,7 +1119,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, "title": "pkgName", - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, @@ -1172,6 +1172,7 @@ Object { "format": "pub-manifest", }, "language": undefined, + "logLevel": "info", "manifestPath": "__WORKSPACE__/tests/fixtures/config/publication.json", "needToGenerateManifest": true, "outputs": Array [ @@ -1193,7 +1194,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, "title": "example", - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, @@ -1264,6 +1264,7 @@ Object { "format": "markdown", }, "language": "language", + "logLevel": "info", "manifestPath": "__SNIP__", "needToGenerateManifest": true, "outputs": Array [ @@ -1310,7 +1311,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/config/workspaceDir/themes", "timeout": 1, "title": "title", - "verbose": false, "vfmOptions": Object { "disableFormatHtml": true, "hardLineBreaks": true, @@ -1364,6 +1364,7 @@ Object { "format": "markdown", }, "language": undefined, + "logLevel": "info", "manifestPath": "__SNIP__", "needToGenerateManifest": true, "outputs": Array [ @@ -1385,7 +1386,6 @@ Object { "themesDir": "__WORKSPACE__/tests/fixtures/config/themes", "timeout": 120000, "title": "Yuno", - "verbose": false, "vfmOptions": Object { "disableFormatHtml": false, "hardLineBreaks": false, From 0faa3339529451aeb80beda8ad92799514554352 Mon Sep 17 00:00:00 2001 From: spring-raining Date: Sun, 16 Jul 2023 00:12:38 +0900 Subject: [PATCH 34/42] chore: Update README --- README.md | 178 +++++++++++++++++++++++++++--------------------------- 1 file changed, 90 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index cc32c36b..ddb58a57 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ Options: -l, --language language -s, --size paper size -T, --theme theme + --log-level specify a log level of console outputs (choices: "silent", "info", + "verbose", "debug", default: "info") -h, --help display help for command ``` @@ -68,58 +70,57 @@ vivliostyle build ``` Options: - -c, --config path to vivliostyle.config.js [vivliostyle.config.js] - -o, --output specify output file name or directory [.pdf] - This option can be specified multiple, then each -o options - can be supplied one -f option. - ex: -o output1 -f webpub -o output2.pdf -f pdf - -f, --format <format> specify output format corresponding output target - If an extension is specified on -o option, this field will be - inferenced automatically. - -s, --size <size> output pdf size - preset: A5, A4, A3, B5, B4, JIS-B5, JIS-B4, letter, legal, - ledger - custom(comma separated): 182mm,257mm or 8.5in,11in - -m, --crop-marks print crop marks - --bleed <bleed> extent of the bleed area for printing with crop marks [3mm] - --crop-offset <offset> distance between the edge of the trim size and the edge of - the media size. [auto (13mm + bleed)] - --css <CSS> custom style CSS code. (ex: ":root {--my-color: lime;}") - --style <stylesheet> additional stylesheet URL or path - --user-style <user_stylesheet> user stylesheet URL or path - -d, --single-doc single HTML document input - -p, --press-ready make generated PDF compatible with press ready PDF/X-1a - [false] - This option is equivalent with "--preflight press-ready" - -t, --timeout <seconds> timeout limit for waiting Vivliostyle process [120] - -T, --theme <theme> theme path or package name - --title <title> title - --author <author> author - -l, --language <language> language - --render-mode <mode> if docker is set, Vivliostyle try to render PDF on Docker - container [local] (choices: "local", "docker") - --preflight <mode> apply the process to generate PDF for printing (choices: - "press-ready", "press-ready-local") - --preflight-option <options...> options for preflight process (ex: gray-scale, - enforce-outline) - Please refer the document of press-ready for further - information. - https://github.com/vibranthq/press-ready - --verbose verbose log output - --no-sandbox launch chrome without sandbox. use this option when - ECONNREFUSED error occurred. - --executable-browser <path> specify a path of executable browser you installed - --image <image> specify a docker image to render - --http launch an HTTP server hosting contents instead of file - protocol - It is useful that requires CORS such as external web fonts. - --viewer <URL> specify a URL of displaying viewer instead of - vivliostyle-cli's one - It is useful that using own viewer that has staging features. - (ex: https://vivliostyle.vercel.app/) - --viewer-param <parameters> specify viewer parameters. - (ex: "allowScripts=false&pixelRatio=16") - -h, --help display help for command + -c, --config <config_file> path to vivliostyle.config.js [vivliostyle.config.js] + -o, --output <path> specify output file name or directory [<title>.pdf] + This option can be specified multiple, then each -o options can + be supplied one -f option. + ex: -o output1 -f webpub -o output2.pdf -f pdf + -f, --format <format> specify output format corresponding output target + If an extension is specified on -o option, this field will be + inferenced automatically. + -s, --size <size> output pdf size + preset: A5, A4, A3, B5, B4, JIS-B5, JIS-B4, letter, legal, + ledger + custom(comma separated): 182mm,257mm or 8.5in,11in + -m, --crop-marks print crop marks + --bleed <bleed> extent of the bleed area for printing with crop marks [3mm] + --crop-offset <offset> distance between the edge of the trim size and the edge of the + media size. [auto (13mm + bleed)] + --css <CSS> custom style CSS code. (ex: ":root {--my-color: lime;}") + --style <stylesheet> additional stylesheet URL or path + --user-style <user_stylesheet> user stylesheet URL or path + -d, --single-doc single HTML document input + -p, --press-ready make generated PDF compatible with press ready PDF/X-1a [false] + This option is equivalent with "--preflight press-ready" + -t, --timeout <seconds> timeout limit for waiting Vivliostyle process [120] + -T, --theme <theme> theme path or package name + --title <title> title + --author <author> author + -l, --language <language> language + --reading-progression <direction> Direction of reading progression (choices: "ltr", "rtl") + --render-mode <mode> if docker is set, Vivliostyle try to render PDF on Docker + container [local] (choices: "local", "docker") + --preflight <mode> apply the process to generate PDF for printing (choices: + "press-ready", "press-ready-local") + --preflight-option <options...> options for preflight process (ex: gray-scale, enforce-outline) + Please refer the document of press-ready for further + information. + https://github.com/vibranthq/press-ready + --no-sandbox launch chrome without sandbox. use this option when + ECONNREFUSED error occurred. + --executable-browser <path> specify a path of executable browser you installed + --image <image> specify a docker image to render + --http launch an HTTP server hosting contents instead of file protocol + It is useful that requires CORS such as external web fonts. + --viewer <URL> specify a URL of displaying viewer instead of vivliostyle-cli's + one + It is useful that using own viewer that has staging features. + (ex: https://vivliostyle.vercel.app/) + --viewer-param <parameters> specify viewer parameters. (ex: + "allowScripts=false&pixelRatio=16") + --log-level <level> specify a log level of console outputs (choices: "silent", + "info", "verbose", "debug", default: "info") + -h, --help display help for command ``` ### `preview` @@ -134,42 +135,43 @@ vivliostyle preview ``` Options: - -c, --config <config_file> path to vivliostyle.config.js - -T, --theme <theme> theme path or package name - -s, --size <size> output pdf size - preset: A5, A4, A3, B5, B4, JIS-B5, JIS-B4, letter, legal, - ledger - custom(comma separated): 182mm,257mm or 8.5in,11in - -m, --crop-marks print crop marks - --bleed <bleed> extent of the bleed area for printing with crop marks [3mm] - --crop-offset <offset> distance between the edge of the trim size and the edge of - the media size. [auto (13mm + bleed)] - --css <CSS> custom style CSS code. (ex: ":root {--my-color: lime;}") - --style <stylesheet> additional stylesheet URL or path - --user-style <user_stylesheet> user stylesheet URL or path - -d, --single-doc single HTML document input - -q, --quick quick loading with rough page count - --title <title> title - --author <author> author - -l, --language <language> language - --verbose verbose log output - --no-sandbox launch chrome without sandbox (use this option to avoid - ECONNREFUSED error) - --executable-browser <path> specify a path of executable browser you installed - --http launch an HTTP server hosting contents instead of file - protocol - It is useful that requires CORS such as external web fonts. - --viewer <URL> specify a URL of displaying viewer instead of - vivliostyle-cli's one - It is useful that using own viewer that has staging features. - (ex: https://vivliostyle.vercel.app/) - --viewer-param <parameters> specify viewer parameters. - (ex: "allowScripts=false&pixelRatio=16") - --browser <browser> EXPERIMENTAL SUPPORT: Specify a browser type to launch - Vivliostyle viewer [chromium] - Currently, Firefox and Webkit support preview command only! - (choices: "chromium", "firefox", "webkit") - -h, --help display help for command + -c, --config <config_file> path to vivliostyle.config.js + -T, --theme <theme> theme path or package name + -s, --size <size> output pdf size + preset: A5, A4, A3, B5, B4, JIS-B5, JIS-B4, letter, legal, + ledger + custom(comma separated): 182mm,257mm or 8.5in,11in + -m, --crop-marks print crop marks + --bleed <bleed> extent of the bleed area for printing with crop marks [3mm] + --crop-offset <offset> distance between the edge of the trim size and the edge of the + media size. [auto (13mm + bleed)] + --css <CSS> custom style CSS code. (ex: ":root {--my-color: lime;}") + --style <stylesheet> additional stylesheet URL or path + --user-style <user_stylesheet> user stylesheet URL or path + -d, --single-doc single HTML document input + -q, --quick quick loading with rough page count + --title <title> title + --author <author> author + -l, --language <language> language + --reading-progression <direction> Direction of reading progression (choices: "ltr", "rtl") + --no-sandbox launch chrome without sandbox (use this option to avoid + ECONNREFUSED error) + --executable-browser <path> specify a path of executable browser you installed + --http launch an HTTP server hosting contents instead of file protocol + It is useful that requires CORS such as external web fonts. + --viewer <URL> specify a URL of displaying viewer instead of vivliostyle-cli's + one + It is useful that using own viewer that has staging features. + (ex: https://vivliostyle.vercel.app/) + --viewer-param <parameters> specify viewer parameters. (ex: + "allowScripts=false&pixelRatio=16") + --browser <browser> EXPERIMENTAL SUPPORT: Specify a browser type to launch + Vivliostyle viewer [chromium] + Currently, Firefox and Webkit support preview command only! + (choices: "chromium", "firefox", "webkit") + --log-level <level> specify a log level of console outputs (choices: "silent", + "info", "verbose", "debug", default: "info") + -h, --help display help for command ``` ## User Guide From 5c055d48acecb1e560c67ebcecc3c50642626a14 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sat, 22 Jul 2023 11:57:00 +0900 Subject: [PATCH 35/42] chore: Restrict copy files for webbook output --- src/output/webbook.ts | 62 ++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/output/webbook.ts b/src/output/webbook.ts index 72a48b2c..4ca0bbfb 100644 --- a/src/output/webbook.ts +++ b/src/output/webbook.ts @@ -248,10 +248,11 @@ export async function supplyWebPublicationManifestForWebbook({ export async function copyWebPublicationAssets({ exportAliases, outputs, + includeAssets, manifestPath, input, outputDir, -}: Pick<MergedConfig, 'exportAliases' | 'outputs'> & { +}: Pick<MergedConfig, 'exportAliases' | 'outputs' | 'includeAssets'> & { input: string; outputDir: string; manifestPath: string; @@ -262,34 +263,45 @@ export async function copyWebPublicationAssets({ target: upath.relative(input, target), })) .filter(({ source }) => !source.startsWith('..')); - const allFiles = await safeGlob('**', { - cwd: input, - ignore: [ - // don't copy auto-generated assets - ...outputs.flatMap(({ format, path: p }) => - !pathContains(input, p) - ? [] - : format === 'webpub' - ? upath.join(upath.relative(input, p), '**') - : upath.relative(input, p), - ), - // including node_modules possibly occurs cyclic reference of symlink - '**/node_modules', - // only include dotfiles starting with `.vs-` - '**/.!(vs-*)', + const allFiles = await safeGlob( + [ + upath.relative(input, manifestPath), + '**/*.{html,html,css}', + ...includeAssets, ], - // follow symbolic links to copy local theme packages - followSymbolicLinks: true, - gitignore: false, - dot: true, - }); + { + cwd: input, + ignore: [ + // don't copy auto-generated assets + ...outputs.flatMap(({ format, path: p }) => + !pathContains(input, p) + ? [] + : format === 'webpub' + ? upath.join(upath.relative(input, p), '**') + : upath.relative(input, p), + ), + // including node_modules possibly occurs cyclic reference of symlink + '**/node_modules', + // only include dotfiles starting with `.vs-` + '**/.!(vs-*)/**', + ], + // follow symbolic links to copy local theme packages + followSymbolicLinks: true, + gitignore: false, + dot: true, + }, + ); debug( 'webbook files', - allFiles.map((file) => { - const alias = relExportAliases.find(({ source }) => source === file); - return alias ? `${file} (alias: ${alias.target})` : file; - }), + JSON.stringify( + allFiles.map((file) => { + const alias = relExportAliases.find(({ source }) => source === file); + return alias ? `${file} (alias: ${alias.target})` : file; + }), + null, + 2, + ), ); const resources: string[] = []; let actualManifestPath = upath.join( From 9b5836fd8c1c25ebb982adb580e2a9cd81efc957 Mon Sep 17 00:00:00 2001 From: spring-raining <harusamex.com@gmail.com> Date: Sat, 22 Jul 2023 16:29:55 +0900 Subject: [PATCH 36/42] chore: Remove a publication manifest from EPUB output --- src/build.ts | 15 ++++-- src/input/config.ts | 19 ++++++-- src/output/epub.ts | 60 +++++++++++++++++++++--- src/output/webbook.ts | 27 +++++------ src/preview.ts | 2 +- src/processor/html.ts | 27 +++++------ src/server.ts | 38 +++++++++------ tests/__snapshots__/config.test.ts.snap | 4 +- tests/__snapshots__/epub.test.ts.snap | 6 --- tests/__snapshots__/webbook.test.ts.snap | 21 ++------- tests/epub.test.ts | 1 + tests/webbook.test.ts | 2 +- 12 files changed, 136 insertions(+), 86 deletions(-) diff --git a/src/build.ts b/src/build.ts index 10409336..a563f7a0 100644 --- a/src/build.ts +++ b/src/build.ts @@ -1,4 +1,5 @@ import chalk from 'chalk'; +import { pathToFileURL } from 'node:url'; import terminalLink from 'terminal-link'; import { getExecutableBrowserPath } from './browser.js'; import { @@ -108,7 +109,7 @@ export async function build(cliFlags: BuildCliFlags) { output = await buildPDFWithContainer({ ...config, input: (config.manifestPath ?? - config.webbookEntryPath ?? + config.webbookEntryUrl ?? config.epubOpfPath) as string, target, }); @@ -116,13 +117,13 @@ export async function build(cliFlags: BuildCliFlags) { output = await buildPDF({ ...config, input: (config.manifestPath ?? - config.webbookEntryPath ?? + config.webbookEntryUrl ?? config.epubOpfPath) as string, target, }); } } else if (format === 'webpub' || format === 'epub') { - const { manifestPath, webbookEntryPath } = config; + const { manifestPath, webbookEntryUrl } = config; let outputDir: string; if (format === 'webpub') { outputDir = target.path; @@ -133,9 +134,11 @@ export async function build(cliFlags: BuildCliFlags) { continue; } + let entryContextUrl: string; let entryHtmlFile: string | undefined; let manifest: PublicationManifest; if (manifestPath) { + entryContextUrl = pathToFileURL(manifestPath).href; manifest = await copyWebPublicationAssets({ ...config, input: config.workspaceDir, @@ -151,11 +154,12 @@ export async function build(cliFlags: BuildCliFlags) { ); } } - } else if (webbookEntryPath) { + } else if (webbookEntryUrl) { const ret = await retrieveWebbookEntry({ - webbookEntryPath, + webbookEntryUrl, outputDir, }); + entryContextUrl = webbookEntryUrl; entryHtmlFile = ret.entryHtmlFile; manifest = ret.manifest || @@ -172,6 +176,7 @@ export async function build(cliFlags: BuildCliFlags) { await exportEpub({ webpubDir: outputDir, entryHtmlFile, + entryContextUrl, manifest, target: target.path, epubVersion: target.version, diff --git a/src/input/config.ts b/src/input/config.ts index 97502ff3..bdad09f7 100644 --- a/src/input/config.ts +++ b/src/input/config.ts @@ -132,7 +132,7 @@ export interface EpubManifestConfig { epubOpfPath: string; } export interface WebbookEntryConfig { - webbookEntryPath: string; + webbookEntryUrl: string; } export type ManifestConfig = XOR< [WebPublicationManifestConfig, WebbookEntryConfig, EpubManifestConfig] @@ -680,7 +680,7 @@ type CommonOpts = Omit< | 'manifestPath' | 'needToGenerateManifest' | 'epubOpfPath' - | 'webbookEntryPath' + | 'webbookEntryUrl' | 'title' | 'author' >; @@ -764,7 +764,20 @@ async function composeSingleInputConfig<T extends CliFlags>( : upath.basename(sourcePath); return { manifestPath, needToGenerateManifest: true }; } else if (input.format === 'html' || input.format === 'webbook') { - return { webbookEntryPath: input.entry }; + const url = isUrlString(input.entry) + ? new URL(input.entry) + : pathToFileURL(input.entry); + // Ensures trailing slash or explicit HTML extensions + if ( + (url.protocol === 'http:' || url.protocol === 'https:') && + !url.pathname.endsWith('/') && + !/\.html?$/.test(url.pathname) + ) { + url.pathname = `${url.pathname}/`; + } + return { + webbookEntryUrl: url.href, + }; } else if (input.format === 'pub-manifest') { return { manifestPath: input.entry }; } else if (input.format === 'epub-opf') { diff --git a/src/output/epub.ts b/src/output/epub.ts index bab31aff..c4a9bdd0 100644 --- a/src/output/epub.ts +++ b/src/output/epub.ts @@ -6,7 +6,7 @@ import GithubSlugger from 'github-slugger'; import type { JSDOM } from 'jsdom'; import { lookup as mime } from 'mime-types'; import fs from 'node:fs'; -import url from 'node:url'; +import { pathToFileURL } from 'node:url'; import { v4 as uuid } from 'uuid'; import serializeToXml from 'w3c-xmlserializer'; import { EPUB_CONTAINER_XML, EPUB_NS, XML_DECLARATION } from '../const.js'; @@ -31,6 +31,7 @@ import { copy, debug, logWarn, + remove, upath, useTmpDirectory, } from '../util.js'; @@ -59,7 +60,7 @@ const changeExtname = (filepath: string, newExt: string) => { const getRelativeHref = (target: string, baseUrl: string, rootUrl: string) => { const absBasePath = upath.join('/', baseUrl); const absRootPath = upath.join('/', rootUrl); - const hrefUrl = new URL(target, url.pathToFileURL(absBasePath)); + const hrefUrl = new URL(target, pathToFileURL(absBasePath)); if (hrefUrl.protocol !== 'file:') { return target; } @@ -67,7 +68,7 @@ const getRelativeHref = (target: string, baseUrl: string, rootUrl: string) => { hrefUrl.pathname = changeExtname(hrefUrl.pathname, '.xhtml'); } const pathname = upath.posix.relative( - url.pathToFileURL(upath.dirname(absRootPath)).pathname, + pathToFileURL(upath.dirname(absRootPath)).pathname, hrefUrl.pathname, ); return `${pathname}${hrefUrl.search}${hrefUrl.hash}`; @@ -109,17 +110,25 @@ const appendManifestProperty = (entry: ManifestEntry, newProperty: string) => { export async function exportEpub({ webpubDir, entryHtmlFile, + entryContextUrl, manifest, target, epubVersion, }: { webpubDir: string; entryHtmlFile?: string; + entryContextUrl?: string; manifest: PublicationManifest; target: string; epubVersion: '3.0'; }) { - debug('Export EPUB'); + debug('Export EPUB', { + webpubDir, + entryContextUrl, + entryHtmlFile, + target, + epubVersion, + }); const [tmpDir] = await useTmpDirectory(); fs.mkdirSync(upath.join(tmpDir, 'META-INF'), { recursive: true }); @@ -241,6 +250,8 @@ export async function exportEpub({ parseResult = await transpileHtmlToXhtml({ target, contextDir: upath.join(tmpDir, 'EPUB'), + entryContextUrl: + entryContextUrl || pathToFileURL(upath.join(webpubDir, '/')).href, landmarks, isTocHtml, isPagelistHtml: target === (pageListResource?.url || entryHtmlRelPath), @@ -288,7 +299,7 @@ export async function exportEpub({ await processHtml(target, false); } - let { tocParseTree } = tocProcessResult; + let { tocParseTree, linkedPubManifest } = tocProcessResult; if (!tocParseTree) { tocParseTree = await supplyTocNavElement({ tocHtml, @@ -298,6 +309,10 @@ export async function exportEpub({ docLanguages, }); } + if (linkedPubManifest) { + await remove(upath.join(tmpDir, 'EPUB', linkedPubManifest)); + delete manifestItem[linkedPubManifest]; + } // EPUB/toc.ncx fs.writeFileSync( @@ -345,12 +360,14 @@ export async function exportEpub({ async function transpileHtmlToXhtml({ target, contextDir, + entryContextUrl, landmarks, isTocHtml, isPagelistHtml, }: { target: string; contextDir: string; + entryContextUrl: string; landmarks: LandmarkEntry[]; isTocHtml: boolean; isPagelistHtml: boolean; @@ -358,6 +375,7 @@ async function transpileHtmlToXhtml({ dom: JSDOM; tocParseTree?: TocResourceTreeRoot; pageListParseTree?: PageListResourceTreeRoot; + linkedPubManifest?: string; hasMathmlContent: boolean; hasRemoteResources: boolean; hasScriptedContent: boolean; @@ -389,6 +407,7 @@ async function transpileHtmlToXhtml({ let tocParseTree: TocResourceTreeRoot | undefined; let pageListParseTree: PageListResourceTreeRoot | undefined; + let linkedPubManifest: string | undefined; if (isTocHtml) { if (!document.querySelector('[epub:type="toc"]')) { @@ -422,6 +441,34 @@ async function transpileHtmlToXhtml({ nav.appendChild(ol); document.body.appendChild(nav); } + + // Remove a publication manifest linked to ToC html. + // When converting to EPUB, HTML files are converted to XHTML files + // and no longer conform to Web publication, so we need to + // explicitly remove the publication manifest. + const publicationLinkEl = document.querySelector( + 'link[href][rel="publication"]', + ); + if (publicationLinkEl) { + const href = publicationLinkEl.getAttribute('href')!.trim(); + if (href.startsWith('#')) { + const scriptEl = document.getElementById(href.slice(1)); + if (scriptEl?.getAttribute('type') === 'application/ld+json') { + scriptEl.parentNode?.removeChild(scriptEl); + } + } else { + const entryUrl = entryContextUrl; + const publicationUrl = new URL(href, new URL(target, entryUrl)).href; + const rootUrl = /^https?:/i.test(entryUrl) + ? new URL('/', entryUrl).href + : new URL('.', entryUrl).href; + const relPublicationPath = upath.relative(rootUrl, publicationUrl); + if (!relPublicationPath.startsWith('..')) { + linkedPubManifest = relPublicationPath; + } + } + publicationLinkEl.parentNode?.removeChild(publicationLinkEl); + } } if (isPagelistHtml) { @@ -441,6 +488,7 @@ async function transpileHtmlToXhtml({ dom, tocParseTree, pageListParseTree, + linkedPubManifest, // FIXME: Yes, I recognize this implementation is inadequate. hasMathmlContent: !!document.querySelector('math'), hasRemoteResources: !!document.querySelector( @@ -451,7 +499,7 @@ async function transpileHtmlToXhtml({ }; } -export async function supplyTocNavElement({ +async function supplyTocNavElement({ tocHtml, tocDom, contextDir, diff --git a/src/output/webbook.ts b/src/output/webbook.ts index 4ca0bbfb..23378b99 100644 --- a/src/output/webbook.ts +++ b/src/output/webbook.ts @@ -49,7 +49,7 @@ export async function prepareWebPublicationDirectory({ } export async function retrieveWebbookEntry({ - webbookEntryPath, + webbookEntryUrl, outputDir, }: WebbookEntryConfig & { outputDir: string; @@ -57,22 +57,19 @@ export async function retrieveWebbookEntry({ entryHtmlFile: string; manifest: PublicationManifest | null; }> { - if (/^https?:\/\//.test(webbookEntryPath)) { + if (/^https?:/i.test(webbookEntryUrl)) { logUpdate('Fetching remote contents'); } const resourceLoader = new ResourceLoader(); - const { dom, baseUrl } = await getJsdomFromUrlOrFile( - webbookEntryPath, - resourceLoader, - ); + const { dom } = await getJsdomFromUrlOrFile(webbookEntryUrl, resourceLoader); const manifest = await fetchLinkedPublicationManifest({ dom, resourceLoader, - baseUrl, + baseUrl: webbookEntryUrl, }); - const rootUrl = /^https?:\/\//.test(baseUrl) - ? new URL('/', baseUrl).href - : new URL('.', baseUrl).href; + const rootUrl = /^https?:/i.test(webbookEntryUrl) + ? new URL('/', webbookEntryUrl).href + : new URL('.', webbookEntryUrl).href; const pathContains = (url: string) => !upath.relative(rootUrl, url).startsWith('..'); const retriever = new Map(resourceLoader.fetcherMap); @@ -80,7 +77,7 @@ export async function retrieveWebbookEntry({ if (manifest) { [manifest.resources || []].flat().forEach((v) => { const url = typeof v === 'string' ? v : v.url; - const fullUrl = new URL(url, baseUrl).href; + const fullUrl = new URL(url, webbookEntryUrl).href; if (!pathContains(fullUrl) || retriever.has(fullUrl)) { return; } @@ -97,8 +94,8 @@ export async function retrieveWebbookEntry({ ) { continue; } - const fullUrl = new URL(url, baseUrl).href; - if (!pathContains(fullUrl) || fullUrl === baseUrl) { + const fullUrl = new URL(url, webbookEntryUrl).href; + if (!pathContains(fullUrl) || fullUrl === webbookEntryUrl) { continue; } const subpathResourceLoader = new ResourceLoader(); @@ -177,7 +174,7 @@ export async function retrieveWebbookEntry({ return { entryHtmlFile: upath.join( outputDir, - normalizeToLocalPath(baseUrl, 'text/html'), + normalizeToLocalPath(webbookEntryUrl, 'text/html'), ), manifest, }; @@ -265,7 +262,7 @@ export async function copyWebPublicationAssets({ .filter(({ source }) => !source.startsWith('..')); const allFiles = await safeGlob( [ - upath.relative(input, manifestPath), + `**/${upath.relative(input, manifestPath)}`, '**/*.{html,html,css}', ...includeAssets, ], diff --git a/src/preview.ts b/src/preview.ts index edb1f02f..f6eb8639 100644 --- a/src/preview.ts +++ b/src/preview.ts @@ -72,7 +72,7 @@ export async function preview(cliFlags: PreviewCliFlags) { const { viewerFullUrl } = await prepareServer({ input: (config.manifestPath ?? - config.webbookEntryPath ?? + config.webbookEntryUrl ?? config.epubOpfPath) as string, workspaceDir: config.workspaceDir, httpServer: config.httpServer, diff --git a/src/processor/html.ts b/src/processor/html.ts index 12d6327f..6ca987b8 100644 --- a/src/processor/html.ts +++ b/src/processor/html.ts @@ -4,7 +4,7 @@ import toHTML from 'hast-util-to-html'; import h from 'hastscript'; import jsdom, { ResourceLoader as BaseResourceLoader, JSDOM } from 'jsdom'; import fs from 'node:fs'; -import url from 'node:url'; +import { fileURLToPath, pathToFileURL } from 'node:url'; import prettier from 'prettier'; import { ManuscriptEntry } from '../input/config.js'; import type { PublicationManifest } from '../schema/publication.schema.js'; @@ -12,6 +12,7 @@ import { DetailError, assertPubManifestSchema, debug, + isUrlString, logWarn, upath, } from '../util.js'; @@ -66,33 +67,27 @@ export async function getJsdomFromUrlOrFile( resourceLoader?: ResourceLoader, ): Promise<{ dom: JSDOM; - baseUrl: string; }> { - let baseUrl = src; + const url = isUrlString(src) ? new URL(src) : pathToFileURL(src); let dom: JSDOM; - if (/^https?:\/\//.test(src)) { - const url = new URL(src); - // Ensures trailing slash or explicit HTML extensions - if (!url.pathname.endsWith('/') && !/\.html?$/.test(url.pathname)) { - url.pathname = `${url.pathname}/`; - } - baseUrl = url.href; + if (url.protocol === 'http:' || url.protocol === 'https:') { dom = await JSDOM.fromURL(src, { virtualConsole, resources: resourceLoader, }); - } else { - baseUrl = /^file:\/\//.test(src) ? src : url.pathToFileURL(src).href; + } else if (url.protocol === 'file:') { if (resourceLoader) { - const file = resourceLoader._readFile(url.fileURLToPath(baseUrl)); - resourceLoader.fetcherMap.set(baseUrl, file); + const file = resourceLoader._readFile(fileURLToPath(url)); + resourceLoader.fetcherMap.set(url.href, file); } - dom = await JSDOM.fromFile(url.fileURLToPath(baseUrl), { + dom = await JSDOM.fromFile(fileURLToPath(url), { virtualConsole, resources: resourceLoader, }); + } else { + throw new Error(`Unsupported protocol: ${url.protocol}`); } - return { dom, baseUrl }; + return { dom }; } export function generateTocHtml({ diff --git a/src/server.ts b/src/server.ts index 47d68b12..7515c6a3 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,5 +1,5 @@ import http from 'node:http'; -import { pathToFileURL, URL } from 'node:url'; +import { fileURLToPath, pathToFileURL, URL } from 'node:url'; import handler from 'serve-handler'; import { viewerRoot } from './const.js'; import { @@ -60,21 +60,29 @@ export async function prepareServer(option: ServerOption): Promise<{ return viewerUrl; })()); - const sourceUrl = await (isUrlString(option.input) + const inputUrl = isUrlString(option.input) ? new URL(option.input) - : option.httpServer || - // Use http server because http viewer cannot access to file protocol - (option.viewer && /^https?:/i.test(option.viewer)) - ? (async () => { - _sourceServer = - _sourceServer || (await launchServer(option.workspaceDir)); - - const sourceUrl = new URL('http://localhost'); - sourceUrl.port = `${_sourceServer.port}`; - sourceUrl.pathname = upath.relative(option.workspaceDir, option.input); - return sourceUrl; - })() - : pathToFileURL(option.input)); + : pathToFileURL(option.input); + const sourceUrl = await (async () => { + if ( + inputUrl.protocol === 'file:' && + (option.httpServer || + // Use http server because http viewer cannot access to file protocol + (option.viewer && /^https?:/i.test(option.viewer))) + ) { + _sourceServer = + _sourceServer || (await launchServer(option.workspaceDir)); + + const sourceUrl = new URL('http://localhost'); + sourceUrl.port = `${_sourceServer.port}`; + sourceUrl.pathname = upath.relative( + option.workspaceDir, + fileURLToPath(inputUrl), + ); + return sourceUrl; + } + return inputUrl; + })(); return { viewerFullUrl: getViewerFullUrl(option, { diff --git a/tests/__snapshots__/config.test.ts.snap b/tests/__snapshots__/config.test.ts.snap index 28e10ede..417ca784 100644 --- a/tests/__snapshots__/config.test.ts.snap +++ b/tests/__snapshots__/config.test.ts.snap @@ -292,7 +292,7 @@ Object { }, "viewer": undefined, "viewerParam": undefined, - "webbookEntryPath": "https://vivliostyle.github.io/vivliostyle_doc/ja/vivliostyle-user-group-vol1/", + "webbookEntryUrl": "https://vivliostyle.github.io/vivliostyle_doc/ja/vivliostyle-user-group-vol1/", "workspaceDir": "__WORKSPACE__", } `; @@ -484,7 +484,7 @@ Object { }, "viewer": undefined, "viewerParam": undefined, - "webbookEntryPath": "__WORKSPACE__/tests/fixtures/config/sample.html", + "webbookEntryUrl": "file://__WORKSPACE__/tests/fixtures/config/sample.html", "workspaceDir": "__WORKSPACE__/tests/fixtures/config", } `; diff --git a/tests/__snapshots__/epub.test.ts.snap b/tests/__snapshots__/epub.test.ts.snap index 8f6d5a73..7c95c552 100644 --- a/tests/__snapshots__/epub.test.ts.snap +++ b/tests/__snapshots__/epub.test.ts.snap @@ -182,11 +182,6 @@ exports[`generate EPUB from single HTML with pub manifest > index.xhtml 1`] = ` <head> <title>Document -