plist 2.0.0 → 3.0.1

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/lib/parse.js CHANGED
@@ -10,6 +10,11 @@ var DOMParser = require('xmldom').DOMParser;
10
10
 
11
11
  exports.parse = parse;
12
12
 
13
+ var TEXT_NODE = 3;
14
+ var CDATA_NODE = 4;
15
+ var COMMENT_NODE = 8;
16
+
17
+
13
18
  /**
14
19
  * We ignore raw text (usually whitespace), <!-- xml comments -->,
15
20
  * and raw CDATA nodes.
@@ -20,11 +25,34 @@ exports.parse = parse;
20
25
  */
21
26
 
22
27
  function shouldIgnoreNode (node) {
23
- return node.nodeType === 3 // text
24
- || node.nodeType === 8 // comment
25
- || node.nodeType === 4; // cdata
28
+ return node.nodeType === TEXT_NODE
29
+ || node.nodeType === COMMENT_NODE
30
+ || node.nodeType === CDATA_NODE;
31
+ }
32
+
33
+ /**
34
+ * Check if the node is empty. Some plist file has such node:
35
+ * <key />
36
+ * this node shoud be ignored.
37
+ *
38
+ * @see https://github.com/TooTallNate/plist.js/issues/66
39
+ * @param {Element} node
40
+ * @returns {Boolean}
41
+ * @api private
42
+ */
43
+ function isEmptyNode(node){
44
+ if(!node.childNodes || node.childNodes.length === 0) {
45
+ return true;
46
+ } else {
47
+ return false;
48
+ }
26
49
  }
27
50
 
51
+ function invariant(test, message) {
52
+ if (!test) {
53
+ throw new Error(message);
54
+ }
55
+ }
28
56
 
29
57
  /**
30
58
  * Parses a Plist XML string. Returns an Object.
@@ -36,9 +64,10 @@ function shouldIgnoreNode (node) {
36
64
 
37
65
  function parse (xml) {
38
66
  var doc = new DOMParser().parseFromString(xml);
39
- if (doc.documentElement.nodeName !== 'plist') {
40
- throw new Error('malformed document. First element should be <plist>');
41
- }
67
+ invariant(
68
+ doc.documentElement.nodeName === 'plist',
69
+ 'malformed document. First element should be <plist>'
70
+ );
42
71
  var plist = parsePlistXML(doc.documentElement);
43
72
 
44
73
  // the root <plist> node gets interpreted as an Array,
@@ -57,41 +86,59 @@ function parse (xml) {
57
86
  */
58
87
 
59
88
  function parsePlistXML (node) {
60
- var i, new_obj, key, val, new_arr, res, d;
89
+ var i, new_obj, key, val, new_arr, res, counter, type;
61
90
 
62
91
  if (!node)
63
92
  return null;
64
93
 
65
94
  if (node.nodeName === 'plist') {
66
95
  new_arr = [];
96
+ if (isEmptyNode(node)) {
97
+ return new_arr;
98
+ }
67
99
  for (i=0; i < node.childNodes.length; i++) {
68
- // ignore comment nodes (text)
69
100
  if (!shouldIgnoreNode(node.childNodes[i])) {
70
101
  new_arr.push( parsePlistXML(node.childNodes[i]));
71
102
  }
72
103
  }
73
104
  return new_arr;
74
-
75
105
  } else if (node.nodeName === 'dict') {
76
106
  new_obj = {};
77
107
  key = null;
108
+ counter = 0;
109
+ if (isEmptyNode(node)) {
110
+ return new_obj;
111
+ }
78
112
  for (i=0; i < node.childNodes.length; i++) {
79
- // ignore comment nodes (text)
80
- if (!shouldIgnoreNode(node.childNodes[i])) {
81
- if (key === null) {
82
- key = parsePlistXML(node.childNodes[i]);
83
- } else {
84
- new_obj[key] = parsePlistXML(node.childNodes[i]);
85
- key = null;
86
- }
113
+ if (shouldIgnoreNode(node.childNodes[i])) continue;
114
+ if (counter % 2 === 0) {
115
+ invariant(
116
+ node.childNodes[i].nodeName === 'key',
117
+ 'Missing key while parsing <dict/>.'
118
+ );
119
+ key = parsePlistXML(node.childNodes[i]);
120
+ } else {
121
+ invariant(
122
+ node.childNodes[i].nodeName !== 'key',
123
+ 'Unexpected key "'
124
+ + parsePlistXML(node.childNodes[i])
125
+ + '" while parsing <dict/>.'
126
+ );
127
+ new_obj[key] = parsePlistXML(node.childNodes[i]);
87
128
  }
129
+ counter += 1;
130
+ }
131
+ if (counter % 2 === 1) {
132
+ throw new Error('Missing value for "' + key + '" while parsing <dict/>');
88
133
  }
89
134
  return new_obj;
90
135
 
91
136
  } else if (node.nodeName === 'array') {
92
137
  new_arr = [];
138
+ if (isEmptyNode(node)) {
139
+ return new_arr;
140
+ }
93
141
  for (i=0; i < node.childNodes.length; i++) {
94
- // ignore comment nodes (text)
95
142
  if (!shouldIgnoreNode(node.childNodes[i])) {
96
143
  res = parsePlistXML(node.childNodes[i]);
97
144
  if (null != res) new_arr.push(res);
@@ -103,44 +150,60 @@ function parsePlistXML (node) {
103
150
  // TODO: what should we do with text types? (CDATA sections)
104
151
 
105
152
  } else if (node.nodeName === 'key') {
106
- res = '';
107
- if(node.childNodes[0]) {
108
- res = node.childNodes[0].nodeValue;
153
+ if (isEmptyNode(node)) {
154
+ return '';
109
155
  }
110
- return res;
111
-
156
+ return node.childNodes[0].nodeValue;
112
157
  } else if (node.nodeName === 'string') {
113
158
  res = '';
114
- for (d=0; d < node.childNodes.length; d++) {
115
- res += node.childNodes[d].nodeValue;
159
+ if (isEmptyNode(node)) {
160
+ return res;
161
+ }
162
+ for (i=0; i < node.childNodes.length; i++) {
163
+ var type = node.childNodes[i].nodeType;
164
+ if (type === TEXT_NODE || type === CDATA_NODE) {
165
+ res += node.childNodes[i].nodeValue;
166
+ }
116
167
  }
117
168
  return res;
118
169
 
119
170
  } else if (node.nodeName === 'integer') {
120
- // parse as base 10 integer
171
+ invariant(
172
+ !isEmptyNode(node),
173
+ 'Cannot parse "" as integer.'
174
+ );
121
175
  return parseInt(node.childNodes[0].nodeValue, 10);
122
176
 
123
177
  } else if (node.nodeName === 'real') {
178
+ invariant(
179
+ !isEmptyNode(node),
180
+ 'Cannot parse "" as real.'
181
+ );
124
182
  res = '';
125
- for (d=0; d < node.childNodes.length; d++) {
126
- if (node.childNodes[d].nodeType === 3) {
127
- res += node.childNodes[d].nodeValue;
183
+ for (i=0; i < node.childNodes.length; i++) {
184
+ if (node.childNodes[i].nodeType === TEXT_NODE) {
185
+ res += node.childNodes[i].nodeValue;
128
186
  }
129
187
  }
130
188
  return parseFloat(res);
131
189
 
132
190
  } else if (node.nodeName === 'data') {
133
191
  res = '';
134
- for (d=0; d < node.childNodes.length; d++) {
135
- if (node.childNodes[d].nodeType === 3) {
136
- res += node.childNodes[d].nodeValue.replace(/\s+/g, '');
192
+ if (isEmptyNode(node)) {
193
+ return Buffer.from(res, 'base64');
194
+ }
195
+ for (i=0; i < node.childNodes.length; i++) {
196
+ if (node.childNodes[i].nodeType === TEXT_NODE) {
197
+ res += node.childNodes[i].nodeValue.replace(/\s+/g, '');
137
198
  }
138
199
  }
139
-
140
- // decode base64 data to a Buffer instance
141
- return new Buffer(res, 'base64');
200
+ return Buffer.from(res, 'base64');
142
201
 
143
202
  } else if (node.nodeName === 'date') {
203
+ invariant(
204
+ !isEmptyNode(node),
205
+ 'Cannot parse "" as Date.'
206
+ )
144
207
  return new Date(node.childNodes[0].nodeValue);
145
208
 
146
209
  } else if (node.nodeName === 'true') {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "plist",
3
3
  "description": "Mac OS X Plist parser/builder for Node.js and browsers",
4
- "version": "2.0.0",
4
+ "version": "3.0.1",
5
5
  "author": "Nathan Rajlich <nathan@tootallnate.net>",
6
6
  "contributors": [
7
7
  "Hans Huebner <hans.huebner@gmail.com>",
@@ -25,17 +25,20 @@
25
25
  ],
26
26
  "main": "index.js",
27
27
  "dependencies": {
28
- "base64-js": "0.0.8",
29
- "xmlbuilder": "4.0.0",
28
+ "base64-js": "^1.2.3",
29
+ "xmlbuilder": "^9.0.7",
30
30
  "xmldom": "0.1.x"
31
31
  },
32
32
  "devDependencies": {
33
- "browserify": "12.0.1",
34
- "mocha": "2.3.3",
35
- "multiline": "1.0.2",
36
- "zuul": "3.7.2"
33
+ "browserify": "^16.1.1",
34
+ "mocha": "^5.0.4",
35
+ "multiline": "^1.0.2",
36
+ "zuul": "^3.10.1"
37
37
  },
38
38
  "scripts": {
39
39
  "test": "make test"
40
+ },
41
+ "engines": {
42
+ "node": ">=6"
40
43
  }
41
44
  }