Skip to content

Commit 85f4755

Browse files
committed
Remove XMLSerializer export
This changes the package to stop exporting the XMLSerializer class, since it was fairly useless anyway. Instead, the main module default exports the serialize() function directly, which is more straightforward and just as good for standalone consumers. jsdom can wrap serialize() in its own XMLSerializer implementation. The serialize() function was changed from taking a boolean second parameter to taking an options object. Additionally, it no longer catches exceptions and re-throws them as DOMExceptions using the domexception package; instead it throws Errors (for well-formedness) or TypeErrors (for misuse) directly. We also consolidate all the tests into a single file, as the division between them was fuzzy. Finally, the package now requires Node.js v10 or later.
1 parent e688049 commit 85f4755

17 files changed

+2015
-1729
lines changed

.eslintignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

.eslintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"no-empty-character-class": "error",
2626
"no-ex-assign": "error",
2727
"no-extra-boolean-cast": "error",
28-
"no-extra-parens": ["error", "all", { "conditionalAssign": false, "nestedBinaryExpressions": false, "returnAssign": false }],
28+
"no-extra-parens": ["error", "all", { "conditionalAssign": false, "nestedBinaryExpressions": false, "returnAssign": false, "enforceForNewInMemberExpressions": false }],
2929
"no-extra-semi": "error",
3030
"no-func-assign": "error",
3131
"no-inner-declarations": "off",

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
11
node_modules/
2-
3-
lib/utils.js
4-
lib/XMLSerializer.js

.travis.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
language: node_js
22
node_js:
3-
- 8
3+
- 10
4+
- 12
45
- stable
6+
7+
install:
8+
- yarn --frozen-lockfile
9+
510
script:
6-
- npm run lint
7-
- npm test
11+
- yarn lint
12+
- yarn test
13+
14+
branches:
15+
only:
16+
- master

README.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,40 @@
22

33
An XML serializer that follows the [W3C specification](https://w3c.github.io/DOM-Parsing/).
44

5-
This module is mostly used as an internal part of [jsdom](https://github.com/jsdom/jsdom). However, you can use it independently with some care. That isn't very well-documented yet, but hopefully the below sample can give you an idea.
5+
This package can be used in Node.js, as long as you feed it a DOM node, e.g. one produced by [jsdom](https://github.com/jsdom/jsdom).
66

77
## Basic usage
88

9+
Assume you have a DOM tree rooted at a node `node`. In Node.js, you could create this using [jsdom](https://github.com/jsdom/jsdom) as follows:
10+
911
```js
10-
const { XMLSerializer } = require("w3c-xmlserializer");
1112
const { JSDOM } = require("jsdom");
1213

1314
const { document } = new JSDOM().window;
14-
const XMLSerializer = XMLSerializer.interface;
15-
const serializer = new XMLSerializer();
16-
const doc = document.createElement("akomaNtoso");
15+
const node = document.createElement("akomaNtoso");
16+
```
17+
18+
Then, you use this package as follows:
19+
1720

18-
console.log(serializer.serializeToString(doc));
21+
```js
22+
const serialize = require("w3c-xmlserializer");
23+
24+
console.log(serialize(node));
1925
// => '<akomantoso xmlns="http://www.w3.org/1999/xhtml"></akomantoso>'
2026
```
27+
28+
## `requireWellFormed` option
29+
30+
By default the input DOM tree is not required to be "well-formed"; any given input will serialize to some output string. You can instead require well-formedness via
31+
32+
```js
33+
serialize(node, { requireWellFormed: true });
34+
```
35+
36+
which will cause `Error`s to be thrown when non-well-formed constructs are encountered. [Per the spec](https://w3c.github.io/DOM-Parsing/#dfn-require-well-formed), this largely is about imposing constraints on the names of elements, attributes, etc.
37+
38+
As a point of reference, on the web platform:
39+
40+
* The [`innerHTML` getter](https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml) uses the require-well-formed mode, i.e. trying to get the `innerHTML` of non-well-formed subtrees will throw.
41+
* The [`xhr.send()` method](https://xhr.spec.whatwg.org/#the-send()-method) does not require well-formedness, i.e. sending non-well-formed `Document`s will serialize and send them anyway.

lib/XMLSerializer-impl.js

Lines changed: 0 additions & 9 deletions
This file was deleted.

lib/index.js

Lines changed: 0 additions & 9 deletions
This file was deleted.

lib/serialization.js renamed to lib/serialize.js

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"use strict";
22

3-
const DOMException = require("domexception");
43
const xnv = require("xml-name-validator");
54

65
const attributeUtils = require("./attributes");
@@ -61,15 +60,15 @@ function recordNamespaceInformation(element, map, prefixMap) {
6160

6261
function serializeDocumentType(node, namespace, prefixMap, requireWellFormed) {
6362
if (requireWellFormed && !PUBID_CHAR.test(node.publicId)) {
64-
throw new Error("Node publicId is not well formed");
63+
throw new Error("Failed to serialize XML: document type node publicId is not well-formed.");
6564
}
6665

6766
if (
6867
requireWellFormed &&
6968
(!XML_CHAR.test(node.systemId) ||
7069
(node.systemId.includes('"') && node.systemId.includes("'")))
7170
) {
72-
throw new Error("Node systemId is not well formed");
71+
throw new Error("Failed to serialize XML: document type node systemId is not well-formed.");
7372
}
7473

7574
let markup = `<!DOCTYPE ${node.name}`;
@@ -94,13 +93,13 @@ function serializeProcessingInstruction(
9493
requireWellFormed &&
9594
(node.target.includes(":") || asciiCaseInsensitiveMatch(node.target, "xml"))
9695
) {
97-
throw new Error("Node target is not well formed");
96+
throw new Error("Failed to serialize XML: processing instruction node target is not well-formed.");
9897
}
9998
if (
10099
requireWellFormed &&
101100
(!XML_CHAR.test(node.data) || node.data.includes("?>"))
102101
) {
103-
throw new Error("Node data is not well formed");
102+
throw new Error("Failed to serialize XML: processing instruction node data is not well-formed.");
104103
}
105104
return `<?${node.target} ${node.data}?>`;
106105
}
@@ -113,7 +112,7 @@ function serializeDocument(
113112
refs
114113
) {
115114
if (requireWellFormed && node.documentElement === null) {
116-
throw new Error("Document does not have a document element");
115+
throw new Error("Failed to serialize XML: document does not have a document element.");
117116
}
118117
let serializedDocument = "";
119118
for (const child of node.childNodes) {
@@ -150,7 +149,7 @@ function serializeDocumentFragment(
150149

151150
function serializeText(node, namespace, prefixMap, requireWellFormed) {
152151
if (requireWellFormed && !XML_CHAR.test(node.data)) {
153-
throw new Error("Node data is not well formed");
152+
throw new Error("Failed to serialize XML: text node data is not well-formed.");
154153
}
155154

156155
return node.data
@@ -161,14 +160,14 @@ function serializeText(node, namespace, prefixMap, requireWellFormed) {
161160

162161
function serializeComment(node, namespace, prefixMap, requireWellFormed) {
163162
if (requireWellFormed && !XML_CHAR.test(node.data)) {
164-
throw new Error("Node data is not well formed");
163+
throw new Error("Failed to serialize XML: comment node data is not well-formed.");
165164
}
166165

167166
if (
168167
requireWellFormed &&
169168
(node.data.includes("--") || node.data.endsWith("-"))
170169
) {
171-
throw new Error("Found hyphens in illegal places");
170+
throw new Error("Failed to serialize XML: found hyphens in illegal places in comment node data.");
172171
}
173172
return `<!--${node.data}-->`;
174173
}
@@ -178,7 +177,7 @@ function serializeElement(node, namespace, prefixMap, requireWellFormed, refs) {
178177
requireWellFormed &&
179178
(node.localName.includes(":") || !xnv.name(node.localName))
180179
) {
181-
throw new Error("localName is not a valid XML name");
180+
throw new Error("Failed to serialize XML: element node localName is not a valid XML name.");
182181
}
183182
let markup = "<";
184183
let qualifiedName = "";
@@ -208,7 +207,7 @@ function serializeElement(node, namespace, prefixMap, requireWellFormed, refs) {
208207
let candidatePrefix = attributeUtils.preferredPrefixString(map, ns, prefix);
209208
if (prefix === "xmlns") {
210209
if (requireWellFormed) {
211-
throw new Error("Elements can't have xmlns prefix");
210+
throw new Error("Failed to serialize XML: element nodes can't have a prefix of \"xmlns\".");
212211
}
213212
candidatePrefix = "xmlns";
214213
}
@@ -359,21 +358,14 @@ function xmlSerialization(node, namespace, prefixMap, requireWellFormed, refs) {
359358
case NODE_TYPES.CDATA_SECTION_NODE:
360359
return serializeCDATASection(node);
361360
default:
362-
throw new TypeError("Only Nodes and Attr objects can be serialized");
361+
throw new TypeError("Failed to serialize XML: only Nodes can be serialized.");
363362
}
364363
}
365364

366-
module.exports.produceXMLSerialization = (root, requireWellFormed) => {
365+
module.exports = (root, { requireWellFormed = false } = {}) => {
367366
const namespacePrefixMap = Object.create(null);
368367
namespacePrefixMap["http://www.w3.org/XML/1998/namespace"] = ["xml"];
369-
try {
370-
return xmlSerialization(root, null, namespacePrefixMap, requireWellFormed, {
371-
prefixIndex: 1
372-
});
373-
} catch (e) {
374-
throw new DOMException(
375-
"Failed to serialize XML: " + e.message,
376-
"InvalidStateError"
377-
);
378-
}
368+
return xmlSerialization(root, null, namespacePrefixMap, requireWellFormed, {
369+
prefixIndex: 1
370+
});
379371
};

package.json

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,23 @@
1010
"version": "1.1.2",
1111
"license": "MIT",
1212
"dependencies": {
13-
"domexception": "^1.0.1",
14-
"webidl-conversions": "^4.0.2",
1513
"xml-name-validator": "^3.0.0"
1614
},
1715
"devDependencies": {
18-
"eslint": "^5.15.2",
19-
"jest": "^24.5.0",
20-
"jsdom": "^14.0.0",
21-
"webidl2js": "^9.2.0"
16+
"eslint": "^6.8.0",
17+
"jest": "^24.9.0",
18+
"jsdom": "^15.2.1"
2219
},
2320
"repository": "jsdom/w3c-xmlserializer",
2421
"files": [
2522
"lib/"
2623
],
27-
"main": "lib/index.js",
24+
"main": "lib/serialize.js",
2825
"scripts": {
29-
"prepare": "node scripts/convert-idl.js",
30-
"pretest": "node scripts/convert-idl.js",
3126
"test": "jest",
3227
"lint": "eslint ."
28+
},
29+
"engines": {
30+
"node": ">=10"
3331
}
3432
}

scripts/.eslintrc.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)