@vivliostyle/cli 9.1.0 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,10 +25,10 @@ import {
25
25
  touchTmpFile,
26
26
  useTmpDirectory,
27
27
  writeFileIfChanged
28
- } from "./chunk-7BGVK3JL.js";
28
+ } from "./chunk-U63QI2TT.js";
29
29
  import {
30
30
  VivliostyleConfigSchema
31
- } from "./chunk-ASL7KKVE.js";
31
+ } from "./chunk-SEVGSYUZ.js";
32
32
  import {
33
33
  CONTAINER_LOCAL_HOSTNAME,
34
34
  CONTAINER_URL,
@@ -230,7 +230,7 @@ function mergeInlineConfig({ tasks, inlineOptions }, inlineConfig) {
230
230
  import { VFM } from "@vivliostyle/vfm";
231
231
  import { lookup as mime } from "mime-types";
232
232
  import fs3 from "node:fs";
233
- import { pathToFileURL as pathToFileURL2 } from "node:url";
233
+ import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "node:url";
234
234
  import npa from "npm-package-arg";
235
235
  import upath2 from "upath";
236
236
 
@@ -351,6 +351,7 @@ function parseTheme({
351
351
  name,
352
352
  specifier: resolvedSpecifier,
353
353
  location: upath2.join(themesDir, "node_modules", name),
354
+ registry: Boolean(parsed.registry),
354
355
  importPath
355
356
  };
356
357
  }
@@ -399,6 +400,26 @@ function parseFileMetadata({
399
400
  }
400
401
  return { title, themes };
401
402
  }
403
+ function parseCustomStyle({
404
+ customStyle,
405
+ entryContextDir
406
+ }) {
407
+ if (isValidUri(customStyle)) {
408
+ return customStyle;
409
+ }
410
+ const stylePath = upath2.resolve(entryContextDir, customStyle);
411
+ if (!pathContains(entryContextDir, stylePath)) {
412
+ throw Error(
413
+ `Custom style file ${customStyle} is not in ${entryContextDir}. Make sure the file is located in the context directory or a subdirectory.`
414
+ );
415
+ }
416
+ if (!fs3.existsSync(stylePath)) {
417
+ throw new Error(`Custom style file not found: ${customStyle}`);
418
+ }
419
+ return pathToFileURL2(stylePath).href.slice(
420
+ pathToFileURL2(entryContextDir).href.replace(/\/$/, "").length + 1
421
+ );
422
+ }
402
423
  function resolveTaskConfig(config, options) {
403
424
  const context = options.cwd ?? cwd;
404
425
  Logger.debug("resolveTaskConfig > context %s", context);
@@ -410,8 +431,6 @@ function resolveTaskConfig(config, options) {
410
431
  const bleed = options.bleed;
411
432
  const cropOffset = options.cropOffset;
412
433
  const css = options.css;
413
- const customStyle = options.style && (isValidUri(options.style) ? options.style : pathToFileURL2(options.style).href);
414
- const customUserStyle = options.userStyle && (isValidUri(options.userStyle) ? options.userStyle : pathToFileURL2(options.userStyle).href);
415
434
  const singleDoc = options.singleDoc ?? false;
416
435
  const quick = options.quick ?? false;
417
436
  const temporaryFilePrefix = config.temporaryFilePrefix ?? `.vs-${Date.now()}.`;
@@ -443,12 +462,14 @@ function resolveTaskConfig(config, options) {
443
462
  const staticRoutes = config.static ?? {};
444
463
  const viteConfig = config.vite;
445
464
  const viteConfigFile = config.viteConfigFile ?? true;
465
+ const customStyle = options.style && parseCustomStyle({ customStyle: options.style, entryContextDir }) || void 0;
466
+ const customUserStyle = options.userStyle && parseCustomStyle({ customStyle: options.userStyle, entryContextDir }) || void 0;
446
467
  const outputs = (() => {
447
468
  const defaultPdfOptions = {
448
469
  format: "pdf",
449
- renderMode: "local",
450
- preflight: config.pressReady ? "press-ready" : void 0,
451
- preflightOption: []
470
+ renderMode: options.renderMode ?? "local",
471
+ preflight: options.preflight ?? (config.pressReady ? "press-ready" : void 0),
472
+ preflightOption: options.preflightOption ?? []
452
473
  };
453
474
  if (config.output) {
454
475
  return config.output.map((target) => {
@@ -490,18 +511,23 @@ function resolveTaskConfig(config, options) {
490
511
  })();
491
512
  const { server, rootUrl } = (() => {
492
513
  let host = config.server?.host ?? false;
514
+ let allowedHosts = config.server?.allowedHosts || [];
493
515
  const port = config.server?.port ?? 13e3;
494
516
  if (outputs.some(
495
517
  (target) => target.format === "pdf" && target.renderMode === "docker"
496
- )) {
518
+ ) && !isInContainer()) {
497
519
  host = true;
520
+ if (Array.isArray(allowedHosts) && !allowedHosts.includes(CONTAINER_LOCAL_HOSTNAME)) {
521
+ allowedHosts.push(CONTAINER_LOCAL_HOSTNAME);
522
+ }
498
523
  }
499
- const rootHostname = isInContainer() ? CONTAINER_LOCAL_HOSTNAME : !host ? "localhost" : host === true ? "0.0.0.0" : host;
524
+ const rootHostname = !host ? "localhost" : host === true ? "0.0.0.0" : host;
500
525
  return {
501
526
  server: {
502
527
  host,
503
528
  port,
504
- proxy: config.server?.proxy ?? {}
529
+ proxy: config.server?.proxy ?? {},
530
+ allowedHosts
505
531
  },
506
532
  rootUrl: `http://${rootHostname}:${port}`
507
533
  };
@@ -620,12 +646,19 @@ function resolveSingleInputConfig({
620
646
  const author = config?.author;
621
647
  const entries = [];
622
648
  const exportAliases = [];
649
+ let isLocalResource = true;
623
650
  if (isValidUri(input.entry)) {
624
- sourcePath = input.entry;
625
- serverRootDir = UseTemporaryServerRoot;
626
- workspaceDir = context;
651
+ const url = new URL(input.entry);
652
+ if (url.protocol === "file:") {
653
+ sourcePath = fileURLToPath(url);
654
+ } else {
655
+ isLocalResource = false;
656
+ sourcePath = input.entry;
657
+ }
627
658
  } else {
628
659
  sourcePath = upath2.resolve(context, input.entry);
660
+ }
661
+ if (isLocalResource) {
629
662
  statFileSync(sourcePath);
630
663
  switch (input.format) {
631
664
  case "webbook":
@@ -648,6 +681,9 @@ function resolveSingleInputConfig({
648
681
  return input.format;
649
682
  }
650
683
  serverRootDir = workspaceDir;
684
+ } else {
685
+ serverRootDir = UseTemporaryServerRoot;
686
+ workspaceDir = context;
651
687
  }
652
688
  const themesDir = upath2.resolve(workspaceDir, "themes");
653
689
  if (input.format === "markdown") {
@@ -713,9 +749,6 @@ function resolveSingleInputConfig({
713
749
  let webbookPath;
714
750
  if (isValidUri(sourcePath)) {
715
751
  const url = new URL(sourcePath);
716
- if (/^https?:/i.test(url.protocol) && !url.pathname.endsWith("/") && !/\.\w+$/.test(url.pathname)) {
717
- url.pathname = `${url.pathname}/`;
718
- }
719
752
  webbookEntryUrl = url.href;
720
753
  } else {
721
754
  const rootFileUrl = pathToFileURL2(workspaceDir).href;
@@ -1169,10 +1202,9 @@ import upath7 from "upath";
1169
1202
  import { copy as copy3, move } from "fs-extra/esm";
1170
1203
  import fs8 from "node:fs";
1171
1204
  import picomatch from "picomatch";
1172
- import prettier from "prettier";
1173
- import parserHtml from "prettier/parser-html";
1174
1205
  import { glob as glob2 } from "tinyglobby";
1175
1206
  import upath6 from "upath";
1207
+ import serializeToXml2 from "w3c-xmlserializer";
1176
1208
  import MIMEType2 from "whatwg-mimetype";
1177
1209
 
1178
1210
  // src/output/webbook.ts
@@ -1190,7 +1222,7 @@ import jsdom, {
1190
1222
  } from "@vivliostyle/jsdom";
1191
1223
  import DOMPurify from "dompurify";
1192
1224
  import { toHtml } from "hast-util-to-html";
1193
- import { fileURLToPath, pathToFileURL as pathToFileURL3 } from "node:url";
1225
+ import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL3 } from "node:url";
1194
1226
  import upath3 from "upath";
1195
1227
  import MIMEType from "whatwg-mimetype";
1196
1228
  import { jsx, jsxs } from "hastscript/jsx-runtime";
@@ -1228,7 +1260,8 @@ var htmlPurify = DOMPurify(
1228
1260
  // @ts-expect-error: jsdom.DOMWindow should have trustedTypes property
1229
1261
  new JSDOM("").window
1230
1262
  );
1231
- var ResourceLoader = class extends BaseResourceLoader {
1263
+ var ResourceLoader = class _ResourceLoader extends BaseResourceLoader {
1264
+ static dataUrlOrigin = "http://localhost/";
1232
1265
  fetcherMap = /* @__PURE__ */ new Map();
1233
1266
  fetch(url, options) {
1234
1267
  Logger.debug(`[JSDOM] Fetching resource: ${url}`);
@@ -1244,7 +1277,7 @@ var ResourceLoader = class extends BaseResourceLoader {
1244
1277
  outputDir,
1245
1278
  onError
1246
1279
  }) {
1247
- const rootHref = /^https?:/i.test(new URL(rootUrl).protocol) ? new URL("/", rootUrl).href : new URL(".", rootUrl).href;
1280
+ const rootHref = rootUrl.startsWith("data:") ? _ResourceLoader.dataUrlOrigin : /^https?:/i.test(rootUrl) ? new URL("/", rootUrl).href : new URL(".", rootUrl).href;
1248
1281
  const normalizeToLocalPath = (urlString, mimeType) => {
1249
1282
  let url = new URL(urlString);
1250
1283
  url.hash = "";
@@ -1295,13 +1328,39 @@ async function getJsdomFromUrlOrFile({
1295
1328
  });
1296
1329
  } else if (url.protocol === "file:") {
1297
1330
  if (resourceLoader) {
1298
- const file = resourceLoader._readFile(fileURLToPath(url));
1331
+ const file = resourceLoader._readFile(fileURLToPath2(url));
1299
1332
  resourceLoader.fetcherMap.set(url.href, file);
1300
1333
  }
1301
- dom = await JSDOM.fromFile(fileURLToPath(url), {
1334
+ dom = await JSDOM.fromFile(fileURLToPath2(url), {
1335
+ virtualConsole,
1336
+ resources: resourceLoader,
1337
+ contentType: src.endsWith(".xhtml") || src.endsWith(".xml") ? "application/xhtml+xml; charset=UTF-8" : "text/html; charset=UTF-8"
1338
+ });
1339
+ } else if (url.protocol === "data:") {
1340
+ const [head, body] = url.href.split(",", 2);
1341
+ const data = decodeURIComponent(body);
1342
+ const buffer = Buffer.from(
1343
+ data,
1344
+ /;base64$/i.test(head) ? "base64" : "utf8"
1345
+ );
1346
+ const dummyUrl = `${ResourceLoader.dataUrlOrigin}index.html`;
1347
+ if (resourceLoader) {
1348
+ let timeoutId;
1349
+ const promise = new Promise((resolve) => {
1350
+ timeoutId = setTimeout(resolve, 0, buffer);
1351
+ });
1352
+ promise.abort = () => {
1353
+ if (timeoutId !== void 0) {
1354
+ clearTimeout(timeoutId);
1355
+ }
1356
+ };
1357
+ resourceLoader.fetcherMap.set(dummyUrl, promise);
1358
+ }
1359
+ dom = new JSDOM(buffer.toString(), {
1302
1360
  virtualConsole,
1303
1361
  resources: resourceLoader,
1304
- contentType: "text/html; charset=UTF-8"
1362
+ contentType: "text/html; charset=UTF-8",
1363
+ url: dummyUrl
1305
1364
  });
1306
1365
  } else {
1307
1366
  throw new Error(`Unsupported protocol: ${url.protocol}`);
@@ -2429,8 +2488,13 @@ async function retrieveWebbookEntry({
2429
2488
  resourceLoader,
2430
2489
  baseUrl: webbookEntryUrl
2431
2490
  }) || {};
2432
- const rootUrl = /^https?:/i.test(webbookEntryUrl) ? new URL("/", webbookEntryUrl).href : new URL(".", webbookEntryUrl).href;
2433
- const pathContains2 = (url) => !upath5.relative(rootUrl, url).startsWith("..");
2491
+ let pathContains2;
2492
+ if (webbookEntryUrl.startsWith("data:")) {
2493
+ pathContains2 = (url) => false;
2494
+ } else {
2495
+ const rootUrl = /^https?:/i.test(webbookEntryUrl) ? new URL("/", webbookEntryUrl).href : new URL(".", webbookEntryUrl).href;
2496
+ pathContains2 = (url) => !upath5.relative(rootUrl, url).startsWith("..");
2497
+ }
2434
2498
  const retriever = new Map(resourceLoader.fetcherMap);
2435
2499
  if (manifest && manifestUrl) {
2436
2500
  [manifest.resources || []].flat().forEach((v2) => {
@@ -2498,7 +2562,11 @@ async function retrieveWebbookEntry({
2498
2562
  manifest && JSON.stringify(manifest, null, 2)
2499
2563
  );
2500
2564
  return {
2501
- entryHtmlFile: upath5.join(outputDir, entryHtml),
2565
+ entryHtmlFile: upath5.join(
2566
+ outputDir,
2567
+ entryHtml,
2568
+ ...upath5.extname(entryHtml) ? [] : ["index.html"]
2569
+ ),
2502
2570
  manifest
2503
2571
  };
2504
2572
  }
@@ -2556,7 +2624,9 @@ async function copyWebPublicationAssets({
2556
2624
  manifestPath,
2557
2625
  input,
2558
2626
  outputDir,
2559
- entries
2627
+ entries,
2628
+ customStyle,
2629
+ customUserStyle
2560
2630
  }) {
2561
2631
  const relExportAliases = exportAliases.map(({ source, target }) => ({
2562
2632
  source: upath5.relative(input, source),
@@ -2568,7 +2638,9 @@ async function copyWebPublicationAssets({
2568
2638
  cwd: input,
2569
2639
  outputs,
2570
2640
  themesDir,
2571
- entries
2641
+ entries,
2642
+ customStyle,
2643
+ customUserStyle
2572
2644
  }),
2573
2645
  ...await glob(
2574
2646
  [
@@ -3011,10 +3083,13 @@ async function transformManuscript(entry, {
3011
3083
  styleOptions: coverEntry
3012
3084
  });
3013
3085
  }
3014
- const html = await prettier.format(content.serialize(), {
3015
- parser: "html",
3016
- plugins: [parserHtml]
3017
- });
3086
+ let html;
3087
+ if (content.window.document.contentType === "application/xhtml+xml") {
3088
+ html = `${XML_DECLARATION}
3089
+ ${serializeToXml2(content.window.document)}`;
3090
+ } else {
3091
+ html = content.serialize();
3092
+ }
3018
3093
  const htmlBuffer = Buffer.from(html, "utf8");
3019
3094
  if (!source || source.type === "file" && !pathEquals(source.pathname, entry.target)) {
3020
3095
  writeFileIfChanged(entry.target, htmlBuffer);
@@ -3110,6 +3185,8 @@ function getAssetMatcherSettings({
3110
3185
  outputs,
3111
3186
  themesDir,
3112
3187
  entries,
3188
+ customStyle,
3189
+ customUserStyle,
3113
3190
  cwd: cwd2,
3114
3191
  ignore = []
3115
3192
  }) {
@@ -3133,7 +3210,12 @@ function getAssetMatcherSettings({
3133
3210
  // Step 2: Glob files matched with `includes`
3134
3211
  // Ignore only files matched `excludes`
3135
3212
  {
3136
- patterns: includes,
3213
+ patterns: [
3214
+ ...includes,
3215
+ // Copy custom (user) style if specified
3216
+ customStyle,
3217
+ customUserStyle
3218
+ ].filter((s) => Boolean(s)),
3137
3219
  ignore: ignorePatterns
3138
3220
  }
3139
3221
  ];
@@ -3164,7 +3246,9 @@ async function copyAssets({
3164
3246
  copyAsset,
3165
3247
  outputs,
3166
3248
  themesDir,
3167
- entries
3249
+ entries,
3250
+ customStyle,
3251
+ customUserStyle
3168
3252
  }) {
3169
3253
  if (pathEquals(entryContextDir, workspaceDir)) {
3170
3254
  return;
@@ -3176,6 +3260,8 @@ async function copyAssets({
3176
3260
  outputs,
3177
3261
  themesDir,
3178
3262
  entries,
3263
+ customStyle,
3264
+ customUserStyle,
3179
3265
  ignore: [
3180
3266
  // don't copy workspace itself
3181
3267
  ...relWorkspaceDir ? [upath6.join(relWorkspaceDir, "**")] : []
@@ -3276,7 +3362,7 @@ function vsDevServerPlugin({
3276
3362
  let server;
3277
3363
  let program;
3278
3364
  const transformCache = /* @__PURE__ */ new Map();
3279
- const projectDeps = /* @__PURE__ */ new Set();
3365
+ let matchProjectDep;
3280
3366
  async function reload(forceUpdate = false) {
3281
3367
  const prevConfig = config;
3282
3368
  config = await reloadConfig(prevConfig, inlineConfig, server?.config);
@@ -3319,14 +3405,34 @@ function vsDevServerPlugin({
3319
3405
  serveAssetsMatcher
3320
3406
  };
3321
3407
  const configPath = locateVivliostyleConfig(inlineConfig);
3408
+ const projectDeps = [];
3322
3409
  if (configPath) {
3323
- projectDeps.add(configPath);
3410
+ projectDeps.push(configPath);
3324
3411
  server?.watcher.add(configPath);
3325
3412
  }
3326
3413
  if (config.viewerInput.type === "webpub") {
3327
- projectDeps.add(config.viewerInput.manifestPath);
3414
+ projectDeps.push(config.viewerInput.manifestPath);
3328
3415
  server?.watcher.add(config.viewerInput.manifestPath);
3329
3416
  }
3417
+ const flattenWatchTarget = (themes) => [...themes].flatMap((theme) => {
3418
+ if (theme.type === "file") {
3419
+ return [theme.source];
3420
+ }
3421
+ if (theme.type === "package" && !theme.registry) {
3422
+ return [theme.specifier];
3423
+ }
3424
+ return [];
3425
+ });
3426
+ const prevThemeFiles = flattenWatchTarget(prevConfig.themeIndexes);
3427
+ const themeFiles = flattenWatchTarget(config.themeIndexes);
3428
+ server?.watcher.unwatch(
3429
+ prevThemeFiles.filter((target) => !themeFiles.includes(target))
3430
+ );
3431
+ server?.watcher.add(themeFiles);
3432
+ projectDeps.push(...themeFiles);
3433
+ matchProjectDep = (pathname) => projectDeps.some(
3434
+ (dep) => pathEquals(dep, pathname) || pathContains(dep, pathname)
3435
+ );
3330
3436
  }
3331
3437
  async function transform(entry, config2, host) {
3332
3438
  const rootUrl = host ? `${server?.config.server.https ? "https" : "http"}://${host}` : config2.rootUrl;
@@ -3343,7 +3449,7 @@ function vsDevServerPlugin({
3343
3449
  }
3344
3450
  return { content: html, etag };
3345
3451
  } catch (error) {
3346
- console.error(getFormattedError(error));
3452
+ server?.config.logger.error(getFormattedError(error));
3347
3453
  transformCache.delete(entry.target);
3348
3454
  return;
3349
3455
  }
@@ -3446,7 +3552,7 @@ function vsDevServerPlugin({
3446
3552
  configureServer(viteServer) {
3447
3553
  server = viteServer;
3448
3554
  const handleUpdate = async (pathname) => {
3449
- if (!projectDeps.has(pathname)) {
3555
+ if (!matchProjectDep?.(pathname)) {
3450
3556
  return;
3451
3557
  }
3452
3558
  await reload();
@@ -3596,7 +3702,8 @@ function getViewerParams(src, {
3596
3702
  customUserStyle,
3597
3703
  singleDoc,
3598
3704
  quick,
3599
- viewerParam
3705
+ viewerParam,
3706
+ base
3600
3707
  }) {
3601
3708
  const pageSizeValue = size && ("format" in size ? size.format : `${size.width} ${size.height}`);
3602
3709
  function escapeParam(url) {
@@ -3605,10 +3712,12 @@ function getViewerParams(src, {
3605
3712
  let viewerParams = src ? `src=${escapeParam(src)}` : "";
3606
3713
  viewerParams += `&bookMode=${!singleDoc}&renderAllPages=${!quick}`;
3607
3714
  if (customStyle) {
3608
- viewerParams += `&style=${escapeParam(customStyle)}`;
3715
+ const param = isValidUri(customStyle) ? customStyle : upath10.posix.join(base, customStyle);
3716
+ viewerParams += `&style=${escapeParam(param)}`;
3609
3717
  }
3610
3718
  if (customUserStyle) {
3611
- viewerParams += `&userStyle=${escapeParam(customUserStyle)}`;
3719
+ const param = isValidUri(customUserStyle) ? customUserStyle : upath10.posix.join(base, customUserStyle);
3720
+ viewerParams += `&userStyle=${escapeParam(param)}`;
3612
3721
  }
3613
3722
  if (pageSizeValue || cropMarks || bleed || cropOffset || css) {
3614
3723
  let pageStyle = "@page{";
@@ -3683,7 +3792,7 @@ async function getViewerFullUrl({
3683
3792
  });
3684
3793
  const viewerParams = getViewerParams(
3685
3794
  sourceUrl === EMPTY_DATA_URI ? void 0 : sourceUrl,
3686
- config
3795
+ { base, ...config }
3687
3796
  );
3688
3797
  viewerUrl.hash = "";
3689
3798
  return `${viewerUrl.href}#${viewerParams}`;
@@ -3800,4 +3909,4 @@ export {
3800
3909
  getViewerFullUrl,
3801
3910
  createViteServer
3802
3911
  };
3803
- //# sourceMappingURL=chunk-KSJHCVYG.js.map
3912
+ //# sourceMappingURL=chunk-XYOW6HSN.js.map