@vivliostyle/cli 9.0.0-next.3 → 9.0.0-next.5
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.
- package/dist/chunk-4ZNBH6IA.js +74 -0
- package/dist/{core/init.js → chunk-6ETFX7W2.js} +39 -27
- package/dist/chunk-BIEQXUOY.js +50 -0
- package/dist/chunk-BPSQMFSC.js +54 -0
- package/dist/chunk-BT47BDZK.js +85 -0
- package/dist/chunk-DMARNPN5.js +1073 -0
- package/dist/chunk-EX7EA34F.js +64 -0
- package/dist/chunk-SPYTZBBG.js +4988 -0
- package/dist/chunk-UQHNZL7X.js +590 -0
- package/dist/chunk-WX6JHPSL.js +16 -0
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +17 -18
- package/dist/commands/build.d.ts +2 -2
- package/dist/commands/build.js +165 -17
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.js +35 -12
- package/dist/commands/preview.d.ts +2 -2
- package/dist/commands/preview.js +98 -12
- package/dist/config/schema.d.ts +225 -218
- package/dist/config/schema.js +50 -588
- package/dist/index.d.ts +326 -8
- package/dist/index.js +47 -42
- package/dist/node-modules.d.ts +21 -0
- package/dist/node-modules.js +9 -0
- package/dist/vite-adapter.d.ts +8 -2
- package/dist/vite-adapter.js +11 -43
- package/package.json +32 -13
- package/dist/browser.d.ts +0 -19
- package/dist/browser.d.ts.map +0 -1
- package/dist/browser.js +0 -154
- package/dist/browser.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/build.d.ts.map +0 -1
- package/dist/commands/build.js.map +0 -1
- package/dist/commands/build.parser.d.ts +0 -3
- package/dist/commands/build.parser.d.ts.map +0 -1
- package/dist/commands/build.parser.js +0 -104
- package/dist/commands/build.parser.js.map +0 -1
- package/dist/commands/cli-flags.d.ts +0 -42
- package/dist/commands/cli-flags.d.ts.map +0 -1
- package/dist/commands/cli-flags.js +0 -58
- package/dist/commands/cli-flags.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/init.parser.d.ts +0 -3
- package/dist/commands/init.parser.d.ts.map +0 -1
- package/dist/commands/init.parser.js +0 -17
- package/dist/commands/init.parser.js.map +0 -1
- package/dist/commands/preview.d.ts.map +0 -1
- package/dist/commands/preview.js.map +0 -1
- package/dist/commands/preview.parser.d.ts +0 -3
- package/dist/commands/preview.parser.d.ts.map +0 -1
- package/dist/commands/preview.parser.js +0 -52
- package/dist/commands/preview.parser.js.map +0 -1
- package/dist/config/load.d.ts +0 -4
- package/dist/config/load.d.ts.map +0 -1
- package/dist/config/load.js +0 -75
- package/dist/config/load.js.map +0 -1
- package/dist/config/merge.d.ts +0 -4
- package/dist/config/merge.d.ts.map +0 -1
- package/dist/config/merge.js +0 -59
- package/dist/config/merge.js.map +0 -1
- package/dist/config/resolve.d.ts +0 -205
- package/dist/config/resolve.d.ts.map +0 -1
- package/dist/config/resolve.js +0 -770
- package/dist/config/resolve.js.map +0 -1
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/config/schema.js.map +0 -1
- package/dist/config/vite.d.ts +0 -6
- package/dist/config/vite.d.ts.map +0 -1
- package/dist/config/vite.js +0 -49
- package/dist/config/vite.js.map +0 -1
- package/dist/const.d.ts +0 -20
- package/dist/const.d.ts.map +0 -1
- package/dist/const.js +0 -41
- package/dist/const.js.map +0 -1
- package/dist/container.d.ts +0 -21
- package/dist/container.d.ts.map +0 -1
- package/dist/container.js +0 -175
- package/dist/container.js.map +0 -1
- package/dist/core/build.d.ts +0 -3
- package/dist/core/build.d.ts.map +0 -1
- package/dist/core/build.js +0 -168
- package/dist/core/build.js.map +0 -1
- package/dist/core/init.d.ts +0 -3
- package/dist/core/init.d.ts.map +0 -1
- package/dist/core/init.js.map +0 -1
- package/dist/core/preview.d.ts +0 -4
- package/dist/core/preview.d.ts.map +0 -1
- package/dist/core/preview.js +0 -114
- package/dist/core/preview.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger.d.ts +0 -30
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -172
- package/dist/logger.js.map +0 -1
- package/dist/output/epub.d.ts +0 -10
- package/dist/output/epub.d.ts.map +0 -1
- package/dist/output/epub.js +0 -505
- package/dist/output/epub.js.map +0 -1
- package/dist/output/pdf-postprocess.d.ts +0 -30
- package/dist/output/pdf-postprocess.d.ts.map +0 -1
- package/dist/output/pdf-postprocess.js +0 -270
- package/dist/output/pdf-postprocess.js.map +0 -1
- package/dist/output/pdf.d.ts +0 -6
- package/dist/output/pdf.d.ts.map +0 -1
- package/dist/output/pdf.js +0 -200
- package/dist/output/pdf.js.map +0 -1
- package/dist/output/webbook.d.ts +0 -45
- package/dist/output/webbook.d.ts.map +0 -1
- package/dist/output/webbook.js +0 -413
- package/dist/output/webbook.js.map +0 -1
- package/dist/processor/compile.d.ts +0 -30
- package/dist/processor/compile.d.ts.map +0 -1
- package/dist/processor/compile.js +0 -348
- package/dist/processor/compile.js.map +0 -1
- package/dist/processor/html.d.ts +0 -107
- package/dist/processor/html.d.ts.map +0 -1
- package/dist/processor/html.js +0 -494
- package/dist/processor/html.js.map +0 -1
- package/dist/processor/markdown.d.ts +0 -12
- package/dist/processor/markdown.d.ts.map +0 -1
- package/dist/processor/markdown.js +0 -23
- package/dist/processor/markdown.js.map +0 -1
- package/dist/processor/theme.d.ts +0 -4
- package/dist/processor/theme.d.ts.map +0 -1
- package/dist/processor/theme.js +0 -41
- package/dist/processor/theme.js.map +0 -1
- package/dist/schema/pub-manifest.d.ts +0 -4
- package/dist/schema/pub-manifest.d.ts.map +0 -1
- package/dist/schema/pub-manifest.js +0 -41
- package/dist/schema/pub-manifest.js.map +0 -1
- package/dist/schema/publication.schema.d.ts +0 -104
- package/dist/schema/publication.schema.d.ts.map +0 -1
- package/dist/schema/publication.schema.js +0 -8
- package/dist/schema/publication.schema.js.map +0 -1
- package/dist/server.d.ts +0 -20
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -126
- package/dist/server.js.map +0 -1
- package/dist/util.d.ts +0 -55
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js +0 -274
- package/dist/util.js.map +0 -1
- package/dist/vite/plugin-util.d.ts +0 -6
- package/dist/vite/plugin-util.d.ts.map +0 -1
- package/dist/vite/plugin-util.js +0 -18
- package/dist/vite/plugin-util.js.map +0 -1
- package/dist/vite/vite-plugin-browser.d.ts +0 -8
- package/dist/vite/vite-plugin-browser.d.ts.map +0 -1
- package/dist/vite/vite-plugin-browser.js +0 -54
- package/dist/vite/vite-plugin-browser.js.map +0 -1
- package/dist/vite/vite-plugin-dev-server.d.ts +0 -8
- package/dist/vite/vite-plugin-dev-server.d.ts.map +0 -1
- package/dist/vite/vite-plugin-dev-server.js +0 -270
- package/dist/vite/vite-plugin-dev-server.js.map +0 -1
- package/dist/vite/vite-plugin-static-serve.d.ts +0 -8
- package/dist/vite/vite-plugin-static-serve.d.ts.map +0 -1
- package/dist/vite/vite-plugin-static-serve.js +0 -31
- package/dist/vite/vite-plugin-static-serve.js.map +0 -1
- package/dist/vite/vite-plugin-viewer.d.ts +0 -8
- package/dist/vite/vite-plugin-viewer.d.ts.map +0 -1
- package/dist/vite/vite-plugin-viewer.js +0 -52
- package/dist/vite/vite-plugin-viewer.js.map +0 -1
- package/dist/vite-adapter.d.ts.map +0 -1
- package/dist/vite-adapter.js.map +0 -1
- package/schemas/pub-manifest/README.md +0 -5
- package/schemas/pub-manifest/module/ItemList.schema.json +0 -32
- package/schemas/pub-manifest/module/bcp.schema.json +0 -7
- package/schemas/pub-manifest/module/context.schema.json +0 -62
- package/schemas/pub-manifest/module/contributor-object.schema.json +0 -42
- package/schemas/pub-manifest/module/contributor.schema.json +0 -26
- package/schemas/pub-manifest/module/date.schema.json +0 -7
- package/schemas/pub-manifest/module/duration.schema.json +0 -7
- package/schemas/pub-manifest/module/item-lists.schema.json +0 -16
- package/schemas/pub-manifest/module/language.schema.json +0 -16
- package/schemas/pub-manifest/module/link.schema.json +0 -60
- package/schemas/pub-manifest/module/localizable-object.schema.json +0 -15
- package/schemas/pub-manifest/module/localizable.schema.json +0 -26
- package/schemas/pub-manifest/module/resource.categorization.schema.json +0 -31
- package/schemas/pub-manifest/module/strings.schema.json +0 -9
- package/schemas/pub-manifest/module/url.schema.json +0 -7
- package/schemas/pub-manifest/module/urls.schema.json +0 -18
- package/schemas/pub-manifest/publication.schema.json +0 -123
|
@@ -0,0 +1,4988 @@
|
|
|
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: { exclude: ["@vivliostyle/viewer"] }
|
|
4745
|
+
};
|
|
4746
|
+
},
|
|
4747
|
+
configureServer(viteServer) {
|
|
4748
|
+
viteServer.middlewares.use(VIEWER_ROOT_PATH, middleware);
|
|
4749
|
+
},
|
|
4750
|
+
configurePreviewServer(viteServer) {
|
|
4751
|
+
viteServer.middlewares.use(VIEWER_ROOT_PATH, serve);
|
|
4752
|
+
},
|
|
4753
|
+
load(id) {
|
|
4754
|
+
if (id === viewerClientRequestPath) {
|
|
4755
|
+
return viewerClientContent;
|
|
4756
|
+
}
|
|
4757
|
+
}
|
|
4758
|
+
};
|
|
4759
|
+
}
|
|
4760
|
+
|
|
4761
|
+
// src/server.ts
|
|
4762
|
+
function getViewerParams(src, {
|
|
4763
|
+
size,
|
|
4764
|
+
cropMarks,
|
|
4765
|
+
bleed,
|
|
4766
|
+
cropOffset,
|
|
4767
|
+
css,
|
|
4768
|
+
customStyle,
|
|
4769
|
+
customUserStyle,
|
|
4770
|
+
singleDoc,
|
|
4771
|
+
quick,
|
|
4772
|
+
viewerParam
|
|
4773
|
+
}) {
|
|
4774
|
+
const pageSizeValue = size && ("format" in size ? size.format : `${size.width} ${size.height}`);
|
|
4775
|
+
function escapeParam(url) {
|
|
4776
|
+
return url.replace(/&/g, "%26");
|
|
4777
|
+
}
|
|
4778
|
+
let viewerParams = src ? `src=${escapeParam(src)}` : "";
|
|
4779
|
+
viewerParams += `&bookMode=${!singleDoc}&renderAllPages=${!quick}`;
|
|
4780
|
+
if (customStyle) {
|
|
4781
|
+
viewerParams += `&style=${escapeParam(customStyle)}`;
|
|
4782
|
+
}
|
|
4783
|
+
if (customUserStyle) {
|
|
4784
|
+
viewerParams += `&userStyle=${escapeParam(customUserStyle)}`;
|
|
4785
|
+
}
|
|
4786
|
+
if (pageSizeValue || cropMarks || bleed || cropOffset || css) {
|
|
4787
|
+
let pageStyle = "@page{";
|
|
4788
|
+
if (pageSizeValue) {
|
|
4789
|
+
pageStyle += `size:${pageSizeValue};`;
|
|
4790
|
+
}
|
|
4791
|
+
if (cropMarks) {
|
|
4792
|
+
pageStyle += `marks:crop cross;`;
|
|
4793
|
+
}
|
|
4794
|
+
if (bleed || cropMarks) {
|
|
4795
|
+
pageStyle += `bleed:${bleed ?? "3mm"};`;
|
|
4796
|
+
}
|
|
4797
|
+
if (cropOffset) {
|
|
4798
|
+
pageStyle += `crop-offset:${cropOffset};`;
|
|
4799
|
+
}
|
|
4800
|
+
pageStyle += "}";
|
|
4801
|
+
viewerParams += `&style=data:,/*<viewer>*/${encodeURIComponent(
|
|
4802
|
+
pageStyle
|
|
4803
|
+
)}/*</viewer>*/${encodeURIComponent(css ?? "")}`;
|
|
4804
|
+
}
|
|
4805
|
+
if (viewerParam) {
|
|
4806
|
+
viewerParams += `&${viewerParam}`;
|
|
4807
|
+
}
|
|
4808
|
+
return viewerParams;
|
|
4809
|
+
}
|
|
4810
|
+
async function getSourceUrl({
|
|
4811
|
+
viewerInput,
|
|
4812
|
+
base,
|
|
4813
|
+
workspaceDir,
|
|
4814
|
+
rootUrl
|
|
4815
|
+
}) {
|
|
4816
|
+
let input;
|
|
4817
|
+
switch (viewerInput.type) {
|
|
4818
|
+
case "webpub":
|
|
4819
|
+
input = viewerInput.manifestPath;
|
|
4820
|
+
break;
|
|
4821
|
+
case "webbook":
|
|
4822
|
+
input = viewerInput.webbookEntryUrl;
|
|
4823
|
+
break;
|
|
4824
|
+
case "epub-opf":
|
|
4825
|
+
input = viewerInput.epubOpfPath;
|
|
4826
|
+
break;
|
|
4827
|
+
case "epub": {
|
|
4828
|
+
if (!fs11.existsSync(viewerInput.epubTmpOutputDir)) {
|
|
4829
|
+
await openEpub(viewerInput.epubPath, viewerInput.epubTmpOutputDir);
|
|
4830
|
+
}
|
|
4831
|
+
input = getDefaultEpubOpfPath(viewerInput.epubTmpOutputDir);
|
|
4832
|
+
break;
|
|
4833
|
+
}
|
|
4834
|
+
default:
|
|
4835
|
+
input = viewerInput;
|
|
4836
|
+
}
|
|
4837
|
+
return (isValidUri(input) ? new URL2(input) : new URL2(
|
|
4838
|
+
upath13.posix.join(base, upath13.relative(workspaceDir, input)),
|
|
4839
|
+
rootUrl
|
|
4840
|
+
)).href;
|
|
4841
|
+
}
|
|
4842
|
+
async function getViewerFullUrl({
|
|
4843
|
+
viewerInput,
|
|
4844
|
+
base,
|
|
4845
|
+
workspaceDir,
|
|
4846
|
+
rootUrl,
|
|
4847
|
+
viewer,
|
|
4848
|
+
...config
|
|
4849
|
+
}) {
|
|
4850
|
+
const viewerUrl = viewer ? new URL2(viewer) : new URL2(`${VIEWER_ROOT_PATH}/index.html`, rootUrl);
|
|
4851
|
+
const sourceUrl = await getSourceUrl({
|
|
4852
|
+
viewerInput,
|
|
4853
|
+
base,
|
|
4854
|
+
workspaceDir,
|
|
4855
|
+
rootUrl
|
|
4856
|
+
});
|
|
4857
|
+
const viewerParams = getViewerParams(
|
|
4858
|
+
sourceUrl === EMPTY_DATA_URI ? void 0 : sourceUrl,
|
|
4859
|
+
config
|
|
4860
|
+
);
|
|
4861
|
+
viewerUrl.hash = "";
|
|
4862
|
+
return `${viewerUrl.href}#${viewerParams}`;
|
|
4863
|
+
}
|
|
4864
|
+
async function createViteServer({
|
|
4865
|
+
config,
|
|
4866
|
+
viteConfig,
|
|
4867
|
+
inlineConfig,
|
|
4868
|
+
mode
|
|
4869
|
+
}) {
|
|
4870
|
+
const viteInlineConfig = {
|
|
4871
|
+
clearScreen: false,
|
|
4872
|
+
configFile: false,
|
|
4873
|
+
appType: "custom",
|
|
4874
|
+
plugins: [
|
|
4875
|
+
vsDevServerPlugin({ config, inlineConfig }),
|
|
4876
|
+
vsViewerPlugin({ config, inlineConfig }),
|
|
4877
|
+
vsBrowserPlugin({ config, inlineConfig }),
|
|
4878
|
+
vsStaticServePlugin({ config, inlineConfig })
|
|
4879
|
+
],
|
|
4880
|
+
server: viteConfig.server,
|
|
4881
|
+
preview: viteConfig.preview,
|
|
4882
|
+
customLogger: viteConfig.customLogger,
|
|
4883
|
+
cacheDir: viteConfig.cacheDir
|
|
4884
|
+
};
|
|
4885
|
+
Logger.debug("createViteServer > viteInlineConfig %O", viteInlineConfig);
|
|
4886
|
+
if (config.context === config.workspaceDir) {
|
|
4887
|
+
const { cacheDir } = viteInlineConfig;
|
|
4888
|
+
registerExitHandler("Removing the Vite cacheDir", () => {
|
|
4889
|
+
if (fs11.existsSync(cacheDir)) {
|
|
4890
|
+
fs11.rmSync(cacheDir, { recursive: true });
|
|
4891
|
+
}
|
|
4892
|
+
});
|
|
4893
|
+
}
|
|
4894
|
+
if (mode === "preview") {
|
|
4895
|
+
return await createServer(viteInlineConfig);
|
|
4896
|
+
} else {
|
|
4897
|
+
return await preview(viteInlineConfig);
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4900
|
+
|
|
4901
|
+
// src/vite/vite-plugin-browser.ts
|
|
4902
|
+
function vsBrowserPlugin({
|
|
4903
|
+
config: _config,
|
|
4904
|
+
inlineConfig
|
|
4905
|
+
}) {
|
|
4906
|
+
let config = _config;
|
|
4907
|
+
let server;
|
|
4908
|
+
let closeBrowser;
|
|
4909
|
+
async function handlePageClose() {
|
|
4910
|
+
await server?.close();
|
|
4911
|
+
runExitHandlers();
|
|
4912
|
+
}
|
|
4913
|
+
async function openPreviewPage() {
|
|
4914
|
+
const url = await getViewerFullUrl(config);
|
|
4915
|
+
const { page, browser } = await launchPreview({
|
|
4916
|
+
mode: "preview",
|
|
4917
|
+
url,
|
|
4918
|
+
config,
|
|
4919
|
+
onPageOpen: async (page2) => {
|
|
4920
|
+
page2.on("close", handlePageClose);
|
|
4921
|
+
const locale = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
4922
|
+
await page2.addInitScript(
|
|
4923
|
+
`window.localStorage.setItem('i18nextLng', '${locale}');`
|
|
4924
|
+
);
|
|
4925
|
+
}
|
|
4926
|
+
});
|
|
4927
|
+
await page.bringToFront();
|
|
4928
|
+
await page.locator("#vivliostyle-input-url").focus({ timeout: 0 });
|
|
4929
|
+
closeBrowser = () => {
|
|
4930
|
+
page.off("close", handlePageClose);
|
|
4931
|
+
browser.close();
|
|
4932
|
+
};
|
|
4933
|
+
}
|
|
4934
|
+
return {
|
|
4935
|
+
name: "vivliostyle:browser",
|
|
4936
|
+
apply: () => Boolean(inlineConfig.openViewer),
|
|
4937
|
+
configureServer(viteServer) {
|
|
4938
|
+
server = viteServer;
|
|
4939
|
+
const _listen = viteServer.listen;
|
|
4940
|
+
viteServer.listen = async (...args) => {
|
|
4941
|
+
const server2 = await _listen(...args);
|
|
4942
|
+
config = await reloadConfig(config, inlineConfig, server2.config);
|
|
4943
|
+
await openPreviewPage();
|
|
4944
|
+
return server2;
|
|
4945
|
+
};
|
|
4946
|
+
},
|
|
4947
|
+
closeBundle() {
|
|
4948
|
+
closeBrowser?.();
|
|
4949
|
+
}
|
|
4950
|
+
};
|
|
4951
|
+
}
|
|
4952
|
+
|
|
4953
|
+
export {
|
|
4954
|
+
cwd,
|
|
4955
|
+
runExitHandlers,
|
|
4956
|
+
gracefulError,
|
|
4957
|
+
pathEquals,
|
|
4958
|
+
isInContainer,
|
|
4959
|
+
isUnicodeSupported,
|
|
4960
|
+
randomBookSymbol,
|
|
4961
|
+
Logger,
|
|
4962
|
+
parseFlagsToInlineConfig,
|
|
4963
|
+
setupConfigFromFlags,
|
|
4964
|
+
loadVivliostyleConfig,
|
|
4965
|
+
warnDeprecatedConfig,
|
|
4966
|
+
mergeConfig,
|
|
4967
|
+
mergeInlineConfig,
|
|
4968
|
+
getFullBrowserName,
|
|
4969
|
+
launchPreview,
|
|
4970
|
+
vsBrowserPlugin,
|
|
4971
|
+
buildWebPublication,
|
|
4972
|
+
cleanupWorkspace,
|
|
4973
|
+
prepareThemeDirectory,
|
|
4974
|
+
compile,
|
|
4975
|
+
copyAssets,
|
|
4976
|
+
vsDevServerPlugin,
|
|
4977
|
+
vsStaticServePlugin,
|
|
4978
|
+
vsViewerPlugin,
|
|
4979
|
+
getViewerFullUrl,
|
|
4980
|
+
createViteServer,
|
|
4981
|
+
CONTAINER_IMAGE,
|
|
4982
|
+
toContainerPath,
|
|
4983
|
+
collectVolumeArgs,
|
|
4984
|
+
runContainer,
|
|
4985
|
+
buildPDFWithContainer,
|
|
4986
|
+
isWebPubConfig,
|
|
4987
|
+
resolveTaskConfig
|
|
4988
|
+
};
|