@amazon/vinyl-xml 1.0.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/dist/index.cjs +976 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.js +986 -0
- package/dist/index.js.map +7 -0
- package/dist/types/current/index.d.ts +14 -0
- package/dist/types/current/index.d.ts.map +1 -0
- package/dist/types/current/index.js +13 -0
- package/dist/types/current/tsconfig.tsbuildinfo +1 -0
- package/dist/types/current/xlink/actuateValidator.d.ts +3 -0
- package/dist/types/current/xlink/actuateValidator.d.ts.map +1 -0
- package/dist/types/current/xlink/actuateValidator.js +11 -0
- package/dist/types/current/xml/ParseXmlHandler.d.ts +56 -0
- package/dist/types/current/xml/ParseXmlHandler.d.ts.map +1 -0
- package/dist/types/current/xml/ParseXmlHandler.js +5 -0
- package/dist/types/current/xml/ParseXmlHandlerImpl.d.ts +49 -0
- package/dist/types/current/xml/ParseXmlHandlerImpl.d.ts.map +1 -0
- package/dist/types/current/xml/ParseXmlHandlerImpl.js +198 -0
- package/dist/types/current/xml/QName.d.ts +33 -0
- package/dist/types/current/xml/QName.d.ts.map +1 -0
- package/dist/types/current/xml/QName.js +19 -0
- package/dist/types/current/xml/XmlSchemaError.d.ts +15 -0
- package/dist/types/current/xml/XmlSchemaError.d.ts.map +1 -0
- package/dist/types/current/xml/XmlSchemaError.js +23 -0
- package/dist/types/current/xml/mappedXmlRules.d.ts +118 -0
- package/dist/types/current/xml/mappedXmlRules.d.ts.map +1 -0
- package/dist/types/current/xml/mappedXmlRules.js +122 -0
- package/dist/types/current/xml/mergeXmlRules.d.ts +62 -0
- package/dist/types/current/xml/mergeXmlRules.d.ts.map +1 -0
- package/dist/types/current/xml/mergeXmlRules.js +89 -0
- package/dist/types/current/xml/parseXml.d.ts +76 -0
- package/dist/types/current/xml/parseXml.d.ts.map +1 -0
- package/dist/types/current/xml/parseXml.js +305 -0
- package/dist/types/current/xml/stringifyXml.d.ts +67 -0
- package/dist/types/current/xml/stringifyXml.d.ts.map +1 -0
- package/dist/types/current/xml/stringifyXml.js +225 -0
- package/dist/types/current/xml/xmlRules.d.ts +207 -0
- package/dist/types/current/xml/xmlRules.d.ts.map +1 -0
- package/dist/types/current/xml/xmlRules.js +134 -0
- package/dist/types/current/xmlns/org/w3/1999/xlink.d.ts +7 -0
- package/dist/types/current/xmlns/org/w3/1999/xlink.d.ts.map +1 -0
- package/dist/types/current/xmlns/org/w3/1999/xlink.js +5 -0
- package/dist/types/current/xmlns/org/w3/2001/XmlSchema.d.ts +17 -0
- package/dist/types/current/xmlns/org/w3/2001/XmlSchema.d.ts.map +1 -0
- package/dist/types/current/xmlns/org/w3/2001/XmlSchema.js +5 -0
- package/dist/types/ts3.7/index.d.ts +14 -0
- package/dist/types/ts3.7/xlink/actuateValidator.d.ts +3 -0
- package/dist/types/ts3.7/xml/ParseXmlHandler.d.ts +56 -0
- package/dist/types/ts3.7/xml/ParseXmlHandlerImpl.d.ts +49 -0
- package/dist/types/ts3.7/xml/QName.d.ts +33 -0
- package/dist/types/ts3.7/xml/XmlSchemaError.d.ts +15 -0
- package/dist/types/ts3.7/xml/mappedXmlRules.d.ts +118 -0
- package/dist/types/ts3.7/xml/mergeXmlRules.d.ts +118 -0
- package/dist/types/ts3.7/xml/parseXml.d.ts +76 -0
- package/dist/types/ts3.7/xml/stringifyXml.d.ts +67 -0
- package/dist/types/ts3.7/xml/xmlRules.d.ts +207 -0
- package/dist/types/ts3.7/xmlns/org/w3/1999/xlink.d.ts +7 -0
- package/dist/types/ts3.7/xmlns/org/w3/2001/XmlSchema.d.ts +17 -0
- package/dist/types/ts4.1/index.d.ts +14 -0
- package/dist/types/ts4.1/xlink/actuateValidator.d.ts +3 -0
- package/dist/types/ts4.1/xml/ParseXmlHandler.d.ts +56 -0
- package/dist/types/ts4.1/xml/ParseXmlHandlerImpl.d.ts +49 -0
- package/dist/types/ts4.1/xml/QName.d.ts +33 -0
- package/dist/types/ts4.1/xml/XmlSchemaError.d.ts +15 -0
- package/dist/types/ts4.1/xml/mappedXmlRules.d.ts +118 -0
- package/dist/types/ts4.1/xml/mergeXmlRules.d.ts +118 -0
- package/dist/types/ts4.1/xml/parseXml.d.ts +77 -0
- package/dist/types/ts4.1/xml/stringifyXml.d.ts +67 -0
- package/dist/types/ts4.1/xml/xmlRules.d.ts +207 -0
- package/dist/types/ts4.1/xmlns/org/w3/1999/xlink.d.ts +7 -0
- package/dist/types/ts4.1/xmlns/org/w3/2001/XmlSchema.d.ts +17 -0
- package/dist/types/ts4.7/index.d.ts +14 -0
- package/dist/types/ts4.7/xlink/actuateValidator.d.ts +3 -0
- package/dist/types/ts4.7/xml/ParseXmlHandler.d.ts +56 -0
- package/dist/types/ts4.7/xml/ParseXmlHandlerImpl.d.ts +49 -0
- package/dist/types/ts4.7/xml/QName.d.ts +33 -0
- package/dist/types/ts4.7/xml/XmlSchemaError.d.ts +15 -0
- package/dist/types/ts4.7/xml/mappedXmlRules.d.ts +118 -0
- package/dist/types/ts4.7/xml/mergeXmlRules.d.ts +118 -0
- package/dist/types/ts4.7/xml/parseXml.d.ts +76 -0
- package/dist/types/ts4.7/xml/stringifyXml.d.ts +67 -0
- package/dist/types/ts4.7/xml/xmlRules.d.ts +207 -0
- package/dist/types/ts4.7/xmlns/org/w3/1999/xlink.d.ts +7 -0
- package/dist/types/ts4.7/xmlns/org/w3/2001/XmlSchema.d.ts +17 -0
- package/docs/XML_PARSING.md +210 -0
- package/package.json +82 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { plainObjectMergeRule, substitute, ValidationError, } from '@amazon/vinyl-util';
|
|
6
|
+
import { XmlRuleType, } from '../xml/xmlRules';
|
|
7
|
+
const locale = {
|
|
8
|
+
illegalRuleOverrideTypeError: 'Cannot override the rule {property} of type {sourceType} with a rule of type {overrideType}',
|
|
9
|
+
illegalQNameOverrideError: 'Cannot override the rule {property} with a different localPart or namespace',
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Merges the given rules into one ruleset.
|
|
13
|
+
*
|
|
14
|
+
* @param rules
|
|
15
|
+
* @return A new XmlRules map with the merged rules.
|
|
16
|
+
*/
|
|
17
|
+
export function mergeXmlRules(...rules) {
|
|
18
|
+
let finalRule = rules[0];
|
|
19
|
+
for (let i = 1; i < rules.length; i++) {
|
|
20
|
+
finalRule = plainObjectMergeRule.merge(finalRule, rules[i], mergeXmlRule);
|
|
21
|
+
}
|
|
22
|
+
return finalRule;
|
|
23
|
+
}
|
|
24
|
+
function mergeXmlRule(sourceA, sourceB, prop) {
|
|
25
|
+
if (sourceA == null)
|
|
26
|
+
return sourceB;
|
|
27
|
+
const sourceAIsLazy = typeof sourceA === 'function';
|
|
28
|
+
const sourceBIsLazy = typeof sourceB === 'function';
|
|
29
|
+
if (sourceAIsLazy || sourceBIsLazy) {
|
|
30
|
+
return () => {
|
|
31
|
+
const a = sourceAIsLazy ? sourceA() : sourceA;
|
|
32
|
+
const b = sourceBIsLazy ? sourceB() : sourceB;
|
|
33
|
+
return mergeXmlRule(a, b, prop);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (sourceA.type !== sourceB.type) {
|
|
37
|
+
throw new ValidationError(substitute(locale.illegalRuleOverrideTypeError, {
|
|
38
|
+
property: prop,
|
|
39
|
+
sourceType: sourceA.type,
|
|
40
|
+
overrideType: sourceB.type,
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
switch (sourceA.type) {
|
|
44
|
+
case XmlRuleType.ATTRIBUTE:
|
|
45
|
+
case XmlRuleType.ELEMENT:
|
|
46
|
+
case XmlRuleType.ELEMENTS: {
|
|
47
|
+
const b = sourceB;
|
|
48
|
+
if (sourceA.namespaceUri !== b.namespaceUri ||
|
|
49
|
+
(sourceA.localPart ?? prop) !== (b.localPart ?? prop)) {
|
|
50
|
+
throw new ValidationError(substitute(locale.illegalQNameOverrideError, {
|
|
51
|
+
property: prop,
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
switch (sourceA.type) {
|
|
58
|
+
case XmlRuleType.ATTRIBUTE: {
|
|
59
|
+
const b = sourceB;
|
|
60
|
+
return {
|
|
61
|
+
...b,
|
|
62
|
+
default: b.default ?? sourceA.default,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
case XmlRuleType.CHARACTERS:
|
|
66
|
+
return sourceB;
|
|
67
|
+
case XmlRuleType.ELEMENT: {
|
|
68
|
+
const b = sourceB;
|
|
69
|
+
return {
|
|
70
|
+
...b,
|
|
71
|
+
required: sourceA.required || b.required,
|
|
72
|
+
rules: mergeXmlRules(sourceA.rules, b.rules),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
case XmlRuleType.ELEMENTS: {
|
|
76
|
+
const b = sourceB;
|
|
77
|
+
const maxOccurs = sourceA.maxOccurs != null && b.maxOccurs != null
|
|
78
|
+
? Math.min(sourceA.maxOccurs, b.maxOccurs)
|
|
79
|
+
: (sourceA.maxOccurs ?? b.maxOccurs);
|
|
80
|
+
return {
|
|
81
|
+
...b,
|
|
82
|
+
minOccurs: Math.max(sourceA.minOccurs, b.minOccurs),
|
|
83
|
+
maxOccurs,
|
|
84
|
+
useEmptyArrays: sourceA.useEmptyArrays || b.useEmptyArrays,
|
|
85
|
+
rules: mergeXmlRules(sourceA.rules, b.rules),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type MutableDeep, StringReader } from '@amazon/vinyl-util';
|
|
2
|
+
import type { Uri } from '../xmlns/org/w3/2001/XmlSchema';
|
|
3
|
+
import type { Attributes, ParseXmlHandler } from './ParseXmlHandler';
|
|
4
|
+
import type { QName } from './QName';
|
|
5
|
+
import type { XmlElement } from './ParseXmlHandlerImpl';
|
|
6
|
+
export declare const XMLNS = "http://www.w3.org/2000/xmlns/";
|
|
7
|
+
/**
|
|
8
|
+
* Nested xml elements push current namespace information onto a stack.
|
|
9
|
+
*
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
export interface StackElement {
|
|
13
|
+
/**
|
|
14
|
+
* The qualified name of the current element on the stack. Used to validate expected closing
|
|
15
|
+
* element.
|
|
16
|
+
*/
|
|
17
|
+
readonly qName: string;
|
|
18
|
+
/**
|
|
19
|
+
* The (mutable) namespaces map for the current stack.
|
|
20
|
+
*/
|
|
21
|
+
readonly namespaceMap: Namespaces;
|
|
22
|
+
/**
|
|
23
|
+
* The default namespace.
|
|
24
|
+
*/
|
|
25
|
+
defaultNamespaceUri: Uri | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parses an XML document using a partial sax-style content handler.
|
|
29
|
+
* This allows for parsing without the entire document being read into memory first.
|
|
30
|
+
*
|
|
31
|
+
* XML Documents are mapped to data structures through content handlers instead of needing an
|
|
32
|
+
* intermediate model.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseXml<T>(str: string, handler: ParseXmlHandler<T>): T & XmlElement<T>;
|
|
35
|
+
/**
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
38
|
+
export type Namespaces = Map<string, Uri | null>;
|
|
39
|
+
/**
|
|
40
|
+
* Given that the cursor is at a ' or ", reads the sequence of characters until an end quote is
|
|
41
|
+
* reached.
|
|
42
|
+
* The returned unescaped string is the text within the quotes.
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
45
|
+
export declare function readAttributeValue(c: StringReader): string;
|
|
46
|
+
/**
|
|
47
|
+
* Reads until the end of the tag >
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
export declare function untilTagClose(c: StringReader): void;
|
|
51
|
+
/**
|
|
52
|
+
* Returns true if the char code is a valid xml name character.
|
|
53
|
+
* @see https://www.w3.org/TR/2008/REC-xml-20081126/#NT-NameChar
|
|
54
|
+
* @param charCode
|
|
55
|
+
* @private
|
|
56
|
+
*/
|
|
57
|
+
export declare function isNameChar(charCode: number): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Reads an xml {@link QName}.
|
|
60
|
+
* The {@link QName.namespaceUri} returned will be null; it must be separately looked up.
|
|
61
|
+
*
|
|
62
|
+
* @param reader The current string reader.
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
export declare function readQName(reader: StringReader): MutableDeep<QName>;
|
|
66
|
+
/**
|
|
67
|
+
* Reads a series of xml attributes, separated by whitespace.
|
|
68
|
+
* Example: `ns1:key1="value1" key2 key3='value3'`
|
|
69
|
+
* Note: consumes all whitespace before and after attributes series.
|
|
70
|
+
*
|
|
71
|
+
* @return Returns a map of attribute `qName:string` to `Attribute` objects.
|
|
72
|
+
*
|
|
73
|
+
* @private
|
|
74
|
+
*/
|
|
75
|
+
export declare function xmlAttributes(reader: StringReader, stackElement: StackElement): MutableDeep<Attributes>;
|
|
76
|
+
//# sourceMappingURL=parseXml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseXml.d.ts","sourceRoot":"","sources":["../../../../src/xml/parseXml.ts"],"names":[],"mappings":"AAKA,OAAO,EAIH,KAAK,WAAW,EAChB,YAAY,EACf,MAAM,oBAAoB,CAAA;AAE3B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAEvD,eAAO,MAAM,KAAK,kCAAkC,CAAA;AAapD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IAEtB;;OAEG;IACH,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAA;IAEjC;;OAEG;IACH,mBAAmB,EAAE,GAAG,GAAG,IAAI,CAAA;CAClC;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACtB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAC5B,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CA6HnB;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,CAAA;AAGhD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAM1D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI,CAYnD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAkCpD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,CAalE;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CACzB,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,YAAY,GAC3B,WAAW,CAAC,UAAU,CAAC,CA8BzB"}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { char, decodeEntities, last, StringReader, } from '@amazon/vinyl-util';
|
|
6
|
+
export const XMLNS = 'http://www.w3.org/2000/xmlns/';
|
|
7
|
+
const LT = char('<');
|
|
8
|
+
const GT = char('>');
|
|
9
|
+
const EQ = char('=');
|
|
10
|
+
const SLASH = char('/');
|
|
11
|
+
const COLON = char(':');
|
|
12
|
+
const QMARK = char('?');
|
|
13
|
+
const EXCL = char('!');
|
|
14
|
+
const APOS = char("'");
|
|
15
|
+
const QUOT = char('"');
|
|
16
|
+
const QUOTES = new Set([APOS, QUOT]);
|
|
17
|
+
/**
|
|
18
|
+
* Parses an XML document using a partial sax-style content handler.
|
|
19
|
+
* This allows for parsing without the entire document being read into memory first.
|
|
20
|
+
*
|
|
21
|
+
* XML Documents are mapped to data structures through content handlers instead of needing an
|
|
22
|
+
* intermediate model.
|
|
23
|
+
*/
|
|
24
|
+
export function parseXml(str, handler) {
|
|
25
|
+
const c = new StringReader(str);
|
|
26
|
+
let current = {
|
|
27
|
+
qName: '#document',
|
|
28
|
+
namespaceMap: new Map(),
|
|
29
|
+
defaultNamespaceUri: null,
|
|
30
|
+
};
|
|
31
|
+
const stack = [current];
|
|
32
|
+
/**
|
|
33
|
+
* Provides the namespace uri for the given prefix on the current stack.
|
|
34
|
+
* @param prefix
|
|
35
|
+
*/
|
|
36
|
+
const namespaceLookup = (prefix) => {
|
|
37
|
+
if (!prefix)
|
|
38
|
+
return current.defaultNamespaceUri;
|
|
39
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
40
|
+
const map = stack[i].namespaceMap;
|
|
41
|
+
if (map.has(prefix))
|
|
42
|
+
return stack[i].namespaceMap.get(prefix);
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
handler.startDocument();
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
c.err(e.message);
|
|
51
|
+
}
|
|
52
|
+
while (c.hasNext()) {
|
|
53
|
+
const charsStart = c.untilChar(LT);
|
|
54
|
+
if (charsStart < c.position) {
|
|
55
|
+
const chars = str.substring(charsStart, c.position);
|
|
56
|
+
try {
|
|
57
|
+
handler.textNode(chars);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
c.err(e.message);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!c.hasNext())
|
|
64
|
+
break; // eos
|
|
65
|
+
c.next(); // <
|
|
66
|
+
if (c.charIf(SLASH)) {
|
|
67
|
+
// </ prepare for ending the element.
|
|
68
|
+
c.white();
|
|
69
|
+
const name = readQName(c);
|
|
70
|
+
if (name.qName !== current.qName)
|
|
71
|
+
c.err(`Expected </${current.qName}> but was </${name.qName}>`);
|
|
72
|
+
stack.pop();
|
|
73
|
+
current = last(stack);
|
|
74
|
+
c.white();
|
|
75
|
+
c.readChar(GT);
|
|
76
|
+
try {
|
|
77
|
+
handler.endElement();
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
c.err(e.message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else if (c.charIf(QMARK)) {
|
|
84
|
+
// <? Processing instruction
|
|
85
|
+
untilTagClose(c);
|
|
86
|
+
}
|
|
87
|
+
else if (c.charIf(EXCL)) {
|
|
88
|
+
// <! Declaration
|
|
89
|
+
if (c.charIf(0x2d /* - */)) {
|
|
90
|
+
c.readChar(0x2d);
|
|
91
|
+
// <!-- Comment
|
|
92
|
+
c.untilString('-->');
|
|
93
|
+
c.read('-->');
|
|
94
|
+
}
|
|
95
|
+
else if (c.stringIf('[CDATA[')) {
|
|
96
|
+
// <![CDATA[
|
|
97
|
+
const charsStart = c.untilString(']]>');
|
|
98
|
+
try {
|
|
99
|
+
handler.cDataNode(str.substring(charsStart, c.position));
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
c.err(e.message);
|
|
103
|
+
}
|
|
104
|
+
c.read(']]>');
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// <! Declaration
|
|
108
|
+
untilTagClose(c);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// < Prepare for beginning the element
|
|
113
|
+
// Create a new stack element, inheriting the namespaces of the parent.
|
|
114
|
+
const name = readQName(c);
|
|
115
|
+
current = {
|
|
116
|
+
qName: name.qName,
|
|
117
|
+
namespaceMap: new Map(),
|
|
118
|
+
defaultNamespaceUri: current.defaultNamespaceUri,
|
|
119
|
+
};
|
|
120
|
+
stack.push(current);
|
|
121
|
+
const attributes = xmlAttributes(c, current);
|
|
122
|
+
// The namespaces can only be looked up after all attributes are parsed;
|
|
123
|
+
// a namespace prefix may be defined after it's used on the same element.
|
|
124
|
+
name.namespaceUri = namespaceLookup(name.prefix);
|
|
125
|
+
for (const attributesKey in attributes) {
|
|
126
|
+
const value = attributes[attributesKey];
|
|
127
|
+
const name = value.name;
|
|
128
|
+
// Attributes do not inherit the default namespace.
|
|
129
|
+
if (name.prefix && !name.namespaceUri) {
|
|
130
|
+
name.namespaceUri = namespaceLookup(name.prefix);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
handler.startElement(name, attributes);
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
c.err(e.message);
|
|
138
|
+
}
|
|
139
|
+
if (c.charIf(SLASH)) {
|
|
140
|
+
c.readChar(GT);
|
|
141
|
+
stack.pop();
|
|
142
|
+
current = last(stack);
|
|
143
|
+
// Closed tag <example/>
|
|
144
|
+
try {
|
|
145
|
+
handler.endElement();
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
c.err(e.message);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
c.readChar(GT);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
return handler.endDocument();
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
return c.err(e.message);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// noinspection GrazieInspection
|
|
164
|
+
/**
|
|
165
|
+
* Given that the cursor is at a ' or ", reads the sequence of characters until an end quote is
|
|
166
|
+
* reached.
|
|
167
|
+
* The returned unescaped string is the text within the quotes.
|
|
168
|
+
* @private
|
|
169
|
+
*/
|
|
170
|
+
export function readAttributeValue(c) {
|
|
171
|
+
const quote = c.next();
|
|
172
|
+
if (!QUOTES.has(quote))
|
|
173
|
+
c.err('Expected \' or "');
|
|
174
|
+
const value = c.substringUntil((charCode) => charCode === quote);
|
|
175
|
+
c.readChar(quote);
|
|
176
|
+
return decodeEntities(value);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Reads until the end of the tag >
|
|
180
|
+
* @private
|
|
181
|
+
*/
|
|
182
|
+
export function untilTagClose(c) {
|
|
183
|
+
while (true) {
|
|
184
|
+
c.until((c) => c === APOS || c === QUOT || c === GT);
|
|
185
|
+
const char = c.next();
|
|
186
|
+
if (QUOTES.has(char)) {
|
|
187
|
+
c.untilChar(char);
|
|
188
|
+
c.next();
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// >
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Returns true if the char code is a valid xml name character.
|
|
198
|
+
* @see https://www.w3.org/TR/2008/REC-xml-20081126/#NT-NameChar
|
|
199
|
+
* @param charCode
|
|
200
|
+
* @private
|
|
201
|
+
*/
|
|
202
|
+
export function isNameChar(charCode) {
|
|
203
|
+
if (charCode >= 0x61 /* a */) {
|
|
204
|
+
if (charCode >= 0xb7 /* · */) {
|
|
205
|
+
if (charCode >= 0x300) {
|
|
206
|
+
if (charCode >= 0x203f) {
|
|
207
|
+
return charCode <= 0x2040;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
return charCode <= 0x36f;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
return charCode === 0xb7; /* · */
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
return charCode <= 0x7a; /* z */
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
if (charCode >= 0x5f /* _ */) {
|
|
223
|
+
return charCode === 0x5f;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
if (charCode >= 0x41 /* A */) {
|
|
227
|
+
return charCode <= 0x5a; /* Z */
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
if (charCode >= 0x2d /* - */) {
|
|
231
|
+
if (charCode >= 0x30 /* 0 */) {
|
|
232
|
+
return charCode <= 0x39; /* 9 */
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
return charCode <= 0x2e; /* . */
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Reads an xml {@link QName}.
|
|
247
|
+
* The {@link QName.namespaceUri} returned will be null; it must be separately looked up.
|
|
248
|
+
*
|
|
249
|
+
* @param reader The current string reader.
|
|
250
|
+
* @private
|
|
251
|
+
*/
|
|
252
|
+
export function readQName(reader) {
|
|
253
|
+
const prefixOrLocalName = reader.substringWhile(isNameChar);
|
|
254
|
+
let prefix, localPart, name;
|
|
255
|
+
if (reader.charIf(COLON)) {
|
|
256
|
+
prefix = prefixOrLocalName;
|
|
257
|
+
localPart = reader.substringWhile(isNameChar);
|
|
258
|
+
name = `${prefix}:${localPart}`;
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
prefix = null;
|
|
262
|
+
localPart = prefixOrLocalName;
|
|
263
|
+
name = localPart;
|
|
264
|
+
}
|
|
265
|
+
return { prefix, localPart, qName: name, namespaceUri: null };
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Reads a series of xml attributes, separated by whitespace.
|
|
269
|
+
* Example: `ns1:key1="value1" key2 key3='value3'`
|
|
270
|
+
* Note: consumes all whitespace before and after attributes series.
|
|
271
|
+
*
|
|
272
|
+
* @return Returns a map of attribute `qName:string` to `Attribute` objects.
|
|
273
|
+
*
|
|
274
|
+
* @private
|
|
275
|
+
*/
|
|
276
|
+
export function xmlAttributes(reader, stackElement) {
|
|
277
|
+
const attributes = {};
|
|
278
|
+
while (true) {
|
|
279
|
+
reader.white();
|
|
280
|
+
if (!isNameChar(reader.peek))
|
|
281
|
+
break;
|
|
282
|
+
const name = readQName(reader);
|
|
283
|
+
reader.white();
|
|
284
|
+
let value = '';
|
|
285
|
+
if (reader.charIf(EQ)) {
|
|
286
|
+
reader.white();
|
|
287
|
+
value = readAttributeValue(reader);
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
// Attributes without values are valid in html, but not xml
|
|
291
|
+
}
|
|
292
|
+
if (name.prefix === 'xmlns') {
|
|
293
|
+
name.namespaceUri = XMLNS;
|
|
294
|
+
stackElement.namespaceMap.set(name.localPart, value === '' ? null : value);
|
|
295
|
+
}
|
|
296
|
+
else if (!name.prefix && name.localPart === 'xmlns') {
|
|
297
|
+
name.namespaceUri = XMLNS;
|
|
298
|
+
stackElement.defaultNamespaceUri = !value ? null : value;
|
|
299
|
+
}
|
|
300
|
+
if (attributes[name.qName] != null)
|
|
301
|
+
reader.err(`Attribute '${name.qName}' is already specified.`);
|
|
302
|
+
attributes[name.qName] = { name, value };
|
|
303
|
+
}
|
|
304
|
+
return attributes;
|
|
305
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type OptionalDeep } from '@amazon/vinyl-util';
|
|
2
|
+
import type { Uri } from '../xmlns/org/w3/2001/XmlSchema';
|
|
3
|
+
import type { MappedXmlRules } from './mappedXmlRules';
|
|
4
|
+
import type { ReservedXmlRuleKeys } from '../xml/xmlRules';
|
|
5
|
+
export interface StringifyOptions {
|
|
6
|
+
/**
|
|
7
|
+
* The indentation. Default: 4 spaces
|
|
8
|
+
*/
|
|
9
|
+
readonly indent: string;
|
|
10
|
+
/**
|
|
11
|
+
* The character(s) between lines. Default: \n
|
|
12
|
+
*/
|
|
13
|
+
readonly newline: string;
|
|
14
|
+
/**
|
|
15
|
+
* A map of namespace uri to prefix strings.
|
|
16
|
+
* If a namespace is not found, a prefix will be generated.
|
|
17
|
+
*/
|
|
18
|
+
readonly prefixMap: ReadonlyMap<Uri, string>;
|
|
19
|
+
/**
|
|
20
|
+
* If true, will include the XML declaration at the beginning of the stringified document.
|
|
21
|
+
* `<?xml version="1.0"?>`
|
|
22
|
+
*/
|
|
23
|
+
readonly includeXmlDeclaration: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare class XmlWriter<T> {
|
|
26
|
+
private readonly rules;
|
|
27
|
+
private readonly options;
|
|
28
|
+
private readonly usedPrefixes;
|
|
29
|
+
private readonly traversed;
|
|
30
|
+
constructor(rules: MappedXmlRules<T>, options?: Partial<StringifyOptions>);
|
|
31
|
+
stringify(object: OptionalDeep<T, ReservedXmlRuleKeys>): string;
|
|
32
|
+
private qName;
|
|
33
|
+
/**
|
|
34
|
+
* Stringifies an element.
|
|
35
|
+
*
|
|
36
|
+
* @param object
|
|
37
|
+
* @param rule
|
|
38
|
+
* @param state
|
|
39
|
+
*/
|
|
40
|
+
private stringifyElement;
|
|
41
|
+
/**
|
|
42
|
+
* Stringifies an element's attributes.
|
|
43
|
+
*
|
|
44
|
+
* @param object
|
|
45
|
+
* @param attributes
|
|
46
|
+
* @return Returns the attributes string. This will include a leading space if there are any
|
|
47
|
+
* found attributes.
|
|
48
|
+
*/
|
|
49
|
+
private stringifyAttributes;
|
|
50
|
+
/**
|
|
51
|
+
* Stringifies a property as a CDATA node.
|
|
52
|
+
*
|
|
53
|
+
* @param object
|
|
54
|
+
* @param charactersRule
|
|
55
|
+
*/
|
|
56
|
+
private stringifyCharacters;
|
|
57
|
+
/**
|
|
58
|
+
* Stringifies an element's child elements.
|
|
59
|
+
*
|
|
60
|
+
* @param object
|
|
61
|
+
* @param elements
|
|
62
|
+
* @param state
|
|
63
|
+
*/
|
|
64
|
+
private stringifyChildren;
|
|
65
|
+
}
|
|
66
|
+
export declare function stringifyXml<T>(object: OptionalDeep<T, ReservedXmlRuleKeys>, rules: MappedXmlRules<T>, options?: Partial<StringifyOptions>): string;
|
|
67
|
+
//# sourceMappingURL=stringifyXml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stringifyXml.d.ts","sourceRoot":"","sources":["../../../../src/xml/stringifyXml.ts"],"names":[],"mappings":"AAKA,OAAO,EAIH,KAAK,YAAY,EAEpB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AACxD,OAAO,KAAK,EAIR,cAAc,EACjB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAazD,MAAM,WAAW,gBAAgB;IAC7B;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IAEvB;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IAExB;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IAE5C;;;OAGG;IACH,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAA;CAC1C;AASD,qBAAa,SAAS,CAAC,CAAC;IAMhB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAL1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyB;IACtD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;gBAGjB,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EACzC,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC;IAQvC,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,EAAE,mBAAmB,CAAC,GAAG,MAAM;IAY/D,OAAO,CAAC,KAAK;IAcb;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IA2DxB;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IA+B3B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAU3B;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;CA0D5B;AAcD,wBAAgB,YAAY,CAAC,CAAC,EAC1B,MAAM,EAAE,YAAY,CAAC,CAAC,EAAE,mBAAmB,CAAC,EAC5C,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EACxB,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GACpC,MAAM,CAGR"}
|