diff --git a/.codeclimate.yml b/.codeclimate.yml index 584e294a..31d139e9 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -3,23 +3,23 @@ engines: enabled: true config: languages: - - ruby - - javascript - - python - - php + - ruby + - javascript + - python + - php eslint: enabled: true fixme: enabled: true ratings: paths: - - "**.inc" - - "**.js" - - "**.jsx" - - "**.module" - - "**.php" - - "**.py" - - "**.rb" + - "**.inc" + - "**.js" + - "**.jsx" + - "**.module" + - "**.php" + - "**.py" + - "**.rb" exclude_paths: - spec/ - lib/**/* diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..021cf318 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "useTabs": false, + "printWidth": 120, + "tabWidth": 2, + "singleQuote": true, + "trailingComma": "es5", + "semi": true, + "arrowParens": "always", + "parser": "babylon", + "bracketSpacing": false +} diff --git a/.travis.yml b/.travis.yml index 68d4b30e..991e648b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,8 @@ cache: directories: - "node_modules" addons: - code_climate: - repo_token: ca31e4b1924e441790988993f307af5359d99d164d8308c39f81c81b8121f30f + code_climate: + repo_token: ca31e4b1924e441790988993f307af5359d99d164d8308c39f81c81b8121f30f after_success: - npm install -g codeclimate-test-reporter - npm install -g coveralls diff --git a/cli.js b/cli.js index 89eb9e13..6592061a 100755 --- a/cli.js +++ b/cli.js @@ -1,100 +1,100 @@ #!/usr/bin/env node -"use strict"; +'use strict'; /*eslint-disable no-console*/ -const fs = require("fs"); -const path = require("path"); -const parser = require("./src/parser"); -const readToEnd = require("./src/read").readToEnd; +const fs = require('fs'); +const path = require('path'); +const parser = require('./src/parser'); +const readToEnd = require('./src/read').readToEnd; -if (process.argv[2] === "--help" || process.argv[2] === "-h") { - console.log("Fast XML Parser " + require(path.join(__dirname + "/package.json")).version); - console.log("----------------"); - console.log("xml2js [-ns|-a|-c|-v|-V] [-o outputfile.json]"); - console.log("cat xmlfile.xml | xml2js [-ns|-a|-c|-v|-V] [-o outputfile.json]"); - console.log("-ns: remove namespace from tag and atrribute name."); - console.log("-a: don't parse attributes."); - console.log("-c: parse values to premitive type."); - console.log("-v: validate before parsing."); - console.log("-V: validate only."); -} else if (process.argv[2] === "--version") { - console.log(require(path.join(__dirname + "/package.json")).version); +if (process.argv[2] === '--help' || process.argv[2] === '-h') { + console.log('Fast XML Parser ' + require(path.join(__dirname + '/package.json')).version); + console.log('----------------'); + console.log('xml2js [-ns|-a|-c|-v|-V] [-o outputfile.json]'); + console.log('cat xmlfile.xml | xml2js [-ns|-a|-c|-v|-V] [-o outputfile.json]'); + console.log('-ns: remove namespace from tag and atrribute name.'); + console.log("-a: don't parse attributes."); + console.log('-c: parse values to premitive type.'); + console.log('-v: validate before parsing.'); + console.log('-V: validate only.'); +} else if (process.argv[2] === '--version') { + console.log(require(path.join(__dirname + '/package.json')).version); } else { - const options = { - ignoreNameSpace: true, - ignoreAttributes: false, - parseNodeValue: true, - parseAttributeValue: true - }; - let fileName = ""; - let outputFileName; - let validate = false; - let validateOnly = false; - for (let i = 2; i < process.argv.length; i++) { - if (process.argv[i] === "-ns") { - options.ignoreNameSpace = false; - } else if (process.argv[i] === "-a") { - options.ignoreAttributes = true; - } else if (process.argv[i] === "-c") { - options.parseNodeValue = false; - options.parseAttributeValue = false; - } else if (process.argv[i] === "-o") { - outputFileName = process.argv[++i]; - } else if (process.argv[i] === "-v") { - validate = true; - } else if (process.argv[i] === "-V") { - validateOnly = true; - } else {//filename - fileName = process.argv[i]; - } + const options = { + ignoreNameSpace: true, + ignoreAttributes: false, + parseNodeValue: true, + parseAttributeValue: true, + }; + let fileName = ''; + let outputFileName; + let validate = false; + let validateOnly = false; + for (let i = 2; i < process.argv.length; i++) { + if (process.argv[i] === '-ns') { + options.ignoreNameSpace = false; + } else if (process.argv[i] === '-a') { + options.ignoreAttributes = true; + } else if (process.argv[i] === '-c') { + options.parseNodeValue = false; + options.parseAttributeValue = false; + } else if (process.argv[i] === '-o') { + outputFileName = process.argv[++i]; + } else if (process.argv[i] === '-v') { + validate = true; + } else if (process.argv[i] === '-V') { + validateOnly = true; + } else { + //filename + fileName = process.argv[i]; } - const callback = function(xmlData) { - let output = ""; - if (validate) { - const result = parser.validate(xmlData); - if (result === true) { - output = JSON.stringify(parser.parse(xmlData, options), null, 4); - } else { - output = result; - } - } else if (validateOnly) { - output = parser.validate(xmlData); - } else { - output = JSON.stringify(parser.parse(xmlData, options), null, 4); - } - if (outputFileName) { - writeToFile(outputFileName, output); - } else { - console.log(output); - } - }; + } + const callback = function(xmlData) { + let output = ''; + if (validate) { + const result = parser.validate(xmlData); + if (result === true) { + output = JSON.stringify(parser.parse(xmlData, options), null, 4); + } else { + output = result; + } + } else if (validateOnly) { + output = parser.validate(xmlData); + } else { + output = JSON.stringify(parser.parse(xmlData, options), null, 4); + } + if (outputFileName) { + writeToFile(outputFileName, output); + } else { + console.log(output); + } + }; - try { - if (!fileName) { - readToEnd(process.stdin, function(err, data) { - if (err) { - throw err; - } - callback(data.toString()); - }); - } else { - fs.readFile(fileName, function(err, data) { - if (err) { - throw err; - } - callback(data.toString()); - }); + try { + if (!fileName) { + readToEnd(process.stdin, function(err, data) { + if (err) { + throw err; } + callback(data.toString()); + }); + } else { + fs.readFile(fileName, function(err, data) { + if (err) { + throw err; + } + callback(data.toString()); + }); } - catch (e) { - console.log("Seems an invalid file or stream." + e); - } + } catch (e) { + console.log('Seems an invalid file or stream.' + e); + } } function writeToFile(fileName, data) { - fs.writeFile(fileName, data, function(err) { - if (err) { - throw err; - } - console.log('JSON output has been written to ' + fileName); - }); + fs.writeFile(fileName, data, function(err) { + if (err) { + throw err; + } + console.log('JSON output has been written to ' + fileName); + }); } diff --git a/package.json b/package.json index b7db4cd0..76f3a8ae 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "bundle": "webpack && webpack --config webpack-prod.config.js", "coverage": "istanbul cover -x \"cli.js\" -x \"spec/*spec.js\" jasmine spec/*spec.js;", "coverage:check": "istanbul check-coverage --branch 90 --statement 90", - "postinstall": "node tasks/postinstall.js" + "postinstall": "node tasks/postinstall.js", + "prettier": "prettier --write src/**/*.js" }, "bin": { "xml2js": "./cli.js" @@ -78,6 +79,7 @@ "istanbul": "^0.4.5", "jasmine": "^3.3.0", "portfinder": "^1.0.19", + "prettier": "^1.15.3", "webpack": "^4.17.3", "webpack-cli": "^3.1.2", "xml2js": "^0.4.19", diff --git a/src/json2xml.js b/src/json2xml.js index 52b336b4..a2fbfefb 100644 --- a/src/json2xml.js +++ b/src/json2xml.js @@ -1,229 +1,262 @@ -"use strict"; +'use strict'; //parse Empty Node as self closing node -const buildOptions = require("./util").buildOptions; +const buildOptions = require('./util').buildOptions; const defaultOptions = { - attributeNamePrefix: "@_", - attrNodeName: false, - textNodeName: "#text", - ignoreAttributes: true, - cdataTagName: false, - cdataPositionChar: "\\c", - format: false, - indentBy: " ", - supressEmptyNode: false, - tagValueProcessor: function(a) {return a}, - attrValueProcessor: function(a) {return a} + attributeNamePrefix: '@_', + attrNodeName: false, + textNodeName: '#text', + ignoreAttributes: true, + cdataTagName: false, + cdataPositionChar: '\\c', + format: false, + indentBy: ' ', + supressEmptyNode: false, + tagValueProcessor: function(a) { + return a; + }, + attrValueProcessor: function(a) { + return a; + }, }; const props = [ - "attributeNamePrefix", - "attrNodeName", - "textNodeName", - "ignoreAttributes", - "cdataTagName", - "cdataPositionChar", - "format", - "indentBy", - "supressEmptyNode", - "tagValueProcessor", - "attrValueProcessor" -] + 'attributeNamePrefix', + 'attrNodeName', + 'textNodeName', + 'ignoreAttributes', + 'cdataTagName', + 'cdataPositionChar', + 'format', + 'indentBy', + 'supressEmptyNode', + 'tagValueProcessor', + 'attrValueProcessor', +]; function Parser(options) { - this.options = buildOptions(options,defaultOptions,props); - if (this.options.ignoreAttributes || this.options.attrNodeName) { - this.isAttribute = function(/*a*/) { return false;}; - } else { - this.attrPrefixLen = this.options.attributeNamePrefix.length; - this.isAttribute = isAttribute; - } - if (this.options.cdataTagName) { - this.isCDATA = isCDATA; - } else { - this.isCDATA = function(/*a*/) { return false;}; - } - this.replaceCDATAstr = replaceCDATAstr; - this.replaceCDATAarr = replaceCDATAarr; - - if (this.options.format) { - this.indentate = indentate; - this.tagEndChar = ">\n"; - this.newLine = "\n"; - } else { - this.indentate = function() { return "";}; - this.tagEndChar = ">"; - this.newLine = ""; - } + this.options = buildOptions(options, defaultOptions, props); + if (this.options.ignoreAttributes || this.options.attrNodeName) { + this.isAttribute = function(/*a*/) { + return false; + }; + } else { + this.attrPrefixLen = this.options.attributeNamePrefix.length; + this.isAttribute = isAttribute; + } + if (this.options.cdataTagName) { + this.isCDATA = isCDATA; + } else { + this.isCDATA = function(/*a*/) { + return false; + }; + } + this.replaceCDATAstr = replaceCDATAstr; + this.replaceCDATAarr = replaceCDATAarr; - if (this.options.supressEmptyNode) { - this.buildTextNode = buildEmptyTextNode; - this.buildObjNode = buildEmptyObjNode; - } else { - this.buildTextNode = buildTextValNode; - this.buildObjNode = buildObjectNode; - } + if (this.options.format) { + this.indentate = indentate; + this.tagEndChar = '>\n'; + this.newLine = '\n'; + } else { + this.indentate = function() { + return ''; + }; + this.tagEndChar = '>'; + this.newLine = ''; + } - this.buildTextValNode = buildTextValNode; - this.buildObjectNode = buildObjectNode; + if (this.options.supressEmptyNode) { + this.buildTextNode = buildEmptyTextNode; + this.buildObjNode = buildEmptyObjNode; + } else { + this.buildTextNode = buildTextValNode; + this.buildObjNode = buildObjectNode; + } + this.buildTextValNode = buildTextValNode; + this.buildObjectNode = buildObjectNode; } Parser.prototype.parse = function(jObj) { - return this.j2x(jObj, 0).val; + return this.j2x(jObj, 0).val; }; Parser.prototype.j2x = function(jObj, level) { - let attrStr = ""; - let val = ""; - const keys = Object.keys(jObj); - const len = keys.length; - for (let i = 0; i < len; i++) { - const key = keys[i]; - if (typeof jObj[key] === "undefined") { + let attrStr = ''; + let val = ''; + const keys = Object.keys(jObj); + const len = keys.length; + for (let i = 0; i < len; i++) { + const key = keys[i]; + if (typeof jObj[key] === 'undefined') { + // supress undefined node + } else if (jObj[key] === null) { + val += this.indentate(level) + '<' + key + '/' + this.tagEndChar; + } else if (typeof jObj[key] !== 'object') { + //premitive type + const attr = this.isAttribute(key); + if (attr) { + attrStr += ' ' + attr + '="' + this.options.attrValueProcessor('' + jObj[key]) + '"'; + } else if (this.isCDATA(key)) { + if (jObj[this.options.textNodeName]) { + val += this.replaceCDATAstr(jObj[this.options.textNodeName], jObj[key]); + } else { + val += this.replaceCDATAstr('', jObj[key]); + } + } else { + //tag value + if (key === this.options.textNodeName) { + if (jObj[this.options.cdataTagName]) { + //value will added while processing cdata + } else { + val += this.options.tagValueProcessor('' + jObj[key]); + } + } else { + val += this.buildTextNode(jObj[key], key, '', level); + } + } + } else if (Array.isArray(jObj[key])) { + //repeated nodes + if (this.isCDATA(key)) { + val += this.indentate(level); + if (jObj[this.options.textNodeName]) { + val += this.replaceCDATAarr(jObj[this.options.textNodeName], jObj[key]); + } else { + val += this.replaceCDATAarr('', jObj[key]); + } + } else { + //nested nodes + const arrLen = jObj[key].length; + for (let j = 0; j < arrLen; j++) { + const item = jObj[key][j]; + if (typeof item === 'undefined') { // supress undefined node - }else if (jObj[key] === null) { - val += this.indentate(level) + "<" + key + "/" + this.tagEndChar; - }else if (typeof jObj[key] !== "object") {//premitive type - const attr = this.isAttribute(key); - if (attr) { - attrStr += " " + attr + "=\"" + this.options.attrValueProcessor("" + jObj[key]) + "\""; - } else if (this.isCDATA(key)) { - if (jObj[this.options.textNodeName]) { - val += this.replaceCDATAstr(jObj[this.options.textNodeName], jObj[key]); - } else { - val += this.replaceCDATAstr("", jObj[key]); - } - } else {//tag value - if (key === this.options.textNodeName) { - if (jObj[this.options.cdataTagName]) { - //value will added while processing cdata - } else { - val += this.options.tagValueProcessor("" + jObj[key]); - } - } else { - val += this.buildTextNode(jObj[key], key, "", level); - } - } - } else if (Array.isArray(jObj[key])) {//repeated nodes - if (this.isCDATA(key)) { - val += this.indentate(level) - if (jObj[this.options.textNodeName]) { - val += this.replaceCDATAarr(jObj[this.options.textNodeName], jObj[key]); - } else { - val += this.replaceCDATAarr("", jObj[key]); - } - } else {//nested nodes - const arrLen = jObj[key].length; - for (let j = 0; j < arrLen; j++) { - const item = jObj[key][j]; - if (typeof item === "undefined") { - // supress undefined node - }else if(item === null){ - val += this.indentate(level) + "<" + key + "/" + this.tagEndChar; - }else if (typeof item === "object") { - const result = this.j2x(item, level + 1); - val += this.buildObjNode(result.val, key, result.attrStr, level); - } else { - val += this.buildTextNode(item, key, "", level); - } - } - } - } else {//nested node - if (this.options.attrNodeName && key === this.options.attrNodeName) { - const Ks = Object.keys(jObj[key]); - const L = Ks.length; - for (let j = 0; j < L; j++) { - attrStr += " " + Ks[j] + "=\"" + this.options.attrValueProcessor("" + jObj[key][Ks[j]]) + "\""; - } - } else { - const result = this.j2x(jObj[key], level + 1); - val += this.buildObjNode(result.val, key, result.attrStr, level); - } + } else if (item === null) { + val += this.indentate(level) + '<' + key + '/' + this.tagEndChar; + } else if (typeof item === 'object') { + const result = this.j2x(item, level + 1); + val += this.buildObjNode(result.val, key, result.attrStr, level); + } else { + val += this.buildTextNode(item, key, '', level); + } + } + } + } else { + //nested node + if (this.options.attrNodeName && key === this.options.attrNodeName) { + const Ks = Object.keys(jObj[key]); + const L = Ks.length; + for (let j = 0; j < L; j++) { + attrStr += ' ' + Ks[j] + '="' + this.options.attrValueProcessor('' + jObj[key][Ks[j]]) + '"'; } + } else { + const result = this.j2x(jObj[key], level + 1); + val += this.buildObjNode(result.val, key, result.attrStr, level); + } } - return {attrStr: attrStr, val: val}; + } + return {attrStr: attrStr, val: val}; }; function replaceCDATAstr(str, cdata) { - str = this.options.tagValueProcessor("" + str); - if (this.options.cdataPositionChar === "" || str === "") { - return str + ""); - } - return str + this.newLine; + str = this.options.tagValueProcessor('' + str); + if (this.options.cdataPositionChar === '' || str === '') { + return str + ''); } + return str + this.newLine; + } } function buildObjectNode(val, key, attrStr, level) { if (attrStr && !val.includes('<')) { - return this.indentate(level) - + "<" + key + attrStr - + ">" - + val - //+ this.newLine - // + this.indentate(level) - + "' + + val + + //+ this.newLine + // + this.indentate(level) + '" + this.options.tagValueProcessor("" + val) + "' + + this.options.tagValueProcessor('' + val) + + ' 1) { - jObj[tagname] = []; - for (var tag in node.child[tagname]) { - jObj[tagname].push( convertToJson(node.child[tagname][tag], options) ); - } - } else { - jObj[tagname] = convertToJson(node.child[tagname][0], options); - } + const keys = Object.keys(node.child); + for (let index = 0; index < keys.length; index++) { + var tagname = keys[index]; + if (node.child[tagname] && node.child[tagname].length > 1) { + jObj[tagname] = []; + for (var tag in node.child[tagname]) { + jObj[tagname].push(convertToJson(node.child[tagname][tag], options)); + } + } else { + jObj[tagname] = convertToJson(node.child[tagname][0], options); } - - //add value - return jObj; + } + + //add value + return jObj; }; -exports.convertToJson = convertToJson; \ No newline at end of file +exports.convertToJson = convertToJson; diff --git a/src/node2json_str.js b/src/node2json_str.js index 864619b1..e3758a46 100644 --- a/src/node2json_str.js +++ b/src/node2json_str.js @@ -1,63 +1,63 @@ -"use strict"; +'use strict'; -const util = require("./util"); -const buildOptions = require("./util").buildOptions; -const x2j = require("./xmlstr2xmlnode"); +const util = require('./util'); +const buildOptions = require('./util').buildOptions; +const x2j = require('./xmlstr2xmlnode'); //TODO: do it later const convertToJsonString = function(node, options) { - options = buildOptions(options,x2j.defaultOptions,x2j.props); + options = buildOptions(options, x2j.defaultOptions, x2j.props); - options.indentBy = options.indentBy || ""; - return _cToJsonStr(node, options,0); -} + options.indentBy = options.indentBy || ''; + return _cToJsonStr(node, options, 0); +}; -const _cToJsonStr = function(node, options,level) { - let jObj = "{"; - - //traver through all the children - const keys = Object.keys(node.child); - - for (let index = 0; index < keys.length; index++) { - var tagname = keys[index]; - if (node.child[tagname] && node.child[tagname].length > 1) { - jObj += "\"" + tagname + "\" : [ "; - for (var tag in node.child[tagname]) { - jObj += _cToJsonStr(node.child[tagname][tag], options) + " , "; - } - jObj = jObj.substr(0,jObj.length-1) + " ] "; //remove extra comma in last - } else { - jObj += "\"" +tagname + "\" : " + _cToJsonStr(node.child[tagname][0], options) + " ,"; - } - } - util.merge(jObj, node.attrsMap); - //add attrsMap as new children - if (util.isEmptyObject(jObj)) { - return util.isExist(node.val) ? node.val : ""; +const _cToJsonStr = function(node, options, level) { + let jObj = '{'; + + //traver through all the children + const keys = Object.keys(node.child); + + for (let index = 0; index < keys.length; index++) { + var tagname = keys[index]; + if (node.child[tagname] && node.child[tagname].length > 1) { + jObj += '"' + tagname + '" : [ '; + for (var tag in node.child[tagname]) { + jObj += _cToJsonStr(node.child[tagname][tag], options) + ' , '; + } + jObj = jObj.substr(0, jObj.length - 1) + ' ] '; //remove extra comma in last } else { - if (util.isExist(node.val)) { - if (!(typeof node.val === "string" && (node.val === "" || node.val === options.cdataPositionChar))) { - jObj += "\"" + options.textNodeName +"\" : " + stringval(node.val); - } - } + jObj += '"' + tagname + '" : ' + _cToJsonStr(node.child[tagname][0], options) + ' ,'; } - //add value - if(jObj[jObj.length-1] === ","){ - jObj = jObj.substr(0,jObj.length-2); + } + util.merge(jObj, node.attrsMap); + //add attrsMap as new children + if (util.isEmptyObject(jObj)) { + return util.isExist(node.val) ? node.val : ''; + } else { + if (util.isExist(node.val)) { + if (!(typeof node.val === 'string' && (node.val === '' || node.val === options.cdataPositionChar))) { + jObj += '"' + options.textNodeName + '" : ' + stringval(node.val); + } } - return jObj + "}"; + } + //add value + if (jObj[jObj.length - 1] === ',') { + jObj = jObj.substr(0, jObj.length - 2); + } + return jObj + '}'; }; -function stringval(v){ - if(v === true || v === false || !isNaN(v)){ - return v; - }else{ - return "\"" + v + "\""; - } +function stringval(v) { + if (v === true || v === false || !isNaN(v)) { + return v; + } else { + return '"' + v + '"'; + } } function indentate(options, level) { - return options.indentBy.repeat(level); + return options.indentBy.repeat(level); } -exports.convertToJsonString = convertToJsonString; \ No newline at end of file +exports.convertToJsonString = convertToJsonString; diff --git a/src/parser.js b/src/parser.js index a291f6d3..8c5a38e9 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,20 +1,20 @@ -"use strict"; +'use strict'; -const nodeToJson = require("./node2json"); -const xmlToNodeobj = require("./xmlstr2xmlnode"); -const x2xmlnode = require("./xmlstr2xmlnode"); -const buildOptions = require("./util").buildOptions; +const nodeToJson = require('./node2json'); +const xmlToNodeobj = require('./xmlstr2xmlnode'); +const x2xmlnode = require('./xmlstr2xmlnode'); +const buildOptions = require('./util').buildOptions; exports.parse = function(xmlData, options) { - options = buildOptions(options, x2xmlnode.defaultOptions, x2xmlnode.props); - return nodeToJson.convertToJson(xmlToNodeobj.getTraversalObj(xmlData, options), options); + options = buildOptions(options, x2xmlnode.defaultOptions, x2xmlnode.props); + return nodeToJson.convertToJson(xmlToNodeobj.getTraversalObj(xmlData, options), options); }; -exports.convertTonimn = require("../src/nimndata").convert2nimn; +exports.convertTonimn = require('../src/nimndata').convert2nimn; exports.getTraversalObj = xmlToNodeobj.getTraversalObj; exports.convertToJson = nodeToJson.convertToJson; -exports.convertToJsonString = require("./node2json_str").convertToJsonString; -exports.validate = require("./validator").validate; -exports.j2xParser = require("./json2xml"); -exports.parseToNimn = function (xmlData,schema,options){ - return exports.convertTonimn(exports.getTraversalObj(xmlData,options), schema, options); +exports.convertToJsonString = require('./node2json_str').convertToJsonString; +exports.validate = require('./validator').validate; +exports.j2xParser = require('./json2xml'); +exports.parseToNimn = function(xmlData, schema, options) { + return exports.convertTonimn(exports.getTraversalObj(xmlData, options), schema, options); }; diff --git a/src/read.js b/src/read.js index 295fa921..642da527 100644 --- a/src/read.js +++ b/src/read.js @@ -1,4 +1,4 @@ -"use strict"; +'use strict'; // Copyright 2013 Timothy J Fontaine // @@ -36,57 +36,57 @@ http.get('http://nodejs.org', function(response) { */ -let stream = require("stream"); -const util = require("util"); +let stream = require('stream'); +const util = require('util'); if (!stream.Transform) { - stream = require("readable-stream"); + stream = require('readable-stream'); } function ReadToEnd(opts) { - if (!(this instanceof ReadToEnd)) { - return new ReadToEnd(opts); - } + if (!(this instanceof ReadToEnd)) { + return new ReadToEnd(opts); + } - stream.Transform.call(this, opts); + stream.Transform.call(this, opts); - this._rte_encoding = opts.encoding || "utf8"; + this._rte_encoding = opts.encoding || 'utf8'; - this._buff = ""; + this._buff = ''; } module.exports = ReadToEnd; util.inherits(ReadToEnd, stream.Transform); ReadToEnd.prototype._transform = function(chunk, encoding, done) { - this._buff += chunk.toString(this._rte_encoding); - this.push(chunk); - done(); + this._buff += chunk.toString(this._rte_encoding); + this.push(chunk); + done(); }; ReadToEnd.prototype._flush = function(done) { - this.emit("complete", undefined, this._buff); - done(); + this.emit('complete', undefined, this._buff); + done(); }; ReadToEnd.readToEnd = function(stream, options, cb) { - if (!cb) { - cb = options; - options = {}; - } + if (!cb) { + cb = options; + options = {}; + } - const dest = new ReadToEnd(options); + const dest = new ReadToEnd(options); - stream.pipe(dest); + stream.pipe(dest); - stream.on("error", function(err) { - stream.unpipe(dest); - cb(err); - }); + stream.on('error', function(err) { + stream.unpipe(dest); + cb(err); + }); - dest.on("complete", cb); + dest.on('complete', cb); - dest.resume(); + dest.resume(); - return dest; + return dest; }; diff --git a/src/util.js b/src/util.js index 9911b780..a772b212 100644 --- a/src/util.js +++ b/src/util.js @@ -1,35 +1,35 @@ -"use strict"; +'use strict'; const getAllMatches = function(string, regex) { - const matches = []; - let match = regex.exec(string); - while (match) { - const allmatches = []; - const len = match.length; - for (let index = 0; index < len; index++) { - allmatches.push(match[index]); - } - matches.push(allmatches); - match = regex.exec(string); + const matches = []; + let match = regex.exec(string); + while (match) { + const allmatches = []; + const len = match.length; + for (let index = 0; index < len; index++) { + allmatches.push(match[index]); } - return matches; + matches.push(allmatches); + match = regex.exec(string); + } + return matches; }; const doesMatch = function(string, regex) { - const match = regex.exec(string); - return !(match === null || typeof match === "undefined"); + const match = regex.exec(string); + return !(match === null || typeof match === 'undefined'); }; const doesNotMatch = function(string, regex) { - return !doesMatch(string, regex); + return !doesMatch(string, regex); }; exports.isExist = function(v) { - return typeof v !== "undefined"; + return typeof v !== 'undefined'; }; exports.isEmptyObject = function(obj) { - return Object.keys(obj).length === 0; + return Object.keys(obj).length === 0; }; /** @@ -38,43 +38,43 @@ exports.isEmptyObject = function(obj) { * @param {*} a */ exports.merge = function(target, a) { - if (a) { - const keys = Object.keys(a); // will return an array of own properties - const len = keys.length; //don't make it inline - for (let i = 0; i < len; i++) { - target[keys[i]] = a[keys[i]]; - } + if (a) { + const keys = Object.keys(a); // will return an array of own properties + const len = keys.length; //don't make it inline + for (let i = 0; i < len; i++) { + target[keys[i]] = a[keys[i]]; } + } }; /* exports.merge =function (b,a){ return Object.assign(b,a); } */ exports.getValue = function(v) { - if (exports.isExist(v)) { - return v; - } else { - return ""; - } + if (exports.isExist(v)) { + return v; + } else { + return ''; + } }; // const fakeCall = function(a) {return a;}; // const fakeCallNoReturn = function() {}; -exports.buildOptions = function(options,defaultOptions,props) { - var newOptions = {}; - if (!options) { - return defaultOptions; //if there are not options - } +exports.buildOptions = function(options, defaultOptions, props) { + var newOptions = {}; + if (!options) { + return defaultOptions; //if there are not options + } - for (let i = 0; i < props.length; i++) { - if ( options[props[i]] !== undefined) { - newOptions[props[i]] = options[props[i]]; - }else{ - newOptions[props[i]] = defaultOptions[props[i]]; - } + for (let i = 0; i < props.length; i++) { + if (options[props[i]] !== undefined) { + newOptions[props[i]] = options[props[i]]; + } else { + newOptions[props[i]] = defaultOptions[props[i]]; } - return newOptions; + } + return newOptions; }; exports.doesMatch = doesMatch; diff --git a/src/validator.js b/src/validator.js index f98b4923..aa800494 100644 --- a/src/validator.js +++ b/src/validator.js @@ -1,135 +1,142 @@ -"use strict"; +'use strict'; -const util = require("./util"); +const util = require('./util'); const defaultOptions = { - allowBooleanAttributes: false, //A tag can have attributes without any value - localeRange: "a-zA-Z" + allowBooleanAttributes: false, //A tag can have attributes without any value + localeRange: 'a-zA-Z', }; -const props = ["allowBooleanAttributes", "localeRange"]; +const props = ['allowBooleanAttributes', 'localeRange']; //const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g"); exports.validate = function(xmlData, options) { - options = util.buildOptions(options,defaultOptions,props); - - //xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line - //xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag - //xmlData = xmlData.replace(/()/g,"");//Remove DOCTYPE - - const tags = []; - let tagFound = false; - if (xmlData[0] === "\ufeff") { // check for byte order mark (BOM) - xmlData = xmlData.substr(1); - } - const regxAttrName = new RegExp("^[_w][\\w\\-.:]*$".replace("_w", "_" + options.localeRange)); - const regxTagName = new RegExp("^([w]|_)[\\w.\\-_:]*".replace("([w", "([" + options.localeRange)); - for (let i = 0; i < xmlData.length; i++) { - - if (xmlData[i] === "<") { //starting of tag - //read until you reach to '>' avoiding any '>' in attribute value - - i++; - if (xmlData[i] === "?") { - i = readPI(xmlData, ++i); - if (i.err) { - return i; - } - } else if (xmlData[i] === "!") { - i = readCommentAndCDATA(xmlData, i); - continue; - } else { - let closingTag = false; - if (xmlData[i] === "/") {//closing tag - closingTag = true; - i++; - } - //read tagname - let tagName = ""; - for (; i < xmlData.length && - xmlData[i] !== ">" && - xmlData[i] !== " " && - xmlData[i] !== "\t"; i++) { - - tagName += xmlData[i]; - } - tagName = tagName.trim(); - //console.log(tagName); - - if (tagName[tagName.length - 1] === "/") {//self closing tag without attributes - tagName = tagName.substring(0, tagName.length - 1); - continue; - } - if (!validateTagName(tagName, regxTagName)) { - return {err: {code: "InvalidTag", msg: "Tag " + tagName + " is an invalid name."}}; - } - - const result = readAttributeStr(xmlData, i); - if (result === false) { - return {err: {code: "InvalidAttr", msg: "Attributes for " + tagName + " have open quote"}}; - } - let attrStr = result.value; - i = result.index; + options = util.buildOptions(options, defaultOptions, props); + + //xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line + //xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag + //xmlData = xmlData.replace(/()/g,"");//Remove DOCTYPE + + const tags = []; + let tagFound = false; + if (xmlData[0] === '\ufeff') { + // check for byte order mark (BOM) + xmlData = xmlData.substr(1); + } + const regxAttrName = new RegExp('^[_w][\\w\\-.:]*$'.replace('_w', '_' + options.localeRange)); + const regxTagName = new RegExp('^([w]|_)[\\w.\\-_:]*'.replace('([w', '([' + options.localeRange)); + for (let i = 0; i < xmlData.length; i++) { + if (xmlData[i] === '<') { + //starting of tag + //read until you reach to '>' avoiding any '>' in attribute value + + i++; + if (xmlData[i] === '?') { + i = readPI(xmlData, ++i); + if (i.err) { + return i; + } + } else if (xmlData[i] === '!') { + i = readCommentAndCDATA(xmlData, i); + continue; + } else { + let closingTag = false; + if (xmlData[i] === '/') { + //closing tag + closingTag = true; + i++; + } + //read tagname + let tagName = ''; + for (; i < xmlData.length && xmlData[i] !== '>' && xmlData[i] !== ' ' && xmlData[i] !== '\t'; i++) { + tagName += xmlData[i]; + } + tagName = tagName.trim(); + //console.log(tagName); - if (attrStr[attrStr.length - 1] === "/") {//self closing tag - attrStr = attrStr.substring(0, attrStr.length - 1); - const isValid = validateAttributeString(attrStr, options, regxAttrName); - if (isValid === true) { - tagFound = true; - //continue; //text may presents after self closing tag - } else { - return isValid; - } - } else if (closingTag) { - if (attrStr.trim().length > 0) { - return {err: {code: "InvalidTag", msg: "closing tag " + tagName + " can't have attributes or invalid starting."}}; - } else { - const otg = tags.pop(); - if (tagName !== otg) { - return {err: {code: "InvalidTag", msg: "closing tag " + otg + " is expected inplace of " + tagName + "."}}; - } - } - } else { - const isValid = validateAttributeString(attrStr, options, regxAttrName); - if (isValid !== true) { - return isValid; - } - tags.push(tagName); - tagFound = true; - } + if (tagName[tagName.length - 1] === '/') { + //self closing tag without attributes + tagName = tagName.substring(0, tagName.length - 1); + continue; + } + if (!validateTagName(tagName, regxTagName)) { + return {err: {code: 'InvalidTag', msg: 'Tag ' + tagName + ' is an invalid name.'}}; + } - //skip tag text value - //It may include comments and CDATA value - for (i++; i < xmlData.length; i++) { - if (xmlData[i] === "<") { - if (xmlData[i + 1] === "!") {//comment or CADATA - i++; - i = readCommentAndCDATA(xmlData, i); - continue; - } else { - break; - } - } - }//end of reading tag text value - if (xmlData[i] === "<") { - i--; - } + const result = readAttributeStr(xmlData, i); + if (result === false) { + return {err: {code: 'InvalidAttr', msg: 'Attributes for ' + tagName + ' have open quote'}}; + } + let attrStr = result.value; + i = result.index; + + if (attrStr[attrStr.length - 1] === '/') { + //self closing tag + attrStr = attrStr.substring(0, attrStr.length - 1); + const isValid = validateAttributeString(attrStr, options, regxAttrName); + if (isValid === true) { + tagFound = true; + //continue; //text may presents after self closing tag + } else { + return isValid; + } + } else if (closingTag) { + if (attrStr.trim().length > 0) { + return { + err: {code: 'InvalidTag', msg: 'closing tag ' + tagName + " can't have attributes or invalid starting."}, + }; + } else { + const otg = tags.pop(); + if (tagName !== otg) { + return { + err: {code: 'InvalidTag', msg: 'closing tag ' + otg + ' is expected inplace of ' + tagName + '.'}, + }; } + } } else { - if (xmlData[i] === " " || xmlData[i] === "\t" || xmlData[i] === "\n" || xmlData[i] === "\r") { - continue; + const isValid = validateAttributeString(attrStr, options, regxAttrName); + if (isValid !== true) { + return isValid; + } + tags.push(tagName); + tagFound = true; + } + + //skip tag text value + //It may include comments and CDATA value + for (i++; i < xmlData.length; i++) { + if (xmlData[i] === '<') { + if (xmlData[i + 1] === '!') { + //comment or CADATA + i++; + i = readCommentAndCDATA(xmlData, i); + continue; + } else { + break; } - return {err: {code: "InvalidChar", msg: "char " + xmlData[i] + " is not expected ."}}; + } + } //end of reading tag text value + if (xmlData[i] === '<') { + i--; } + } + } else { + if (xmlData[i] === ' ' || xmlData[i] === '\t' || xmlData[i] === '\n' || xmlData[i] === '\r') { + continue; + } + return {err: {code: 'InvalidChar', msg: 'char ' + xmlData[i] + ' is not expected .'}}; } + } - if (!tagFound) { - return {err: {code: "InvalidXml", msg: "Start tag expected."}}; - } else if (tags.length > 0) { - return {err: {code: "InvalidXml", msg: "Invalid " + JSON.stringify(tags, null, 4).replace(/\r?\n/g, "") + " found."}}; - } + if (!tagFound) { + return {err: {code: 'InvalidXml', msg: 'Start tag expected.'}}; + } else if (tags.length > 0) { + return { + err: {code: 'InvalidXml', msg: 'Invalid ' + JSON.stringify(tags, null, 4).replace(/\r?\n/g, '') + ' found.'}, + }; + } - return true; + return true; }; /** @@ -138,71 +145,77 @@ exports.validate = function(xmlData, options) { * @param {*} i */ function readPI(xmlData, i) { - var start = i; - for (; i < xmlData.length; i++) { - if (xmlData[i] == "?" || xmlData[i] == " ") {//tagname - var tagname = xmlData.substr(start, i - start); - if (i > 5 && tagname === "xml") { - return {err: {code: "InvalidXml", msg: "XML declaration allowed only at the start of the document."}}; - } else if (xmlData[i] == "?" && xmlData[i + 1] == ">") { - //check if valid attribut string - i++; - break; - } else { - continue; - } - } + var start = i; + for (; i < xmlData.length; i++) { + if (xmlData[i] == '?' || xmlData[i] == ' ') { + //tagname + var tagname = xmlData.substr(start, i - start); + if (i > 5 && tagname === 'xml') { + return {err: {code: 'InvalidXml', msg: 'XML declaration allowed only at the start of the document.'}}; + } else if (xmlData[i] == '?' && xmlData[i + 1] == '>') { + //check if valid attribut string + i++; + break; + } else { + continue; + } } - return i; + } + return i; } function readCommentAndCDATA(xmlData, i) { - if (xmlData.length > i + 5 && xmlData[i + 1] === "-" && xmlData[i + 2] === "-") {//comment - for (i += 3; i < xmlData.length; i++) { - if (xmlData[i] === "-" && xmlData[i + 1] === "-" && xmlData[i + 2] === ">") { - i += 2; - break; - } - } - } else if (xmlData.length > i + 8 && - xmlData[i + 1] === "D" && - xmlData[i + 2] === "O" && - xmlData[i + 3] === "C" && - xmlData[i + 4] === "T" && - xmlData[i + 5] === "Y" && - xmlData[i + 6] === "P" && - xmlData[i + 7] === "E") { - let angleBracketsCount = 1; - for (i += 8; i < xmlData.length; i++) { - if (xmlData[i] === "<") {angleBracketsCount++;} - else if (xmlData[i] === ">") { - angleBracketsCount--; - if (angleBracketsCount === 0) { - break; - } - } - } - } else if (xmlData.length > i + 9 && - xmlData[i + 1] === "[" && - xmlData[i + 2] === "C" && - xmlData[i + 3] === "D" && - xmlData[i + 4] === "A" && - xmlData[i + 5] === "T" && - xmlData[i + 6] === "A" && - xmlData[i + 7] === "[") { - - for (i += 8; i < xmlData.length; i++) { - if (xmlData[i] === "]" && xmlData[i + 1] === "]" && xmlData[i + 2] === ">") { - i += 2; - break; - } + if (xmlData.length > i + 5 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') { + //comment + for (i += 3; i < xmlData.length; i++) { + if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') { + i += 2; + break; + } + } + } else if ( + xmlData.length > i + 8 && + xmlData[i + 1] === 'D' && + xmlData[i + 2] === 'O' && + xmlData[i + 3] === 'C' && + xmlData[i + 4] === 'T' && + xmlData[i + 5] === 'Y' && + xmlData[i + 6] === 'P' && + xmlData[i + 7] === 'E' + ) { + let angleBracketsCount = 1; + for (i += 8; i < xmlData.length; i++) { + if (xmlData[i] === '<') { + angleBracketsCount++; + } else if (xmlData[i] === '>') { + angleBracketsCount--; + if (angleBracketsCount === 0) { + break; } + } + } + } else if ( + xmlData.length > i + 9 && + xmlData[i + 1] === '[' && + xmlData[i + 2] === 'C' && + xmlData[i + 3] === 'D' && + xmlData[i + 4] === 'A' && + xmlData[i + 5] === 'T' && + xmlData[i + 6] === 'A' && + xmlData[i + 7] === '[' + ) { + for (i += 8; i < xmlData.length; i++) { + if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') { + i += 2; + break; + } } + } - return i; + return i; } -var doubleQuote = "\""; +var doubleQuote = '"'; var singleQuote = "'"; /** @@ -211,85 +224,87 @@ var singleQuote = "'"; * @param {number} i */ function readAttributeStr(xmlData, i) { - let attrStr = ""; - let startChar = ""; - for (; i < xmlData.length; i++) { - if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) { - if (startChar === "") { - startChar = xmlData[i]; - } else if (startChar !== xmlData[i]) { - //if vaue is enclosed with double quote then single quotes are allowed inside the value and vice versa - continue; - } else { - startChar = ""; - } - } else if (xmlData[i] === ">") { - if (startChar === "") { - break; - } - } - attrStr += xmlData[i]; - } - if (startChar !== "") { - return false; + let attrStr = ''; + let startChar = ''; + for (; i < xmlData.length; i++) { + if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) { + if (startChar === '') { + startChar = xmlData[i]; + } else if (startChar !== xmlData[i]) { + //if vaue is enclosed with double quote then single quotes are allowed inside the value and vice versa + continue; + } else { + startChar = ''; + } + } else if (xmlData[i] === '>') { + if (startChar === '') { + break; + } } + attrStr += xmlData[i]; + } + if (startChar !== '') { + return false; + } - return {value: attrStr, index: i}; + return {value: attrStr, index: i}; } /** * Select all the attributes whether valid or invalid. */ -const validAttrStrRegxp = new RegExp("(\\s*)([^\\s=]+)(\\s*=)?(\\s*(['\"])(([\\s\\S])*?)\\5)?", "g"); +const validAttrStrRegxp = new RegExp('(\\s*)([^\\s=]+)(\\s*=)?(\\s*([\'"])(([\\s\\S])*?)\\5)?', 'g'); //attr, ="sd", a="amit's", a="sd"b="saf", ab cd="" function validateAttributeString(attrStr, options, regxAttrName) { - //console.log("start:"+attrStr+":end"); + //console.log("start:"+attrStr+":end"); - //if(attrStr.trim().length === 0) return true; //empty string + //if(attrStr.trim().length === 0) return true; //empty string - const matches = util.getAllMatches(attrStr, validAttrStrRegxp); - const attrNames = {}; + const matches = util.getAllMatches(attrStr, validAttrStrRegxp); + const attrNames = {}; - for (let i = 0; i < matches.length; i++) { - //console.log(matches[i]); + for (let i = 0; i < matches.length; i++) { + //console.log(matches[i]); - if (matches[i][1].length === 0) {//nospace before attribute name: a="sd"b="saf" - return {err: {code: "InvalidAttr", msg: "attribute " + matches[i][2] + " has no space in starting."}}; - } else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {//independent attribute: ab - return {err: {code: "InvalidAttr", msg: "boolean attribute " + matches[i][2] + " is not allowed."}}; - } - /* else if(matches[i][6] === undefined){//attribute without value: ab= + if (matches[i][1].length === 0) { + //nospace before attribute name: a="sd"b="saf" + return {err: {code: 'InvalidAttr', msg: 'attribute ' + matches[i][2] + ' has no space in starting.'}}; + } else if (matches[i][3] === undefined && !options.allowBooleanAttributes) { + //independent attribute: ab + return {err: {code: 'InvalidAttr', msg: 'boolean attribute ' + matches[i][2] + ' is not allowed.'}}; + } + /* else if(matches[i][6] === undefined){//attribute without value: ab= return { err: { code:"InvalidAttr",msg:"attribute " + matches[i][2] + " has no value assigned."}}; } */ - const attrName = matches[i][2]; - if (!validateAttrName(attrName, regxAttrName)) { - return {err: {code: "InvalidAttr", msg: "attribute " + attrName + " is an invalid name."}}; - } - if (!attrNames.hasOwnProperty(attrName)) {//check for duplicate attribute. - attrNames[attrName] = 1; - } else { - return {err: {code: "InvalidAttr", msg: "attribute " + attrName + " is repeated."}}; - } + const attrName = matches[i][2]; + if (!validateAttrName(attrName, regxAttrName)) { + return {err: {code: 'InvalidAttr', msg: 'attribute ' + attrName + ' is an invalid name.'}}; } + if (!attrNames.hasOwnProperty(attrName)) { + //check for duplicate attribute. + attrNames[attrName] = 1; + } else { + return {err: {code: 'InvalidAttr', msg: 'attribute ' + attrName + ' is repeated.'}}; + } + } - return true; - + return true; } // const validAttrRegxp = /^[_a-zA-Z][\w\-.:]*$/; function validateAttrName(attrName, regxAttrName) { - // const validAttrRegxp = new RegExp(regxAttrName); - return util.doesMatch(attrName, regxAttrName); + // const validAttrRegxp = new RegExp(regxAttrName); + return util.doesMatch(attrName, regxAttrName); } //const startsWithXML = new RegExp("^[Xx][Mm][Ll]"); // startsWith = /^([a-zA-Z]|_)[\w.\-_:]*/; function validateTagName(tagname, regxTagName) { - /*if(util.doesMatch(tagname,startsWithXML)) return false; + /*if(util.doesMatch(tagname,startsWithXML)) return false; else*/ - return !util.doesNotMatch(tagname, regxTagName); + return !util.doesNotMatch(tagname, regxTagName); } diff --git a/src/xmlNode.js b/src/xmlNode.js index b5a55de1..e5835a93 100644 --- a/src/xmlNode.js +++ b/src/xmlNode.js @@ -1,16 +1,17 @@ -"use strict"; +'use strict'; module.exports = function(tagname, parent, val) { - this.tagname = tagname; - this.parent = parent; - this.child = {};//child tags - this.attrsMap = {};//attributes map - this.val = val;//text only - this.addChild = function(child) { - if ( Array.isArray(this.child[child.tagname]) ){//already presents - this.child[child.tagname].push(child); - } else { - this.child[child.tagname] = [child]; - } - }; + this.tagname = tagname; + this.parent = parent; + this.child = {}; //child tags + this.attrsMap = {}; //attributes map + this.val = val; //text only + this.addChild = function(child) { + if (Array.isArray(this.child[child.tagname])) { + //already presents + this.child[child.tagname].push(child); + } else { + this.child[child.tagname] = [child]; + } + }; }; diff --git a/src/xmlstr2xmlnode.js b/src/xmlstr2xmlnode.js index d36a41f3..16122ee6 100644 --- a/src/xmlstr2xmlnode.js +++ b/src/xmlstr2xmlnode.js @@ -1,212 +1,242 @@ -"use strict"; +'use strict'; -const util = require("./util"); -const buildOptions = require("./util").buildOptions; -const xmlNode = require("./xmlNode"); -const TagType = {"OPENING": 1, "CLOSING": 2, "SELF": 3, "CDATA": 4}; -let regx = "<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|(([\\w:\\-._]*:)?([\\w:\\-._]+))([^>]*)>|((\\/)(([\\w:\\-._]*:)?([\\w:\\-._]+))\\s*>))([^<]*)"; +const util = require('./util'); +const buildOptions = require('./util').buildOptions; +const xmlNode = require('./xmlNode'); +const TagType = {OPENING: 1, CLOSING: 2, SELF: 3, CDATA: 4}; +let regx = + '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|(([\\w:\\-._]*:)?([\\w:\\-._]+))([^>]*)>|((\\/)(([\\w:\\-._]*:)?([\\w:\\-._]+))\\s*>))([^<]*)'; //const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g"); //const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g"); //polyfill if (!Number.parseInt && window.parseInt) { - Number.parseInt = window.parseInt - } - if (!Number.parseFloat && window.parseFloat) { - Number.parseFloat= window.parseFloat - } + Number.parseInt = window.parseInt; +} +if (!Number.parseFloat && window.parseFloat) { + Number.parseFloat = window.parseFloat; +} const defaultOptions = { - attributeNamePrefix: "@_", - attrNodeName: false, - textNodeName: "#text", - ignoreAttributes: true, - ignoreNameSpace: false, - allowBooleanAttributes: false, //a tag can have attributes without any value - //ignoreRootElement : false, - parseNodeValue: true, - parseAttributeValue: false, - arrayMode: false, - trimValues: true, //Trim string values of tag and attributes - cdataTagName: false, - cdataPositionChar: "\\c", - localeRange: "", - tagValueProcessor: function(a) {return a}, - attrValueProcessor: function(a) {return a} - //decodeStrict: false, + attributeNamePrefix: '@_', + attrNodeName: false, + textNodeName: '#text', + ignoreAttributes: true, + ignoreNameSpace: false, + allowBooleanAttributes: false, //a tag can have attributes without any value + //ignoreRootElement : false, + parseNodeValue: true, + parseAttributeValue: false, + arrayMode: false, + trimValues: true, //Trim string values of tag and attributes + cdataTagName: false, + cdataPositionChar: '\\c', + localeRange: '', + tagValueProcessor: function(a) { + return a; + }, + attrValueProcessor: function(a) { + return a; + }, + //decodeStrict: false, }; exports.defaultOptions = defaultOptions; -const props = ["attributeNamePrefix", "attrNodeName", "textNodeName", "ignoreAttributes", "ignoreNameSpace", "allowBooleanAttributes", "parseNodeValue", "parseAttributeValue", "arrayMode", "trimValues", "cdataTagName", "cdataPositionChar", "localeRange", "tagValueProcessor", "attrValueProcessor", "parseTrueNumberOnly"]; +const props = [ + 'attributeNamePrefix', + 'attrNodeName', + 'textNodeName', + 'ignoreAttributes', + 'ignoreNameSpace', + 'allowBooleanAttributes', + 'parseNodeValue', + 'parseAttributeValue', + 'arrayMode', + 'trimValues', + 'cdataTagName', + 'cdataPositionChar', + 'localeRange', + 'tagValueProcessor', + 'attrValueProcessor', + 'parseTrueNumberOnly', +]; exports.props = props; const getTraversalObj = function(xmlData, options) { - options = buildOptions(options,defaultOptions,props); - //xmlData = xmlData.replace(/\r?\n/g, " ");//make it single line - xmlData = xmlData.replace(//g, "");//Remove comments - - const xmlObj = new xmlNode("!xml"); - let currentNode = xmlObj; - - regx = regx.replace(/\[\\w/g, "[" + options.localeRange + "\\w"); - const tagsRegx = new RegExp(regx, "g"); - let tag = tagsRegx.exec(xmlData); - let nextTag = tagsRegx.exec(xmlData); - while (tag) { - const tagType = checkForTagType(tag); - - if (tagType === TagType.CLOSING) { - //add parsed data to parent node - if (currentNode.parent && tag[14]) { - currentNode.parent.val = util.getValue(currentNode.parent.val) + "" + processTagValue(tag[14], options); - } - - currentNode = currentNode.parent; - } else if (tagType === TagType.CDATA) { - if (options.cdataTagName) { - //add cdata node - const childNode = new xmlNode(options.cdataTagName, currentNode, tag[3]); - childNode.attrsMap = buildAttributesMap(tag[8], options); - currentNode.addChild(childNode); - //for backtracking - currentNode.val = util.getValue(currentNode.val) + options.cdataPositionChar; - //add rest value to parent node - if (tag[14]) { - currentNode.val += processTagValue(tag[14], options); - } - } else { - currentNode.val = (currentNode.val || "") + (tag[3] || "") + processTagValue(tag[14], options); - } - } else if (tagType === TagType.SELF) { - if (currentNode && tag[14]) { - currentNode.val = util.getValue(currentNode.val) + "" + processTagValue(tag[14], options); - } - - const childNode = new xmlNode(options.ignoreNameSpace ? tag[7] : tag[5], currentNode, ""); - if (tag[8] && tag[8].length > 0) { - tag[8] = tag[8].substr(0, tag[8].length - 1); - } - childNode.attrsMap = buildAttributesMap(tag[8], options); - currentNode.addChild(childNode); - } else {//TagType.OPENING - const childNode = new xmlNode(options.ignoreNameSpace ? tag[7] : tag[5], currentNode, processTagValue(tag[14], options)); - childNode.attrsMap = buildAttributesMap(tag[8], options); - currentNode.addChild(childNode); - currentNode = childNode; + options = buildOptions(options, defaultOptions, props); + //xmlData = xmlData.replace(/\r?\n/g, " ");//make it single line + xmlData = xmlData.replace(//g, ''); //Remove comments + + const xmlObj = new xmlNode('!xml'); + let currentNode = xmlObj; + + regx = regx.replace(/\[\\w/g, '[' + options.localeRange + '\\w'); + const tagsRegx = new RegExp(regx, 'g'); + let tag = tagsRegx.exec(xmlData); + let nextTag = tagsRegx.exec(xmlData); + while (tag) { + const tagType = checkForTagType(tag); + + if (tagType === TagType.CLOSING) { + //add parsed data to parent node + if (currentNode.parent && tag[14]) { + currentNode.parent.val = util.getValue(currentNode.parent.val) + '' + processTagValue(tag[14], options); + } + + currentNode = currentNode.parent; + } else if (tagType === TagType.CDATA) { + if (options.cdataTagName) { + //add cdata node + const childNode = new xmlNode(options.cdataTagName, currentNode, tag[3]); + childNode.attrsMap = buildAttributesMap(tag[8], options); + currentNode.addChild(childNode); + //for backtracking + currentNode.val = util.getValue(currentNode.val) + options.cdataPositionChar; + //add rest value to parent node + if (tag[14]) { + currentNode.val += processTagValue(tag[14], options); } - - tag = nextTag; - nextTag = tagsRegx.exec(xmlData); + } else { + currentNode.val = (currentNode.val || '') + (tag[3] || '') + processTagValue(tag[14], options); + } + } else if (tagType === TagType.SELF) { + if (currentNode && tag[14]) { + currentNode.val = util.getValue(currentNode.val) + '' + processTagValue(tag[14], options); + } + + const childNode = new xmlNode(options.ignoreNameSpace ? tag[7] : tag[5], currentNode, ''); + if (tag[8] && tag[8].length > 0) { + tag[8] = tag[8].substr(0, tag[8].length - 1); + } + childNode.attrsMap = buildAttributesMap(tag[8], options); + currentNode.addChild(childNode); + } else { + //TagType.OPENING + const childNode = new xmlNode( + options.ignoreNameSpace ? tag[7] : tag[5], + currentNode, + processTagValue(tag[14], options) + ); + childNode.attrsMap = buildAttributesMap(tag[8], options); + currentNode.addChild(childNode); + currentNode = childNode; } - return xmlObj; + tag = nextTag; + nextTag = tagsRegx.exec(xmlData); + } + + return xmlObj; }; function processTagValue(val, options) { - if (val) { - if (options.trimValues) { - val = val.trim(); - } - val = options.tagValueProcessor(val); - val = parseValue(val, options.parseNodeValue, options.parseTrueNumberOnly); + if (val) { + if (options.trimValues) { + val = val.trim(); } + val = options.tagValueProcessor(val); + val = parseValue(val, options.parseNodeValue, options.parseTrueNumberOnly); + } - return val; + return val; } function checkForTagType(match) { - if (match[4] === "]]>") { - return TagType.CDATA; - } else if (match[10] === "/") { - return TagType.CLOSING; - } else if (typeof match[8] !== "undefined" && match[8].substr(match[8].length - 1) === "/") { - return TagType.SELF; - } else { - return TagType.OPENING; - } + if (match[4] === ']]>') { + return TagType.CDATA; + } else if (match[10] === '/') { + return TagType.CLOSING; + } else if (typeof match[8] !== 'undefined' && match[8].substr(match[8].length - 1) === '/') { + return TagType.SELF; + } else { + return TagType.OPENING; + } } function resolveNameSpace(tagname, options) { - if (options.ignoreNameSpace) { - const tags = tagname.split(":"); - const prefix = tagname.charAt(0) === "/" ? "/" : ""; - if (tags[0] === "xmlns") { - return ""; - } - if (tags.length === 2) { - tagname = prefix + tags[1]; - } + if (options.ignoreNameSpace) { + const tags = tagname.split(':'); + const prefix = tagname.charAt(0) === '/' ? '/' : ''; + if (tags[0] === 'xmlns') { + return ''; } - return tagname; + if (tags.length === 2) { + tagname = prefix + tags[1]; + } + } + return tagname; } function parseValue(val, shouldParse, parseTrueNumberOnly) { - if (shouldParse && typeof val === "string") { - let parsed; - if (val.trim() === "" || isNaN(val)) { - parsed = val === "true" ? true : val === "false" ? false : val; - } else { - if(val.indexOf("0x") !== -1){//support hexa decimal - parsed = Number.parseInt(val,16); - } else if (val.indexOf(".") !== -1) { - parsed = Number.parseFloat(val); - } else { - parsed = Number.parseInt(val, 10); - } - if(parseTrueNumberOnly){ - parsed = String(parsed) === val ? parsed : val; - - } - } - return parsed; + if (shouldParse && typeof val === 'string') { + let parsed; + if (val.trim() === '' || isNaN(val)) { + parsed = val === 'true' ? true : val === 'false' ? false : val; } else { - if (util.isExist(val)) { - return val; - } else { - return ""; - } + if (val.indexOf('0x') !== -1) { + //support hexa decimal + parsed = Number.parseInt(val, 16); + } else if (val.indexOf('.') !== -1) { + parsed = Number.parseFloat(val); + } else { + parsed = Number.parseInt(val, 10); + } + if (parseTrueNumberOnly) { + parsed = String(parsed) === val ? parsed : val; + } + } + return parsed; + } else { + if (util.isExist(val)) { + return val; + } else { + return ''; } + } } //TODO: change regex to capture NS //const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm"); -const attrsRegx = new RegExp("([^\\s=]+)\\s*(=\\s*(['\"])(.*?)\\3)?", "g"); +const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])(.*?)\\3)?', 'g'); function buildAttributesMap(attrStr, options) { - if (!options.ignoreAttributes && typeof attrStr === "string") { - attrStr = attrStr.replace(/\r?\n/g, " "); - //attrStr = attrStr || attrStr.trim(); - - const matches = util.getAllMatches(attrStr, attrsRegx); - const len = matches.length; //don't make it inline - const attrs = {}; - for (let i = 0; i < len; i++) { - const attrName = resolveNameSpace(matches[i][1], options); - if (attrName.length) { - if (matches[i][4] !== undefined) { - if (options.trimValues) { - matches[i][4] = matches[i][4].trim(); - } - matches[i][4] = options.attrValueProcessor(matches[i][4]); - attrs[options.attributeNamePrefix + attrName] = parseValue(matches[i][4], options.parseAttributeValue, options.parseTrueNumberOnly); - } else if (options.allowBooleanAttributes) { - attrs[options.attributeNamePrefix + attrName] = true; - } - - } + if (!options.ignoreAttributes && typeof attrStr === 'string') { + attrStr = attrStr.replace(/\r?\n/g, ' '); + //attrStr = attrStr || attrStr.trim(); + + const matches = util.getAllMatches(attrStr, attrsRegx); + const len = matches.length; //don't make it inline + const attrs = {}; + for (let i = 0; i < len; i++) { + const attrName = resolveNameSpace(matches[i][1], options); + if (attrName.length) { + if (matches[i][4] !== undefined) { + if (options.trimValues) { + matches[i][4] = matches[i][4].trim(); + } + matches[i][4] = options.attrValueProcessor(matches[i][4]); + attrs[options.attributeNamePrefix + attrName] = parseValue( + matches[i][4], + options.parseAttributeValue, + options.parseTrueNumberOnly + ); + } else if (options.allowBooleanAttributes) { + attrs[options.attributeNamePrefix + attrName] = true; } - if (!Object.keys(attrs).length) { - return; - } - if (options.attrNodeName) { - const attrCollection = {}; - attrCollection[options.attrNodeName] = attrs; - return attrCollection; - } - return attrs; + } + } + if (!Object.keys(attrs).length) { + return; + } + if (options.attrNodeName) { + const attrCollection = {}; + attrCollection[options.attrNodeName] = attrs; + return attrCollection; } + return attrs; + } } exports.getTraversalObj = getTraversalObj; diff --git a/tasks/postinstall.js b/tasks/postinstall.js index 5eebb13e..5ad14b9a 100644 --- a/tasks/postinstall.js +++ b/tasks/postinstall.js @@ -1,17 +1,17 @@ -//Display message after instalation +// Display message after instalation const stars = '\x1B[1m***\x1B[0m'; -const yellow = "\033[1;33m"; -//const light_green = "\033[1;32m"; -const light_blue = "\033[1;34m"; -const NC="\033[0m"; // No Color +const yellow = '\033[1;33m'; +// const light_green = "\033[1;32m"; +const light_blue = '\033[1;34m'; +const NC = '\033[0m'; // No Color console.log('' + stars + ' Thank you for using fast-xml-parser! ' + stars); console.log(''); -console.log(yellow+"Donating to an open source project is more affordable"+NC); +console.log(yellow + 'Donating to an open source project is more affordable' + NC); console.log(light_blue + ' https://opencollective.com/fast-xml-parser/donate' + NC); -console.log( light_blue + ' https://www.patreon.com/bePatron?u=9531404' + NC); -console.log( light_blue + ' https://www.paypal.me/amitkumarguptagwl' + NC); +console.log(light_blue + ' https://www.patreon.com/bePatron?u=9531404' + NC); +console.log(light_blue + ' https://www.paypal.me/amitkumarguptagwl' + NC); console.log(''); console.log(''); process.exit(0); diff --git a/yarn.lock b/yarn.lock index 8b141994..757c5fab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3897,6 +3897,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier@^1.15.3: + version "1.15.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.3.tgz#1feaac5bdd181237b54dbe65d874e02a1472786a" + integrity sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg== + private@^0.1.6: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"