typescriptify and cleanup

This commit is contained in:
Dror Gluska
2019-05-31 11:27:48 +03:00
parent 28006be580
commit cfebc962f0
28 changed files with 2863 additions and 1845 deletions

1
.gitignore vendored
View File

@@ -35,3 +35,4 @@ jspm_packages
# Optional REPL history # Optional REPL history
.node_repl_history .node_repl_history
/dist

51
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,51 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}\\node_stream_zip.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
]
},
{
"type": "node",
"request": "launch",
"name": "Mocha All",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": [
"--timeout",
"999999",
"--colors",
"-r",
"ts-node/register",
"test/**/*.spec.ts"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"type": "node",
"request": "launch",
"name": "Mocha Current File",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": [
"--timeout",
"999999",
"--colors",
"-r",
"ts-node/register",
"${file}"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"cSpell.words": [
]
}

614
package-lock.json generated Normal file
View File

@@ -0,0 +1,614 @@
{
"name": "xml-streamer",
"version": "0.2.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@babel/code-frame": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
"integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
"dev": true,
"requires": {
"@babel/highlight": "^7.0.0"
}
},
"@babel/highlight": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
"integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
"dev": true,
"requires": {
"chalk": "^2.0.0",
"esutils": "^2.0.2",
"js-tokens": "^4.0.0"
}
},
"@types/lodash": {
"version": "4.14.132",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.132.tgz",
"integrity": "sha512-RNUU1rrh85NgUJcjOOr96YXr+RHwInGbaQCZmlitqOaCKXffj8bh+Zxwuq5rjDy5OgzFldDVoqk4pyLEDiwxIw==",
"dev": true
},
"@types/mocha": {
"version": "5.2.7",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
"integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
"dev": true
},
"@types/node": {
"version": "12.0.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.4.tgz",
"integrity": "sha512-j8YL2C0fXq7IONwl/Ud5Kt0PeXw22zGERt+HSSnwbKOJVsAGkEz3sFCYwaF9IOuoG1HOtE0vKCj6sXF7Q0+Vaw==",
"dev": true
},
"@types/should": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/@types/should/-/should-13.0.0.tgz",
"integrity": "sha512-Mi6YZ2ABnnGGFMuiBDP0a8s1ZDCDNHqP97UH8TyDmCWuGGavpsFMfJnAMYaaqmDlSCOCNbVLHBrSDEOpx/oLhw==",
"dev": true,
"requires": {
"should": "*"
}
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"arg": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz",
"integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==",
"dev": true
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"requires": {
"sprintf-js": "~1.0.2"
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"builtin-modules": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
"dev": true
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"commander": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
"dev": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"debug": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.0.0.tgz",
"integrity": "sha1-ib2d9nMrUSVrxnBTQrugLtEhMe8=",
"dev": true,
"requires": {
"ms": "0.6.2"
}
},
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
},
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
"dev": true
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"glob": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
"integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"graceful-fs": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz",
"integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=",
"dev": true
},
"growl": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz",
"integrity": "sha1-Sy3sjZB+k9szZiTc7AGDUC+MlCg=",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
"jade": {
"version": "0.26.3",
"resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
"integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=",
"dev": true,
"requires": {
"commander": "0.6.1",
"mkdirp": "0.3.0"
},
"dependencies": {
"commander": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz",
"integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=",
"dev": true
},
"mkdirp": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
"integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=",
"dev": true
}
}
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"lodash": {
"version": "4.17.5",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
"integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw=="
},
"lru-cache": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
"integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=",
"dev": true
},
"make-error": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
"integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
"minimist": "0.0.8"
}
},
"mocha": {
"version": "1.21.5",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.5.tgz",
"integrity": "sha1-fFiwkXTfl25DSiOx6NY5hz/FKek=",
"dev": true,
"requires": {
"commander": "2.3.0",
"debug": "2.0.0",
"diff": "1.0.8",
"escape-string-regexp": "1.0.2",
"glob": "3.2.3",
"growl": "1.8.1",
"jade": "0.26.3",
"mkdirp": "0.5.0"
},
"dependencies": {
"commander": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz",
"integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=",
"dev": true
},
"diff": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/diff/-/diff-1.0.8.tgz",
"integrity": "sha1-NDJ2MI7Jkbe8giZ+1VvBQR+XFmY=",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz",
"integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=",
"dev": true
},
"glob": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz",
"integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=",
"dev": true,
"requires": {
"graceful-fs": "~2.0.0",
"inherits": "2",
"minimatch": "~0.2.11"
}
},
"minimatch": {
"version": "0.2.14",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
"integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=",
"dev": true,
"requires": {
"lru-cache": "2",
"sigmund": "~1.0.0"
}
},
"mkdirp": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
"integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
"dev": true,
"requires": {
"minimist": "0.0.8"
}
}
}
},
"ms": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz",
"integrity": "sha1-2JwhJMb9wTU9Zai3e/GqxLGTcIw=",
"dev": true
},
"nan": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
},
"node-expat": {
"version": "2.3.15",
"resolved": "https://registry.npmjs.org/node-expat/-/node-expat-2.3.15.tgz",
"integrity": "sha1-lqU6C8AGSdr3j0/AdKI+ZEwiO0w=",
"requires": {
"bindings": "^1.2.1",
"nan": "^2.3.5"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"resolve": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz",
"integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
},
"semver": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"dev": true
},
"should": {
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz",
"integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==",
"dev": true,
"requires": {
"should-equal": "^2.0.0",
"should-format": "^3.0.3",
"should-type": "^1.4.0",
"should-type-adaptors": "^1.0.1",
"should-util": "^1.0.0"
}
},
"should-equal": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz",
"integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==",
"dev": true,
"requires": {
"should-type": "^1.4.0"
}
},
"should-format": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz",
"integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=",
"dev": true,
"requires": {
"should-type": "^1.3.0",
"should-type-adaptors": "^1.0.1"
}
},
"should-type": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz",
"integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=",
"dev": true
},
"should-type-adaptors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz",
"integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==",
"dev": true,
"requires": {
"should-type": "^1.3.0",
"should-util": "^1.0.0"
}
},
"should-util": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz",
"integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=",
"dev": true
},
"sigmund": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"source-map-support": {
"version": "0.5.12",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz",
"integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"ts-node": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.2.0.tgz",
"integrity": "sha512-m8XQwUurkbYqXrKqr3WHCW310utRNvV5OnRVeISeea7LoCWVcdfeB/Ntl8JYWFh+WRoUAdBgESrzKochQt7sMw==",
"dev": true,
"requires": {
"arg": "^4.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.6",
"yn": "^3.0.0"
},
"dependencies": {
"diff": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
"integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==",
"dev": true
}
}
},
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
"dev": true
},
"tslint": {
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.17.0.tgz",
"integrity": "sha512-pflx87WfVoYepTet3xLfDOLDm9Jqi61UXIKePOuca0qoAZyrGWonDG9VTbji58Fy+8gciUn8Bt7y69+KEVjc/w==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"builtin-modules": "^1.1.1",
"chalk": "^2.3.0",
"commander": "^2.12.1",
"diff": "^3.2.0",
"glob": "^7.1.1",
"js-yaml": "^3.13.1",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.1",
"resolve": "^1.3.2",
"semver": "^5.3.0",
"tslib": "^1.8.0",
"tsutils": "^2.29.0"
}
},
"tsutils": {
"version": "2.29.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
"integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
}
},
"typescript": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.1.tgz",
"integrity": "sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==",
"dev": true
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"yn": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.0.tgz",
"integrity": "sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg==",
"dev": true
}
}
}

View File

@@ -24,19 +24,39 @@
"url": "https://github.com/Sai1919/xml-streamer" "url": "https://github.com/Sai1919/xml-streamer"
}, },
"dependencies": { "dependencies": {
"node-expat": "2.3.15", "lodash": "4.17.5",
"lodash": "4.17.5" "node-expat": "2.3.15"
}, },
"devDependencies": { "devDependencies": {
"mocha": "^1.21.4", "mocha": "^1.21.4",
"should": "11.1.1", "should": "^13.2.3",
"standard": "8.5.0" "@types/lodash": "^4.14.132",
"@types/mocha": "^5.2.7",
"@types/node": "^12.0.4",
"@types/should": "^13.0.0",
"ts-node": "^8.2.0",
"tslint": "^5.17.0",
"typescript": "^3.5.1"
}, },
"optionalDependencies": {}, "optionalDependencies": {},
"main": "./parser", "main": "dist/parser.js",
"types": "dist/parser.d.ts",
"scripts": { "scripts": {
"test": "mocha && standard" "performance-test": "node --prof node_modules/mocha/bin/_mocha -r ts-node/register test/**/*.spec.ts",
"test-one": "mocha -r ts-node/register",
"test": "mocha -r ts-node/register test/**/*.spec.ts",
"lint": "tslint --project .",
"dryrun": "tsc -noEmit",
"build": "tsc",
"prepublish": "npm run lint && npm run dryrun && npm run test",
"install": "npm run build"
}, },
"contributors": [
{
"name": "Dror Gluska",
"email": "drorgl@gmail.com"
}
],
"maintainers": [ "maintainers": [
{ {
"name": "Sai Teja", "name": "Sai Teja",

281
parser.js
View File

@@ -1,281 +0,0 @@
var expat = require('node-expat')
var _ = require('lodash')
var util = require('util')
var stream = require('stream')
var ParserState = require('./parserState')
var defaults = {
resourcePath: '',
emitOnNodeName: false,
attrsKey: '$',
textKey: '_',
explicitArray: true,
verbatimText: false
}
function XmlParser (opts) {
this.opts = _.defaults(opts, defaults)
this.parserState = new ParserState()
this.parser = new expat.Parser('UTF-8')
stream.Transform.call(this)
this._readableState.objectMode = true
}
util.inherits(XmlParser, stream.Transform)
XmlParser.prototype.checkForInterestedNodeListeners = function () {
var ignore = [ 'end', 'prefinish', 'data', 'error' ]
var eventNames = Object.keys(this._events)
for (var i = 0; i < eventNames.length; i++) {
if (_.includes(ignore, eventNames[i], 0)) continue
this.parserState.interestedNodes.push(eventNames[i])
}
}
XmlParser.prototype._transform = function (chunk, encoding, callback) {
if (encoding !== 'buffer') this.emit('error', new Error('unsupported encoding'))
this.processChunk(chunk)
callback()
}
XmlParser.prototype.processChunk = function (chunk) {
var parser = this.parser
var state = this.parserState
if (state.isRootNode) {
this.checkForInterestedNodeListeners()
registerEvents.call(this)
}
if (typeof chunk === 'string') {
if (!parser.parse('', true)) processError.call(this)
} else {
if (!parser.parse(chunk.toString())) processError.call(this)
}
}
XmlParser.prototype.parse = function (chunk, cb) {
var parser = this.parser
var state = this.parserState
var error
if (state.isRootNode) {
this.checkForInterestedNodeListeners()
registerEvents.call(this)
}
if (typeof chunk === Buffer) chunk = chunk.toString()
this.on('error', function (err) {
error = err
})
if (!parser.parse(chunk)) {
error = processError.call(this)
}
if (error) return cb(error)
return cb(null, this._readableState.buffer)
}
function registerEvents () {
var scope = this
var parser = this.parser
var state = this.parserState
var lastIndex
var resourcePath = this.opts.resourcePath
var attrsKey = this.opts.attrsKey
var textKey = this.opts.textKey
var interestedNodes = state.interestedNodes
var explicitArray = this.opts.explicitArray
var verbatimText = this.opts.verbatimText;
parser.on('startElement', function (name, attrs) {
if (state.isRootNode) state.isRootNode = false;
state.currentPath = state.currentPath + '/' + name
checkForResourcePath(name)
if (state.isPathfound) processStartElement(name, attrs)
})
parser.on('endElement', function (name) {
state.lastEndedNode = name
lastIndex = state.currentPath.lastIndexOf('/' + name)
state.currentPath = state.currentPath.substring(0, lastIndex)
if (state.isPathfound) processEndElement(name)
checkForResourcePath(name)
})
parser.on('text', function (text) {
if (state.isPathfound) processText(text)
})
parser.on('error', function (err) {
processError.call(this, err)
})
function processStartElement (name, attrs) {
if (!name) return
var obj = {}
if (attrs && !_.isEmpty(attrs)) obj[attrsKey] = attrs
var tempObj = state.object
var path = getRelativePath(name)
if (!path) {
if (attrs && !_.isEmpty(attrs)) state.object[attrsKey] = attrs
return
}
var tokens = path.split('.')
for (var i = 0; i < tokens.length; i++) {
if (tempObj[tokens[i]] && !(explicitArray === false && i === tokens.length - 1)) {
tempObj = tempObj[tokens[i]]
} else {
// if explicitArray is true then create each node as array
// irrespective of how many nodes are there with same name.
tempObj[tokens[i]] = explicitArray ? [] : obj
tempObj = tempObj[tokens[i]]
}
if (Array.isArray(tempObj) && i !== tokens.length - 1) tempObj = tempObj[tempObj.length - 1]
}
if (Array.isArray(tempObj)) {
tempObj.push(obj)
}
}
function processEndElement (name) {
if (resourcePath) {
var index = resourcePath.lastIndexOf('/')
var rpath = resourcePath.substring(0, index)
if (rpath === state.currentPath) {
scope.push(state.object)
if (scope.opts.emitOnNodeName) scope.emit(name, state.object)
state.object = {}
}
} else {
if (_.includes(interestedNodes, name, 0)) {
emitInterestedNode(name)
if (state.firstFoundNode === name) {
state.object = {}
state.firstFoundNode = ''
state.isPathfound = false
}
}
}
}
function emitInterestedNode (name) {
var index
var xpath
var pathTokens
xpath = state.currentPath.substring(1)
pathTokens = xpath.split('/')
pathTokens.push(name)
index = pathTokens.indexOf(state.firstFoundNode)
pathTokens = _.drop(pathTokens, index + 1)
var tempObj = state.object
for (var i = 0; i < pathTokens.length; i++) {
tempObj = tempObj[pathTokens[i]]
}
if (Array.isArray(tempObj)) tempObj = tempObj[tempObj.length - 1]
scope.emit(name, tempObj)
scope.push(tempObj)
}
function processText (text) {
if ((!text) || ((!verbatimText) && !/\S/.test(text))) {
return
}
var path = getRelativePath()
var tempObj = state.object
if (!path) {
if (!state.object[textKey]) state.object[textKey] = ''
state.object[textKey] = state.object[textKey] + text
return
}
var tokens = path.split('.')
for (var i = 0; i < tokens.length; i++) {
if (tempObj[tokens[i]]) {
tempObj = tempObj[tokens[i]]
} else {
tempObj[tokens[i]] = explicitArray ? [] : {}
tempObj = tempObj[tokens[i]]
}
if (Array.isArray(tempObj) && i !== tokens.length - 1) tempObj = tempObj[tempObj.length - 1]
}
if (Array.isArray(tempObj)) {
var obj = tempObj[tempObj.length - 1]
if (!obj[textKey]) obj[textKey] = ''
obj[textKey] = obj[textKey] + text
} else {
if (!tempObj[textKey]) tempObj[textKey] = ''
tempObj[textKey] = tempObj[textKey] + text
}
}
function checkForResourcePath (name) {
if (resourcePath) {
if (state.currentPath.indexOf(resourcePath) === 0) {
state.isPathfound = true
} else {
state.isPathfound = false
}
} else {
if (_.includes(interestedNodes, name, 0)) {
state.isPathfound = true
if (!state.firstFoundNode) {
state.firstFoundNode = name
}
}
}
}
function getRelativePath () {
var tokens
var jsonPath
var index
if (resourcePath) {
var xpath = state.currentPath.substring(resourcePath.length)
if (!xpath) return
if (xpath[0] === '/') xpath = xpath.substring(1)
tokens = xpath.split('/')
jsonPath = tokens.join('.')
} else {
xpath = state.currentPath.substring(1)
tokens = xpath.split('/')
index = tokens.indexOf(state.firstFoundNode)
tokens = _.drop(tokens, index + 1)
jsonPath = tokens.join('.')
}
return jsonPath
}
}
function processError (err) {
var parser = this.parser
var error = ''
if (err) {
error = err
} else {
error = parser.getError()
}
error = new Error(error + ' at line no: ' + parser.getCurrentLineNumber())
this.emit('error', error)
return error
}
XmlParser.prototype._flush = function (callback) {
this.processChunk('')
callback()
}
module.exports = XmlParser

View File

@@ -1,13 +0,0 @@
function ParserState () {
this.currentPath = ''
this.lastEndedNode = ''
this.isPathfound = false
this.object = {}
this.paused = false
this.isRootNode = true
this.firstFoundNode = ''
this.interestedNodes = []
}
module.exports = ParserState

300
src/parser.ts Normal file
View File

@@ -0,0 +1,300 @@
/// <reference types="../typings/node-expat" />
import _ from "lodash";
import * as expat from "node-expat";
import stream from "stream";
import util from "util";
import { ParserState } from "./parserState";
const defaults = {
resourcePath: "",
emitOnNodeName: false,
attrsKey: "$",
textKey: "_",
explicitArray: true,
verbatimText: false
};
export interface IXmlParserOptions {
resourcePath?: string;
emitOnNodeName?: boolean;
attrsKey?: string;
textKey?: string;
explicitArray?: boolean;
verbatimText?: boolean;
}
export class XmlParser extends stream.Transform {
public parserState: ParserState;
private opts: IXmlParserOptions;
private _readableState: { objectMode: true, buffer: any };
private parser: expat.Parser;
constructor(opts?: IXmlParserOptions) {
super();
this.opts = _.defaults(opts, defaults);
this.parserState = new ParserState();
this.parser = new expat.Parser();
this._readableState.objectMode = true;
}
public checkForInterestedNodeListeners() {
const ignore = ["end", "prefinish", "data", "error"];
const eventNames = Object.keys((this as any)._events);
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < eventNames.length; i++) {
if (_.includes(ignore, eventNames[i], 0)) { continue; }
this.parserState.interestedNodes.push(eventNames[i]);
}
}
public _transform(chunk: Buffer | string, encoding: string, callback: () => void) {
if (encoding !== "buffer") { this.emit("error", new Error("unsupported encoding")); }
this.processChunk(chunk);
callback();
}
public processChunk(chunk: string | Buffer) {
const parser = this.parser;
const state = this.parserState;
if (state.isRootNode) {
this.checkForInterestedNodeListeners();
registerEvents.call(this);
}
if (typeof chunk === "string") {
if (!parser.parse("", true)) { processError.call(this); }
} else {
if (!parser.parse(chunk.toString())) { processError.call(this); }
}
}
public parse(chunk: Buffer | string, cb: (error: Error, data?: Buffer) => void) {
const parser = this.parser;
const state = this.parserState;
let error;
if (state.isRootNode) {
this.checkForInterestedNodeListeners();
registerEvents.call(this);
}
if (chunk instanceof Buffer) { chunk = chunk.toString(); }
this.on("error", (err) => {
error = err;
});
if (!parser.parse(chunk)) {
error = processError.call(this);
}
if (error) { return cb(error); }
const result = [];
while (this._readableState.buffer.length > 0) {
result.push(this._readableState.buffer.consume());
}
return cb(null, result as any);
}
public _flush(callback: () => void) {
this.processChunk("");
callback();
}
}
function registerEvents() {
const scope = this;
const parser: expat.Parser = this.parser;
const state = this.parserState;
let lastIndex;
const resourcePath = this.opts.resourcePath;
const attrsKey = this.opts.attrsKey;
const textKey = this.opts.textKey;
const interestedNodes = state.interestedNodes;
const explicitArray = this.opts.explicitArray;
const verbatimText = this.opts.verbatimText;
parser.on("startElement", (name, attrs) => {
if (state.isRootNode) { state.isRootNode = false; }
state.currentPath = state.currentPath + "/" + name;
checkForResourcePath(name);
if (state.isPathfound) { processStartElement(name, attrs); }
});
parser.on("endElement", (name) => {
state.lastEndedNode = name;
lastIndex = state.currentPath.lastIndexOf("/" + name);
state.currentPath = state.currentPath.substring(0, lastIndex);
if (state.isPathfound) { processEndElement(name); }
checkForResourcePath(name);
});
parser.on("text", (text) => {
if (state.isPathfound) { processText(text); }
});
parser.on("error", function(err) {
processError.call(this, err);
});
function processStartElement(name: string, attrs: any) {
if (!name) { return; }
const obj: any = {};
if (attrs && !_.isEmpty(attrs)) { obj[attrsKey] = attrs; }
let tempObj = state.object;
const path = getRelativePath(/*name*/);
if (!path) {
if (attrs && !_.isEmpty(attrs)) { state.object[attrsKey] = attrs; }
return;
}
const tokens = path.split(".");
for (let i = 0; i < tokens.length; i++) {
if (tempObj[tokens[i]] && !(explicitArray === false && i === tokens.length - 1)) {
tempObj = tempObj[tokens[i]];
} else {
// if explicitArray is true then create each node as array
// irrespective of how many nodes are there with same name.
tempObj[tokens[i]] = explicitArray ? [] : obj;
tempObj = tempObj[tokens[i]];
}
if (Array.isArray(tempObj) && i !== tokens.length - 1) { tempObj = tempObj[tempObj.length - 1]; }
}
if (Array.isArray(tempObj)) {
tempObj.push(obj);
}
}
function processEndElement(name: string) {
if (resourcePath) {
const index = resourcePath.lastIndexOf("/");
const rpath = resourcePath.substring(0, index);
if (rpath === state.currentPath) {
scope.push(state.object);
if (scope.opts.emitOnNodeName) { scope.emit(name, state.object); }
state.object = {};
}
} else {
if (_.includes(interestedNodes, name, 0)) {
emitInterestedNode(name);
if (state.firstFoundNode === name) {
state.object = {};
state.firstFoundNode = "";
state.isPathfound = false;
}
}
}
}
function emitInterestedNode(name: string) {
let index;
let xpath;
let pathTokens;
xpath = state.currentPath.substring(1);
pathTokens = xpath.split("/");
pathTokens.push(name);
index = pathTokens.indexOf(state.firstFoundNode);
pathTokens = _.drop(pathTokens, index + 1);
let tempObj = state.object;
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < pathTokens.length; i++) {
tempObj = tempObj[pathTokens[i] as any];
}
if (Array.isArray(tempObj)) { tempObj = tempObj[tempObj.length - 1]; }
scope.emit(name, tempObj);
scope.push(tempObj);
}
function processText(text: string) {
if ((!text) || ((!verbatimText) && !/\S/.test(text))) {
return;
}
const path = getRelativePath();
let tempObj = state.object;
if (!path) {
if (!state.object[textKey]) { state.object[textKey] = ""; }
state.object[textKey] = state.object[textKey] + text;
return;
}
const tokens = path.split(".");
for (let i = 0; i < tokens.length; i++) {
if (tempObj[tokens[i]]) {
tempObj = tempObj[tokens[i]];
} else {
tempObj[tokens[i]] = explicitArray ? [] : {};
tempObj = tempObj[tokens[i]];
}
if (Array.isArray(tempObj) && i !== tokens.length - 1) { tempObj = tempObj[tempObj.length - 1]; }
}
if (Array.isArray(tempObj)) {
const obj = tempObj[tempObj.length - 1];
if (!obj[textKey]) { obj[textKey] = ""; }
obj[textKey] = obj[textKey] + text;
} else {
if (!tempObj[textKey]) { tempObj[textKey] = ""; }
tempObj[textKey] = tempObj[textKey] + text;
}
}
function checkForResourcePath(name: string) {
if (resourcePath) {
if (state.currentPath.indexOf(resourcePath) === 0) {
state.isPathfound = true;
} else {
state.isPathfound = false;
}
} else {
if (_.includes(interestedNodes, name, 0)) {
state.isPathfound = true;
if (!state.firstFoundNode) {
state.firstFoundNode = name;
}
}
}
}
function getRelativePath() {
let tokens;
let jsonPath;
let index;
if (resourcePath) {
let xpath = state.currentPath.substring(resourcePath.length);
if (!xpath) { return; }
if (xpath[0] === "/") { xpath = xpath.substring(1); }
tokens = xpath.split("/");
jsonPath = tokens.join(".");
} else {
const xpath = state.currentPath.substring(1);
tokens = xpath.split("/");
index = tokens.indexOf(state.firstFoundNode);
tokens = _.drop(tokens, index + 1);
jsonPath = tokens.join(".");
}
return jsonPath;
}
}
function processError(err: Error) {
const parser = this.parser;
let error: Error = null;
if (err) {
error = err;
} else {
error = parser.getError();
}
error = new Error(error + " at line no: " + parser.getCurrentLineNumber());
this.emit("error", error);
return error;
}

11
src/parserState.ts Normal file
View File

@@ -0,0 +1,11 @@
export class ParserState {
public currentPath = "";
public lastEndedNode = "";
public isPathfound = false;
public object: any = {};
public paused = false;
public isRootNode = true;
public firstFoundNode = "";
public interestedNodes: string[] = [];
}

146
test/basic.spec.ts Normal file
View File

@@ -0,0 +1,146 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("Basic behavior", () => {
it("should properly parse a simple file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
const expectedData = [
{
$: { id: "1", test: "hello" },
subitem:
[{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{
$: { id: "2" },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}];
const actualData: string[] = [];
let dataEventCount = 0;
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("error", (err) => {
should(err).not.be.ok();
done(err);
});
parser.on("end", () => {
// console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData);
should(dataEventCount).equal(2);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a medium size file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
let dataEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(10);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a file containing many nodes.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
let dataEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(296);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a xml simple file in which nodes contain text values randomly.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/randomText.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
const expectedData = [{
$: { id: "1", test: "hello" }, _: " item one two",
subitem: [{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{
$: { id: "2" }, _: " item one two three four",
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}
];
const actualData: string[] = [];
let dataEventCount = 0;
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData);
should(dataEventCount).equal(2);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a huge file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
// console.log(parser)
let dataEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(2072);
done();
});
xmlStream.pipe(parser);
});
});

View File

@@ -0,0 +1,30 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("CData and comments in xml", () => {
it("should properly parse a simple file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/CData-comments.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
let dataEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(296);
done();
});
xmlStream.pipe(parser);
});
});

136
test/emit.spec.ts Normal file
View File

@@ -0,0 +1,136 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("emitOnNodeName", () => {
it("should properly emit events on node names.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true });
const expectedData = [
{
$: { id: "1", test: "hello" },
subitem:
[{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{
$: { id: "2" },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}];
const actualData: string[] = [];
const itemData : string[] = [];
let dataEventCount = 0;
let itemCount = 0;
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("item", (item) => {
itemData.push(item);
itemCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData);
should(dataEventCount).equal(2);
should(itemData).deepEqual(expectedData);
should(itemCount).equal(2);
done();
});
xmlStream.pipe(parser);
});
it("should properly emit events on node names while parsing a medium size file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml");
const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true });
let dataEventCount = 0;
let itemCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("item", (data) => {
itemCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(10);
should(itemCount).equal(10);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a file containing many nodes.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml");
const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true });
let dataEventCount = 0;
let itemCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("item", (data) => {
itemCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(296);
should(itemCount).equal(296);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a huge file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml");
const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true });
let dataEventCount = 0;
let itemCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("item", (item) => {
itemCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(2072);
should(itemCount).equal(2072);
done();
});
xmlStream.pipe(parser);
});
});

45
test/error.spec.ts Normal file
View File

@@ -0,0 +1,45 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("Error Handling", () => {
it("should properly return error if the xml file is corrupted.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/corrupted.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
let dataEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
// console.log(err)
should(err.message).equal("mismatched tag at line no: 11");
done();
});
xmlStream.pipe(parser);
});
it("should properly return error if the large xml file is corrupted.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/largeCorruptedFile.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
let dataEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
// console.log(err)
should(err.message).equal("mismatched tag at line no: 8346");
done();
});
xmlStream.pipe(parser);
});
});

250
test/explicit_array.spec.ts Normal file
View File

@@ -0,0 +1,250 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("should respect explicitArray constructor option", () => {
it("should properly parse a simple file with explicitArray set to false.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/item.xml");
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
const expectedData = [
{
$: { id: "1", test: "hello" },
subitem: { $: { sub: "2" }, _: "two" }
},
{
$: { id: "2" },
subitem: { _: "five" }
}];
parser.parse(xml.toString(), (err, data) => {
if (err) { done(err); }
// console.log('data=', JSON.stringify(data))
should(data).deepEqual(expectedData);
done();
});
});
it("should properly parse a medium size file with explicitArray set to false.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/medium.xml");
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
const expectedData = [
{
$: {
id: "1",
test: "hello"
},
subitem: {
$: {
sub: "2"
},
_: "two"
}
},
{
$: {
id: "2"
},
subitem: {
_: "five"
}
},
{
$: {
id: "3",
test: "hello"
},
subitem: {
$: {
sub: "2"
},
_: "two"
}
},
{
$: {
id: "4",
test: "hello"
},
subitem: {
$: {
sub: "2"
},
_: "two"
}
},
{
$: {
id: "5",
test: "hello"
},
subitem: {
$: {
sub: "2"
},
_: "two"
}
},
{
$: {
id: "6",
test: "hello"
},
subitem: {
$: {
sub: "2"
},
_: "two"
}
},
{
$: {
id: "7",
test: "hello"
},
subitem: {
$: {
sub: "2"
},
_: "two"
}
},
{
$: {
id: "8",
test: "hello"
},
subitem: {
$: {
sub: "2"
},
_: "two"
}
},
{
$: {
id: "9",
test: "hello"
},
subitem: {
$: {
sub: "2"
},
_: "two"
}
},
{
$: {
id: "10",
test: "hello"
},
subitem: {
$: {
sub: "2"
},
_: "two"
}
}
];
parser.parse(xml, (err, data) => {
if (err) { done(err); }
should(data).deepEqual(expectedData);
should(data.length).equal(10);
done();
});
});
it("should properly parse a file containing many nodes when explicitArray set to false.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/manyItems.xml");
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
parser.parse(xml, (err, data) => {
if (err) { done(err); }
should(data.length).equal(296);
done();
});
});
it("should properly parse a xml simple file in which nodes contain text values randomly when explicitArray set to false.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/randomText.xml");
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
const expectedData = [{
$: { id: "1", test: "hello" }, _: " item one two",
subitem: { $: { sub: "2" }, _: "two" }
},
{
$: { id: "2" }, _: " item one two three four",
subitem: { _: "five" }
}
];
parser.parse(xml, (err, data) => {
if (err) { done(err); }
should(data).deepEqual(expectedData);
should(data.length).equal(2);
done();
});
});
it("should properly parse a huge file with explicitArray set to false.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/hugeFile.xml");
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
// console.log(parser)
parser.parse(xml, (err, data) => {
if (err) { done(err); }
should(data.length).equal(2072);
done();
});
});
it("should properly return error if the xml file is corrupted.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/corrupted.xml");
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
parser.parse(xml, (err, data) => {
// console.log(err)
should(err.message).equal("mismatched tag at line no: 11");
should(data).not.be.ok();
done();
});
});
it("should properly generate objects when special symbols are passed as attrs and text keys and explicitArray is false in the options.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
const parser = new XmlParser({ resourcePath: "/items/item", attrsKey: "!", textKey: "%", explicitArray: false });
const expectedData = [
{
"!": { id: "1", test: "hello" },
"subitem": { "!": { sub: "2" }, "%": "two" }
},
{
"!": { id: "2" },
"subitem": { "%": "five" }
}];
const actualData : string[] = [];
let dataEventCount = 0;
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData);
should(dataEventCount).equal(2);
done();
});
xmlStream.pipe(parser);
});
});

321
test/interested.spec.ts Normal file
View File

@@ -0,0 +1,321 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("interested Nodes", () => {
it("should properly parse a simple file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
const parser = new XmlParser();
const expectedData =
[
{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" },
{
$: { id: "1", test: "hello" },
subitem: [{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{ _: "three" },
{ _: "four" },
{ _: "five" },
{
$: { id: "2" },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}
];
const actualData: string[] = [];
let dataEventCount = 0;
const expectedItems = [
{
$: { id: "1", test: "hello" },
subitem:
[{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{
$: { id: "2" },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}];
const actualItems: string[] = [];
const actualSubitems: string[] = [];
const expectedSubitems = [
{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" },
{ _: "three" },
{ _: "four" },
{ _: "five" }
];
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("error", (err) => {
should(err).not.be.ok();
done(err);
});
parser.on("item", (item) => {
actualItems.push(item);
});
parser.on("subitem", (subitem) => {
actualSubitems.push(subitem);
});
parser.on("end", () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData);
should(actualItems).deepEqual(expectedItems);
should(actualSubitems).deepEqual(expectedSubitems);
should(actualSubitems.length).equal(5);
should(actualItems.length).equal(2);
should(dataEventCount).equal(7);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a medium size file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml");
const parser = new XmlParser();
let dataEventCount = 0;
let itemEventCount = 0;
let subitemEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("item", (item) => {
itemEventCount++;
});
parser.on("subitem", (subitem) => {
subitemEventCount++;
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemEventCount)
// console.log('subitemEventCount=', subitemEventCount)
should(dataEventCount).equal(31);
should(itemEventCount).equal(10);
should(subitemEventCount).equal(21);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a file containing many nodes.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml");
const parser = new XmlParser();
let dataEventCount = 0;
let itemEventCount = 0;
let subitemEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("item", (item) => {
itemEventCount++;
});
parser.on("subitem", (subitem) => {
subitemEventCount++;
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemEventCount)
// console.log('subitemEventCount=', subitemEventCount)
should(itemEventCount).equal(296);
should(subitemEventCount).equal(600);
should(dataEventCount).equal(896);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a xml simple file in which nodes contain text values randomly.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/randomText.xml");
const parser = new XmlParser();
const expectedData =
[
{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" },
{
$: { id: "1", test: "hello" }, _: " item one two",
subitem: [{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{ _: "three" },
{ _: "four" },
{ _: "five" },
{
$: { id: "2" }, _: " item one two three four",
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}
];
const expectedItems = [
{
$: { id: "1", test: "hello" }, _: " item one two",
subitem:
[{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{
$: { id: "2" }, _: " item one two three four",
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}];
const actualItems: string[] = [];
const actualSubitems: string[] = [];
const expectedSubitems = [
{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" },
{ _: "three" },
{ _: "four" },
{ _: "five" }
];
const actualData: string[] = [];
let dataEventCount = 0;
let itemEventCount = 0;
let subitemEventCount = 0;
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("item", (item) => {
itemEventCount++;
actualItems.push(item);
});
parser.on("subitem", (subitem) => {
subitemEventCount++;
actualSubitems.push(subitem);
});
parser.on("end", () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemEventCount)
// console.log('subitemEventCount=', subitemEventCount)
should(actualData).deepEqual(expectedData);
should(actualItems).deepEqual(expectedItems);
should(actualSubitems).deepEqual(expectedSubitems);
should(dataEventCount).equal(7);
should(itemEventCount).equal(2);
should(subitemEventCount).equal(5);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a huge file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml");
const parser = new XmlParser();
let dataEventCount = 0;
let itemEventCount = 0;
let subitemEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("item", (item) => {
itemEventCount++;
});
parser.on("subitem", (subitem) => {
subitemEventCount++;
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemEventCount)
// console.log('subitemEventCount=', subitemEventCount)
should(dataEventCount).equal(6272);
should(itemEventCount).equal(2072);
should(subitemEventCount).equal(4200);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a simple file and return when root element when listening on it.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
const parser = new XmlParser();
const expectedData =
[{
item: [{
$: { id: "1", test: "hello" },
subitem: [{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{
$: { id: "2" }, subitem: [{ _: "three" }, { _: "four" },
{ _: "five" }]
}]
}];
const actualData: string[] = [];
let dataEventCount = 0;
let itemsEventCount = 0;
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("error", (err) => {
should(err).not.be.ok();
done(err);
});
parser.on("items", (item) => {
itemsEventCount++;
});
parser.on("end", () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemsEventCount)
should(actualData).deepEqual(expectedData);
should(itemsEventCount).equal(1);
should(dataEventCount).equal(1);
done();
});
xmlStream.pipe(parser);
});
});

117
test/options.spec.ts Normal file
View File

@@ -0,0 +1,117 @@
import fs from "fs";
import "mocha";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("should respect the options passed", () => {
it("should properly generate objects with $ as key for attrs and _ as key for text value of node.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
const expectedData = [
{
$: { id: "1", test: "hello" },
subitem:
[{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{
$: { id: "2" },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}];
const actualData: string[] = [];
let dataEventCount = 0;
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData);
should(dataEventCount).equal(2);
done();
});
xmlStream.pipe(parser);
});
it("should properly generate objects with passed attrs and text keys in the options.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
const parser = new XmlParser({ resourcePath: "/items/item", attrsKey: "attrs", textKey: "text" });
const expectedData = [
{
attrs: { id: "1", test: "hello" },
subitem:
[{ attrs: { sub: "TESTING SUB" }, text: "one" },
{ attrs: { sub: "2" }, text: "two" }]
},
{
attrs: { id: "2" },
subitem: [{ text: "three" }, { text: "four" }, { text: "five" }]
}];
const actualData: string[] = [];
let dataEventCount = 0;
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData);
should(dataEventCount).equal(2);
done();
});
xmlStream.pipe(parser);
});
it("should properly generate objects when special symbols are passed as attrs and text keys in the options.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
const parser = new XmlParser({ resourcePath: "/items/item", attrsKey: "!", textKey: "%" });
const expectedData = [
{
"!": { id: "1", test: "hello" },
"subitem":
[{ "!": { sub: "TESTING SUB" }, "%": "one" },
{ "!": { sub: "2" }, "%": "two" }]
},
{
"!": { id: "2" },
"subitem": [{ "%": "three" }, { "%": "four" }, { "%": "five" }]
}];
const actualData: string[] = [];
let dataEventCount = 0;
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData);
should(dataEventCount).equal(2);
done();
});
xmlStream.pipe(parser);
});
});

97
test/parse.spec.ts Normal file
View File

@@ -0,0 +1,97 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("Parse function should work properly", () => {
it("should properly parse a simple file.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/item.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
const expectedData = [
{
$: { id: "1", test: "hello" },
subitem:
[{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{
$: { id: "2" },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}];
parser.parse(xml.toString(), (err, data) => {
if (err) { done(err); }
should(data).deepEqual(expectedData);
done();
});
});
it("should properly parse a medium size file.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/medium.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
parser.parse(xml, (err, data) => {
if (err) { done(err); }
should(data.length).equal(10);
done();
});
});
it("should properly parse a file containing many nodes.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/manyItems.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
parser.parse(xml, (err, data) => {
if (err) { done(err); }
should(data.length).equal(296);
done();
});
});
it("should properly parse a xml simple file in which nodes contain text values randomly.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/randomText.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
const expectedData = [{
$: { id: "1", test: "hello" }, _: " item one two",
subitem: [{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{
$: { id: "2" }, _: " item one two three four",
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}
];
parser.parse(xml, (err, data) => {
if (err) { done(err); }
should(data).deepEqual(expectedData);
should(data.length).equal(2);
done();
});
});
it("should properly parse a huge file.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/hugeFile.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
// console.log(parser)
parser.parse(xml, (err, data) => {
if (err) { done(err); }
should(data.length).equal(2072);
done();
});
});
it("should properly return error if the xml file is corrupted.", (done) => {
const xml = fs.readFileSync("./test/TestFiles/corrupted.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
parser.parse(xml, (err, data) => {
// console.log(err)
should(err.message).equal("mismatched tag at line no: 11");
should(data).not.be.ok();
done();
});
});
});

View File

@@ -0,0 +1,82 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("pause and resume", () => {
it("should properly parse a simple file.", function(done) {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
const expectedData = [
{
$: { id: "1", test: "hello" },
subitem:
[{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{
$: { id: "2" },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}];
const actualData: string[] = [];
let dataEventCount = 0;
let isSetTimeoutHappened = true;
this.timeout(4000);
parser.on("data", (data) => {
actualData.push(data);
parser.pause();
should(isSetTimeoutHappened).equal(true);
setTimeout(() => {
parser.resume();
isSetTimeoutHappened = true;
}, 1000);
dataEventCount++;
isSetTimeoutHappened = false;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData);
should(dataEventCount).equal(2);
done();
});
xmlStream.pipe(parser);
});
it("should emit data events with 1sec interval between each using pause and resume.", function(done) {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
let dataEventCount = 0;
let isSetTimeoutHappened = true;
this.timeout(20000);
parser.on("data", (data) => {
parser.pause();
should(isSetTimeoutHappened).equal(true);
setTimeout(() => {
parser.resume();
isSetTimeoutHappened = true;
}, 1000);
dataEventCount++;
isSetTimeoutHappened = false;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(10);
done();
});
xmlStream.pipe(parser);
});
});

103
test/performance.spec.ts Normal file
View File

@@ -0,0 +1,103 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe.skip("performance testing", () => {
it("should properly parse more than 500 MB of file.", function(done) {
const parser = new XmlParser({ resourcePath: "/items/item" });
// var wsStream = fs.createWriteStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml')
// var rsStream = fs.createReadStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml')
let dataEventCount = 0;
// var maxRSSMemoryTaken = 0
// var rss
const startTime = Date.now();
const xmlStream = new stream.Readable();
xmlStream._read = function noop() {
// nop
};
let dataChunk;
this.timeout(900000);
const firstChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/firstChunk.xml");
xmlStream.push(firstChunk);
for (let i = 0; i < 2200; i++) {
dataChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/repetitiveChunk.xml");
xmlStream.push(dataChunk);
}
const endingChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/endingChunk.xml");
xmlStream.push(endingChunk);
xmlStream.push(null);
parser.on("data", (data) => {
// rss = process.memoryUsage().rss
// if (rss > maxRSSMemoryTaken) maxRSSMemoryTaken = rss
dataEventCount++;
});
parser.on("error", (err) => {
should(err).not.be.ok();
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
// console.log('RSS memory=', rss)
const TimeTaken = Date.now() - startTime;
// console.log('time taken=', TimeTaken)
should(TimeTaken).be.belowOrEqual(300000);
should(dataEventCount).equal(4558400);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse more than 1 GB of file.", function(done) {
const parser = new XmlParser({ resourcePath: "/items/item" });
// var wsStream = fs.createWriteStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml')
// var rsStream = fs.createReadStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml')
let dataEventCount = 0;
// var maxRSSMemoryTaken = 0
// var rss
const startTime = Date.now();
const xmlStream = new stream.Readable();
xmlStream._read = function noop() {
// nop
};
let dataChunk;
this.timeout(900000);
const firstChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/firstChunk.xml");
xmlStream.push(firstChunk);
for (let i = 0; i < 4400; i++) {
dataChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/repetitiveChunk.xml");
xmlStream.push(dataChunk);
}
const endingChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/endingChunk.xml");
xmlStream.push(endingChunk);
xmlStream.push(null);
parser.on("data", (data) => {
// rss = process.memoryUsage().rss
// if (rss > maxRSSMemoryTaken) maxRSSMemoryTaken = rss
dataEventCount++;
});
parser.on("error", (err) => {
should(err).not.be.ok();
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
// console.log('RSS memory=', rss)
const TimeTaken = Date.now() - startTime;
// console.log('time taken=', TimeTaken)
should(TimeTaken).be.belowOrEqual(700000);
should(dataEventCount).equal(9116800);
done();
});
xmlStream.pipe(parser);
});
});

105
test/read.spec.ts Normal file
View File

@@ -0,0 +1,105 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("read method", () => {
it("should properly parse a simple file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
const expectedData = [
{
$: { id: "1", test: "hello" },
subitem:
[{ $: { sub: "TESTING SUB" }, _: "one" },
{ $: { sub: "2" }, _: "two" }]
},
{
$: { id: "2" },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
}];
const actualData: string[] = [];
let obj;
let Timeout: any;
parser.on("readable", () => {
Timeout = setInterval(() => {
obj = parser.read();
if (obj) {
actualData.push(obj);
}
obj = null;
}, 50);
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount)
clearInterval(Timeout);
should(actualData).deepEqual(expectedData);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a file containing many nodes.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
let objCount = 0;
const endEventOcurred = false;
parser.on("readable", () => {
read();
});
function read() {
while (parser.read()) { objCount++; }
if (!endEventOcurred) { setTimeout(read, 50); }
}
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log(objCount)
should(objCount).deepEqual(296);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a huge.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
let objCount = 0;
const endEventOcurred = false;
parser.on("readable", () => {
read();
});
function read() {
while (parser.read()) { objCount++; }
if (!endEventOcurred) { setTimeout(read, 50); }
}
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log(objCount)
should(objCount).deepEqual(2072);
done();
});
xmlStream.pipe(parser);
});
});

111
test/same_name.spec.ts Normal file
View File

@@ -0,0 +1,111 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("nodes with same names", () => {
it("should properly parse a simple file containing nodes with same names.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/nodesWithSameNames.xml");
const parser = new XmlParser();
const actualData: string[] = [];
const actualItems: string[] = [];
let dataEventCount = 0;
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("error", (err) => {
should(err).not.be.ok();
done(err);
});
parser.on("item", (item) => {
actualItems.push(item);
});
parser.on("end", () => {
should(actualItems.length).equal(18);
should(dataEventCount).equal(18);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a simple file containing nodes with same names and emit events on multiple nodes.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/nodesWithSameNames.xml");
const parser = new XmlParser();
let dataEventCount = 0;
let itemEventCount = 0;
let subitemEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
should(err).not.be.ok();
done(err);
});
parser.on("item", (item) => {
itemEventCount++;
});
parser.on("subitem", (subitem) => {
subitemEventCount++;
});
parser.on("end", () => {
should(itemEventCount).equal(18);
should(subitemEventCount).equal(13);
should(dataEventCount).equal(31);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a medium size file with same names randomly.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/nodesWithSameNamesRandomly.xml");
const parser = new XmlParser();
let dataEventCount = 0;
let itemEventCount = 0;
let subitemEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("item", (item) => {
itemEventCount++;
});
parser.on("subitem", (subitem) => {
subitemEventCount++;
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemEventCount)
// console.log('subitemEventCount=', subitemEventCount)
should(dataEventCount).equal(32);
should(itemEventCount).equal(19);
should(subitemEventCount).equal(13);
done();
});
xmlStream.pipe(parser);
});
});

File diff suppressed because it is too large Load Diff

64
test/uncompressed.spec.ts Normal file
View File

@@ -0,0 +1,64 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("should properly handle uncompressed files", () => {
it("should properly parse a uncompressed xml file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
const gzip = zlib.createGzip();
const gunzip = zlib.createGunzip();
let dataEventCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(10);
done();
});
xmlStream.pipe(gzip).pipe(gunzip).pipe(parser);
});
it("should properly parse uncompressed file and go fine with pause and resume.", function(done) {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
const gzip = zlib.createGzip();
const gunzip = zlib.createGunzip();
let dataEventCount = 0;
let isSetTimeoutHappened = true;
this.timeout(20000);
parser.on("data", (data) => {
parser.pause();
should(isSetTimeoutHappened).equal(true);
setTimeout(() => {
parser.resume();
isSetTimeoutHappened = true;
}, 2000);
dataEventCount++;
isSetTimeoutHappened = false;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(10);
done();
});
xmlStream.pipe(gzip).pipe(gunzip).pipe(parser);
});
});

View File

@@ -0,0 +1,99 @@
import fs from "fs";
import "mocha";
import should from "should";
import stream from "stream";
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe("wrong resourcePath", () => {
it("should be able to detect the wrong resourcePath at root level.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
const parser = new XmlParser({ resourcePath: "/wrong/noNodes", emitOnNodeName: true });
const actualData : string[] = [];
const itemData : string[] = [];
let dataEventCount = 0;
let itemCount = 0;
parser.on("data", (data) => {
actualData.push(data);
dataEventCount++;
});
parser.on("item", (item) => {
itemData.push(item);
itemCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount)
should(actualData.length).equal(0);
should(dataEventCount).equal(0);
should(itemData.length).equal(0);
should(itemCount).equal(0);
done();
});
xmlStream.pipe(parser);
});
it("should be able to detect wrong resourcePath while parsing xml", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml");
const parser = new XmlParser({ resourcePath: "/wrong/noNodes", emitOnNodeName: true });
let dataEventCount = 0;
let itemCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("item", (data) => {
itemCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(0);
should(itemCount).equal(0);
done();
});
xmlStream.pipe(parser);
});
it("should properly parse a huge file.", (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml");
const parser = new XmlParser({ resourcePath: "/wrong/path", emitOnNodeName: true });
let dataEventCount = 0;
let itemCount = 0;
parser.on("data", (data) => {
dataEventCount++;
});
parser.on("item", (item) => {
itemCount++;
});
parser.on("error", (err) => {
done(err);
});
parser.on("end", () => {
// console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(0);
should(itemCount).equal(0);
done();
});
xmlStream.pipe(parser);
});
});

33
tsconfig.json Normal file
View File

@@ -0,0 +1,33 @@
{
"compilerOptions": {
"types": [
"node"
],
"outDir": "dist",
"moduleResolution": "node",
"module": "commonjs",
"removeComments": true,
"sourceMap": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports":true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"declaration": true,
"resolveJsonModule": true,
"target":"es2017",
"lib": ["es2017"],
"typeRoots": [
"./node_modules/@types",
"./typings"
]
},
"include": [
"./typings/**/*.d.ts",
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}

22
tslint.json Normal file
View File

@@ -0,0 +1,22 @@
{
"rules": {
"no-console": [false],
"variable-name": false,
"radix": false,
"object-literal-sort-keys": false,
"trailing-comma":[false],
"indent": [true,"tabs"],
"max-line-length": [false],
"no-string-literal": false,
"class-name": false,
"no-namespace": [false],
"no-bitwise": false
},
"extends": "tslint:recommended",
"linterOptions": {
"exclude":[
"*.json",
"**/*.json"
]
}
}

91
typings/node-expat/index.d.ts vendored Normal file
View File

@@ -0,0 +1,91 @@
// Type definitions for node-expat 2.3.x
// Project: http://nodejs.org/
// Definitions by: Microsoft TypeScript <http://typescriptlang.org>
// Oleksandr Fedorchuk <https://github.com/kol-93>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
declare module "node-expat" {
import { Stream } from "stream";
import { EventEmitter } from "events";
interface NameSpace<ValueType = any> {
[key: string]: ValueType;
}
interface TypedEmitter<EventMapType extends NameSpace<any[]>> extends EventEmitter {
addListener<Event extends keyof EventMapType> (event: Event, listener: (...args: EventMapType[Event]) => void): this;
on<Event extends keyof EventMapType> (event: Event, listener: (...args: EventMapType[Event]) => void): this;
once<Event extends keyof EventMapType> (event: Event, listener: (...args: EventMapType[Event]) => void): this;
prependListener<Event extends keyof EventMapType> (event: Event, listener: (...args: EventMapType[Event]) => void): this;
prependOnceListener<Event extends keyof EventMapType> (event: Event, listener: (...args: EventMapType[Event]) => void): this;
removeListener<Event extends keyof EventMapType> (event: Event, listener: (...args: EventMapType[Event]) => void): this;
emit<Event extends keyof EventMapType> (event: Event, ...args: EventMapType[Event]): boolean;
addListener (event: string | symbol, listener: (...args: any[]) => void): this;
on (event: string | symbol, listener: (...args: any[]) => void): this;
once (event: string | symbol, listener: (...args: any[]) => void): this;
prependListener (event: string | symbol, listener: (...args: any[]) => void): this;
prependOnceListener (event: string | symbol, listener: (...args: any[]) => void): this;
removeListener (event: string | symbol, listener: (...args: any[]) => void): this;
emit (event: string | symbol, ...args: any[]): boolean;
}
interface ParserEventsMap extends NameSpace<any[]> {
startElement: [string, NameSpace<string>];
endElement: [string];
text: [string];
comment: [string];
processingInstruction: [string, string];
xmlDecl: [string | null, string | null, boolean];
startCdata: [];
endCdata: [];
entityDecl: [string | null, boolean, string | null, string | null, string | null, string | null, string | null];
end: [];
close: [];
error: [string | Error];
}
export class Parser extends Stream implements NodeJS.WritableStream, TypedEmitter<ParserEventsMap>
{
readonly writable: boolean;
stop(): this;
pause(): this;
resume(): this;
parse(buf: string | Buffer, isFinal?: boolean): boolean;
setEncoding(value: string): this;
/// @TODO setUnknownEncoding
getError(): string | null;
destroy(): this;
destroySoon(): this;
write(buffer: Buffer | string, cb?: Function): boolean;
write(str: string, encoding?: string, cb?: Function): boolean;
end(cb?: Function): boolean;
end(chunk: Buffer, cb?: Function): boolean;
end(chunk: string, cb?: Function): boolean;
end(chunk: string, encding?: string, cb?: Function): boolean;
reset(this: Parser): this;
getCurrentLineNumber(this: Parser): number;
getCurrentColumnNumber(this: Parser): number;
getCurrentByteIndex(this: Parser): number;
on (event: "startElement", listener:(name:string, attrs: any)=>void):this;
on (event: "endElement", listener:(name:string)=>void):this;
on (event: "text", listener:(text:string)=>void):this;
on (event: "processingInstruction", listener:(target:any, data:any)=>void):this;
on (event: "comment", listener:(s:string)=>void):this;
on (event: "xmlDecl", listener:(version:string, encoding: string, standalone: boolean)=>void):this;
on (event: "startCdata", listener:()=>void):this;
on (event: "endCdata", listener:()=>void):this;
on (event: "entityDecl", listener:(entityName:string, isParameterEntity:boolean, value:string, base:string, systemId:string, publicId:string, notationName:string)=>void):this;
on (event: "error", listener:(e:Error)=>void):this;
on (event: "close", listener:()=>void):this;
}
export function createParser(callback?: (name: string, attributes: NameSpace<string>) => void): Parser;
}

View File

@@ -0,0 +1,3 @@
{
}