@likecoin/epubcheck-ts 0.6.0 → 0.6.2
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 +8 -3
- package/bin/epubcheck.js +15 -2
- package/bin/epubcheck.ts +15 -2
- package/dist/index.cjs +206 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +204 -31
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
|
-
import { XmlDocument, XmlElement } from 'libxml2-wasm';
|
|
2
1
|
import { parse, walk } from 'css-tree';
|
|
3
2
|
import { unzipSync, strFromU8, gunzipSync } from 'fflate';
|
|
4
3
|
|
|
5
|
-
// src/
|
|
4
|
+
// src/util/xml-engine.ts
|
|
5
|
+
var engine;
|
|
6
|
+
async function loadXmlEngine() {
|
|
7
|
+
engine ??= await import('libxml2-wasm');
|
|
8
|
+
}
|
|
9
|
+
function getXmlDocument() {
|
|
10
|
+
if (!engine) {
|
|
11
|
+
throw new Error("libxml2-wasm not initialized \u2014 call loadXmlEngine() first");
|
|
12
|
+
}
|
|
13
|
+
return engine.XmlDocument;
|
|
14
|
+
}
|
|
15
|
+
function getXmlElement() {
|
|
16
|
+
if (!engine) {
|
|
17
|
+
throw new Error("libxml2-wasm not initialized \u2014 call loadXmlEngine() first");
|
|
18
|
+
}
|
|
19
|
+
return engine.XmlElement;
|
|
20
|
+
}
|
|
6
21
|
|
|
7
22
|
// src/messages/messages.ts
|
|
8
23
|
var severityOverrides = /* @__PURE__ */ new Map();
|
|
@@ -2691,6 +2706,31 @@ var PROFILE_DC_TYPE = {
|
|
|
2691
2706
|
dict: "dictionary",
|
|
2692
2707
|
preview: "preview"
|
|
2693
2708
|
};
|
|
2709
|
+
var TYPE_TO_PROFILE = {
|
|
2710
|
+
dictionary: "dict",
|
|
2711
|
+
edupub: "edupub",
|
|
2712
|
+
index: "idx",
|
|
2713
|
+
preview: "preview"
|
|
2714
|
+
};
|
|
2715
|
+
var RESERVED_PREFIX_URIS = {
|
|
2716
|
+
dcterms: "http://purl.org/dc/terms/",
|
|
2717
|
+
marc: "http://id.loc.gov/vocabulary/",
|
|
2718
|
+
media: "http://www.idpf.org/epub/vocab/overlays/#",
|
|
2719
|
+
onix: "http://www.editeur.org/ONIX/book/codelists/current.html#",
|
|
2720
|
+
rendition: "http://www.idpf.org/vocab/rendition/#",
|
|
2721
|
+
schema: "http://schema.org/",
|
|
2722
|
+
xsd: "http://www.w3.org/2001/XMLSchema#",
|
|
2723
|
+
a11y: "http://www.idpf.org/epub/vocab/package/a11y/#"
|
|
2724
|
+
};
|
|
2725
|
+
function isValidURI(uri) {
|
|
2726
|
+
if (!uri) return false;
|
|
2727
|
+
try {
|
|
2728
|
+
new URL(uri);
|
|
2729
|
+
return true;
|
|
2730
|
+
} catch {
|
|
2731
|
+
return false;
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2694
2734
|
var DICTIONARY_TYPE_VALUES = /* @__PURE__ */ new Set([
|
|
2695
2735
|
"monolingual",
|
|
2696
2736
|
"bilingual",
|
|
@@ -2798,11 +2838,13 @@ var OPFValidator = class {
|
|
|
2798
2838
|
this.validatePackageAttributes(context, opfPath);
|
|
2799
2839
|
this.validateMetadata(context, opfPath);
|
|
2800
2840
|
if (this.packageDoc.version !== "2.0") {
|
|
2841
|
+
this.validatePrefixDeclarations(context, opfPath, opfXml);
|
|
2801
2842
|
this.validateMetaPrefixes(context, opfPath, opfXml);
|
|
2802
2843
|
}
|
|
2803
2844
|
this.validateLinkElements(context, opfPath);
|
|
2804
2845
|
this.validateManifest(context, opfPath);
|
|
2805
2846
|
this.validateSpine(context, opfPath);
|
|
2847
|
+
this.validatePageMap(context, opfPath, opfXml);
|
|
2806
2848
|
this.validateFallbackChains(context, opfPath);
|
|
2807
2849
|
this.validateUndeclaredResources(context, opfPath);
|
|
2808
2850
|
if (this.packageDoc.version === "2.0") {
|
|
@@ -2833,6 +2875,7 @@ var OPFValidator = class {
|
|
|
2833
2875
|
if (this.packageDoc.version.startsWith("3.")) {
|
|
2834
2876
|
this.validateAccessibilityMetadata(context, opfPath);
|
|
2835
2877
|
this.validateProfileDcType(context, opfPath);
|
|
2878
|
+
this.validateDcTypeProfileSwitch(context, opfPath);
|
|
2836
2879
|
this.validateEdupubMetadata(context, opfPath);
|
|
2837
2880
|
this.validateDictionaryMetadata(context, opfPath);
|
|
2838
2881
|
this.validatePreviewMetadata(context, opfPath);
|
|
@@ -3036,6 +3079,22 @@ var OPFValidator = class {
|
|
|
3036
3079
|
});
|
|
3037
3080
|
}
|
|
3038
3081
|
}
|
|
3082
|
+
// Mirrors Java's EPUBProfile.makeTypeCompatible flow.
|
|
3083
|
+
validateDcTypeProfileSwitch(context, opfPath) {
|
|
3084
|
+
if (!this.packageDoc) return;
|
|
3085
|
+
for (const dc of this.packageDoc.dcElements) {
|
|
3086
|
+
if (dc.name !== "type") continue;
|
|
3087
|
+
const inferred = TYPE_TO_PROFILE[dc.value.trim().toLowerCase()];
|
|
3088
|
+
if (inferred && inferred !== context.options.profile) {
|
|
3089
|
+
pushMessage(context.messages, {
|
|
3090
|
+
id: MessageId.OPF_064,
|
|
3091
|
+
message: `OPF declares type "${dc.value.trim().toLowerCase()}"; consider validating using the "${inferred}" profile.`,
|
|
3092
|
+
location: { path: opfPath }
|
|
3093
|
+
});
|
|
3094
|
+
return;
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3039
3098
|
/**
|
|
3040
3099
|
* Build lookup maps for manifest items
|
|
3041
3100
|
*/
|
|
@@ -3052,6 +3111,13 @@ var OPFValidator = class {
|
|
|
3052
3111
|
*/
|
|
3053
3112
|
validatePackageAttributes(context, opfPath) {
|
|
3054
3113
|
if (!this.packageDoc) return;
|
|
3114
|
+
if (this.packageDoc.isLegacyOebps12) {
|
|
3115
|
+
pushMessage(context.messages, {
|
|
3116
|
+
id: MessageId.OPF_047,
|
|
3117
|
+
message: "OPF file is using OEBPS 1.2 syntax allowing backwards compatibility.",
|
|
3118
|
+
location: { path: opfPath }
|
|
3119
|
+
});
|
|
3120
|
+
}
|
|
3055
3121
|
if (this.packageDoc.versionDeclared === false) {
|
|
3056
3122
|
pushMessage(context.messages, {
|
|
3057
3123
|
id: MessageId.OPF_001,
|
|
@@ -3976,14 +4042,24 @@ var OPFValidator = class {
|
|
|
3976
4042
|
const resolvedPath = resolvePath(opfPath, basePathNoQuery);
|
|
3977
4043
|
const resolvedPathDecoded = basePathDecodedNoQuery !== basePathNoQuery ? resolvePath(opfPath, basePathDecodedNoQuery) : resolvedPath;
|
|
3978
4044
|
const fileExists = context.files.has(resolvedPath) || context.files.has(resolvedPathDecoded);
|
|
3979
|
-
const
|
|
3980
|
-
if (!fileExists && !
|
|
4045
|
+
const manifestItem = this.manifestByHref.get(basePathNoQuery) ?? this.manifestByHref.get(basePathDecodedNoQuery);
|
|
4046
|
+
if (!fileExists && !manifestItem) {
|
|
3981
4047
|
pushMessage(context.messages, {
|
|
3982
4048
|
id: MessageId.RSC_007w,
|
|
3983
4049
|
message: `Referenced resource "${resolvedPath}" could not be found in the EPUB`,
|
|
3984
4050
|
location: { path: opfPath }
|
|
3985
4051
|
});
|
|
3986
4052
|
}
|
|
4053
|
+
if (manifestItem) {
|
|
4054
|
+
const inSpine = this.packageDoc.spine.some((ref) => ref.idref === manifestItem.id);
|
|
4055
|
+
if (!inSpine) {
|
|
4056
|
+
pushMessage(context.messages, {
|
|
4057
|
+
id: MessageId.OPF_067,
|
|
4058
|
+
message: `Resource "${manifestItem.href}" is referenced as a link but is also declared as a manifest item.`,
|
|
4059
|
+
location: { path: opfPath }
|
|
4060
|
+
});
|
|
4061
|
+
}
|
|
4062
|
+
}
|
|
3987
4063
|
}
|
|
3988
4064
|
}
|
|
3989
4065
|
/**
|
|
@@ -4265,6 +4341,56 @@ var OPFValidator = class {
|
|
|
4265
4341
|
}
|
|
4266
4342
|
}
|
|
4267
4343
|
}
|
|
4344
|
+
// Mirrors Java's PrefixDeclarationParser + VocabUtil.parsePrefixDeclaration,
|
|
4345
|
+
// but emits only the four main IDs (not Java's OPF-004a..f sub-codes).
|
|
4346
|
+
validatePrefixDeclarations(context, opfPath, opfXml) {
|
|
4347
|
+
const stripped = stripXmlComments(opfXml);
|
|
4348
|
+
const match = /<package[^>]*\sprefix\s*=\s*["']([^"']*)["']/.exec(stripped);
|
|
4349
|
+
if (!match) return;
|
|
4350
|
+
const raw = match[1] ?? "";
|
|
4351
|
+
if (raw !== raw.trim()) {
|
|
4352
|
+
pushMessage(context.messages, {
|
|
4353
|
+
id: MessageId.OPF_004,
|
|
4354
|
+
message: "The value of the prefix attribute has leading or trailing whitespace.",
|
|
4355
|
+
location: { path: opfPath }
|
|
4356
|
+
});
|
|
4357
|
+
}
|
|
4358
|
+
const parts = raw.trim().split(/\s+/).filter(Boolean);
|
|
4359
|
+
for (let i = 0; i < parts.length; ) {
|
|
4360
|
+
const token = parts[i] ?? "";
|
|
4361
|
+
if (token.endsWith(":") && token.length > 1) {
|
|
4362
|
+
const prefix = token.slice(0, -1);
|
|
4363
|
+
const uri = parts[i + 1];
|
|
4364
|
+
if (!uri || uri.endsWith(":")) {
|
|
4365
|
+
pushMessage(context.messages, {
|
|
4366
|
+
id: MessageId.OPF_005,
|
|
4367
|
+
message: `The prefix "${prefix}" is declared but no URI is bound to it.`,
|
|
4368
|
+
location: { path: opfPath }
|
|
4369
|
+
});
|
|
4370
|
+
i += 1;
|
|
4371
|
+
continue;
|
|
4372
|
+
}
|
|
4373
|
+
if (!isValidURI(uri)) {
|
|
4374
|
+
pushMessage(context.messages, {
|
|
4375
|
+
id: MessageId.OPF_006,
|
|
4376
|
+
message: `The value "${uri}" bound to prefix "${prefix}" is not a valid URI.`,
|
|
4377
|
+
location: { path: opfPath }
|
|
4378
|
+
});
|
|
4379
|
+
}
|
|
4380
|
+
const reservedUri = RESERVED_PREFIX_URIS[prefix];
|
|
4381
|
+
if (reservedUri !== void 0 && reservedUri !== uri) {
|
|
4382
|
+
pushMessage(context.messages, {
|
|
4383
|
+
id: MessageId.OPF_007,
|
|
4384
|
+
message: `The prefix "${prefix}" is reserved and must not be re-declared.`,
|
|
4385
|
+
location: { path: opfPath }
|
|
4386
|
+
});
|
|
4387
|
+
}
|
|
4388
|
+
i += 2;
|
|
4389
|
+
} else {
|
|
4390
|
+
i += 1;
|
|
4391
|
+
}
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4268
4394
|
/**
|
|
4269
4395
|
* RSC-005: all id attributes on elements in the OPF document must be unique.
|
|
4270
4396
|
* Mirrors Java's id-unique.sch / opf.sch opf_idAttrUnique pattern, which
|
|
@@ -4273,16 +4399,7 @@ var OPFValidator = class {
|
|
|
4273
4399
|
*/
|
|
4274
4400
|
validateMetaPrefixes(context, opfPath, opfXml) {
|
|
4275
4401
|
if (!this.packageDoc) return;
|
|
4276
|
-
const RESERVED =
|
|
4277
|
-
"dcterms",
|
|
4278
|
-
"marc",
|
|
4279
|
-
"onix",
|
|
4280
|
-
"schema",
|
|
4281
|
-
"xsd",
|
|
4282
|
-
"a11y",
|
|
4283
|
-
"media",
|
|
4284
|
-
"rendition"
|
|
4285
|
-
]);
|
|
4402
|
+
const RESERVED = new Set(Object.keys(RESERVED_PREFIX_URIS));
|
|
4286
4403
|
const declared = new Set(Object.keys(this.packageDoc.prefixes ?? {}));
|
|
4287
4404
|
const reported = /* @__PURE__ */ new Set();
|
|
4288
4405
|
const reportIfUndeclared = (prefix) => {
|
|
@@ -4489,6 +4606,26 @@ var OPFValidator = class {
|
|
|
4489
4606
|
}
|
|
4490
4607
|
}
|
|
4491
4608
|
}
|
|
4609
|
+
validatePageMap(context, opfPath, opfXml) {
|
|
4610
|
+
if (!this.packageDoc) return;
|
|
4611
|
+
const stripped = stripXmlComments(opfXml);
|
|
4612
|
+
const m = /<spine\b[^>]*\spage-map\s*=\s*["']([^"']*)["']/.exec(stripped);
|
|
4613
|
+
if (!m) return;
|
|
4614
|
+
const pageMapId = (m[1] ?? "").trim();
|
|
4615
|
+
pushMessage(context.messages, {
|
|
4616
|
+
id: MessageId.OPF_062,
|
|
4617
|
+
message: `Found Adobe page-map attribute on spine element (page-map="${pageMapId}")`,
|
|
4618
|
+
location: { path: opfPath }
|
|
4619
|
+
});
|
|
4620
|
+
if (!pageMapId) return;
|
|
4621
|
+
if (!this.manifestById.has(pageMapId)) {
|
|
4622
|
+
pushMessage(context.messages, {
|
|
4623
|
+
id: MessageId.OPF_063,
|
|
4624
|
+
message: `The Adobe page-map item "${pageMapId}" was not found in the manifest`,
|
|
4625
|
+
location: { path: opfPath }
|
|
4626
|
+
});
|
|
4627
|
+
}
|
|
4628
|
+
}
|
|
4492
4629
|
/**
|
|
4493
4630
|
* Validate fallback chains
|
|
4494
4631
|
*/
|
|
@@ -4960,7 +5097,7 @@ var SKMValidator = class {
|
|
|
4960
5097
|
const content = typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
4961
5098
|
let doc = null;
|
|
4962
5099
|
try {
|
|
4963
|
-
doc =
|
|
5100
|
+
doc = getXmlDocument().fromString(content);
|
|
4964
5101
|
} catch {
|
|
4965
5102
|
pushMessage(context.messages, {
|
|
4966
5103
|
id: MessageId.RSC_016,
|
|
@@ -5197,7 +5334,7 @@ var SMILValidator = class {
|
|
|
5197
5334
|
const content = typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
5198
5335
|
let doc = null;
|
|
5199
5336
|
try {
|
|
5200
|
-
doc =
|
|
5337
|
+
doc = getXmlDocument().fromString(content);
|
|
5201
5338
|
} catch {
|
|
5202
5339
|
pushMessage(context.messages, {
|
|
5203
5340
|
id: MessageId.RSC_016,
|
|
@@ -6107,7 +6244,7 @@ var ContentValidator = class {
|
|
|
6107
6244
|
const svgContent = new TextDecoder().decode(svgData);
|
|
6108
6245
|
let doc;
|
|
6109
6246
|
try {
|
|
6110
|
-
doc =
|
|
6247
|
+
doc = getXmlDocument().fromString(svgContent);
|
|
6111
6248
|
this.extractAndRegisterIDs(path, doc.root, registry);
|
|
6112
6249
|
} catch (e) {
|
|
6113
6250
|
pushMessage(context.messages, {
|
|
@@ -6125,7 +6262,7 @@ var ContentValidator = class {
|
|
|
6125
6262
|
const svgContent = new TextDecoder().decode(svgData);
|
|
6126
6263
|
let doc;
|
|
6127
6264
|
try {
|
|
6128
|
-
doc =
|
|
6265
|
+
doc = getXmlDocument().fromString(svgContent);
|
|
6129
6266
|
} catch {
|
|
6130
6267
|
return;
|
|
6131
6268
|
}
|
|
@@ -6171,7 +6308,7 @@ var ContentValidator = class {
|
|
|
6171
6308
|
const svgContent = new TextDecoder().decode(svgData);
|
|
6172
6309
|
let doc;
|
|
6173
6310
|
try {
|
|
6174
|
-
doc =
|
|
6311
|
+
doc = getXmlDocument().fromString(svgContent);
|
|
6175
6312
|
} catch {
|
|
6176
6313
|
return;
|
|
6177
6314
|
}
|
|
@@ -6521,7 +6658,7 @@ var ContentValidator = class {
|
|
|
6521
6658
|
}
|
|
6522
6659
|
let doc = null;
|
|
6523
6660
|
try {
|
|
6524
|
-
doc =
|
|
6661
|
+
doc = getXmlDocument().fromString(content);
|
|
6525
6662
|
} catch (error) {
|
|
6526
6663
|
if (error instanceof Error) {
|
|
6527
6664
|
const { message, line, column } = this.parseLibxmlError(error.message);
|
|
@@ -7212,6 +7349,9 @@ var ContentValidator = class {
|
|
|
7212
7349
|
const docDir = path.includes("/") ? path.substring(0, path.lastIndexOf("/")) : "";
|
|
7213
7350
|
const opfDir = context.opfPath?.includes("/") ? context.opfPath.substring(0, context.opfPath.lastIndexOf("/")) : "";
|
|
7214
7351
|
const tocAnchors = tocNav.find(".//html:a[@href]", HTML_NS);
|
|
7352
|
+
if (context.contentFeatures) {
|
|
7353
|
+
context.contentFeatures.tocLinkCount = (context.contentFeatures.tocLinkCount ?? 0) + tocAnchors.length;
|
|
7354
|
+
}
|
|
7215
7355
|
const tocLinks = [];
|
|
7216
7356
|
for (const anchor of tocAnchors) {
|
|
7217
7357
|
const href = this.getAttribute(anchor, "href")?.trim();
|
|
@@ -8334,6 +8474,10 @@ var ContentValidator = class {
|
|
|
8334
8474
|
if (!features.hasRDFa && root.get(".//*[@property]")) {
|
|
8335
8475
|
features.hasRDFa = true;
|
|
8336
8476
|
}
|
|
8477
|
+
if (context.options.profile === "edupub") {
|
|
8478
|
+
const sections = root.find(".//html:body//html:section", XHTML_NS);
|
|
8479
|
+
features.sectionCount = (features.sectionCount ?? 0) + sections.length;
|
|
8480
|
+
}
|
|
8337
8481
|
}
|
|
8338
8482
|
validateImages(context, path, root) {
|
|
8339
8483
|
const packageDoc = context.packageDocument;
|
|
@@ -8599,6 +8743,7 @@ var ContentValidator = class {
|
|
|
8599
8743
|
}
|
|
8600
8744
|
return Number.parseInt(el.name.substring(1), 10);
|
|
8601
8745
|
};
|
|
8746
|
+
const XmlElement = getXmlElement();
|
|
8602
8747
|
const directElementChildren = (parent) => {
|
|
8603
8748
|
const out = [];
|
|
8604
8749
|
let n = parent.firstChild;
|
|
@@ -10207,11 +10352,13 @@ function toJSONReport(result) {
|
|
|
10207
10352
|
2
|
|
10208
10353
|
);
|
|
10209
10354
|
}
|
|
10355
|
+
|
|
10356
|
+
// src/ncx/validator.ts
|
|
10210
10357
|
var NCXValidator = class {
|
|
10211
10358
|
validate(context, ncxContent, ncxPath, registry) {
|
|
10212
10359
|
let doc = null;
|
|
10213
10360
|
try {
|
|
10214
|
-
doc =
|
|
10361
|
+
doc = getXmlDocument().fromString(ncxContent);
|
|
10215
10362
|
} catch (error) {
|
|
10216
10363
|
if (error instanceof Error) {
|
|
10217
10364
|
pushMessage(context.messages, {
|
|
@@ -10269,6 +10416,13 @@ var NCXValidator = class {
|
|
|
10269
10416
|
});
|
|
10270
10417
|
return;
|
|
10271
10418
|
}
|
|
10419
|
+
if (uidContent !== uidContent.trim()) {
|
|
10420
|
+
pushMessage(context.messages, {
|
|
10421
|
+
id: MessageId.NCX_004,
|
|
10422
|
+
message: "NCX dtb:uid meta content has leading or trailing whitespace.",
|
|
10423
|
+
location: { path, line: uidElement.line }
|
|
10424
|
+
});
|
|
10425
|
+
}
|
|
10272
10426
|
context.ncxUid = uidContent.trim();
|
|
10273
10427
|
}
|
|
10274
10428
|
checkNavMap(context, root, path) {
|
|
@@ -10898,8 +11052,8 @@ var OCFValidator = class {
|
|
|
10898
11052
|
zip = ZipReader.open(context.data);
|
|
10899
11053
|
} catch (error) {
|
|
10900
11054
|
pushMessage(context.messages, {
|
|
10901
|
-
id: MessageId.
|
|
10902
|
-
message: `Failed to open EPUB
|
|
11055
|
+
id: MessageId.PKG_004,
|
|
11056
|
+
message: `Failed to open EPUB ZIP: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
10903
11057
|
});
|
|
10904
11058
|
return;
|
|
10905
11059
|
}
|
|
@@ -10933,8 +11087,8 @@ var OCFValidator = class {
|
|
|
10933
11087
|
const compressionInfo = zip.getMimetypeCompressionInfo();
|
|
10934
11088
|
if (compressionInfo === null) {
|
|
10935
11089
|
pushMessage(messages, {
|
|
10936
|
-
id: MessageId.
|
|
10937
|
-
message: "
|
|
11090
|
+
id: MessageId.PKG_003,
|
|
11091
|
+
message: "Unable to read EPUB file header, likely corrupted",
|
|
10938
11092
|
location: { path: "mimetype" }
|
|
10939
11093
|
});
|
|
10940
11094
|
return;
|
|
@@ -11961,11 +12115,11 @@ var RelaxNGValidator = class extends BaseSchemaValidator {
|
|
|
11961
12115
|
try {
|
|
11962
12116
|
const libxml2 = await import('libxml2-wasm');
|
|
11963
12117
|
const LibRelaxNGValidator = libxml2.RelaxNGValidator;
|
|
11964
|
-
const { XmlDocument
|
|
11965
|
-
const doc =
|
|
12118
|
+
const { XmlDocument } = libxml2;
|
|
12119
|
+
const doc = XmlDocument.fromString(xml);
|
|
11966
12120
|
try {
|
|
11967
12121
|
const schemaContent = await loadSchema(schemaPath);
|
|
11968
|
-
const schemaDoc =
|
|
12122
|
+
const schemaDoc = XmlDocument.fromString(schemaContent);
|
|
11969
12123
|
try {
|
|
11970
12124
|
const validator = LibRelaxNGValidator.fromDoc(schemaDoc);
|
|
11971
12125
|
try {
|
|
@@ -12175,6 +12329,7 @@ var EpubCheck = class _EpubCheck {
|
|
|
12175
12329
|
*/
|
|
12176
12330
|
async check(data, filename) {
|
|
12177
12331
|
const startTime = performance.now();
|
|
12332
|
+
await loadXmlEngine();
|
|
12178
12333
|
const context = {
|
|
12179
12334
|
data,
|
|
12180
12335
|
options: this.options,
|
|
@@ -12199,7 +12354,7 @@ var EpubCheck = class _EpubCheck {
|
|
|
12199
12354
|
await this.runPipeline(context);
|
|
12200
12355
|
} catch (error) {
|
|
12201
12356
|
pushMessage(context.messages, {
|
|
12202
|
-
id: MessageId.
|
|
12357
|
+
id: MessageId.PKG_008,
|
|
12203
12358
|
message: error instanceof Error ? error.message : "Unknown validation error"
|
|
12204
12359
|
});
|
|
12205
12360
|
} finally {
|
|
@@ -12216,6 +12371,7 @@ var EpubCheck = class _EpubCheck {
|
|
|
12216
12371
|
*/
|
|
12217
12372
|
async checkExpanded(files) {
|
|
12218
12373
|
const startTime = performance.now();
|
|
12374
|
+
await loadXmlEngine();
|
|
12219
12375
|
const context = {
|
|
12220
12376
|
data: new Uint8Array(0),
|
|
12221
12377
|
options: this.options,
|
|
@@ -12241,7 +12397,7 @@ var EpubCheck = class _EpubCheck {
|
|
|
12241
12397
|
await this.runPipeline(context);
|
|
12242
12398
|
} catch (error) {
|
|
12243
12399
|
pushMessage(context.messages, {
|
|
12244
|
-
id: MessageId.
|
|
12400
|
+
id: MessageId.PKG_008,
|
|
12245
12401
|
message: error instanceof Error ? error.message : "Unknown validation error"
|
|
12246
12402
|
});
|
|
12247
12403
|
} finally {
|
|
@@ -12260,6 +12416,7 @@ var EpubCheck = class _EpubCheck {
|
|
|
12260
12416
|
async checkSingleFile(data, filename) {
|
|
12261
12417
|
const startTime = performance.now();
|
|
12262
12418
|
const mode = this.options.mode;
|
|
12419
|
+
await loadXmlEngine();
|
|
12263
12420
|
const context = {
|
|
12264
12421
|
data: new Uint8Array(0),
|
|
12265
12422
|
options: this.options,
|
|
@@ -12308,7 +12465,7 @@ var EpubCheck = class _EpubCheck {
|
|
|
12308
12465
|
}
|
|
12309
12466
|
} catch (error) {
|
|
12310
12467
|
pushMessage(context.messages, {
|
|
12311
|
-
id: MessageId.
|
|
12468
|
+
id: MessageId.PKG_008,
|
|
12312
12469
|
message: error instanceof Error ? error.message : "Unknown validation error"
|
|
12313
12470
|
});
|
|
12314
12471
|
} finally {
|
|
@@ -12358,6 +12515,15 @@ var EpubCheck = class _EpubCheck {
|
|
|
12358
12515
|
const profile = context.options.profile;
|
|
12359
12516
|
const opfPath = context.opfPath ?? "";
|
|
12360
12517
|
if (profile === "edupub") {
|
|
12518
|
+
const sectionCount = features.sectionCount ?? 0;
|
|
12519
|
+
const tocLinkCount = features.tocLinkCount ?? 0;
|
|
12520
|
+
if (sectionCount > 0 && sectionCount !== tocLinkCount) {
|
|
12521
|
+
pushMessage(context.messages, {
|
|
12522
|
+
id: MessageId.NAV_004,
|
|
12523
|
+
message: "The Navigation Document should contain the full hierarchy of headings in the document for EDUPUB.",
|
|
12524
|
+
location: { path: opfPath }
|
|
12525
|
+
});
|
|
12526
|
+
}
|
|
12361
12527
|
if (features.hasPageBreak && !features.hasPageList) {
|
|
12362
12528
|
pushMessage(context.messages, {
|
|
12363
12529
|
id: MessageId.NAV_003,
|
|
@@ -12698,7 +12864,14 @@ var EpubCheck = class _EpubCheck {
|
|
|
12698
12864
|
message: "For maximum compatibility, use only lowercase characters for the EPUB file extension.",
|
|
12699
12865
|
location: { path: filename }
|
|
12700
12866
|
});
|
|
12867
|
+
return;
|
|
12701
12868
|
}
|
|
12869
|
+
const isEpub2 = context.version.startsWith("2");
|
|
12870
|
+
pushMessage(context.messages, {
|
|
12871
|
+
id: isEpub2 ? MessageId.PKG_017 : MessageId.PKG_024,
|
|
12872
|
+
message: `EPUB file has an uncommon extension "${extension}".`,
|
|
12873
|
+
location: { path: filename }
|
|
12874
|
+
});
|
|
12702
12875
|
}
|
|
12703
12876
|
/**
|
|
12704
12877
|
* Build a filtered report from validation context
|