@likecoin/epubcheck-ts 0.1.0 → 0.2.1
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/README.md +65 -10
- package/bin/epubcheck.js +170 -0
- package/bin/epubcheck.ts +227 -0
- package/dist/index.cjs +1756 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +1756 -48
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -6,14 +6,21 @@ A TypeScript port of [EPUBCheck](https://github.com/w3c/epubcheck) - the officia
|
|
|
6
6
|
[](https://www.npmjs.com/package/@likecoin/epubcheck-ts)
|
|
7
7
|
[](./LICENSE)
|
|
8
8
|
|
|
9
|
+
> **Note**: This library is primarily developed for internal use at [3ook.com](https://3ook.com/about) and is built with AI-assisted development. While it has comprehensive test coverage (208 tests) and ~65% feature parity with Java EPUBCheck, it may not be suitable for mission-critical production workloads. For production environments requiring full EPUB validation, consider using the official [Java EPUBCheck](https://github.com/w3c/epubcheck). Contributions and feedback are welcome!
|
|
10
|
+
|
|
9
11
|
## Features
|
|
10
12
|
|
|
13
|
+
- **CLI and programmatic API**: Use as a command-line tool or integrate into your application
|
|
11
14
|
- **Cross-platform**: Works in Node.js (18+) and modern browsers
|
|
12
|
-
- **Partial EPUB validation**: Currently ~
|
|
15
|
+
- **Partial EPUB validation**: Currently ~65% of EPUBCheck feature parity
|
|
13
16
|
- **Zero native dependencies**: Pure JavaScript/WebAssembly, no compilation required
|
|
14
17
|
- **TypeScript first**: Full type definitions included
|
|
15
18
|
- **Tree-shakable**: ESM with proper exports for optimal bundling
|
|
16
19
|
|
|
20
|
+
## Try it Online
|
|
21
|
+
|
|
22
|
+
Try the live demo at **[likecoin.github.io/epubcheck-ts](https://likecoin.github.io/epubcheck-ts/)** - validate your EPUB files directly in the browser without uploading to any server.
|
|
23
|
+
|
|
17
24
|
## Installation
|
|
18
25
|
|
|
19
26
|
```bash
|
|
@@ -22,6 +29,49 @@ npm install @likecoin/epubcheck-ts
|
|
|
22
29
|
|
|
23
30
|
## Quick Start
|
|
24
31
|
|
|
32
|
+
### Command Line Interface (CLI)
|
|
33
|
+
|
|
34
|
+
**Quick validation:**
|
|
35
|
+
```bash
|
|
36
|
+
npx @likecoin/epubcheck-ts book.epub
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Or install globally:**
|
|
40
|
+
```bash
|
|
41
|
+
npm install -g @likecoin/epubcheck-ts
|
|
42
|
+
epubcheck-ts book.epub
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**CLI Options:**
|
|
46
|
+
```bash
|
|
47
|
+
epubcheck-ts <file.epub> [options]
|
|
48
|
+
|
|
49
|
+
Options:
|
|
50
|
+
-j, --json <file> Output JSON report to file (use '-' for stdout)
|
|
51
|
+
-q, --quiet Suppress console output (errors only)
|
|
52
|
+
-p, --profile <name> Validation profile (default|dict|edupub|idx|preview)
|
|
53
|
+
-w, --fail-on-warnings Exit with code 1 if warnings are found
|
|
54
|
+
-v, --version Show version information
|
|
55
|
+
-h, --help Show this help message
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Examples:**
|
|
59
|
+
```bash
|
|
60
|
+
# Basic validation
|
|
61
|
+
epubcheck-ts book.epub
|
|
62
|
+
|
|
63
|
+
# Generate JSON report
|
|
64
|
+
epubcheck-ts book.epub --json report.json
|
|
65
|
+
|
|
66
|
+
# Quiet mode for CI/CD
|
|
67
|
+
epubcheck-ts book.epub --quiet --fail-on-warnings
|
|
68
|
+
|
|
69
|
+
# Validate with specific profile
|
|
70
|
+
epubcheck-ts dictionary.epub --profile dict
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Note:** This CLI provides ~65% coverage of Java EPUBCheck features. For complete EPUB 3 conformance testing, use the [official Java EPUBCheck](https://github.com/w3c/epubcheck).
|
|
74
|
+
|
|
25
75
|
### ES Modules (recommended)
|
|
26
76
|
|
|
27
77
|
```typescript
|
|
@@ -214,19 +264,20 @@ This library is a TypeScript port of the Java-based [EPUBCheck](https://github.c
|
|
|
214
264
|
|
|
215
265
|
| Component | Status | Completeness | Notes |
|
|
216
266
|
|-----------|--------|--------------|-------|
|
|
217
|
-
| OCF Container | 🟡 Partial | ~
|
|
218
|
-
| Package Document (OPF) | 🟡 Partial | ~
|
|
219
|
-
| Content Documents | 🟡 Partial | ~
|
|
220
|
-
| Navigation Document | 🟡 Partial | ~
|
|
267
|
+
| OCF Container | 🟡 Partial | ~70% | ZIP structure, mimetype (uncompressed check), container.xml |
|
|
268
|
+
| Package Document (OPF) | 🟡 Partial | ~70% | Metadata, manifest, spine, collections, version/date validation |
|
|
269
|
+
| Content Documents | 🟡 Partial | ~70% | XHTML structure, script/MathML/SVG detection, link validation |
|
|
270
|
+
| Navigation Document | 🟡 Partial | ~40% | Nav structure, NCX validation, remote link validation |
|
|
221
271
|
| Schema Validation | 🟡 Partial | ~70% | RelaxNG, XSD, Schematron working |
|
|
222
|
-
| CSS |
|
|
272
|
+
| CSS | 🟡 Partial | ~50% | @font-face, @import, media overlay classes, position warnings |
|
|
273
|
+
| Cross-reference Validation | 🟡 Partial | ~75% | Reference tracking, fragment validation, undeclared resources |
|
|
274
|
+
| Accessibility Checks | 🟡 Partial | ~75% | Empty links, image alt, SVG titles, MathML alttext |
|
|
223
275
|
| Media Overlays | ❌ Not Started | 0% | Planned |
|
|
224
|
-
|
|
|
225
|
-
| Accessibility Checks | ❌ Not Started | 0% | Alt text, etc. |
|
|
276
|
+
| Media Validation | ❌ Not Started | 0% | Planned |
|
|
226
277
|
|
|
227
278
|
Legend: 🟢 Complete | 🟡 Partial | 🔴 Basic | ❌ Not Started
|
|
228
279
|
|
|
229
|
-
**Overall Progress: ~
|
|
280
|
+
**Overall Progress: ~65% of Java EPUBCheck features**
|
|
230
281
|
|
|
231
282
|
See [PROJECT_STATUS.md](./PROJECT_STATUS.md) for detailed comparison.
|
|
232
283
|
|
|
@@ -306,7 +357,7 @@ Legend: ✅ Implemented
|
|
|
306
357
|
| Aspect | epubcheck-ts | EPUBCheck (Java) |
|
|
307
358
|
|--------|--------------|------------------|
|
|
308
359
|
| Runtime | Node.js / Browser | JVM |
|
|
309
|
-
| Feature Parity | ~
|
|
360
|
+
| Feature Parity | ~65% | 100% |
|
|
310
361
|
| Bundle Size | ~55KB JS + ~1.5MB WASM | ~15MB |
|
|
311
362
|
| Installation | `npm install` | Download JAR |
|
|
312
363
|
| Integration | Native JS/TS | CLI or Java API |
|
|
@@ -332,6 +383,10 @@ This is an independent TypeScript implementation inspired by the Java-based [EPU
|
|
|
332
383
|
- [DAISY Consortium](https://daisy.org/) - Maintainers of EPUBCheck
|
|
333
384
|
- [libxml2-wasm](https://github.com/jameslan/libxml2-wasm) - WebAssembly XML processing
|
|
334
385
|
|
|
386
|
+
## Built by 3ook.com
|
|
387
|
+
|
|
388
|
+
This project is built and maintained by the [3ook.com](https://3ook.com/about) team. 3ook is a Web3 eBook platform where authors can publish EPUB ebooks and readers can collect them as digital assets. If you're an author looking to publish your ebook, check out [3ook.com](https://3ook.com/about).
|
|
389
|
+
|
|
335
390
|
## Related Projects
|
|
336
391
|
|
|
337
392
|
- [EPUBCheck](https://github.com/w3c/epubcheck) - Official Java validator
|
package/bin/epubcheck.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
4
|
+
import { basename } from "node:path";
|
|
5
|
+
const { EpubCheck, toJSONReport } = await import("../dist/index.js");
|
|
6
|
+
const VERSION = "0.2.1";
|
|
7
|
+
const { values, positionals } = parseArgs({
|
|
8
|
+
options: {
|
|
9
|
+
json: { type: "string", short: "j" },
|
|
10
|
+
quiet: { type: "boolean", short: "q", default: false },
|
|
11
|
+
profile: { type: "string", short: "p" },
|
|
12
|
+
usage: { type: "boolean", short: "u", default: false },
|
|
13
|
+
version: { type: "boolean", short: "v", default: false },
|
|
14
|
+
help: { type: "boolean", short: "h", default: false },
|
|
15
|
+
"fail-on-warnings": { type: "boolean", short: "w", default: false }
|
|
16
|
+
},
|
|
17
|
+
allowPositionals: true,
|
|
18
|
+
strict: false
|
|
19
|
+
});
|
|
20
|
+
if (values.version) {
|
|
21
|
+
console.log(`EPUBCheck-TS v${VERSION}`);
|
|
22
|
+
console.log("TypeScript EPUB validator for Node.js and browsers");
|
|
23
|
+
console.log();
|
|
24
|
+
console.log("Note: This is ~65% feature-complete compared to Java EPUBCheck.");
|
|
25
|
+
console.log("For production validation: https://github.com/w3c/epubcheck");
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
if (values.help || positionals.length === 0) {
|
|
29
|
+
console.log(`EPUBCheck-TS v${VERSION} - EPUB Validator
|
|
30
|
+
|
|
31
|
+
Usage: epubcheck-ts <file.epub> [options]
|
|
32
|
+
|
|
33
|
+
Arguments:
|
|
34
|
+
<file.epub> Path to EPUB file to validate
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
-j, --json <file> Output JSON report to file (use '-' for stdout)
|
|
38
|
+
-q, --quiet Suppress console output (errors only)
|
|
39
|
+
-p, --profile <name> Validation profile (default|dict|edupub|idx|preview)
|
|
40
|
+
-u, --usage Include usage messages (best practices)
|
|
41
|
+
-w, --fail-on-warnings Exit with code 1 if warnings are found
|
|
42
|
+
-v, --version Show version information
|
|
43
|
+
-h, --help Show this help message
|
|
44
|
+
|
|
45
|
+
Examples:
|
|
46
|
+
epubcheck-ts book.epub
|
|
47
|
+
epubcheck-ts book.epub --json report.json
|
|
48
|
+
epubcheck-ts book.epub --quiet --fail-on-warnings
|
|
49
|
+
epubcheck-ts book.epub --profile dict
|
|
50
|
+
|
|
51
|
+
Exit Codes:
|
|
52
|
+
0 No errors (or only warnings if --fail-on-warnings not set)
|
|
53
|
+
1 Validation errors found (or warnings with --fail-on-warnings)
|
|
54
|
+
2 Runtime error (file not found, invalid arguments, etc.)
|
|
55
|
+
|
|
56
|
+
Note: This tool provides ~65% coverage of Java EPUBCheck features.
|
|
57
|
+
Missing features: Media Overlays, advanced ARIA checks, encryption/signatures.
|
|
58
|
+
For complete EPUB 3 conformance testing, use: https://github.com/w3c/epubcheck
|
|
59
|
+
|
|
60
|
+
Report issues: https://github.com/likecoin/epubcheck-ts/issues
|
|
61
|
+
`);
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
async function main() {
|
|
65
|
+
const filePath = positionals[0];
|
|
66
|
+
if (!filePath) {
|
|
67
|
+
console.error("Error: No EPUB file specified");
|
|
68
|
+
console.error("Run with --help for usage information");
|
|
69
|
+
process.exit(2);
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
if (!values.quiet) {
|
|
73
|
+
console.log(`Validating: ${basename(filePath)}`);
|
|
74
|
+
console.log();
|
|
75
|
+
}
|
|
76
|
+
const epubData = await readFile(filePath);
|
|
77
|
+
const startTime = Date.now();
|
|
78
|
+
const options = {};
|
|
79
|
+
if (values.profile) {
|
|
80
|
+
options.profile = values.profile;
|
|
81
|
+
}
|
|
82
|
+
if (values.usage) {
|
|
83
|
+
options.includeUsage = true;
|
|
84
|
+
}
|
|
85
|
+
const result = await EpubCheck.validate(epubData, options);
|
|
86
|
+
const elapsedMs = Date.now() - startTime;
|
|
87
|
+
if (values.json !== void 0) {
|
|
88
|
+
const jsonContent = toJSONReport(result);
|
|
89
|
+
if (values.json === "-") {
|
|
90
|
+
if (values.quiet) {
|
|
91
|
+
console.log(jsonContent);
|
|
92
|
+
} else {
|
|
93
|
+
console.log("\nJSON Report:");
|
|
94
|
+
console.log(jsonContent);
|
|
95
|
+
}
|
|
96
|
+
} else if (typeof values.json === "string") {
|
|
97
|
+
await writeFile(values.json, jsonContent, "utf-8");
|
|
98
|
+
if (!values.quiet) {
|
|
99
|
+
console.log(`JSON report written to: ${values.json}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!values.quiet) {
|
|
104
|
+
const fatal = result.messages.filter((m) => m.severity === "fatal");
|
|
105
|
+
const errors = result.messages.filter((m) => m.severity === "error");
|
|
106
|
+
const warnings = result.messages.filter((m) => m.severity === "warning");
|
|
107
|
+
const info = result.messages.filter((m) => m.severity === "info");
|
|
108
|
+
const usage = result.messages.filter((m) => m.severity === "usage");
|
|
109
|
+
const printMessages = (messages, color, label) => {
|
|
110
|
+
if (messages.length === 0) return;
|
|
111
|
+
for (const msg of messages) {
|
|
112
|
+
const locationStr = msg.location ? ` (${msg.location.path}${msg.location.line !== void 0 ? `:${String(msg.location.line)}` : ""})` : "";
|
|
113
|
+
console.log(`${color}${label}${locationStr}: ${msg.message}\x1B[0m`);
|
|
114
|
+
if (msg.id) {
|
|
115
|
+
console.log(` \x1B[90mID: ${msg.id}\x1B[0m`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
console.log();
|
|
119
|
+
};
|
|
120
|
+
if (fatal.length > 0) {
|
|
121
|
+
printMessages(fatal, "\x1B[31m\x1B[1m", "FATAL");
|
|
122
|
+
}
|
|
123
|
+
if (errors.length > 0) {
|
|
124
|
+
printMessages(errors, "\x1B[31m", "ERROR");
|
|
125
|
+
}
|
|
126
|
+
if (warnings.length > 0) {
|
|
127
|
+
printMessages(warnings, "\x1B[33m", "WARNING");
|
|
128
|
+
}
|
|
129
|
+
if (info.length > 0 && result.messages.length < 20) {
|
|
130
|
+
printMessages(info, "\x1B[36m", "INFO");
|
|
131
|
+
}
|
|
132
|
+
if (usage.length > 0) {
|
|
133
|
+
printMessages(usage, "\x1B[90m", "USAGE");
|
|
134
|
+
}
|
|
135
|
+
console.log("\u2500".repeat(60));
|
|
136
|
+
const summaryColor = result.valid ? "\x1B[32m" : "\x1B[31m";
|
|
137
|
+
const summaryIcon = result.valid ? "\u2713" : "\u2717";
|
|
138
|
+
console.log(
|
|
139
|
+
`${summaryColor}${summaryIcon} ${result.valid ? "Valid EPUB" : "Invalid EPUB"}\x1B[0m`
|
|
140
|
+
);
|
|
141
|
+
console.log();
|
|
142
|
+
console.log(` Errors: ${String(result.errorCount + result.fatalCount)}`);
|
|
143
|
+
console.log(` Warnings: ${String(result.warningCount)}`);
|
|
144
|
+
if (info.length > 0) {
|
|
145
|
+
console.log(` Info: ${String(result.infoCount)}`);
|
|
146
|
+
}
|
|
147
|
+
if (usage.length > 0) {
|
|
148
|
+
console.log(` Usages: ${String(result.usageCount)}`);
|
|
149
|
+
}
|
|
150
|
+
console.log(` Time: ${String(elapsedMs)}ms`);
|
|
151
|
+
console.log();
|
|
152
|
+
if (result.errorCount === 0 && result.fatalCount === 0) {
|
|
153
|
+
console.log(
|
|
154
|
+
"\x1B[90mNote: This validator provides ~65% coverage of Java EPUBCheck.\x1B[0m"
|
|
155
|
+
);
|
|
156
|
+
console.log("\x1B[90mFor complete validation: https://github.com/w3c/epubcheck\x1B[0m");
|
|
157
|
+
console.log();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const shouldFail = result.errorCount > 0 || result.fatalCount > 0 || values["fail-on-warnings"] && result.warningCount > 0;
|
|
161
|
+
process.exit(shouldFail ? 1 : 0);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error("\x1B[31mError:\x1B[0m", error instanceof Error ? error.message : String(error));
|
|
164
|
+
if (error instanceof Error && error.stack && !values.quiet) {
|
|
165
|
+
console.error("\x1B[90m" + error.stack + "\x1B[0m");
|
|
166
|
+
}
|
|
167
|
+
process.exit(2);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
void main();
|
package/bin/epubcheck.ts
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* EPUBCheck-TS CLI
|
|
4
|
+
*
|
|
5
|
+
* A minimalist command-line interface for EPUB validation.
|
|
6
|
+
* For full EPUBCheck features, use the official Java version:
|
|
7
|
+
* https://github.com/w3c/epubcheck
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
11
|
+
import { parseArgs } from 'node:util';
|
|
12
|
+
import { basename } from 'node:path';
|
|
13
|
+
|
|
14
|
+
// Dynamic import to support both ESM and CJS builds
|
|
15
|
+
const { EpubCheck, toJSONReport } = await import('../dist/index.js');
|
|
16
|
+
|
|
17
|
+
const VERSION = '0.2.1';
|
|
18
|
+
|
|
19
|
+
// Parse command line arguments
|
|
20
|
+
const { values, positionals } = parseArgs({
|
|
21
|
+
options: {
|
|
22
|
+
json: { type: 'string', short: 'j' },
|
|
23
|
+
quiet: { type: 'boolean', short: 'q', default: false },
|
|
24
|
+
profile: { type: 'string', short: 'p' },
|
|
25
|
+
usage: { type: 'boolean', short: 'u', default: false },
|
|
26
|
+
version: { type: 'boolean', short: 'v', default: false },
|
|
27
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
28
|
+
'fail-on-warnings': { type: 'boolean', short: 'w', default: false },
|
|
29
|
+
},
|
|
30
|
+
allowPositionals: true,
|
|
31
|
+
strict: false,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Show version
|
|
35
|
+
if (values.version) {
|
|
36
|
+
console.log(`EPUBCheck-TS v${VERSION}`);
|
|
37
|
+
console.log('TypeScript EPUB validator for Node.js and browsers');
|
|
38
|
+
console.log();
|
|
39
|
+
console.log('Note: This is ~65% feature-complete compared to Java EPUBCheck.');
|
|
40
|
+
console.log('For production validation: https://github.com/w3c/epubcheck');
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Show help
|
|
45
|
+
if (values.help || positionals.length === 0) {
|
|
46
|
+
console.log(`EPUBCheck-TS v${VERSION} - EPUB Validator
|
|
47
|
+
|
|
48
|
+
Usage: epubcheck-ts <file.epub> [options]
|
|
49
|
+
|
|
50
|
+
Arguments:
|
|
51
|
+
<file.epub> Path to EPUB file to validate
|
|
52
|
+
|
|
53
|
+
Options:
|
|
54
|
+
-j, --json <file> Output JSON report to file (use '-' for stdout)
|
|
55
|
+
-q, --quiet Suppress console output (errors only)
|
|
56
|
+
-p, --profile <name> Validation profile (default|dict|edupub|idx|preview)
|
|
57
|
+
-u, --usage Include usage messages (best practices)
|
|
58
|
+
-w, --fail-on-warnings Exit with code 1 if warnings are found
|
|
59
|
+
-v, --version Show version information
|
|
60
|
+
-h, --help Show this help message
|
|
61
|
+
|
|
62
|
+
Examples:
|
|
63
|
+
epubcheck-ts book.epub
|
|
64
|
+
epubcheck-ts book.epub --json report.json
|
|
65
|
+
epubcheck-ts book.epub --quiet --fail-on-warnings
|
|
66
|
+
epubcheck-ts book.epub --profile dict
|
|
67
|
+
|
|
68
|
+
Exit Codes:
|
|
69
|
+
0 No errors (or only warnings if --fail-on-warnings not set)
|
|
70
|
+
1 Validation errors found (or warnings with --fail-on-warnings)
|
|
71
|
+
2 Runtime error (file not found, invalid arguments, etc.)
|
|
72
|
+
|
|
73
|
+
Note: This tool provides ~65% coverage of Java EPUBCheck features.
|
|
74
|
+
Missing features: Media Overlays, advanced ARIA checks, encryption/signatures.
|
|
75
|
+
For complete EPUB 3 conformance testing, use: https://github.com/w3c/epubcheck
|
|
76
|
+
|
|
77
|
+
Report issues: https://github.com/likecoin/epubcheck-ts/issues
|
|
78
|
+
`);
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Main validation logic
|
|
83
|
+
async function main(): Promise<void> {
|
|
84
|
+
const filePath = positionals[0];
|
|
85
|
+
|
|
86
|
+
if (!filePath) {
|
|
87
|
+
console.error('Error: No EPUB file specified');
|
|
88
|
+
console.error('Run with --help for usage information');
|
|
89
|
+
process.exit(2);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
// Read EPUB file
|
|
94
|
+
if (!values.quiet) {
|
|
95
|
+
console.log(`Validating: ${basename(filePath)}`);
|
|
96
|
+
console.log();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const epubData = await readFile(filePath);
|
|
100
|
+
|
|
101
|
+
// Validate
|
|
102
|
+
const startTime = Date.now();
|
|
103
|
+
const options: {
|
|
104
|
+
profile?: 'default' | 'dict' | 'edupub' | 'idx' | 'preview';
|
|
105
|
+
includeUsage?: boolean;
|
|
106
|
+
} = {};
|
|
107
|
+
if (values.profile) {
|
|
108
|
+
options.profile = values.profile as 'default' | 'dict' | 'edupub' | 'idx' | 'preview';
|
|
109
|
+
}
|
|
110
|
+
if (values.usage) {
|
|
111
|
+
options.includeUsage = true;
|
|
112
|
+
}
|
|
113
|
+
const result = await EpubCheck.validate(epubData, options);
|
|
114
|
+
const elapsedMs = Date.now() - startTime;
|
|
115
|
+
|
|
116
|
+
// Output JSON report if requested
|
|
117
|
+
if (values.json !== undefined) {
|
|
118
|
+
const jsonContent = toJSONReport(result); // Already stringified
|
|
119
|
+
|
|
120
|
+
if (values.json === '-') {
|
|
121
|
+
// Output to stdout - suppress other output
|
|
122
|
+
if (values.quiet) {
|
|
123
|
+
console.log(jsonContent);
|
|
124
|
+
} else {
|
|
125
|
+
// If not quiet, output after other messages
|
|
126
|
+
console.log('\nJSON Report:');
|
|
127
|
+
console.log(jsonContent);
|
|
128
|
+
}
|
|
129
|
+
} else if (typeof values.json === 'string') {
|
|
130
|
+
await writeFile(values.json, jsonContent, 'utf-8');
|
|
131
|
+
if (!values.quiet) {
|
|
132
|
+
console.log(`JSON report written to: ${values.json}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Console output (unless quiet mode)
|
|
138
|
+
if (!values.quiet) {
|
|
139
|
+
// Group messages by severity
|
|
140
|
+
const fatal = result.messages.filter((m) => m.severity === 'fatal');
|
|
141
|
+
const errors = result.messages.filter((m) => m.severity === 'error');
|
|
142
|
+
const warnings = result.messages.filter((m) => m.severity === 'warning');
|
|
143
|
+
const info = result.messages.filter((m) => m.severity === 'info');
|
|
144
|
+
const usage = result.messages.filter((m) => m.severity === 'usage');
|
|
145
|
+
|
|
146
|
+
// Print messages with colors
|
|
147
|
+
const printMessages = (
|
|
148
|
+
messages: typeof result.messages,
|
|
149
|
+
color: string,
|
|
150
|
+
label: string,
|
|
151
|
+
): void => {
|
|
152
|
+
if (messages.length === 0) return;
|
|
153
|
+
|
|
154
|
+
for (const msg of messages) {
|
|
155
|
+
const locationStr = msg.location
|
|
156
|
+
? ` (${msg.location.path}${msg.location.line !== undefined ? `:${String(msg.location.line)}` : ''})`
|
|
157
|
+
: '';
|
|
158
|
+
console.log(`${color}${label}${locationStr}: ${msg.message}\x1b[0m`);
|
|
159
|
+
if (msg.id) {
|
|
160
|
+
console.log(` \x1b[90mID: ${msg.id}\x1b[0m`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
console.log();
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (fatal.length > 0) {
|
|
167
|
+
printMessages(fatal, '\x1b[31m\x1b[1m', 'FATAL');
|
|
168
|
+
}
|
|
169
|
+
if (errors.length > 0) {
|
|
170
|
+
printMessages(errors, '\x1b[31m', 'ERROR');
|
|
171
|
+
}
|
|
172
|
+
if (warnings.length > 0) {
|
|
173
|
+
printMessages(warnings, '\x1b[33m', 'WARNING');
|
|
174
|
+
}
|
|
175
|
+
if (info.length > 0 && result.messages.length < 20) {
|
|
176
|
+
// Only show info if total messages is small
|
|
177
|
+
printMessages(info, '\x1b[36m', 'INFO');
|
|
178
|
+
}
|
|
179
|
+
if (usage.length > 0) {
|
|
180
|
+
printMessages(usage, '\x1b[90m', 'USAGE');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Summary
|
|
184
|
+
console.log('─'.repeat(60));
|
|
185
|
+
const summaryColor = result.valid ? '\x1b[32m' : '\x1b[31m';
|
|
186
|
+
const summaryIcon = result.valid ? '✓' : '✗';
|
|
187
|
+
console.log(
|
|
188
|
+
`${summaryColor}${summaryIcon} ${result.valid ? 'Valid EPUB' : 'Invalid EPUB'}\x1b[0m`,
|
|
189
|
+
);
|
|
190
|
+
console.log();
|
|
191
|
+
console.log(` Errors: ${String(result.errorCount + result.fatalCount)}`);
|
|
192
|
+
console.log(` Warnings: ${String(result.warningCount)}`);
|
|
193
|
+
if (info.length > 0) {
|
|
194
|
+
console.log(` Info: ${String(result.infoCount)}`);
|
|
195
|
+
}
|
|
196
|
+
if (usage.length > 0) {
|
|
197
|
+
console.log(` Usages: ${String(result.usageCount)}`);
|
|
198
|
+
}
|
|
199
|
+
console.log(` Time: ${String(elapsedMs)}ms`);
|
|
200
|
+
console.log();
|
|
201
|
+
|
|
202
|
+
// Show limitation notice if there were no major errors
|
|
203
|
+
if (result.errorCount === 0 && result.fatalCount === 0) {
|
|
204
|
+
console.log(
|
|
205
|
+
'\x1b[90mNote: This validator provides ~65% coverage of Java EPUBCheck.\x1b[0m',
|
|
206
|
+
);
|
|
207
|
+
console.log('\x1b[90mFor complete validation: https://github.com/w3c/epubcheck\x1b[0m');
|
|
208
|
+
console.log();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Determine exit code
|
|
213
|
+
const shouldFail =
|
|
214
|
+
result.errorCount > 0 ||
|
|
215
|
+
result.fatalCount > 0 ||
|
|
216
|
+
(values['fail-on-warnings'] && result.warningCount > 0);
|
|
217
|
+
process.exit(shouldFail ? 1 : 0);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error('\x1b[31mError:\x1b[0m', error instanceof Error ? error.message : String(error));
|
|
220
|
+
if (error instanceof Error && error.stack && !values.quiet) {
|
|
221
|
+
console.error('\x1b[90m' + error.stack + '\x1b[0m');
|
|
222
|
+
}
|
|
223
|
+
process.exit(2);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
void main();
|