From 5dc22db638c22515426a8d9a7efc3a53100e61dc Mon Sep 17 00:00:00 2001 From: productdevbook Date: Thu, 13 Aug 2026 17:31:36 +0200 Subject: [PATCH] =?UTF-8?q?test(xlsx):=20validate=20the=20package=20itself?= =?UTF-8?q?=20=E2=80=94=20content=20types,=20rels,=20core=20props?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last unchecked parts, and the ones whose failure is worst: a malformed `.rels` breaks the whole workbook rather than one sheet. All valid, across every relationship file in every document. 133 parts across eleven documents now. Their schemas are in ECMA-376 Part 2, which is a third download and — for `opc-coreProperties.xsd` — needs an edit before it will load at all. It imports the Dublin Core schemas over plain HTTP, dublincore.org answers with a 302 that Java does not follow, and the parser reads the string "302 Found" as schema content and gives up. The header carries the four commands that fetch them and make the imports relative. `--opc` is optional; without it the script behaves as before. Checked for teeth on this path specifically, since it is a new schema: renaming `` to `` in the content-types writer turns `[Content_Types].xml` red with the schema naming exactly what it expected. Only VML is left unchecked, and the reason stays recorded where the mapping is: `vml-main.xsd` does not load in `javax.xml.validation` at all. pnpm test green — 10,571 tests, 232 files. --- scripts/validate-ooxml.mjs | 44 +++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/scripts/validate-ooxml.mjs b/scripts/validate-ooxml.mjs index 1c2f8f4..73d3244 100644 --- a/scripts/validate-ooxml.mjs +++ b/scripts/validate-ooxml.mjs @@ -40,10 +40,30 @@ // unzip -o ECMA-376-4_5th_edition_december_2016.zip OfficeOpenXML-XMLSchema-Transitional.zip // unzip -o OfficeOpenXML-XMLSchema-Transitional.zip -d xsd-t // -// node scripts/validate-ooxml.mjs --schema xsd-t/sml.xsd +// node scripts/validate-ooxml.mjs --schema xsd-t/sml.xsd [--opc opc/] // // `--schema` must point at `sml.xsd` inside the extracted directory: it // imports its siblings by relative path. +// +// `--opc` is optional and covers the package itself — `[Content_Types].xml`, +// every `.rels`, and `docProps/core.xml`. Those schemas are in Part 2: +// +// curl -sSLO https://ecma-international.org/wp-content/uploads/ECMA-376-2_5th_edition_december_2021.zip +// unzip -o ECMA-376-2_5th_edition_december_2021.zip OpenPackagingConventions-XMLSchema.zip +// unzip -o OpenPackagingConventions-XMLSchema.zip -d opc +// +// `opc-coreProperties.xsd` then needs one edit before it will load at +// all: it imports the Dublin Core schemas over plain HTTP, and +// dublincore.org answers with a 302 that Java does not follow — the +// parser reads the string "302 Found" as schema content and gives up. +// Fetch them and make the imports relative: +// +// cd opc +// for f in dc dcterms dcmitype; do +// curl -sSLO "http://dublincore.org/schemas/xmls/qdc/2003/04/02/$f.xsd" +// done +// sed -i 's|http://dublincore.org/schemas/xmls/qdc/2003/04/02/||g' \ +// opc-coreProperties.xsd dc.xsd dcterms.xsd import { execFileSync } from "node:child_process" import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs" @@ -56,6 +76,7 @@ for (let i = 2; i < process.argv.length; i += 2) { args.set(process.argv[i].replace(/^--/, ""), process.argv[i + 1]) } const schema = args.get("schema") +const opc = args.get("opc") const dist = args.get("dist") ?? new URL("../dist/index.mjs", import.meta.url).href if (!schema) { @@ -262,15 +283,22 @@ function partsToCheck(pkg, schemaDir) { } else if (part === "docProps/app.xml") { out.push([part, join(schemaDir, "shared-documentPropertiesExtended.xsd")]) } + // The package itself, when the Part 2 schemas were given. A + // malformed `.rels` breaks the whole workbook rather than one sheet, + // so it is worth the extra download. + else if (opc && part === "[Content_Types].xml") { + out.push([part, join(opc, "opc-contentTypes.xsd")]) + } else if (opc && /(^|\/)_rels\/[^/]+\.rels$/.test(part)) { + out.push([part, join(opc, "opc-relationships.xsd")]) + } else if (opc && part === "docProps/core.xml") { + out.push([part, join(opc, "opc-coreProperties.xsd")]) + } // Not checked, and why: // - // [Content_Types].xml, *.rels, docProps/core.xml their schemas are - // in ECMA-376 Part 2 (OPC), a separate download from the Part 4 - // set this script asks for. - // xl/drawings/vmlDrawing*.vml `vml-main.xsd` - // does not load in `javax.xml.validation` at all — it throws - // before reading any document. VML is a legacy format with a - // famously loose schema, and this is not the place to fight it. + // xl/drawings/vmlDrawing*.vml `vml-main.xsd` does not load in + // `javax.xml.validation` at all — it throws before reading any + // document. VML is a legacy format with a famously loose schema, + // and this is not the place to fight it. } return out }