@vivliostyle/cli 8.17.0 → 9.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/dist/browser.d.ts +12 -1
  2. package/dist/browser.d.ts.map +1 -1
  3. package/dist/browser.js +108 -13
  4. package/dist/browser.js.map +1 -1
  5. package/dist/commands/build.js +10 -44
  6. package/dist/commands/build.js.map +1 -1
  7. package/dist/commands/build.parser.d.ts +0 -5
  8. package/dist/commands/build.parser.d.ts.map +1 -1
  9. package/dist/commands/build.parser.js +15 -23
  10. package/dist/commands/build.parser.js.map +1 -1
  11. package/dist/commands/cli-flags.d.ts +42 -0
  12. package/dist/commands/cli-flags.d.ts.map +1 -0
  13. package/dist/commands/cli-flags.js +58 -0
  14. package/dist/commands/cli-flags.js.map +1 -0
  15. package/dist/commands/init.js +4 -12
  16. package/dist/commands/init.js.map +1 -1
  17. package/dist/commands/preview.js +4 -37
  18. package/dist/commands/preview.js.map +1 -1
  19. package/dist/commands/preview.parser.d.ts.map +1 -1
  20. package/dist/commands/preview.parser.js +6 -4
  21. package/dist/commands/preview.parser.js.map +1 -1
  22. package/dist/config/load.d.ts +7 -0
  23. package/dist/config/load.d.ts.map +1 -0
  24. package/dist/config/load.js +72 -0
  25. package/dist/config/load.js.map +1 -0
  26. package/dist/config/merge.d.ts +4 -0
  27. package/dist/config/merge.d.ts.map +1 -0
  28. package/dist/config/merge.js +78 -0
  29. package/dist/config/merge.js.map +1 -0
  30. package/dist/{input/config.d.ts → config/resolve.d.ts} +81 -82
  31. package/dist/config/resolve.d.ts.map +1 -0
  32. package/dist/config/resolve.js +727 -0
  33. package/dist/config/resolve.js.map +1 -0
  34. package/dist/{input → config}/schema.d.ts +6023 -3542
  35. package/dist/config/schema.d.ts.map +1 -0
  36. package/dist/config/schema.js +562 -0
  37. package/dist/config/schema.js.map +1 -0
  38. package/dist/config/vite.d.ts +53 -0
  39. package/dist/config/vite.d.ts.map +1 -0
  40. package/dist/config/vite.js +19 -0
  41. package/dist/config/vite.js.map +1 -0
  42. package/dist/const.d.ts +2 -0
  43. package/dist/const.d.ts.map +1 -1
  44. package/dist/const.js +2 -0
  45. package/dist/const.js.map +1 -1
  46. package/dist/container.d.ts +12 -3
  47. package/dist/container.d.ts.map +1 -1
  48. package/dist/container.js +130 -30
  49. package/dist/container.js.map +1 -1
  50. package/dist/core/build.d.ts +3 -0
  51. package/dist/core/build.d.ts.map +1 -0
  52. package/dist/core/build.js +168 -0
  53. package/dist/core/build.js.map +1 -0
  54. package/dist/core/init.d.ts +3 -0
  55. package/dist/core/init.d.ts.map +1 -0
  56. package/dist/{init.js → core/init.js} +14 -19
  57. package/dist/core/init.js.map +1 -0
  58. package/dist/core/preview.d.ts +3 -0
  59. package/dist/core/preview.d.ts.map +1 -0
  60. package/dist/core/preview.js +93 -0
  61. package/dist/core/preview.js.map +1 -0
  62. package/dist/index.d.ts +32 -4
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +44 -3
  65. package/dist/index.js.map +1 -1
  66. package/dist/logger.d.ts +23 -0
  67. package/dist/logger.d.ts.map +1 -0
  68. package/dist/logger.js +161 -0
  69. package/dist/logger.js.map +1 -0
  70. package/dist/output/epub.d.ts.map +1 -1
  71. package/dist/output/epub.js +17 -15
  72. package/dist/output/epub.js.map +1 -1
  73. package/dist/output/pdf-postprocess.d.ts +2 -3
  74. package/dist/output/pdf-postprocess.d.ts.map +1 -1
  75. package/dist/output/pdf-postprocess.js +94 -29
  76. package/dist/output/pdf-postprocess.js.map +1 -1
  77. package/dist/output/pdf.d.ts +4 -7
  78. package/dist/output/pdf.d.ts.map +1 -1
  79. package/dist/output/pdf.js +77 -141
  80. package/dist/output/pdf.js.map +1 -1
  81. package/dist/output/webbook.d.ts +10 -10
  82. package/dist/output/webbook.d.ts.map +1 -1
  83. package/dist/output/webbook.js +44 -62
  84. package/dist/output/webbook.js.map +1 -1
  85. package/dist/processor/compile.d.ts +24 -10
  86. package/dist/processor/compile.d.ts.map +1 -1
  87. package/dist/processor/compile.js +195 -130
  88. package/dist/processor/compile.js.map +1 -1
  89. package/dist/processor/html.d.ts +29 -17
  90. package/dist/processor/html.d.ts.map +1 -1
  91. package/dist/processor/html.js +112 -88
  92. package/dist/processor/html.js.map +1 -1
  93. package/dist/processor/markdown.d.ts +1 -1
  94. package/dist/processor/markdown.d.ts.map +1 -1
  95. package/dist/processor/theme.d.ts +3 -3
  96. package/dist/processor/theme.d.ts.map +1 -1
  97. package/dist/processor/theme.js +29 -53
  98. package/dist/processor/theme.js.map +1 -1
  99. package/dist/server.d.ts +17 -38
  100. package/dist/server.d.ts.map +1 -1
  101. package/dist/server.js +76 -102
  102. package/dist/server.js.map +1 -1
  103. package/dist/util.d.ts +9 -29
  104. package/dist/util.d.ts.map +1 -1
  105. package/dist/util.js +54 -158
  106. package/dist/util.js.map +1 -1
  107. package/dist/vite/plugin-util.d.ts +6 -0
  108. package/dist/vite/plugin-util.d.ts.map +1 -0
  109. package/dist/vite/plugin-util.js +20 -0
  110. package/dist/vite/plugin-util.js.map +1 -0
  111. package/dist/vite/vite-plugin-browser.d.ts +8 -0
  112. package/dist/vite/vite-plugin-browser.d.ts.map +1 -0
  113. package/dist/vite/vite-plugin-browser.js +57 -0
  114. package/dist/vite/vite-plugin-browser.js.map +1 -0
  115. package/dist/vite/vite-plugin-dev-server.d.ts +8 -0
  116. package/dist/vite/vite-plugin-dev-server.d.ts.map +1 -0
  117. package/dist/vite/vite-plugin-dev-server.js +267 -0
  118. package/dist/vite/vite-plugin-dev-server.js.map +1 -0
  119. package/dist/vite/vite-plugin-static-serve.d.ts +8 -0
  120. package/dist/vite/vite-plugin-static-serve.d.ts.map +1 -0
  121. package/dist/vite/vite-plugin-static-serve.js +24 -0
  122. package/dist/vite/vite-plugin-static-serve.js.map +1 -0
  123. package/dist/vite/vite-plugin-viewer.d.ts +8 -0
  124. package/dist/vite/vite-plugin-viewer.d.ts.map +1 -0
  125. package/dist/vite/vite-plugin-viewer.js +52 -0
  126. package/dist/vite/vite-plugin-viewer.js.map +1 -0
  127. package/dist/vite-adapter.d.ts +3 -0
  128. package/dist/vite-adapter.d.ts.map +1 -0
  129. package/dist/vite-adapter.js +27 -0
  130. package/dist/vite-adapter.js.map +1 -0
  131. package/package.json +13 -15
  132. package/dist/build.d.ts +0 -21
  133. package/dist/build.d.ts.map +0 -1
  134. package/dist/build.js +0 -119
  135. package/dist/build.js.map +0 -1
  136. package/dist/init.d.ts +0 -16
  137. package/dist/init.d.ts.map +0 -1
  138. package/dist/init.js.map +0 -1
  139. package/dist/input/config.d.ts.map +0 -1
  140. package/dist/input/config.js +0 -807
  141. package/dist/input/config.js.map +0 -1
  142. package/dist/input/input-types.d.ts +0 -34
  143. package/dist/input/input-types.d.ts.map +0 -1
  144. package/dist/input/input-types.js +0 -34
  145. package/dist/input/input-types.js.map +0 -1
  146. package/dist/input/schema.d.ts.map +0 -1
  147. package/dist/input/schema.js +0 -233
  148. package/dist/input/schema.js.map +0 -1
  149. package/dist/output/output-types.d.ts +0 -26
  150. package/dist/output/output-types.d.ts.map +0 -1
  151. package/dist/output/output-types.js +0 -23
  152. package/dist/output/output-types.js.map +0 -1
  153. package/dist/preview.d.ts +0 -11
  154. package/dist/preview.d.ts.map +0 -1
  155. package/dist/preview.js +0 -191
  156. package/dist/preview.js.map +0 -1
@@ -1,807 +0,0 @@
1
- import { VFM } from '@vivliostyle/vfm';
2
- import chalk from 'chalk';
3
- import * as cheerio from 'cheerio';
4
- import fs from 'fs';
5
- import { createRequire } from 'node:module';
6
- import upath from 'upath';
7
- import { pathToFileURL } from 'url';
8
- import * as v from 'valibot';
9
- import { getExecutableBrowserPath } from '../browser.js';
10
- import { COVER_HTML_FILENAME, COVER_HTML_IMAGE_ALT, EPUB_OUTPUT_VERSION, MANIFEST_FILENAME, TOC_FILENAME, TOC_TITLE, } from '../const.js';
11
- import { CONTAINER_IMAGE } from '../container.js';
12
- import { detectInputFormat, detectManuscriptMediaType, } from '../input/input-types.js';
13
- import { checkOutputFormat, checkPreflightMode, checkRenderMode, detectOutputFormat, } from '../output/output-types.js';
14
- import { readMarkdownMetadata } from '../processor/markdown.js';
15
- import { parsePackageName } from '../processor/theme.js';
16
- import { DetailError, cwd, debug, isUrlString, log, logWarn, openEpubToTmpDirectory, parseJsonc, pathEquals, prettifySchemaError, readJSON, statFileSync, touchTmpFile, } from '../util.js';
17
- import { VivliostyleConfigSchema, } from './schema.js';
18
- const DEFAULT_TIMEOUT = 2 * 60 * 1000; // 2 minutes
19
- const DEFAULT_ASSET_EXTENSIONS = [
20
- 'png',
21
- 'jpg',
22
- 'jpeg',
23
- 'svg',
24
- 'gif',
25
- 'webp',
26
- 'apng',
27
- 'ttf',
28
- 'otf',
29
- 'woff',
30
- 'woff2',
31
- ];
32
- const require = createRequire(import.meta.url);
33
- export function validateTimeoutFlag(val) {
34
- return Number.isFinite(+val) && +val > 0 ? +val * 1000 : DEFAULT_TIMEOUT;
35
- }
36
- export function contextResolve(context, loc) {
37
- return loc && upath.resolve(context, loc);
38
- }
39
- function normalizeEntry(e) {
40
- if (typeof e === 'object') {
41
- return e;
42
- }
43
- return { path: e };
44
- }
45
- // parse theme locator
46
- export function parseTheme({ theme, context, workspaceDir, themesDir, }) {
47
- const { specifier, import: importPath } = typeof theme === 'string' ? { specifier: theme, import: undefined } : theme;
48
- // url
49
- if (isUrlString(specifier)) {
50
- return {
51
- type: 'uri',
52
- name: upath.basename(specifier),
53
- location: specifier,
54
- };
55
- }
56
- // bare .css file
57
- const stylePath = upath.resolve(context, specifier);
58
- if (fs.existsSync(stylePath) && stylePath.endsWith('.css')) {
59
- const sourceRelPath = upath.relative(context, stylePath);
60
- return {
61
- type: 'file',
62
- name: upath.basename(specifier),
63
- source: stylePath,
64
- location: upath.resolve(workspaceDir, sourceRelPath),
65
- };
66
- }
67
- // node_modules, local pkg
68
- const parsed = parsePackageName(specifier, context);
69
- if (!parsed) {
70
- throw new Error(`Invalid package name: ${specifier}`);
71
- }
72
- // To security reason, Vivliostyle CLI disallow other than npm registry or local file as download source
73
- // TODO: Add option that user can allow an unofficial registry explicitly
74
- if (!parsed.registry && parsed.type !== 'directory') {
75
- throw new Error(`This package specifier is not allowed: ${specifier}`);
76
- }
77
- let name = parsed.name;
78
- let resolvedSpecifier = specifier;
79
- if (parsed.type === 'directory' && parsed.fetchSpec) {
80
- const pkgJsonPath = upath.join(parsed.fetchSpec, 'package.json');
81
- if (fs.existsSync(pkgJsonPath)) {
82
- const packageJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
83
- name = packageJson.name;
84
- resolvedSpecifier = parsed.fetchSpec;
85
- }
86
- }
87
- if (!name) {
88
- throw new Error(`Could not determine the package name: ${specifier}`);
89
- }
90
- return {
91
- type: 'package',
92
- name,
93
- specifier: resolvedSpecifier,
94
- location: upath.join(themesDir, 'packages', name),
95
- importPath,
96
- };
97
- }
98
- function parsePageSize(size) {
99
- const [width, height, ...others] = `${size}`.split(',');
100
- if (!width || others.length) {
101
- throw new Error(`Cannot parse size: ${size}`);
102
- }
103
- else if (width && height) {
104
- return {
105
- width,
106
- height,
107
- };
108
- }
109
- else {
110
- return {
111
- format: width,
112
- };
113
- }
114
- }
115
- function parseFileMetadata({ type, sourcePath, workspaceDir, themesDir, }) {
116
- const sourceDir = upath.dirname(sourcePath);
117
- let title;
118
- let themes;
119
- if (type === 'text/markdown') {
120
- const metadata = readMarkdownMetadata(sourcePath);
121
- title = metadata.title;
122
- if (metadata.vfm?.theme && themesDir) {
123
- themes = [metadata.vfm.theme]
124
- .flat()
125
- .filter((entry) => !!entry && (typeof entry === 'string' || typeof entry === 'object'))
126
- .map((theme) => parseTheme({
127
- theme,
128
- context: sourceDir,
129
- workspaceDir,
130
- themesDir,
131
- }));
132
- }
133
- }
134
- else {
135
- const $ = cheerio.load(fs.readFileSync(sourcePath, 'utf8'));
136
- title = $('title')?.text() || undefined;
137
- }
138
- return { title, themes };
139
- }
140
- export async function collectVivliostyleConfig(cliFlags) {
141
- const load = async (configPath) => {
142
- let config;
143
- let jsonRaw;
144
- try {
145
- if (upath.extname(configPath) === '.json') {
146
- jsonRaw = fs.readFileSync(configPath, 'utf8');
147
- config = parseJsonc(jsonRaw);
148
- }
149
- else {
150
- // Clear require cache to reload CJS config files
151
- delete require.cache[require.resolve(configPath)];
152
- const url = pathToFileURL(configPath);
153
- // Invalidate cache for ESM config files
154
- // https://github.com/nodejs/node/issues/49442
155
- url.search = `version=${Date.now()}`;
156
- config = (await import(url.href)).default;
157
- jsonRaw = JSON.stringify(config, null, 2);
158
- }
159
- }
160
- catch (error) {
161
- const thrownError = error;
162
- throw new DetailError(`An error occurred on loading a config file: ${configPath}`, thrownError.stack ?? thrownError.message);
163
- }
164
- const result = v.safeParse(VivliostyleConfigSchema, config);
165
- if (result.success) {
166
- return result.output;
167
- }
168
- else {
169
- const errorString = prettifySchemaError(jsonRaw, result.issues);
170
- throw new DetailError(`Validation of vivliostyle config failed. Please check the schema: ${configPath}`, errorString);
171
- }
172
- };
173
- let configEntry = {};
174
- let vivliostyleConfigPath;
175
- if (cliFlags.configPath) {
176
- vivliostyleConfigPath = upath.resolve(cwd, cliFlags.configPath);
177
- }
178
- else {
179
- vivliostyleConfigPath = ['.js', '.mjs', '.cjs']
180
- .map((ext) => upath.join(cwd, `vivliostyle.config${ext}`))
181
- .find((p) => fs.existsSync(p));
182
- }
183
- // let vivliostyleConfig: VivliostyleConfigSchema | undefined;
184
- if (vivliostyleConfigPath) {
185
- configEntry = {
186
- vivliostyleConfigPath,
187
- vivliostyleConfig: [await load(vivliostyleConfigPath)].flat(),
188
- };
189
- }
190
- else if (cliFlags.input &&
191
- upath.basename(cliFlags.input).startsWith('vivliostyle.config')) {
192
- // Load an input argument as a Vivliostyle config
193
- try {
194
- const inputPath = upath.resolve(cwd, cliFlags.input);
195
- const inputConfig = await load(inputPath);
196
- cliFlags = {
197
- ...cliFlags,
198
- input: undefined,
199
- };
200
- configEntry = {
201
- vivliostyleConfigPath: inputPath,
202
- vivliostyleConfig: [inputConfig].flat(),
203
- };
204
- }
205
- catch (_err) { }
206
- }
207
- if (cliFlags.executableChromium) {
208
- logWarn(chalk.yellowBright("'--executable-chromium' option was deprecated and will be removed in a future release. Please replace with '--executable-browser' option."));
209
- cliFlags.executableBrowser = cliFlags.executableChromium;
210
- }
211
- if (cliFlags.verbose) {
212
- logWarn(chalk.yellowBright("'--verbose' option was deprecated and will be removed in a future release. Please replace with '--log-level verbose' option."));
213
- }
214
- if (cliFlags.sandbox === false) {
215
- logWarn(chalk.yellowBright("'--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."));
216
- }
217
- const configEntries = (configEntry.vivliostyleConfig ?? []).flat();
218
- if (configEntries.some((config) => config.includeAssets)) {
219
- logWarn(chalk.yellowBright("'includeAssets' property of Vivliostyle config was deprecated and will be removed in a future release. Please use 'copyAsset.includes' property instead."));
220
- }
221
- if (configEntries.some((config) => config.tocTitle)) {
222
- logWarn(chalk.yellowBright("'tocTitle' property of Vivliostyle config was deprecated and will be removed in a future release. Please use 'toc.title' property instead."));
223
- }
224
- return {
225
- cliFlags,
226
- ...configEntry,
227
- };
228
- }
229
- export async function mergeConfig(cliFlags, config, context, prevConfig) {
230
- debug('context directory', context);
231
- debug('cliFlags', cliFlags);
232
- debug('vivliostyle.config.js', config);
233
- let entryContextDir;
234
- let workspaceDir;
235
- if (cliFlags.input && !config && isUrlString(cliFlags.input)) {
236
- workspaceDir = entryContextDir = cwd;
237
- }
238
- else {
239
- entryContextDir = upath.resolve(cliFlags.input && !config
240
- ? upath.dirname(upath.resolve(context, cliFlags.input))
241
- : (contextResolve(context, config?.entryContext) ?? context));
242
- workspaceDir =
243
- contextResolve(context, config?.workspaceDir) ?? entryContextDir;
244
- }
245
- const themesDir = upath.join(workspaceDir, 'themes');
246
- const language = cliFlags.language ?? config?.language ?? undefined;
247
- const readingProgression = cliFlags.readingProgression ?? config?.readingProgression ?? undefined;
248
- const sizeFlag = cliFlags.size ?? config?.size;
249
- const size = sizeFlag ? parsePageSize(sizeFlag) : undefined;
250
- const cropMarks = cliFlags.cropMarks ?? false;
251
- const bleed = cliFlags.bleed;
252
- const cropOffset = cliFlags.cropOffset;
253
- const css = cliFlags.css;
254
- const customStyle = cliFlags.style &&
255
- (isUrlString(cliFlags.style)
256
- ? cliFlags.style
257
- : pathToFileURL(cliFlags.style).href);
258
- const customUserStyle = cliFlags.userStyle &&
259
- (isUrlString(cliFlags.userStyle)
260
- ? cliFlags.userStyle
261
- : pathToFileURL(cliFlags.userStyle).href);
262
- const singleDoc = cliFlags.singleDoc ?? false;
263
- const quick = cliFlags.quick ?? false;
264
- const pressReady = cliFlags.pressReady ?? config?.pressReady ?? false;
265
- const renderMode = cliFlags.renderMode ?? 'local';
266
- const preflight = cliFlags.preflight ?? (pressReady ? 'press-ready' : null);
267
- const preflightOption = cliFlags.preflightOption ?? [];
268
- const documentProcessorFactory = config?.documentProcessor ?? VFM;
269
- const vfmOptions = {
270
- ...config?.vfm,
271
- hardLineBreaks: config?.vfm?.hardLineBreaks ?? false,
272
- disableFormatHtml: config?.vfm?.disableFormatHtml ?? false,
273
- };
274
- const timeout = cliFlags.timeout ?? config?.timeout ?? DEFAULT_TIMEOUT;
275
- const sandbox = cliFlags.sandbox ?? false;
276
- const browserType = cliFlags.browser ?? config?.browser ?? 'chromium';
277
- const proxyServer = cliFlags.proxyServer ?? process.env.HTTP_PROXY ?? undefined;
278
- const proxy = proxyServer
279
- ? {
280
- server: proxyServer,
281
- bypass: cliFlags.proxyBypass ?? process.env.NOPROXY ?? undefined,
282
- username: cliFlags.proxyUser,
283
- password: cliFlags.proxyPass,
284
- }
285
- : undefined;
286
- const executableBrowser = cliFlags.executableBrowser ?? getExecutableBrowserPath(browserType);
287
- const image = cliFlags.image ?? config?.image ?? CONTAINER_IMAGE;
288
- const httpServer = cliFlags.http ?? config?.http ?? false;
289
- const viewer = cliFlags.viewer ?? config?.viewer ?? undefined;
290
- const viewerParam = cliFlags.viewerParam ?? config?.viewerParam ?? undefined;
291
- const logLevel = cliFlags.logLevel ??
292
- ((cliFlags.verbose && 'verbose') || undefined) ??
293
- 'silent';
294
- const ignoreHttpsErrors = cliFlags.ignoreHttpsErrors ?? false;
295
- const rootThemes = cliFlags.theme
296
- ? [
297
- parseTheme({
298
- theme: cliFlags.theme,
299
- context: cwd,
300
- workspaceDir,
301
- themesDir,
302
- }),
303
- ]
304
- : config?.theme
305
- ? [config.theme].flat().map((theme) => parseTheme({
306
- theme,
307
- context,
308
- workspaceDir,
309
- themesDir,
310
- }))
311
- : [];
312
- const themeIndexes = new Set(rootThemes);
313
- const outputs = (() => {
314
- if (cliFlags.targets?.length) {
315
- return cliFlags.targets.map(({ path: outputPath, format }) => {
316
- if (format === 'pdf') {
317
- return {
318
- path: upath.resolve(outputPath),
319
- format,
320
- renderMode,
321
- preflight,
322
- preflightOption,
323
- };
324
- }
325
- else if (format === 'epub') {
326
- return {
327
- path: upath.resolve(outputPath),
328
- format,
329
- version: EPUB_OUTPUT_VERSION,
330
- };
331
- }
332
- else {
333
- return {
334
- path: upath.resolve(outputPath),
335
- format,
336
- };
337
- }
338
- });
339
- }
340
- if (config?.output) {
341
- return (Array.isArray(config.output) ? config.output : [config.output]).map((target) => {
342
- const targetObj = typeof target === 'string' ? { path: target } : target;
343
- const outputPath = upath.resolve(context, targetObj.path);
344
- const format = targetObj.format ?? detectOutputFormat(outputPath);
345
- if (!checkOutputFormat(format)) {
346
- throw new Error(`Unknown format: ${format}`);
347
- }
348
- if (format === 'pdf') {
349
- const outputRenderMode = targetObj.renderMode ?? renderMode;
350
- const outputPreflight = targetObj.preflight ?? preflight;
351
- if (!checkRenderMode(outputRenderMode)) {
352
- throw new Error(`Unknown renderMode: ${outputRenderMode}`);
353
- }
354
- if (outputPreflight !== null &&
355
- !checkPreflightMode(outputPreflight)) {
356
- throw new Error(`Unknown preflight: ${outputPreflight}`);
357
- }
358
- return {
359
- ...targetObj,
360
- path: outputPath,
361
- format,
362
- renderMode: outputRenderMode,
363
- preflight: outputPreflight,
364
- preflightOption: targetObj.preflightOption ?? preflightOption,
365
- };
366
- }
367
- else if (format === 'epub') {
368
- return {
369
- ...targetObj,
370
- path: outputPath,
371
- format,
372
- version: EPUB_OUTPUT_VERSION,
373
- };
374
- }
375
- else {
376
- return { ...targetObj, path: outputPath, format };
377
- }
378
- });
379
- }
380
- // Outputs a pdf file if any output configuration is not set
381
- const filename = config?.title ? `${config.title}.pdf` : 'output.pdf';
382
- return [
383
- {
384
- path: upath.resolve(context, filename),
385
- format: 'pdf',
386
- renderMode,
387
- preflight,
388
- preflightOption,
389
- },
390
- ];
391
- })();
392
- const cover = (() => {
393
- if (!config?.cover) {
394
- return undefined;
395
- }
396
- const obj = typeof config.cover === 'string' ? { src: config.cover } : config.cover;
397
- if (!('htmlPath' in obj)) {
398
- obj.htmlPath = COVER_HTML_FILENAME;
399
- }
400
- return {
401
- src: upath.resolve(entryContextDir, obj.src),
402
- name: obj.name || COVER_HTML_IMAGE_ALT,
403
- htmlPath: (obj.htmlPath && upath.resolve(workspaceDir, obj.htmlPath)) ||
404
- undefined,
405
- };
406
- })();
407
- const copyAsset = (() => {
408
- const { includes: _includes, excludes = [], includeFileExtensions = [], excludeFileExtensions = [], } = config?.copyAsset || {};
409
- const includes = _includes || [config?.includeAssets ?? []].flat();
410
- const notAllowedPatternRe = /(^\s*[/\\]|^(.*[/\\])?\s*\.\.\s*([/\\].*)?$)/g;
411
- // See the special characters of glob pattern
412
- // https://github.com/micromatch/picomatch
413
- const notAllowedExtensionRe = /([\\/*?@+!|(){}[\]]|\.\.|^\s*\.)/g;
414
- Object.entries({ includes, excludes }).forEach(([propName, patterns]) => {
415
- patterns.forEach((pattern) => {
416
- if (notAllowedPatternRe.test(pattern)) {
417
- throw new Error(`Invalid pattern was found in copyAsset.${propName} option: ${pattern}`);
418
- }
419
- });
420
- });
421
- Object.entries({ includeFileExtensions, excludeFileExtensions }).forEach(([propName, patterns]) => {
422
- patterns.forEach((pattern) => {
423
- if (notAllowedExtensionRe.test(pattern)) {
424
- throw new Error(`Invalid pattern was found in copyAsset.${propName} option: ${pattern}`);
425
- }
426
- });
427
- });
428
- return {
429
- includes,
430
- excludes,
431
- fileExtensions: [
432
- ...new Set([...DEFAULT_ASSET_EXTENSIONS, ...includeFileExtensions]),
433
- ].filter((ext) => !excludeFileExtensions.includes(ext)),
434
- };
435
- })();
436
- const commonOpts = {
437
- entryContextDir,
438
- workspaceDir,
439
- themesDir,
440
- copyAsset,
441
- outputs,
442
- themeIndexes,
443
- rootThemes,
444
- size,
445
- cropMarks,
446
- bleed,
447
- cropOffset,
448
- css,
449
- customStyle,
450
- customUserStyle,
451
- singleDoc,
452
- quick,
453
- language,
454
- readingProgression,
455
- documentProcessorFactory,
456
- vfmOptions,
457
- cover,
458
- timeout,
459
- sandbox,
460
- executableBrowser,
461
- browserType,
462
- proxy,
463
- image,
464
- httpServer,
465
- viewer,
466
- viewerParam,
467
- logLevel,
468
- ignoreHttpsErrors,
469
- };
470
- if (!cliFlags.input && !config) {
471
- throw new Error('No input is set. Please set an appropriate entry or a Vivliostyle config file.');
472
- }
473
- const parsedConfig = cliFlags.input
474
- ? await composeSingleInputConfig(commonOpts, cliFlags, config, prevConfig)
475
- : await composeProjectConfig(commonOpts, cliFlags, config, context, prevConfig);
476
- debug('parsedConfig', JSON.stringify(parsedConfig, null, 2));
477
- return parsedConfig;
478
- }
479
- async function composeSingleInputConfig(otherConfig, cliFlags, config, prevConfig) {
480
- debug('entering single entry config mode');
481
- let sourcePath;
482
- let input;
483
- const title = cliFlags.title ?? config?.title;
484
- const author = cliFlags.author ?? config?.author;
485
- const workspaceDir = otherConfig.workspaceDir;
486
- const entries = [];
487
- const exportAliases = [];
488
- const tmpPrefix = `.vs-${Date.now()}.`;
489
- if (cliFlags.input && isUrlString(cliFlags.input)) {
490
- sourcePath = cliFlags.input;
491
- input = { format: 'webbook', entry: sourcePath };
492
- }
493
- else {
494
- sourcePath = upath.resolve(cliFlags.input);
495
- input = detectInputFormat(sourcePath);
496
- // Check file exists
497
- statFileSync(sourcePath);
498
- }
499
- if (input.format === 'markdown') {
500
- // Single input file; create temporary file
501
- const type = detectManuscriptMediaType(sourcePath);
502
- const metadata = parseFileMetadata({ type, sourcePath, workspaceDir });
503
- const relDir = upath.relative(otherConfig.entryContextDir, upath.dirname(sourcePath));
504
- let target;
505
- if (prevConfig) {
506
- const prevEntry = prevConfig.entries.find((e) => e.source === sourcePath);
507
- if (!prevEntry) {
508
- throw new Error('Failed to reload config');
509
- }
510
- target = prevEntry.target;
511
- }
512
- else {
513
- target = upath
514
- .resolve(workspaceDir, relDir, `${tmpPrefix}${upath.basename(sourcePath)}`)
515
- .replace(/\.md$/, '.html');
516
- await touchTmpFile(target);
517
- }
518
- const themes = metadata.themes ?? [...otherConfig.rootThemes];
519
- themes.forEach((t) => otherConfig.themeIndexes.add(t));
520
- entries.push({
521
- type,
522
- source: sourcePath,
523
- target,
524
- title: metadata.title,
525
- themes,
526
- });
527
- exportAliases.push({
528
- source: target,
529
- target: upath.resolve(upath.dirname(target), upath.basename(sourcePath).replace(/\.md$/, '.html')),
530
- });
531
- }
532
- let fallbackTitle;
533
- const manifestDeclaration = await (async () => {
534
- if (input.format === 'markdown') {
535
- // create temporary manifest file
536
- const manifestPath = upath.resolve(workspaceDir, `${tmpPrefix}${MANIFEST_FILENAME}`);
537
- await touchTmpFile(manifestPath);
538
- exportAliases.push({
539
- source: manifestPath,
540
- target: upath.resolve(workspaceDir, MANIFEST_FILENAME),
541
- });
542
- fallbackTitle =
543
- entries.length === 1 && entries[0].title
544
- ? entries[0].title
545
- : upath.basename(sourcePath);
546
- return { manifestPath, needToGenerateManifest: true };
547
- }
548
- else if (input.format === 'html' || input.format === 'webbook') {
549
- const url = isUrlString(input.entry)
550
- ? new URL(input.entry)
551
- : pathToFileURL(input.entry);
552
- // Ensures trailing slash or explicit HTML extensions
553
- if ((url.protocol === 'http:' || url.protocol === 'https:') &&
554
- !url.pathname.endsWith('/') &&
555
- !/\.html?$/.test(url.pathname)) {
556
- url.pathname = `${url.pathname}/`;
557
- }
558
- return {
559
- webbookEntryUrl: url.href,
560
- };
561
- }
562
- else if (input.format === 'pub-manifest') {
563
- return { manifestPath: input.entry };
564
- }
565
- else if (input.format === 'epub-opf') {
566
- return { epubOpfPath: input.entry };
567
- }
568
- else if (input.format === 'epub') {
569
- const { epubOpfPath } = await openEpubToTmpDirectory(input.entry);
570
- return { epubOpfPath };
571
- }
572
- else {
573
- throw new Error('Failed to export manifest declaration');
574
- }
575
- })();
576
- return {
577
- ...otherConfig,
578
- ...manifestDeclaration,
579
- entries,
580
- input,
581
- exportAliases,
582
- title: title || fallbackTitle,
583
- author: author,
584
- };
585
- }
586
- async function composeProjectConfig(otherConfig, cliFlags, config, context, prevConfig) {
587
- debug('entering project config mode');
588
- const { entryContextDir, workspaceDir, themesDir, themeIndexes, rootThemes, outputs, cover, } = otherConfig;
589
- const pkgJsonPath = upath.resolve(entryContextDir, 'package.json');
590
- const pkgJson = fs.existsSync(pkgJsonPath)
591
- ? readJSON(pkgJsonPath)
592
- : undefined;
593
- if (pkgJson) {
594
- debug('located package.json path', pkgJsonPath);
595
- }
596
- const exportAliases = [];
597
- const tmpPrefix = `.vs-${Date.now()}.`;
598
- const tocConfig = (() => {
599
- const c = typeof config?.toc === 'object'
600
- ? config.toc
601
- : typeof config?.toc === 'string'
602
- ? { htmlPath: config.toc }
603
- : {};
604
- return {
605
- tocTitle: c.title ?? config?.tocTitle ?? TOC_TITLE,
606
- target: upath.resolve(workspaceDir, c.htmlPath ?? TOC_FILENAME),
607
- sectionDepth: c.sectionDepth ?? 0,
608
- transform: {
609
- transformDocumentList: c.transformDocumentList,
610
- transformSectionList: c.transformSectionList,
611
- },
612
- };
613
- })();
614
- const ensureCoverImage = (src) => {
615
- const absPath = src && upath.resolve(entryContextDir, src);
616
- if (absPath) {
617
- statFileSync(absPath, {
618
- errorMessage: 'Specified cover image does not exist',
619
- });
620
- }
621
- return absPath;
622
- };
623
- const projectTitle = cliFlags.title ?? config?.title ?? pkgJson?.name;
624
- const projectAuthor = cliFlags.author ?? config?.author ?? pkgJson?.author;
625
- const isContentsEntry = (entry) => entry.rel === 'contents';
626
- const isCoverEntry = (entry) => entry.rel === 'cover';
627
- const isArticleEntry = (entry) => !isContentsEntry(entry) && !isCoverEntry(entry);
628
- async function parseEntry(entry) {
629
- const getInputInfo = (entryPath) => {
630
- const source = upath.resolve(entryContextDir, entryPath);
631
- if (!isUrlString(source)) {
632
- statFileSync(source);
633
- }
634
- const type = detectManuscriptMediaType(source);
635
- return {
636
- ...parseFileMetadata({
637
- type,
638
- sourcePath: source,
639
- workspaceDir,
640
- themesDir,
641
- }),
642
- source,
643
- type,
644
- };
645
- };
646
- const getTargetPath = (source) => upath.resolve(workspaceDir, upath.relative(entryContextDir, source).replace(/\.md$/, '.html'));
647
- if ((isContentsEntry(entry) || isCoverEntry(entry)) && entry.path) {
648
- const source = upath.resolve(entryContextDir, entry.path);
649
- try {
650
- statFileSync(source);
651
- /* v8 ignore next 10 */
652
- }
653
- catch (error) {
654
- // For backward compatibility, we allow missing files then assume that option as `output` field.
655
- logWarn(chalk.yellowBright(`The "path" option is set but the file does not exist: ${source}\nMaybe you want to set the "output" field instead.`));
656
- entry.output = entry.path;
657
- entry.path = undefined;
658
- }
659
- }
660
- if (isContentsEntry(entry)) {
661
- const inputInfo = entry.path ? getInputInfo(entry.path) : undefined;
662
- let target = entry.output
663
- ? upath.resolve(workspaceDir, entry.output)
664
- : inputInfo?.source && getTargetPath(inputInfo.source);
665
- const themes = entry.theme
666
- ? [entry.theme].flat().map((theme) => parseTheme({
667
- theme,
668
- context,
669
- workspaceDir,
670
- themesDir,
671
- }))
672
- : (inputInfo?.themes ?? [...rootThemes]);
673
- themes.forEach((t) => themeIndexes.add(t));
674
- target ??= tocConfig.target;
675
- if (inputInfo?.source && pathEquals(inputInfo.source, target)) {
676
- const tmpPath = upath.resolve(upath.dirname(target), `${tmpPrefix}${upath.basename(target)}`);
677
- exportAliases.push({ source: tmpPath, target });
678
- await touchTmpFile(tmpPath);
679
- target = tmpPath;
680
- }
681
- const parsedEntry = {
682
- rel: 'contents',
683
- ...tocConfig,
684
- target,
685
- title: entry.title ?? inputInfo?.title ?? projectTitle,
686
- themes,
687
- pageBreakBefore: entry.pageBreakBefore,
688
- pageCounterReset: entry.pageCounterReset,
689
- ...(inputInfo && {
690
- template: { source: inputInfo.source, type: inputInfo.type },
691
- }),
692
- };
693
- return parsedEntry;
694
- }
695
- if (isCoverEntry(entry)) {
696
- const inputInfo = entry.path ? getInputInfo(entry.path) : undefined;
697
- let target = entry.output
698
- ? upath.resolve(workspaceDir, entry.output)
699
- : inputInfo?.source && getTargetPath(inputInfo.source);
700
- const themes = entry.theme
701
- ? [entry.theme].flat().map((theme) => parseTheme({
702
- theme,
703
- context,
704
- workspaceDir,
705
- themesDir,
706
- }))
707
- : (inputInfo?.themes ?? []); // Don't inherit rootThemes for cover documents
708
- themes.forEach((t) => themeIndexes.add(t));
709
- const coverImageSrc = ensureCoverImage(entry.imageSrc || cover?.src);
710
- if (!coverImageSrc) {
711
- throw new Error(`A CoverEntryObject is set in the entry list but a location of cover file is not set. Please set 'cover' property in your config file.`);
712
- }
713
- target ??= upath.resolve(workspaceDir, entry.path || cover?.htmlPath || COVER_HTML_FILENAME);
714
- if (inputInfo?.source && pathEquals(inputInfo.source, target)) {
715
- const tmpPath = upath.resolve(upath.dirname(target), `${tmpPrefix}${upath.basename(target)}`);
716
- exportAliases.push({ source: tmpPath, target });
717
- await touchTmpFile(tmpPath);
718
- target = tmpPath;
719
- }
720
- const parsedEntry = {
721
- rel: 'cover',
722
- target,
723
- title: entry.title ?? inputInfo?.title ?? projectTitle,
724
- themes,
725
- coverImageSrc,
726
- coverImageAlt: entry.imageAlt || cover?.name || COVER_HTML_IMAGE_ALT,
727
- pageBreakBefore: entry.pageBreakBefore,
728
- ...(inputInfo && {
729
- template: { source: inputInfo.source, type: inputInfo.type },
730
- }),
731
- };
732
- return parsedEntry;
733
- }
734
- if (isArticleEntry(entry)) {
735
- const inputInfo = getInputInfo(entry.path);
736
- const target = entry.output
737
- ? upath.resolve(workspaceDir, entry.output)
738
- : getTargetPath(inputInfo.source);
739
- const themes = entry.theme
740
- ? [entry.theme]
741
- .flat()
742
- .map((theme) => parseTheme({ theme, context, workspaceDir, themesDir }))
743
- : (inputInfo.themes ?? [...rootThemes]);
744
- themes.forEach((t) => themeIndexes.add(t));
745
- const parsedEntry = {
746
- type: inputInfo.type,
747
- source: inputInfo.source,
748
- target,
749
- title: entry.title ?? inputInfo.title ?? projectTitle,
750
- themes,
751
- ...(entry.rel && { rel: entry.rel }),
752
- };
753
- return parsedEntry;
754
- }
755
- /* v8 ignore next */
756
- throw new Error('Unknown entry type');
757
- }
758
- const entries = await Promise.all([config?.entry || []].flat().map(normalizeEntry).map(parseEntry));
759
- if (!entries.length) {
760
- throw new Error('The entry fields seems to be empty. Make sure your Vivliostyle configuration.');
761
- }
762
- let fallbackProjectTitle;
763
- if (!projectTitle) {
764
- if (entries.length === 1 && entries[0].title) {
765
- fallbackProjectTitle = entries[0].title;
766
- }
767
- else {
768
- fallbackProjectTitle = upath.basename(outputs[0].path);
769
- log(`\n${chalk.yellow('Could not find any appropriate publication title. We set ')}${chalk.bold.yellow(`"${fallbackProjectTitle}"`)}${chalk.yellow(' as a fallback.')}`);
770
- }
771
- }
772
- if (!!config?.toc && !entries.find(({ rel }) => rel === 'contents')) {
773
- entries.unshift({
774
- rel: 'contents',
775
- ...tocConfig,
776
- themes: [...rootThemes],
777
- });
778
- }
779
- if (cover?.htmlPath && !entries.find(({ rel }) => rel === 'cover')) {
780
- entries.unshift({
781
- rel: 'cover',
782
- target: cover.htmlPath,
783
- title: projectTitle,
784
- themes: [], // Don't inherit rootThemes for cover documents
785
- coverImageSrc: ensureCoverImage(cover.src),
786
- coverImageAlt: cover.name,
787
- });
788
- }
789
- const coverEntires = entries.filter(({ rel }) => rel === 'cover');
790
- if (coverEntires.length !== new Set(coverEntires.map((v) => v.target)).size) {
791
- throw new Error('Multiple cover entries which has same output were found. Each cover entries must have an unique path.');
792
- }
793
- return {
794
- ...otherConfig,
795
- entries,
796
- input: {
797
- format: 'pub-manifest',
798
- entry: upath.join(workspaceDir, MANIFEST_FILENAME),
799
- },
800
- exportAliases,
801
- manifestPath: upath.join(workspaceDir, MANIFEST_FILENAME),
802
- title: projectTitle || fallbackProjectTitle,
803
- author: projectAuthor,
804
- needToGenerateManifest: true,
805
- };
806
- }
807
- //# sourceMappingURL=config.js.map