@likecoin/epubcheck-ts 0.3.9 → 0.4.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.
package/README.md CHANGED
@@ -1,25 +1,24 @@
1
1
  # epubcheck-ts
2
2
 
3
- A TypeScript port of [EPUBCheck](https://github.com/w3c/epubcheck) - the official conformance checker for EPUB publications.
3
+ Validate EPUB files in Node.js and the browser. A TypeScript implementation of [EPUBCheck](https://github.com/w3c/epubcheck).
4
4
 
5
5
  [![CI](https://github.com/likecoin/epubcheck-ts/actions/workflows/ci.yml/badge.svg)](https://github.com/likecoin/epubcheck-ts/actions/workflows/ci.yml)
6
6
  [![npm](https://img.shields.io/npm/v/%40likecoin%2Fepubcheck-ts)](https://www.npmjs.com/package/@likecoin/epubcheck-ts)
7
7
  [![License](https://img.shields.io/npm/l/%40likecoin%2Fepubcheck-ts)](./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 (892 passing / 945 total tests) and ~88% 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!
9
+ > **Status**: 93% feature parity with Java EPUBCheck (1061 tests passing, 74 skipped). See [PROJECT_STATUS.md](./PROJECT_STATUS.md) for details. For full EPUB 3 conformance testing, use the official [Java EPUBCheck](https://github.com/w3c/epubcheck).
10
10
 
11
11
  ## Features
12
12
 
13
- - **CLI and programmatic API**: Use as a command-line tool or integrate into your application
14
- - **Cross-platform**: Works in Node.js (18+) and modern browsers
15
- - **Partial EPUB validation**: Currently ~88% of EPUBCheck feature parity
16
- - **Zero native dependencies**: Pure JavaScript/WebAssembly, no compilation required
17
- - **TypeScript first**: Full type definitions included
18
- - **Tree-shakable**: ESM with proper exports for optimal bundling
13
+ - **CLI and API** Use as a CLI tool (`npx @likecoin/epubcheck-ts book.epub`) or import as a library
14
+ - **Browser support** — Works in Node.js 18+ and modern browsers via pure JS + WASM
15
+ - **No native dependencies** No Java, no compilation — `npm install` and go
16
+ - **TypeScript** Full type definitions included
17
+ - **Tree-shakable** ESM with proper exports for minimal bundle impact
19
18
 
20
19
  ## Try it Online
21
20
 
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.
21
+ Live demo at **[likecoin.github.io/epubcheck-ts](https://likecoin.github.io/epubcheck-ts/)** validate EPUB files in the browser without uploading to any server.
23
22
 
24
23
  ## Installation
25
24
 
@@ -50,7 +49,13 @@ Options:
50
49
  -j, --json <file> Output JSON report to file (use '-' for stdout)
51
50
  -q, --quiet Suppress console output (errors only)
52
51
  -p, --profile <name> Validation profile (default|dict|edupub|idx|preview)
52
+ -u, --usage Include usage messages (best practices)
53
+ -f, --fatal Show only fatal errors
54
+ -e, --error Show fatal errors and errors
55
+ --warn Show fatal errors, errors, and warnings
56
+ -c, --customMessages <file> Override message severities (TSV: ID<tab>SEVERITY)
53
57
  -w, --fail-on-warnings Exit with code 1 if warnings are found
58
+ -l, --listChecks List all message IDs and severities
54
59
  -v, --version Show version information
55
60
  -h, --help Show this help message
56
61
  ```
@@ -68,9 +73,14 @@ epubcheck-ts book.epub --quiet --fail-on-warnings
68
73
 
69
74
  # Validate with specific profile
70
75
  epubcheck-ts dictionary.epub --profile dict
71
- ```
72
76
 
73
- **Note:** This CLI provides ~88% coverage of Java EPUBCheck features. For complete EPUB 3 conformance testing, use the [official Java EPUBCheck](https://github.com/w3c/epubcheck).
77
+ # Show only errors (hide warnings/info)
78
+ epubcheck-ts book.epub --error
79
+
80
+ # Enable suppressed accessibility checks
81
+ printf "ACC-004\tWARNING\nACC-005\tWARNING\n" > overrides.txt
82
+ epubcheck-ts book.epub -c overrides.txt
83
+ ```
74
84
 
75
85
  ### ES Modules (recommended)
76
86
 
@@ -181,6 +191,9 @@ interface EpubCheckOptions {
181
191
 
182
192
  /** Locale for messages (default: 'en') */
183
193
  locale?: string;
194
+
195
+ /** Custom message severity overrides (message ID → severity) */
196
+ customMessages?: Map<string, MessageSeverity>;
184
197
  }
185
198
  ```
186
199
 
@@ -266,20 +279,20 @@ This library is a TypeScript port of the Java-based [EPUBCheck](https://github.c
266
279
 
267
280
  | Component | Status | Completeness | Notes |
268
281
  |-----------|--------|--------------|-------|
269
- | OCF Container | 🟡 Partial | ~90% | ZIP structure, mimetype, container.xml, encryption.xml obfuscation |
270
- | Package Document (OPF) | 🟢 Complete | ~90% | Metadata, manifest, spine, collections, Schematron-equivalent checks |
271
- | Content Documents | 🟡 Partial | ~85% | XHTML structure, CSS url(), @import, SVG, entities, title, SSML, XML version |
272
- | Navigation Document | 🟢 Complete | ~85% | Nav content model, landmarks, labels, reading order, hidden |
273
- | Schema Validation | 🟡 Partial | ~50% | RelaxNG for OPF/container; XHTML/SVG disabled (libxml2 limitation) |
274
- | CSS | 🟡 Partial | ~70% | @font-face, @import, url() extraction, position, forbidden properties |
275
- | Cross-reference Validation | 🟢 Complete | ~90% | Reference tracking, fragments, fallbacks, remote resources |
276
- | Accessibility Checks | 🟡 Partial | ~30% | Basic checks only (empty links, image alt, SVG titles) |
277
- | Media Overlays | Not Started | 0% | Planned |
282
+ | OCF Container | 🟢 Complete | ~92% | ZIP structure, mimetype, container.xml, encryption.xml obfuscation |
283
+ | Package Document (OPF) | 🟢 Complete | ~92% | Metadata, manifest, spine, collections, Schematron-equivalent checks |
284
+ | Content Documents | 🟢 Complete | ~93% | XHTML structure, CSS url(), @import, SVG, entities, title, SSML, XML version |
285
+ | Navigation Document | 🟢 Complete | ~95% | Nav content model, landmarks, labels, reading order, hidden, nested-ol |
286
+ | Schema Validation | 🟡 Partial | ~55% | RelaxNG for OPF/container; XHTML/SVG disabled (libxml2 limitation) |
287
+ | CSS | 🟡 Partial | ~85% | @font-face, @import, url() extraction, position, forbidden properties, alt style tags |
288
+ | Cross-reference Validation | 🟢 Complete | ~92% | Reference tracking, fragments, fallbacks, remote resources, cross-document features |
289
+ | Accessibility Checks | 🟢 Complete | ~71% | 12/17 ACC checks: table, image alt, hyperlink, MathML, SVG, epub:type, OPF metadata |
290
+ | Media Overlays | 🟡 Partial | ~70% | SMIL structure, timing, audio, OPF metadata, duration validation |
278
291
  | Media Validation | ❌ Not Started | 0% | Planned |
279
292
 
280
293
  Legend: 🟢 Complete | 🟡 Partial | 🔴 Basic | ❌ Not Started
281
294
 
282
- **Overall Progress: ~88% of Java EPUBCheck features**
295
+ **Overall Progress: ~93% of Java EPUBCheck features**
283
296
 
284
297
  See [PROJECT_STATUS.md](./PROJECT_STATUS.md) for detailed comparison.
285
298
 
@@ -364,13 +377,13 @@ Legend: ✅ Implemented
364
377
  | Aspect | epubcheck-ts | EPUBCheck (Java) |
365
378
  |--------|--------------|------------------|
366
379
  | Runtime | Node.js / Browser | JVM |
367
- | Feature Parity | ~88% | 100% |
380
+ | Feature Parity | ~93% | 100% |
368
381
  | Bundle Size | ~450KB JS + ~1.6MB WASM | ~15MB |
369
382
  | Installation | `npm install` | Download JAR |
370
383
  | Integration | Native JS/TS | CLI or Java API |
371
384
  | Performance | Comparable | Baseline |
372
385
 
373
- **Note:** epubcheck-ts is currently in active development. See [PROJECT_STATUS.md](./PROJECT_STATUS.md) for detailed feature comparison.
386
+ See [PROJECT_STATUS.md](./PROJECT_STATUS.md) for detailed feature comparison.
374
387
 
375
388
  ## Contributing
376
389
 
package/bin/epubcheck.js CHANGED
@@ -3,13 +3,17 @@ import { readFile, writeFile } from "node:fs/promises";
3
3
  import { parseArgs } from "node:util";
4
4
  import { basename } from "node:path";
5
5
  const { EpubCheck, toJSONReport } = await import("../dist/index.js");
6
- const VERSION = "0.3.9";
6
+ const VERSION = "0.4.0";
7
7
  const { values, positionals } = parseArgs({
8
8
  options: {
9
9
  json: { type: "string", short: "j" },
10
10
  quiet: { type: "boolean", short: "q", default: false },
11
11
  profile: { type: "string", short: "p" },
12
12
  usage: { type: "boolean", short: "u", default: false },
13
+ fatal: { type: "boolean", short: "f", default: false },
14
+ error: { type: "boolean", short: "e", default: false },
15
+ warn: { type: "boolean", default: false },
16
+ customMessages: { type: "string", short: "c" },
13
17
  version: { type: "boolean", short: "v", default: false },
14
18
  help: { type: "boolean", short: "h", default: false },
15
19
  "fail-on-warnings": { type: "boolean", short: "w", default: false },
@@ -22,7 +26,7 @@ if (values.version) {
22
26
  console.log(`EPUBCheck-TS v${VERSION}`);
23
27
  console.log("TypeScript EPUB validator for Node.js and browsers");
24
28
  console.log();
25
- console.log("Note: This is ~88% feature-complete compared to Java EPUBCheck.");
29
+ console.log("Note: This is ~93% feature-complete compared to Java EPUBCheck.");
26
30
  console.log("For production validation: https://github.com/w3c/epubcheck");
27
31
  process.exit(0);
28
32
  }
@@ -45,6 +49,10 @@ Options:
45
49
  -q, --quiet Suppress console output (errors only)
46
50
  -p, --profile <name> Validation profile (default|dict|edupub|idx|preview)
47
51
  -u, --usage Include usage messages (best practices)
52
+ -f, --fatal Show only fatal errors
53
+ -e, --error Show fatal errors and errors
54
+ --warn Show fatal errors, errors, and warnings
55
+ -c, --customMessages <file> Override message severities (TSV: ID\\tSEVERITY)
48
56
  -w, --fail-on-warnings Exit with code 1 if warnings are found
49
57
  -l, --listChecks List all message IDs and severities
50
58
  -v, --version Show version information
@@ -61,8 +69,8 @@ Exit Codes:
61
69
  1 Validation errors found (or warnings with --fail-on-warnings)
62
70
  2 Runtime error (file not found, invalid arguments, etc.)
63
71
 
64
- Note: This tool provides ~88% coverage of Java EPUBCheck features.
65
- Missing features: Media Overlays, advanced ARIA checks, encryption/signatures.
72
+ Note: This tool provides ~93% coverage of Java EPUBCheck features.
73
+ Missing features: single-file/directory validation, advanced ARIA, XHTML/SVG schema.
66
74
  For complete EPUB 3 conformance testing, use: https://github.com/w3c/epubcheck
67
75
 
68
76
  Report issues: https://github.com/likecoin/epubcheck-ts/issues
@@ -90,10 +98,31 @@ async function main() {
90
98
  if (values.usage) {
91
99
  options.includeUsage = true;
92
100
  }
101
+ if (typeof values.customMessages === "string") {
102
+ const { parseCustomMessages } = await import("../dist/index.js");
103
+ const cmContent = await readFile(values.customMessages, "utf-8");
104
+ options.customMessages = parseCustomMessages(cmContent);
105
+ }
93
106
  const result = await EpubCheck.validate(epubData, options);
94
107
  const elapsedMs = Date.now() - startTime;
108
+ const severityRank = {
109
+ fatal: 0,
110
+ error: 1,
111
+ warning: 2,
112
+ info: 3,
113
+ usage: 4
114
+ };
115
+ let maxRank = 4;
116
+ if (values.fatal) maxRank = 0;
117
+ else if (values.error) maxRank = 1;
118
+ else if (values.warn) maxRank = 2;
119
+ const isFiltered = values.fatal || values.error || values.warn;
120
+ const displayMessages = result.messages.filter(
121
+ (m) => severityRank[m.severity] <= maxRank
122
+ );
95
123
  if (values.json !== void 0) {
96
- const jsonContent = toJSONReport(result);
124
+ const filteredResult = isFiltered ? { ...result, messages: displayMessages } : result;
125
+ const jsonContent = toJSONReport(filteredResult);
97
126
  if (values.json === "-") {
98
127
  if (values.quiet) {
99
128
  console.log(jsonContent);
@@ -109,11 +138,11 @@ async function main() {
109
138
  }
110
139
  }
111
140
  if (!values.quiet) {
112
- const fatal = result.messages.filter((m) => m.severity === "fatal");
113
- const errors = result.messages.filter((m) => m.severity === "error");
114
- const warnings = result.messages.filter((m) => m.severity === "warning");
115
- const info = result.messages.filter((m) => m.severity === "info");
116
- const usage = result.messages.filter((m) => m.severity === "usage");
141
+ const fatal = displayMessages.filter((m) => m.severity === "fatal");
142
+ const errors = displayMessages.filter((m) => m.severity === "error");
143
+ const warnings = displayMessages.filter((m) => m.severity === "warning");
144
+ const info = displayMessages.filter((m) => m.severity === "info");
145
+ const usage = displayMessages.filter((m) => m.severity === "usage");
117
146
  const printMessages = (messages, color, label) => {
118
147
  if (messages.length === 0) return;
119
148
  for (const msg of messages) {
@@ -134,7 +163,7 @@ async function main() {
134
163
  if (warnings.length > 0) {
135
164
  printMessages(warnings, "\x1B[33m", "WARNING");
136
165
  }
137
- if (info.length > 0 && result.messages.length < 20) {
166
+ if (info.length > 0 && displayMessages.length < 20) {
138
167
  printMessages(info, "\x1B[36m", "INFO");
139
168
  }
140
169
  if (usage.length > 0) {
@@ -159,7 +188,7 @@ async function main() {
159
188
  console.log();
160
189
  if (result.errorCount === 0 && result.fatalCount === 0) {
161
190
  console.log(
162
- "\x1B[90mNote: This validator provides ~88% coverage of Java EPUBCheck.\x1B[0m"
191
+ "\x1B[90mNote: This validator provides ~93% coverage of Java EPUBCheck.\x1B[0m"
163
192
  );
164
193
  console.log("\x1B[90mFor complete validation: https://github.com/w3c/epubcheck\x1B[0m");
165
194
  console.log();
package/bin/epubcheck.ts CHANGED
@@ -10,12 +10,12 @@
10
10
  import { readFile, writeFile } from 'node:fs/promises';
11
11
  import { parseArgs } from 'node:util';
12
12
  import { basename } from 'node:path';
13
- import type { EpubCheckOptions, EPUBProfile, ValidationMessage } from '../src/types.js';
13
+ import type { EpubCheckOptions, EPUBProfile, Severity, ValidationMessage } from '../src/types.js';
14
14
 
15
15
  // Dynamic import to support both ESM and CJS builds
16
16
  const { EpubCheck, toJSONReport } = await import('../dist/index.js');
17
17
 
18
- const VERSION = '0.3.9';
18
+ const VERSION = '0.4.0';
19
19
 
20
20
  // Parse command line arguments
21
21
  const { values, positionals } = parseArgs({
@@ -24,6 +24,10 @@ const { values, positionals } = parseArgs({
24
24
  quiet: { type: 'boolean', short: 'q', default: false },
25
25
  profile: { type: 'string', short: 'p' },
26
26
  usage: { type: 'boolean', short: 'u', default: false },
27
+ fatal: { type: 'boolean', short: 'f', default: false },
28
+ error: { type: 'boolean', short: 'e', default: false },
29
+ warn: { type: 'boolean', default: false },
30
+ customMessages: { type: 'string', short: 'c' },
27
31
  version: { type: 'boolean', short: 'v', default: false },
28
32
  help: { type: 'boolean', short: 'h', default: false },
29
33
  'fail-on-warnings': { type: 'boolean', short: 'w', default: false },
@@ -38,7 +42,7 @@ if (values.version) {
38
42
  console.log(`EPUBCheck-TS v${VERSION}`);
39
43
  console.log('TypeScript EPUB validator for Node.js and browsers');
40
44
  console.log();
41
- console.log('Note: This is ~88% feature-complete compared to Java EPUBCheck.');
45
+ console.log('Note: This is ~93% feature-complete compared to Java EPUBCheck.');
42
46
  console.log('For production validation: https://github.com/w3c/epubcheck');
43
47
  process.exit(0);
44
48
  }
@@ -65,6 +69,10 @@ Options:
65
69
  -q, --quiet Suppress console output (errors only)
66
70
  -p, --profile <name> Validation profile (default|dict|edupub|idx|preview)
67
71
  -u, --usage Include usage messages (best practices)
72
+ -f, --fatal Show only fatal errors
73
+ -e, --error Show fatal errors and errors
74
+ --warn Show fatal errors, errors, and warnings
75
+ -c, --customMessages <file> Override message severities (TSV: ID\\tSEVERITY)
68
76
  -w, --fail-on-warnings Exit with code 1 if warnings are found
69
77
  -l, --listChecks List all message IDs and severities
70
78
  -v, --version Show version information
@@ -81,8 +89,8 @@ Exit Codes:
81
89
  1 Validation errors found (or warnings with --fail-on-warnings)
82
90
  2 Runtime error (file not found, invalid arguments, etc.)
83
91
 
84
- Note: This tool provides ~88% coverage of Java EPUBCheck features.
85
- Missing features: Media Overlays, advanced ARIA checks, encryption/signatures.
92
+ Note: This tool provides ~93% coverage of Java EPUBCheck features.
93
+ Missing features: single-file/directory validation, advanced ARIA, XHTML/SVG schema.
86
94
  For complete EPUB 3 conformance testing, use: https://github.com/w3c/epubcheck
87
95
 
88
96
  Report issues: https://github.com/likecoin/epubcheck-ts/issues
@@ -118,12 +126,35 @@ async function main(): Promise<void> {
118
126
  if (values.usage) {
119
127
  options.includeUsage = true;
120
128
  }
129
+ if (typeof values.customMessages === 'string') {
130
+ const { parseCustomMessages } = await import('../dist/index.js');
131
+ const cmContent = await readFile(values.customMessages, 'utf-8');
132
+ options.customMessages = parseCustomMessages(cmContent);
133
+ }
121
134
  const result = await EpubCheck.validate(epubData, options);
122
135
  const elapsedMs = Date.now() - startTime;
123
136
 
137
+ // Most restrictive severity flag wins (--fatal overrides --error overrides --warn)
138
+ const severityRank: Record<Severity, number> = {
139
+ fatal: 0,
140
+ error: 1,
141
+ warning: 2,
142
+ info: 3,
143
+ usage: 4,
144
+ };
145
+ let maxRank = 4;
146
+ if (values.fatal) maxRank = 0;
147
+ else if (values.error) maxRank = 1;
148
+ else if (values.warn) maxRank = 2;
149
+ const isFiltered = values.fatal || values.error || values.warn;
150
+ const displayMessages = result.messages.filter(
151
+ (m: ValidationMessage) => severityRank[m.severity] <= maxRank,
152
+ );
153
+
124
154
  // Output JSON report if requested
125
155
  if (values.json !== undefined) {
126
- const jsonContent = toJSONReport(result); // Already stringified
156
+ const filteredResult = isFiltered ? { ...result, messages: displayMessages } : result;
157
+ const jsonContent = toJSONReport(filteredResult); // Already stringified
127
158
 
128
159
  if (values.json === '-') {
129
160
  // Output to stdout - suppress other output
@@ -145,11 +176,11 @@ async function main(): Promise<void> {
145
176
  // Console output (unless quiet mode)
146
177
  if (!values.quiet) {
147
178
  // Group messages by severity
148
- const fatal = result.messages.filter((m: ValidationMessage) => m.severity === 'fatal');
149
- const errors = result.messages.filter((m: ValidationMessage) => m.severity === 'error');
150
- const warnings = result.messages.filter((m: ValidationMessage) => m.severity === 'warning');
151
- const info = result.messages.filter((m: ValidationMessage) => m.severity === 'info');
152
- const usage = result.messages.filter((m: ValidationMessage) => m.severity === 'usage');
179
+ const fatal = displayMessages.filter((m: ValidationMessage) => m.severity === 'fatal');
180
+ const errors = displayMessages.filter((m: ValidationMessage) => m.severity === 'error');
181
+ const warnings = displayMessages.filter((m: ValidationMessage) => m.severity === 'warning');
182
+ const info = displayMessages.filter((m: ValidationMessage) => m.severity === 'info');
183
+ const usage = displayMessages.filter((m: ValidationMessage) => m.severity === 'usage');
153
184
 
154
185
  // Print messages with colors
155
186
  const printMessages = (
@@ -180,8 +211,7 @@ async function main(): Promise<void> {
180
211
  if (warnings.length > 0) {
181
212
  printMessages(warnings, '\x1b[33m', 'WARNING');
182
213
  }
183
- if (info.length > 0 && result.messages.length < 20) {
184
- // Only show info if total messages is small
214
+ if (info.length > 0 && displayMessages.length < 20) {
185
215
  printMessages(info, '\x1b[36m', 'INFO');
186
216
  }
187
217
  if (usage.length > 0) {
@@ -210,7 +240,7 @@ async function main(): Promise<void> {
210
240
  // Show limitation notice if there were no major errors
211
241
  if (result.errorCount === 0 && result.fatalCount === 0) {
212
242
  console.log(
213
- '\x1b[90mNote: This validator provides ~88% coverage of Java EPUBCheck.\x1b[0m',
243
+ '\x1b[90mNote: This validator provides ~93% coverage of Java EPUBCheck.\x1b[0m',
214
244
  );
215
245
  console.log('\x1b[90mFor complete validation: https://github.com/w3c/epubcheck\x1b[0m');
216
246
  console.log();