@vivliostyle/cli 9.0.0-next.2 → 9.0.0-next.4

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.
Files changed (186) hide show
  1. package/dist/chunk-BIEQXUOY.js +50 -0
  2. package/dist/chunk-DMARNPN5.js +1073 -0
  3. package/dist/chunk-DMGAKSPH.js +590 -0
  4. package/dist/{core/init.js → chunk-E7QOXL6M.js} +39 -26
  5. package/dist/chunk-EX7EA34F.js +64 -0
  6. package/dist/chunk-LASMQBPP.js +85 -0
  7. package/dist/chunk-QC3BOE2G.js +74 -0
  8. package/dist/chunk-VBEHGS67.js +4990 -0
  9. package/dist/chunk-WX6JHPSL.js +16 -0
  10. package/dist/chunk-Y3ETBU5J.js +54 -0
  11. package/dist/cli.d.ts +0 -2
  12. package/dist/cli.js +17 -18
  13. package/dist/commands/build.d.ts +2 -2
  14. package/dist/commands/build.js +165 -17
  15. package/dist/commands/init.d.ts +2 -2
  16. package/dist/commands/init.js +35 -12
  17. package/dist/commands/preview.d.ts +2 -2
  18. package/dist/commands/preview.js +98 -12
  19. package/dist/config/schema.d.ts +1707 -1671
  20. package/dist/config/schema.js +50 -575
  21. package/dist/index.d.ts +116 -7
  22. package/dist/index.js +38 -42
  23. package/dist/node-modules.d.ts +21 -0
  24. package/dist/node-modules.js +9 -0
  25. package/dist/vite-adapter.d.ts +8 -2
  26. package/dist/vite-adapter.js +11 -28
  27. package/package.json +34 -15
  28. package/dist/browser.d.ts +0 -19
  29. package/dist/browser.d.ts.map +0 -1
  30. package/dist/browser.js +0 -154
  31. package/dist/browser.js.map +0 -1
  32. package/dist/cli.d.ts.map +0 -1
  33. package/dist/cli.js.map +0 -1
  34. package/dist/commands/build.d.ts.map +0 -1
  35. package/dist/commands/build.js.map +0 -1
  36. package/dist/commands/build.parser.d.ts +0 -3
  37. package/dist/commands/build.parser.d.ts.map +0 -1
  38. package/dist/commands/build.parser.js +0 -102
  39. package/dist/commands/build.parser.js.map +0 -1
  40. package/dist/commands/cli-flags.d.ts +0 -42
  41. package/dist/commands/cli-flags.d.ts.map +0 -1
  42. package/dist/commands/cli-flags.js +0 -58
  43. package/dist/commands/cli-flags.js.map +0 -1
  44. package/dist/commands/init.d.ts.map +0 -1
  45. package/dist/commands/init.js.map +0 -1
  46. package/dist/commands/init.parser.d.ts +0 -3
  47. package/dist/commands/init.parser.d.ts.map +0 -1
  48. package/dist/commands/init.parser.js +0 -17
  49. package/dist/commands/init.parser.js.map +0 -1
  50. package/dist/commands/preview.d.ts.map +0 -1
  51. package/dist/commands/preview.js.map +0 -1
  52. package/dist/commands/preview.parser.d.ts +0 -3
  53. package/dist/commands/preview.parser.d.ts.map +0 -1
  54. package/dist/commands/preview.parser.js +0 -50
  55. package/dist/commands/preview.parser.js.map +0 -1
  56. package/dist/config/load.d.ts +0 -4
  57. package/dist/config/load.d.ts.map +0 -1
  58. package/dist/config/load.js +0 -75
  59. package/dist/config/load.js.map +0 -1
  60. package/dist/config/merge.d.ts +0 -4
  61. package/dist/config/merge.d.ts.map +0 -1
  62. package/dist/config/merge.js +0 -55
  63. package/dist/config/merge.js.map +0 -1
  64. package/dist/config/resolve.d.ts +0 -205
  65. package/dist/config/resolve.d.ts.map +0 -1
  66. package/dist/config/resolve.js +0 -770
  67. package/dist/config/resolve.js.map +0 -1
  68. package/dist/config/schema.d.ts.map +0 -1
  69. package/dist/config/schema.js.map +0 -1
  70. package/dist/config/vite.d.ts +0 -6
  71. package/dist/config/vite.d.ts.map +0 -1
  72. package/dist/config/vite.js +0 -47
  73. package/dist/config/vite.js.map +0 -1
  74. package/dist/const.d.ts +0 -20
  75. package/dist/const.d.ts.map +0 -1
  76. package/dist/const.js +0 -41
  77. package/dist/const.js.map +0 -1
  78. package/dist/container.d.ts +0 -21
  79. package/dist/container.d.ts.map +0 -1
  80. package/dist/container.js +0 -175
  81. package/dist/container.js.map +0 -1
  82. package/dist/core/build.d.ts +0 -3
  83. package/dist/core/build.d.ts.map +0 -1
  84. package/dist/core/build.js +0 -167
  85. package/dist/core/build.js.map +0 -1
  86. package/dist/core/init.d.ts +0 -3
  87. package/dist/core/init.d.ts.map +0 -1
  88. package/dist/core/init.js.map +0 -1
  89. package/dist/core/preview.d.ts +0 -4
  90. package/dist/core/preview.d.ts.map +0 -1
  91. package/dist/core/preview.js +0 -113
  92. package/dist/core/preview.js.map +0 -1
  93. package/dist/index.d.ts.map +0 -1
  94. package/dist/index.js.map +0 -1
  95. package/dist/logger.d.ts +0 -23
  96. package/dist/logger.d.ts.map +0 -1
  97. package/dist/logger.js +0 -162
  98. package/dist/logger.js.map +0 -1
  99. package/dist/output/epub.d.ts +0 -10
  100. package/dist/output/epub.d.ts.map +0 -1
  101. package/dist/output/epub.js +0 -505
  102. package/dist/output/epub.js.map +0 -1
  103. package/dist/output/pdf-postprocess.d.ts +0 -30
  104. package/dist/output/pdf-postprocess.d.ts.map +0 -1
  105. package/dist/output/pdf-postprocess.js +0 -270
  106. package/dist/output/pdf-postprocess.js.map +0 -1
  107. package/dist/output/pdf.d.ts +0 -6
  108. package/dist/output/pdf.d.ts.map +0 -1
  109. package/dist/output/pdf.js +0 -200
  110. package/dist/output/pdf.js.map +0 -1
  111. package/dist/output/webbook.d.ts +0 -45
  112. package/dist/output/webbook.d.ts.map +0 -1
  113. package/dist/output/webbook.js +0 -413
  114. package/dist/output/webbook.js.map +0 -1
  115. package/dist/processor/compile.d.ts +0 -30
  116. package/dist/processor/compile.d.ts.map +0 -1
  117. package/dist/processor/compile.js +0 -349
  118. package/dist/processor/compile.js.map +0 -1
  119. package/dist/processor/html.d.ts +0 -107
  120. package/dist/processor/html.d.ts.map +0 -1
  121. package/dist/processor/html.js +0 -494
  122. package/dist/processor/html.js.map +0 -1
  123. package/dist/processor/markdown.d.ts +0 -12
  124. package/dist/processor/markdown.d.ts.map +0 -1
  125. package/dist/processor/markdown.js +0 -23
  126. package/dist/processor/markdown.js.map +0 -1
  127. package/dist/processor/theme.d.ts +0 -4
  128. package/dist/processor/theme.d.ts.map +0 -1
  129. package/dist/processor/theme.js +0 -41
  130. package/dist/processor/theme.js.map +0 -1
  131. package/dist/schema/pub-manifest.d.ts +0 -4
  132. package/dist/schema/pub-manifest.d.ts.map +0 -1
  133. package/dist/schema/pub-manifest.js +0 -41
  134. package/dist/schema/pub-manifest.js.map +0 -1
  135. package/dist/schema/publication.schema.d.ts +0 -104
  136. package/dist/schema/publication.schema.d.ts.map +0 -1
  137. package/dist/schema/publication.schema.js +0 -8
  138. package/dist/schema/publication.schema.js.map +0 -1
  139. package/dist/server.d.ts +0 -20
  140. package/dist/server.d.ts.map +0 -1
  141. package/dist/server.js +0 -117
  142. package/dist/server.js.map +0 -1
  143. package/dist/util.d.ts +0 -55
  144. package/dist/util.d.ts.map +0 -1
  145. package/dist/util.js +0 -269
  146. package/dist/util.js.map +0 -1
  147. package/dist/vite/plugin-util.d.ts +0 -6
  148. package/dist/vite/plugin-util.d.ts.map +0 -1
  149. package/dist/vite/plugin-util.js +0 -18
  150. package/dist/vite/plugin-util.js.map +0 -1
  151. package/dist/vite/vite-plugin-browser.d.ts +0 -8
  152. package/dist/vite/vite-plugin-browser.d.ts.map +0 -1
  153. package/dist/vite/vite-plugin-browser.js +0 -54
  154. package/dist/vite/vite-plugin-browser.js.map +0 -1
  155. package/dist/vite/vite-plugin-dev-server.d.ts +0 -8
  156. package/dist/vite/vite-plugin-dev-server.d.ts.map +0 -1
  157. package/dist/vite/vite-plugin-dev-server.js +0 -270
  158. package/dist/vite/vite-plugin-dev-server.js.map +0 -1
  159. package/dist/vite/vite-plugin-static-serve.d.ts +0 -8
  160. package/dist/vite/vite-plugin-static-serve.d.ts.map +0 -1
  161. package/dist/vite/vite-plugin-static-serve.js +0 -31
  162. package/dist/vite/vite-plugin-static-serve.js.map +0 -1
  163. package/dist/vite/vite-plugin-viewer.d.ts +0 -8
  164. package/dist/vite/vite-plugin-viewer.d.ts.map +0 -1
  165. package/dist/vite/vite-plugin-viewer.js +0 -52
  166. package/dist/vite/vite-plugin-viewer.js.map +0 -1
  167. package/dist/vite-adapter.d.ts.map +0 -1
  168. package/dist/vite-adapter.js.map +0 -1
  169. package/schemas/pub-manifest/README.md +0 -5
  170. package/schemas/pub-manifest/module/ItemList.schema.json +0 -32
  171. package/schemas/pub-manifest/module/bcp.schema.json +0 -7
  172. package/schemas/pub-manifest/module/context.schema.json +0 -62
  173. package/schemas/pub-manifest/module/contributor-object.schema.json +0 -42
  174. package/schemas/pub-manifest/module/contributor.schema.json +0 -26
  175. package/schemas/pub-manifest/module/date.schema.json +0 -7
  176. package/schemas/pub-manifest/module/duration.schema.json +0 -7
  177. package/schemas/pub-manifest/module/item-lists.schema.json +0 -16
  178. package/schemas/pub-manifest/module/language.schema.json +0 -16
  179. package/schemas/pub-manifest/module/link.schema.json +0 -60
  180. package/schemas/pub-manifest/module/localizable-object.schema.json +0 -15
  181. package/schemas/pub-manifest/module/localizable.schema.json +0 -26
  182. package/schemas/pub-manifest/module/resource.categorization.schema.json +0 -31
  183. package/schemas/pub-manifest/module/strings.schema.json +0 -9
  184. package/schemas/pub-manifest/module/url.schema.json +0 -7
  185. package/schemas/pub-manifest/module/urls.schema.json +0 -18
  186. package/schemas/pub-manifest/publication.schema.json +0 -123
@@ -0,0 +1,4990 @@
1
+ import {
2
+ COVER_HTML_FILENAME,
3
+ COVER_HTML_IMAGE_ALT,
4
+ EMPTY_DATA_URI,
5
+ EPUB_CONTAINER_XML,
6
+ EPUB_LANDMARKS_COVER_ENTRY,
7
+ EPUB_LANDMARKS_TITLE,
8
+ EPUB_LANDMARKS_TOC_ENTRY,
9
+ EPUB_NS,
10
+ EPUB_OUTPUT_VERSION,
11
+ MANIFEST_FILENAME,
12
+ TOC_FILENAME,
13
+ TOC_TITLE,
14
+ VIEWER_ROOT_PATH,
15
+ XML_DECLARATION,
16
+ cliVersion,
17
+ viewerRoot
18
+ } from "./chunk-EX7EA34F.js";
19
+ import {
20
+ importNodeModule
21
+ } from "./chunk-WX6JHPSL.js";
22
+ import {
23
+ VivliostyleConfigSchema,
24
+ VivliostyleInlineConfig
25
+ } from "./chunk-DMARNPN5.js";
26
+ import {
27
+ __callDispose,
28
+ __using
29
+ } from "./chunk-BIEQXUOY.js";
30
+
31
+ // src/util.ts
32
+ import { codeFrameColumns } from "@babel/code-frame";
33
+ import {
34
+ evaluate,
35
+ parse
36
+ } from "@humanwhocodes/momoa";
37
+ import AjvModule from "ajv";
38
+ import AjvFormatsModule from "ajv-formats";
39
+ import { XMLParser } from "fast-xml-parser";
40
+ import { removeSync } from "fs-extra/esm";
41
+ import StreamZip from "node-stream-zip";
42
+ import fs from "node:fs";
43
+ import readline from "node:readline";
44
+ import util from "node:util";
45
+ import tmp from "tmp";
46
+ import upath from "upath";
47
+ import { gray as gray2, red, redBright as redBright2 } from "yoctocolors";
48
+
49
+ // src/logger.ts
50
+ import debug from "debug";
51
+ import yoctoSpinner from "yocto-spinner";
52
+ import {
53
+ blueBright,
54
+ gray,
55
+ greenBright,
56
+ redBright,
57
+ yellowBright
58
+ } from "yoctocolors";
59
+ var isUnicodeSupported = process.platform !== "win32" || Boolean(process.env.WT_SESSION);
60
+ var randomBookSymbol = ["\u{1F4D5}", "\u{1F4D7}", "\u{1F4D8}", "\u{1F4D9}"][Math.floor(Math.random() * 4)];
61
+ var infoSymbol = blueBright("INFO");
62
+ var successSymbol = greenBright("SUCCESS");
63
+ var warnSymbol = yellowBright("WARN");
64
+ var errorSymbol = redBright("ERROR");
65
+ var Logger = class _Logger {
66
+ /**
67
+ * 0: silent 1: info 2: verbose 3: debug
68
+ */
69
+ static #logLevel = 0;
70
+ static #loggerInstance;
71
+ static #nonBlockingLogPrinted = false;
72
+ static #customLogger;
73
+ static debug = debug("vs-cli");
74
+ static get #spinner() {
75
+ return this.#loggerInstance && this.#loggerInstance.#_spinner;
76
+ }
77
+ static get isInteractive() {
78
+ return Boolean(
79
+ !this.#customLogger && process.stderr.isTTY && process.env.TERM !== "dumb" && !("CI" in process.env) && !import.meta.env?.VITEST && !debug.enabled("vs-cli") && // Prevent stream output in docker container so that not to spawn process
80
+ !isInContainer()
81
+ );
82
+ }
83
+ static startLogging(text) {
84
+ if (this.#logLevel === 0) {
85
+ return;
86
+ }
87
+ if (!this.isInteractive) {
88
+ this.logInfo(text);
89
+ return;
90
+ }
91
+ if (this.#loggerInstance) {
92
+ this.#loggerInstance.#_spinner.text = text;
93
+ return this.#loggerInstance;
94
+ }
95
+ this.#loggerInstance = new _Logger();
96
+ this.#loggerInstance.#_spinner.start(text);
97
+ return this.#loggerInstance;
98
+ }
99
+ static suspendLogging(text) {
100
+ if (this.#logLevel === 0) {
101
+ return;
102
+ }
103
+ if (!this.#spinner || !this.isInteractive) {
104
+ this.logInfo(text);
105
+ return;
106
+ }
107
+ const currentMsg = this.#spinner?.text;
108
+ this.logUpdate(currentMsg);
109
+ this.#spinner.stop(`${infoSymbol} ${text}
110
+ `);
111
+ return {
112
+ [Symbol.dispose]() {
113
+ if (_Logger.isInteractive) {
114
+ console.log("");
115
+ _Logger.#spinner?.start(currentMsg);
116
+ _Logger.#nonBlockingLogPrinted = true;
117
+ }
118
+ }
119
+ };
120
+ }
121
+ static log(...messages) {
122
+ if (this.#logLevel < 1) {
123
+ return;
124
+ }
125
+ console.log(...messages);
126
+ }
127
+ static logUpdate(...messages) {
128
+ if (!this.#spinner || !this.isInteractive) {
129
+ this.logInfo(...messages);
130
+ return;
131
+ }
132
+ this.#spinner.stop(
133
+ this.#nonBlockingLogPrinted ? void 0 : `${infoSymbol} ${this.#spinner.text}`
134
+ );
135
+ this.#spinner.start(messages.join(" "));
136
+ this.#nonBlockingLogPrinted = false;
137
+ }
138
+ static getMessage(message, symbol) {
139
+ return !this.#customLogger && symbol ? `${symbol} ${message}` : message;
140
+ }
141
+ static #nonBlockingLog(logMethod, message) {
142
+ if (!this.#spinner || !this.isInteractive) {
143
+ if (isInContainer()) {
144
+ message = `${gray("[Docker]")} ${message}`;
145
+ }
146
+ this.#logLevel >= 3 ? this.debug(message) : (this.#customLogger || console)[logMethod](message);
147
+ return;
148
+ }
149
+ this.logUpdate(this.#spinner.text);
150
+ this.#nonBlockingLogPrinted = true;
151
+ this.#spinner.stop(message);
152
+ this.#spinner.start();
153
+ }
154
+ static logSuccess(...messages) {
155
+ if (this.#logLevel < 1) {
156
+ return;
157
+ }
158
+ this.#nonBlockingLog(
159
+ "info",
160
+ this.getMessage(messages.join(" "), successSymbol)
161
+ );
162
+ }
163
+ static logError(...messages) {
164
+ if (this.#logLevel < 1) {
165
+ return;
166
+ }
167
+ this.#nonBlockingLog(
168
+ "error",
169
+ this.getMessage(messages.join(" "), errorSymbol)
170
+ );
171
+ }
172
+ static logWarn(...messages) {
173
+ if (this.#logLevel < 1) {
174
+ return;
175
+ }
176
+ this.#nonBlockingLog(
177
+ "warn",
178
+ this.getMessage(messages.join(" "), warnSymbol)
179
+ );
180
+ }
181
+ static logInfo(...messages) {
182
+ if (this.#logLevel < 1) {
183
+ return;
184
+ }
185
+ this.#nonBlockingLog(
186
+ "info",
187
+ this.getMessage(messages.join(" "), infoSymbol)
188
+ );
189
+ }
190
+ static logVerbose(...messages) {
191
+ if (this.#logLevel < 2) {
192
+ return;
193
+ }
194
+ this.#nonBlockingLog("info", this.getMessage(messages.join(" ")));
195
+ }
196
+ static setLogLevel(level) {
197
+ if (!level) {
198
+ return;
199
+ }
200
+ this.#logLevel = {
201
+ silent: 0,
202
+ info: 1,
203
+ verbose: 2,
204
+ debug: 3
205
+ }[level];
206
+ if (this.#logLevel >= 3) {
207
+ debug.enable("vs-cli");
208
+ }
209
+ }
210
+ static setCustomLogger(logger) {
211
+ this.#customLogger = logger;
212
+ }
213
+ #_spinner;
214
+ constructor() {
215
+ this.#_spinner = yoctoSpinner({
216
+ spinner: {
217
+ frames: isUnicodeSupported ? ["\u2581\u2581\u2571 ", "\u2581\u2551\u2581 ", "\u2572\u2581\u2581 ", "\u2581\u2581\u2581 ", "\u2581\u2581\u2581 ", "\u2581\u2581\u2581 "] : ["- ", "\\ ", "| ", "/ "],
218
+ interval: 80
219
+ },
220
+ color: "gray"
221
+ });
222
+ return this;
223
+ }
224
+ [Symbol.dispose]() {
225
+ this.#_spinner.stop(
226
+ _Logger.#nonBlockingLogPrinted ? void 0 : `${infoSymbol} ${this.#_spinner.text}`
227
+ );
228
+ _Logger.#loggerInstance = void 0;
229
+ }
230
+ };
231
+
232
+ // schemas/pub-manifest/module/bcp.schema.json
233
+ var bcp_schema_default = {
234
+ $schema: "http://json-schema.org/draft-07/schema#",
235
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/bcp.schema.json",
236
+ title: "BCP47 Language tag",
237
+ type: "string",
238
+ pattern: "^((?:(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?:([A-Za-z]{2,3}(-(?:[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?:[A-Za-z]{4}))?(-(?:[A-Za-z]{2}|[0-9]{3}))?(-(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?:[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?:x(-[A-Za-z0-9]{1,8})+))?)|(?:x(-[A-Za-z0-9]{1,8})+))$"
239
+ };
240
+
241
+ // schemas/pub-manifest/module/context.schema.json
242
+ var context_schema_default = {
243
+ $schema: "http://json-schema.org/draft-07/schema#",
244
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/context.schema.json",
245
+ title: "Publication Contexts",
246
+ type: "array",
247
+ items: [
248
+ {
249
+ const: "https://schema.org"
250
+ },
251
+ {
252
+ const: "https://www.w3.org/ns/pub-context"
253
+ }
254
+ ],
255
+ additionalItems: {
256
+ anyOf: [
257
+ {
258
+ type: "string"
259
+ },
260
+ {
261
+ type: "object",
262
+ properties: {
263
+ language: {
264
+ $ref: "bcp.schema.json"
265
+ },
266
+ direction: false
267
+ },
268
+ required: ["language"]
269
+ },
270
+ {
271
+ type: "object",
272
+ properties: {
273
+ direction: {
274
+ type: "string",
275
+ enum: ["ltr", "rtl"]
276
+ },
277
+ language: false
278
+ },
279
+ required: ["direction"]
280
+ },
281
+ {
282
+ type: "object",
283
+ properties: {
284
+ language: {
285
+ $ref: "bcp.schema.json"
286
+ },
287
+ direction: {
288
+ type: "string",
289
+ enum: ["ltr", "rtl"]
290
+ }
291
+ },
292
+ required: ["language", "direction"]
293
+ },
294
+ {
295
+ type: "object",
296
+ properties: {
297
+ language: false,
298
+ direction: false
299
+ }
300
+ }
301
+ ]
302
+ }
303
+ };
304
+
305
+ // schemas/pub-manifest/module/contributor-object.schema.json
306
+ var contributor_object_schema_default = {
307
+ $schema: "http://json-schema.org/draft-07/schema#",
308
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/contributor-object.schema.json",
309
+ title: "Contributor Object",
310
+ type: "object",
311
+ properties: {
312
+ name: {
313
+ $ref: "localizable.schema.json"
314
+ },
315
+ id: {
316
+ $ref: "url.schema.json"
317
+ },
318
+ type: {
319
+ oneOf: [
320
+ {
321
+ type: "string",
322
+ enum: ["Person", "Organization"],
323
+ default: "Person"
324
+ },
325
+ {
326
+ type: "array",
327
+ items: {
328
+ type: "string"
329
+ },
330
+ contains: {
331
+ enum: ["Person", "Organization"]
332
+ }
333
+ }
334
+ ]
335
+ },
336
+ url: {
337
+ $ref: "url.schema.json"
338
+ },
339
+ identifier: {
340
+ type: "array",
341
+ items: {
342
+ type: "string"
343
+ }
344
+ }
345
+ },
346
+ required: ["name"]
347
+ };
348
+
349
+ // schemas/pub-manifest/module/contributor.schema.json
350
+ var contributor_schema_default = {
351
+ $schema: "http://json-schema.org/draft-07/schema#",
352
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/contributor.schema.json",
353
+ title: "Contributor",
354
+ anyOf: [
355
+ {
356
+ type: "string"
357
+ },
358
+ {
359
+ type: "array",
360
+ items: {
361
+ anyOf: [
362
+ {
363
+ type: "string"
364
+ },
365
+ {
366
+ $ref: "contributor-object.schema.json"
367
+ }
368
+ ]
369
+ }
370
+ },
371
+ {
372
+ $ref: "contributor-object.schema.json"
373
+ }
374
+ ]
375
+ };
376
+
377
+ // schemas/pub-manifest/module/date.schema.json
378
+ var date_schema_default = {
379
+ $schema: "http://json-schema.org/draft-07/schema#",
380
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/date.schema.json",
381
+ title: "Dates",
382
+ type: "string",
383
+ format: "date-time"
384
+ };
385
+
386
+ // schemas/pub-manifest/module/duration.schema.json
387
+ var duration_schema_default = {
388
+ $schema: "http://json-schema.org/draft-07/schema#",
389
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/duration.schema.json",
390
+ title: "Duration",
391
+ type: "string",
392
+ pattern: "^P(?!$)((\\d+Y)|(\\d+\\.\\d+Y$))?((\\d+M)|(\\d+\\.\\d+M$))?((\\d+W)|(\\d+\\.\\d+W$))?((\\d+D)|(\\d+\\.\\d+D$))?(T(?=\\d)((\\d+H)|(\\d+\\.\\d+H$))?((\\d+M)|(\\d+\\.\\d+M$))?(\\d+(\\.\\d+)?S)?)??$"
393
+ };
394
+
395
+ // schemas/pub-manifest/module/item-lists.schema.json
396
+ var item_lists_schema_default = {
397
+ $schema: "http://json-schema.org/draft-07/schema#",
398
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/item-lists.schema.json",
399
+ title: "Lists of ItemList",
400
+ oneOf: [
401
+ {
402
+ $ref: "ItemList.schema.json"
403
+ },
404
+ {
405
+ type: "array",
406
+ items: {
407
+ $ref: "ItemList.schema.json"
408
+ }
409
+ }
410
+ ]
411
+ };
412
+
413
+ // schemas/pub-manifest/module/ItemList.schema.json
414
+ var ItemList_schema_default = {
415
+ $schema: "http://json-schema.org/draft-07/schema#",
416
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/ItemList.schema.json",
417
+ title: "schema.org ItemList object",
418
+ type: "object",
419
+ properties: {
420
+ type: {
421
+ oneOf: [
422
+ {
423
+ type: "string",
424
+ const: "ItemList"
425
+ },
426
+ {
427
+ type: "array",
428
+ items: {
429
+ type: "string"
430
+ },
431
+ contains: {
432
+ const: "ItemList"
433
+ }
434
+ }
435
+ ]
436
+ },
437
+ itemListElement: {
438
+ type: ["array"],
439
+ items: {
440
+ type: "string"
441
+ }
442
+ }
443
+ },
444
+ required: ["type", "itemListElement"]
445
+ };
446
+
447
+ // schemas/pub-manifest/module/language.schema.json
448
+ var language_schema_default = {
449
+ $schema: "http://json-schema.org/draft-07/schema#",
450
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/language.schema.json",
451
+ title: "Languages",
452
+ oneOf: [
453
+ {
454
+ $ref: "bcp.schema.json"
455
+ },
456
+ {
457
+ type: "array",
458
+ items: {
459
+ $ref: "bcp.schema.json"
460
+ }
461
+ }
462
+ ]
463
+ };
464
+
465
+ // schemas/pub-manifest/module/link.schema.json
466
+ var link_schema_default = {
467
+ $schema: "http://json-schema.org/draft-07/schema#",
468
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/link.schema.json",
469
+ title: "Publication Links",
470
+ type: "object",
471
+ properties: {
472
+ type: {
473
+ oneOf: [
474
+ {
475
+ type: "string",
476
+ const: "LinkedResource"
477
+ },
478
+ {
479
+ type: "array",
480
+ items: {
481
+ type: "string"
482
+ },
483
+ contains: {
484
+ const: "LinkedResource"
485
+ }
486
+ }
487
+ ]
488
+ },
489
+ url: {
490
+ $ref: "url.schema.json"
491
+ },
492
+ encodingFormat: {
493
+ type: "string"
494
+ },
495
+ name: {
496
+ $ref: "localizable.schema.json"
497
+ },
498
+ description: {
499
+ anyOf: [
500
+ {
501
+ type: "string"
502
+ },
503
+ {
504
+ $ref: "localizable-object.schema.json"
505
+ }
506
+ ]
507
+ },
508
+ rel: {
509
+ type: ["string", "array"],
510
+ items: {
511
+ type: "string"
512
+ }
513
+ },
514
+ integrity: {
515
+ type: "string"
516
+ },
517
+ duration: {
518
+ $ref: "duration.schema.json"
519
+ },
520
+ alternate: {
521
+ $ref: "resource.categorization.schema.json"
522
+ }
523
+ },
524
+ required: ["url"]
525
+ };
526
+
527
+ // schemas/pub-manifest/module/localizable-object.schema.json
528
+ var localizable_object_schema_default = {
529
+ $schema: "http://json-schema.org/draft-07/schema#",
530
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/localizable-object.schema.json",
531
+ title: "Localizable String Object",
532
+ type: "object",
533
+ properties: {
534
+ value: {
535
+ type: "string"
536
+ },
537
+ language: {
538
+ $ref: "bcp.schema.json"
539
+ }
540
+ },
541
+ required: ["value"]
542
+ };
543
+
544
+ // schemas/pub-manifest/module/localizable.schema.json
545
+ var localizable_schema_default = {
546
+ $schema: "http://json-schema.org/draft-07/schema#",
547
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/localizable.schema.json",
548
+ title: "Localizable String or Object",
549
+ anyOf: [
550
+ {
551
+ type: "string"
552
+ },
553
+ {
554
+ type: "array",
555
+ items: {
556
+ anyOf: [
557
+ {
558
+ type: "string"
559
+ },
560
+ {
561
+ $ref: "localizable-object.schema.json"
562
+ }
563
+ ]
564
+ }
565
+ },
566
+ {
567
+ $ref: "localizable-object.schema.json"
568
+ }
569
+ ]
570
+ };
571
+
572
+ // schemas/pub-manifest/module/resource.categorization.schema.json
573
+ var resource_categorization_schema_default = {
574
+ $schema: "http://json-schema.org/draft-07/schema#",
575
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/resource.categorization.schema.json",
576
+ title: "Resource Categorization",
577
+ oneOf: [
578
+ {
579
+ oneOf: [
580
+ {
581
+ $ref: "url.schema.json"
582
+ },
583
+ {
584
+ $ref: "link.schema.json"
585
+ }
586
+ ]
587
+ },
588
+ {
589
+ type: "array",
590
+ items: {
591
+ anyOf: [
592
+ {
593
+ $ref: "url.schema.json"
594
+ },
595
+ {
596
+ $ref: "link.schema.json"
597
+ }
598
+ ]
599
+ },
600
+ uniqueItems: true
601
+ }
602
+ ]
603
+ };
604
+
605
+ // schemas/pub-manifest/module/strings.schema.json
606
+ var strings_schema_default = {
607
+ $schema: "http://json-schema.org/draft-07/schema#",
608
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/strings.schema.json",
609
+ title: "Unique strings",
610
+ type: ["string", "array"],
611
+ items: {
612
+ type: "string"
613
+ }
614
+ };
615
+
616
+ // schemas/pub-manifest/module/url.schema.json
617
+ var url_schema_default = {
618
+ $schema: "http://json-schema.org/draft-07/schema#",
619
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/url.schema.json",
620
+ title: "URL",
621
+ type: "string",
622
+ format: "uri-reference"
623
+ };
624
+
625
+ // schemas/pub-manifest/module/urls.schema.json
626
+ var urls_schema_default = {
627
+ $schema: "http://json-schema.org/draft-07/schema#",
628
+ $id: "https://www.w3.org/ns/pub-schema/manifest/module/urls.schema.json",
629
+ title: "URLs",
630
+ oneOf: [
631
+ {
632
+ type: "string",
633
+ format: "uri-reference"
634
+ },
635
+ {
636
+ type: "array",
637
+ items: {
638
+ type: "string",
639
+ format: "uri-reference"
640
+ }
641
+ }
642
+ ]
643
+ };
644
+
645
+ // schemas/pub-manifest/publication.schema.json
646
+ var publication_schema_default = {
647
+ $schema: "http://json-schema.org/draft-07/schema#",
648
+ $id: "https://www.w3.org/ns/pub-schema/manifest/publication.schema.json",
649
+ title: "Publication Manifest",
650
+ type: "object",
651
+ properties: {
652
+ "@context": {
653
+ $ref: "module/context.schema.json"
654
+ },
655
+ type: {
656
+ type: ["string", "array"],
657
+ items: {
658
+ type: "string"
659
+ }
660
+ },
661
+ conformsTo: {
662
+ oneOf: [
663
+ {
664
+ $ref: "module/url.schema.json"
665
+ },
666
+ {
667
+ type: "array",
668
+ items: {
669
+ $ref: "module/url.schema.json"
670
+ }
671
+ }
672
+ ]
673
+ },
674
+ id: {
675
+ type: "string"
676
+ },
677
+ abridged: {
678
+ type: "boolean"
679
+ },
680
+ accessMode: {
681
+ $ref: "module/strings.schema.json"
682
+ },
683
+ accessModeSufficient: {
684
+ $ref: "module/item-lists.schema.json"
685
+ },
686
+ accessibilityFeature: {
687
+ $ref: "module/strings.schema.json"
688
+ },
689
+ accessibilityHazard: {
690
+ $ref: "module/strings.schema.json"
691
+ },
692
+ accessibilitySummary: {
693
+ $ref: "module/localizable.schema.json"
694
+ },
695
+ artist: {
696
+ $ref: "module/contributor.schema.json"
697
+ },
698
+ author: {
699
+ $ref: "module/contributor.schema.json"
700
+ },
701
+ colorist: {
702
+ $ref: "module/contributor.schema.json"
703
+ },
704
+ contributor: {
705
+ $ref: "module/contributor.schema.json"
706
+ },
707
+ creator: {
708
+ $ref: "module/contributor.schema.json"
709
+ },
710
+ editor: {
711
+ $ref: "module/contributor.schema.json"
712
+ },
713
+ illustrator: {
714
+ $ref: "module/contributor.schema.json"
715
+ },
716
+ inker: {
717
+ $ref: "module/contributor.schema.json"
718
+ },
719
+ letterer: {
720
+ $ref: "module/contributor.schema.json"
721
+ },
722
+ penciler: {
723
+ $ref: "module/contributor.schema.json"
724
+ },
725
+ publisher: {
726
+ $ref: "module/contributor.schema.json"
727
+ },
728
+ readBy: {
729
+ $ref: "module/contributor.schema.json"
730
+ },
731
+ translator: {
732
+ $ref: "module/contributor.schema.json"
733
+ },
734
+ url: {
735
+ $ref: "module/urls.schema.json"
736
+ },
737
+ duration: {
738
+ $ref: "module/duration.schema.json"
739
+ },
740
+ inLanguage: {
741
+ $ref: "module/language.schema.json"
742
+ },
743
+ dateModified: {
744
+ $ref: "module/date.schema.json"
745
+ },
746
+ datePublished: {
747
+ $ref: "module/date.schema.json"
748
+ },
749
+ name: {
750
+ $ref: "module/localizable.schema.json"
751
+ },
752
+ readingOrder: {
753
+ $ref: "module/resource.categorization.schema.json"
754
+ },
755
+ resources: {
756
+ $ref: "module/resource.categorization.schema.json"
757
+ },
758
+ links: {
759
+ $ref: "module/resource.categorization.schema.json"
760
+ },
761
+ readingProgression: {
762
+ type: "string",
763
+ enum: ["ltr", "rtl"],
764
+ default: "ltr"
765
+ }
766
+ },
767
+ required: ["@context", "conformsTo"]
768
+ };
769
+
770
+ // src/schema/pub-manifest.ts
771
+ var publicationSchemas = [
772
+ bcp_schema_default,
773
+ context_schema_default,
774
+ contributor_object_schema_default,
775
+ contributor_schema_default,
776
+ date_schema_default,
777
+ duration_schema_default,
778
+ item_lists_schema_default,
779
+ ItemList_schema_default,
780
+ language_schema_default,
781
+ link_schema_default,
782
+ localizable_object_schema_default,
783
+ localizable_schema_default,
784
+ resource_categorization_schema_default,
785
+ strings_schema_default,
786
+ url_schema_default,
787
+ urls_schema_default,
788
+ publication_schema_default
789
+ ];
790
+
791
+ // src/util.ts
792
+ var cwd = upath.normalize(process.cwd());
793
+ var beforeExitHandlers = [];
794
+ var registerExitHandler = (debugMessage, handler) => {
795
+ beforeExitHandlers.push(() => {
796
+ Logger.debug(debugMessage);
797
+ handler();
798
+ });
799
+ };
800
+ function runExitHandlers() {
801
+ while (beforeExitHandlers.length) {
802
+ try {
803
+ beforeExitHandlers.shift()?.();
804
+ } catch (e) {
805
+ }
806
+ }
807
+ }
808
+ var exitSignals = ["exit", "SIGINT", "SIGTERM"];
809
+ exitSignals.forEach((sig) => {
810
+ process.once(sig, (signal, exitCode) => {
811
+ runExitHandlers();
812
+ if (process.exitCode === void 0) {
813
+ process.exitCode = exitCode !== void 0 ? 128 + exitCode : Number(signal);
814
+ }
815
+ process.exit();
816
+ });
817
+ });
818
+ if (process.platform === "win32") {
819
+ const rl = readline.createInterface({
820
+ input: process.stdin,
821
+ output: process.stdout
822
+ });
823
+ rl.on("SIGINT", () => {
824
+ runExitHandlers();
825
+ process.exit(1);
826
+ });
827
+ registerExitHandler("Closing readline interface", () => {
828
+ rl.close();
829
+ });
830
+ }
831
+ var DetailError = class extends Error {
832
+ constructor(message, detail) {
833
+ super(message);
834
+ this.detail = detail;
835
+ }
836
+ };
837
+ function getFormattedError(err) {
838
+ return err instanceof DetailError ? `${err.message}
839
+ ${err.detail}` : err.stack || `${err.message}`;
840
+ }
841
+ function gracefulError(err) {
842
+ console.log(`${redBright2("ERROR")} ${getFormattedError(err)}
843
+
844
+ ${gray2("If you think this is a bug, please report at https://github.com/vivliostyle/vivliostyle-cli/issues")}`);
845
+ process.exit(1);
846
+ }
847
+ function readJSON(path) {
848
+ try {
849
+ return JSON.parse(fs.readFileSync(path, "utf8"));
850
+ } catch (err) {
851
+ return void 0;
852
+ }
853
+ }
854
+ function statFileSync(filePath, {
855
+ errorMessage = "Specified input does not exist"
856
+ } = {}) {
857
+ try {
858
+ return fs.statSync(filePath);
859
+ } catch (err) {
860
+ if (err.code === "ENOENT") {
861
+ throw new Error(`${errorMessage}: ${filePath}`);
862
+ }
863
+ throw err;
864
+ }
865
+ }
866
+ async function inflateZip(filePath, dest) {
867
+ return await new Promise((res, rej) => {
868
+ try {
869
+ const zip = new StreamZip({
870
+ file: filePath,
871
+ storeEntries: true
872
+ });
873
+ zip.on("error", (err) => {
874
+ rej(err);
875
+ });
876
+ zip.on("ready", async () => {
877
+ await util.promisify(zip.extract)(null, dest);
878
+ await util.promisify(zip.close)();
879
+ Logger.debug(`Unzipped ${filePath} to ${dest}`);
880
+ res();
881
+ });
882
+ } catch (err) {
883
+ rej(err);
884
+ }
885
+ });
886
+ }
887
+ function useTmpDirectory() {
888
+ return new Promise((res, rej) => {
889
+ tmp.dir({ unsafeCleanup: true }, (err, path, clear) => {
890
+ if (err) {
891
+ return rej(err);
892
+ }
893
+ Logger.debug(`Created the temporary directory: ${path}`);
894
+ if (import.meta.env?.VITEST) {
895
+ return res([path, () => {
896
+ }]);
897
+ }
898
+ const callback = () => {
899
+ removeSync(path);
900
+ };
901
+ registerExitHandler(
902
+ `Removing the temporary directory: ${path}`,
903
+ callback
904
+ );
905
+ res([path, callback]);
906
+ });
907
+ });
908
+ }
909
+ function touchTmpFile(path) {
910
+ fs.mkdirSync(upath.dirname(path), { recursive: true });
911
+ fs.closeSync(fs.openSync(path, "a"));
912
+ Logger.debug(`Created the temporary file: ${path}`);
913
+ const callback = () => {
914
+ removeSync(path);
915
+ };
916
+ registerExitHandler(`Removing the temporary file: ${path}`, callback);
917
+ return callback;
918
+ }
919
+ function pathEquals(path1, path2) {
920
+ return upath.relative(path1, path2) === "";
921
+ }
922
+ function pathContains(parentPath, childPath) {
923
+ const rel = upath.relative(parentPath, childPath);
924
+ return rel !== "" && !rel.startsWith("..");
925
+ }
926
+ function isValidUri(str) {
927
+ return /^(https?|file|data):/i.test(str);
928
+ }
929
+ function isInContainer() {
930
+ return fs.existsSync("/opt/vivliostyle-cli/.vs-cli-version");
931
+ }
932
+ async function openEpub(epubPath, tmpDir) {
933
+ await inflateZip(epubPath, tmpDir);
934
+ Logger.debug(`Created the temporary EPUB directory: ${tmpDir}`);
935
+ const deleteEpub = () => {
936
+ fs.rmSync(tmpDir, { recursive: true });
937
+ };
938
+ registerExitHandler(
939
+ `Removing the temporary EPUB directory: ${tmpDir}`,
940
+ deleteEpub
941
+ );
942
+ return deleteEpub;
943
+ }
944
+ function getDefaultEpubOpfPath(epubDir) {
945
+ const containerXmlPath = upath.join(epubDir, "META-INF/container.xml");
946
+ const xmlParser = new XMLParser({
947
+ ignoreAttributes: false
948
+ });
949
+ const { container } = xmlParser.parse(
950
+ fs.readFileSync(containerXmlPath, "utf8")
951
+ );
952
+ const rootfile = [container.rootfiles.rootfile].flat()[0];
953
+ const epubOpfPath = upath.join(epubDir, rootfile["@_full-path"]);
954
+ return epubOpfPath;
955
+ }
956
+ function getEpubRootDir(epubOpfPath) {
957
+ function traverse(dir) {
958
+ const files = fs.readdirSync(dir);
959
+ if (files.includes("META-INF") && pathEquals(epubOpfPath, getDefaultEpubOpfPath(dir))) {
960
+ return dir;
961
+ }
962
+ const next = upath.dirname(dir);
963
+ if (pathEquals(dir, next)) {
964
+ return;
965
+ }
966
+ return traverse(next);
967
+ }
968
+ return traverse(upath.dirname(epubOpfPath));
969
+ }
970
+ var Ajv = AjvModule.default;
971
+ var addFormats = AjvFormatsModule.default;
972
+ var getAjvValidatorFunction = (schema, refSchemas) => (obj) => {
973
+ const ajv = new Ajv({ strict: false });
974
+ addFormats(ajv);
975
+ if (refSchemas) {
976
+ ajv.addSchema(refSchemas);
977
+ }
978
+ const validate = ajv.compile(schema);
979
+ const valid = validate(obj);
980
+ if (!valid) {
981
+ throw validate.errors?.[0] || new Error();
982
+ }
983
+ return true;
984
+ };
985
+ var assertPubManifestSchema = getAjvValidatorFunction(
986
+ publication_schema_default,
987
+ publicationSchemas
988
+ );
989
+ function parseJsonc(rawJsonc) {
990
+ const ast = parse(rawJsonc, {
991
+ mode: "jsonc",
992
+ ranges: false,
993
+ tokens: false
994
+ });
995
+ return evaluate(ast);
996
+ }
997
+ function prettifySchemaError(rawJsonc, issues) {
998
+ const parsed = parse(rawJsonc, {
999
+ mode: "jsonc",
1000
+ ranges: false,
1001
+ tokens: false
1002
+ });
1003
+ function traverse(issues2, depth) {
1004
+ return issues2.flatMap((issue) => {
1005
+ const p = issue.path?.length || 0;
1006
+ if (!issue.issues) {
1007
+ return [[[issue], depth + p]];
1008
+ }
1009
+ return traverse(issue.issues, depth + p).map(([i, d]) => [
1010
+ [issue, ...i],
1011
+ d
1012
+ ]);
1013
+ });
1014
+ }
1015
+ const all = traverse(issues, 0);
1016
+ const maxDepth = Math.max(...all.map(([, d]) => d));
1017
+ const issuesTraversed = all.find(([, d]) => d === maxDepth)[0];
1018
+ let jsonValue = parsed.body;
1019
+ for (const p of issuesTraversed.flatMap((v3) => v3.path ?? [])) {
1020
+ let childValue;
1021
+ if (p.type === "object" && jsonValue.type === "Object") {
1022
+ childValue = jsonValue.members.find(
1023
+ (m) => m.name.type === "Identifier" && m.name.name === p.key || m.name.type === "String" && m.name.value === p.key
1024
+ )?.value;
1025
+ }
1026
+ if (p.type === "array" && jsonValue.type === "Array") {
1027
+ childValue = jsonValue.elements[p.key]?.value;
1028
+ }
1029
+ if (childValue) {
1030
+ jsonValue = childValue;
1031
+ } else {
1032
+ break;
1033
+ }
1034
+ }
1035
+ let message = `${red(issuesTraversed.at(-1).message)}`;
1036
+ if (jsonValue) {
1037
+ message += `
1038
+ ${codeFrameColumns(rawJsonc, jsonValue.loc, {
1039
+ highlightCode: true
1040
+ })}`;
1041
+ }
1042
+ return message;
1043
+ }
1044
+ function writeFileIfChanged(filePath, content) {
1045
+ if (!fs.existsSync(filePath) || !fs.readFileSync(filePath).equals(content)) {
1046
+ fs.mkdirSync(upath.dirname(filePath), { recursive: true });
1047
+ fs.writeFileSync(filePath, content);
1048
+ }
1049
+ }
1050
+
1051
+ // src/commands/cli-flags.ts
1052
+ import upath2 from "upath";
1053
+ import * as v from "valibot";
1054
+ function parseFlagsToInlineConfig(argv, setupProgram) {
1055
+ const program = setupProgram();
1056
+ program.parse(argv);
1057
+ let options = program.opts();
1058
+ const input = program.args?.[0];
1059
+ options = warnDeprecatedFlags(options);
1060
+ let inlineConfig = { input, ...options };
1061
+ if (input && !options.config && upath2.basename(input).startsWith("vivliostyle.config")) {
1062
+ inlineConfig = { config: input, ...options };
1063
+ }
1064
+ return v.parse(VivliostyleInlineConfig, inlineConfig);
1065
+ }
1066
+ function setupConfigFromFlags(flags) {
1067
+ if (!flags.input) {
1068
+ if (flags.enableViewerStartPage) {
1069
+ return {
1070
+ tasks: [{ entry: [] }],
1071
+ inlineOptions: {
1072
+ input: { format: "webbook", entry: EMPTY_DATA_URI }
1073
+ }
1074
+ };
1075
+ } else {
1076
+ throw new Error(
1077
+ "No input is set. Please set an appropriate entry or a Vivliostyle config file."
1078
+ );
1079
+ }
1080
+ }
1081
+ return {
1082
+ tasks: [{ entry: [] }],
1083
+ inlineOptions: {}
1084
+ };
1085
+ }
1086
+ function warnDeprecatedFlags(options) {
1087
+ const modifiedOptions = { ...options };
1088
+ if (options.executableChromium) {
1089
+ Logger.logWarn(
1090
+ "'--executable-chromium' option was deprecated and will be removed in a future release. Please replace with '--executable-browser' option."
1091
+ );
1092
+ modifiedOptions.executableBrowser = options.executableChromium;
1093
+ }
1094
+ if (options.verbose) {
1095
+ Logger.logWarn(
1096
+ "'--verbose' option was deprecated and will be removed in a future release. Please replace with '--log-level verbose' option."
1097
+ );
1098
+ modifiedOptions.logLevel = "verbose";
1099
+ }
1100
+ if (options.sandbox === false) {
1101
+ Logger.logWarn(
1102
+ "'--no-sandbox' option was deprecated and will be removed in a future release. It is no longer necessary because the sandbox is disabled by default."
1103
+ );
1104
+ }
1105
+ if (options.http) {
1106
+ Logger.logWarn(
1107
+ "'--http' option was deprecated and will be removed in a future release. It is unnecessary because the HTTP server starts automatically."
1108
+ );
1109
+ }
1110
+ return modifiedOptions;
1111
+ }
1112
+
1113
+ // src/config/load.ts
1114
+ import fs2 from "node:fs";
1115
+ import { createRequire } from "node:module";
1116
+ import { pathToFileURL } from "node:url";
1117
+ import upath3 from "upath";
1118
+ import * as v2 from "valibot";
1119
+ var require2 = createRequire(import.meta.url);
1120
+ function locateVivliostyleConfig({
1121
+ config,
1122
+ cwd: cwd2 = cwd
1123
+ }) {
1124
+ if (config) {
1125
+ return upath3.resolve(cwd2, config);
1126
+ }
1127
+ return [".js", ".mjs", ".cjs", ".json"].map((ext) => upath3.join(cwd2, `vivliostyle.config${ext}`)).find((p) => fs2.existsSync(p));
1128
+ }
1129
+ async function loadVivliostyleConfig({
1130
+ config,
1131
+ configData,
1132
+ cwd: cwd2
1133
+ }) {
1134
+ if (configData) {
1135
+ return v2.parse(VivliostyleConfigSchema, configData);
1136
+ }
1137
+ const absPath = locateVivliostyleConfig({ config, cwd: cwd2 });
1138
+ if (!absPath) {
1139
+ return;
1140
+ }
1141
+ let parsedConfig;
1142
+ let jsonRaw;
1143
+ try {
1144
+ if (upath3.extname(absPath) === ".json") {
1145
+ jsonRaw = fs2.readFileSync(absPath, "utf8");
1146
+ parsedConfig = parseJsonc(jsonRaw);
1147
+ } else {
1148
+ delete require2.cache[require2.resolve(absPath)];
1149
+ const url = pathToFileURL(absPath);
1150
+ url.search = `version=${Date.now()}`;
1151
+ parsedConfig = (await import(
1152
+ /* @vite-ignore */
1153
+ url.href
1154
+ )).default;
1155
+ jsonRaw = JSON.stringify(parsedConfig, null, 2);
1156
+ }
1157
+ } catch (error) {
1158
+ const thrownError = error;
1159
+ throw new DetailError(
1160
+ `An error occurred on loading a config file: ${absPath}`,
1161
+ thrownError.stack ?? thrownError.message
1162
+ );
1163
+ }
1164
+ const result = v2.safeParse(VivliostyleConfigSchema, parsedConfig);
1165
+ if (result.success) {
1166
+ const { tasks, inlineOptions } = result.output;
1167
+ return {
1168
+ tasks,
1169
+ inlineOptions: {
1170
+ ...inlineOptions,
1171
+ cwd: cwd2 ?? cwd,
1172
+ config: absPath
1173
+ }
1174
+ };
1175
+ } else {
1176
+ const errorString = prettifySchemaError(jsonRaw, result.issues);
1177
+ throw new DetailError(
1178
+ `Validation of vivliostyle config failed. Please check the schema: ${config}`,
1179
+ errorString
1180
+ );
1181
+ }
1182
+ }
1183
+ function warnDeprecatedConfig(config) {
1184
+ if (config.tasks.some((task) => task.includeAssets)) {
1185
+ Logger.logWarn(
1186
+ "'includeAssets' property of Vivliostyle config was deprecated and will be removed in a future release. Please use 'copyAsset.includes' property instead."
1187
+ );
1188
+ }
1189
+ if (config.tasks.some((task) => task.tocTitle)) {
1190
+ Logger.logWarn(
1191
+ "'tocTitle' property of Vivliostyle config was deprecated and will be removed in a future release. Please use 'toc.title' property instead."
1192
+ );
1193
+ }
1194
+ if (config.tasks.some((task) => task.http)) {
1195
+ Logger.logWarn(
1196
+ "'http' property of Vivliostyle config was deprecated and will be removed in a future release. This option is enabled by default, and the file protocol is no longer supported."
1197
+ );
1198
+ }
1199
+ }
1200
+
1201
+ // src/config/merge.ts
1202
+ var pruneObject = (obj) => {
1203
+ const ret = { ...obj };
1204
+ for (const key in ret) {
1205
+ if (ret[key] === void 0 || ret[key] === null) {
1206
+ delete ret[key];
1207
+ }
1208
+ }
1209
+ return ret;
1210
+ };
1211
+ function mergeConfig(base, override) {
1212
+ return {
1213
+ tasks: base.tasks.map((task, i) => ({
1214
+ ...pruneObject(task),
1215
+ ...pruneObject(override)
1216
+ })),
1217
+ inlineOptions: base.inlineOptions
1218
+ };
1219
+ }
1220
+ function mergeInlineConfig({ tasks, inlineOptions }, inlineConfig) {
1221
+ const {
1222
+ theme,
1223
+ size,
1224
+ pressReady,
1225
+ title,
1226
+ author,
1227
+ language,
1228
+ readingProgression,
1229
+ timeout,
1230
+ image,
1231
+ viewer,
1232
+ viewerParam,
1233
+ browser,
1234
+ output,
1235
+ renderMode,
1236
+ preflight,
1237
+ preflightOption,
1238
+ vite,
1239
+ viteConfigFile,
1240
+ host,
1241
+ port,
1242
+ ...overrideInlineOptions
1243
+ } = inlineConfig;
1244
+ return {
1245
+ tasks: tasks.map((task) => ({
1246
+ ...pruneObject(task),
1247
+ ...pruneObject({
1248
+ theme,
1249
+ size,
1250
+ pressReady,
1251
+ title,
1252
+ author,
1253
+ language,
1254
+ readingProgression,
1255
+ timeout,
1256
+ image,
1257
+ viewer,
1258
+ viewerParam,
1259
+ browser,
1260
+ vite,
1261
+ viteConfigFile
1262
+ }),
1263
+ output: (output?.length ? output : task.output)?.map((o) => ({
1264
+ ...pruneObject(o),
1265
+ ...pruneObject({
1266
+ renderMode,
1267
+ preflight,
1268
+ preflightOption
1269
+ })
1270
+ })),
1271
+ server: {
1272
+ ...pruneObject(task.server ?? {}),
1273
+ ...pruneObject({ host, port })
1274
+ }
1275
+ })),
1276
+ inlineOptions: {
1277
+ ...pruneObject(inlineOptions),
1278
+ ...pruneObject(
1279
+ overrideInlineOptions
1280
+ )
1281
+ }
1282
+ };
1283
+ }
1284
+
1285
+ // src/browser.ts
1286
+ import fs3 from "node:fs";
1287
+ async function launchBrowser({
1288
+ browserType,
1289
+ proxy,
1290
+ executablePath,
1291
+ headless,
1292
+ noSandbox,
1293
+ disableWebSecurity,
1294
+ disableDevShmUsage
1295
+ }) {
1296
+ const playwright = await importNodeModule("playwright-core");
1297
+ playwright.firefox.executablePath;
1298
+ const options = browserType === "chromium" ? {
1299
+ executablePath,
1300
+ chromiumSandbox: !noSandbox,
1301
+ headless,
1302
+ args: [
1303
+ "--allow-file-access-from-files",
1304
+ disableWebSecurity && "--disable-web-security",
1305
+ disableDevShmUsage && "--disable-dev-shm-usage",
1306
+ // #357: Set devicePixelRatio=1 otherwise it causes layout issues in HiDPI displays
1307
+ headless && "--force-device-scale-factor=1",
1308
+ // set Chromium language to English to avoid locale-dependent issues (e.g. minimum font size)
1309
+ "--lang=en"
1310
+ // ...(!headless && process.platform === 'darwin'
1311
+ // ? ['-AppleLanguages', '(en)']
1312
+ // : []),
1313
+ ].filter((value) => Boolean(value)),
1314
+ env: { ...process.env, LANG: "en.UTF-8" },
1315
+ proxy
1316
+ } : (
1317
+ // TODO: Investigate appropriate settings on Firefox & Webkit
1318
+ { executablePath, headless }
1319
+ );
1320
+ const browser = await playwright[browserType].launch(options);
1321
+ registerExitHandler("Closing browser", () => {
1322
+ browser.close();
1323
+ });
1324
+ return browser;
1325
+ }
1326
+ async function getExecutableBrowserPath(browserType) {
1327
+ const playwright = await importNodeModule("playwright-core");
1328
+ return playwright[browserType].executablePath();
1329
+ }
1330
+ function getFullBrowserName(browserType) {
1331
+ return {
1332
+ chromium: "Chromium",
1333
+ firefox: "Firefox",
1334
+ webkit: "Webkit"
1335
+ }[browserType];
1336
+ }
1337
+ function checkBrowserAvailability(path) {
1338
+ return fs3.existsSync(path);
1339
+ }
1340
+ async function downloadBrowser(browserType) {
1341
+ const { registry } = await importNodeModule("playwright-core/lib/server");
1342
+ const executable = registry.findExecutable(browserType);
1343
+ {
1344
+ var _stack = [];
1345
+ try {
1346
+ const _2 = __using(_stack, Logger.suspendLogging(
1347
+ "Rendering browser is not installed yet. Downloading now."
1348
+ ));
1349
+ await registry.install([executable], false);
1350
+ } catch (_) {
1351
+ var _error = _, _hasError = true;
1352
+ } finally {
1353
+ __callDispose(_stack, _error, _hasError);
1354
+ }
1355
+ }
1356
+ return executable.executablePath();
1357
+ }
1358
+ async function launchPreview({
1359
+ mode,
1360
+ url,
1361
+ onBrowserOpen,
1362
+ onPageOpen,
1363
+ config: { browser: browserConfig, proxy, sandbox, ignoreHttpsErrors }
1364
+ }) {
1365
+ let executableBrowser = browserConfig.executablePath;
1366
+ if (executableBrowser) {
1367
+ if (!checkBrowserAvailability(executableBrowser)) {
1368
+ throw new Error(
1369
+ `Cannot find the browser. Please check the executable browser path: ${executableBrowser}`
1370
+ );
1371
+ }
1372
+ } else {
1373
+ executableBrowser = await getExecutableBrowserPath(browserConfig.type);
1374
+ if (!checkBrowserAvailability(executableBrowser)) {
1375
+ await downloadBrowser(browserConfig.type);
1376
+ }
1377
+ }
1378
+ Logger.debug(`Executing browser path: ${executableBrowser}`);
1379
+ const browser = await launchBrowser({
1380
+ browserType: browserConfig.type,
1381
+ proxy,
1382
+ executablePath: executableBrowser,
1383
+ headless: mode === "build",
1384
+ noSandbox: !sandbox,
1385
+ disableDevShmUsage: isInContainer()
1386
+ });
1387
+ await onBrowserOpen?.(browser);
1388
+ const page = await browser.newPage({
1389
+ viewport: mode === "build" ? (
1390
+ // This viewport size important to detect headless environment in Vivliostyle viewer
1391
+ // https://github.com/vivliostyle/vivliostyle.js/blob/73bcf323adcad80126b0175630609451ccd09d8a/packages/core/src/vivliostyle/vgen.ts#L2489-L2500
1392
+ {
1393
+ width: 800,
1394
+ height: 600
1395
+ }
1396
+ ) : null,
1397
+ ignoreHTTPSErrors: ignoreHttpsErrors
1398
+ });
1399
+ await onPageOpen?.(page);
1400
+ page.on("dialog", () => {
1401
+ });
1402
+ await page.goto(url);
1403
+ return { browser, page };
1404
+ }
1405
+
1406
+ // src/server.ts
1407
+ import fs11 from "node:fs";
1408
+ import { URL as URL2 } from "node:url";
1409
+ import upath13 from "upath";
1410
+ import {
1411
+ createServer,
1412
+ preview
1413
+ } from "vite";
1414
+
1415
+ // src/vite/vite-plugin-dev-server.ts
1416
+ import escapeRe from "escape-string-regexp";
1417
+ import { pathToFileURL as pathToFileURL7 } from "node:url";
1418
+ import picomatch2 from "picomatch";
1419
+ import sirv from "sirv";
1420
+ import upath10 from "upath";
1421
+
1422
+ // src/config/resolve.ts
1423
+ import { VFM } from "@vivliostyle/vfm";
1424
+ import { lookup as mime } from "mime-types";
1425
+ import fs5 from "node:fs";
1426
+ import { pathToFileURL as pathToFileURL3 } from "node:url";
1427
+ import npa from "npm-package-arg";
1428
+ import upath5 from "upath";
1429
+
1430
+ // src/container.ts
1431
+ import { execFile } from "node:child_process";
1432
+ import process2 from "node:process";
1433
+ import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "node:url";
1434
+ import { promisify } from "node:util";
1435
+ import upath4 from "upath";
1436
+ var execFileAsync = promisify(execFile);
1437
+ var CONTAINER_IMAGE = `ghcr.io/vivliostyle/cli:${cliVersion}`;
1438
+ var CONTAINER_ROOT_DIR = "/data";
1439
+ var CONTAINER_LOCAL_HOSTNAME = "host.docker.internal";
1440
+ function toContainerPath(urlOrAbsPath) {
1441
+ if (isValidUri(urlOrAbsPath)) {
1442
+ if (urlOrAbsPath.toLowerCase().startsWith("file")) {
1443
+ return pathToFileURL2(
1444
+ upath4.posix.join(
1445
+ CONTAINER_ROOT_DIR,
1446
+ upath4.toUnix(fileURLToPath(urlOrAbsPath)).replace(/^\w:/, "")
1447
+ )
1448
+ ).href;
1449
+ } else {
1450
+ return urlOrAbsPath;
1451
+ }
1452
+ }
1453
+ return upath4.posix.join(
1454
+ CONTAINER_ROOT_DIR,
1455
+ upath4.toUnix(urlOrAbsPath).replace(/^\w:/, "")
1456
+ );
1457
+ }
1458
+ function collectVolumeArgs(mountPoints) {
1459
+ return mountPoints.filter((p, i, array) => {
1460
+ if (i !== array.indexOf(p)) {
1461
+ return false;
1462
+ }
1463
+ let parent = p;
1464
+ while (!pathEquals(parent, upath4.dirname(parent))) {
1465
+ parent = upath4.dirname(parent);
1466
+ if (array.includes(parent)) {
1467
+ return false;
1468
+ }
1469
+ }
1470
+ return true;
1471
+ }).map((p) => `${p}:${toContainerPath(p)}`);
1472
+ }
1473
+ async function runContainer({
1474
+ image,
1475
+ userVolumeArgs,
1476
+ commandArgs,
1477
+ entrypoint,
1478
+ env,
1479
+ workdir
1480
+ }) {
1481
+ const commandExists = await importNodeModule("command-exists");
1482
+ if (!await commandExists("docker")) {
1483
+ throw new Error(
1484
+ `Docker isn't be installed. To use this feature, you'll need to install Docker.`
1485
+ );
1486
+ }
1487
+ const versionCmd = await execFileAsync("docker", [
1488
+ "version",
1489
+ "--format",
1490
+ "{{.Server.Version}}"
1491
+ ]);
1492
+ const version = versionCmd.stdout.trim();
1493
+ const [major, minor] = version.split(".").map(Number);
1494
+ if (major < 20 || major === 20 && minor < 10) {
1495
+ throw new Error(
1496
+ `Docker version ${version} is not supported. Please upgrade to Docker 20.10.0 or later.`
1497
+ );
1498
+ }
1499
+ try {
1500
+ var _stack = [];
1501
+ try {
1502
+ const _2 = __using(_stack, Logger.suspendLogging("Launching docker container"));
1503
+ const args = [
1504
+ "run",
1505
+ ...Logger.isInteractive ? ["-it"] : [],
1506
+ "--rm",
1507
+ ...entrypoint ? ["--entrypoint", entrypoint] : [],
1508
+ ...env ? env.flatMap(([k, v3]) => ["-e", `${k}=${v3}`]) : [],
1509
+ ...process2.env.DEBUG ? ["-e", `DEBUG=${process2.env.DEBUG}`] : [],
1510
+ ...userVolumeArgs.flatMap((arg) => ["-v", arg]),
1511
+ ...workdir ? ["-w", workdir] : [],
1512
+ image,
1513
+ ...commandArgs
1514
+ ];
1515
+ Logger.debug(`docker ${args.join(" ")}`);
1516
+ const { execa } = await importNodeModule("execa");
1517
+ const proc = execa("docker", args, {
1518
+ stdio: "inherit"
1519
+ });
1520
+ await proc;
1521
+ } catch (_) {
1522
+ var _error = _, _hasError = true;
1523
+ } finally {
1524
+ __callDispose(_stack, _error, _hasError);
1525
+ }
1526
+ } catch (error) {
1527
+ throw new Error(
1528
+ "An error occurred on the running container. Please see logs above."
1529
+ );
1530
+ }
1531
+ }
1532
+ async function buildPDFWithContainer({
1533
+ target,
1534
+ config,
1535
+ inlineConfig
1536
+ }) {
1537
+ const sourceUrl = new URL(await getSourceUrl(config));
1538
+ if (sourceUrl.origin === config.rootUrl) {
1539
+ sourceUrl.hostname = CONTAINER_LOCAL_HOSTNAME;
1540
+ }
1541
+ const bypassedOption = {
1542
+ ...inlineConfig,
1543
+ input: {
1544
+ format: "webbook",
1545
+ entry: sourceUrl.href
1546
+ },
1547
+ output: [
1548
+ {
1549
+ ...target,
1550
+ path: toContainerPath(target.path)
1551
+ }
1552
+ ]
1553
+ };
1554
+ await runContainer({
1555
+ image: config.image,
1556
+ userVolumeArgs: collectVolumeArgs([
1557
+ config.context,
1558
+ upath4.dirname(target.path)
1559
+ ]),
1560
+ env: [["VS_CLI_BUILD_PDF_OPTIONS", JSON.stringify(bypassedOption)]],
1561
+ commandArgs: ["build"],
1562
+ workdir: toContainerPath(config.context)
1563
+ });
1564
+ return target.path;
1565
+ }
1566
+
1567
+ // src/processor/markdown.ts
1568
+ import {
1569
+ readMetadata
1570
+ } from "@vivliostyle/vfm";
1571
+ import fs4 from "node:fs";
1572
+ import vfile from "vfile";
1573
+ function safeReadMetadata(content) {
1574
+ try {
1575
+ return readMetadata(content);
1576
+ } catch {
1577
+ return {};
1578
+ }
1579
+ }
1580
+ async function processMarkdown(documentProcessorFactory, filepath, options = {}) {
1581
+ const markdownString = fs4.readFileSync(filepath, "utf8");
1582
+ const processor = documentProcessorFactory(
1583
+ options,
1584
+ safeReadMetadata(markdownString)
1585
+ );
1586
+ const processed = await processor.process(
1587
+ vfile({ path: filepath, contents: markdownString })
1588
+ );
1589
+ return processed;
1590
+ }
1591
+ function readMarkdownMetadata(filepath) {
1592
+ return safeReadMetadata(fs4.readFileSync(filepath, "utf8"));
1593
+ }
1594
+
1595
+ // src/config/resolve.ts
1596
+ var manuscriptMediaTypes = [
1597
+ "text/markdown",
1598
+ "text/html",
1599
+ "application/xhtml+xml"
1600
+ ];
1601
+ var DEFAULT_ASSET_EXTENSIONS = [
1602
+ "png",
1603
+ "jpg",
1604
+ "jpeg",
1605
+ "svg",
1606
+ "gif",
1607
+ "webp",
1608
+ "apng",
1609
+ "ttf",
1610
+ "otf",
1611
+ "woff",
1612
+ "woff2"
1613
+ ];
1614
+ function isManuscriptMediaType(mediaType) {
1615
+ return !!(mediaType && manuscriptMediaTypes.includes(mediaType));
1616
+ }
1617
+ function isWebPubConfig(config) {
1618
+ return config.viewerInput.type === "webpub";
1619
+ }
1620
+ function isWebbookConfig(config) {
1621
+ return config.viewerInput.type === "webbook";
1622
+ }
1623
+ function parsePackageName(specifier, cwd2) {
1624
+ try {
1625
+ let result = npa(specifier, cwd2);
1626
+ if (result.type === "git" && result.saveSpec?.startsWith("github:")) {
1627
+ result = npa(`file:${specifier}`, cwd2);
1628
+ }
1629
+ return result;
1630
+ } catch (error) {
1631
+ return null;
1632
+ }
1633
+ }
1634
+ function parseTheme({
1635
+ theme,
1636
+ context,
1637
+ workspaceDir,
1638
+ themesDir
1639
+ }) {
1640
+ const { specifier, import: importPath } = typeof theme === "string" ? { specifier: theme, import: void 0 } : theme;
1641
+ if (isValidUri(specifier)) {
1642
+ return {
1643
+ type: "uri",
1644
+ name: upath5.basename(specifier),
1645
+ location: specifier
1646
+ };
1647
+ }
1648
+ const stylePath = upath5.resolve(context, specifier);
1649
+ if (fs5.existsSync(stylePath) && stylePath.endsWith(".css")) {
1650
+ const sourceRelPath = upath5.relative(context, stylePath);
1651
+ return {
1652
+ type: "file",
1653
+ name: upath5.basename(specifier),
1654
+ source: stylePath,
1655
+ location: upath5.resolve(workspaceDir, sourceRelPath)
1656
+ };
1657
+ }
1658
+ const parsed = parsePackageName(specifier, context);
1659
+ if (!parsed) {
1660
+ throw new Error(`Invalid package name: ${specifier}`);
1661
+ }
1662
+ if (!parsed.registry && parsed.type !== "directory") {
1663
+ throw new Error(`This package specifier is not allowed: ${specifier}`);
1664
+ }
1665
+ let name = parsed.name;
1666
+ let resolvedSpecifier = specifier;
1667
+ if (parsed.type === "directory" && parsed.fetchSpec) {
1668
+ const pkgJsonPath = upath5.join(parsed.fetchSpec, "package.json");
1669
+ if (fs5.existsSync(pkgJsonPath)) {
1670
+ const packageJson = JSON.parse(fs5.readFileSync(pkgJsonPath, "utf8"));
1671
+ name = packageJson.name;
1672
+ resolvedSpecifier = parsed.fetchSpec;
1673
+ }
1674
+ }
1675
+ if (!name) {
1676
+ throw new Error(`Could not determine the package name: ${specifier}`);
1677
+ }
1678
+ return {
1679
+ type: "package",
1680
+ name,
1681
+ specifier: resolvedSpecifier,
1682
+ location: upath5.join(themesDir, "node_modules", name),
1683
+ importPath
1684
+ };
1685
+ }
1686
+ function parsePageSize(size) {
1687
+ const [width, height, ...others] = `${size}`.split(",");
1688
+ if (!width || others.length) {
1689
+ throw new Error(`Cannot parse size: ${size}`);
1690
+ } else if (width && height) {
1691
+ return {
1692
+ width,
1693
+ height
1694
+ };
1695
+ } else {
1696
+ return {
1697
+ format: width
1698
+ };
1699
+ }
1700
+ }
1701
+ function parseFileMetadata({
1702
+ contentType,
1703
+ sourcePath,
1704
+ workspaceDir,
1705
+ themesDir
1706
+ }) {
1707
+ const sourceDir = upath5.dirname(sourcePath);
1708
+ let title;
1709
+ let themes;
1710
+ if (contentType === "text/markdown") {
1711
+ const metadata = readMarkdownMetadata(sourcePath);
1712
+ title = metadata.title;
1713
+ if (metadata.vfm?.theme && themesDir) {
1714
+ themes = [metadata.vfm.theme].flat().filter(
1715
+ (entry) => !!entry && (typeof entry === "string" || typeof entry === "object")
1716
+ ).map(
1717
+ (theme) => parseTheme({
1718
+ theme,
1719
+ context: sourceDir,
1720
+ workspaceDir,
1721
+ themesDir
1722
+ })
1723
+ );
1724
+ }
1725
+ } else {
1726
+ const content = fs5.readFileSync(sourcePath, "utf8");
1727
+ title = content.match(/<title>([^<]*)<\/title>/)?.[1] || void 0;
1728
+ }
1729
+ return { title, themes };
1730
+ }
1731
+ function resolveTaskConfig(config, options) {
1732
+ const context = options.cwd ?? cwd;
1733
+ Logger.debug("resolveTaskConfig > context %s", context);
1734
+ const entryContextDir = config.entryContext ? upath5.resolve(context, config.entryContext) : context;
1735
+ const language = config.language;
1736
+ const readingProgression = config.readingProgression;
1737
+ const size = config.size ? parsePageSize(config.size) : void 0;
1738
+ const cropMarks = options.cropMarks ?? false;
1739
+ const bleed = options.bleed;
1740
+ const cropOffset = options.cropOffset;
1741
+ const css = options.css;
1742
+ const customStyle = options.style && (isValidUri(options.style) ? options.style : pathToFileURL3(options.style).href);
1743
+ const customUserStyle = options.userStyle && (isValidUri(options.userStyle) ? options.userStyle : pathToFileURL3(options.userStyle).href);
1744
+ const singleDoc = options.singleDoc ?? false;
1745
+ const quick = options.quick ?? false;
1746
+ const temporaryFilePrefix = config.temporaryFilePrefix ?? `.vs-${Date.now()}.`;
1747
+ const documentProcessorFactory = config?.documentProcessor ?? VFM;
1748
+ const vfmOptions = {
1749
+ ...config?.vfm,
1750
+ hardLineBreaks: config?.vfm?.hardLineBreaks ?? false,
1751
+ disableFormatHtml: config?.vfm?.disableFormatHtml ?? false
1752
+ };
1753
+ const timeout = config.timeout ?? 12e4;
1754
+ const sandbox = options.sandbox ?? false;
1755
+ const browser = {
1756
+ type: config.browser ?? "chromium",
1757
+ executablePath: options.executableBrowser
1758
+ };
1759
+ const proxyServer = options.proxyServer ?? process.env.HTTP_PROXY ?? void 0;
1760
+ const proxy = proxyServer ? {
1761
+ server: proxyServer,
1762
+ bypass: options.proxyBypass ?? process.env.NOPROXY ?? void 0,
1763
+ username: options.proxyUser,
1764
+ password: options.proxyPass
1765
+ } : void 0;
1766
+ const image = config.image ?? CONTAINER_IMAGE;
1767
+ const viewer = config.viewer ?? void 0;
1768
+ const viewerParam = config.viewerParam ?? void 0;
1769
+ const logLevel = options.logLevel ?? "silent";
1770
+ const ignoreHttpsErrors = options.ignoreHttpsErrors ?? false;
1771
+ const base = config.base ?? "/vivliostyle";
1772
+ const staticRoutes = config.static ?? {};
1773
+ const viteConfig = config.vite;
1774
+ const viteConfigFile = config.viteConfigFile ?? true;
1775
+ const outputs = (() => {
1776
+ const defaultPdfOptions = {
1777
+ format: "pdf",
1778
+ renderMode: "local",
1779
+ preflight: config.pressReady ? "press-ready" : void 0,
1780
+ preflightOption: []
1781
+ };
1782
+ if (config.output) {
1783
+ return config.output.map((target) => {
1784
+ const outputPath = upath5.resolve(context, target.path);
1785
+ const format = target.format;
1786
+ switch (format) {
1787
+ case "pdf":
1788
+ return {
1789
+ ...defaultPdfOptions,
1790
+ ...target,
1791
+ format,
1792
+ path: outputPath
1793
+ };
1794
+ case "epub":
1795
+ return {
1796
+ ...target,
1797
+ format,
1798
+ path: outputPath,
1799
+ version: EPUB_OUTPUT_VERSION
1800
+ };
1801
+ case "webpub":
1802
+ return {
1803
+ ...target,
1804
+ format,
1805
+ path: outputPath
1806
+ };
1807
+ default:
1808
+ return format;
1809
+ }
1810
+ });
1811
+ }
1812
+ const filename = config.title ? `${config.title}.pdf` : "output.pdf";
1813
+ return [
1814
+ {
1815
+ ...defaultPdfOptions,
1816
+ path: upath5.resolve(context, filename)
1817
+ }
1818
+ ];
1819
+ })();
1820
+ const { server, rootUrl } = (() => {
1821
+ let host = config.server?.host ?? false;
1822
+ const port = config.server?.port ?? 13e3;
1823
+ if (outputs.some(
1824
+ (target) => target.format === "pdf" && target.renderMode === "docker"
1825
+ )) {
1826
+ host = true;
1827
+ }
1828
+ const rootHostname = isInContainer() ? CONTAINER_LOCAL_HOSTNAME : !host ? "localhost" : host === true ? "0.0.0.0" : host;
1829
+ return {
1830
+ server: {
1831
+ host,
1832
+ port,
1833
+ proxy: config.server?.proxy ?? {}
1834
+ },
1835
+ rootUrl: `http://${rootHostname}:${port}`
1836
+ };
1837
+ })();
1838
+ const cover = config.cover && {
1839
+ src: upath5.resolve(entryContextDir, config.cover.src),
1840
+ name: config.cover.name || COVER_HTML_IMAGE_ALT
1841
+ };
1842
+ const copyAsset = {
1843
+ includes: config.copyAsset?.includes ?? config.includeAssets ?? [],
1844
+ excludes: config.copyAsset?.excludes ?? [],
1845
+ fileExtensions: [
1846
+ .../* @__PURE__ */ new Set([
1847
+ ...DEFAULT_ASSET_EXTENSIONS,
1848
+ ...config.copyAsset?.includeFileExtensions ?? []
1849
+ ])
1850
+ ].filter(
1851
+ (ext) => !(config.copyAsset?.excludeFileExtensions ?? []).includes(ext)
1852
+ )
1853
+ };
1854
+ const themeIndexes = /* @__PURE__ */ new Set();
1855
+ const projectConfig = !options.config && options.input ? resolveSingleInputConfig({
1856
+ config,
1857
+ input: options.input,
1858
+ context,
1859
+ temporaryFilePrefix,
1860
+ themeIndexes,
1861
+ base
1862
+ }) : resolveComposedProjectConfig({
1863
+ config,
1864
+ context,
1865
+ entryContextDir,
1866
+ outputs,
1867
+ temporaryFilePrefix,
1868
+ themeIndexes,
1869
+ rootUrl,
1870
+ cover
1871
+ });
1872
+ for (const output of outputs) {
1873
+ const relPath = upath5.relative(context, output.path);
1874
+ if (pathContains(output.path, entryContextDir) || pathEquals(output.path, entryContextDir)) {
1875
+ throw new Error(
1876
+ `The output path is set to "${relPath}", but this will overwrite the original manuscript file. Please specify a different path.`
1877
+ );
1878
+ }
1879
+ if (pathContains(output.path, projectConfig.workspaceDir) || pathEquals(output.path, projectConfig.workspaceDir)) {
1880
+ throw new Error(
1881
+ `The output path is set to "${relPath}", but this will overwrite the working directory of Vivliostyle. Please specify a different path.`
1882
+ );
1883
+ }
1884
+ }
1885
+ const { entries, workspaceDir } = projectConfig;
1886
+ const duplicatedTarget = entries.find(
1887
+ (v1, i) => entries.findLastIndex((v22) => v1.target === v22.target) !== i
1888
+ )?.target;
1889
+ if (duplicatedTarget) {
1890
+ const sourceFile = entries.find(
1891
+ (entry) => entry.target === duplicatedTarget && entry.source?.type === "file"
1892
+ )?.source;
1893
+ throw new Error(
1894
+ `The output path "${upath5.relative(workspaceDir, duplicatedTarget)}" will overwrite existing content.` + (sourceFile ? ` Please choose a different name for the source file: ${sourceFile.pathname}` : "")
1895
+ );
1896
+ }
1897
+ const resolvedConfig = {
1898
+ ...projectConfig,
1899
+ context,
1900
+ entryContextDir,
1901
+ outputs,
1902
+ themeIndexes,
1903
+ copyAsset,
1904
+ temporaryFilePrefix,
1905
+ size,
1906
+ cropMarks,
1907
+ bleed,
1908
+ cropOffset,
1909
+ css,
1910
+ customStyle,
1911
+ customUserStyle,
1912
+ singleDoc,
1913
+ quick,
1914
+ language,
1915
+ readingProgression,
1916
+ documentProcessorFactory,
1917
+ vfmOptions,
1918
+ cover,
1919
+ timeout,
1920
+ sandbox,
1921
+ browser,
1922
+ proxy,
1923
+ image,
1924
+ viewer,
1925
+ viewerParam,
1926
+ logLevel,
1927
+ ignoreHttpsErrors,
1928
+ base,
1929
+ server,
1930
+ static: staticRoutes,
1931
+ rootUrl,
1932
+ viteConfig,
1933
+ viteConfigFile
1934
+ };
1935
+ return resolvedConfig;
1936
+ }
1937
+ function resolveSingleInputConfig({
1938
+ config,
1939
+ input,
1940
+ context,
1941
+ temporaryFilePrefix,
1942
+ themeIndexes,
1943
+ base
1944
+ }) {
1945
+ Logger.debug("entering single entry config mode");
1946
+ let sourcePath;
1947
+ let workspaceDir;
1948
+ const inputFormat = input.format;
1949
+ const title = config?.title;
1950
+ const author = config?.author;
1951
+ const entries = [];
1952
+ const exportAliases = [];
1953
+ if (isValidUri(input.entry)) {
1954
+ sourcePath = input.entry;
1955
+ workspaceDir = context;
1956
+ } else {
1957
+ sourcePath = upath5.resolve(context, input.entry);
1958
+ statFileSync(sourcePath);
1959
+ switch (input.format) {
1960
+ case "webbook":
1961
+ case "markdown":
1962
+ case "pub-manifest":
1963
+ case "epub":
1964
+ workspaceDir = upath5.dirname(sourcePath);
1965
+ break;
1966
+ case "epub-opf": {
1967
+ const rootDir = getEpubRootDir(sourcePath);
1968
+ if (!rootDir) {
1969
+ throw new Error(
1970
+ `Could not determine the EPUB root directory for the OPF file: ${sourcePath}`
1971
+ );
1972
+ }
1973
+ workspaceDir = rootDir;
1974
+ break;
1975
+ }
1976
+ default:
1977
+ return input.format;
1978
+ }
1979
+ }
1980
+ const themesDir = upath5.resolve(workspaceDir, "themes");
1981
+ if (input.format === "markdown") {
1982
+ const contentType = "text/markdown";
1983
+ const metadata = parseFileMetadata({
1984
+ contentType,
1985
+ sourcePath,
1986
+ workspaceDir
1987
+ });
1988
+ const target = upath5.resolve(
1989
+ workspaceDir,
1990
+ `${temporaryFilePrefix}${upath5.basename(sourcePath)}`
1991
+ ).replace(/\.md$/, ".html");
1992
+ touchTmpFile(target);
1993
+ const themes = metadata.themes ?? config.theme?.map(
1994
+ (theme) => parseTheme({
1995
+ theme,
1996
+ context,
1997
+ workspaceDir,
1998
+ themesDir
1999
+ })
2000
+ ) ?? [];
2001
+ themes.forEach((t) => themeIndexes.add(t));
2002
+ entries.push({
2003
+ contentType,
2004
+ source: {
2005
+ type: "file",
2006
+ pathname: sourcePath,
2007
+ contentType
2008
+ },
2009
+ target,
2010
+ title: metadata.title,
2011
+ themes
2012
+ });
2013
+ exportAliases.push({
2014
+ source: target,
2015
+ target: upath5.resolve(
2016
+ upath5.dirname(target),
2017
+ upath5.basename(sourcePath).replace(/\.md$/, ".html")
2018
+ )
2019
+ });
2020
+ }
2021
+ let fallbackTitle;
2022
+ let viewerInput;
2023
+ if (inputFormat === "markdown") {
2024
+ const manifestPath = upath5.resolve(
2025
+ workspaceDir,
2026
+ `${temporaryFilePrefix}${MANIFEST_FILENAME}`
2027
+ );
2028
+ touchTmpFile(manifestPath);
2029
+ exportAliases.push({
2030
+ source: manifestPath,
2031
+ target: upath5.resolve(workspaceDir, MANIFEST_FILENAME)
2032
+ });
2033
+ fallbackTitle = entries.length === 1 && entries[0].title ? entries[0].title : upath5.basename(sourcePath);
2034
+ viewerInput = {
2035
+ type: "webpub",
2036
+ manifestPath,
2037
+ needToGenerateManifest: true
2038
+ };
2039
+ } else if (inputFormat === "webbook") {
2040
+ let webbookEntryUrl;
2041
+ let webbookPath;
2042
+ if (isValidUri(sourcePath)) {
2043
+ const url = new URL(sourcePath);
2044
+ if (/^https?:/i.test(url.protocol) && !url.pathname.endsWith("/") && !/\.html?$/.test(url.pathname)) {
2045
+ url.pathname = `${url.pathname}/`;
2046
+ }
2047
+ webbookEntryUrl = url.href;
2048
+ } else {
2049
+ const rootFileUrl = pathToFileURL3(workspaceDir).href;
2050
+ const urlPath = pathToFileURL3(sourcePath).href.slice(rootFileUrl.length);
2051
+ webbookEntryUrl = `${base}${urlPath}`;
2052
+ webbookPath = sourcePath;
2053
+ }
2054
+ viewerInput = { type: "webbook", webbookEntryUrl, webbookPath };
2055
+ } else if (inputFormat === "pub-manifest") {
2056
+ viewerInput = {
2057
+ type: "webpub",
2058
+ manifestPath: sourcePath,
2059
+ needToGenerateManifest: false
2060
+ };
2061
+ } else if (inputFormat === "epub-opf") {
2062
+ viewerInput = { type: "epub-opf", epubOpfPath: sourcePath };
2063
+ } else if (inputFormat === "epub") {
2064
+ viewerInput = {
2065
+ type: "epub",
2066
+ epubPath: sourcePath,
2067
+ epubTmpOutputDir: upath5.join(
2068
+ sourcePath,
2069
+ `../${temporaryFilePrefix}${upath5.basename(sourcePath)}`
2070
+ )
2071
+ };
2072
+ } else {
2073
+ return inputFormat;
2074
+ }
2075
+ return {
2076
+ workspaceDir,
2077
+ themesDir,
2078
+ entries,
2079
+ input: {
2080
+ format: inputFormat,
2081
+ entry: sourcePath
2082
+ },
2083
+ viewerInput,
2084
+ exportAliases,
2085
+ title: title || fallbackTitle,
2086
+ author
2087
+ };
2088
+ }
2089
+ function resolveComposedProjectConfig({
2090
+ config,
2091
+ context,
2092
+ entryContextDir,
2093
+ outputs,
2094
+ temporaryFilePrefix,
2095
+ themeIndexes,
2096
+ rootUrl,
2097
+ cover
2098
+ }) {
2099
+ Logger.debug("entering composed project config mode");
2100
+ const workspaceDir = upath5.resolve(
2101
+ context,
2102
+ config.workspaceDir ?? ".vivliostyle"
2103
+ );
2104
+ const themesDir = upath5.resolve(workspaceDir, "themes");
2105
+ const pkgJsonPath = upath5.resolve(entryContextDir, "package.json");
2106
+ const pkgJson = fs5.existsSync(pkgJsonPath) ? readJSON(pkgJsonPath) : void 0;
2107
+ if (pkgJson) {
2108
+ Logger.debug("located package.json path", pkgJsonPath);
2109
+ }
2110
+ const exportAliases = [];
2111
+ const rootThemes = config.theme?.map(
2112
+ (theme) => parseTheme({
2113
+ theme,
2114
+ context,
2115
+ workspaceDir,
2116
+ themesDir
2117
+ })
2118
+ ) ?? [];
2119
+ rootThemes.forEach((t) => themeIndexes.add(t));
2120
+ const tocConfig = {
2121
+ tocTitle: config.toc?.title ?? config?.tocTitle ?? TOC_TITLE,
2122
+ target: upath5.resolve(workspaceDir, config.toc?.htmlPath ?? TOC_FILENAME),
2123
+ sectionDepth: config.toc?.sectionDepth ?? 0,
2124
+ transform: {
2125
+ transformDocumentList: config.toc?.transformDocumentList,
2126
+ transformSectionList: config.toc?.transformSectionList
2127
+ }
2128
+ };
2129
+ const coverHtml = config.cover && ("htmlPath" in config.cover && !config.cover.htmlPath ? void 0 : upath5.resolve(
2130
+ workspaceDir,
2131
+ config.cover?.htmlPath || COVER_HTML_FILENAME
2132
+ ));
2133
+ const ensureCoverImage = (src) => {
2134
+ const absPath = src && upath5.resolve(entryContextDir, src);
2135
+ if (absPath) {
2136
+ statFileSync(absPath, {
2137
+ errorMessage: "Specified cover image does not exist"
2138
+ });
2139
+ }
2140
+ return absPath;
2141
+ };
2142
+ const projectTitle = config?.title ?? pkgJson?.name;
2143
+ const projectAuthor = config?.author ?? pkgJson?.author;
2144
+ const isContentsEntry = (entry) => entry.rel === "contents";
2145
+ const isCoverEntry = (entry) => entry.rel === "cover";
2146
+ const isArticleEntry = (entry) => !isContentsEntry(entry) && !isCoverEntry(entry);
2147
+ function parseEntry(entry) {
2148
+ const getInputInfo = (entryPath) => {
2149
+ if (/^https?:/.test(entryPath)) {
2150
+ return {
2151
+ type: "uri",
2152
+ href: entryPath,
2153
+ rootDir: upath5.join(workspaceDir, new URL(entryPath).host)
2154
+ };
2155
+ } else if (entryPath.startsWith("/")) {
2156
+ return {
2157
+ type: "uri",
2158
+ href: `${rootUrl}${entryPath}`,
2159
+ rootDir: upath5.join(workspaceDir, "localhost")
2160
+ };
2161
+ }
2162
+ const pathname = upath5.resolve(entryContextDir, entryPath);
2163
+ statFileSync(pathname);
2164
+ const contentType = mime(pathname);
2165
+ if (!isManuscriptMediaType(contentType)) {
2166
+ throw new Error(
2167
+ `Invalid manuscript type ${contentType} detected: ${entry}`
2168
+ );
2169
+ }
2170
+ return {
2171
+ type: "file",
2172
+ pathname,
2173
+ contentType,
2174
+ metadata: parseFileMetadata({
2175
+ contentType,
2176
+ sourcePath: pathname,
2177
+ workspaceDir,
2178
+ themesDir
2179
+ })
2180
+ };
2181
+ };
2182
+ const getTargetPath = (source) => {
2183
+ switch (source.type) {
2184
+ case "file":
2185
+ return upath5.resolve(
2186
+ workspaceDir,
2187
+ upath5.relative(entryContextDir, source.pathname).replace(/\.md$/, ".html")
2188
+ );
2189
+ case "uri": {
2190
+ const url = new URL(source.href, "a://dummy");
2191
+ let pathname = url.pathname;
2192
+ if (!/\.html?$/.test(pathname)) {
2193
+ pathname = `${pathname.replace(/\/$/, "")}/index.html`;
2194
+ }
2195
+ return upath5.join(source.rootDir, pathname);
2196
+ }
2197
+ default:
2198
+ return source;
2199
+ }
2200
+ };
2201
+ if ((isContentsEntry(entry) || isCoverEntry(entry)) && entry.path) {
2202
+ const source = upath5.resolve(entryContextDir, entry.path);
2203
+ try {
2204
+ statFileSync(source);
2205
+ } catch (error) {
2206
+ Logger.logWarn(
2207
+ `The "path" option is set but the file does not exist: ${source}
2208
+ Maybe you want to set the "output" field instead.`
2209
+ );
2210
+ entry.output = entry.path;
2211
+ entry.path = void 0;
2212
+ }
2213
+ }
2214
+ if (isContentsEntry(entry)) {
2215
+ const inputInfo = entry.path ? getInputInfo(entry.path) : void 0;
2216
+ const { metadata, ...template } = inputInfo || {};
2217
+ let target = entry.output ? upath5.resolve(workspaceDir, entry.output) : inputInfo && getTargetPath(inputInfo);
2218
+ const themes = entry.theme ? [entry.theme].flat().map(
2219
+ (theme) => parseTheme({
2220
+ theme,
2221
+ context,
2222
+ workspaceDir,
2223
+ themesDir
2224
+ })
2225
+ ) : metadata?.themes ?? [...rootThemes];
2226
+ themes.forEach((t) => themeIndexes.add(t));
2227
+ target ??= tocConfig.target;
2228
+ if (inputInfo?.type === "file" && pathEquals(inputInfo.pathname, target)) {
2229
+ const tmpPath = upath5.resolve(
2230
+ upath5.dirname(target),
2231
+ `${temporaryFilePrefix}${upath5.basename(target)}`
2232
+ );
2233
+ exportAliases.push({ source: tmpPath, target });
2234
+ touchTmpFile(tmpPath);
2235
+ target = tmpPath;
2236
+ }
2237
+ const parsedEntry = {
2238
+ rel: "contents",
2239
+ ...tocConfig,
2240
+ target,
2241
+ title: entry.title ?? metadata?.title ?? projectTitle,
2242
+ themes,
2243
+ pageBreakBefore: entry.pageBreakBefore,
2244
+ pageCounterReset: entry.pageCounterReset,
2245
+ ..."type" in template && { template }
2246
+ };
2247
+ return parsedEntry;
2248
+ }
2249
+ if (isCoverEntry(entry)) {
2250
+ const inputInfo = entry.path ? getInputInfo(entry.path) : void 0;
2251
+ const { metadata, ...template } = inputInfo || {};
2252
+ let target = entry.output ? upath5.resolve(workspaceDir, entry.output) : inputInfo && getTargetPath(inputInfo);
2253
+ const themes = entry.theme ? [entry.theme].flat().map(
2254
+ (theme) => parseTheme({
2255
+ theme,
2256
+ context,
2257
+ workspaceDir,
2258
+ themesDir
2259
+ })
2260
+ ) : metadata?.themes ?? [];
2261
+ themes.forEach((t) => themeIndexes.add(t));
2262
+ const coverImageSrc = ensureCoverImage(entry.imageSrc || cover?.src);
2263
+ if (!coverImageSrc) {
2264
+ throw new Error(
2265
+ `A CoverEntryConfig is set in the entry list but a location of cover file is not set. Please set 'cover' property in your config file.`
2266
+ );
2267
+ }
2268
+ target ??= upath5.resolve(
2269
+ workspaceDir,
2270
+ entry.path || coverHtml || COVER_HTML_FILENAME
2271
+ );
2272
+ if (inputInfo?.type === "file" && pathEquals(inputInfo.pathname, target)) {
2273
+ const tmpPath = upath5.resolve(
2274
+ upath5.dirname(target),
2275
+ `${temporaryFilePrefix}${upath5.basename(target)}`
2276
+ );
2277
+ exportAliases.push({ source: tmpPath, target });
2278
+ touchTmpFile(tmpPath);
2279
+ target = tmpPath;
2280
+ }
2281
+ const parsedEntry = {
2282
+ rel: "cover",
2283
+ target,
2284
+ title: entry.title ?? metadata?.title ?? projectTitle,
2285
+ themes,
2286
+ coverImageSrc,
2287
+ coverImageAlt: entry.imageAlt || cover?.name || COVER_HTML_IMAGE_ALT,
2288
+ pageBreakBefore: entry.pageBreakBefore,
2289
+ ..."type" in template && { template }
2290
+ };
2291
+ return parsedEntry;
2292
+ }
2293
+ if (isArticleEntry(entry)) {
2294
+ const inputInfo = getInputInfo(entry.path);
2295
+ const { metadata, ...source } = inputInfo;
2296
+ const target = entry.output ? upath5.resolve(workspaceDir, entry.output) : getTargetPath(inputInfo);
2297
+ const themes = entry.theme ? [entry.theme].flat().map(
2298
+ (theme) => parseTheme({ theme, context, workspaceDir, themesDir })
2299
+ ) : metadata?.themes ?? [...rootThemes];
2300
+ themes.forEach((t) => themeIndexes.add(t));
2301
+ const parsedEntry = {
2302
+ contentType: inputInfo.type === "file" ? inputInfo.contentType : "text/html",
2303
+ source,
2304
+ target,
2305
+ title: entry.title ?? metadata?.title ?? projectTitle,
2306
+ themes,
2307
+ ...entry.rel && { rel: entry.rel }
2308
+ };
2309
+ return parsedEntry;
2310
+ }
2311
+ return entry;
2312
+ }
2313
+ const entries = config.entry.map(parseEntry);
2314
+ let fallbackProjectTitle;
2315
+ if (!projectTitle) {
2316
+ if (entries.length === 1 && entries[0].title) {
2317
+ fallbackProjectTitle = entries[0].title;
2318
+ } else {
2319
+ fallbackProjectTitle = upath5.basename(outputs[0].path);
2320
+ }
2321
+ }
2322
+ if (!!config?.toc && !entries.find(({ rel }) => rel === "contents")) {
2323
+ entries.unshift({
2324
+ rel: "contents",
2325
+ ...tocConfig,
2326
+ themes: [...rootThemes]
2327
+ });
2328
+ }
2329
+ if (cover && coverHtml && !entries.find(({ rel }) => rel === "cover")) {
2330
+ entries.unshift({
2331
+ rel: "cover",
2332
+ target: coverHtml,
2333
+ title: projectTitle,
2334
+ themes: [],
2335
+ // Don't inherit rootThemes for cover documents
2336
+ coverImageSrc: ensureCoverImage(cover.src),
2337
+ coverImageAlt: cover.name
2338
+ });
2339
+ }
2340
+ return {
2341
+ workspaceDir,
2342
+ themesDir,
2343
+ entries,
2344
+ input: {
2345
+ format: "pub-manifest",
2346
+ entry: upath5.join(workspaceDir, MANIFEST_FILENAME)
2347
+ },
2348
+ viewerInput: {
2349
+ type: "webpub",
2350
+ manifestPath: upath5.join(workspaceDir, MANIFEST_FILENAME),
2351
+ needToGenerateManifest: true
2352
+ },
2353
+ exportAliases,
2354
+ title: projectTitle || fallbackProjectTitle,
2355
+ author: projectAuthor
2356
+ };
2357
+ }
2358
+
2359
+ // src/processor/compile.ts
2360
+ import { copy as copy3, move, remove as remove3 } from "fs-extra/esm";
2361
+ import fs9 from "node:fs";
2362
+ import picomatch from "picomatch";
2363
+ import prettier from "prettier";
2364
+ import parserHtml from "prettier/parser-html";
2365
+ import { glob as glob2 } from "tinyglobby";
2366
+ import upath9 from "upath";
2367
+ import MIMEType2 from "whatwg-mimetype";
2368
+
2369
+ // src/output/webbook.ts
2370
+ import { copy as copy2, remove as remove2 } from "fs-extra/esm";
2371
+ import { lookup as mime3 } from "mime-types";
2372
+ import fs7 from "node:fs";
2373
+ import { pathToFileURL as pathToFileURL6 } from "node:url";
2374
+ import { glob } from "tinyglobby";
2375
+ import upath8 from "upath";
2376
+
2377
+ // src/processor/html.tsx
2378
+ import jsdom, {
2379
+ ResourceLoader as BaseResourceLoader,
2380
+ JSDOM
2381
+ } from "@vivliostyle/jsdom";
2382
+ import DOMPurify from "dompurify";
2383
+ import { toHtml } from "hast-util-to-html";
2384
+ import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL4 } from "node:url";
2385
+ import upath6 from "upath";
2386
+ import MIMEType from "whatwg-mimetype";
2387
+ import { jsx, jsxs } from "hastscript/jsx-runtime";
2388
+ var createVirtualConsole = (onError) => {
2389
+ const virtualConsole = new jsdom.VirtualConsole();
2390
+ virtualConsole.on("error", (message) => {
2391
+ Logger.debug("[JSDOM Console] error:", message);
2392
+ });
2393
+ virtualConsole.on("warn", (message) => {
2394
+ Logger.debug("[JSDOM Console] warn:", message);
2395
+ });
2396
+ virtualConsole.on("log", (message) => {
2397
+ Logger.debug("[JSDOM Console] log:", message);
2398
+ });
2399
+ virtualConsole.on("info", (message) => {
2400
+ Logger.debug("[JSDOM Console] info:", message);
2401
+ });
2402
+ virtualConsole.on("dir", (message) => {
2403
+ Logger.debug("[JSDOM Console] dir:", message);
2404
+ });
2405
+ virtualConsole.on("jsdomError", (error) => {
2406
+ if (error.message === "Could not parse CSS stylesheet") {
2407
+ return;
2408
+ }
2409
+ onError(
2410
+ new DetailError(
2411
+ "Error occurred when loading content",
2412
+ error.stack ?? error.message
2413
+ )
2414
+ );
2415
+ });
2416
+ return virtualConsole;
2417
+ };
2418
+ var htmlPurify = DOMPurify(new JSDOM("").window);
2419
+ var ResourceLoader = class extends BaseResourceLoader {
2420
+ fetcherMap = /* @__PURE__ */ new Map();
2421
+ fetch(url, options) {
2422
+ Logger.debug(`[JSDOM] Fetching resource: ${url}`);
2423
+ const fetcher = super.fetch(url, options);
2424
+ if (fetcher) {
2425
+ this.fetcherMap.set(url, fetcher);
2426
+ }
2427
+ return fetcher;
2428
+ }
2429
+ static async saveFetchedResources({
2430
+ fetcherMap,
2431
+ rootUrl,
2432
+ outputDir,
2433
+ onError
2434
+ }) {
2435
+ const rootHref = /^https?:/i.test(new URL(rootUrl).protocol) ? new URL("/", rootUrl).href : new URL(".", rootUrl).href;
2436
+ const normalizeToLocalPath = (urlString, mimeType) => {
2437
+ let url = new URL(urlString);
2438
+ url.hash = "";
2439
+ if (mimeType === "text/html" && !/\.html?$/.test(url.pathname)) {
2440
+ url.pathname = `${url.pathname.replace(/\/$/, "")}/index.html`;
2441
+ }
2442
+ let relTarget = upath6.relative(rootHref, url.href);
2443
+ return decodeURI(relTarget);
2444
+ };
2445
+ const fetchedResources = [];
2446
+ await Promise.allSettled(
2447
+ [...fetcherMap.entries()].flatMap(async ([url, fetcher]) => {
2448
+ if (!url.startsWith(rootHref)) {
2449
+ return [];
2450
+ }
2451
+ return fetcher.then(async (buffer) => {
2452
+ let encodingFormat;
2453
+ try {
2454
+ const contentType = fetcher.response?.headers["content-type"];
2455
+ if (contentType) {
2456
+ encodingFormat = new MIMEType(contentType).essence;
2457
+ }
2458
+ } catch (e) {
2459
+ }
2460
+ const relTarget = normalizeToLocalPath(url, encodingFormat);
2461
+ const target = upath6.join(outputDir, relTarget);
2462
+ fetchedResources.push({ url: relTarget, encodingFormat });
2463
+ writeFileIfChanged(target, buffer);
2464
+ }).catch(onError);
2465
+ })
2466
+ );
2467
+ return fetchedResources;
2468
+ }
2469
+ };
2470
+ async function getJsdomFromUrlOrFile({
2471
+ src,
2472
+ resourceLoader,
2473
+ virtualConsole = createVirtualConsole((error) => {
2474
+ throw error;
2475
+ })
2476
+ }) {
2477
+ const url = isValidUri(src) ? new URL(src) : pathToFileURL4(src);
2478
+ let dom;
2479
+ if (url.protocol === "http:" || url.protocol === "https:") {
2480
+ dom = await JSDOM.fromURL(src, {
2481
+ virtualConsole,
2482
+ resources: resourceLoader
2483
+ });
2484
+ } else if (url.protocol === "file:") {
2485
+ if (resourceLoader) {
2486
+ const file = resourceLoader._readFile(fileURLToPath2(url));
2487
+ resourceLoader.fetcherMap.set(url.href, file);
2488
+ }
2489
+ dom = await JSDOM.fromFile(fileURLToPath2(url), {
2490
+ virtualConsole,
2491
+ resources: resourceLoader,
2492
+ contentType: "text/html; charset=UTF-8"
2493
+ });
2494
+ } else {
2495
+ throw new Error(`Unsupported protocol: ${url.protocol}`);
2496
+ }
2497
+ return dom;
2498
+ }
2499
+ function getJsdomFromString({
2500
+ html,
2501
+ virtualConsole = createVirtualConsole((error) => {
2502
+ throw error;
2503
+ })
2504
+ }) {
2505
+ return new JSDOM(html, {
2506
+ virtualConsole
2507
+ });
2508
+ }
2509
+ async function getStructuredSectionFromHtml(htmlPath, href) {
2510
+ const dom = await getJsdomFromUrlOrFile({ src: htmlPath });
2511
+ const { document } = dom.window;
2512
+ const allHeadings = [...document.querySelectorAll("h1, h2, h3, h4, h5, h6")].filter((el) => {
2513
+ return !el.matches("blockquote *");
2514
+ }).sort((a, b) => {
2515
+ const position = a.compareDocumentPosition(b);
2516
+ return position & 2 ? 1 : position & 4 ? -1 : 0;
2517
+ });
2518
+ function traverse(headers) {
2519
+ if (headers.length === 0) {
2520
+ return [];
2521
+ }
2522
+ const [head, ...tail] = headers;
2523
+ const section = head.parentElement;
2524
+ const id = head.id || section.id;
2525
+ const level = Number(head.tagName.slice(1));
2526
+ let i = tail.findIndex((s) => Number(s.tagName.slice(1)) <= level);
2527
+ i = i === -1 ? tail.length : i;
2528
+ return [
2529
+ {
2530
+ headingHtml: htmlPurify.sanitize(head.innerHTML),
2531
+ headingText: head.textContent?.trim().replace(/\s+/g, " ") || "",
2532
+ level,
2533
+ ...href && id && { href: `${href}#${encodeURIComponent(id)}` },
2534
+ ...id && { id },
2535
+ children: traverse(tail.slice(0, i))
2536
+ },
2537
+ ...traverse(tail.slice(i))
2538
+ ];
2539
+ }
2540
+ return traverse(allHeadings);
2541
+ }
2542
+ var getTocHtmlStyle = ({
2543
+ pageBreakBefore,
2544
+ pageCounterReset
2545
+ }) => {
2546
+ if (!pageBreakBefore && typeof pageCounterReset !== "number") {
2547
+ return null;
2548
+ }
2549
+ return (
2550
+ /* css */
2551
+ `
2552
+ ${pageBreakBefore ? `:root {
2553
+ break-before: ${pageBreakBefore};
2554
+ }` : ""}
2555
+ ${typeof pageCounterReset === "number" ? `@page :nth(1) {
2556
+ counter-reset: page ${Math.floor(pageCounterReset - 1)};
2557
+ }` : ""}
2558
+ `
2559
+ );
2560
+ };
2561
+ var defaultTocTransform = {
2562
+ transformDocumentList: (nodeList) => (propsList) => {
2563
+ return /* @__PURE__ */ jsx("ol", { children: nodeList.map((a, i) => [a, propsList[i]]).flatMap(
2564
+ ([{ href, title, sections }, { children, ...otherProps }]) => {
2565
+ if (sections?.length === 1 && sections[0].level === 1) {
2566
+ return [children].flat().flatMap((e) => {
2567
+ if (e.type === "element" && e.tagName === "ol") {
2568
+ return e.children;
2569
+ }
2570
+ return e;
2571
+ });
2572
+ }
2573
+ return /* @__PURE__ */ jsxs("li", { ...otherProps, children: [
2574
+ /* @__PURE__ */ jsx("a", { ...{ href }, children: title }),
2575
+ children
2576
+ ] });
2577
+ }
2578
+ ) });
2579
+ },
2580
+ transformSectionList: (nodeList) => (propsList) => {
2581
+ return /* @__PURE__ */ jsx("ol", { children: nodeList.map((a, i) => [a, propsList[i]]).map(
2582
+ ([{ headingHtml, href, level }, { children, ...otherProps }]) => {
2583
+ const headingContent = {
2584
+ type: "raw",
2585
+ value: headingHtml
2586
+ };
2587
+ return /* @__PURE__ */ jsxs("li", { ...otherProps, "data-section-level": level, children: [
2588
+ href ? /* @__PURE__ */ jsx("a", { ...{ href }, children: headingContent }) : /* @__PURE__ */ jsx("span", { children: headingContent }),
2589
+ children
2590
+ ] });
2591
+ }
2592
+ ) });
2593
+ }
2594
+ };
2595
+ function generateDefaultTocHtml({
2596
+ language,
2597
+ title
2598
+ }) {
2599
+ const toc = /* @__PURE__ */ jsxs("html", { lang: language, children: [
2600
+ /* @__PURE__ */ jsxs("head", { children: [
2601
+ /* @__PURE__ */ jsx("meta", { charset: "utf-8" }),
2602
+ /* @__PURE__ */ jsx("title", { children: title || "" }),
2603
+ /* @__PURE__ */ jsx("style", { "data-vv-style": true })
2604
+ ] }),
2605
+ /* @__PURE__ */ jsxs("body", { children: [
2606
+ /* @__PURE__ */ jsx("h1", { children: title || "" }),
2607
+ /* @__PURE__ */ jsx("nav", { id: "toc", role: "doc-toc" })
2608
+ ] })
2609
+ ] });
2610
+ return toHtml(toc);
2611
+ }
2612
+ async function generateTocListSection({
2613
+ entries,
2614
+ distDir,
2615
+ sectionDepth,
2616
+ transform = {}
2617
+ }) {
2618
+ const {
2619
+ transformDocumentList = defaultTocTransform.transformDocumentList,
2620
+ transformSectionList = defaultTocTransform.transformSectionList
2621
+ } = transform;
2622
+ const structure = await Promise.all(
2623
+ entries.map(async (entry) => {
2624
+ const href = encodeURI(upath6.relative(distDir, entry.target));
2625
+ const sections = sectionDepth >= 1 ? await getStructuredSectionFromHtml(entry.target, href) : [];
2626
+ return {
2627
+ title: entry.title || upath6.basename(entry.target, ".html"),
2628
+ href: encodeURI(upath6.relative(distDir, entry.target)),
2629
+ sections,
2630
+ children: []
2631
+ // TODO
2632
+ };
2633
+ })
2634
+ );
2635
+ const docToc = transformDocumentList(structure)(
2636
+ structure.map((doc) => {
2637
+ function renderSectionList(sections) {
2638
+ const nodeList = sections.flatMap((section) => {
2639
+ if (section.level > sectionDepth) {
2640
+ return [];
2641
+ }
2642
+ return section;
2643
+ });
2644
+ if (nodeList.length === 0) {
2645
+ return [];
2646
+ }
2647
+ return transformSectionList(nodeList)(
2648
+ nodeList.map((node) => ({
2649
+ children: [renderSectionList(node.children || [])].flat()
2650
+ }))
2651
+ );
2652
+ }
2653
+ return {
2654
+ children: [renderSectionList(doc.sections || [])].flat()
2655
+ };
2656
+ })
2657
+ );
2658
+ return toHtml(docToc, { allowDangerousHtml: true });
2659
+ }
2660
+ async function processTocHtml(dom, {
2661
+ manifestPath,
2662
+ tocTitle,
2663
+ styleOptions = {},
2664
+ entries,
2665
+ distDir,
2666
+ sectionDepth,
2667
+ transform
2668
+ }) {
2669
+ const { document } = dom.window;
2670
+ if (!document.querySelector(
2671
+ 'link[rel="publication"][type="application/ld+json"]'
2672
+ )) {
2673
+ const l = document.createElement("link");
2674
+ l.setAttribute("rel", "publication");
2675
+ l.setAttribute("type", "application/ld+json");
2676
+ l.setAttribute("href", encodeURI(upath6.relative(distDir, manifestPath)));
2677
+ document.head.appendChild(l);
2678
+ }
2679
+ const style = document.querySelector("style[data-vv-style]");
2680
+ if (style) {
2681
+ const textContent = getTocHtmlStyle(styleOptions);
2682
+ if (textContent) {
2683
+ style.textContent = textContent;
2684
+ } else {
2685
+ style.remove();
2686
+ }
2687
+ }
2688
+ const nav = document.querySelector('nav, [role="doc-toc"]');
2689
+ if (nav && !nav.hasChildNodes()) {
2690
+ const h2 = document.createElement("h2");
2691
+ h2.textContent = tocTitle;
2692
+ nav.appendChild(h2);
2693
+ nav.innerHTML += await generateTocListSection({
2694
+ entries,
2695
+ distDir,
2696
+ sectionDepth,
2697
+ transform
2698
+ });
2699
+ }
2700
+ return dom;
2701
+ }
2702
+ var getCoverHtmlStyle = ({
2703
+ pageBreakBefore
2704
+ }) => (
2705
+ /* css */
2706
+ `
2707
+ ${pageBreakBefore ? `:root {
2708
+ break-before: ${pageBreakBefore};
2709
+ }` : ""}
2710
+ body {
2711
+ margin: 0;
2712
+ }
2713
+ [role="doc-cover"] {
2714
+ display: block;
2715
+ width: 100vw;
2716
+ height: 100vh;
2717
+ object-fit: contain;
2718
+ }
2719
+ @page {
2720
+ margin: 0;
2721
+ }
2722
+ `
2723
+ );
2724
+ function generateDefaultCoverHtml({
2725
+ language,
2726
+ title
2727
+ }) {
2728
+ const toc = /* @__PURE__ */ jsxs("html", { lang: language, children: [
2729
+ /* @__PURE__ */ jsxs("head", { children: [
2730
+ /* @__PURE__ */ jsx("meta", { charset: "utf-8" }),
2731
+ /* @__PURE__ */ jsx("title", { children: title || "" }),
2732
+ /* @__PURE__ */ jsx("style", { "data-vv-style": true })
2733
+ ] }),
2734
+ /* @__PURE__ */ jsx("body", { children: /* @__PURE__ */ jsx("section", { role: "region", "aria-label": "Cover", children: /* @__PURE__ */ jsx("img", { role: "doc-cover" }) }) })
2735
+ ] });
2736
+ return toHtml(toc);
2737
+ }
2738
+ async function processCoverHtml(dom, {
2739
+ imageSrc,
2740
+ imageAlt,
2741
+ styleOptions = {}
2742
+ }) {
2743
+ const { document } = dom.window;
2744
+ const style = document.querySelector("style[data-vv-style]");
2745
+ if (style) {
2746
+ const textContent = getCoverHtmlStyle(styleOptions);
2747
+ if (textContent) {
2748
+ style.textContent = textContent;
2749
+ } else {
2750
+ style.remove();
2751
+ }
2752
+ }
2753
+ const cover = document.querySelector('img[role="doc-cover"]');
2754
+ if (cover && !cover.hasAttribute("src")) {
2755
+ cover.setAttribute("src", encodeURI(imageSrc));
2756
+ }
2757
+ if (cover && !cover.hasAttribute("alt")) {
2758
+ cover.setAttribute("alt", imageAlt);
2759
+ }
2760
+ return dom;
2761
+ }
2762
+ async function processManuscriptHtml(dom, {
2763
+ title,
2764
+ style,
2765
+ contentType,
2766
+ language
2767
+ }) {
2768
+ const { document } = dom.window;
2769
+ if (title) {
2770
+ if (!document.querySelector("title")) {
2771
+ const t = document.createElement("title");
2772
+ document.head.appendChild(t);
2773
+ }
2774
+ document.title = title;
2775
+ }
2776
+ for (const s of style ?? []) {
2777
+ const l = document.createElement("link");
2778
+ l.setAttribute("rel", "stylesheet");
2779
+ l.setAttribute("type", "text/css");
2780
+ l.setAttribute("href", encodeURI(s));
2781
+ document.head.appendChild(l);
2782
+ }
2783
+ if (language) {
2784
+ if (contentType === "application/xhtml+xml") {
2785
+ if (!document.documentElement.getAttribute("xml:lang")) {
2786
+ document.documentElement.setAttribute("lang", language);
2787
+ document.documentElement.setAttribute("xml:lang", language);
2788
+ }
2789
+ } else {
2790
+ if (!document.documentElement.getAttribute("lang")) {
2791
+ document.documentElement.setAttribute("lang", language);
2792
+ }
2793
+ }
2794
+ }
2795
+ return dom;
2796
+ }
2797
+ async function fetchLinkedPublicationManifest({
2798
+ dom,
2799
+ resourceLoader,
2800
+ baseUrl
2801
+ }) {
2802
+ const { document } = dom.window;
2803
+ const linkEl = document.querySelector('link[href][rel="publication"]');
2804
+ if (!linkEl) {
2805
+ return null;
2806
+ }
2807
+ const href = linkEl.getAttribute("href").trim();
2808
+ let manifest;
2809
+ let manifestUrl = baseUrl;
2810
+ if (href.startsWith("#")) {
2811
+ const scriptEl = document.getElementById(href.slice(1));
2812
+ if (scriptEl?.getAttribute("type") !== "application/ld+json") {
2813
+ return null;
2814
+ }
2815
+ Logger.debug(`Found embedded publication manifest: ${href}`);
2816
+ try {
2817
+ manifest = JSON.parse(scriptEl.innerHTML);
2818
+ } catch (error) {
2819
+ const thrownError = error;
2820
+ throw new DetailError(
2821
+ "Failed to parse manifest data",
2822
+ typeof thrownError.stack
2823
+ );
2824
+ }
2825
+ } else {
2826
+ Logger.debug(`Found linked publication manifest: ${href}`);
2827
+ const url = new URL(href, baseUrl);
2828
+ manifestUrl = url.href;
2829
+ const buffer = await resourceLoader.fetch(url.href);
2830
+ if (!buffer) {
2831
+ throw new Error(`Failed to fetch manifest JSON file: ${url.href}`);
2832
+ }
2833
+ const manifestJson = buffer.toString();
2834
+ try {
2835
+ manifest = JSON.parse(manifestJson);
2836
+ } catch (error) {
2837
+ const thrownError = error;
2838
+ throw new DetailError(
2839
+ "Failed to parse manifest data",
2840
+ typeof thrownError.stack
2841
+ );
2842
+ }
2843
+ }
2844
+ try {
2845
+ assertPubManifestSchema(manifest);
2846
+ } catch (error) {
2847
+ Logger.logWarn(
2848
+ `Publication manifest validation failed. Processing continues, but some problems may occur.
2849
+ ${error}`
2850
+ );
2851
+ }
2852
+ return {
2853
+ manifest: decodePublicationManifest(manifest),
2854
+ manifestUrl
2855
+ };
2856
+ }
2857
+ function parseTocDocument(dom) {
2858
+ const { document } = dom.window;
2859
+ const docTocEl = document.querySelectorAll('[role="doc-toc"]');
2860
+ if (docTocEl.length === 0) {
2861
+ return null;
2862
+ }
2863
+ const tocRoot = docTocEl.item(0);
2864
+ const parseTocItem = (element) => {
2865
+ if (element.tagName !== "LI") {
2866
+ return null;
2867
+ }
2868
+ const label = element.children.item(0);
2869
+ const ol = element.children.item(1);
2870
+ if (!label || label.tagName !== "A" && label.tagName !== "SPAN") {
2871
+ return null;
2872
+ }
2873
+ if (!ol || ol.tagName !== "OL") {
2874
+ return { element, label };
2875
+ }
2876
+ const children = Array.from(ol.children).reduce((acc, val) => {
2877
+ if (!acc) {
2878
+ return acc;
2879
+ }
2880
+ const res = parseTocItem(val);
2881
+ return res && [...acc, res];
2882
+ }, []);
2883
+ return children && {
2884
+ element,
2885
+ label,
2886
+ children
2887
+ };
2888
+ };
2889
+ let heading;
2890
+ for (let child of Array.from(tocRoot.children)) {
2891
+ if (child.tagName === "OL") {
2892
+ const children = Array.from(child.children).reduce((acc, val) => {
2893
+ if (!acc) {
2894
+ return acc;
2895
+ }
2896
+ const res = parseTocItem(val);
2897
+ return res && [...acc, res];
2898
+ }, []);
2899
+ return children && { element: tocRoot, heading, children };
2900
+ } else if (["H1", "H2", "H3", "H4", "H5", "H6", "HGROUP"].includes(child.tagName)) {
2901
+ heading = child;
2902
+ } else {
2903
+ return null;
2904
+ }
2905
+ }
2906
+ return null;
2907
+ }
2908
+ function parsePageListDocument(dom) {
2909
+ const { document } = dom.window;
2910
+ const docPageListEl = document.querySelectorAll('[role="doc-pagelist"]');
2911
+ if (docPageListEl.length === 0) {
2912
+ return null;
2913
+ }
2914
+ const pageListRoot = docPageListEl.item(0);
2915
+ let heading;
2916
+ for (let child of Array.from(pageListRoot.children)) {
2917
+ if (child.tagName === "OL") {
2918
+ const children = Array.from(child.children).reduce((acc, element) => {
2919
+ return acc && (element.tagName === "LI" ? [...acc, { element }] : null);
2920
+ }, []);
2921
+ return children && { element: pageListRoot, heading, children };
2922
+ } else if (["H1", "H2", "H3", "H4", "H5", "H6", "HGROUP"].includes(child.tagName)) {
2923
+ heading = child;
2924
+ } else {
2925
+ return null;
2926
+ }
2927
+ }
2928
+ return null;
2929
+ }
2930
+
2931
+ // src/output/epub.ts
2932
+ import archiver from "archiver";
2933
+ import { lookup as lookupLanguage } from "bcp-47-match";
2934
+ import { XMLBuilder } from "fast-xml-parser";
2935
+ import { copy, remove } from "fs-extra/esm";
2936
+ import GithubSlugger from "github-slugger";
2937
+ import { lookup as mime2 } from "mime-types";
2938
+ import fs6 from "node:fs";
2939
+ import { pathToFileURL as pathToFileURL5 } from "node:url";
2940
+ import upath7 from "upath";
2941
+ import { v4 as uuid } from "uuid";
2942
+ import serializeToXml from "w3c-xmlserializer";
2943
+ var TOC_ID = "toc";
2944
+ var LANDMARKS_ID = "landmarks";
2945
+ var PAGELIST_ID = "page-list";
2946
+ var COVER_IMAGE_MIMETYPES = [
2947
+ "image/gif",
2948
+ "image/jpeg",
2949
+ "image/png",
2950
+ "image/svg+xml",
2951
+ "image/webp"
2952
+ ];
2953
+ var changeExtname = (filepath, newExt) => {
2954
+ let ext = upath7.extname(filepath);
2955
+ return `${filepath.slice(0, -ext.length)}${newExt}`;
2956
+ };
2957
+ var getRelativeHref = (target, baseUrl, rootUrl) => {
2958
+ const absBasePath = upath7.join("/", baseUrl);
2959
+ const absRootPath = upath7.join("/", rootUrl);
2960
+ const hrefUrl = new URL(encodeURI(target), pathToFileURL5(absBasePath));
2961
+ if (hrefUrl.protocol !== "file:") {
2962
+ return target;
2963
+ }
2964
+ if (/\.html?$/.test(hrefUrl.pathname)) {
2965
+ hrefUrl.pathname = changeExtname(hrefUrl.pathname, ".xhtml");
2966
+ }
2967
+ const pathname = upath7.posix.relative(
2968
+ pathToFileURL5(upath7.dirname(absRootPath)).pathname,
2969
+ hrefUrl.pathname
2970
+ );
2971
+ return `${pathname}${hrefUrl.search}${hrefUrl.hash}`;
2972
+ };
2973
+ var normalizeLocalizableString = (value, availableLanguages) => {
2974
+ if (!value) {
2975
+ return;
2976
+ }
2977
+ const values = [value].flat().map((value2) => typeof value2 === "string" ? { value: value2 } : value2);
2978
+ const localizedValues = values.filter(
2979
+ (v3) => !!v3.language
2980
+ );
2981
+ const preferredLang = lookupLanguage(
2982
+ localizedValues.map((v3) => v3.language),
2983
+ availableLanguages
2984
+ );
2985
+ if (preferredLang) {
2986
+ return localizedValues[localizedValues.findIndex((v3) => v3.language === preferredLang)].value;
2987
+ }
2988
+ return values.find((v3) => !v3.language)?.value;
2989
+ };
2990
+ var appendManifestProperty = (entry, newProperty) => {
2991
+ entry.properties = entry.properties ? Array.from(/* @__PURE__ */ new Set([...entry.properties.split(" "), newProperty])).join(
2992
+ " "
2993
+ ) : newProperty;
2994
+ };
2995
+ async function exportEpub({
2996
+ webpubDir,
2997
+ entryHtmlFile,
2998
+ manifest,
2999
+ relManifestPath,
3000
+ target,
3001
+ epubVersion
3002
+ }) {
3003
+ Logger.debug("Export EPUB", {
3004
+ webpubDir,
3005
+ entryHtmlFile,
3006
+ relManifestPath,
3007
+ target,
3008
+ epubVersion
3009
+ });
3010
+ const [tmpDir] = await useTmpDirectory();
3011
+ fs6.mkdirSync(upath7.join(tmpDir, "META-INF"), { recursive: true });
3012
+ await copy(webpubDir, upath7.join(tmpDir, "EPUB"));
3013
+ const uid = `urn:uuid:${uuid()}`;
3014
+ const entryHtmlRelPath = entryHtmlFile && upath7.relative(webpubDir, upath7.resolve(webpubDir, entryHtmlFile));
3015
+ const findPublicationLink = (relType, list, filter) => [list].flat().find(
3016
+ (e) => typeof e === "object" && e.rel === relType && (!filter || filter(e))
3017
+ );
3018
+ const tocResource = findPublicationLink("contents", [
3019
+ ...[manifest.readingOrder || []].flat(),
3020
+ ...[manifest.resources || []].flat()
3021
+ ]);
3022
+ const pageListResource = findPublicationLink("pagelist", [
3023
+ ...[manifest.readingOrder || []].flat(),
3024
+ ...[manifest.resources || []].flat()
3025
+ ]);
3026
+ const pictureCoverResource = findPublicationLink(
3027
+ "cover",
3028
+ manifest.resources,
3029
+ (e) => COVER_IMAGE_MIMETYPES.includes(e.encodingFormat || mime2(e.url) || "")
3030
+ );
3031
+ const htmlCoverResource = findPublicationLink(
3032
+ "cover",
3033
+ [
3034
+ ...[manifest.readingOrder || []].flat(),
3035
+ ...[manifest.resources || []].flat()
3036
+ ],
3037
+ (e) => /\.html?$/.test(e.url)
3038
+ );
3039
+ const manifestItem = [
3040
+ ...[manifest.links || []].flat(),
3041
+ ...[manifest.readingOrder || []].flat(),
3042
+ ...[manifest.resources || []].flat()
3043
+ ].reduce(
3044
+ (acc, val) => {
3045
+ const { url, encodingFormat } = typeof val === "string" ? { url: val } : val;
3046
+ try {
3047
+ new URL(url);
3048
+ return acc;
3049
+ } catch (e) {
3050
+ }
3051
+ if (!fs6.existsSync(upath7.join(tmpDir, "EPUB", url))) {
3052
+ return acc;
3053
+ }
3054
+ const mediaType = encodingFormat || mime2(url) || "text/plain";
3055
+ acc[url] = {
3056
+ href: url,
3057
+ mediaType
3058
+ };
3059
+ if (/\.html?$/.test(url)) {
3060
+ acc[url].href = changeExtname(url, ".xhtml");
3061
+ acc[url].mediaType = "application/xhtml+xml";
3062
+ }
3063
+ if (url === pictureCoverResource?.url) {
3064
+ acc[url].properties = "cover-image";
3065
+ }
3066
+ return acc;
3067
+ },
3068
+ {}
3069
+ );
3070
+ const htmlFiles = Object.keys(manifestItem).filter(
3071
+ (url) => /\.html?$/.test(url)
3072
+ );
3073
+ let tocHtml = htmlFiles.find((f) => f === tocResource?.url);
3074
+ const readingOrder = [manifest.readingOrder || entryHtmlRelPath].flat().flatMap((v3) => v3 ? typeof v3 === "string" ? { url: v3 } : v3 : []);
3075
+ if (!tocHtml) {
3076
+ Logger.logWarn(
3077
+ "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."
3078
+ );
3079
+ tocHtml = htmlFiles.find((f) => f === entryHtmlRelPath) || readingOrder[0].url;
3080
+ }
3081
+ const spineItems = readingOrder.map(({ url }) => ({
3082
+ href: changeExtname(url, ".xhtml")
3083
+ }));
3084
+ if (!(tocHtml in manifestItem)) {
3085
+ manifestItem[tocHtml] = {
3086
+ href: changeExtname(tocHtml, ".xhtml"),
3087
+ mediaType: "application/xhtml+xml"
3088
+ };
3089
+ }
3090
+ appendManifestProperty(manifestItem[tocHtml], "nav");
3091
+ const landmarks = [
3092
+ {
3093
+ type: "toc",
3094
+ href: `${manifestItem[tocHtml].href}#${TOC_ID}`,
3095
+ text: EPUB_LANDMARKS_TOC_ENTRY
3096
+ }
3097
+ ];
3098
+ if (htmlCoverResource) {
3099
+ landmarks.push({
3100
+ type: "cover",
3101
+ href: changeExtname(htmlCoverResource.url, ".xhtml"),
3102
+ text: EPUB_LANDMARKS_COVER_ENTRY
3103
+ });
3104
+ }
3105
+ const contextDir = upath7.join(tmpDir, "EPUB");
3106
+ const processHtml = async (target2) => {
3107
+ let parseResult;
3108
+ try {
3109
+ parseResult = await transpileHtmlToXhtml({
3110
+ target: target2,
3111
+ contextDir
3112
+ });
3113
+ } catch (error) {
3114
+ const thrownError = error;
3115
+ throw new DetailError(
3116
+ `Failed to transpile document to XHTML: ${target2}`,
3117
+ thrownError.stack ?? thrownError.message
3118
+ );
3119
+ }
3120
+ if (parseResult.hasMathmlContent) {
3121
+ appendManifestProperty(manifestItem[target2], "mathml");
3122
+ }
3123
+ if (parseResult.hasRemoteResources) {
3124
+ appendManifestProperty(manifestItem[target2], "remote-resources");
3125
+ }
3126
+ if (parseResult.hasScriptedContent) {
3127
+ appendManifestProperty(manifestItem[target2], "scripted");
3128
+ }
3129
+ if (parseResult.hasSvgContent) {
3130
+ appendManifestProperty(manifestItem[target2], "svg");
3131
+ }
3132
+ return parseResult;
3133
+ };
3134
+ const processResult = {};
3135
+ Logger.debug(`Transpiling ToC HTML to XHTML: ${tocHtml}`);
3136
+ processResult[tocHtml] = await processHtml(tocHtml);
3137
+ for (const target2 of htmlFiles.filter((f) => f !== tocHtml)) {
3138
+ Logger.debug(`Transpiling HTML to XHTML: ${target2}`);
3139
+ processResult[target2] = await processHtml(target2);
3140
+ }
3141
+ const { document: entryDocument } = processResult[tocHtml].dom.window;
3142
+ const docLanguages = [manifest.inLanguage].flat().filter((v3) => Boolean(v3));
3143
+ if (docLanguages.length === 0) {
3144
+ docLanguages.push(entryDocument.documentElement.lang || "en");
3145
+ }
3146
+ const docTitle = normalizeLocalizableString(manifest.name, docLanguages) || entryDocument.title;
3147
+ if (!docTitle) {
3148
+ throw new Error("EPUB must have a title of one or more characters");
3149
+ }
3150
+ const { tocResourceTree } = await processTocDocument({
3151
+ dom: processResult[tocHtml].dom,
3152
+ target: tocHtml,
3153
+ contextDir,
3154
+ readingOrder,
3155
+ docLanguages,
3156
+ landmarks
3157
+ });
3158
+ const pageListHtml = pageListResource?.url || entryHtmlRelPath;
3159
+ if (pageListHtml && pageListHtml in processResult) {
3160
+ await processPagelistDocument({
3161
+ dom: processResult[pageListHtml].dom,
3162
+ target: pageListHtml,
3163
+ contextDir
3164
+ });
3165
+ }
3166
+ if (relManifestPath) {
3167
+ await remove(upath7.join(tmpDir, "EPUB", relManifestPath));
3168
+ delete manifestItem[relManifestPath];
3169
+ }
3170
+ fs6.writeFileSync(
3171
+ upath7.join(tmpDir, "META-INF/container.xml"),
3172
+ EPUB_CONTAINER_XML,
3173
+ "utf8"
3174
+ );
3175
+ Logger.debug(`Generating content.opf`);
3176
+ fs6.writeFileSync(
3177
+ upath7.join(tmpDir, "EPUB/content.opf"),
3178
+ buildEpubPackageDocument({
3179
+ epubVersion,
3180
+ uid,
3181
+ docTitle,
3182
+ docLanguages,
3183
+ manifest,
3184
+ spineItems,
3185
+ manifestItems: Object.values(manifestItem)
3186
+ }),
3187
+ "utf8"
3188
+ );
3189
+ await compressEpub({ target, sourceDir: tmpDir });
3190
+ }
3191
+ async function writeAsXhtml(dom, absPath) {
3192
+ const xhtml = `${XML_DECLARATION}
3193
+ ${serializeToXml(dom.window.document)}`;
3194
+ await fs6.promises.writeFile(changeExtname(absPath, ".xhtml"), xhtml, "utf8");
3195
+ }
3196
+ async function transpileHtmlToXhtml({
3197
+ target,
3198
+ contextDir
3199
+ }) {
3200
+ const absPath = upath7.join(contextDir, target);
3201
+ const dom = await getJsdomFromUrlOrFile({ src: absPath });
3202
+ const { document } = dom.window;
3203
+ document.documentElement.removeAttribute("xmlns");
3204
+ document.documentElement.setAttribute("xmlns:epub", EPUB_NS);
3205
+ document.querySelectorAll("a[href]").forEach((el) => {
3206
+ const href = decodeURI(el.getAttribute("href"));
3207
+ el.setAttribute("href", getRelativeHref(href, target, target));
3208
+ });
3209
+ await writeAsXhtml(dom, absPath);
3210
+ await fs6.promises.unlink(absPath);
3211
+ return {
3212
+ dom,
3213
+ // FIXME: Yes, I recognize this implementation is inadequate.
3214
+ hasMathmlContent: !!document.querySelector("math"),
3215
+ hasRemoteResources: !!document.querySelector(
3216
+ '[src^="http://"], [src^="https://"]'
3217
+ ),
3218
+ hasScriptedContent: !!document.querySelector("script, form"),
3219
+ hasSvgContent: !!document.querySelector("svg")
3220
+ };
3221
+ }
3222
+ function replaceWithNavElement(dom, el) {
3223
+ const nav = dom.window.document.createElement("nav");
3224
+ while (el.firstChild) {
3225
+ nav.appendChild(el.firstChild);
3226
+ }
3227
+ for (let i = 0; i < el.attributes.length; i++) {
3228
+ nav.attributes.setNamedItem(el.attributes[i].cloneNode());
3229
+ }
3230
+ el.parentNode?.replaceChild(nav, el);
3231
+ return nav;
3232
+ }
3233
+ async function processTocDocument({
3234
+ dom,
3235
+ target,
3236
+ contextDir,
3237
+ readingOrder,
3238
+ docLanguages,
3239
+ landmarks
3240
+ }) {
3241
+ const { document } = dom.window;
3242
+ let tocResourceTree = null;
3243
+ if (!document.querySelector("nav[epub:type]")) {
3244
+ tocResourceTree = parseTocDocument(dom);
3245
+ if (tocResourceTree) {
3246
+ const nav = replaceWithNavElement(dom, tocResourceTree.element);
3247
+ nav.setAttribute("id", TOC_ID);
3248
+ nav.setAttribute("epub:type", "toc");
3249
+ } else {
3250
+ Logger.debug(`Generating toc nav element: ${target}`);
3251
+ const nav = document.createElement("nav");
3252
+ nav.setAttribute("id", TOC_ID);
3253
+ nav.setAttribute("role", "doc-toc");
3254
+ nav.setAttribute("epub:type", "toc");
3255
+ nav.setAttribute("hidden", "");
3256
+ const h2 = document.createElement("h2");
3257
+ h2.textContent = TOC_TITLE;
3258
+ nav.appendChild(h2);
3259
+ const ol = document.createElement("ol");
3260
+ tocResourceTree = {
3261
+ element: nav,
3262
+ children: []
3263
+ };
3264
+ for (const content of readingOrder) {
3265
+ let name = normalizeLocalizableString(content.name, docLanguages);
3266
+ if (!name) {
3267
+ const dom2 = await getJsdomFromUrlOrFile({
3268
+ src: upath7.join(contextDir, changeExtname(content.url, ".xhtml"))
3269
+ });
3270
+ name = dom2.window.document.title;
3271
+ }
3272
+ const li = document.createElement("li");
3273
+ const a = document.createElement("a");
3274
+ a.textContent = name;
3275
+ a.href = getRelativeHref(content.url, "", target);
3276
+ li.appendChild(a);
3277
+ ol.appendChild(li);
3278
+ tocResourceTree.children.push({ element: li, label: a });
3279
+ }
3280
+ nav.appendChild(ol);
3281
+ document.body.appendChild(nav);
3282
+ Logger.debug("Generated toc nav element", nav.outerHTML);
3283
+ }
3284
+ if (landmarks.length > 0) {
3285
+ Logger.debug(`Generating landmark nav element: ${target}`);
3286
+ const nav = document.createElement("nav");
3287
+ nav.setAttribute("epub:type", "landmarks");
3288
+ nav.setAttribute("id", LANDMARKS_ID);
3289
+ nav.setAttribute("hidden", "");
3290
+ const h2 = document.createElement("h2");
3291
+ h2.textContent = EPUB_LANDMARKS_TITLE;
3292
+ nav.appendChild(h2);
3293
+ const ol = document.createElement("ol");
3294
+ for (const { type, href, text } of landmarks) {
3295
+ const li = document.createElement("li");
3296
+ const a = document.createElement("a");
3297
+ a.setAttribute("epub:type", type);
3298
+ a.setAttribute("href", getRelativeHref(href, "", target));
3299
+ a.text = text;
3300
+ li.appendChild(a);
3301
+ ol.appendChild(li);
3302
+ }
3303
+ nav.appendChild(ol);
3304
+ document.body.appendChild(nav);
3305
+ Logger.debug("Generated landmark nav element", nav.outerHTML);
3306
+ }
3307
+ }
3308
+ const publicationLinkEl = document.querySelector(
3309
+ 'link[href][rel="publication"]'
3310
+ );
3311
+ if (publicationLinkEl) {
3312
+ const href = publicationLinkEl.getAttribute("href").trim();
3313
+ if (href.startsWith("#")) {
3314
+ const scriptEl = document.getElementById(href.slice(1));
3315
+ if (scriptEl?.getAttribute("type") === "application/ld+json") {
3316
+ scriptEl.parentNode?.removeChild(scriptEl);
3317
+ }
3318
+ }
3319
+ publicationLinkEl.parentNode?.removeChild(publicationLinkEl);
3320
+ }
3321
+ const absPath = upath7.join(contextDir, target);
3322
+ await writeAsXhtml(dom, absPath);
3323
+ return { tocResourceTree };
3324
+ }
3325
+ async function processPagelistDocument({
3326
+ dom,
3327
+ target,
3328
+ contextDir
3329
+ }) {
3330
+ const pageListResourceTree = parsePageListDocument(dom);
3331
+ if (pageListResourceTree) {
3332
+ const nav = replaceWithNavElement(dom, pageListResourceTree.element);
3333
+ nav.setAttribute("id", PAGELIST_ID);
3334
+ nav.setAttribute("epub:type", "page-list");
3335
+ }
3336
+ const absPath = upath7.join(contextDir, target);
3337
+ await writeAsXhtml(dom, absPath);
3338
+ return { pageListResourceTree };
3339
+ }
3340
+ function buildEpubPackageDocument({
3341
+ epubVersion,
3342
+ manifest,
3343
+ uid,
3344
+ docTitle,
3345
+ docLanguages,
3346
+ spineItems,
3347
+ manifestItems
3348
+ }) {
3349
+ const slugger = new GithubSlugger();
3350
+ slugger.reset();
3351
+ const bookIdentifier = slugger.slug("bookid");
3352
+ const normalizeDate = (value) => value && `${new Date(value).toISOString().split(".")[0]}Z`;
3353
+ const transformToGenericTextNode = (value, attributes) => [value].flat().filter(Boolean).map((v3) => ({ ...attributes || {}, "#text": `${value}` }));
3354
+ const transformContributor = (contributorMap) => Object.entries(contributorMap).flatMap(
3355
+ ([type, contributor]) => contributor ? [contributor].flat().map((entry, index) => ({
3356
+ _id: slugger.slug(`${type}-${index + 1}`),
3357
+ "#text": typeof entry === "string" ? entry : normalizeLocalizableString(entry.name, docLanguages)
3358
+ })) : []
3359
+ );
3360
+ const itemIdMap = /* @__PURE__ */ new Map();
3361
+ manifestItems.forEach(({ href }) => {
3362
+ itemIdMap.set(href, slugger.slug(href));
3363
+ });
3364
+ const builder = new XMLBuilder({
3365
+ format: true,
3366
+ ignoreAttributes: false,
3367
+ attributeNamePrefix: "_"
3368
+ });
3369
+ return builder.build({
3370
+ "?xml": {
3371
+ _version: "1.0",
3372
+ _encoding: "UTF-8"
3373
+ },
3374
+ package: {
3375
+ _xmlns: "http://www.idpf.org/2007/opf",
3376
+ _version: epubVersion,
3377
+ "_unique-identifier": bookIdentifier,
3378
+ "_xml:lang": docLanguages[0],
3379
+ metadata: {
3380
+ "_xmlns:dc": "http://purl.org/dc/elements/1.1/",
3381
+ "dc:identifier": {
3382
+ _id: bookIdentifier,
3383
+ "#text": uid
3384
+ },
3385
+ "dc:title": docTitle,
3386
+ "dc:language": docLanguages,
3387
+ "dc:creator": transformContributor({
3388
+ // TODO: Define proper order
3389
+ author: manifest.author,
3390
+ creator: manifest.creator,
3391
+ editor: manifest.editor,
3392
+ artist: manifest.artist,
3393
+ illustrator: manifest.illustrator,
3394
+ colorist: manifest.colorist,
3395
+ penciler: manifest.penciler,
3396
+ inker: manifest.inker,
3397
+ letterer: manifest.letterer,
3398
+ translator: manifest.translator,
3399
+ readBy: manifest.readBy
3400
+ }),
3401
+ "dc:publisher": transformContributor({
3402
+ publisher: manifest.publisher
3403
+ }),
3404
+ "dc:contributor": transformContributor({
3405
+ contributor: manifest.contributor
3406
+ }),
3407
+ "dc:date": transformToGenericTextNode(
3408
+ normalizeDate(manifest.datePublished)
3409
+ ),
3410
+ "dc:rights": transformToGenericTextNode(
3411
+ manifest.copyrightHolder && `\xA9 ${manifest.copyrightYear ? `${manifest.copyrightYear} ` : ""}${manifest.copyrightHolder}`
3412
+ ),
3413
+ "dc:subject": transformToGenericTextNode(
3414
+ manifest["dc:subject"] || manifest.subject
3415
+ ),
3416
+ meta: [
3417
+ ...transformToGenericTextNode(
3418
+ normalizeDate(manifest.dateModified || Date.now()),
3419
+ {
3420
+ _property: "dcterms:modified"
3421
+ }
3422
+ ),
3423
+ ...(() => {
3424
+ const coverImage = manifestItems.find(
3425
+ (it) => it.properties === "cover-image"
3426
+ );
3427
+ return coverImage ? [{ _name: "cover", _content: itemIdMap.get(coverImage.href) }] : [];
3428
+ })()
3429
+ ]
3430
+ },
3431
+ manifest: {
3432
+ item: manifestItems.map(({ href, mediaType, properties }) => ({
3433
+ _id: itemIdMap.get(href),
3434
+ _href: encodeURI(href),
3435
+ "_media-type": mediaType,
3436
+ ...properties ? { _properties: properties } : {}
3437
+ }))
3438
+ },
3439
+ spine: {
3440
+ ...manifest.readingProgression ? { "_page-progression-direction": manifest.readingProgression } : {},
3441
+ itemref: [
3442
+ ...spineItems.map(({ href }) => ({
3443
+ _idref: itemIdMap.get(href)
3444
+ }))
3445
+ ]
3446
+ }
3447
+ }
3448
+ });
3449
+ }
3450
+ async function compressEpub({
3451
+ target,
3452
+ sourceDir
3453
+ }) {
3454
+ Logger.debug(`Compressing EPUB: ${target}`);
3455
+ const output = fs6.createWriteStream(target);
3456
+ const archive = archiver("zip", {
3457
+ zlib: { level: 9 }
3458
+ // Compression level
3459
+ });
3460
+ return new Promise((resolve, reject) => {
3461
+ output.on("close", () => {
3462
+ Logger.debug(`Compressed EPUB: ${target}`);
3463
+ resolve();
3464
+ });
3465
+ output.on("error", reject);
3466
+ archive.on("warning", reject);
3467
+ archive.on("error", reject);
3468
+ archive.pipe(output);
3469
+ archive.append("application/epub+zip", {
3470
+ name: "mimetype",
3471
+ // mimetype should not be compressed
3472
+ // https://www.w3.org/TR/epub-33/#sec-zip-container-mime
3473
+ store: true
3474
+ });
3475
+ archive.directory(upath7.join(sourceDir, "META-INF"), "META-INF");
3476
+ archive.directory(upath7.join(sourceDir, "EPUB"), "EPUB");
3477
+ archive.finalize();
3478
+ });
3479
+ }
3480
+
3481
+ // src/output/webbook.ts
3482
+ function sortManifestResources(manifest) {
3483
+ if (!Array.isArray(manifest.resources)) {
3484
+ return;
3485
+ }
3486
+ manifest.resources = [...manifest.resources].sort(
3487
+ (a, b) => (typeof a === "string" ? a : a.url) > (typeof b === "string" ? b : b.url) ? 1 : -1
3488
+ );
3489
+ }
3490
+ async function prepareWebPublicationDirectory({
3491
+ outputDir
3492
+ }) {
3493
+ if (fs7.existsSync(outputDir)) {
3494
+ Logger.debug("going to remove existing webpub", outputDir);
3495
+ await remove2(outputDir);
3496
+ }
3497
+ fs7.mkdirSync(outputDir, { recursive: true });
3498
+ }
3499
+ function transformPublicationManifest(entity, transformer) {
3500
+ const { url: transformUrl } = transformer;
3501
+ const transformUrlOrPublicationLinks = (e) => {
3502
+ if (typeof e === "string") {
3503
+ return transformUrl(e);
3504
+ }
3505
+ const ret2 = { ...e };
3506
+ ret2.url = transformUrl(e.url);
3507
+ return ret2;
3508
+ };
3509
+ const ret = { ...entity };
3510
+ for (const [key, tr] of Object.entries({
3511
+ conformsTo: transformUrl,
3512
+ url: transformUrl,
3513
+ readingOrder: transformUrlOrPublicationLinks,
3514
+ resources: transformUrlOrPublicationLinks,
3515
+ links: transformUrlOrPublicationLinks
3516
+ })) {
3517
+ if (key in ret) {
3518
+ ret[key] = Array.isArray(ret[key]) ? ret[key].map(tr) : tr(ret[key]);
3519
+ }
3520
+ }
3521
+ return ret;
3522
+ }
3523
+ function decodePublicationManifest(input) {
3524
+ return transformPublicationManifest(input, {
3525
+ url: decodeURI
3526
+ });
3527
+ }
3528
+ function encodePublicationManifest(input) {
3529
+ return transformPublicationManifest(input, {
3530
+ url: encodeURI
3531
+ });
3532
+ }
3533
+ function writePublicationManifest(output, options) {
3534
+ const entries = options.entries.map((entry) => ({
3535
+ url: entry.path,
3536
+ ...entry.title && { name: entry.title },
3537
+ ...entry.encodingFormat && { encodingFormat: entry.encodingFormat },
3538
+ ...entry.rel && { rel: entry.rel },
3539
+ ...(entry.rel === "contents" || entry.rel === "cover") && {
3540
+ type: "LinkedResource"
3541
+ }
3542
+ }));
3543
+ const links = [
3544
+ options.links || []
3545
+ ].flat();
3546
+ const resources = [
3547
+ options.resources || []
3548
+ ].flat();
3549
+ if (options.cover) {
3550
+ const mimeType = mime3(options.cover.url);
3551
+ if (mimeType) {
3552
+ resources.push({
3553
+ rel: "cover",
3554
+ url: options.cover.url,
3555
+ name: options.cover.name,
3556
+ encodingFormat: mimeType
3557
+ });
3558
+ } else {
3559
+ Logger.logWarn(
3560
+ `Cover image "${options.cover}" was set in your configuration but couldn\u2019t detect the image metadata. Please check a valid cover file is placed.`
3561
+ );
3562
+ }
3563
+ }
3564
+ const publication = {
3565
+ "@context": ["https://schema.org", "https://www.w3.org/ns/pub-context"],
3566
+ type: "Book",
3567
+ conformsTo: "https://github.com/vivliostyle/vivliostyle-cli",
3568
+ ...options.title && { name: options.title },
3569
+ ...options.author && { author: options.author },
3570
+ ...options.language && { inLanguage: options.language },
3571
+ ...options.readingProgression && {
3572
+ readingProgression: options.readingProgression
3573
+ },
3574
+ dateModified: options.modified,
3575
+ readingOrder: entries,
3576
+ resources,
3577
+ links
3578
+ };
3579
+ const encodedManifest = encodePublicationManifest(publication);
3580
+ Logger.debug(
3581
+ "writePublicationManifest path: %s content: %O",
3582
+ output,
3583
+ encodedManifest
3584
+ );
3585
+ try {
3586
+ assertPubManifestSchema(encodedManifest);
3587
+ } catch (error) {
3588
+ const thrownError = error;
3589
+ throw new DetailError(
3590
+ `Validation of publication manifest failed. Please check the schema: ${output}`,
3591
+ typeof thrownError === "string" ? thrownError : thrownError.stack ?? thrownError.message
3592
+ );
3593
+ }
3594
+ fs7.mkdirSync(upath8.dirname(output), { recursive: true });
3595
+ fs7.writeFileSync(output, JSON.stringify(encodedManifest, null, 2));
3596
+ return publication;
3597
+ }
3598
+ async function retrieveWebbookEntry({
3599
+ viewerInput,
3600
+ outputDir
3601
+ }) {
3602
+ const webbookEntryUrl = viewerInput.webbookPath ? pathToFileURL6(viewerInput.webbookPath).href : viewerInput.webbookEntryUrl;
3603
+ if (/^https?:/i.test(webbookEntryUrl)) {
3604
+ Logger.logUpdate("Fetching remote contents");
3605
+ }
3606
+ const resourceLoader = new ResourceLoader();
3607
+ const dom = await getJsdomFromUrlOrFile({
3608
+ src: webbookEntryUrl,
3609
+ resourceLoader
3610
+ });
3611
+ const entryHtml = viewerInput.webbookPath ? upath8.basename(viewerInput.webbookPath) : decodeURI(dom.window.location.pathname);
3612
+ const { manifest, manifestUrl } = await fetchLinkedPublicationManifest({
3613
+ dom,
3614
+ resourceLoader,
3615
+ baseUrl: webbookEntryUrl
3616
+ }) || {};
3617
+ const rootUrl = /^https?:/i.test(webbookEntryUrl) ? new URL("/", webbookEntryUrl).href : new URL(".", webbookEntryUrl).href;
3618
+ const pathContains2 = (url) => !upath8.relative(rootUrl, url).startsWith("..");
3619
+ const retriever = new Map(resourceLoader.fetcherMap);
3620
+ if (manifest && manifestUrl) {
3621
+ [manifest.resources || []].flat().forEach((v3) => {
3622
+ const url = typeof v3 === "string" ? v3 : v3.url;
3623
+ const fullUrl = new URL(encodeURI(url), manifestUrl).href;
3624
+ if (!pathContains2(fullUrl) || retriever.has(fullUrl)) {
3625
+ return;
3626
+ }
3627
+ const fetchPromise = resourceLoader.fetch(fullUrl);
3628
+ if (fetchPromise && !retriever.has(fullUrl)) {
3629
+ retriever.set(fullUrl, fetchPromise);
3630
+ }
3631
+ });
3632
+ for (const v3 of [manifest.readingOrder || []].flat()) {
3633
+ const url = typeof v3 === "string" ? v3 : v3.url;
3634
+ if (!/\.html?$/.test(url) && !(typeof v3 === "string" || v3.encodingFormat === "text/html")) {
3635
+ continue;
3636
+ }
3637
+ const fullUrl = new URL(encodeURI(url), manifestUrl).href;
3638
+ if (!pathContains2(fullUrl) || fullUrl === webbookEntryUrl) {
3639
+ continue;
3640
+ }
3641
+ const subpathResourceLoader = new ResourceLoader();
3642
+ await getJsdomFromUrlOrFile({
3643
+ src: fullUrl,
3644
+ resourceLoader: subpathResourceLoader,
3645
+ virtualConsole: createVirtualConsole((error) => {
3646
+ Logger.logError(`Failed to fetch webbook resources: ${error.detail}`);
3647
+ })
3648
+ });
3649
+ subpathResourceLoader.fetcherMap.forEach(
3650
+ (v4, k) => !retriever.has(k) && retriever.set(k, v4)
3651
+ );
3652
+ }
3653
+ }
3654
+ const fetchedResources = await ResourceLoader.saveFetchedResources({
3655
+ fetcherMap: retriever,
3656
+ rootUrl: webbookEntryUrl,
3657
+ outputDir,
3658
+ /* v8 ignore next 4 */
3659
+ onError: (error) => {
3660
+ Logger.debug(error);
3661
+ Logger.logError(`Failed to fetch webbook resources: ${error}`);
3662
+ }
3663
+ });
3664
+ if (manifest) {
3665
+ const referencedContents = [
3666
+ ...[manifest.readingOrder || []].flat(),
3667
+ ...[manifest.resources || []].flat()
3668
+ ].map((v3) => typeof v3 === "string" ? v3 : v3.url);
3669
+ manifest.resources = [
3670
+ ...[manifest.resources || []].flat(),
3671
+ ...fetchedResources.filter(
3672
+ ({ url }) => !referencedContents.includes(url)
3673
+ )
3674
+ ];
3675
+ sortManifestResources(manifest);
3676
+ }
3677
+ Logger.debug(
3678
+ "Saved webbook resources",
3679
+ fetchedResources.map((v3) => v3.url)
3680
+ );
3681
+ Logger.debug(
3682
+ "Publication manifest from webbook",
3683
+ manifest && JSON.stringify(manifest, null, 2)
3684
+ );
3685
+ return {
3686
+ entryHtmlFile: upath8.join(outputDir, entryHtml),
3687
+ manifest
3688
+ };
3689
+ }
3690
+ async function supplyWebPublicationManifestForWebbook({
3691
+ entryHtmlFile,
3692
+ outputDir,
3693
+ ...config
3694
+ }) {
3695
+ Logger.debug(`Generating publication manifest from HTML: ${entryHtmlFile}`);
3696
+ const dom = await getJsdomFromUrlOrFile({ src: entryHtmlFile });
3697
+ const { document } = dom.window;
3698
+ const language = config.language || document.documentElement.lang || void 0;
3699
+ const title = config.title || document.title || "";
3700
+ const author = config.author || document.querySelector('meta[name="author"]')?.getAttribute("content") || "";
3701
+ const entry = upath8.relative(outputDir, entryHtmlFile);
3702
+ const allFiles = await glob("**", {
3703
+ cwd: outputDir
3704
+ });
3705
+ const manifest = writePublicationManifest(
3706
+ upath8.join(outputDir, MANIFEST_FILENAME),
3707
+ {
3708
+ title,
3709
+ author,
3710
+ language,
3711
+ readingProgression: config.readingProgression,
3712
+ modified: (/* @__PURE__ */ new Date()).toISOString(),
3713
+ entries: [{ path: entry }],
3714
+ resources: allFiles.filter((f) => f !== entry)
3715
+ }
3716
+ );
3717
+ sortManifestResources(manifest);
3718
+ const link = document.createElement("link");
3719
+ link.setAttribute("rel", "publication");
3720
+ link.setAttribute("type", "application/ld+json");
3721
+ link.setAttribute(
3722
+ "href",
3723
+ upath8.relative(
3724
+ upath8.dirname(entryHtmlFile),
3725
+ upath8.join(outputDir, MANIFEST_FILENAME)
3726
+ )
3727
+ );
3728
+ document.head.appendChild(link);
3729
+ await fs7.promises.writeFile(entryHtmlFile, dom.serialize(), "utf8");
3730
+ Logger.debug(
3731
+ "Generated publication manifest from HTML",
3732
+ JSON.stringify(manifest, null, 2)
3733
+ );
3734
+ return manifest;
3735
+ }
3736
+ async function copyWebPublicationAssets({
3737
+ exportAliases,
3738
+ outputs,
3739
+ copyAsset,
3740
+ themesDir,
3741
+ manifestPath,
3742
+ input,
3743
+ outputDir,
3744
+ entries
3745
+ }) {
3746
+ const relExportAliases = exportAliases.map(({ source, target }) => ({
3747
+ source: upath8.relative(input, source),
3748
+ target: upath8.relative(input, target)
3749
+ })).filter(({ source }) => !source.startsWith(".."));
3750
+ const allFiles = /* @__PURE__ */ new Set([
3751
+ ...await globAssetFiles({
3752
+ copyAsset,
3753
+ cwd: input,
3754
+ outputs,
3755
+ themesDir,
3756
+ entries
3757
+ }),
3758
+ ...await glob(
3759
+ [
3760
+ `**/${upath8.relative(input, manifestPath)}`,
3761
+ "**/*.{html,htm,xhtml,xht,css}"
3762
+ ],
3763
+ {
3764
+ cwd: input,
3765
+ ignore: [
3766
+ ...getIgnoreAssetPatterns({
3767
+ cwd: input,
3768
+ outputs,
3769
+ entries
3770
+ }),
3771
+ ...getIgnoreThemeExamplePatterns({
3772
+ cwd: input,
3773
+ themesDir
3774
+ }),
3775
+ // Ignore node_modules in the root directory
3776
+ "node_modules/**",
3777
+ // only include dotfiles starting with `.vs-`
3778
+ "**/.!(vs-*)/**"
3779
+ ],
3780
+ // follow symbolic links to copy local theme packages
3781
+ followSymbolicLinks: true,
3782
+ dot: true
3783
+ }
3784
+ )
3785
+ ]);
3786
+ for (const alias of relExportAliases) {
3787
+ allFiles.delete(alias.target);
3788
+ }
3789
+ Logger.debug(
3790
+ "webbook files",
3791
+ JSON.stringify(
3792
+ [...allFiles].map((file) => {
3793
+ const alias = relExportAliases.find(({ source }) => source === file);
3794
+ return alias ? `${file} (alias: ${alias.target})` : file;
3795
+ }),
3796
+ null,
3797
+ 2
3798
+ )
3799
+ );
3800
+ const resources = [];
3801
+ let actualManifestPath = upath8.join(
3802
+ outputDir,
3803
+ upath8.relative(input, manifestPath)
3804
+ );
3805
+ for (const file of allFiles) {
3806
+ const alias = relExportAliases.find(({ source }) => source === file);
3807
+ const relTarget = alias?.target || file;
3808
+ resources.push(relTarget);
3809
+ const target = upath8.join(outputDir, relTarget);
3810
+ fs7.mkdirSync(upath8.dirname(target), { recursive: true });
3811
+ await copy2(upath8.join(input, file), target);
3812
+ if (alias && pathEquals(upath8.join(input, alias.source), manifestPath)) {
3813
+ actualManifestPath = target;
3814
+ }
3815
+ }
3816
+ Logger.debug("webbook publication.json", actualManifestPath);
3817
+ const manifest = decodePublicationManifest(
3818
+ JSON.parse(fs7.readFileSync(actualManifestPath, "utf8"))
3819
+ );
3820
+ for (const entry of relExportAliases) {
3821
+ const rewriteAliasPath = (e) => {
3822
+ if (typeof e === "string") {
3823
+ return pathEquals(e, entry.source) ? entry.source : e;
3824
+ }
3825
+ if (pathEquals(e.url, entry.source)) {
3826
+ e.url = entry.target;
3827
+ }
3828
+ return e;
3829
+ };
3830
+ if (manifest.links) {
3831
+ manifest.links = Array.isArray(manifest.links) ? manifest.links.map(rewriteAliasPath) : rewriteAliasPath(manifest.links);
3832
+ }
3833
+ if (manifest.readingOrder) {
3834
+ manifest.readingOrder = Array.isArray(manifest.readingOrder) ? manifest.readingOrder.map(rewriteAliasPath) : rewriteAliasPath(manifest.readingOrder);
3835
+ }
3836
+ if (manifest.resources) {
3837
+ manifest.resources = Array.isArray(manifest.resources) ? manifest.resources.map(rewriteAliasPath) : rewriteAliasPath(manifest.resources);
3838
+ }
3839
+ }
3840
+ const normalizeToUrl = (val) => [val || []].flat().map((e) => typeof e === "string" ? e : e.url);
3841
+ const preDefinedResources = [
3842
+ ...normalizeToUrl(manifest.links),
3843
+ ...normalizeToUrl(manifest.readingOrder),
3844
+ ...normalizeToUrl(manifest.resources)
3845
+ ];
3846
+ manifest.resources = [
3847
+ ...[manifest.resources || []].flat(),
3848
+ ...resources.flatMap((file) => {
3849
+ if (preDefinedResources.includes(file) || // Omit publication.json itself
3850
+ pathEquals(file, upath8.relative(outputDir, actualManifestPath))) {
3851
+ return [];
3852
+ }
3853
+ return file;
3854
+ })
3855
+ ];
3856
+ sortManifestResources(manifest);
3857
+ fs7.writeFileSync(
3858
+ actualManifestPath,
3859
+ JSON.stringify(encodePublicationManifest(manifest), null, 2)
3860
+ );
3861
+ return { manifest, actualManifestPath };
3862
+ }
3863
+ async function buildWebPublication({
3864
+ target,
3865
+ config
3866
+ }) {
3867
+ let outputDir;
3868
+ if (target.format === "webpub") {
3869
+ outputDir = target.path;
3870
+ await prepareWebPublicationDirectory({ outputDir });
3871
+ } else {
3872
+ [outputDir] = await useTmpDirectory();
3873
+ }
3874
+ let entryHtmlFile;
3875
+ let manifest;
3876
+ let actualManifestPath;
3877
+ if (config.viewerInput.type === "webpub") {
3878
+ const ret = await copyWebPublicationAssets({
3879
+ ...config,
3880
+ input: config.workspaceDir,
3881
+ outputDir,
3882
+ manifestPath: config.viewerInput.manifestPath
3883
+ });
3884
+ manifest = ret.manifest;
3885
+ actualManifestPath = ret.actualManifestPath;
3886
+ if (config.input.format === "markdown") {
3887
+ const entry = [manifest.readingOrder].flat()[0];
3888
+ if (entry) {
3889
+ entryHtmlFile = upath8.join(
3890
+ outputDir,
3891
+ typeof entry === "string" ? entry : entry.url
3892
+ );
3893
+ }
3894
+ }
3895
+ } else if (isWebbookConfig(config)) {
3896
+ const ret = await retrieveWebbookEntry({
3897
+ viewerInput: config.viewerInput,
3898
+ outputDir
3899
+ });
3900
+ entryHtmlFile = ret.entryHtmlFile;
3901
+ manifest = ret.manifest || await supplyWebPublicationManifestForWebbook({
3902
+ ...config,
3903
+ entryHtmlFile: ret.entryHtmlFile,
3904
+ outputDir
3905
+ });
3906
+ } else {
3907
+ throw new Error("No entry specified");
3908
+ }
3909
+ if (target.format === "epub") {
3910
+ await exportEpub({
3911
+ webpubDir: outputDir,
3912
+ entryHtmlFile,
3913
+ manifest,
3914
+ relManifestPath: actualManifestPath && upath8.relative(outputDir, actualManifestPath),
3915
+ target: target.path,
3916
+ epubVersion: target.version
3917
+ });
3918
+ }
3919
+ return target.path;
3920
+ }
3921
+
3922
+ // src/processor/theme.ts
3923
+ import Arborist from "@npmcli/arborist";
3924
+ import fs8 from "node:fs";
3925
+ async function checkThemeInstallationNecessity({
3926
+ themesDir,
3927
+ themeIndexes
3928
+ }) {
3929
+ if (!fs8.existsSync(themesDir)) {
3930
+ return [...themeIndexes].some((theme) => theme.type === "package");
3931
+ }
3932
+ const commonOpt = {
3933
+ path: themesDir,
3934
+ lockfileVersion: 3
3935
+ };
3936
+ const arb = new Arborist(commonOpt);
3937
+ const tree = await arb.loadActual();
3938
+ const pkgs = Array.from(tree.children.keys());
3939
+ return [...themeIndexes].some(
3940
+ (theme) => theme.type === "package" && !pkgs.includes(theme.name)
3941
+ );
3942
+ }
3943
+ async function installThemeDependencies({
3944
+ themesDir,
3945
+ themeIndexes
3946
+ }) {
3947
+ fs8.mkdirSync(themesDir, { recursive: true });
3948
+ try {
3949
+ const commonOpt = {
3950
+ path: themesDir,
3951
+ lockfileVersion: 3
3952
+ };
3953
+ const tree = await new Arborist(commonOpt).buildIdealTree();
3954
+ const existing = Array.from(tree.children.keys());
3955
+ const add = [
3956
+ ...new Set(
3957
+ [...themeIndexes].flatMap(
3958
+ (theme) => theme.type === "package" ? [theme.specifier] : []
3959
+ )
3960
+ )
3961
+ ];
3962
+ const rm = existing.filter((v3) => !add.includes(v3));
3963
+ const opt = { ...commonOpt, rm, add };
3964
+ const arb = new Arborist(opt);
3965
+ await arb.reify(opt);
3966
+ return;
3967
+ } catch (error) {
3968
+ const thrownError = error;
3969
+ throw new DetailError(
3970
+ "An error occurred during the installation of the theme",
3971
+ thrownError.stack ?? thrownError.message
3972
+ );
3973
+ }
3974
+ }
3975
+
3976
+ // src/processor/compile.ts
3977
+ function locateThemePath(theme, from) {
3978
+ if (theme.type === "uri") {
3979
+ return theme.location;
3980
+ }
3981
+ if (theme.type === "file") {
3982
+ return upath9.relative(from, theme.location);
3983
+ }
3984
+ if (theme.importPath) {
3985
+ return [theme.importPath].flat().map((locator) => {
3986
+ const resolvedPath = upath9.resolve(theme.location, locator);
3987
+ if (!pathContains(theme.location, resolvedPath) || !fs9.existsSync(resolvedPath)) {
3988
+ throw new Error(
3989
+ `Could not find a style path ${theme.importPath} for the theme: ${theme.name}.`
3990
+ );
3991
+ }
3992
+ return upath9.relative(from, resolvedPath);
3993
+ });
3994
+ } else {
3995
+ const pkgJsonPath = upath9.join(theme.location, "package.json");
3996
+ const packageJson = JSON.parse(fs9.readFileSync(pkgJsonPath, "utf8"));
3997
+ const maybeStyle = packageJson?.vivliostyle?.theme?.style ?? packageJson.style ?? packageJson.main;
3998
+ if (!maybeStyle) {
3999
+ throw new DetailError(
4000
+ `Could not find a style file for the theme: ${theme.name}.`,
4001
+ "Please ensure this package satisfies a `vivliostyle.theme.style` property."
4002
+ );
4003
+ }
4004
+ return upath9.relative(from, upath9.join(theme.location, maybeStyle));
4005
+ }
4006
+ }
4007
+ async function cleanupWorkspace({
4008
+ entryContextDir,
4009
+ workspaceDir,
4010
+ themesDir,
4011
+ entries
4012
+ }) {
4013
+ if (pathEquals(workspaceDir, entryContextDir) || pathContains(workspaceDir, entryContextDir) || entries.some(
4014
+ (entry) => entry.source?.type === "file" && pathContains(workspaceDir, entry.source.pathname)
4015
+ )) {
4016
+ return;
4017
+ }
4018
+ Logger.debug("cleanup workspace files", workspaceDir);
4019
+ let movedWorkspacePath;
4020
+ if (pathContains(workspaceDir, themesDir) && fs9.existsSync(themesDir)) {
4021
+ movedWorkspacePath = upath9.join(
4022
+ upath9.dirname(workspaceDir),
4023
+ `.vs-${Date.now()}`
4024
+ );
4025
+ const movedThemePath = upath9.join(
4026
+ movedWorkspacePath,
4027
+ upath9.relative(workspaceDir, themesDir)
4028
+ );
4029
+ fs9.mkdirSync(upath9.dirname(movedThemePath), { recursive: true });
4030
+ registerExitHandler(
4031
+ `Removing the moved workspace directory: ${movedWorkspacePath}`,
4032
+ () => {
4033
+ if (movedWorkspacePath && fs9.existsSync(movedWorkspacePath)) {
4034
+ fs9.rmSync(movedWorkspacePath, { recursive: true, force: true });
4035
+ }
4036
+ }
4037
+ );
4038
+ await move(themesDir, movedThemePath);
4039
+ }
4040
+ await remove3(workspaceDir);
4041
+ if (movedWorkspacePath) {
4042
+ await move(movedWorkspacePath, workspaceDir);
4043
+ }
4044
+ }
4045
+ async function prepareThemeDirectory({
4046
+ themesDir,
4047
+ themeIndexes
4048
+ }) {
4049
+ if (fs9.existsSync(upath9.join(themesDir, "packages")) && !fs9.existsSync(upath9.join(themesDir, "node_modules"))) {
4050
+ fs9.renameSync(
4051
+ upath9.join(themesDir, "packages"),
4052
+ upath9.join(themesDir, "node_modules")
4053
+ );
4054
+ }
4055
+ if (await checkThemeInstallationNecessity({ themesDir, themeIndexes })) {
4056
+ Logger.startLogging("Installing theme files");
4057
+ await installThemeDependencies({ themesDir, themeIndexes });
4058
+ }
4059
+ for (const theme of themeIndexes) {
4060
+ if (theme.type === "file" && !pathEquals(theme.source, theme.location)) {
4061
+ fs9.mkdirSync(upath9.dirname(theme.location), { recursive: true });
4062
+ await copy3(theme.source, theme.location);
4063
+ }
4064
+ }
4065
+ }
4066
+ async function transformManuscript(entry, {
4067
+ entryContextDir,
4068
+ workspaceDir,
4069
+ viewerInput: { manifestPath },
4070
+ title,
4071
+ entries,
4072
+ language,
4073
+ documentProcessorFactory,
4074
+ vfmOptions
4075
+ }) {
4076
+ const source = entry.rel === "contents" || entry.rel === "cover" ? entry.template : entry.source;
4077
+ let content;
4078
+ let resourceLoader;
4079
+ const style = entry.themes.flatMap(
4080
+ (theme) => locateThemePath(theme, upath9.dirname(entry.target))
4081
+ );
4082
+ if (source?.type === "file") {
4083
+ if (source.contentType === "text/markdown") {
4084
+ const vfile2 = await processMarkdown(
4085
+ documentProcessorFactory,
4086
+ source.pathname,
4087
+ {
4088
+ ...vfmOptions,
4089
+ style,
4090
+ title: entry.title,
4091
+ language: language ?? void 0
4092
+ }
4093
+ );
4094
+ content = getJsdomFromString({ html: String(vfile2) });
4095
+ } else if (source.contentType === "text/html" || source.contentType === "application/xhtml+xml") {
4096
+ content = await getJsdomFromUrlOrFile({ src: source.pathname });
4097
+ content = await processManuscriptHtml(content, {
4098
+ style,
4099
+ title: entry.title,
4100
+ contentType: source.contentType,
4101
+ language
4102
+ });
4103
+ } else {
4104
+ if (!pathEquals(source.pathname, entry.target)) {
4105
+ await copy3(source.pathname, entry.target);
4106
+ }
4107
+ }
4108
+ } else if (source?.type === "uri") {
4109
+ resourceLoader = new ResourceLoader();
4110
+ try {
4111
+ await getJsdomFromUrlOrFile({
4112
+ src: source.href,
4113
+ resourceLoader,
4114
+ virtualConsole: createVirtualConsole((error) => {
4115
+ Logger.logError(`Failed to fetch resources: ${error.detail}`);
4116
+ })
4117
+ });
4118
+ } catch (error) {
4119
+ throw new DetailError(
4120
+ `Failed to fetch the content from ${source.href}`,
4121
+ error.stack ?? error.message
4122
+ );
4123
+ }
4124
+ const contentFetcher = resourceLoader.fetcherMap.get(source.href);
4125
+ if (contentFetcher) {
4126
+ const buffer = await contentFetcher;
4127
+ const contentType = contentFetcher.response?.headers["content-type"];
4128
+ if (!contentType || new MIMEType2(contentType).essence !== "text/html") {
4129
+ throw new Error(`The content is not an HTML document: ${source.href}`);
4130
+ }
4131
+ content = getJsdomFromString({ html: buffer.toString("utf8") });
4132
+ content = await processManuscriptHtml(content, {
4133
+ style,
4134
+ title: entry.title,
4135
+ contentType: "text/html",
4136
+ language
4137
+ });
4138
+ }
4139
+ } else if (entry.rel === "contents") {
4140
+ content = getJsdomFromString({
4141
+ html: generateDefaultTocHtml({
4142
+ language,
4143
+ title
4144
+ })
4145
+ });
4146
+ content = await processManuscriptHtml(content, {
4147
+ style,
4148
+ title,
4149
+ contentType: "text/html",
4150
+ language
4151
+ });
4152
+ } else if (entry.rel === "cover") {
4153
+ content = getJsdomFromString({
4154
+ html: generateDefaultCoverHtml({ language, title: entry.title })
4155
+ });
4156
+ content = await processManuscriptHtml(content, {
4157
+ style,
4158
+ title: entry.title,
4159
+ contentType: "text/html",
4160
+ language
4161
+ });
4162
+ }
4163
+ if (!content) {
4164
+ return;
4165
+ }
4166
+ if (entry.rel === "contents") {
4167
+ const contentsEntry = entry;
4168
+ const manuscriptEntries = entries.filter(
4169
+ (e) => "source" in e
4170
+ );
4171
+ content = await processTocHtml(content, {
4172
+ entries: manuscriptEntries,
4173
+ manifestPath,
4174
+ distDir: upath9.dirname(contentsEntry.target),
4175
+ tocTitle: contentsEntry.tocTitle,
4176
+ sectionDepth: contentsEntry.sectionDepth,
4177
+ styleOptions: contentsEntry,
4178
+ transform: contentsEntry.transform
4179
+ });
4180
+ }
4181
+ if (entry.rel === "cover") {
4182
+ const coverEntry = entry;
4183
+ content = await processCoverHtml(content, {
4184
+ imageSrc: upath9.relative(
4185
+ upath9.join(
4186
+ entryContextDir,
4187
+ upath9.relative(workspaceDir, coverEntry.target),
4188
+ ".."
4189
+ ),
4190
+ coverEntry.coverImageSrc
4191
+ ),
4192
+ imageAlt: coverEntry.coverImageAlt,
4193
+ styleOptions: coverEntry
4194
+ });
4195
+ }
4196
+ const html = await prettier.format(content.serialize(), {
4197
+ parser: "html",
4198
+ plugins: [parserHtml]
4199
+ });
4200
+ const htmlBuffer = Buffer.from(html, "utf8");
4201
+ if (!source || source.type === "file" && !pathEquals(source.pathname, entry.target)) {
4202
+ writeFileIfChanged(entry.target, htmlBuffer);
4203
+ }
4204
+ if (source?.type === "uri" && resourceLoader) {
4205
+ const { response } = resourceLoader.fetcherMap.get(source.href);
4206
+ const contentFetcher = Promise.resolve(
4207
+ htmlBuffer
4208
+ );
4209
+ contentFetcher.abort = () => {
4210
+ };
4211
+ contentFetcher.response = response;
4212
+ resourceLoader.fetcherMap.set(source.href, contentFetcher);
4213
+ await ResourceLoader.saveFetchedResources({
4214
+ fetcherMap: resourceLoader.fetcherMap,
4215
+ rootUrl: source.href,
4216
+ outputDir: source.rootDir
4217
+ });
4218
+ }
4219
+ return html;
4220
+ }
4221
+ async function generateManifest({
4222
+ entryContextDir,
4223
+ workspaceDir,
4224
+ viewerInput: { manifestPath },
4225
+ title,
4226
+ author,
4227
+ entries,
4228
+ language,
4229
+ readingProgression,
4230
+ cover
4231
+ }) {
4232
+ const manifestEntries = entries.map((entry) => ({
4233
+ title: entry.rel === "contents" && entry.tocTitle || entry.title,
4234
+ path: upath9.relative(workspaceDir, entry.target),
4235
+ encodingFormat: !("contentType" in entry) || entry.contentType === "text/markdown" || entry.contentType === "text/html" ? void 0 : entry.contentType,
4236
+ rel: entry.rel
4237
+ }));
4238
+ writePublicationManifest(manifestPath, {
4239
+ title,
4240
+ author,
4241
+ language,
4242
+ readingProgression,
4243
+ cover: cover && {
4244
+ url: upath9.relative(entryContextDir, cover.src),
4245
+ name: cover.name
4246
+ },
4247
+ entries: manifestEntries,
4248
+ modified: (/* @__PURE__ */ new Date()).toISOString()
4249
+ });
4250
+ }
4251
+ async function compile(config) {
4252
+ const tocEntries = [];
4253
+ for (const entry of config.entries) {
4254
+ if (entry.rel === "contents") {
4255
+ tocEntries.push(entry);
4256
+ continue;
4257
+ }
4258
+ await transformManuscript(entry, config);
4259
+ }
4260
+ for (const entry of tocEntries) {
4261
+ await transformManuscript(entry, config);
4262
+ }
4263
+ if (config.viewerInput.needToGenerateManifest) {
4264
+ await generateManifest(config);
4265
+ }
4266
+ }
4267
+ function getIgnoreThemeExamplePatterns({
4268
+ themesDir,
4269
+ cwd: cwd2
4270
+ }) {
4271
+ return pathContains(cwd2, themesDir) ? [
4272
+ `${upath9.relative(cwd2, themesDir)}/node_modules/*/example`,
4273
+ `${upath9.relative(cwd2, themesDir)}/node_modules/*/*/example`
4274
+ ] : [];
4275
+ }
4276
+ function getIgnoreAssetPatterns({
4277
+ outputs,
4278
+ entries,
4279
+ cwd: cwd2
4280
+ }) {
4281
+ return [
4282
+ ...outputs.flatMap(
4283
+ ({ format, path: p }) => !pathContains(cwd2, p) ? [] : format === "webpub" ? upath9.join(upath9.relative(cwd2, p), "**") : upath9.relative(cwd2, p)
4284
+ ),
4285
+ ...entries.flatMap(({ template }) => {
4286
+ return template?.type === "file" && pathContains(cwd2, template.pathname) ? upath9.relative(cwd2, template.pathname) : [];
4287
+ })
4288
+ ];
4289
+ }
4290
+ function getAssetMatcherSettings({
4291
+ copyAsset: { fileExtensions, includes, excludes },
4292
+ outputs,
4293
+ themesDir,
4294
+ entries,
4295
+ cwd: cwd2,
4296
+ ignore = []
4297
+ }) {
4298
+ const ignorePatterns = [
4299
+ ...ignore,
4300
+ ...excludes,
4301
+ ...getIgnoreAssetPatterns({ outputs, entries, cwd: cwd2 })
4302
+ ];
4303
+ Logger.debug("globAssetFiles > ignorePatterns", ignorePatterns);
4304
+ return [
4305
+ // Step 1: Glob files with an extension in `fileExtension`
4306
+ // Ignore files in node_modules directory, theme example files and files matched `excludes`
4307
+ {
4308
+ patterns: fileExtensions.map((ext) => `**/*.${ext}`),
4309
+ ignore: [
4310
+ "**/node_modules/**",
4311
+ ...ignorePatterns,
4312
+ ...getIgnoreThemeExamplePatterns({ themesDir, cwd: cwd2 })
4313
+ ]
4314
+ },
4315
+ // Step 2: Glob files matched with `includes`
4316
+ // Ignore only files matched `excludes`
4317
+ {
4318
+ patterns: includes,
4319
+ ignore: ignorePatterns
4320
+ }
4321
+ ];
4322
+ }
4323
+ function getAssetMatcher(arg) {
4324
+ const matchers = getAssetMatcherSettings(arg).map(
4325
+ ({ patterns, ignore }) => picomatch(patterns, { ignore })
4326
+ );
4327
+ return (test) => matchers.some((matcher) => matcher(test));
4328
+ }
4329
+ async function globAssetFiles(arg) {
4330
+ const settings = getAssetMatcherSettings(arg);
4331
+ return new Set(
4332
+ (await Promise.all(
4333
+ settings.map(
4334
+ ({ patterns, ignore }) => glob2(patterns, {
4335
+ cwd: arg.cwd,
4336
+ ignore,
4337
+ followSymbolicLinks: true
4338
+ })
4339
+ )
4340
+ )).flat()
4341
+ );
4342
+ }
4343
+ async function copyAssets({
4344
+ entryContextDir,
4345
+ workspaceDir,
4346
+ copyAsset,
4347
+ outputs,
4348
+ themesDir,
4349
+ entries
4350
+ }) {
4351
+ if (pathEquals(entryContextDir, workspaceDir)) {
4352
+ return;
4353
+ }
4354
+ const relWorkspaceDir = upath9.relative(entryContextDir, workspaceDir);
4355
+ const assets = await globAssetFiles({
4356
+ copyAsset,
4357
+ cwd: entryContextDir,
4358
+ outputs,
4359
+ themesDir,
4360
+ entries,
4361
+ ignore: [
4362
+ // don't copy workspace itself
4363
+ ...relWorkspaceDir ? [upath9.join(relWorkspaceDir, "**")] : []
4364
+ ]
4365
+ });
4366
+ Logger.debug("assets", assets);
4367
+ for (const asset of assets) {
4368
+ const target = upath9.join(workspaceDir, asset);
4369
+ fs9.mkdirSync(upath9.dirname(target), { recursive: true });
4370
+ await copy3(upath9.resolve(entryContextDir, asset), target);
4371
+ }
4372
+ }
4373
+
4374
+ // src/vite/plugin-util.ts
4375
+ var headStartTagRe = /<head[^>]*>/i;
4376
+ var prependToHead = (html, content) => html.replace(headStartTagRe, (match) => `${match}
4377
+ ${content}`);
4378
+ async function reloadConfig(prevConfig, inlineConfig, resolvedViteConfig) {
4379
+ let config = await loadVivliostyleConfig(inlineConfig) ?? setupConfigFromFlags(inlineConfig);
4380
+ config = mergeInlineConfig(config, inlineConfig);
4381
+ config = mergeConfig(config, {
4382
+ temporaryFilePrefix: prevConfig.temporaryFilePrefix,
4383
+ server: resolvedViteConfig?.server
4384
+ });
4385
+ const taskConfig = resolveTaskConfig(config.tasks[0], config.inlineOptions);
4386
+ return taskConfig;
4387
+ }
4388
+
4389
+ // src/vite/vite-plugin-dev-server.ts
4390
+ function createEntriesRouteLookup(entries, cwd2) {
4391
+ const extns = ["", "html", "htm"];
4392
+ const toAssume = (uri) => {
4393
+ let i = 0, x, len = uri.length - 1;
4394
+ if (uri.charCodeAt(len) === 47) {
4395
+ uri = uri.substring(0, len);
4396
+ }
4397
+ let arr = [], tmp2 = `${uri}/index`;
4398
+ for (; i < extns.length; i++) {
4399
+ x = extns[i] ? `.${extns[i]}` : "";
4400
+ if (uri) arr.push(uri + x);
4401
+ arr.push(tmp2 + x);
4402
+ }
4403
+ return arr;
4404
+ };
4405
+ const cache = entries.reduce((acc, e) => {
4406
+ acc[`/${upath10.relative(cwd2, e.target).normalize().replace(/\\+/g, "/")}`] = e;
4407
+ return acc;
4408
+ }, {});
4409
+ return (uri) => {
4410
+ let i = 0, data, arr = toAssume(uri);
4411
+ for (; i < arr.length; i++) {
4412
+ if (data = cache[arr[i]]) return [data, arr[i]];
4413
+ }
4414
+ };
4415
+ }
4416
+ function getWorkspaceMatcher({
4417
+ workspaceDir,
4418
+ themesDir,
4419
+ viewerInput,
4420
+ themeIndexes
4421
+ }) {
4422
+ let entryFiles = [];
4423
+ switch (viewerInput.type) {
4424
+ case "webpub":
4425
+ entryFiles = [upath10.relative(workspaceDir, viewerInput.manifestPath)];
4426
+ break;
4427
+ case "epub":
4428
+ entryFiles = [
4429
+ upath10.join(
4430
+ upath10.relative(workspaceDir, viewerInput.epubTmpOutputDir),
4431
+ "**"
4432
+ )
4433
+ ];
4434
+ break;
4435
+ case "epub-opf":
4436
+ case "webbook":
4437
+ entryFiles = ["**"];
4438
+ break;
4439
+ default:
4440
+ entryFiles = viewerInput;
4441
+ }
4442
+ return picomatch2(
4443
+ [
4444
+ ...entryFiles,
4445
+ ...pathContains(workspaceDir, themesDir) ? [upath10.join(upath10.relative(workspaceDir, themesDir), "**")] : [],
4446
+ ...[...themeIndexes].flatMap(
4447
+ (theme) => theme.type === "file" && pathContains(workspaceDir, theme.location) ? [upath10.relative(workspaceDir, theme.location)] : []
4448
+ )
4449
+ ],
4450
+ { dot: true, ignore: ["node_modules/**"] }
4451
+ );
4452
+ }
4453
+ function vsDevServerPlugin({
4454
+ config: _config,
4455
+ inlineConfig
4456
+ }) {
4457
+ let config = _config;
4458
+ let server;
4459
+ let program;
4460
+ const transformCache = /* @__PURE__ */ new Map();
4461
+ const projectDeps = /* @__PURE__ */ new Set();
4462
+ async function reload(forceUpdate = false) {
4463
+ const prevConfig = config;
4464
+ config = await reloadConfig(prevConfig, inlineConfig, server?.config);
4465
+ transformCache.clear();
4466
+ const needToUpdateManifest = forceUpdate || // FIXME: More precise comparison
4467
+ JSON.stringify(prevConfig) !== JSON.stringify(config);
4468
+ if (isWebPubConfig(config) && config.viewerInput.needToGenerateManifest && needToUpdateManifest) {
4469
+ await generateManifest(config);
4470
+ }
4471
+ await prepareThemeDirectory(config);
4472
+ const entriesLookup = createEntriesRouteLookup(
4473
+ config.entries,
4474
+ config.workspaceDir
4475
+ );
4476
+ const urlMatchRe = new RegExp(
4477
+ `^${escapeRe(config.base)}(/[^?#]*)([?#].*)?$`
4478
+ );
4479
+ const serveWorkspace = sirv(config.workspaceDir, {
4480
+ dev: true,
4481
+ etag: false,
4482
+ dotfiles: true,
4483
+ extensions: []
4484
+ });
4485
+ const serveWorkspaceMatcher = getWorkspaceMatcher(config);
4486
+ const serveAssets = sirv(config.entryContextDir, {
4487
+ dev: true,
4488
+ etag: false,
4489
+ extensions: []
4490
+ });
4491
+ const serveAssetsMatcher = getAssetMatcher({
4492
+ ...config,
4493
+ cwd: config.entryContextDir
4494
+ });
4495
+ program = {
4496
+ entriesLookup,
4497
+ urlMatchRe,
4498
+ serveWorkspace,
4499
+ serveWorkspaceMatcher,
4500
+ serveAssets,
4501
+ serveAssetsMatcher
4502
+ };
4503
+ const configPath = locateVivliostyleConfig(inlineConfig);
4504
+ if (configPath) {
4505
+ projectDeps.add(configPath);
4506
+ server?.watcher.add(configPath);
4507
+ }
4508
+ if (config.viewerInput.type === "webpub") {
4509
+ projectDeps.add(config.viewerInput.manifestPath);
4510
+ server?.watcher.add(config.viewerInput.manifestPath);
4511
+ }
4512
+ }
4513
+ async function transform(entry, config2) {
4514
+ const promise = (async () => {
4515
+ try {
4516
+ const html = await transformManuscript(entry, config2);
4517
+ if (!html) {
4518
+ transformCache.delete(entry.target);
4519
+ return;
4520
+ }
4521
+ const etag = `W/"${Date.now()}"`;
4522
+ if (entry.source?.type === "file") {
4523
+ server?.watcher.add(entry.source.pathname);
4524
+ }
4525
+ return { content: html, etag };
4526
+ } catch (error) {
4527
+ console.error(getFormattedError(error));
4528
+ transformCache.delete(entry.target);
4529
+ return;
4530
+ }
4531
+ })();
4532
+ transformCache.set(entry.target, promise);
4533
+ return await promise;
4534
+ }
4535
+ async function invalidate(entry, config2) {
4536
+ const cwd2 = pathToFileURL7(config2.workspaceDir);
4537
+ const target = pathToFileURL7(entry.target);
4538
+ if (target.href.indexOf(cwd2.href) !== 0) {
4539
+ return;
4540
+ }
4541
+ transformCache.delete(entry.target);
4542
+ config2.entries.filter((entry2) => entry2.rel === "contents").forEach((entry2) => {
4543
+ transformCache.delete(entry2.target);
4544
+ });
4545
+ server?.ws.send({
4546
+ type: "full-reload",
4547
+ path: target.href.slice(cwd2.href.length)
4548
+ });
4549
+ }
4550
+ const devServerMiddleware = async function vivliostyleDevServerMiddleware(req, res, next) {
4551
+ if (!isWebPubConfig(config) || !program) {
4552
+ return next();
4553
+ }
4554
+ const { entriesLookup, urlMatchRe } = program;
4555
+ const [_, pathname, qs] = decodeURI(req.url).match(urlMatchRe) ?? [];
4556
+ const match = pathname && entriesLookup(pathname);
4557
+ if (!match) {
4558
+ return next();
4559
+ }
4560
+ const [entry, expected] = match;
4561
+ if (pathname !== expected) {
4562
+ res.statusCode = 301;
4563
+ res.setHeader("Location", `${expected}${qs || ""}`);
4564
+ return res.end();
4565
+ }
4566
+ const cachePromise = transformCache.get(entry.target);
4567
+ if (cachePromise) {
4568
+ const cached = await cachePromise;
4569
+ if (!cached) {
4570
+ return next();
4571
+ }
4572
+ if (req.headers["if-none-match"] === cached.etag) {
4573
+ res.statusCode = 304;
4574
+ return res.end();
4575
+ } else {
4576
+ res.statusCode = 200;
4577
+ res.setHeader("Content-Type", "text/html;charset=utf-8");
4578
+ res.setHeader("Cache-Control", "no-cache");
4579
+ res.setHeader("Etag", cached.etag);
4580
+ return res.end(cached.content);
4581
+ }
4582
+ }
4583
+ if (entry.rel === "contents") {
4584
+ const _config2 = { ...config };
4585
+ await Promise.all(
4586
+ _config2.entries.flatMap(
4587
+ (e) => isWebPubConfig(_config2) && e.rel !== "contents" && e.rel !== "cover" ? transform(e, _config2) : []
4588
+ )
4589
+ );
4590
+ }
4591
+ const result = await transform(entry, config);
4592
+ if (!result) {
4593
+ return next();
4594
+ }
4595
+ res.statusCode = 200;
4596
+ res.setHeader("Content-Type", "text/html;charset=utf-8");
4597
+ res.setHeader("Cache-Control", "no-cache");
4598
+ res.setHeader("Etag", result.etag);
4599
+ return res.end(result.content);
4600
+ };
4601
+ const serveWorkspaceMiddleware = async function vivliostyleServeWorkspaceMiddleware(req, res, next) {
4602
+ if (!config || !program) {
4603
+ return next();
4604
+ }
4605
+ const {
4606
+ urlMatchRe,
4607
+ serveWorkspace,
4608
+ serveWorkspaceMatcher,
4609
+ serveAssets,
4610
+ serveAssetsMatcher
4611
+ } = program;
4612
+ const [_, pathname] = decodeURI(req.url).match(urlMatchRe) ?? [];
4613
+ if (pathname && serveWorkspaceMatcher(pathname.slice(1))) {
4614
+ req.url = req.url.slice(config.base.length);
4615
+ return serveWorkspace(req, res, next);
4616
+ }
4617
+ if (pathname && serveAssetsMatcher(pathname.slice(1))) {
4618
+ req.url = req.url.slice(config.base.length);
4619
+ return serveAssets(req, res, next);
4620
+ }
4621
+ next();
4622
+ };
4623
+ return {
4624
+ name: "vivliostyle:dev-server",
4625
+ enforce: "pre",
4626
+ configureServer(viteServer) {
4627
+ server = viteServer;
4628
+ const handleUpdate = async (pathname) => {
4629
+ if (!projectDeps.has(pathname)) {
4630
+ return;
4631
+ }
4632
+ await reload();
4633
+ viteServer.ws.send({
4634
+ type: "full-reload",
4635
+ path: "*"
4636
+ });
4637
+ };
4638
+ viteServer.watcher.on("add", handleUpdate);
4639
+ viteServer.watcher.on("change", handleUpdate);
4640
+ viteServer.watcher.on("unlink", handleUpdate);
4641
+ return () => {
4642
+ viteServer.middlewares.use(devServerMiddleware);
4643
+ viteServer.middlewares.use(serveWorkspaceMiddleware);
4644
+ };
4645
+ },
4646
+ configurePreviewServer(viteServer) {
4647
+ return () => {
4648
+ viteServer.middlewares.use(
4649
+ config.base,
4650
+ sirv(config.workspaceDir, { dev: true, etag: false, extensions: [] })
4651
+ );
4652
+ };
4653
+ },
4654
+ async buildStart() {
4655
+ await reload(true);
4656
+ },
4657
+ async handleHotUpdate(ctx) {
4658
+ const entry = config?.entries.find(
4659
+ (e) => e.source?.type === "file" && e.source.pathname === ctx.file || !e.source && e.target === ctx.file
4660
+ );
4661
+ if (config && entry) {
4662
+ await invalidate(entry, config);
4663
+ }
4664
+ }
4665
+ };
4666
+ }
4667
+
4668
+ // src/vite/vite-plugin-static-serve.ts
4669
+ import sirv2 from "sirv";
4670
+ import upath11 from "upath";
4671
+ function vsStaticServePlugin({
4672
+ config: _config,
4673
+ inlineConfig
4674
+ }) {
4675
+ let config = _config;
4676
+ const createMiddlewares = () => Object.entries(config.static).flatMap(
4677
+ ([base, dirs]) => dirs.map(
4678
+ (dir) => [
4679
+ base,
4680
+ sirv2(upath11.resolve(config.context, dir), {
4681
+ dev: true,
4682
+ etag: false
4683
+ })
4684
+ ]
4685
+ )
4686
+ );
4687
+ return {
4688
+ name: "vivliostyle:static-serve",
4689
+ apply: () => Boolean(inlineConfig.enableStaticServe),
4690
+ configureServer(viteServer) {
4691
+ return () => {
4692
+ createMiddlewares().forEach(([base, middleware]) => {
4693
+ viteServer.middlewares.use(base, middleware);
4694
+ });
4695
+ };
4696
+ },
4697
+ configurePreviewServer(viteServer) {
4698
+ return () => {
4699
+ createMiddlewares().forEach(([base, middleware]) => {
4700
+ viteServer.middlewares.use(base, middleware);
4701
+ });
4702
+ };
4703
+ }
4704
+ };
4705
+ }
4706
+
4707
+ // src/vite/vite-plugin-viewer.ts
4708
+ import fs10 from "node:fs";
4709
+ import sirv3 from "sirv";
4710
+ import upath12 from "upath";
4711
+ var viewerClientId = "@vivliostyle:viewer:client";
4712
+ var viewerClientRequestPath = `/${viewerClientId}`;
4713
+ var viewerClientContent = (
4714
+ /* js */
4715
+ `
4716
+ if (import.meta.hot) {
4717
+ import.meta.hot.on('vite:beforeFullReload', (e) => {
4718
+ location.reload();
4719
+ });
4720
+ }`
4721
+ );
4722
+ function vsViewerPlugin(_) {
4723
+ const serveRootDir = upath12.join(viewerRoot, "lib");
4724
+ const serve = sirv3(serveRootDir, { dev: false, etag: true });
4725
+ let cachedIndexHtml;
4726
+ const middleware = async function vivliostyleViewerMiddleware(req, res, next) {
4727
+ if (req.url === "/" || req.url === "/index.html") {
4728
+ cachedIndexHtml ??= prependToHead(
4729
+ fs10.readFileSync(upath12.join(serveRootDir, "index.html"), "utf-8"),
4730
+ `<script type="module" src="${viewerClientRequestPath}"></script>`
4731
+ );
4732
+ res.statusCode = 200;
4733
+ res.setHeader("Content-Type", "text/html;charset=utf-8");
4734
+ res.setHeader("Cache-Control", "no-cache");
4735
+ return res.end(cachedIndexHtml);
4736
+ } else {
4737
+ return serve(req, res, next);
4738
+ }
4739
+ };
4740
+ return {
4741
+ name: "vivliostyle:viewer",
4742
+ config() {
4743
+ return {
4744
+ optimizeDeps: {
4745
+ exclude: ["@vivliostyle/viewer"]
4746
+ }
4747
+ };
4748
+ },
4749
+ configureServer(viteServer) {
4750
+ viteServer.middlewares.use(VIEWER_ROOT_PATH, middleware);
4751
+ },
4752
+ configurePreviewServer(viteServer) {
4753
+ viteServer.middlewares.use(VIEWER_ROOT_PATH, serve);
4754
+ },
4755
+ load(id) {
4756
+ if (id === viewerClientRequestPath) {
4757
+ return viewerClientContent;
4758
+ }
4759
+ }
4760
+ };
4761
+ }
4762
+
4763
+ // src/server.ts
4764
+ function getViewerParams(src, {
4765
+ size,
4766
+ cropMarks,
4767
+ bleed,
4768
+ cropOffset,
4769
+ css,
4770
+ customStyle,
4771
+ customUserStyle,
4772
+ singleDoc,
4773
+ quick,
4774
+ viewerParam
4775
+ }) {
4776
+ const pageSizeValue = size && ("format" in size ? size.format : `${size.width} ${size.height}`);
4777
+ function escapeParam(url) {
4778
+ return url.replace(/&/g, "%26");
4779
+ }
4780
+ let viewerParams = src ? `src=${escapeParam(src)}` : "";
4781
+ viewerParams += `&bookMode=${!singleDoc}&renderAllPages=${!quick}`;
4782
+ if (customStyle) {
4783
+ viewerParams += `&style=${escapeParam(customStyle)}`;
4784
+ }
4785
+ if (customUserStyle) {
4786
+ viewerParams += `&userStyle=${escapeParam(customUserStyle)}`;
4787
+ }
4788
+ if (pageSizeValue || cropMarks || bleed || cropOffset || css) {
4789
+ let pageStyle = "@page{";
4790
+ if (pageSizeValue) {
4791
+ pageStyle += `size:${pageSizeValue};`;
4792
+ }
4793
+ if (cropMarks) {
4794
+ pageStyle += `marks:crop cross;`;
4795
+ }
4796
+ if (bleed || cropMarks) {
4797
+ pageStyle += `bleed:${bleed ?? "3mm"};`;
4798
+ }
4799
+ if (cropOffset) {
4800
+ pageStyle += `crop-offset:${cropOffset};`;
4801
+ }
4802
+ pageStyle += "}";
4803
+ viewerParams += `&style=data:,/*<viewer>*/${encodeURIComponent(
4804
+ pageStyle
4805
+ )}/*</viewer>*/${encodeURIComponent(css ?? "")}`;
4806
+ }
4807
+ if (viewerParam) {
4808
+ viewerParams += `&${viewerParam}`;
4809
+ }
4810
+ return viewerParams;
4811
+ }
4812
+ async function getSourceUrl({
4813
+ viewerInput,
4814
+ base,
4815
+ workspaceDir,
4816
+ rootUrl
4817
+ }) {
4818
+ let input;
4819
+ switch (viewerInput.type) {
4820
+ case "webpub":
4821
+ input = viewerInput.manifestPath;
4822
+ break;
4823
+ case "webbook":
4824
+ input = viewerInput.webbookEntryUrl;
4825
+ break;
4826
+ case "epub-opf":
4827
+ input = viewerInput.epubOpfPath;
4828
+ break;
4829
+ case "epub": {
4830
+ if (!fs11.existsSync(viewerInput.epubTmpOutputDir)) {
4831
+ await openEpub(viewerInput.epubPath, viewerInput.epubTmpOutputDir);
4832
+ }
4833
+ input = getDefaultEpubOpfPath(viewerInput.epubTmpOutputDir);
4834
+ break;
4835
+ }
4836
+ default:
4837
+ input = viewerInput;
4838
+ }
4839
+ return (isValidUri(input) ? new URL2(input) : new URL2(
4840
+ upath13.posix.join(base, upath13.relative(workspaceDir, input)),
4841
+ rootUrl
4842
+ )).href;
4843
+ }
4844
+ async function getViewerFullUrl({
4845
+ viewerInput,
4846
+ base,
4847
+ workspaceDir,
4848
+ rootUrl,
4849
+ viewer,
4850
+ ...config
4851
+ }) {
4852
+ const viewerUrl = viewer ? new URL2(viewer) : new URL2(`${VIEWER_ROOT_PATH}/index.html`, rootUrl);
4853
+ const sourceUrl = await getSourceUrl({
4854
+ viewerInput,
4855
+ base,
4856
+ workspaceDir,
4857
+ rootUrl
4858
+ });
4859
+ const viewerParams = getViewerParams(
4860
+ sourceUrl === EMPTY_DATA_URI ? void 0 : sourceUrl,
4861
+ config
4862
+ );
4863
+ viewerUrl.hash = "";
4864
+ return `${viewerUrl.href}#${viewerParams}`;
4865
+ }
4866
+ async function createViteServer({
4867
+ config,
4868
+ viteConfig,
4869
+ inlineConfig,
4870
+ mode
4871
+ }) {
4872
+ const viteInlineConfig = {
4873
+ clearScreen: false,
4874
+ configFile: false,
4875
+ appType: "custom",
4876
+ plugins: [
4877
+ vsDevServerPlugin({ config, inlineConfig }),
4878
+ vsViewerPlugin({ config, inlineConfig }),
4879
+ vsBrowserPlugin({ config, inlineConfig }),
4880
+ vsStaticServePlugin({ config, inlineConfig })
4881
+ ],
4882
+ server: viteConfig.server,
4883
+ preview: viteConfig.preview,
4884
+ customLogger: viteConfig.customLogger,
4885
+ cacheDir: viteConfig.cacheDir
4886
+ };
4887
+ Logger.debug("createViteServer > viteInlineConfig %O", viteInlineConfig);
4888
+ if (config.context === config.workspaceDir) {
4889
+ const { cacheDir } = viteInlineConfig;
4890
+ registerExitHandler("Removing the Vite cacheDir", () => {
4891
+ if (fs11.existsSync(cacheDir)) {
4892
+ fs11.rmSync(cacheDir, { recursive: true });
4893
+ }
4894
+ });
4895
+ }
4896
+ if (mode === "preview") {
4897
+ return await createServer(viteInlineConfig);
4898
+ } else {
4899
+ return await preview(viteInlineConfig);
4900
+ }
4901
+ }
4902
+
4903
+ // src/vite/vite-plugin-browser.ts
4904
+ function vsBrowserPlugin({
4905
+ config: _config,
4906
+ inlineConfig
4907
+ }) {
4908
+ let config = _config;
4909
+ let server;
4910
+ let closeBrowser;
4911
+ async function handlePageClose() {
4912
+ await server?.close();
4913
+ runExitHandlers();
4914
+ }
4915
+ async function openPreviewPage() {
4916
+ const url = await getViewerFullUrl(config);
4917
+ const { page, browser } = await launchPreview({
4918
+ mode: "preview",
4919
+ url,
4920
+ config,
4921
+ onPageOpen: async (page2) => {
4922
+ page2.on("close", handlePageClose);
4923
+ const locale = Intl.DateTimeFormat().resolvedOptions().locale;
4924
+ await page2.addInitScript(
4925
+ `window.localStorage.setItem('i18nextLng', '${locale}');`
4926
+ );
4927
+ }
4928
+ });
4929
+ await page.bringToFront();
4930
+ await page.locator("#vivliostyle-input-url").focus({ timeout: 0 });
4931
+ closeBrowser = () => {
4932
+ page.off("close", handlePageClose);
4933
+ browser.close();
4934
+ };
4935
+ }
4936
+ return {
4937
+ name: "vivliostyle:browser",
4938
+ apply: () => Boolean(inlineConfig.openViewer),
4939
+ configureServer(viteServer) {
4940
+ server = viteServer;
4941
+ const _listen = viteServer.listen;
4942
+ viteServer.listen = async (...args) => {
4943
+ const server2 = await _listen(...args);
4944
+ config = await reloadConfig(config, inlineConfig, server2.config);
4945
+ await openPreviewPage();
4946
+ return server2;
4947
+ };
4948
+ },
4949
+ closeBundle() {
4950
+ closeBrowser?.();
4951
+ }
4952
+ };
4953
+ }
4954
+
4955
+ export {
4956
+ cwd,
4957
+ runExitHandlers,
4958
+ gracefulError,
4959
+ pathEquals,
4960
+ isInContainer,
4961
+ isUnicodeSupported,
4962
+ randomBookSymbol,
4963
+ Logger,
4964
+ parseFlagsToInlineConfig,
4965
+ setupConfigFromFlags,
4966
+ loadVivliostyleConfig,
4967
+ warnDeprecatedConfig,
4968
+ mergeConfig,
4969
+ mergeInlineConfig,
4970
+ getFullBrowserName,
4971
+ launchPreview,
4972
+ vsBrowserPlugin,
4973
+ buildWebPublication,
4974
+ cleanupWorkspace,
4975
+ prepareThemeDirectory,
4976
+ compile,
4977
+ copyAssets,
4978
+ vsDevServerPlugin,
4979
+ vsStaticServePlugin,
4980
+ vsViewerPlugin,
4981
+ getViewerFullUrl,
4982
+ createViteServer,
4983
+ CONTAINER_IMAGE,
4984
+ toContainerPath,
4985
+ collectVolumeArgs,
4986
+ runContainer,
4987
+ buildPDFWithContainer,
4988
+ isWebPubConfig,
4989
+ resolveTaskConfig
4990
+ };