@xmldom/xmldom 0.8.4 → 0.8.6
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/CHANGELOG.md +18 -0
- package/lib/conventions.js +33 -0
- package/lib/dom.js +177 -36
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.8.6](https://github.com/xmldom/xmldom/compare/0.8.5...0.8.6)
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Properly check nodes before replacement [`#457`](https://github.com/xmldom/xmldom/pull/457) / [`#455`](https://github.com/xmldom/xmldom/issues/455) / [`#456`](https://github.com/xmldom/xmldom/issues/456)
|
|
12
|
+
|
|
13
|
+
Thank you, [@edemaine](https://github.com/edemaine), [@pedro-l9](https://github.com/pedro-l9), for your contributions
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [0.8.5](https://github.com/xmldom/xmldom/compare/0.8.4...0.8.5)
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- fix: Restore ES5 compatibility [`#452`](https://github.com/xmldom/xmldom/pull/452) / [`#453`](https://github.com/xmldom/xmldom/issues/453)
|
|
21
|
+
|
|
22
|
+
Thank you, [@fengxinming](https://github.com/fengxinming), for your contributions
|
|
23
|
+
|
|
24
|
+
|
|
7
25
|
## [0.8.4](https://github.com/xmldom/xmldom/compare/0.8.3...0.8.4)
|
|
8
26
|
|
|
9
27
|
### Fixed
|
package/lib/conventions.js
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Ponyfill for `Array.prototype.find` which is only available in ES6 runtimes.
|
|
5
|
+
*
|
|
6
|
+
* Works with anything that has a `length` property and index access properties, including NodeList.
|
|
7
|
+
*
|
|
8
|
+
* @template {unknown} T
|
|
9
|
+
* @param {Array<T> | ({length:number, [number]: T})} list
|
|
10
|
+
* @param {function (item: T, index: number, list:Array<T> | ({length:number, [number]: T})):boolean} predicate
|
|
11
|
+
* @param {Partial<Pick<ArrayConstructor['prototype'], 'find'>>?} ac `Array.prototype` by default,
|
|
12
|
+
* allows injecting a custom implementation in tests
|
|
13
|
+
* @returns {T | undefined}
|
|
14
|
+
*
|
|
15
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
|
|
16
|
+
* @see https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.find
|
|
17
|
+
*/
|
|
18
|
+
function find(list, predicate, ac) {
|
|
19
|
+
if (ac === undefined) {
|
|
20
|
+
ac = Array.prototype;
|
|
21
|
+
}
|
|
22
|
+
if (list && typeof ac.find === 'function') {
|
|
23
|
+
return ac.find.call(list, predicate);
|
|
24
|
+
}
|
|
25
|
+
for (var i = 0; i < list.length; i++) {
|
|
26
|
+
if (Object.prototype.hasOwnProperty.call(list, i)) {
|
|
27
|
+
var item = list[i];
|
|
28
|
+
if (predicate.call(undefined, item, i, list)) {
|
|
29
|
+
return item;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
3
35
|
/**
|
|
4
36
|
* "Shallow freezes" an object to render it immutable.
|
|
5
37
|
* Uses `Object.freeze` if available,
|
|
@@ -165,6 +197,7 @@ var NAMESPACE = freeze({
|
|
|
165
197
|
})
|
|
166
198
|
|
|
167
199
|
exports.assign = assign;
|
|
200
|
+
exports.find = find;
|
|
168
201
|
exports.freeze = freeze;
|
|
169
202
|
exports.MIME_TYPE = MIME_TYPE;
|
|
170
203
|
exports.NAMESPACE = NAMESPACE;
|
package/lib/dom.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
var conventions = require("./conventions");
|
|
2
2
|
|
|
3
|
+
var find = conventions.find;
|
|
3
4
|
var NAMESPACE = conventions.NAMESPACE;
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -176,14 +177,6 @@ NodeList.prototype = {
|
|
|
176
177
|
}
|
|
177
178
|
return buf.join('');
|
|
178
179
|
},
|
|
179
|
-
/**
|
|
180
|
-
* @private
|
|
181
|
-
* @param {function (Node):boolean} predicate
|
|
182
|
-
* @returns {Node | undefined}
|
|
183
|
-
*/
|
|
184
|
-
find: function (predicate) {
|
|
185
|
-
return Array.prototype.find.call(this, predicate);
|
|
186
|
-
},
|
|
187
180
|
/**
|
|
188
181
|
* @private
|
|
189
182
|
* @param {function (Node):boolean} predicate
|
|
@@ -475,7 +468,7 @@ Node.prototype = {
|
|
|
475
468
|
return _insertBefore(this,newChild,refChild);
|
|
476
469
|
},
|
|
477
470
|
replaceChild:function(newChild, oldChild){//raises
|
|
478
|
-
this
|
|
471
|
+
_insertBefore(this, newChild,oldChild, assertPreReplacementValidityInDocument);
|
|
479
472
|
if(oldChild){
|
|
480
473
|
this.removeChild(oldChild);
|
|
481
474
|
}
|
|
@@ -597,6 +590,7 @@ function _visitNode(node,callback){
|
|
|
597
590
|
|
|
598
591
|
|
|
599
592
|
function Document(){
|
|
593
|
+
this.ownerDocument = this;
|
|
600
594
|
}
|
|
601
595
|
|
|
602
596
|
function _onAddAttribute(doc,el,newAttr){
|
|
@@ -748,14 +742,41 @@ function isTextNode(node) {
|
|
|
748
742
|
*/
|
|
749
743
|
function isElementInsertionPossible(doc, child) {
|
|
750
744
|
var parentChildNodes = doc.childNodes || [];
|
|
751
|
-
if (
|
|
745
|
+
if (find(parentChildNodes, isElementNode) || isDocTypeNode(child)) {
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
var docTypeNode = find(parentChildNodes, isDocTypeNode);
|
|
749
|
+
return !(child && docTypeNode && parentChildNodes.indexOf(docTypeNode) > parentChildNodes.indexOf(child));
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Check if en element node can be inserted before `child`, or at the end if child is falsy,
|
|
754
|
+
* according to the presence and position of a doctype node on the same level.
|
|
755
|
+
*
|
|
756
|
+
* @param {Node} doc The document node
|
|
757
|
+
* @param {Node} child the node that would become the nextSibling if the element would be inserted
|
|
758
|
+
* @returns {boolean} `true` if an element can be inserted before child
|
|
759
|
+
* @private
|
|
760
|
+
* https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
|
|
761
|
+
*/
|
|
762
|
+
function isElementReplacementPossible(doc, child) {
|
|
763
|
+
var parentChildNodes = doc.childNodes || [];
|
|
764
|
+
|
|
765
|
+
function hasElementChildThatIsNotChild(node) {
|
|
766
|
+
return isElementNode(node) && node !== child;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (find(parentChildNodes, hasElementChildThatIsNotChild)) {
|
|
752
770
|
return false;
|
|
753
771
|
}
|
|
754
|
-
var docTypeNode =
|
|
772
|
+
var docTypeNode = find(parentChildNodes, isDocTypeNode);
|
|
755
773
|
return !(child && docTypeNode && parentChildNodes.indexOf(docTypeNode) > parentChildNodes.indexOf(child));
|
|
756
774
|
}
|
|
775
|
+
|
|
757
776
|
/**
|
|
758
777
|
* @private
|
|
778
|
+
* Steps 1-5 of the checks before inserting and before replacing a child are the same.
|
|
779
|
+
*
|
|
759
780
|
* @param {Node} parent the parent node to insert `node` into
|
|
760
781
|
* @param {Node} node the node to insert
|
|
761
782
|
* @param {Node=} child the node that should become the `nextSibling` of `node`
|
|
@@ -763,18 +784,26 @@ function isElementInsertionPossible(doc, child) {
|
|
|
763
784
|
* @throws DOMException for several node combinations that would create a DOM that is not well-formed.
|
|
764
785
|
* @throws DOMException if `child` is provided but is not a child of `parent`.
|
|
765
786
|
* @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
|
|
787
|
+
* @see https://dom.spec.whatwg.org/#concept-node-replace
|
|
766
788
|
*/
|
|
767
|
-
function
|
|
789
|
+
function assertPreInsertionValidity1to5(parent, node, child) {
|
|
790
|
+
// 1. If `parent` is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException.
|
|
768
791
|
if (!hasValidParentNodeType(parent)) {
|
|
769
792
|
throw new DOMException(HIERARCHY_REQUEST_ERR, 'Unexpected parent node type ' + parent.nodeType);
|
|
770
793
|
}
|
|
794
|
+
// 2. If `node` is a host-including inclusive ancestor of `parent`, then throw a "HierarchyRequestError" DOMException.
|
|
795
|
+
// not implemented!
|
|
796
|
+
// 3. If `child` is non-null and its parent is not `parent`, then throw a "NotFoundError" DOMException.
|
|
771
797
|
if (child && child.parentNode !== parent) {
|
|
772
798
|
throw new DOMException(NOT_FOUND_ERR, 'child not in parent');
|
|
773
799
|
}
|
|
774
800
|
if (
|
|
801
|
+
// 4. If `node` is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException.
|
|
775
802
|
!hasInsertableNodeType(node) ||
|
|
803
|
+
// 5. If either `node` is a Text node and `parent` is a document,
|
|
776
804
|
// the sax parser currently adds top level text nodes, this will be fixed in 0.9.0
|
|
777
805
|
// || (node.nodeType === Node.TEXT_NODE && parent.nodeType === Node.DOCUMENT_NODE)
|
|
806
|
+
// or `node` is a doctype and `parent` is not a document, then throw a "HierarchyRequestError" DOMException.
|
|
778
807
|
(isDocTypeNode(node) && parent.nodeType !== Node.DOCUMENT_NODE)
|
|
779
808
|
) {
|
|
780
809
|
throw new DOMException(
|
|
@@ -782,36 +811,137 @@ function _insertBefore(parent, node, child) {
|
|
|
782
811
|
'Unexpected node type ' + node.nodeType + ' for parent node type ' + parent.nodeType
|
|
783
812
|
);
|
|
784
813
|
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* @private
|
|
818
|
+
* Step 6 of the checks before inserting and before replacing a child are different.
|
|
819
|
+
*
|
|
820
|
+
* @param {Document} parent the parent node to insert `node` into
|
|
821
|
+
* @param {Node} node the node to insert
|
|
822
|
+
* @param {Node | undefined} child the node that should become the `nextSibling` of `node`
|
|
823
|
+
* @returns {Node}
|
|
824
|
+
* @throws DOMException for several node combinations that would create a DOM that is not well-formed.
|
|
825
|
+
* @throws DOMException if `child` is provided but is not a child of `parent`.
|
|
826
|
+
* @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
|
|
827
|
+
* @see https://dom.spec.whatwg.org/#concept-node-replace
|
|
828
|
+
*/
|
|
829
|
+
function assertPreInsertionValidityInDocument(parent, node, child) {
|
|
785
830
|
var parentChildNodes = parent.childNodes || [];
|
|
786
831
|
var nodeChildNodes = node.childNodes || [];
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
throw new DOMException(HIERARCHY_REQUEST_ERR, 'Element in fragment can not be inserted before doctype');
|
|
795
|
-
}
|
|
832
|
+
|
|
833
|
+
// DocumentFragment
|
|
834
|
+
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
835
|
+
var nodeChildElements = nodeChildNodes.filter(isElementNode);
|
|
836
|
+
// If node has more than one element child or has a Text node child.
|
|
837
|
+
if (nodeChildElements.length > 1 || find(nodeChildNodes, isTextNode)) {
|
|
838
|
+
throw new DOMException(HIERARCHY_REQUEST_ERR, 'More than one element or text in fragment');
|
|
796
839
|
}
|
|
797
|
-
if
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
840
|
+
// Otherwise, if `node` has one element child and either `parent` has an element child,
|
|
841
|
+
// `child` is a doctype, or `child` is non-null and a doctype is following `child`.
|
|
842
|
+
if (nodeChildElements.length === 1 && !isElementInsertionPossible(parent, child)) {
|
|
843
|
+
throw new DOMException(HIERARCHY_REQUEST_ERR, 'Element in fragment can not be inserted before doctype');
|
|
801
844
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
845
|
+
}
|
|
846
|
+
// Element
|
|
847
|
+
if (isElementNode(node)) {
|
|
848
|
+
// `parent` has an element child, `child` is a doctype,
|
|
849
|
+
// or `child` is non-null and a doctype is following `child`.
|
|
850
|
+
if (!isElementInsertionPossible(parent, child)) {
|
|
851
|
+
throw new DOMException(HIERARCHY_REQUEST_ERR, 'Only one element can be added and only after doctype');
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
// DocumentType
|
|
855
|
+
if (isDocTypeNode(node)) {
|
|
856
|
+
// `parent` has a doctype child,
|
|
857
|
+
if (find(parentChildNodes, isDocTypeNode)) {
|
|
858
|
+
throw new DOMException(HIERARCHY_REQUEST_ERR, 'Only one doctype is allowed');
|
|
859
|
+
}
|
|
860
|
+
var parentElementChild = find(parentChildNodes, isElementNode);
|
|
861
|
+
// `child` is non-null and an element is preceding `child`,
|
|
862
|
+
if (child && parentChildNodes.indexOf(parentElementChild) < parentChildNodes.indexOf(child)) {
|
|
863
|
+
throw new DOMException(HIERARCHY_REQUEST_ERR, 'Doctype can only be inserted before an element');
|
|
864
|
+
}
|
|
865
|
+
// or `child` is null and `parent` has an element child.
|
|
866
|
+
if (!child && parentElementChild) {
|
|
867
|
+
throw new DOMException(HIERARCHY_REQUEST_ERR, 'Doctype can not be appended since element is present');
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* @private
|
|
874
|
+
* Step 6 of the checks before inserting and before replacing a child are different.
|
|
875
|
+
*
|
|
876
|
+
* @param {Document} parent the parent node to insert `node` into
|
|
877
|
+
* @param {Node} node the node to insert
|
|
878
|
+
* @param {Node | undefined} child the node that should become the `nextSibling` of `node`
|
|
879
|
+
* @returns {Node}
|
|
880
|
+
* @throws DOMException for several node combinations that would create a DOM that is not well-formed.
|
|
881
|
+
* @throws DOMException if `child` is provided but is not a child of `parent`.
|
|
882
|
+
* @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
|
|
883
|
+
* @see https://dom.spec.whatwg.org/#concept-node-replace
|
|
884
|
+
*/
|
|
885
|
+
function assertPreReplacementValidityInDocument(parent, node, child) {
|
|
886
|
+
var parentChildNodes = parent.childNodes || [];
|
|
887
|
+
var nodeChildNodes = node.childNodes || [];
|
|
888
|
+
|
|
889
|
+
// DocumentFragment
|
|
890
|
+
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
891
|
+
var nodeChildElements = nodeChildNodes.filter(isElementNode);
|
|
892
|
+
// If `node` has more than one element child or has a Text node child.
|
|
893
|
+
if (nodeChildElements.length > 1 || find(nodeChildNodes, isTextNode)) {
|
|
894
|
+
throw new DOMException(HIERARCHY_REQUEST_ERR, 'More than one element or text in fragment');
|
|
895
|
+
}
|
|
896
|
+
// Otherwise, if `node` has one element child and either `parent` has an element child that is not `child` or a doctype is following `child`.
|
|
897
|
+
if (nodeChildElements.length === 1 && !isElementReplacementPossible(parent, child)) {
|
|
898
|
+
throw new DOMException(HIERARCHY_REQUEST_ERR, 'Element in fragment can not be inserted before doctype');
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
// Element
|
|
902
|
+
if (isElementNode(node)) {
|
|
903
|
+
// `parent` has an element child that is not `child` or a doctype is following `child`.
|
|
904
|
+
if (!isElementReplacementPossible(parent, child)) {
|
|
905
|
+
throw new DOMException(HIERARCHY_REQUEST_ERR, 'Only one element can be added and only after doctype');
|
|
813
906
|
}
|
|
814
907
|
}
|
|
908
|
+
// DocumentType
|
|
909
|
+
if (isDocTypeNode(node)) {
|
|
910
|
+
function hasDoctypeChildThatIsNotChild(node) {
|
|
911
|
+
return isDocTypeNode(node) && node !== child;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// `parent` has a doctype child that is not `child`,
|
|
915
|
+
if (find(parentChildNodes, hasDoctypeChildThatIsNotChild)) {
|
|
916
|
+
throw new DOMException(HIERARCHY_REQUEST_ERR, 'Only one doctype is allowed');
|
|
917
|
+
}
|
|
918
|
+
var parentElementChild = find(parentChildNodes, isElementNode);
|
|
919
|
+
// or an element is preceding `child`.
|
|
920
|
+
if (child && parentChildNodes.indexOf(parentElementChild) < parentChildNodes.indexOf(child)) {
|
|
921
|
+
throw new DOMException(HIERARCHY_REQUEST_ERR, 'Doctype can only be inserted before an element');
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* @private
|
|
928
|
+
* @param {Node} parent the parent node to insert `node` into
|
|
929
|
+
* @param {Node} node the node to insert
|
|
930
|
+
* @param {Node=} child the node that should become the `nextSibling` of `node`
|
|
931
|
+
* @returns {Node}
|
|
932
|
+
* @throws DOMException for several node combinations that would create a DOM that is not well-formed.
|
|
933
|
+
* @throws DOMException if `child` is provided but is not a child of `parent`.
|
|
934
|
+
* @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
|
|
935
|
+
*/
|
|
936
|
+
function _insertBefore(parent, node, child, _inDocumentAssertion) {
|
|
937
|
+
// To ensure pre-insertion validity of a node into a parent before a child, run these steps:
|
|
938
|
+
assertPreInsertionValidity1to5(parent, node, child);
|
|
939
|
+
|
|
940
|
+
// If parent is a document, and any of the statements below, switched on the interface node implements,
|
|
941
|
+
// are true, then throw a "HierarchyRequestError" DOMException.
|
|
942
|
+
if (parent.nodeType === Node.DOCUMENT_NODE) {
|
|
943
|
+
(_inDocumentAssertion || assertPreInsertionValidityInDocument)(parent, node, child);
|
|
944
|
+
}
|
|
815
945
|
|
|
816
946
|
var cp = node.parentNode;
|
|
817
947
|
if(cp){
|
|
@@ -919,6 +1049,17 @@ Document.prototype = {
|
|
|
919
1049
|
}
|
|
920
1050
|
return _removeChild(this,oldChild);
|
|
921
1051
|
},
|
|
1052
|
+
replaceChild: function (newChild, oldChild) {
|
|
1053
|
+
//raises
|
|
1054
|
+
_insertBefore(this, newChild, oldChild, assertPreReplacementValidityInDocument);
|
|
1055
|
+
newChild.ownerDocument = this;
|
|
1056
|
+
if (oldChild) {
|
|
1057
|
+
this.removeChild(oldChild);
|
|
1058
|
+
}
|
|
1059
|
+
if (isElementNode(newChild)) {
|
|
1060
|
+
this.documentElement = newChild;
|
|
1061
|
+
}
|
|
1062
|
+
},
|
|
922
1063
|
// Introduced in DOM Level 2:
|
|
923
1064
|
importNode : function(importedNode,deep){
|
|
924
1065
|
return importNode(this,importedNode,deep);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmldom/xmldom",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6",
|
|
4
4
|
"description": "A pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"w3c",
|
|
@@ -29,13 +29,15 @@
|
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
31
|
"lint": "eslint lib test",
|
|
32
|
+
"format": "prettier --write test",
|
|
32
33
|
"changelog": "auto-changelog --unreleased-only",
|
|
33
34
|
"start": "nodemon --watch package.json --watch lib --watch test --exec 'npm --silent run test && npm --silent run lint'",
|
|
34
35
|
"stryker": "stryker run",
|
|
35
36
|
"stryker:dry-run": "stryker run -m '' --reporters progress",
|
|
36
37
|
"test": "jest",
|
|
38
|
+
"testrelease": "npm test && eslint lib",
|
|
37
39
|
"version": "./changelog-has-version.sh",
|
|
38
|
-
"release": "np --no-yarn"
|
|
40
|
+
"release": "np --no-yarn --test-script testrelease --branch release-0.8.x patch"
|
|
39
41
|
},
|
|
40
42
|
"engines": {
|
|
41
43
|
"node": ">=10.0.0"
|