xml-toolkit 1.0.39 → 1.0.41

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/index.js CHANGED
@@ -1,21 +1,20 @@
1
- for (const name of [
2
-
3
- 'XMLLexer',
4
- 'XMLReader',
5
- 'SAXEvent',
6
- 'AttributesMap',
7
- 'MoxyLikeJsonEncoder',
8
- 'XMLNode',
9
- 'XMLSchemata',
10
- 'SOAP11',
11
- 'SOAP12',
12
- 'EntityResolver',
13
- 'XMLIterator',
14
- 'XMLParser',
15
- 'SOAPFault',
16
- 'SOAPEncoding',
17
-
18
- ]) module.exports [name] = require ('./lib/' + name)
1
+ module.exports = {
2
+ AttributesMap: require ('./lib/AttributesMap'),
3
+ EntityResolver: require ('./lib/EntityResolver'),
4
+ MoxyLikeJsonEncoder: require ('./lib/MoxyLikeJsonEncoder'),
5
+ SOAP11: require ('./lib/SOAP11'),
6
+ SOAP12: require ('./lib/SOAP12'),
7
+ SOAPEncoding: require ('./lib/SOAPEncoding'),
8
+ SOAPFault: require ('./lib/SOAPFault'),
9
+ SAXEvent: require ('./lib/SAXEvent'),
10
+ XMLIterator: require ('./lib/XMLIterator'),
11
+ XMLNode: require ('./lib/XMLNode'),
12
+ XMLLexer: require ('./lib/XMLLexer'),
13
+ XMLParser: require ('./lib/XMLParser'),
14
+ XMLPrinter: require ('./lib/XMLPrinter'),
15
+ XMLReader: require ('./lib/XMLReader'),
16
+ XMLSchemata: require ('./lib/XMLSchemata'),
17
+ }
19
18
 
20
19
  module.exports.SOAP = v => {switch (String (v)) {
21
20
 
package/lib/XMLNode.js CHANGED
@@ -1,4 +1,6 @@
1
+ const os = require ('os')
1
2
  const SAXEvent = require ('./SAXEvent.js')
3
+ const XMLPrinter = require ('./XMLPrinter.js')
2
4
  const AttributesMap = require ('./AttributesMap')
3
5
  const NamespacesMap = require ('./NamespacesMap')
4
6
  const MoxyLikeJsonEncoder = require ('./MoxyLikeJsonEncoder')
@@ -10,6 +12,19 @@ const PARENT = Symbol ('_parent')
10
12
  const LEVEL = Symbol ('_level')
11
13
  const NS_MAP = Symbol ('_ns_map')
12
14
 
15
+ const stringEscape = require ('string-escape-map')
16
+
17
+ const ESC = [
18
+ ['<', '&lt;'],
19
+ ['>', '&gt;'],
20
+ ['&', '&amp;'],
21
+ [String.fromCharCode (10), '&#xA;'],
22
+ [String.fromCharCode (13), '&#xD;'],
23
+ ]
24
+
25
+ const ESC_BODY = new stringEscape (ESC)
26
+ const ESC_ATTR = new stringEscape ([...ESC, ['"', '&quot;']])
27
+
13
28
  const m2o =
14
29
  Object.fromEntries ? m => Object.fromEntries (m.entries ()) :
15
30
  m => {let o = {}; for (const [k, v] of m.entries ()) o [k] = v; return o}
@@ -126,17 +141,27 @@ const XMLNode = class extends SAXEvent {
126
141
 
127
142
  }
128
143
 
129
- toString () {
144
+ toString (options, level = 0) {
130
145
 
131
- const {src} = this; if (this.type !== SAXEvent.TYPES.END_ELEMENT || this.isSelfEnclosed) return src
146
+ if (arguments.length === 0) return this.toSourceString ()
132
147
 
133
- let s = this.src
148
+ const xp = new XMLPrinter ({level, ...options})
134
149
 
135
- for (const child of this.children) s += child
150
+ xp.writeNode (this)
136
151
 
137
- s += this.xml
152
+ return xp.text
138
153
 
139
- return s
154
+ }
155
+
156
+ toSourceString () {
157
+
158
+ const {src} = this; if (this.type !== SAXEvent.TYPES.END_ELEMENT || this.isSelfEnclosed) return src
159
+
160
+ let s = this.src
161
+
162
+ for (const child of this.children) s += child.toSourceString ()
163
+
164
+ return s + this.xml
140
165
 
141
166
  }
142
167
 
@@ -0,0 +1,280 @@
1
+ const os = require ('os')
2
+ const stringEscape = require ('string-escape-map')
3
+ const SAXEvent = require ('./SAXEvent.js')
4
+
5
+ const CH_QQ = '"'.charCodeAt (0)
6
+ const CH_GT = '>'.charCodeAt (0)
7
+
8
+ const ESC = [
9
+
10
+ [ '"', '&quot;'],
11
+ [ '<', '&lt;'],
12
+ [ '>', '&gt;'],
13
+ [ '&', '&amp;'],
14
+
15
+ ['\n', '&#xA;'],
16
+ ['\r', '&#xD;'],
17
+
18
+ ]
19
+
20
+ const isSafeNonNegative = value => Number.isSafeInteger (value) && value >= 0
21
+
22
+ const XMLPrinter = class {
23
+
24
+ constructor (o = {}) {
25
+
26
+ this.level = o.level ?? 0
27
+
28
+ if (!isSafeNonNegative (this.level)) throw Error ('Invalid level: ' + o.level)
29
+
30
+ const setSpaceOption = name => {
31
+
32
+ const value = o [name] || 0
33
+
34
+ this [name] = (() => {
35
+
36
+ switch (typeof value) {
37
+
38
+ case 'string': return value
39
+
40
+ case 'number': if (isSafeNonNegative (value)) return ' '.repeat (value)
41
+
42
+ default: throw Error (`Invalid ${name}: ${value}`)
43
+
44
+ }
45
+
46
+ }) ()
47
+
48
+ }
49
+
50
+ setSpaceOption ('space')
51
+
52
+ if (this.space) {
53
+
54
+ setSpaceOption ('attrSpace')
55
+
56
+ this.EOL = o.EOL ?? os.EOL
57
+
58
+ if (typeof this.EOL !== 'string') throw Error ('Invalid EOL: ' + o.EOL)
59
+
60
+ }
61
+ else {
62
+
63
+ for (const name of ['attrSpace', 'EOL']) if (o [name] != null) throw Error (`Without .space, .${name} cannot be set`)
64
+
65
+ this.EOL = ''
66
+
67
+ }
68
+
69
+ this.encodeLineBreaks = o.encodeLineBreaks ?? true
70
+
71
+ {
72
+
73
+ const to = this.encodeLineBreaks ? Infinity : -2
74
+
75
+ this.ESC_BODY = new stringEscape (ESC.slice (1, to))
76
+ this.ESC_ATTR = new stringEscape (ESC.slice (0, to))
77
+
78
+ }
79
+
80
+ this.decl = o.decl
81
+
82
+ this.reset ()
83
+
84
+ }
85
+
86
+ reset () {
87
+
88
+ this.text = ''
89
+ this.stack = []
90
+ this.isOpening = false
91
+
92
+ const {decl} = this; if (decl) this.writeXMLDecl (decl)
93
+
94
+ return this
95
+
96
+ }
97
+
98
+ newLine (delta = 0) {
99
+
100
+ this.text += `${this.EOL}${this.space.repeat (this.level + this.stack.length + delta)}`
101
+
102
+ }
103
+
104
+ get lastCharCode () {
105
+
106
+ const {text} = this
107
+
108
+ return text.charCodeAt (text.length - 1)
109
+
110
+ }
111
+
112
+ openElement (name) {
113
+
114
+ if (this.stack.length !== 0) {
115
+ if (this.attrSpace && this.lastCharCode === CH_QQ) this.newLine (-1)
116
+ if (this.isOpening) this.text += `>`
117
+ }
118
+
119
+ if (this.space && this.text) this.newLine ()
120
+
121
+ this.text += `<${name}`
122
+ this.stack.push (name)
123
+
124
+ this.isOpening = true
125
+
126
+ return this
127
+
128
+ }
129
+
130
+ writeAttribute (name, value) {
131
+
132
+ if (!this.isOpening) throw Error ('No attribute allowed at this point')
133
+
134
+ if (typeof name !== 'string') throw Error ('The attribute name must be a string, not ' + (typeof name))
135
+
136
+ if (typeof value !== 'string') throw Error ('The attribute value must be a string, not ' + (typeof value))
137
+
138
+ if (this.attrSpace) {
139
+
140
+ this.newLine (-1)
141
+
142
+ this.text += this.attrSpace
143
+
144
+ }
145
+ else {
146
+
147
+ this.text += ' '
148
+
149
+ }
150
+
151
+ this.text += `${name}="${this.ESC_ATTR.escape (value)}"`
152
+
153
+ return this
154
+
155
+ }
156
+
157
+ writeCharacters (value) {
158
+
159
+ if (this.stack.length === 0) throw Error ('No element started yet')
160
+
161
+ if (typeof value !== 'string') throw Error ('The text content must be a string, not ' + (typeof value))
162
+
163
+ if (value.length === 0) return this
164
+
165
+ if (this.isOpening) {
166
+ if (this.attrSpace && this.lastCharCode === CH_QQ) this.newLine (-1)
167
+ this.text += `>`
168
+ this.isOpening = false
169
+ }
170
+
171
+ this.text += this.ESC_BODY.escape (value)
172
+
173
+ return this
174
+
175
+ }
176
+
177
+ closeElement () {
178
+
179
+ const name = this.stack.pop ()
180
+
181
+ if (this.isOpening) {
182
+ if (this.attrSpace && this.lastCharCode === CH_QQ) this.newLine ()
183
+ this.text += ` />`
184
+ this.isOpening = false
185
+ }
186
+ else {
187
+ if (this.space && this.lastCharCode === CH_GT) this.newLine ()
188
+ this.text += `</${name}>`
189
+ }
190
+
191
+ return this
192
+
193
+ }
194
+
195
+ writeNode (node) {
196
+
197
+ if (node.type !== SAXEvent.TYPES.END_ELEMENT) return this.writeCharacters (node.src)
198
+
199
+ this.openElement (node.name)
200
+
201
+ if (this.stack.length === 1) {
202
+
203
+ const {namespacesMap} = node; if (namespacesMap != null) {
204
+
205
+ let min = Infinity; for (const name of namespacesMap.keys ()) {
206
+
207
+ const {length} = name
208
+
209
+ if (min > length) min = length
210
+
211
+ }
212
+
213
+ min += 2
214
+
215
+ const prefixes = new Set ()
216
+
217
+ const checkName = name => {
218
+
219
+ if (name.length < min) return
220
+
221
+ const pos = name.indexOf (':'); if (pos === -1) return
222
+
223
+ const prefix = name.substring (0, pos); if (!namespacesMap.has (prefix)) return
224
+
225
+ prefixes.add (prefix)
226
+
227
+ }
228
+
229
+ const checkNames = n => {
230
+
231
+ checkName (n.name)
232
+
233
+ for (const [name, value] of n.attributes.entries ()) {
234
+ checkName (name)
235
+ checkName (value)
236
+ }
237
+
238
+ for (const child of n.children) if (child.type === SAXEvent.TYPES.END_ELEMENT) checkNames (child); else checkName (child.src)
239
+
240
+ }
241
+
242
+ checkNames (node)
243
+
244
+ for (const prefix of prefixes.values ())
245
+
246
+ this.writeAttribute ('xmlns:' + prefix, namespacesMap.get (prefix))
247
+
248
+ }
249
+
250
+ }
251
+
252
+ for (const [name, value] of node.attributes.entries ()) this.writeAttribute (name, value)
253
+
254
+ for (const child of node.children) this.writeNode (child)
255
+
256
+ this.closeElement ()
257
+
258
+ return this
259
+
260
+ }
261
+
262
+ writeXMLDecl ({encoding, standalone} = {}) {
263
+
264
+ if (this.stack.length !== 0) throw Error ('The document is already started')
265
+
266
+ this.text += '<?xml version="1.0"'
267
+
268
+ if (encoding != null) this.text += ` encoding="${encoding}"`
269
+
270
+ if (standalone != null) this.text += ` standalone="${standalone ? 'yes' : 'no'}"`
271
+
272
+ this.text += '?>'
273
+
274
+ return this
275
+
276
+ }
277
+
278
+ }
279
+
280
+ module.exports = XMLPrinter
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xml-toolkit",
3
- "version": "1.0.39",
3
+ "version": "1.0.41",
4
4
  "description": "XML parser, marshaller, SOAP adapter",
5
5
  "main": "index.js",
6
6
  "files": [