├── .esdoc.json
├── .eslintignore
├── .eslintrc.json
├── .gitattributes
├── .github
└── workflows
│ ├── linter.yml
│ └── test.yml
├── .gitignore
├── LICENSE
├── README.md
├── docs
├── ast
│ └── source
│ │ ├── .external-ecmascript.js.json
│ │ ├── Parser.js.json
│ │ ├── errors
│ │ ├── NotImplemented.js.json
│ │ └── ParserError.js.json
│ │ ├── index.js.json
│ │ └── strategies
│ │ ├── Base.js.json
│ │ ├── Csv.js.json
│ │ ├── Json.js.json
│ │ ├── Xml.js.json
│ │ ├── Xml
│ │ ├── XmlTag.js.json
│ │ └── index.js.json
│ │ ├── Yaml.js.json
│ │ └── index.js.json
├── badge.svg
├── coverage.json
├── css
│ ├── github.css
│ ├── identifiers.css
│ ├── manual.css
│ ├── prettify-tomorrow.css
│ ├── search.css
│ ├── source.css
│ ├── style.css
│ └── test.css
├── file
│ └── src
│ │ ├── Parser.js.html
│ │ ├── errors
│ │ ├── NotImplemented.js.html
│ │ └── ParserError.js.html
│ │ ├── index.js.html
│ │ └── strategies
│ │ ├── Base.js.html
│ │ ├── Csv.js.html
│ │ ├── Json.js.html
│ │ ├── Xml.js.html
│ │ ├── Xml
│ │ ├── XmlTag.js.html
│ │ └── index.js.html
│ │ ├── Yaml.js.html
│ │ └── index.js.html
├── identifiers.html
├── image
│ ├── badge.svg
│ ├── esdoc-logo-mini-black.png
│ ├── esdoc-logo-mini.png
│ ├── github.png
│ ├── manual-badge.svg
│ └── search.png
├── index.html
├── index.json
├── inject
│ └── css
│ │ └── 0-parser.css
├── lint.json
├── manual
│ ├── README.html
│ ├── csv.html
│ ├── index.html
│ ├── json.html
│ ├── xml.html
│ └── yaml.html
├── script
│ ├── inherited-summary.js
│ ├── inner-link.js
│ ├── manual.js
│ ├── patch-for-local.js
│ ├── prettify
│ │ ├── Apache-License-2.0.txt
│ │ └── prettify.js
│ ├── pretty-print.js
│ ├── search.js
│ ├── search_index.js
│ └── test-summary.js
└── source.html
├── esdocs
├── css
│ └── parser.css
└── manual
│ ├── csv.md
│ ├── json.md
│ ├── xml.md
│ └── yaml.md
├── package-lock.json
├── package.json
├── src
├── Parser.js
├── errors
│ ├── NotImplemented.js
│ └── ParserError.js
├── index.js
└── strategies
│ ├── Base.js
│ ├── Csv.js
│ ├── Json.js
│ ├── Xml
│ ├── XmlTag.js
│ └── index.js
│ ├── Yaml.js
│ └── index.js
└── tests
├── Parser.test.js
├── data
└── services.json
└── strategies
├── Base.test.js
├── Csv.test.js
├── Json.test.js
├── Xml.test.js
└── Yaml.test.js
/.esdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "source": "./src",
3 | "destination": "./docs",
4 | "plugins": [
5 | {
6 | "name": "esdoc-standard-plugin",
7 | "brand": {
8 | "title": "parser - the all-in-one filetype parser for javascript",
9 | "description": "The easiest parser for JSON, XML, CSV and YAML. Use it as simple as JSON.stringify() or JSON.parse(). All in one place."
10 | },
11 | "option": {
12 | "manual": {
13 | "index": "./README.md",
14 | "asset": "",
15 | "globalIndex": true,
16 | "files": [
17 | "./esdocs/manual/json.md",
18 | "./esdocs/manual/csv.md",
19 | "./esdocs/manual/xml.md",
20 | "./esdocs/manual/yaml.md"
21 | ]
22 | }
23 | }
24 | },
25 | {
26 | "name": "esdoc-ecmascript-proposal-plugin",
27 | "option": { "all": true }
28 | },
29 | {
30 | "name": "esdoc-inject-style-plugin",
31 | "option": {
32 | "enable": true,
33 | "styles": [
34 | "./esdocs/css/parser.css"
35 | ]
36 | }
37 | }
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "commonjs": true,
4 | "es6": true,
5 | "jest": true,
6 | "node": true
7 | },
8 | "extends": [
9 | "standard"
10 | ],
11 | "globals": {
12 | "Atomics": "readonly",
13 | "SharedArrayBuffer": "readonly"
14 | },
15 | "parserOptions": {
16 | "ecmaVersion": 11
17 | },
18 | "rules": {
19 | },
20 | "ignorePatterns": ["docs/**"]
21 | }
22 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.github/workflows/linter.yml:
--------------------------------------------------------------------------------
1 | name: linter
2 | on: push
3 | jobs:
4 | linter:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v2
8 | - name: Install modules
9 | run: npm install
10 | - name: Run linter
11 | run: npm run lint
12 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: push
3 | jobs:
4 | test:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v2
8 | - name: Install modules
9 | run: npm install
10 | - name: Run tests
11 | run: npm run test
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | nohup.out
4 | parserblade.tar.gz
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 Matheus Hernandes (onhernandes.github.io)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # parserblade
2 |
3 | 
4 |
5 | A all-in-one parser for Javascript, heavily inspired by: https://github.com/nathanmac/Parser. It allows you to work with JSON, XML, CSV and YAML more without worrying about which module install. It's designed to work just as `JSON.parse` and `JSON.stringify` does, with some improvements.
6 |
7 | See [docs](https://onhernandes.github.io/parserblade) for more info and examples.
8 |
9 | ## Installation
10 |
11 | ```sh
12 | $ npm install --save parserblade
13 | ```
14 |
15 | ## Usage
16 |
17 | Every filetype has two main methods: `stringify()` and `parse()`, both receives two arguments, `data` containing any type of data and an options object.
18 |
19 | ### CSV
20 |
21 | ```javascript
22 | const { csv } = require('parserblade')
23 |
24 | // Parsing
25 | const input = 'title,platform\nStardew Valley,Steam'
26 | const result = csv.parse(input)
27 | console.log(result) // [ { title: 'Stardew Valley', platform: 'Steam' } ]
28 |
29 | // Stringifying
30 | console.log(
31 | csv.stringify(result)
32 | ) // 'title,platform\nStardew Valley,Steam'
33 | ```
34 |
35 | ### YAML
36 |
37 | ```javascript
38 | const { yaml } = require('parserblade')
39 |
40 | // Parsing
41 | const input = 'title: Stardew Valley\nplatform: Steam'
42 | const result = yaml.parse(input)
43 | console.log(result) // { title: 'Stardew Valley', platform: 'Steam' }
44 |
45 | // Stringifying
46 | console.log(
47 | yaml.stringify(result)
48 | ) // 'title: Stardew Valley\nplatform: Steam'
49 | ```
50 |
51 | ### XML
52 |
53 | ```javascript
54 | const { xml } = require('parserblade')
55 |
56 | // Parsing
57 | const input = 'lodash '
58 | const result = xml.parse(input)
59 | console.log(result) // { package: 'lodash' }
60 |
61 | // Stringifying
62 | console.log(
63 | xml.stringify(result)
64 | ) // 'lodash '
65 | ```
66 |
67 | ## License
68 |
69 | MIT ©
70 |
--------------------------------------------------------------------------------
/docs/badge.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | document
13 | document
14 | 51%
15 | 51%
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/coverage.json:
--------------------------------------------------------------------------------
1 | {
2 | "coverage": "51.47%",
3 | "expectCount": 68,
4 | "actualCount": 35,
5 | "files": {
6 | "src/Parser.js": {
7 | "expectCount": 8,
8 | "actualCount": 6,
9 | "undocumentLines": [
10 | 55,
11 | 57
12 | ]
13 | },
14 | "src/errors/NotImplemented.js": {
15 | "expectCount": 1,
16 | "actualCount": 1,
17 | "undocumentLines": []
18 | },
19 | "src/errors/ParserError.js": {
20 | "expectCount": 1,
21 | "actualCount": 1,
22 | "undocumentLines": []
23 | },
24 | "src/index.js": {
25 | "expectCount": 2,
26 | "actualCount": 0,
27 | "undocumentLines": [
28 | 1,
29 | 7
30 | ]
31 | },
32 | "src/strategies/Base.js": {
33 | "expectCount": 9,
34 | "actualCount": 7,
35 | "undocumentLines": [
36 | 1,
37 | 2
38 | ]
39 | },
40 | "src/strategies/Csv.js": {
41 | "expectCount": 11,
42 | "actualCount": 5,
43 | "undocumentLines": [
44 | 1,
45 | 2,
46 | 3,
47 | 4,
48 | 5,
49 | 6
50 | ]
51 | },
52 | "src/strategies/Json.js": {
53 | "expectCount": 8,
54 | "actualCount": 5,
55 | "undocumentLines": [
56 | 1,
57 | 2,
58 | 3
59 | ]
60 | },
61 | "src/strategies/Xml/XmlTag.js": {
62 | "expectCount": 4,
63 | "actualCount": 0,
64 | "undocumentLines": [
65 | 1,
66 | 7,
67 | 14,
68 | 18
69 | ]
70 | },
71 | "src/strategies/Xml/index.js": {
72 | "expectCount": 14,
73 | "actualCount": 7,
74 | "undocumentLines": [
75 | 1,
76 | 2,
77 | 3,
78 | 4,
79 | 5,
80 | 6,
81 | 7
82 | ]
83 | },
84 | "src/strategies/Yaml.js": {
85 | "expectCount": 6,
86 | "actualCount": 3,
87 | "undocumentLines": [
88 | 1,
89 | 2,
90 | 3
91 | ]
92 | },
93 | "src/strategies/index.js": {
94 | "expectCount": 4,
95 | "actualCount": 0,
96 | "undocumentLines": [
97 | 1,
98 | 2,
99 | 3,
100 | 4
101 | ]
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/docs/css/github.css:
--------------------------------------------------------------------------------
1 | /* github markdown */
2 | .github-markdown {
3 | font-size: 16px;
4 | }
5 |
6 | .github-markdown h1,
7 | .github-markdown h2,
8 | .github-markdown h3,
9 | .github-markdown h4,
10 | .github-markdown h5 {
11 | margin-top: 1em;
12 | margin-bottom: 16px;
13 | font-weight: bold;
14 | padding: 0;
15 | }
16 |
17 | .github-markdown h1:nth-of-type(1) {
18 | margin-top: 0;
19 | }
20 |
21 | .github-markdown h1 {
22 | font-size: 2em;
23 | padding-bottom: 0.3em;
24 | }
25 |
26 | .github-markdown h2 {
27 | font-size: 1.75em;
28 | padding-bottom: 0.3em;
29 | }
30 |
31 | .github-markdown h3 {
32 | font-size: 1.5em;
33 | }
34 |
35 | .github-markdown h4 {
36 | font-size: 1.25em;
37 | }
38 |
39 | .github-markdown h5 {
40 | font-size: 1em;
41 | }
42 |
43 | .github-markdown ul, .github-markdown ol {
44 | padding-left: 2em;
45 | }
46 |
47 | .github-markdown pre > code {
48 | font-size: 0.85em;
49 | }
50 |
51 | .github-markdown table {
52 | margin-bottom: 1em;
53 | border-collapse: collapse;
54 | border-spacing: 0;
55 | }
56 |
57 | .github-markdown table tr {
58 | background-color: #fff;
59 | border-top: 1px solid #ccc;
60 | }
61 |
62 | .github-markdown table th,
63 | .github-markdown table td {
64 | padding: 6px 13px;
65 | border: 1px solid #ddd;
66 | }
67 |
68 | .github-markdown table tr:nth-child(2n) {
69 | background-color: #f8f8f8;
70 | }
71 |
72 | .github-markdown hr {
73 | border-right: 0;
74 | border-bottom: 1px solid #e5e5e5;
75 | border-left: 0;
76 | border-top: 0;
77 | }
78 |
79 | /** badge(.svg) does not have border */
80 | .github-markdown img:not([src*=".svg"]) {
81 | max-width: 100%;
82 | box-shadow: 1px 1px 1px rgba(0,0,0,0.5);
83 | }
84 |
--------------------------------------------------------------------------------
/docs/css/identifiers.css:
--------------------------------------------------------------------------------
1 | .identifiers-wrap {
2 | display: flex;
3 | align-items: flex-start;
4 | }
5 |
6 | .identifier-dir-tree {
7 | background: #fff;
8 | border: solid 1px #ddd;
9 | border-radius: 0.25em;
10 | top: 52px;
11 | position: -webkit-sticky;
12 | position: sticky;
13 | max-height: calc(100vh - 155px);
14 | overflow-y: scroll;
15 | min-width: 200px;
16 | margin-left: 1em;
17 | }
18 |
19 | .identifier-dir-tree-header {
20 | padding: 0.5em;
21 | background-color: #fafafa;
22 | border-bottom: solid 1px #ddd;
23 | }
24 |
25 | .identifier-dir-tree-content {
26 | padding: 0 0.5em 0;
27 | }
28 |
29 | .identifier-dir-tree-content > div {
30 | padding-top: 0.25em;
31 | padding-bottom: 0.25em;
32 | }
33 |
34 | .identifier-dir-tree-content a {
35 | color: inherit;
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/docs/css/manual.css:
--------------------------------------------------------------------------------
1 | .github-markdown .manual-toc {
2 | padding-left: 0;
3 | }
4 |
5 | .manual-index .manual-cards {
6 | display: flex;
7 | flex-wrap: wrap;
8 | }
9 |
10 | .manual-index .manual-card-wrap {
11 | width: 280px;
12 | padding: 10px 20px 10px 0;
13 | box-sizing: border-box;
14 | }
15 |
16 | .manual-index .manual-card-wrap > h1 {
17 | margin: 0;
18 | font-size: 1em;
19 | font-weight: 600;
20 | padding: 0.2em 0 0.2em 0.5em;
21 | border-radius: 0.1em 0.1em 0 0;
22 | border: none;
23 | }
24 |
25 | .manual-index .manual-card-wrap > h1 span {
26 | color: #555;
27 | }
28 |
29 | .manual-index .manual-card {
30 | height: 200px;
31 | overflow: hidden;
32 | border: solid 1px rgba(230, 230, 230, 0.84);
33 | border-radius: 0 0 0.1em 0.1em;
34 | padding: 8px;
35 | position: relative;
36 | }
37 |
38 | .manual-index .manual-card > div {
39 | transform: scale(0.4);
40 | transform-origin: 0 0;
41 | width: 250%;
42 | }
43 |
44 | .manual-index .manual-card > a {
45 | position: absolute;
46 | top: 0;
47 | left: 0;
48 | width: 100%;
49 | height: 100%;
50 | background: rgba(210, 210, 210, 0.1);
51 | }
52 |
53 | .manual-index .manual-card > a:hover {
54 | background: none;
55 | }
56 |
57 | .manual-index .manual-badge {
58 | margin: 0;
59 | }
60 |
61 | .manual-index .manual-user-index {
62 | margin-bottom: 1em;
63 | border-bottom: solid 1px #ddd;
64 | }
65 |
66 | .manual-root .navigation {
67 | padding-left: 4px;
68 | margin-top: 4px;
69 | }
70 |
71 | .navigation .manual-toc-root > div {
72 | padding-left: 0.25em;
73 | padding-right: 0.75em;
74 | }
75 |
76 | .github-markdown .manual-toc-title a {
77 | color: inherit;
78 | }
79 |
80 | .manual-breadcrumb-list {
81 | font-size: 0.8em;
82 | margin-bottom: 1em;
83 | }
84 |
85 | .manual-toc-title a:hover {
86 | color: #039BE5;
87 | }
88 |
89 | .manual-toc li {
90 | margin: 0.75em 0;
91 | list-style-type: none;
92 | }
93 |
94 | .navigation .manual-toc [class^="indent-h"] a {
95 | color: #666;
96 | }
97 |
98 | .navigation .manual-toc .indent-h1 a {
99 | color: #555;
100 | font-weight: 600;
101 | display: block;
102 | }
103 |
104 | .manual-toc .indent-h1 {
105 | display: block;
106 | margin: 0.4em 0 0 0.25em;
107 | padding: 0.2em 0 0.2em 0.5em;
108 | border-radius: 0.1em;
109 | }
110 |
111 | .manual-root .navigation .manual-toc li:not(.indent-h1) {
112 | margin-top: 0.5em;
113 | }
114 |
115 | .manual-toc .indent-h2 {
116 | display: none;
117 | margin-left: 1.5em;
118 | }
119 | .manual-toc .indent-h3 {
120 | display: none;
121 | margin-left: 2.5em;
122 | }
123 | .manual-toc .indent-h4 {
124 | display: none;
125 | margin-left: 3.5em;
126 | }
127 | .manual-toc .indent-h5 {
128 | display: none;
129 | margin-left: 4.5em;
130 | }
131 |
132 | .manual-nav li {
133 | margin: 0.75em 0;
134 | }
135 |
--------------------------------------------------------------------------------
/docs/css/prettify-tomorrow.css:
--------------------------------------------------------------------------------
1 | /* Tomorrow Theme */
2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
3 | /* Pretty printing styles. Used with prettify.js. */
4 | /* SPAN elements with the classes below are added by prettyprint. */
5 | /* plain text */
6 | .pln {
7 | color: #4d4d4c; }
8 |
9 | @media screen {
10 | /* string content */
11 | .str {
12 | color: #718c00; }
13 |
14 | /* a keyword */
15 | .kwd {
16 | color: #8959a8; }
17 |
18 | /* a comment */
19 | .com {
20 | color: #8e908c; }
21 |
22 | /* a type name */
23 | .typ {
24 | color: #4271ae; }
25 |
26 | /* a literal value */
27 | .lit {
28 | color: #f5871f; }
29 |
30 | /* punctuation */
31 | .pun {
32 | color: #4d4d4c; }
33 |
34 | /* lisp open bracket */
35 | .opn {
36 | color: #4d4d4c; }
37 |
38 | /* lisp close bracket */
39 | .clo {
40 | color: #4d4d4c; }
41 |
42 | /* a markup tag name */
43 | .tag {
44 | color: #c82829; }
45 |
46 | /* a markup attribute name */
47 | .atn {
48 | color: #f5871f; }
49 |
50 | /* a markup attribute value */
51 | .atv {
52 | color: #3e999f; }
53 |
54 | /* a declaration */
55 | .dec {
56 | color: #f5871f; }
57 |
58 | /* a variable name */
59 | .var {
60 | color: #c82829; }
61 |
62 | /* a function name */
63 | .fun {
64 | color: #4271ae; } }
65 | /* Use higher contrast and text-weight for printable form. */
66 | @media print, projection {
67 | .str {
68 | color: #060; }
69 |
70 | .kwd {
71 | color: #006;
72 | font-weight: bold; }
73 |
74 | .com {
75 | color: #600;
76 | font-style: italic; }
77 |
78 | .typ {
79 | color: #404;
80 | font-weight: bold; }
81 |
82 | .lit {
83 | color: #044; }
84 |
85 | .pun, .opn, .clo {
86 | color: #440; }
87 |
88 | .tag {
89 | color: #006;
90 | font-weight: bold; }
91 |
92 | .atn {
93 | color: #404; }
94 |
95 | .atv {
96 | color: #060; } }
97 | /* Style */
98 | /*
99 | pre.prettyprint {
100 | background: white;
101 | font-family: Consolas, Monaco, 'Andale Mono', monospace;
102 | font-size: 12px;
103 | line-height: 1.5;
104 | border: 1px solid #ccc;
105 | padding: 10px; }
106 | */
107 |
108 | /* Specify class=linenums on a pre to get line numbering */
109 | ol.linenums {
110 | margin-top: 0;
111 | margin-bottom: 0; }
112 |
113 | /* IE indents via margin-left */
114 | li.L0,
115 | li.L1,
116 | li.L2,
117 | li.L3,
118 | li.L4,
119 | li.L5,
120 | li.L6,
121 | li.L7,
122 | li.L8,
123 | li.L9 {
124 | /* */ }
125 |
126 | /* Alternate shading for lines */
127 | li.L1,
128 | li.L3,
129 | li.L5,
130 | li.L7,
131 | li.L9 {
132 | /* */ }
133 |
--------------------------------------------------------------------------------
/docs/css/search.css:
--------------------------------------------------------------------------------
1 | /* search box */
2 | .search-box {
3 | position: absolute;
4 | top: 10px;
5 | right: 50px;
6 | padding-right: 8px;
7 | padding-bottom: 10px;
8 | line-height: normal;
9 | font-size: 12px;
10 | }
11 |
12 | .search-box img {
13 | width: 20px;
14 | vertical-align: top;
15 | }
16 |
17 | .search-input {
18 | display: inline;
19 | visibility: hidden;
20 | width: 0;
21 | padding: 2px;
22 | height: 1.5em;
23 | outline: none;
24 | background: transparent;
25 | border: 1px #0af;
26 | border-style: none none solid none;
27 | vertical-align: bottom;
28 | }
29 |
30 | .search-input-edge {
31 | display: none;
32 | width: 1px;
33 | height: 5px;
34 | background-color: #0af;
35 | vertical-align: bottom;
36 | }
37 |
38 | .search-result {
39 | position: absolute;
40 | display: none;
41 | height: 600px;
42 | width: 100%;
43 | padding: 0;
44 | margin-top: 5px;
45 | margin-left: 24px;
46 | background: white;
47 | box-shadow: 1px 1px 4px rgb(0,0,0);
48 | white-space: nowrap;
49 | overflow-y: scroll;
50 | }
51 |
52 | .search-result-import-path {
53 | color: #aaa;
54 | font-size: 12px;
55 | }
56 |
57 | .search-result li {
58 | list-style: none;
59 | padding: 2px 4px;
60 | }
61 |
62 | .search-result li a {
63 | display: block;
64 | }
65 |
66 | .search-result li.selected {
67 | background: #ddd;
68 | }
69 |
70 | .search-result li.search-separator {
71 | background: rgb(37, 138, 175);
72 | color: white;
73 | }
74 |
75 | .search-box.active .search-input {
76 | visibility: visible;
77 | transition: width 0.2s ease-out;
78 | width: 300px;
79 | }
80 |
81 | .search-box.active .search-input-edge {
82 | display: inline-block;
83 | }
84 |
85 |
--------------------------------------------------------------------------------
/docs/css/source.css:
--------------------------------------------------------------------------------
1 | table.files-summary {
2 | width: 100%;
3 | margin: 10px 0;
4 | border-spacing: 0;
5 | border: 0;
6 | border-collapse: collapse;
7 | text-align: right;
8 | }
9 |
10 | table.files-summary tbody tr:hover {
11 | background: #eee;
12 | }
13 |
14 | table.files-summary td:first-child,
15 | table.files-summary td:nth-of-type(2) {
16 | text-align: left;
17 | }
18 |
19 | table.files-summary[data-use-coverage="false"] td.coverage {
20 | display: none;
21 | }
22 |
23 | table.files-summary thead {
24 | background: #fafafa;
25 | }
26 |
27 | table.files-summary td {
28 | border: solid 1px #ddd;
29 | padding: 4px 10px;
30 | vertical-align: top;
31 | }
32 |
33 | table.files-summary td.identifiers > span {
34 | display: block;
35 | margin-top: 4px;
36 | }
37 | table.files-summary td.identifiers > span:first-child {
38 | margin-top: 0;
39 | }
40 |
41 | table.files-summary .coverage-count {
42 | font-size: 12px;
43 | color: #aaa;
44 | display: inline-block;
45 | min-width: 40px;
46 | }
47 |
48 | .total-coverage-count {
49 | position: relative;
50 | bottom: 2px;
51 | font-size: 12px;
52 | color: #666;
53 | font-weight: 500;
54 | padding-left: 5px;
55 | }
56 |
--------------------------------------------------------------------------------
/docs/css/style.css:
--------------------------------------------------------------------------------
1 | @import url(https://fonts.googleapis.com/css?family=Roboto:400,300,700);
2 | @import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600,700);
3 | @import url(./manual.css);
4 | @import url(./source.css);
5 | @import url(./test.css);
6 | @import url(./identifiers.css);
7 | @import url(./github.css);
8 | @import url(./search.css);
9 |
10 | * {
11 | margin: 0;
12 | padding: 0;
13 | text-decoration: none;
14 | }
15 |
16 | html
17 | {
18 | font-family: 'Source Sans Pro', 'Roboto', sans-serif;
19 | overflow: auto;
20 | /*font-size: 14px;*/
21 | /*color: #4d4e53;*/
22 | /*color: rgba(0, 0, 0, .68);*/
23 | color: #555;
24 | background-color: #fff;
25 | }
26 |
27 | a {
28 | /*color: #0095dd;*/
29 | /*color:rgb(37, 138, 175);*/
30 | color: #039BE5;
31 | }
32 |
33 | code a:hover {
34 | text-decoration: underline;
35 | }
36 |
37 | ul, ol {
38 | padding-left: 20px;
39 | }
40 |
41 | ul li {
42 | list-style: disc;
43 | margin: 4px 0;
44 | }
45 |
46 | ol li {
47 | margin: 4px 0;
48 | }
49 |
50 | h1 {
51 | margin-bottom: 10px;
52 | font-size: 34px;
53 | font-weight: 300;
54 | border-bottom: solid 1px #ddd;
55 | }
56 |
57 | h2 {
58 | margin-top: 24px;
59 | margin-bottom: 10px;
60 | font-size: 20px;
61 | border-bottom: solid 1px #ddd;
62 | font-weight: 300;
63 | }
64 |
65 | h3 {
66 | position: relative;
67 | font-size: 16px;
68 | margin-bottom: 12px;
69 | padding: 4px;
70 | font-weight: 300;
71 | }
72 |
73 | details {
74 | cursor: pointer;
75 | }
76 |
77 | del {
78 | text-decoration: line-through;
79 | }
80 |
81 | p {
82 | margin-bottom: 15px;
83 | line-height: 1.5;
84 | }
85 |
86 | code {
87 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
88 | }
89 |
90 | pre > code {
91 | display: block;
92 | }
93 |
94 | pre.prettyprint, pre > code {
95 | padding: 4px;
96 | margin: 1em 0;
97 | background-color: #f5f5f5;
98 | border-radius: 3px;
99 | }
100 |
101 | pre.prettyprint > code {
102 | margin: 0;
103 | }
104 |
105 | p > code,
106 | li > code {
107 | padding: 0.2em 0.5em;
108 | margin: 0;
109 | font-size: 85%;
110 | background-color: rgba(0,0,0,0.04);
111 | border-radius: 3px;
112 | }
113 |
114 | .code {
115 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
116 | font-size: 13px;
117 | }
118 |
119 | .import-path pre.prettyprint,
120 | .import-path pre.prettyprint code {
121 | margin: 0;
122 | padding: 0;
123 | border: none;
124 | background: white;
125 | }
126 |
127 | .layout-container {
128 | /*display: flex;*/
129 | /*flex-direction: row;*/
130 | /*justify-content: flex-start;*/
131 | /*align-items: stretch;*/
132 | }
133 |
134 | .layout-container > header {
135 | display: flex;
136 | height: 40px;
137 | line-height: 40px;
138 | font-size: 16px;
139 | padding: 0 10px;
140 | margin: 0;
141 | position: fixed;
142 | width: 100%;
143 | z-index: 1;
144 | background-color: #fafafa;
145 | top: 0;
146 | border-bottom: solid 1px #ddd;
147 | }
148 | .layout-container > header > a{
149 | margin: 0 5px;
150 | color: #444;
151 | }
152 |
153 | .layout-container > header > a.repo-url-github {
154 | font-size: 0;
155 | display: inline-block;
156 | width: 20px;
157 | height: 38px;
158 | background: url("../image/github.png") no-repeat center;
159 | background-size: 20px;
160 | vertical-align: top;
161 | }
162 |
163 | .navigation {
164 | position: fixed;
165 | top: 0;
166 | left: 0;
167 | box-sizing: border-box;
168 | width: 250px;
169 | height: 100%;
170 | padding-top: 40px;
171 | padding-left: 15px;
172 | padding-bottom: 2em;
173 | margin-top:1em;
174 | overflow-x: scroll;
175 | box-shadow: rgba(255, 255, 255, 1) -1px 0 0 inset;
176 | border-right: 1px solid #ddd;
177 | }
178 |
179 | .navigation ul {
180 | padding: 0;
181 | }
182 |
183 | .navigation li {
184 | list-style: none;
185 | margin: 4px 0;
186 | white-space: nowrap;
187 | }
188 |
189 | .navigation li a {
190 | color: #666;
191 | }
192 |
193 | .navigation .nav-dir-path {
194 | display: block;
195 | margin-top: 0.7em;
196 | margin-bottom: 0.25em;
197 | font-weight: 600;
198 | }
199 |
200 | .kind-class,
201 | .kind-interface,
202 | .kind-function,
203 | .kind-typedef,
204 | .kind-variable,
205 | .kind-external {
206 | margin-left: 0.75em;
207 | width: 1.2em;
208 | height: 1.2em;
209 | display: inline-block;
210 | text-align: center;
211 | border-radius: 0.2em;
212 | margin-right: 0.2em;
213 | font-weight: bold;
214 | line-height: 1.2em;
215 | }
216 |
217 | .kind-class {
218 | color: #009800;
219 | background-color: #bfe5bf;
220 | }
221 |
222 | .kind-interface {
223 | color: #fbca04;
224 | background-color: #fef2c0;
225 | }
226 |
227 | .kind-function {
228 | color: #6b0090;
229 | background-color: #d6bdde;
230 | }
231 |
232 | .kind-variable {
233 | color: #eb6420;
234 | background-color: #fad8c7;
235 | }
236 |
237 | .kind-typedef {
238 | color: #db001e;
239 | background-color: #edbec3;
240 | }
241 |
242 | .kind-external {
243 | color: #0738c3;
244 | background-color: #bbcbea;
245 | }
246 |
247 | .summary span[class^="kind-"] {
248 | margin-left: 0;
249 | }
250 |
251 | h1 .version,
252 | h1 .url a {
253 | font-size: 14px;
254 | color: #aaa;
255 | }
256 |
257 | .content {
258 | margin-top: 40px;
259 | margin-left: 250px;
260 | padding: 10px 50px 10px 20px;
261 | }
262 |
263 | .header-notice {
264 | font-size: 14px;
265 | color: #aaa;
266 | margin: 0;
267 | }
268 |
269 | .expression-extends .prettyprint {
270 | margin-left: 10px;
271 | background: white;
272 | }
273 |
274 | .extends-chain {
275 | border-bottom: 1px solid#ddd;
276 | padding-bottom: 10px;
277 | margin-bottom: 10px;
278 | }
279 |
280 | .extends-chain span:nth-of-type(1) {
281 | padding-left: 10px;
282 | }
283 |
284 | .extends-chain > div {
285 | margin: 5px 0;
286 | }
287 |
288 | .description table {
289 | font-size: 14px;
290 | border-spacing: 0;
291 | border: 0;
292 | border-collapse: collapse;
293 | }
294 |
295 | .description thead {
296 | background: #999;
297 | color: white;
298 | }
299 |
300 | .description table td,
301 | .description table th {
302 | border: solid 1px #ddd;
303 | padding: 4px;
304 | font-weight: normal;
305 | }
306 |
307 | .flat-list ul {
308 | padding-left: 0;
309 | }
310 |
311 | .flat-list li {
312 | display: inline;
313 | list-style: none;
314 | }
315 |
316 | table.summary {
317 | width: 100%;
318 | margin: 10px 0;
319 | border-spacing: 0;
320 | border: 0;
321 | border-collapse: collapse;
322 | }
323 |
324 | table.summary thead {
325 | background: #fafafa;
326 | }
327 |
328 | table.summary td {
329 | border: solid 1px #ddd;
330 | padding: 4px 10px;
331 | }
332 |
333 | table.summary tbody td:nth-child(1) {
334 | text-align: right;
335 | white-space: nowrap;
336 | min-width: 64px;
337 | vertical-align: top;
338 | }
339 |
340 | table.summary tbody td:nth-child(2) {
341 | width: 100%;
342 | border-right: none;
343 | }
344 |
345 | table.summary tbody td:nth-child(3) {
346 | white-space: nowrap;
347 | border-left: none;
348 | vertical-align: top;
349 | }
350 |
351 | table.summary td > div:nth-of-type(2) {
352 | padding-top: 4px;
353 | padding-left: 15px;
354 | }
355 |
356 | table.summary td p {
357 | margin-bottom: 0;
358 | }
359 |
360 | .inherited-summary thead td {
361 | padding-left: 2px;
362 | }
363 |
364 | .inherited-summary thead a {
365 | color: white;
366 | }
367 |
368 | .inherited-summary .summary tbody {
369 | display: none;
370 | }
371 |
372 | .inherited-summary .summary .toggle {
373 | padding: 0 4px;
374 | font-size: 12px;
375 | cursor: pointer;
376 | }
377 | .inherited-summary .summary .toggle.closed:before {
378 | content: "▶";
379 | }
380 | .inherited-summary .summary .toggle.opened:before {
381 | content: "▼";
382 | }
383 |
384 | .member, .method {
385 | margin-bottom: 24px;
386 | }
387 |
388 | table.params {
389 | width: 100%;
390 | margin: 10px 0;
391 | border-spacing: 0;
392 | border: 0;
393 | border-collapse: collapse;
394 | }
395 |
396 | table.params thead {
397 | background: #eee;
398 | color: #aaa;
399 | }
400 |
401 | table.params td {
402 | padding: 4px;
403 | border: solid 1px #ddd;
404 | }
405 |
406 | table.params td p {
407 | margin: 0;
408 | }
409 |
410 | .content .detail > * {
411 | margin: 15px 0;
412 | }
413 |
414 | .content .detail > h3 {
415 | color: black;
416 | background-color: #f0f0f0;
417 | }
418 |
419 | .content .detail > div {
420 | margin-left: 10px;
421 | }
422 |
423 | .content .detail > .import-path {
424 | margin-top: -8px;
425 | }
426 |
427 | .content .detail + .detail {
428 | margin-top: 30px;
429 | }
430 |
431 | .content .detail .throw td:first-child {
432 | padding-right: 10px;
433 | }
434 |
435 | .content .detail h4 + :not(pre) {
436 | padding-left: 0;
437 | margin-left: 10px;
438 | }
439 |
440 | .content .detail h4 + ul li {
441 | list-style: none;
442 | }
443 |
444 | .return-param * {
445 | display: inline;
446 | }
447 |
448 | .argument-params {
449 | margin-bottom: 20px;
450 | }
451 |
452 | .return-type {
453 | padding-right: 10px;
454 | font-weight: normal;
455 | }
456 |
457 | .return-desc {
458 | margin-left: 10px;
459 | margin-top: 4px;
460 | }
461 |
462 | .return-desc p {
463 | margin: 0;
464 | }
465 |
466 | .deprecated, .experimental, .instance-docs {
467 | border-left: solid 5px orange;
468 | padding-left: 4px;
469 | margin: 4px 0;
470 | }
471 |
472 | tr.listen p,
473 | tr.throw p,
474 | tr.emit p{
475 | margin-bottom: 10px;
476 | }
477 |
478 | .version, .since {
479 | color: #aaa;
480 | }
481 |
482 | h3 .right-info {
483 | position: absolute;
484 | right: 4px;
485 | font-size: 14px;
486 | }
487 |
488 | .version + .since:before {
489 | content: '| ';
490 | }
491 |
492 | .see {
493 | margin-top: 10px;
494 | }
495 |
496 | .see h4 {
497 | margin: 4px 0;
498 | }
499 |
500 | .content .detail h4 + .example-doc {
501 | margin: 6px 0;
502 | }
503 |
504 | .example-caption {
505 | position: relative;
506 | bottom: -1px;
507 | display: inline-block;
508 | padding: 4px;
509 | font-style: italic;
510 | background-color: #f5f5f5;
511 | font-weight: bold;
512 | border-radius: 3px;
513 | border-bottom-left-radius: 0;
514 | border-bottom-right-radius: 0;
515 | }
516 |
517 | .example-caption + pre.source-code {
518 | margin-top: 0;
519 | border-top-left-radius: 0;
520 | }
521 |
522 | footer, .file-footer {
523 | text-align: right;
524 | font-style: italic;
525 | font-weight: 100;
526 | font-size: 13px;
527 | margin-right: 50px;
528 | margin-left: 270px;
529 | border-top: 1px solid #ddd;
530 | padding-top: 30px;
531 | margin-top: 20px;
532 | padding-bottom: 10px;
533 | }
534 |
535 | footer img {
536 | width: 24px;
537 | vertical-align: middle;
538 | padding-left: 4px;
539 | position: relative;
540 | top: -3px;
541 | opacity: 0.6;
542 | }
543 |
544 | pre.source-code {
545 | padding: 4px;
546 | }
547 |
548 | pre.raw-source-code > code {
549 | padding: 0;
550 | margin: 0;
551 | font-size: 12px;
552 | background: #fff;
553 | border: solid 1px #ddd;
554 | line-height: 1.5;
555 | }
556 |
557 | pre.raw-source-code > code > ol {
558 | counter-reset:number;
559 | list-style:none;
560 | margin:0;
561 | padding:0;
562 | overflow: hidden;
563 | }
564 |
565 | pre.raw-source-code > code > ol li:before {
566 | counter-increment: number;
567 | content: counter(number);
568 | display: inline-block;
569 | min-width: 3em;
570 | color: #aaa;
571 | text-align: right;
572 | padding-right: 1em;
573 | }
574 |
575 | pre.source-code.line-number {
576 | padding: 0;
577 | }
578 |
579 | pre.source-code ol {
580 | background: #eee;
581 | padding-left: 40px;
582 | }
583 |
584 | pre.source-code li {
585 | background: white;
586 | padding-left: 4px;
587 | list-style: decimal;
588 | margin: 0;
589 | }
590 |
591 | pre.source-code.line-number li.active {
592 | background: rgb(255, 255, 150) !important;
593 | }
594 |
595 | pre.source-code.line-number li.error-line {
596 | background: #ffb8bf;
597 | }
598 |
599 | .inner-link-active {
600 | /*background: rgb(255, 255, 150) !important;*/
601 | background: #039BE5 !important;
602 | color: #fff !important;
603 | padding-left: 0.1em !important;
604 | }
605 |
606 | .inner-link-active a {
607 | color: inherit;
608 | }
609 |
--------------------------------------------------------------------------------
/docs/css/test.css:
--------------------------------------------------------------------------------
1 | table.test-summary thead {
2 | background: #fafafa;
3 | }
4 |
5 | table.test-summary thead .test-description {
6 | width: 50%;
7 | }
8 |
9 | table.test-summary {
10 | width: 100%;
11 | margin: 10px 0;
12 | border-spacing: 0;
13 | border: 0;
14 | border-collapse: collapse;
15 | }
16 |
17 | table.test-summary thead .test-count {
18 | width: 3em;
19 | }
20 |
21 | table.test-summary tbody tr:hover {
22 | background-color: #eee;
23 | }
24 |
25 | table.test-summary td {
26 | border: solid 1px #ddd;
27 | padding: 4px 10px;
28 | vertical-align: top;
29 | }
30 |
31 | table.test-summary td p {
32 | margin: 0;
33 | }
34 |
35 | table.test-summary tr.test-interface .toggle {
36 | display: inline-block;
37 | float: left;
38 | margin-right: 4px;
39 | cursor: pointer;
40 | font-size: 0.8em;
41 | padding-top: 0.25em;
42 | }
43 |
44 | table.test-summary tr.test-interface .toggle.opened:before {
45 | content: '▼';
46 | }
47 |
48 | table.test-summary tr.test-interface .toggle.closed:before {
49 | content: '▶';
50 | }
51 |
52 | table.test-summary .test-target > span {
53 | display: block;
54 | margin-top: 4px;
55 | }
56 | table.test-summary .test-target > span:first-child {
57 | margin-top: 0;
58 | }
59 |
--------------------------------------------------------------------------------
/docs/file/src/Parser.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/Parser.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/Parser.js
37 |
/**
38 | * Parser - Receives any strategy and safely implement it
39 | *
40 | * @constructor
41 | * @param {Base} strategy - Any strategy implementing the Base's prototype
42 | */
43 | function Parser (strategy) {
44 | this.strategy = strategy
45 | }
46 |
47 | /**
48 | * Parser.prototype.parse - Exposes the parsing from string to any valid JS type with the strategy
49 | *
50 | * @param {string} data
51 | * @param {object} options
52 | */
53 | Parser.prototype.parse = function parse (data, options) {
54 | return this.strategy.parse(data, options)
55 | }
56 |
57 | /**
58 | * Parser.prototype.stringify - Exposes the stringify() method from any valid JS type to expected type with the strategy
59 | *
60 | * @param {*} data
61 | * @param {object} options
62 | */
63 | Parser.prototype.stringify = function stringify (data, options) {
64 | return this.strategy.stringify(data, options)
65 | }
66 |
67 | /**
68 | * Parser.prototype.valid - Exposes the valid() method from strategy. Checks if given data is valid
69 | *
70 | * @param {string} data
71 | * @param {object} options
72 | */
73 | Parser.prototype.valid = function stringify (data, options) {
74 | return this.strategy.valid(data, options)
75 | }
76 |
77 | /**
78 | * Parser.prototype.pipeStringify - Exposes the pipeStringify() method from strategy. Streams data through stringify
79 | */
80 | Parser.prototype.pipeStringify = function pipeStringify () {
81 | return this.strategy.pipeStringify()
82 | }
83 |
84 | /**
85 | * Parser.prototype.pipeParse - Exposes the pipeParse() method from strategy. Streams data through parse
86 | */
87 | Parser.prototype.pipeParse = function pipeParse () {
88 | return this.strategy.pipeParse()
89 | }
90 |
91 | Parser.prototype.get = function get (data, path) {}
92 |
93 | Parser.prototype.has = function has (data, path) {}
94 |
95 | module.exports = Parser
96 |
97 |
98 |
99 |
100 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/docs/file/src/errors/NotImplemented.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/errors/NotImplemented.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/errors/NotImplemented.js
37 |
/**
38 | * NotImplemented
39 | */
40 | function NotImplemented () {
41 | this.name = 'NotImplemented'
42 | this.message = 'This method haven\'t been implemented yet'
43 | }
44 |
45 | NotImplemented.prototype = new Error()
46 |
47 | module.exports = NotImplemented
48 |
49 |
50 |
51 |
52 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/docs/file/src/errors/ParserError.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/errors/ParserError.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/errors/ParserError.js
37 |
/**
38 | * ParseError
39 | *
40 | * @param {string} format - which format the error ocurred
41 | * @param {object} context - any context info for debugging
42 | */
43 | function ParseError (format, context = {}) {
44 | this.name = 'ParseError'
45 | this.message = `Failed to parse ${format}`
46 | this.context = context
47 | }
48 |
49 | ParseError.prototype = new Error()
50 |
51 | module.exports = ParseError
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/docs/file/src/index.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/index.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/index.js
37 |
const {
38 | Json,
39 | Xml,
40 | Yaml,
41 | Csv
42 | } = require('./strategies')
43 | const Parser = require('./Parser')
44 |
45 | module.exports = {
46 | json: new Parser(new Json()),
47 | xml: new Parser(new Xml()),
48 | yaml: new Parser(new Yaml()),
49 | csv: new Parser(new Csv())
50 | }
51 |
52 |
53 |
54 |
55 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/docs/file/src/strategies/Base.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/strategies/Base.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/strategies/Base.js
37 |
const NotImplemented = require('../errors/NotImplemented')
38 | const ParserError = require('../errors/ParserError')
39 |
40 | /**
41 | * Base class for strategies around the Parser
42 | * Every function that haven't been implemented
43 | * will throw an NotImplementedError
44 | *
45 | * @constructor
46 | */
47 | function Base () {}
48 |
49 | /**
50 | * Base.prototype.stringify - receives * form of data and turns it into a string
51 | *
52 | * @param {*} data
53 | * @param {object} options
54 | * @throws {NotImplemented} This method must be implemented
55 | */
56 | Base.prototype.stringify = function stringify (data, options) {
57 | throw new NotImplemented()
58 | }
59 |
60 | /**
61 | * Base.prototype.parse - receives an string and translate it to valid JavaScript
62 | *
63 | * @param {string} data
64 | * @param {object} options
65 | * @throws {NotImplemented} This method must be implemented
66 | */
67 | Base.prototype.parse = function parse (data, options) {
68 | throw new NotImplemented()
69 | }
70 |
71 | /**
72 | * Base.prototype.pipe - prototype for streams
73 | *
74 | * @throws {NotImplemented} This method must be implemented
75 | */
76 | Base.prototype.pipe = function pipe () {
77 | throw new NotImplemented()
78 | }
79 |
80 | /**
81 | * Base.prototype.valid - checks if a given value is valid in desired format using the implemented method parse(), ignoring any exception
82 | *
83 | * @param {object} options - any option accepted for the implemented method parse()
84 | * @returns {boolean} wether or not the given data is a valid format
85 | */
86 | Base.prototype.valid = function valid (data, options = {}) {
87 | try {
88 | this.parse(data, options)
89 | return true
90 | } catch (error) {
91 | if (error instanceof ParserError) {
92 | return false
93 | }
94 |
95 | throw error
96 | }
97 | }
98 |
99 | /**
100 | * Base.prototype.pipeParse - prototype for streams
101 | *
102 | * @throws {NotImplemented} This method must be implemented
103 | */
104 | Base.prototype.pipeParse = function pipeParse () {
105 | throw new NotImplemented()
106 | }
107 |
108 | /**
109 | * Base.prototype.pipeStringify - prototype for streams
110 | *
111 | * @throws {NotImplemented} This method must be implemented
112 | */
113 | Base.prototype.pipeStringify = function pipeStringify () {
114 | throw new NotImplemented()
115 | }
116 |
117 | module.exports = Base
118 |
119 |
120 |
121 |
122 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/docs/file/src/strategies/Csv.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/strategies/Csv.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/strategies/Csv.js
37 |
const Base = require('./Base')
38 | const ParserError = require('../errors/ParserError')
39 | const csvParser = require('csv-parse/lib/sync')
40 | const csvStringify = require('csv-stringify/lib/sync')
41 | const csvParserStream = require('csv-parse')
42 | const csvStringifyStream = require('csv-stringify')
43 |
44 | /**
45 | * Csv - Support for CSV filetype
46 | *
47 | * @constructor
48 | */
49 | function Csv () {}
50 |
51 | Csv.prototype = Object.create(Base.prototype)
52 |
53 | /**
54 | * Csv.prototype.parse - receives an CSV string and returns valid JS
55 | *
56 | * @param {string} data
57 | * @param {object} [options]
58 | * @param {(boolean|array|function)} [options.headers] - If should parse first line as the headers, default is true
59 | * @param {(string|Buffer)} [options.delimiter] - Which delimiters to use when parsing, defaults to comma `,`
60 | * @param {number} [options.skipLines] - How many lines it should skip before parsing, defaults to 1
61 | * @param {number} [options.offset] - How many lines it should parse, defaults to -1
62 | * @returns {array}
63 | */
64 | Csv.prototype.parse = function parse (data, options = {}) {
65 | const config = {
66 | columns: true,
67 | skip_empty_lines: true,
68 | delimiter: options.delimiter || ',',
69 | from_line: options.skipLines || 1
70 | }
71 |
72 | if (Object.prototype.hasOwnProperty.apply(options, ['headers'])) {
73 | config.columns = options.headers
74 | }
75 |
76 | if (options.offset) {
77 | config.to_line = options.offset
78 | }
79 |
80 | try {
81 | return csvParser(data, config)
82 | } catch (e) {
83 | const context = {
84 | code: e.code,
85 | message: e.message,
86 | column: e.column,
87 | emptyLines: e.empty_lines,
88 | header: e.header,
89 | index: e.index,
90 | lines: e.lines,
91 | quoting: e.quoting,
92 | records: e.records
93 | }
94 |
95 | throw new ParserError('csv', context)
96 | }
97 | }
98 |
99 | /**
100 | * Csv.prototype.stringify - receives * valid JS data and returns it as CSV
101 | *
102 | * @param {array} data
103 | * @param {object} [options]
104 | * @param {boolean} [options.headers] - If should set first line as the headers, default is true
105 | * @param {(array|object)} [options.columns] - Custom column mapping, see examples for more
106 | * @returns {string}
107 | */
108 | Csv.prototype.stringify = function stringify (data, options = {}) {
109 | const config = {
110 | header: true
111 | }
112 |
113 | if (options.headers === false) {
114 | config.header = false
115 | }
116 |
117 | if (options.columns) {
118 | config.columns = options.columns
119 | }
120 |
121 | return csvStringify(data, config)
122 | }
123 |
124 | /**
125 | * Csv.prototype.pipeParse - allow streaming data
126 | *
127 | * @param {object} [options]
128 | * @param {(boolean|array|function)} [options.headers] - If should parse first line as the headers, default is true
129 | * @param {string} [options.delimiter] - Which delimiters to use when parsing, defaults to comma `,`
130 | */
131 | Csv.prototype.pipeParse = function pipeParse (options = {}) {
132 | const config = {
133 | delimiter: options.delimiter || ',',
134 | columns: Reflect.has(options, 'headers') ? options.headers : true
135 | }
136 |
137 | return csvParserStream(config)
138 | }
139 |
140 | /**
141 | * Csv.prototype.pipeStringify - stream
142 | *
143 | * @param {array} data
144 | * @param {object} [options]
145 | * @param {boolean} [options.headers] - If should set first line as the headers, default is true
146 | * @param {string} [options.delimiter] - Which delimiters to use when parsing, defaults to comma `,`
147 | * @param {(array|object)} [options.columns] - Custom column mapping, see examples for more
148 | */
149 | Csv.prototype.pipeStringify = function pipeStringify (options = {}) {
150 | const config = {
151 | delimiter: options.delimiter || ',',
152 | header: Reflect.has(options, 'headers') ? !!options.headers : true
153 | }
154 |
155 | if (Reflect.has(options, 'columns')) {
156 | config.columns = options.columns
157 | }
158 |
159 | return csvStringifyStream(config)
160 | }
161 |
162 | module.exports = Csv
163 |
164 |
165 |
166 |
167 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/docs/file/src/strategies/Json.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/strategies/Json.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/strategies/Json.js
37 |
const Base = require('./Base')
38 | const ParserError = require('../errors/ParserError')
39 | const JSONStream = require('JSONStream')
40 |
41 | /**
42 | * Json - Support for JSON filetype
43 | *
44 | * @constructor
45 | */
46 | function Json () {}
47 |
48 | Json.prototype = Object.create(Base.prototype)
49 |
50 | /**
51 | * Json.prototype.parse - receives an JSON string and returns valid JS
52 | *
53 | * @param {string} data
54 | * @throws {ParserError} In case the JSON string is not valid, ParserError will be thrown
55 | * @returns {*}
56 | */
57 | Json.prototype.parse = function parse (data) {
58 | try {
59 | return JSON.parse(data)
60 | } catch (e) {
61 | throw new ParserError('json')
62 | }
63 | }
64 |
65 | /**
66 | * Json.prototype.stringify - receives * valid JS data and returns it as JSON
67 | *
68 | * @param {*} data
69 | * @returns {string}
70 | */
71 | Json.prototype.stringify = function stringify (data) {
72 | return JSON.stringify(data)
73 | }
74 |
75 | /**
76 | * Json.prototype.pipeStringify - helps to stream object or array into JSON valid data
77 | *
78 | * @param {object} [config] - sets config for stream
79 | * @param {string} [config.type='array'] - which type of data you're streaming, defaults do array
80 | * @returns {WritableStream}
81 | */
82 | Json.prototype.pipeStringify = function pipeStringify (config = {}) {
83 | config.type = config.type || 'array'
84 | const streams = {
85 | object: JSONStream.stringifyObject,
86 | array: JSONStream.stringify
87 | }
88 |
89 | const fn = streams[config.type]
90 |
91 | if (!fn) {
92 | throw new ParserError(`Supplied type "${config.type}" is not allowed. Use either "array" or "object"`)
93 | }
94 |
95 | return fn()
96 | }
97 |
98 | /**
99 | * Json.prototype.pipeStringify - helps to stream JSON data to JS
100 | *
101 | * @param {object} [config] - sets config for stream
102 | * @param {string} [config.path] - select which data path to be parsed from JSON to JS
103 | * @returns {Stream}
104 | */
105 | Json.prototype.pipeParse = function pipeParse (config) {
106 | return JSONStream.parse()
107 | }
108 |
109 | module.exports = Json
110 |
111 |
112 |
113 |
114 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/docs/file/src/strategies/Xml.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/strategies/Xml.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/strategies/Xml.js
37 |
const Base = require('./Base')
38 | const ParserError = require('../errors/ParserError')
39 | const xml = require('xml-js')
40 |
41 | /**
42 | * Xml - Support for XML filetype
43 | *
44 | * @constructor
45 | */
46 | function Xml () {
47 | this.XML_VERSION_TAG = {
48 | _declaration: {
49 | _attributes: {
50 | version: '1.0',
51 | encoding: 'utf-8'
52 | }
53 | }
54 | }
55 | }
56 |
57 | Xml.prototype = Object.create(Base.prototype)
58 |
59 | /**
60 | * Xml.prototype.setXmlDeclaration - sets XML declaration tag on first position of array or object
61 | *
62 | * @param {(object|array)} data - input data
63 | * @returns {(object|array)}
64 | */
65 | Xml.prototype.setXmlDeclaration = function setXmlDeclaration (data) {
66 | if (Array.isArray(data)) {
67 | data = [this.XML_VERSION_TAG, ...data]
68 | } else {
69 | data = { ...this.XML_VERSION_TAG, ...data }
70 | }
71 |
72 | return data
73 | }
74 |
75 | /**
76 | * Xml.prototype.stringify - receives * valid JS data and returns it as XML
77 | *
78 | * @param {(object|array)} data
79 | * @param {Object} options - options for turning JS data into XML
80 | * @param {boolean} options.ignoreDeclaration - don't output XML version tag, default is true
81 | * @example
82 | * // returns '<?xml version="1.0" encoding="utf-8"?><package>parser</package>'
83 | * const data = { package: 'parser' }
84 | * Xml().stringify(data)
85 | * @example
86 | * // returns '<package>parser</package>'
87 | * const data = { package: 'parser' }
88 | * Xml().stringify(data, { ignoreDeclaration: true })
89 | * @returns {string}
90 | */
91 | Xml.prototype.stringify = function stringify (data, options = {}) {
92 | const config = {
93 | compact: true,
94 | ignoreDeclaration: false
95 | }
96 |
97 | data = this.setXmlDeclaration(data)
98 |
99 | if (options.ignoreDeclaration) {
100 | config.ignoreDeclaration = true
101 | }
102 |
103 | return xml.js2xml(data, config)
104 | }
105 |
106 | /**
107 | * Xml.prototype.parse - receives an XML string and translate it to valid JavaScript
108 | *
109 | * @param {string} data
110 | * @param {object} options
111 | * @param {object} options.showDeclaration - force parsing XML declaration tag
112 | * @throws {NotImplemented} This method must be implemented
113 | */
114 | Xml.prototype.parse = function parse (data, options = {}) {
115 | try {
116 | const config = {
117 | compact: true,
118 | ignoreDeclaration: true,
119 | nativeType: true,
120 | nativeTypeAttributes: true
121 | }
122 |
123 | if (options.showDeclaration) {
124 | config.ignoreDeclaration = false
125 | }
126 |
127 | return xml.xml2js(data, config)
128 | } catch (error) {
129 | throw new ParserError(error.message)
130 | }
131 | }
132 |
133 | module.exports = Xml
134 |
135 |
136 |
137 |
138 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/docs/file/src/strategies/Xml/XmlTag.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/strategies/Xml/XmlTag.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/strategies/Xml/XmlTag.js
37 |
function XmlDeclaration (version, encoding) {
38 | this.name = 'declaration'
39 | this.version = version
40 | this.encoding = encoding
41 | }
42 |
43 | function XmlTag (name, value, attributes, tags) {
44 | this.name = name
45 | this.value = value
46 | this.attributes = attributes
47 | this.tags = tags
48 | }
49 |
50 | XmlTag.prototype.reset = function reset () {
51 | return new XmlTag(this.name, this.value, this.attributes, this.tags)
52 | }
53 |
54 | function XmlCharacterData (cdata) {
55 | this.name = 'cdata'
56 | this.cdata = cdata
57 | }
58 |
59 | module.exports = { XmlTag, XmlDeclaration, XmlCharacterData }
60 |
61 |
62 |
63 |
64 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/docs/file/src/strategies/Xml/index.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/strategies/Xml/index.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/strategies/Xml/index.js
37 |
const Base = require('../Base')
38 | const ParserError = require('../../errors/ParserError')
39 | const xml = require('xml-js')
40 | const NotImplemented = require('../../errors/NotImplemented')
41 | const { Transform } = require('stream')
42 | const StreamParser = require('node-xml-stream')
43 | const { XmlTag, XmlCharacterData, XmlDeclaration } = require('./XmlTag')
44 |
45 | /**
46 | * Xml - Support for XML filetype
47 | *
48 | * @constructor
49 | */
50 | function Xml () {
51 | this.XML_VERSION_TAG = {
52 | _declaration: {
53 | _attributes: {
54 | version: '1.0',
55 | encoding: 'utf-8'
56 | }
57 | }
58 | }
59 |
60 | this.XML_JS_KEYS = {
61 | declarationKey: '_declaration',
62 | instructionKey: '_instruction',
63 | attributesKey: '_attributes',
64 | textKey: '_text',
65 | cdataKey: '_cdata',
66 | doctypeKey: '_doctype',
67 | commentKey: '_comment',
68 | parentKey: '_parent',
69 | typeKey: '_type',
70 | nameKey: '_name',
71 | elementsKey: '_elements'
72 | }
73 | }
74 |
75 | Xml.prototype = Object.create(Base.prototype)
76 |
77 | /**
78 | * Xml.prototype.setXmlDeclaration - sets XML declaration tag on first position of array or object
79 | *
80 | * @param {(object|array)} data - input data
81 | * @returns {(object|array)}
82 | */
83 | Xml.prototype.setXmlDeclaration = function setXmlDeclaration (data) {
84 | if (Array.isArray(data)) {
85 | data = [this.XML_VERSION_TAG, ...data]
86 | } else {
87 | data = { ...this.XML_VERSION_TAG, ...data }
88 | }
89 |
90 | return data
91 | }
92 |
93 | /**
94 | * Xml.prototype.stringify - receives * valid JS data and returns it as XML
95 | *
96 | * @param {(object|array)} data
97 | * @param {Object} [options] - options for turning JS data into XML
98 | * @param {boolean} [options.ignoreDeclaration] - don't output XML version tag, default is true
99 | * @returns {string}
100 | */
101 | Xml.prototype.stringify = function stringify (data, options = {}) {
102 | const config = {
103 | compact: true,
104 | ignoreDeclaration: false
105 | }
106 |
107 | data = this.setXmlDeclaration(data)
108 |
109 | if (options.ignoreDeclaration) {
110 | config.ignoreDeclaration = true
111 | }
112 |
113 | return xml.js2xml(data, config)
114 | }
115 |
116 | /**
117 | * Xml.prototype.parse - receives an XML string and translate it to valid JavaScript
118 | *
119 | * @param {string} data
120 | * @param {object} [options]
121 | * @param {boolean} [options.showDeclaration] - force parsing XML declaration tag
122 | * @param {boolean} [options.verbose] - makes xml2js return non compact mode, defaults to false
123 | * @param {boolean} [options.experimentalXmlTag] - use experimental XmlTag prototype, default is false
124 | * @throws {NotImplemented} This method must be implemented
125 | */
126 | Xml.prototype.parse = function parse (data, options = {}) {
127 | try {
128 | const config = {
129 | compact: true,
130 | ignoreDeclaration: true,
131 | nativeType: true,
132 | nativeTypeAttributes: true
133 | }
134 |
135 | if (options.showDeclaration) {
136 | config.ignoreDeclaration = false
137 | }
138 |
139 | if (options.verbose) {
140 | config.compact = false
141 | }
142 |
143 | const result = xml.xml2js(data, config)
144 |
145 | if (options.experimentalXmlTag) {
146 | return this.toXmlTag(result)
147 | }
148 |
149 | return result
150 | } catch (error) {
151 | throw new ParserError(error.message)
152 | }
153 | }
154 |
155 | /**
156 | * Xml.prototype.toXmlTag - turns xml2js non-compact result into XmlTag and XmlResult
157 | *
158 | * @param {object} xml2jsResult
159 | * @throws {NotImplemented}
160 | */
161 | Xml.prototype.toXmlTag = function toXmlTag (xml2jsResult) {
162 | throw new NotImplemented()
163 | }
164 |
165 | /**
166 | * Xml.prototype.pipeParse - stream
167 | *
168 | * @param {object} [options]
169 | * @param {Number} [options.depth=0]
170 | */
171 | Xml.prototype.pipeParse = function pipeParse (options = {}) {
172 | options.depth = options.depth || 0
173 | const parser = new StreamParser()
174 |
175 | let index = 0
176 | let parsedTags = new Map()
177 | const toEmit = []
178 | const lastTag = {
179 | index: null,
180 | name: null,
181 | tagIndex: null
182 | }
183 |
184 | const getFirstTagName = map => {
185 | if (map.has(0) === false) {
186 | return null
187 | }
188 |
189 | const mapPosZero = map.get(0)
190 | const arrayMap = Array.from(mapPosZero)
191 |
192 | if (arrayMap.length === 0) {
193 | return null
194 | }
195 |
196 | const keyValue = arrayMap[0]
197 |
198 | if (keyValue.length === 0) {
199 | return null
200 | }
201 |
202 | return keyValue[0]
203 | }
204 |
205 | parser.on('opentag', (name, attrs) => {
206 | const inheritFrom = {
207 | index: null,
208 | name: null
209 | }
210 |
211 | if (index >= 1) {
212 | const beforeIndex = index - 1
213 | const beforeKey = [
214 | ...parsedTags
215 | .get(beforeIndex)
216 | .keys()
217 | ].reverse()[0]
218 | inheritFrom.index = beforeIndex
219 | inheritFrom.name = beforeKey
220 | }
221 |
222 | if (!parsedTags.has(index)) {
223 | parsedTags.set(index, new Map())
224 | }
225 |
226 | if (!parsedTags.get(index).has(name)) {
227 | parsedTags.get(index).set(name, [])
228 | }
229 |
230 | const tag = new XmlTag(name, null, attrs, [])
231 | tag.inheritFrom = inheritFrom
232 |
233 | lastTag.index = index
234 | lastTag.name = name
235 | lastTag.tagIndex = parsedTags.get(index).get(name).push(tag) - 1
236 | tag.inheritFrom.tagIndex = lastTag.tagIndex
237 | index = index + 1
238 | })
239 |
240 | parser.on('text', (text) => {
241 | parsedTags
242 | .get(lastTag.index)
243 | .get(lastTag.name)[lastTag.tagIndex]
244 | .value = text
245 |
246 | lastTag.index = null
247 | lastTag.name = null
248 | lastTag.tagIndex = null
249 | })
250 |
251 | parser.on('closetag', (name) => {
252 | index = index - 1
253 |
254 | if (index === options.depth) {
255 | /**
256 | * must reorganize data to a single object
257 | * them emit it
258 | */
259 | let entries = Array.from(parsedTags).reverse()
260 | entries = entries.map(
261 | ([intIndex, tagsMap]) => ({
262 | intIndex, tagsMap: Array.from(tagsMap).reverse()
263 | })
264 | )
265 | entries.pop()
266 | entries.forEach(entry => {
267 | const intIndex = entry.intIndex === 0 ? entry.intIndex : entry.intIndex - 1
268 | const indexedTags = parsedTags.get(intIndex)
269 |
270 | entry.tagsMap.forEach(tag => {
271 | const list = tag[1]
272 | list.forEach(tagToBePushed => {
273 | indexedTags
274 | .get(tagToBePushed.inheritFrom.name)[0]
275 | .tags
276 | .push(tagToBePushed.reset())
277 | })
278 | })
279 | })
280 |
281 | parsedTags
282 | .get(index)
283 | .get(name)
284 | .forEach(tag => toEmit.push(tag.reset()))
285 | }
286 |
287 | if (name === getFirstTagName(parsedTags)) {
288 | parsedTags = new Map()
289 | }
290 | })
291 |
292 | parser.on('cdata', cdata => {
293 | const CData = new XmlCharacterData(cdata)
294 | toEmit.push(CData)
295 | })
296 |
297 | parser.on('instruction', (name, attrs) => {
298 | const declaration = new XmlDeclaration(attrs.version, attrs.encoding)
299 | toEmit.push(declaration)
300 | })
301 |
302 | return new Transform({
303 | objectMode: true,
304 | transform (chunk, encoding, ack) {
305 | parser.write(chunk.toString())
306 |
307 | if (toEmit.length > 0) {
308 | this.push(toEmit.shift())
309 | }
310 |
311 | ack()
312 | }
313 | })
314 | }
315 |
316 | /**
317 | * Xml.prototype.pipeStringify - stream from JS data into XML
318 | *
319 | * @param {object} [options] - all options to stringify
320 | * @param {object} [options.mainTag] - the wrapping tag
321 | * @param {string} [options.mainTag.name] - the wrapping tag's name
322 | */
323 | Xml.prototype.pipeStringify = function pipeStringify (options = {}) {
324 | options.mainTag = options.mainTag || {}
325 | const defaultContent = 'FAKE_CONTENT'
326 | const name = options.mainTag.name
327 | const contents = options.mainTag.text || defaultContent
328 | const tag = { [name || 'fake']: contents }
329 | const stringified = this.stringify(tag)
330 |
331 | const lastIndexOfArrow = stringified.lastIndexOf('<')
332 | let initialTag = stringified.substr(0, lastIndexOfArrow)
333 |
334 | if (initialTag.indexOf(defaultContent) !== -1) {
335 | initialTag.replace(defaultContent, '')
336 | }
337 |
338 | let endingTag = stringified.substr(
339 | lastIndexOfArrow,
340 | stringified.length
341 | )
342 |
343 | if (Reflect.has(options.mainTag, 'name') === false) {
344 | const firstArrowIndex = initialTag.indexOf('>') + 1
345 | initialTag = initialTag.substr(0, firstArrowIndex)
346 | endingTag = ''
347 | }
348 |
349 | const xml = this
350 | let isFirstData = true
351 |
352 | return new Transform({
353 | objectMode: true,
354 | transform (chunk, encoding, ack) {
355 | const options = {
356 | ignoreDeclaration: true
357 | }
358 |
359 | if (isFirstData) {
360 | this.push(initialTag)
361 | isFirstData = false
362 | }
363 |
364 | const toBePushed = xml.stringify(chunk, options)
365 | this.push(toBePushed)
366 |
367 | ack()
368 | },
369 | flush (cb) {
370 | this.push(endingTag)
371 | cb()
372 | }
373 | })
374 | }
375 |
376 | module.exports = Xml
377 |
378 |
379 |
380 |
381 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
--------------------------------------------------------------------------------
/docs/file/src/strategies/Yaml.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/strategies/Yaml.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/strategies/Yaml.js
37 |
const Base = require('./Base')
38 | const ParserError = require('../errors/ParserError')
39 | const yaml = require('js-yaml')
40 |
41 | /**
42 | * Yaml - Support for YAML filetype
43 | *
44 | * @constructor
45 | */
46 | function Yaml () {
47 | }
48 |
49 | Yaml.prototype = Object.create(Base.prototype)
50 |
51 | /**
52 | * Yaml.prototype.stringify - receives * valid JS data and returns it as YAML
53 | *
54 | * @param {object} data
55 | * @param {Object} options - options for turning JS data into YAML
56 | * @throws {ParserError} For invalid data type
57 | * @returns {string}
58 | */
59 | Yaml.prototype.stringify = function stringify (data, options = {}) {
60 | if (Array.isArray(data)) {
61 | throw new ParserError('Only plain objects are accepted for stringify()')
62 | }
63 |
64 | return yaml.safeDump(data)
65 | }
66 |
67 | /**
68 | * Yaml.prototype.parse - receives an YAML string and translate it to valid JavaScript
69 | *
70 | * @param {string} data
71 | * @param {object} options
72 | * @returns {object} Parsed YAML data as JS object
73 | */
74 | Yaml.prototype.parse = function parse (data, options = {}) {
75 | try {
76 | return yaml.safeLoad(data)
77 | } catch (e) {
78 | const context = {
79 | errorName: e.name,
80 | message: e.message,
81 | mark: e.mark
82 | }
83 |
84 | throw new ParserError('yaml', context)
85 | }
86 | }
87 |
88 | module.exports = Yaml
89 |
90 |
91 |
92 |
93 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/docs/file/src/strategies/index.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/strategies/index.js | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | src/strategies/index.js
37 |
const Json = require('./Json')
38 | const Xml = require('./Xml')
39 | const Yaml = require('./Yaml')
40 | const Csv = require('./Csv')
41 |
42 | module.exports = { Json, Xml, Yaml, Csv }
43 |
44 |
45 |
46 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/docs/identifiers.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Reference | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | References
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/docs/image/badge.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | document
13 | document
14 | @ratio@
15 | @ratio@
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/image/esdoc-logo-mini-black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onhernandes/parserblade/8c8b2bf1fc1baa16c953e121ad7029bf936c8686/docs/image/esdoc-logo-mini-black.png
--------------------------------------------------------------------------------
/docs/image/esdoc-logo-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onhernandes/parserblade/8c8b2bf1fc1baa16c953e121ad7029bf936c8686/docs/image/esdoc-logo-mini.png
--------------------------------------------------------------------------------
/docs/image/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onhernandes/parserblade/8c8b2bf1fc1baa16c953e121ad7029bf936c8686/docs/image/github.png
--------------------------------------------------------------------------------
/docs/image/manual-badge.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | manual
13 | manual
14 | @value@
15 | @value@
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/image/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onhernandes/parserblade/8c8b2bf1fc1baa16c953e121ad7029bf936c8686/docs/image/search.png
--------------------------------------------------------------------------------
/docs/inject/css/0-parser.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onhernandes/parserblade/8c8b2bf1fc1baa16c953e121ad7029bf936c8686/docs/inject/css/0-parser.css
--------------------------------------------------------------------------------
/docs/lint.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "valid",
4 | "filePath": "src/strategies/Base.js",
5 | "lines": [
6 | {
7 | "lineNumber": 49,
8 | "line": "Base.prototype.valid = function valid (data, options = {}) {"
9 | }
10 | ],
11 | "codeParams": [
12 | "data",
13 | "options"
14 | ],
15 | "docParams": [
16 | "options"
17 | ]
18 | },
19 | {
20 | "name": "pipeStringify",
21 | "filePath": "src/strategies/Csv.js",
22 | "lines": [
23 | {
24 | "lineNumber": 112,
25 | "line": "Csv.prototype.pipeStringify = function pipeStringify (options = {}) {"
26 | }
27 | ],
28 | "codeParams": [
29 | "options"
30 | ],
31 | "docParams": [
32 | "data",
33 | "options"
34 | ]
35 | }
36 | ]
--------------------------------------------------------------------------------
/docs/manual/README.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Manual | parser
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
44 |
45 |
46 | parser A all-in-one parser for Javascript, heavily inspired by: https://github.com/nathanmac/Parser . It allows you to work with JSON, XML, CSV and YAML more without worrying about which module install. It's designed to work just as JSON.parse
and JSON.stringify
does, with some improvements.
47 |
Installation $ npm install --save parser
48 |
49 |
Usage Every filetype has two main methods: stringify()
and parse()
, both receives two arguments, data
containing any type of data and an options object.
50 |
CSV const { csv } = require('parser')
51 |
52 | // Parsing
53 | const input = 'title,platform\nStardew Valley,Steam'
54 | const result = csv.parse(input)
55 | console.log(result) // [ { title: 'Stardew Valley', platform: 'Steam' } ]
56 |
57 | // Stringifying
58 | console.log(
59 | csv.stringify(result)
60 | ) // 'title,platform\nStardew Valley,Steam'
61 |
62 |
YAML const { yaml } = require('parser')
63 |
64 | // Parsing
65 | const input = 'title: Stardew Valley\nplatform: Steam'
66 | const result = yaml.parse(input)
67 | console.log(result) // { title: 'Stardew Valley', platform: 'Steam' }
68 |
69 | // Stringifying
70 | console.log(
71 | yaml.stringify(result)
72 | ) // 'title: Stardew Valley\nplatform: Steam'
73 |
74 |
XML const { xml } = require('parser')
75 |
76 | // Parsing
77 | const input = '<?xml version="1.0" encoding="utf-8"?><package>lodash</package>'
78 | const result = xml.parse(input)
79 | console.log(result) // { package: 'lodash' }
80 |
81 | // Stringifying
82 | console.log(
83 | xml.stringify(result)
84 | ) // '<?xml version="1.0" encoding="utf-8"?><package>lodash</package>'
85 |
86 |
License MIT ©
87 |
88 |
89 |
90 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/docs/manual/yaml.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Manual | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
30 |
31 |
44 |
68 |
86 |
96 |
97 |
98 |
99 | YAML Usage Both yaml.parse()
and yaml.stringify()
accepts the data to be parsed/stringified as the first argument, and an option's object as the second.
100 |
Parse const assert = require('assert'')
101 | const { yaml } = require('parserblade')
102 | const input = 'series: Bleach\nseasons: 16'
103 | const result = yaml.parse(data)
104 | assert.deepStrictEqual(
105 | result,
106 | { series: 'Bleach', seasons: 16 }
107 | )
108 |
109 |
Stringify const assert = require('assert'')
110 | const { yaml } = require('parserblade')
111 | const input = { series: 'Bleach', seasons: 16 }
112 | const result = yaml.parse(data)
113 | assert.equal(
114 | result,
115 | 'series: Bleach\nseasons: 16'
116 | )
117 |
118 |
Valid Just checks if given string is a valid YAML
119 |
const assert = require('assert')
120 | const { yaml } = require('parserblade')
121 | const result = yaml.valid('[name:\nStardew')
122 |
123 | assert.equal(
124 | result,
125 | false
126 | )
127 |
128 |
129 |
130 |
131 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/docs/script/inherited-summary.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | function toggle(ev) {
3 | var button = ev.target;
4 | var parent = ev.target.parentElement;
5 | while(parent) {
6 | if (parent.tagName === 'TABLE' && parent.classList.contains('summary')) break;
7 | parent = parent.parentElement;
8 | }
9 |
10 | if (!parent) return;
11 |
12 | var tbody = parent.querySelector('tbody');
13 | if (button.classList.contains('opened')) {
14 | button.classList.remove('opened');
15 | button.classList.add('closed');
16 | tbody.style.display = 'none';
17 | } else {
18 | button.classList.remove('closed');
19 | button.classList.add('opened');
20 | tbody.style.display = 'block';
21 | }
22 | }
23 |
24 | var buttons = document.querySelectorAll('.inherited-summary thead .toggle');
25 | for (var i = 0; i < buttons.length; i++) {
26 | buttons[i].addEventListener('click', toggle);
27 | }
28 | })();
29 |
--------------------------------------------------------------------------------
/docs/script/inner-link.js:
--------------------------------------------------------------------------------
1 | // inner link(#foo) can not correctly scroll, because page has fixed header,
2 | // so, I manually scroll.
3 | (function(){
4 | var matched = location.hash.match(/errorLines=([\d,]+)/);
5 | if (matched) return;
6 |
7 | function adjust() {
8 | window.scrollBy(0, -55);
9 | var el = document.querySelector('.inner-link-active');
10 | if (el) el.classList.remove('inner-link-active');
11 |
12 | // ``[ ] . ' " @`` are not valid in DOM id. so must escape these.
13 | var id = location.hash.replace(/([\[\].'"@$])/g, '\\$1');
14 | var el = document.querySelector(id);
15 | if (el) el.classList.add('inner-link-active');
16 | }
17 |
18 | window.addEventListener('hashchange', adjust);
19 |
20 | if (location.hash) {
21 | setTimeout(adjust, 0);
22 | }
23 | })();
24 |
25 | (function(){
26 | var els = document.querySelectorAll('[href^="#"]');
27 | var href = location.href.replace(/#.*$/, ''); // remove existed hash
28 | for (var i = 0; i < els.length; i++) {
29 | var el = els[i];
30 | el.href = href + el.getAttribute('href'); // because el.href is absolute path
31 | }
32 | })();
33 |
--------------------------------------------------------------------------------
/docs/script/manual.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | var matched = location.pathname.match(/\/(manual\/.*\.html)$/);
3 | if (!matched) return;
4 |
5 | var currentName = matched[1];
6 | var cssClass = '.navigation .manual-toc li[data-link="' + currentName + '"]';
7 | var styleText = cssClass + '{ display: block; }\n';
8 | styleText += cssClass + '.indent-h1 a { color: #039BE5 }';
9 | var style = document.createElement('style');
10 | style.textContent = styleText;
11 | document.querySelector('head').appendChild(style);
12 | })();
13 |
--------------------------------------------------------------------------------
/docs/script/patch-for-local.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | if (location.protocol === 'file:') {
3 | var elms = document.querySelectorAll('a[href="./"]');
4 | for (var i = 0; i < elms.length; i++) {
5 | elms[i].href = './index.html';
6 | }
7 | }
8 | })();
9 |
--------------------------------------------------------------------------------
/docs/script/prettify/Apache-License-2.0.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/docs/script/pretty-print.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | prettyPrint();
3 | var lines = document.querySelectorAll('.prettyprint.linenums li[class^="L"]');
4 | for (var i = 0; i < lines.length; i++) {
5 | lines[i].id = 'lineNumber' + (i + 1);
6 | }
7 |
8 | var matched = location.hash.match(/errorLines=([\d,]+)/);
9 | if (matched) {
10 | var lines = matched[1].split(',');
11 | for (var i = 0; i < lines.length; i++) {
12 | var id = '#lineNumber' + lines[i];
13 | var el = document.querySelector(id);
14 | el.classList.add('error-line');
15 | }
16 | return;
17 | }
18 |
19 | if (location.hash) {
20 | // ``[ ] . ' " @`` are not valid in DOM id. so must escape these.
21 | var id = location.hash.replace(/([\[\].'"@$])/g, '\\$1');
22 | var line = document.querySelector(id);
23 | if (line) line.classList.add('active');
24 | }
25 | })();
26 |
--------------------------------------------------------------------------------
/docs/script/search.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | var searchIndex = window.esdocSearchIndex;
3 | var searchBox = document.querySelector('.search-box');
4 | var input = document.querySelector('.search-input');
5 | var result = document.querySelector('.search-result');
6 | var selectedIndex = -1;
7 | var prevText;
8 |
9 | // active search box and focus when mouse enter on search box.
10 | searchBox.addEventListener('mouseenter', function(){
11 | searchBox.classList.add('active');
12 | input.focus();
13 | });
14 |
15 | // search with text when key is upped.
16 | input.addEventListener('keyup', function(ev){
17 | var text = ev.target.value.toLowerCase();
18 | if (!text) {
19 | result.style.display = 'none';
20 | result.innerHTML = '';
21 | return;
22 | }
23 |
24 | if (text === prevText) return;
25 | prevText = text;
26 |
27 | var html = {class: [], method: [], member: [], function: [], variable: [], typedef: [], external: [], file: [], test: [], testFile: []};
28 | var len = searchIndex.length;
29 | var kind;
30 | for (var i = 0; i < len; i++) {
31 | var pair = searchIndex[i];
32 | if (pair[0].indexOf(text) !== -1) {
33 | kind = pair[3];
34 | html[kind].push('' + pair[2] + ' ');
35 | }
36 | }
37 |
38 | var innerHTML = '';
39 | for (kind in html) {
40 | var list = html[kind];
41 | if (!list.length) continue;
42 | innerHTML += '' + kind + ' \n' + list.join('\n');
43 | }
44 | result.innerHTML = innerHTML;
45 | if (innerHTML) result.style.display = 'block';
46 | selectedIndex = -1;
47 | });
48 |
49 | // down, up and enter key are pressed, select search result.
50 | input.addEventListener('keydown', function(ev){
51 | if (ev.keyCode === 40) {
52 | // arrow down
53 | var current = result.children[selectedIndex];
54 | var selected = result.children[selectedIndex + 1];
55 | if (selected && selected.classList.contains('search-separator')) {
56 | var selected = result.children[selectedIndex + 2];
57 | selectedIndex++;
58 | }
59 |
60 | if (selected) {
61 | if (current) current.classList.remove('selected');
62 | selectedIndex++;
63 | selected.classList.add('selected');
64 | }
65 | } else if (ev.keyCode === 38) {
66 | // arrow up
67 | var current = result.children[selectedIndex];
68 | var selected = result.children[selectedIndex - 1];
69 | if (selected && selected.classList.contains('search-separator')) {
70 | var selected = result.children[selectedIndex - 2];
71 | selectedIndex--;
72 | }
73 |
74 | if (selected) {
75 | if (current) current.classList.remove('selected');
76 | selectedIndex--;
77 | selected.classList.add('selected');
78 | }
79 | } else if (ev.keyCode === 13) {
80 | // enter
81 | var current = result.children[selectedIndex];
82 | if (current) {
83 | var link = current.querySelector('a');
84 | if (link) location.href = link.href;
85 | }
86 | } else {
87 | return;
88 | }
89 |
90 | ev.preventDefault();
91 | });
92 |
93 | // select search result when search result is mouse over.
94 | result.addEventListener('mousemove', function(ev){
95 | var current = result.children[selectedIndex];
96 | if (current) current.classList.remove('selected');
97 |
98 | var li = ev.target;
99 | while (li) {
100 | if (li.nodeName === 'LI') break;
101 | li = li.parentElement;
102 | }
103 |
104 | if (li) {
105 | selectedIndex = Array.prototype.indexOf.call(result.children, li);
106 | li.classList.add('selected');
107 | }
108 | });
109 |
110 | // clear search result when body is clicked.
111 | document.body.addEventListener('click', function(ev){
112 | selectedIndex = -1;
113 | result.style.display = 'none';
114 | result.innerHTML = '';
115 | });
116 |
117 | })();
118 |
--------------------------------------------------------------------------------
/docs/script/test-summary.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | function toggle(ev) {
3 | var button = ev.target;
4 | var parent = ev.target.parentElement;
5 | while(parent) {
6 | if (parent.tagName === 'TR' && parent.classList.contains('test-interface')) break;
7 | parent = parent.parentElement;
8 | }
9 |
10 | if (!parent) return;
11 |
12 | var direction;
13 | if (button.classList.contains('opened')) {
14 | button.classList.remove('opened');
15 | button.classList.add('closed');
16 | direction = 'closed';
17 | } else {
18 | button.classList.remove('closed');
19 | button.classList.add('opened');
20 | direction = 'opened';
21 | }
22 |
23 | var targetDepth = parseInt(parent.dataset.testDepth, 10) + 1;
24 | var nextElement = parent.nextElementSibling;
25 | while (nextElement) {
26 | var depth = parseInt(nextElement.dataset.testDepth, 10);
27 | if (depth >= targetDepth) {
28 | if (direction === 'opened') {
29 | if (depth === targetDepth) nextElement.style.display = '';
30 | } else if (direction === 'closed') {
31 | nextElement.style.display = 'none';
32 | var innerButton = nextElement.querySelector('.toggle');
33 | if (innerButton && innerButton.classList.contains('opened')) {
34 | innerButton.classList.remove('opened');
35 | innerButton.classList.add('closed');
36 | }
37 | }
38 | } else {
39 | break;
40 | }
41 | nextElement = nextElement.nextElementSibling;
42 | }
43 | }
44 |
45 | var buttons = document.querySelectorAll('.test-summary tr.test-interface .toggle');
46 | for (var i = 0; i < buttons.length; i++) {
47 | buttons[i].addEventListener('click', toggle);
48 | }
49 |
50 | var topDescribes = document.querySelectorAll('.test-summary tr[data-test-depth="0"]');
51 | for (var i = 0; i < topDescribes.length; i++) {
52 | topDescribes[i].style.display = '';
53 | }
54 | })();
55 |
--------------------------------------------------------------------------------
/docs/source.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Source | parserblade
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
35 |
36 | Source 35/68
37 |
38 |
39 |
40 |
41 | File
42 | Identifier
43 | Document
44 | Size
45 | Lines
46 | Updated
47 |
48 |
49 |
50 |
51 |
52 | src/Parser.js
53 | -
54 | 75 % 6/8
55 | 1613 byte
56 | 59
57 | 2020-08-04 00:14:09 (UTC)
58 |
59 |
60 | src/errors/NotImplemented.js
61 | -
62 | 100 % 1/1
63 | 222 byte
64 | 11
65 | 2020-07-06 16:42:39 (UTC)
66 |
67 |
68 | src/errors/ParserError.js
69 | -
70 | 100 % 1/1
71 | 353 byte
72 | 15
73 | 2020-07-02 23:47:30 (UTC)
74 |
75 |
76 | src/index.js
77 | -
78 | 0 % 0/2
79 | 245 byte
80 | 14
81 | 2020-07-05 17:38:00 (UTC)
82 |
83 |
84 | src/strategies/Base.js
85 | -
86 | 77 % 7/9
87 | 2042 byte
88 | 81
89 | 2020-08-04 00:14:09 (UTC)
90 |
91 |
92 | src/strategies/Csv.js
93 | -
94 | 45 % 5/11
95 | 3635 byte
96 | 126
97 | 2020-09-05 23:29:55 (UTC)
98 |
99 |
100 | src/strategies/Json.js
101 | -
102 | 62 % 5/8
103 | 1836 byte
104 | 73
105 | 2020-08-04 00:14:09 (UTC)
106 |
107 |
108 | src/strategies/Xml/XmlTag.js
109 | -
110 | 0 % 0/4
111 | 535 byte
112 | 23
113 | 2020-08-01 22:37:28 (UTC)
114 |
115 |
116 | src/strategies/Xml/index.js
117 | -
118 | 50 % 7/14
119 | 8128 byte
120 | 340
121 | 2020-09-03 01:55:36 (UTC)
122 |
123 |
124 | src/strategies/Yaml.js
125 | -
126 | 50 % 3/6
127 | 1197 byte
128 | 52
129 | 2020-07-02 23:47:30 (UTC)
130 |
131 |
132 | src/strategies/index.js
133 | -
134 | 0 % 0/4
135 | 163 byte
136 | 6
137 | 2020-07-05 19:27:29 (UTC)
138 |
139 |
140 |
141 |
142 |
143 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/esdocs/css/parser.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onhernandes/parserblade/8c8b2bf1fc1baa16c953e121ad7029bf936c8686/esdocs/css/parser.css
--------------------------------------------------------------------------------
/esdocs/manual/csv.md:
--------------------------------------------------------------------------------
1 | # CSV
2 |
3 | Works with CSV data. I haven't tested with xlsx or other similar data types yet.
4 |
5 | ## Usage
6 |
7 | Both `csv.parse()` and `csv.stringify()` accepts the data to be parsed/stringified as the first argument, and an option's object as the second.
8 |
9 | ## Parse
10 |
11 | Parses CSV string to JS data, automatically uses first line as headers. Pass data as first argument.
12 |
13 | ```javascript
14 | const assert = require('assert')
15 | const { csv } = require('parserblade')
16 | const input = 'title,platform\nStardew Valley,Steam'
17 | const result = csv.parse(input)
18 |
19 | assert.deepStrictEqual(
20 | result,
21 | [ { title: 'Stardew Valley', platform: 'Steam' } ]
22 | )
23 | ```
24 |
25 | ### Parse headers
26 |
27 | Don't use first line as headers. Pass `{ headers: false }` as second parameter.
28 |
29 | ```javascript
30 | const assert = require('assert')
31 | const { csv } = require('parserblade')
32 | const input = 'name,email\nNetflix,contact@netflix.com'
33 | const result = csv.parse(input, { headers: false })
34 |
35 | assert.deepStrictEqual(
36 | result,
37 | [
38 | ['name', 'email'],
39 | ['Netflix', 'contact@netflix.com']
40 | ]
41 | )
42 | ```
43 |
44 | Specify headers passing `{ headers: ['name', 'email'] }`
45 |
46 | ```javascript
47 | const assert = require('assert')
48 | const { csv } = require('parserblade')
49 | const input = 'name,email\nNetflix,contact@netflix.com'
50 | const result = csv.parse(input, { headers: false })
51 |
52 | assert.deepStrictEqual(
53 | result,
54 | [
55 | { name: 'Netflix', email: 'contact@netflix.com' }
56 | ]
57 | )
58 | ```
59 |
60 | Specify a function to transform headers passing `{ headers: header => header.toUpperCase() }`
61 |
62 | ```javascript
63 | const assert = require('assert')
64 | const { csv } = require('parserblade')
65 | const input = 'name,email\nNetflix,contact@netflix.com'
66 | const result = csv.parse(input, { headers: false })
67 |
68 | assert.deepStrictEqual(
69 | result,
70 | [
71 | { NAME: 'Netflix', EMAIL: 'contact@netflix.com' }
72 | ]
73 | )
74 | ```
75 |
76 | ### Parse with custom delimiters
77 |
78 | Uses custom delimiters. Anything you want! Pass `{ delimiter: ';' }` as option.
79 | ```javascript
80 | const assert = require('assert')
81 | const { csv } = require('parserblade')
82 | const input = 'name;email\nNetflix;contact@netflix.com'
83 | const result = csv.parse(input, { delimiter: ';' })
84 |
85 | assert.deepStrictEqual(
86 | result,
87 | [ { name: 'Netflix', email: 'contact@netflix.com' } ]
88 | )
89 | ```
90 |
91 | ### Parse skipping some lines
92 |
93 | Pass `{ skipLines: 2 }` as option.
94 |
95 | ```javascript
96 | const assert = require('assert')
97 | const { csv } = require('parserblade')
98 | const input = 'coll streaming platforms\nname,email\nNetflix,contact@netflix.com'
99 | const result = csv.parse(input, { skipLines: 2 })
100 |
101 | assert.deepStrictEqual(
102 | result,
103 | [ { name: 'Netflix', email: 'contact@netflix.com' } ]
104 | )
105 | ```
106 |
107 | ### Parse offset
108 |
109 | Pass `{ offset: 2 }` as option.
110 |
111 | ```javascript
112 | const assert = require('assert')
113 | const { csv } = require('parserblade')
114 | const input = 'name,email\nNetflix,contact@netflix.com\nAmazon,contact@amazon.com'
115 | const result = csv.parse(input, { offset: 2 })
116 |
117 | assert.deepStrictEqual(
118 | result,
119 | [ { name: 'Netflix', email: 'contact@netflix.com' } ]
120 | )
121 | ```
122 |
123 | ## Stringify
124 |
125 | Simply transforms JS array of objects into CSV
126 |
127 | ```javascript
128 | const assert = require('assert')
129 | const { csv } = require('parserblade')
130 | const input = [
131 | { name: 'Netflix', email: 'contact@netflix.com' }
132 | ]
133 | const result = csv.stringify(input)
134 |
135 | assert.equal(
136 | result,
137 | 'name,email\nNetflix,contact@netflix.com'
138 | )
139 | ```
140 |
141 | ### Stringify omitting headers
142 |
143 | Pass `{ headers: false }` as options
144 |
145 | ```javascript
146 | const assert = require('assert')
147 | const { csv } = require('parserblade')
148 | const input = [
149 | { name: 'Netflix', email: 'contact@netflix.com' }
150 | ]
151 | const result = csv.stringify(input)
152 |
153 | assert.equal(
154 | result,
155 | 'Netflix,contact@netflix.com'
156 | )
157 | ```
158 |
159 | ### Stringify with custom column names/headers
160 |
161 | Specifying custom columns is easy in many forms, like just pass `{ columns: [ { key: '', header: '' } ] }` as options.
162 |
163 | Or `{ columns: ['name', 'email'] }`.
164 |
165 | Or `{ columns: { name: 'Name', email: 'Email' } }`.
166 |
167 | ```javascript
168 | const assert = require('assert')
169 | const { csv } = require('parserblade')
170 | const input = [
171 | { name: 'Netflix', email: 'contact@netflix.com' }
172 | ]
173 |
174 | const columns = [
175 | { key: 'name', header: 'Platform' },
176 | { key: 'email', header: 'e-mail' }
177 | ]
178 |
179 | const result = csv.stringify(input, { columns })
180 |
181 | assert.equal(
182 | result,
183 | 'Platform,e-mail\nNetflix,contact@netflix.com'
184 | )
185 | ```
186 |
187 | ## Valid
188 |
189 | Just checks if given string is a valid CSV
190 |
191 | ```javascript
192 | const assert = require('assert')
193 | const { csv } = require('parserblade')
194 | const result = csv.valid('name\nstardew,pokemon')
195 |
196 | assert.equal(
197 | result,
198 | false
199 | )
200 | ```
201 |
202 | ## Stream
203 |
204 | ### pipeStringify
205 |
206 | Turns JS data into CSV
207 |
208 | ```javascript
209 | const { csv } = require('parserblade')
210 | const { Readable } = require('stream')
211 | const fs = require('fs')
212 |
213 | const input = [{ game: 'Killing Floor' }, { game: 'Stardew Valley' }]
214 | const reader = new Readable({
215 | objectMode: true,
216 | read (size) {
217 | const next = input.shift()
218 | this.push(next || null)
219 | }
220 | })
221 |
222 | const writer = csv.pipeStringify()
223 | const toFile = fs.createWriteStream('./data-test.csv')
224 |
225 | reader
226 | .pipe(writer)
227 | .pipe(toFile)
228 | .on('error', console.log)
229 | .on('end', () => {
230 | console.log('done')
231 | })
232 | ```
233 |
234 | ### pipeStringify with custom delimiter
235 |
236 | ```javascript
237 | const { csv } = require('parserblade')
238 | const { Readable } = require('stream')
239 | const fs = require('fs')
240 |
241 | const input = [{ game: 'Killing Floor' }, { game: 'Stardew Valley' }]
242 | const reader = new Readable({
243 | objectMode: true,
244 | read (size) {
245 | const next = input.shift()
246 | this.push(next || null)
247 | }
248 | })
249 |
250 | const writer = csv.pipeStringify({ delimiter: ';' })
251 | const toFile = fs.createWriteStream('./data-test.csv')
252 |
253 | reader
254 | .pipe(writer)
255 | .pipe(toFile)
256 | .on('error', console.log)
257 | .on('end', () => {
258 | console.log('done')
259 | })
260 | ```
261 |
262 | ### pipeStringify with custom column names
263 |
264 | ```javascript
265 | const { csv } = require('parserblade')
266 | const { Readable } = require('stream')
267 | const fs = require('fs')
268 |
269 | const input = [{ game: 'Killing Floor' }, { game: 'Stardew Valley' }]
270 | const reader = new Readable({
271 | objectMode: true,
272 | read (size) {
273 | const next = input.shift()
274 | this.push(next || null)
275 | }
276 | })
277 |
278 | const columns = [
279 | { key: 'game', header: 'title' }
280 | ]
281 |
282 | const writer = csv.pipeStringify({ columns })
283 | const toFile = fs.createWriteStream('./data-test.csv')
284 |
285 | reader
286 | .pipe(writer)
287 | .pipe(toFile)
288 | .on('error', console.log)
289 | .on('end', () => {
290 | console.log('done')
291 | })
292 | ```
293 |
294 | ### pipeStringify reordering columns
295 |
296 | ```javascript
297 | const { csv } = require('parserblade')
298 | const { Readable } = require('stream')
299 | const fs = require('fs')
300 |
301 | const input = [{ game: 'Killing Floor', platform: 'Windows 10' }, { game: 'Stardew Valley', platform: 'Windows 10' }]
302 | const reader = new Readable({
303 | objectMode: true,
304 | read (size) {
305 | const next = input.shift()
306 | this.push(next || null)
307 | }
308 | })
309 |
310 | const columns = [
311 | { key: 'platform' },
312 | { key: 'game' }
313 | ]
314 |
315 | const writer = csv.pipeStringify({ columns })
316 | const toFile = fs.createWriteStream('./data-test.csv')
317 |
318 | reader
319 | .pipe(writer)
320 | .pipe(toFile)
321 | .on('error', console.log)
322 | .on('end', () => {
323 | console.log('done')
324 | })
325 | ```
326 |
327 | ### pipeParse
328 |
329 | ```javascript
330 | const { csv } = require('parserblade')
331 | const fs = require('fs')
332 | const path = require('path')
333 | const filepath = path.resolve(__dirname, '../data/services.csv')
334 |
335 | const reader = fs.createReadStream(filepath)
336 | const writer = csv.pipeParse()
337 |
338 | reader
339 | .pipe(writer)
340 | .on('readable', console.log)
341 | .on('error', console.log)
342 | .on('end', () => {
343 | console.log('done')
344 | })
345 | ```
346 |
347 | ### pipeParse setting custom delimiter
348 |
349 | ```javascript
350 | const { csv } = require('parserblade')
351 | const fs = require('fs')
352 | const path = require('path')
353 | const filepath = path.resolve(__dirname, '../data/services.csv')
354 |
355 | const reader = fs.createReadStream(filepath)
356 | const writer = csv.pipeParse({ delimiter: ';' })
357 |
358 | reader
359 | .pipe(writer)
360 | .on('readable', console.log)
361 | .on('error', console.log)
362 | .on('end', () => {
363 | console.log('done')
364 | })
365 | ```
366 |
367 | ### pipeParse without using first line as header
368 |
369 | ```javascript
370 | const { csv } = require('parserblade')
371 | const fs = require('fs')
372 | const path = require('path')
373 | const filepath = path.resolve(__dirname, '../data/services.csv')
374 |
375 | const reader = fs.createReadStream(filepath)
376 | const writer = csv.pipeParse({ headers: false })
377 |
378 | reader
379 | .pipe(writer)
380 | .on('readable', console.log)
381 | .on('error', console.log)
382 | .on('end', () => {
383 | console.log('done')
384 | })
385 | ```
386 |
--------------------------------------------------------------------------------
/esdocs/manual/json.md:
--------------------------------------------------------------------------------
1 | # JSON
2 |
3 | ## Parse
4 |
5 | There's no magic here. It just calls native's `JSON.parse`, currently there's no additional parameters.
6 |
7 | ```javascript
8 | const assert = require('assert')
9 | const { json } = require('parserblade')
10 | const input = '[{"game":"Stardew Valley"}]'
11 | const result = json.parse(input)
12 |
13 | assert.deepStrictEqual(
14 | result,
15 | [ { game: 'Stardew Valley' } ]
16 | )
17 | ```
18 |
19 | ## Stringify
20 |
21 | There's no magic here. It just calls native's `JSON.stringify`, currently there's no additional parameters.
22 |
23 | ```javascript
24 | const assert = require('assert')
25 | const { json } = require('parserblade')
26 | const input = [ { game: 'Stardew Valley' } ]
27 | const result = json.stringify(input)
28 |
29 | assert.equal(
30 | result,
31 | '[{"game":"Stardew Valley"}]'
32 | )
33 | ```
34 |
35 | ## Valid
36 |
37 | Just checks if given string is a valid JSON data
38 |
39 | ```javascript
40 | const assert = require('assert')
41 | const { json } = require('parserblade')
42 | const result = json.valid('{')
43 |
44 | assert.equal(
45 | result,
46 | false
47 | )
48 | ```
49 |
50 | ## Stream
51 |
52 | ### Stringify an array
53 |
54 | ```javascript
55 | const { json } = require('parserblade')
56 | const { Readable } = require('stream')
57 | const fs = require('fs')
58 |
59 | const input = [{ game: 'Killing Floor' }, { game: 'Stardew Valley' }]
60 | const reader = new Readable({
61 | objectMode: true,
62 | read (size) {
63 | const next = input.shift()
64 |
65 | if (!next) {
66 | this.push(null)
67 | } else {
68 | this.push(next)
69 | }
70 | }
71 | })
72 |
73 | const writer = json.pipeStringify()
74 | const toFile = fs.createWriteStream('./data-test.json')
75 |
76 | reader
77 | .pipe(writer)
78 | .pipe(toFile)
79 | .on('error', console.log)
80 | .on('end', () => {
81 | console.log('done')
82 | })
83 | ```
84 |
85 | ### Stringify an object
86 |
87 | You must pass `{ type: 'object' }` as param. Defaults to `array`.
88 |
89 | Data must be an array of `[ key, value ]`. Like from `Object.entries({ game: 'Stardew Valley' })`
90 |
91 | ```javascript
92 | const { json } = require('parserblade')
93 | const { Readable } = require('stream')
94 | const fs = require('fs')
95 |
96 | const input = Object.entries({
97 | name: 'Rodolfo'
98 | })
99 |
100 | const reader = new Readable({
101 | objectMode: true,
102 | read (size) {
103 | const next = input.shift()
104 |
105 | if (!next) {
106 | this.push(null)
107 | } else {
108 | this.push(next)
109 | }
110 | }
111 | })
112 |
113 | const writer = json.pipeStringify({ type: 'object' })
114 | const toFile = fs.createWriteStream('./data-test.json')
115 |
116 | reader
117 | .pipe(writer)
118 | .pipe(toFile)
119 | .on('error', console.log)
120 | .on('end', () => {
121 | console.log('done')
122 | })
123 | ```
124 |
125 | ### Parse
126 |
127 | ```javascript
128 | const { json } = require('parserblade')
129 | const fs = require('fs')
130 | const path = require('path')
131 | const filepath = path.resolve(__dirname, '../data/services.json')
132 |
133 | const reader = fs.createReadStream(filepath)
134 | const writer = json.pipeParse()
135 |
136 | reader
137 | .pipe(writer)
138 | .on('data', console.log)
139 | .on('error', console.log)
140 | .on('end', () => {
141 | console.log('done')
142 | })
143 | ```
144 |
--------------------------------------------------------------------------------
/esdocs/manual/xml.md:
--------------------------------------------------------------------------------
1 | # XML
2 |
3 | Works with XML data. I haven't tested with xlsx or other similar data types yet. There's a lot of things to improve here.
4 |
5 | ## Usage
6 |
7 | Both `xml.parse()` and `xml.stringify()` accepts the data to be parsed/stringified as the first argument, and an option's object as the second.
8 |
9 | ### Parse
10 |
11 | ```javascript
12 | const assert = require('assert')
13 | const { xml } = require('parserblade')
14 | const input = 'Naruto Shippuden Storm 3 playstation '
15 | const result = xml.parse(input)
16 |
17 | assert.deepStrictEqual(
18 | result,
19 | {
20 | games: {
21 | name: { _text: 'Naruto Shippuden Storm 3' },
22 | platform: { _text: 'playstation' }
23 | }
24 | }
25 | )
26 | ```
27 |
28 | ```javascript
29 | const assert = require('assert')
30 | const { xml } = require('parserblade')
31 | const input = 'mongoose sequelize '
32 | const result = xml.parse(input)
33 |
34 | assert.deepStrictEqual(
35 | result,
36 | {
37 | packages: {
38 | name: [
39 | { _text: 'mongoose' },
40 | { _text: 'sequelize' }
41 | ]
42 | }
43 | }
44 | )
45 | ```
46 |
47 | #### Parse XML including declaration
48 |
49 | Pass `{ showDeclaration: true }` as option.
50 |
51 | ```javascript
52 | const assert = require('assert')
53 | const { xml } = require('parserblade')
54 | const input = 'mongoose sequelize '
55 | const result = xml.parse(input, { showDeclaration: true })
56 |
57 | assert.deepStrictEqual(
58 | result,
59 | {
60 | _declaration: {
61 | _attributes: {
62 | encoding: 'utf-8',
63 | version: 1
64 | }
65 | },
66 | packages: {
67 | name: [
68 | { _text: 'mongoose' },
69 | { _text: 'sequelize' }
70 | ]
71 | }
72 | }
73 | )
74 | ```
75 |
76 | #### Parse XML in verbose mode
77 |
78 | Pass `{ verbose: true }` as option.
79 |
80 | ```javascript
81 | const assert = require('assert')
82 | const { xml } = require('parserblade')
83 | const input = 'Naruto Shippuden Storm 3 playstation '
84 | const result = xml.parse(input, { verbose: true })
85 | const expected = {
86 | elements: [
87 | {
88 | type: 'element',
89 | name: 'games',
90 | elements: [
91 | {
92 | type: 'element',
93 | name: 'name',
94 | elements: [
95 | {
96 | type: 'text',
97 | text: 'Naruto Shippuden Storm 3'
98 | }
99 | ]
100 | },
101 | {
102 | type: 'element',
103 | name: 'platform',
104 | elements: [
105 | {
106 | type: 'text',
107 | text: 'playstation'
108 | }
109 | ]
110 | },
111 | ]
112 | }
113 | ]
114 | }
115 |
116 | assert.deepStrictEqual(
117 | result,
118 | expected
119 | )
120 | ```
121 |
122 | ### Stringify
123 |
124 | ```javascript
125 | const assert = require('assert')
126 | const { xml } = require('parserblade')
127 | const input = { game: 'Stardew Valley' }
128 | const result = xml.stringify(input)
129 |
130 | assert.deepStrictEqual(
131 | result,
132 | 'Stardew Valley '
133 | )
134 | ```
135 |
136 | #### Stringify without XML declaration
137 |
138 | ```javascript
139 | const assert = require('assert')
140 | const { xml } = require('parserblade')
141 | const input = { game: 'Stardew Valley' }
142 | const result = xml.stringify(input, { ignoreDeclaration: true })
143 |
144 | assert.deepStrictEqual(
145 | result,
146 | 'Stardew Valley '
147 | )
148 | ```
149 |
150 | #### Stringify array
151 |
152 | ```javascript
153 | const assert = require('assert')
154 | const { xml } = require('parserblade')
155 | const input = {
156 | packages: [
157 | { name: 'lodash' }
158 | ]
159 | }
160 | const result = xml.stringify(input)
161 |
162 | assert.deepStrictEqual(
163 | result,
164 | 'lodash '
165 | )
166 | ```
167 |
168 | #### Stringify with metadata
169 |
170 | ```javascript
171 | const assert = require('assert')
172 | const { xml } = require('parserblade')
173 | const input = {
174 | packages: [
175 | {
176 | _text: 'lodash',
177 | _attributes: { lang: 'nodejs' }
178 | },
179 | {
180 | _text: 'flash',
181 | _attributes: { lang: 'python' }
182 | }
183 | ]
184 | }
185 | const result = xml.stringify(input)
186 |
187 | assert.deepStrictEqual(
188 | result,
189 | 'lodash flash '
190 | )
191 | ```
192 |
193 | ### Valid
194 |
195 | Just checks if given string is a valid XML
196 |
197 | ```javascript
198 | const assert = require('assert')
199 | const { xml } = require('parserblade')
200 | const result = xml.valid('phrase ')
201 |
202 | assert.equal(
203 | result,
204 | false
205 | )
206 | ```
207 |
208 | ## Stream
209 |
210 | ### pipeParse
211 |
212 | You may specify in which depth it should emit data, defaults to 0.
213 |
214 | ```javascript
215 | const { Readable } = require('stream')
216 | const { xml } = require('parserblade')
217 | const input = `
218 |
219 |
220 | Naruto Shippuden Storm 3
221 |
222 | platform
223 |
224 | This is another tag
225 |
226 |
227 | Third tag another
228 |
229 |
230 |
231 | Netflix
232 |
233 | Possible description here
234 |
235 |
236 |
237 | `.split('')
238 |
239 | const reader = new Readable({
240 | read () {
241 | const next = input.shift()
242 | if (typeof next === 'string') {
243 | this.push(next)
244 | } else {
245 | this.push(null)
246 | }
247 | }
248 | })
249 |
250 | reader
251 | .pipe(xml.pipeParse())
252 | .on('data', console.log)
253 | .on('error', console.log)
254 | .on('end', () => console.log('stream ended'))
255 | ```
256 |
257 | ### pipeStringify
258 |
259 | You can set which tag wraps everything with `{ mainTag: { name, text, attributes } }`
260 |
261 | ```javascript
262 | const { Readable } = require('stream')
263 | const { xml } = require('parserblade')
264 | const input = [{ name: 'Starcraft II' }]
265 |
266 | const reader = new Readable({
267 | objectMode: true,
268 | read () {
269 | const next = input.shift()
270 | this.push(next || null)
271 | }
272 | })
273 |
274 | reader
275 | .pipe(xml.pipeParse({ mainTag: { name: 'games' } }))
276 | .on('data', console.log)
277 | .on('error', console.log)
278 | .on('end', () => console.log('stream ended'))
279 | ```
280 |
--------------------------------------------------------------------------------
/esdocs/manual/yaml.md:
--------------------------------------------------------------------------------
1 | # YAML
2 |
3 | ## Usage
4 |
5 | Both `yaml.parse()` and `yaml.stringify()` accepts the data to be parsed/stringified as the first argument, and an option's object as the second.
6 |
7 | ### Parse
8 |
9 | ```javascript
10 | const assert = require('assert'')
11 | const { yaml } = require('parserblade')
12 | const input = 'series: Bleach\nseasons: 16'
13 | const result = yaml.parse(data)
14 | assert.deepStrictEqual(
15 | result,
16 | { series: 'Bleach', seasons: 16 }
17 | )
18 | ```
19 |
20 | ### Stringify
21 |
22 | ```javascript
23 | const assert = require('assert'')
24 | const { yaml } = require('parserblade')
25 | const input = { series: 'Bleach', seasons: 16 }
26 | const result = yaml.parse(data)
27 | assert.equal(
28 | result,
29 | 'series: Bleach\nseasons: 16'
30 | )
31 | ```
32 |
33 | ### Valid
34 |
35 | Just checks if given string is a valid YAML
36 |
37 | ```javascript
38 | const assert = require('assert')
39 | const { yaml } = require('parserblade')
40 | const result = yaml.valid('[name:\nStardew')
41 |
42 | assert.equal(
43 | result,
44 | false
45 | )
46 | ```
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "parserblade",
3 | "version": "1.2.0",
4 | "description": "The easiest parser for JSON, XML, CSV and YAML. Use it as simple as JSON.stringify() or JSON.parse(). All in one place.",
5 | "homepage": "https://onhernandes.github.io/parserblade",
6 | "author": {
7 | "name": "Matheus Hernandes",
8 | "email": "",
9 | "url": "onhernandes.github.io"
10 | },
11 | "files": [
12 | "src"
13 | ],
14 | "main": "src/index.js",
15 | "keywords": [
16 | "parser",
17 | "data-parser",
18 | "stream",
19 | "stream-parser",
20 | "streaming-parser",
21 | "csv-parser",
22 | "csv-parser-stream",
23 | "csv-stringify",
24 | "csv-stringify-stream",
25 | "transform-csv",
26 | "csv-streaming",
27 | "csv",
28 | "yaml-parser",
29 | "yaml-stringify",
30 | "yaml-stringify-stream",
31 | "yaml-parser-stream",
32 | "transform-yaml",
33 | "yaml",
34 | "xml-parser",
35 | "xml-stringify",
36 | "xml-parser-stream",
37 | "xml-stringify-stream",
38 | "transform-xml",
39 | "xml",
40 | "json",
41 | "json-parser",
42 | "json-stringify",
43 | "json-stringify-stream",
44 | "json-parser-stream"
45 | ],
46 | "devDependencies": {
47 | "esdoc": "^1.1.0",
48 | "esdoc-ecmascript-proposal-plugin": "^1.0.0",
49 | "esdoc-inject-style-plugin": "^1.0.0",
50 | "esdoc-standard-plugin": "^1.0.0",
51 | "eslint": "^7.4.0",
52 | "eslint-config-standard": "^14.1.1",
53 | "eslint-plugin-import": "^2.22.0",
54 | "eslint-plugin-node": "^11.1.0",
55 | "eslint-plugin-promise": "^4.2.1",
56 | "eslint-plugin-standard": "^4.0.1",
57 | "husky": "^3.0.9",
58 | "jest": "^26.1.0",
59 | "lint-staged": "^9.4.3"
60 | },
61 | "engines": {
62 | "npm": ">= 4.0.0"
63 | },
64 | "lint-staged": {
65 | "*.js": [
66 | "eslint --fix",
67 | "git add"
68 | ]
69 | },
70 | "husky": {
71 | "hooks": {
72 | "pre-commit": "lint-staged"
73 | }
74 | },
75 | "scripts": {
76 | "test": "./node_modules/.bin/jest --no-cache",
77 | "tdd": "./node_modules/.bin/jest --watch --no-cache",
78 | "docs": "./node_modules/.bin/esdoc",
79 | "lint": "./node_modules/.bin/eslint",
80 | "publish-docs": "./node_modules/.bin/esdoc && git subtree push --prefix docs origin gh-pages",
81 | "tarfile": "tar -czvf parserblade.tar.gz LICENSE package* README.md src"
82 | },
83 | "repository": "git@github.com:onhernandes/parserblade.git",
84 | "jest": {
85 | "testEnvironment": "node"
86 | },
87 | "license": "MIT",
88 | "dependencies": {
89 | "JSONStream": "^1.3.5",
90 | "csv-parse": "^4.11.1",
91 | "csv-stringify": "^5.5.0",
92 | "js-yaml": "^3.14.0",
93 | "node-xml-stream": "^1.0.2",
94 | "xml-js": "^1.6.11"
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/Parser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Parser - Receives any strategy and safely implement it
3 | *
4 | * @constructor
5 | * @param {Base} strategy - Any strategy implementing the Base's prototype
6 | */
7 | function Parser (strategy) {
8 | this.strategy = strategy
9 | }
10 |
11 | /**
12 | * Parser.prototype.parse - Exposes the parsing from string to any valid JS type with the strategy
13 | *
14 | * @param {string} data
15 | * @param {object} options
16 | */
17 | Parser.prototype.parse = function parse (data, options) {
18 | return this.strategy.parse(data, options)
19 | }
20 |
21 | /**
22 | * Parser.prototype.stringify - Exposes the stringify() method from any valid JS type to expected type with the strategy
23 | *
24 | * @param {*} data
25 | * @param {object} options
26 | */
27 | Parser.prototype.stringify = function stringify (data, options) {
28 | return this.strategy.stringify(data, options)
29 | }
30 |
31 | /**
32 | * Parser.prototype.valid - Exposes the valid() method from strategy. Checks if given data is valid
33 | *
34 | * @param {string} data
35 | * @param {object} options
36 | */
37 | Parser.prototype.valid = function stringify (data, options) {
38 | return this.strategy.valid(data, options)
39 | }
40 |
41 | /**
42 | * Parser.prototype.pipeStringify - Exposes the pipeStringify() method from strategy. Streams data through stringify
43 | */
44 | Parser.prototype.pipeStringify = function pipeStringify () {
45 | return this.strategy.pipeStringify()
46 | }
47 |
48 | /**
49 | * Parser.prototype.pipeParse - Exposes the pipeParse() method from strategy. Streams data through parse
50 | */
51 | Parser.prototype.pipeParse = function pipeParse () {
52 | return this.strategy.pipeParse()
53 | }
54 |
55 | Parser.prototype.get = function get (data, path) {}
56 |
57 | Parser.prototype.has = function has (data, path) {}
58 |
59 | module.exports = Parser
60 |
--------------------------------------------------------------------------------
/src/errors/NotImplemented.js:
--------------------------------------------------------------------------------
1 | /**
2 | * NotImplemented
3 | */
4 | function NotImplemented () {
5 | this.name = 'NotImplemented'
6 | this.message = 'This method haven\'t been implemented yet'
7 | }
8 |
9 | NotImplemented.prototype = new Error()
10 |
11 | module.exports = NotImplemented
12 |
--------------------------------------------------------------------------------
/src/errors/ParserError.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ParseError
3 | *
4 | * @param {string} format - which format the error ocurred
5 | * @param {object} context - any context info for debugging
6 | */
7 | function ParseError (format, context = {}) {
8 | this.name = 'ParseError'
9 | this.message = `Failed to parse ${format}`
10 | this.context = context
11 | }
12 |
13 | ParseError.prototype = new Error()
14 |
15 | module.exports = ParseError
16 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const {
2 | Json,
3 | Xml,
4 | Yaml,
5 | Csv
6 | } = require('./strategies')
7 | const Parser = require('./Parser')
8 |
9 | module.exports = {
10 | json: new Parser(new Json()),
11 | xml: new Parser(new Xml()),
12 | yaml: new Parser(new Yaml()),
13 | csv: new Parser(new Csv())
14 | }
15 |
--------------------------------------------------------------------------------
/src/strategies/Base.js:
--------------------------------------------------------------------------------
1 | const NotImplemented = require('../errors/NotImplemented')
2 | const ParserError = require('../errors/ParserError')
3 |
4 | /**
5 | * Base class for strategies around the Parser
6 | * Every function that haven't been implemented
7 | * will throw an NotImplementedError
8 | *
9 | * @constructor
10 | */
11 | function Base () {}
12 |
13 | /**
14 | * Base.prototype.stringify - receives * form of data and turns it into a string
15 | *
16 | * @param {*} data
17 | * @param {object} options
18 | * @throws {NotImplemented} This method must be implemented
19 | */
20 | Base.prototype.stringify = function stringify (data, options) {
21 | throw new NotImplemented()
22 | }
23 |
24 | /**
25 | * Base.prototype.parse - receives an string and translate it to valid JavaScript
26 | *
27 | * @param {string} data
28 | * @param {object} options
29 | * @throws {NotImplemented} This method must be implemented
30 | */
31 | Base.prototype.parse = function parse (data, options) {
32 | throw new NotImplemented()
33 | }
34 |
35 | /**
36 | * Base.prototype.pipe - prototype for streams
37 | *
38 | * @throws {NotImplemented} This method must be implemented
39 | */
40 | Base.prototype.pipe = function pipe () {
41 | throw new NotImplemented()
42 | }
43 |
44 | /**
45 | * Base.prototype.valid - checks if a given value is valid in desired format using the implemented method parse(), ignoring any exception
46 | *
47 | * @param {object} options - any option accepted for the implemented method parse()
48 | * @returns {boolean} wether or not the given data is a valid format
49 | */
50 | Base.prototype.valid = function valid (data, options = {}) {
51 | try {
52 | this.parse(data, options)
53 | return true
54 | } catch (error) {
55 | if (error instanceof ParserError) {
56 | return false
57 | }
58 |
59 | throw error
60 | }
61 | }
62 |
63 | /**
64 | * Base.prototype.pipeParse - prototype for streams
65 | *
66 | * @throws {NotImplemented} This method must be implemented
67 | */
68 | Base.prototype.pipeParse = function pipeParse () {
69 | throw new NotImplemented()
70 | }
71 |
72 | /**
73 | * Base.prototype.pipeStringify - prototype for streams
74 | *
75 | * @throws {NotImplemented} This method must be implemented
76 | */
77 | Base.prototype.pipeStringify = function pipeStringify () {
78 | throw new NotImplemented()
79 | }
80 |
81 | module.exports = Base
82 |
--------------------------------------------------------------------------------
/src/strategies/Csv.js:
--------------------------------------------------------------------------------
1 | const Base = require('./Base')
2 | const ParserError = require('../errors/ParserError')
3 | const csvParser = require('csv-parse/lib/sync')
4 | const csvStringify = require('csv-stringify/lib/sync')
5 | const csvParserStream = require('csv-parse')
6 | const csvStringifyStream = require('csv-stringify')
7 |
8 | /**
9 | * Csv - Support for CSV filetype
10 | *
11 | * @constructor
12 | */
13 | function Csv () {}
14 |
15 | Csv.prototype = Object.create(Base.prototype)
16 |
17 | /**
18 | * Csv.prototype.parse - receives an CSV string and returns valid JS
19 | *
20 | * @param {string} data
21 | * @param {object} [options]
22 | * @param {(boolean|array|function)} [options.headers] - If should parse first line as the headers, default is true
23 | * @param {(string|Buffer)} [options.delimiter] - Which delimiters to use when parsing, defaults to comma `,`
24 | * @param {number} [options.skipLines] - How many lines it should skip before parsing, defaults to 1
25 | * @param {number} [options.offset] - How many lines it should parse, defaults to -1
26 | * @returns {array}
27 | */
28 | Csv.prototype.parse = function parse (data, options = {}) {
29 | const config = {
30 | columns: true,
31 | skip_empty_lines: true,
32 | delimiter: options.delimiter || ',',
33 | from_line: options.skipLines || 1
34 | }
35 |
36 | if (Object.prototype.hasOwnProperty.apply(options, ['headers'])) {
37 | config.columns = options.headers
38 | }
39 |
40 | if (options.offset) {
41 | config.to_line = options.offset
42 | }
43 |
44 | try {
45 | return csvParser(data, config)
46 | } catch (e) {
47 | const context = {
48 | code: e.code,
49 | message: e.message,
50 | column: e.column,
51 | emptyLines: e.empty_lines,
52 | header: e.header,
53 | index: e.index,
54 | lines: e.lines,
55 | quoting: e.quoting,
56 | records: e.records
57 | }
58 |
59 | throw new ParserError('csv', context)
60 | }
61 | }
62 |
63 | /**
64 | * Csv.prototype.stringify - receives * valid JS data and returns it as CSV
65 | *
66 | * @param {array} data
67 | * @param {object} [options]
68 | * @param {boolean} [options.headers] - If should set first line as the headers, default is true
69 | * @param {(array|object)} [options.columns] - Custom column mapping, see examples for more
70 | * @returns {string}
71 | */
72 | Csv.prototype.stringify = function stringify (data, options = {}) {
73 | const config = {
74 | header: true
75 | }
76 |
77 | if (options.headers === false) {
78 | config.header = false
79 | }
80 |
81 | if (options.columns) {
82 | config.columns = options.columns
83 | }
84 |
85 | return csvStringify(data, config)
86 | }
87 |
88 | /**
89 | * Csv.prototype.pipeParse - allow streaming data
90 | *
91 | * @param {object} [options]
92 | * @param {(boolean|array|function)} [options.headers] - If should parse first line as the headers, default is true
93 | * @param {string} [options.delimiter] - Which delimiters to use when parsing, defaults to comma `,`
94 | */
95 | Csv.prototype.pipeParse = function pipeParse (options = {}) {
96 | const config = {
97 | delimiter: options.delimiter || ',',
98 | columns: Reflect.has(options, 'headers') ? options.headers : true
99 | }
100 |
101 | return csvParserStream(config)
102 | }
103 |
104 | /**
105 | * Csv.prototype.pipeStringify - stream
106 | *
107 | * @param {array} data
108 | * @param {object} [options]
109 | * @param {boolean} [options.headers] - If should set first line as the headers, default is true
110 | * @param {string} [options.delimiter] - Which delimiters to use when parsing, defaults to comma `,`
111 | * @param {(array|object)} [options.columns] - Custom column mapping, see examples for more
112 | */
113 | Csv.prototype.pipeStringify = function pipeStringify (options = {}) {
114 | const config = {
115 | delimiter: options.delimiter || ',',
116 | header: Reflect.has(options, 'headers') ? !!options.headers : true
117 | }
118 |
119 | if (Reflect.has(options, 'columns')) {
120 | config.columns = options.columns
121 | }
122 |
123 | return csvStringifyStream(config)
124 | }
125 |
126 | module.exports = Csv
127 |
--------------------------------------------------------------------------------
/src/strategies/Json.js:
--------------------------------------------------------------------------------
1 | const Base = require('./Base')
2 | const ParserError = require('../errors/ParserError')
3 | const JSONStream = require('JSONStream')
4 |
5 | /**
6 | * Json - Support for JSON filetype
7 | *
8 | * @constructor
9 | */
10 | function Json () {}
11 |
12 | Json.prototype = Object.create(Base.prototype)
13 |
14 | /**
15 | * Json.prototype.parse - receives an JSON string and returns valid JS
16 | *
17 | * @param {string} data
18 | * @throws {ParserError} In case the JSON string is not valid, ParserError will be thrown
19 | * @returns {*}
20 | */
21 | Json.prototype.parse = function parse (data) {
22 | try {
23 | return JSON.parse(data)
24 | } catch (e) {
25 | throw new ParserError('json')
26 | }
27 | }
28 |
29 | /**
30 | * Json.prototype.stringify - receives * valid JS data and returns it as JSON
31 | *
32 | * @param {*} data
33 | * @returns {string}
34 | */
35 | Json.prototype.stringify = function stringify (data) {
36 | return JSON.stringify(data)
37 | }
38 |
39 | /**
40 | * Json.prototype.pipeStringify - helps to stream object or array into JSON valid data
41 | *
42 | * @param {object} [config] - sets config for stream
43 | * @param {string} [config.type='array'] - which type of data you're streaming, defaults do array
44 | * @returns {WritableStream}
45 | */
46 | Json.prototype.pipeStringify = function pipeStringify (config = {}) {
47 | config.type = config.type || 'array'
48 | const streams = {
49 | object: JSONStream.stringifyObject,
50 | array: JSONStream.stringify
51 | }
52 |
53 | const fn = streams[config.type]
54 |
55 | if (!fn) {
56 | throw new ParserError(`Supplied type "${config.type}" is not allowed. Use either "array" or "object"`)
57 | }
58 |
59 | return fn()
60 | }
61 |
62 | /**
63 | * Json.prototype.pipeStringify - helps to stream JSON data to JS
64 | *
65 | * @param {object} [config] - sets config for stream
66 | * @param {string} [config.path] - select which data path to be parsed from JSON to JS
67 | * @returns {Stream}
68 | */
69 | Json.prototype.pipeParse = function pipeParse (config) {
70 | return JSONStream.parse()
71 | }
72 |
73 | module.exports = Json
74 |
--------------------------------------------------------------------------------
/src/strategies/Xml/XmlTag.js:
--------------------------------------------------------------------------------
1 | function XmlDeclaration (version, encoding) {
2 | this.name = 'declaration'
3 | this.version = version
4 | this.encoding = encoding
5 | }
6 |
7 | function XmlTag (name, value, attributes, tags) {
8 | this.name = name
9 | this.value = value
10 | this.attributes = attributes
11 | this.tags = tags
12 | }
13 |
14 | XmlTag.prototype.reset = function reset () {
15 | return new XmlTag(this.name, this.value, this.attributes, this.tags)
16 | }
17 |
18 | function XmlCharacterData (cdata) {
19 | this.name = 'cdata'
20 | this.cdata = cdata
21 | }
22 |
23 | module.exports = { XmlTag, XmlDeclaration, XmlCharacterData }
24 |
--------------------------------------------------------------------------------
/src/strategies/Xml/index.js:
--------------------------------------------------------------------------------
1 | const Base = require('../Base')
2 | const ParserError = require('../../errors/ParserError')
3 | const xml = require('xml-js')
4 | const NotImplemented = require('../../errors/NotImplemented')
5 | const { Transform } = require('stream')
6 | const StreamParser = require('node-xml-stream')
7 | const { XmlTag, XmlCharacterData, XmlDeclaration } = require('./XmlTag')
8 |
9 | /**
10 | * Xml - Support for XML filetype
11 | *
12 | * @constructor
13 | */
14 | function Xml () {
15 | this.XML_VERSION_TAG = {
16 | _declaration: {
17 | _attributes: {
18 | version: '1.0',
19 | encoding: 'utf-8'
20 | }
21 | }
22 | }
23 |
24 | this.XML_JS_KEYS = {
25 | declarationKey: '_declaration',
26 | instructionKey: '_instruction',
27 | attributesKey: '_attributes',
28 | textKey: '_text',
29 | cdataKey: '_cdata',
30 | doctypeKey: '_doctype',
31 | commentKey: '_comment',
32 | parentKey: '_parent',
33 | typeKey: '_type',
34 | nameKey: '_name',
35 | elementsKey: '_elements'
36 | }
37 | }
38 |
39 | Xml.prototype = Object.create(Base.prototype)
40 |
41 | /**
42 | * Xml.prototype.setXmlDeclaration - sets XML declaration tag on first position of array or object
43 | *
44 | * @param {(object|array)} data - input data
45 | * @returns {(object|array)}
46 | */
47 | Xml.prototype.setXmlDeclaration = function setXmlDeclaration (data) {
48 | if (Array.isArray(data)) {
49 | data = [this.XML_VERSION_TAG, ...data]
50 | } else {
51 | data = { ...this.XML_VERSION_TAG, ...data }
52 | }
53 |
54 | return data
55 | }
56 |
57 | /**
58 | * Xml.prototype.stringify - receives * valid JS data and returns it as XML
59 | *
60 | * @param {(object|array)} data
61 | * @param {Object} [options] - options for turning JS data into XML
62 | * @param {boolean} [options.ignoreDeclaration] - don't output XML version tag, default is true
63 | * @returns {string}
64 | */
65 | Xml.prototype.stringify = function stringify (data, options = {}) {
66 | const config = {
67 | compact: true,
68 | ignoreDeclaration: false
69 | }
70 |
71 | data = this.setXmlDeclaration(data)
72 |
73 | if (options.ignoreDeclaration) {
74 | config.ignoreDeclaration = true
75 | }
76 |
77 | return xml.js2xml(data, config)
78 | }
79 |
80 | /**
81 | * Xml.prototype.parse - receives an XML string and translate it to valid JavaScript
82 | *
83 | * @param {string} data
84 | * @param {object} [options]
85 | * @param {boolean} [options.showDeclaration] - force parsing XML declaration tag
86 | * @param {boolean} [options.verbose] - makes xml2js return non compact mode, defaults to false
87 | * @param {boolean} [options.experimentalXmlTag] - use experimental XmlTag prototype, default is false
88 | * @throws {NotImplemented} This method must be implemented
89 | */
90 | Xml.prototype.parse = function parse (data, options = {}) {
91 | try {
92 | const config = {
93 | compact: true,
94 | ignoreDeclaration: true,
95 | nativeType: true,
96 | nativeTypeAttributes: true
97 | }
98 |
99 | if (options.showDeclaration) {
100 | config.ignoreDeclaration = false
101 | }
102 |
103 | if (options.verbose) {
104 | config.compact = false
105 | }
106 |
107 | const result = xml.xml2js(data, config)
108 |
109 | if (options.experimentalXmlTag) {
110 | return this.toXmlTag(result)
111 | }
112 |
113 | return result
114 | } catch (error) {
115 | throw new ParserError(error.message)
116 | }
117 | }
118 |
119 | /**
120 | * Xml.prototype.toXmlTag - turns xml2js non-compact result into XmlTag and XmlResult
121 | *
122 | * @param {object} xml2jsResult
123 | * @throws {NotImplemented}
124 | */
125 | Xml.prototype.toXmlTag = function toXmlTag (xml2jsResult) {
126 | throw new NotImplemented()
127 | }
128 |
129 | /**
130 | * Xml.prototype.pipeParse - stream
131 | *
132 | * @param {object} [options]
133 | * @param {Number} [options.depth=0]
134 | */
135 | Xml.prototype.pipeParse = function pipeParse (options = {}) {
136 | options.depth = options.depth || 0
137 | const parser = new StreamParser()
138 |
139 | let index = 0
140 | let parsedTags = new Map()
141 | const toEmit = []
142 | const lastTag = {
143 | index: null,
144 | name: null,
145 | tagIndex: null
146 | }
147 |
148 | const getFirstTagName = map => {
149 | if (map.has(0) === false) {
150 | return null
151 | }
152 |
153 | const mapPosZero = map.get(0)
154 | const arrayMap = Array.from(mapPosZero)
155 |
156 | if (arrayMap.length === 0) {
157 | return null
158 | }
159 |
160 | const keyValue = arrayMap[0]
161 |
162 | if (keyValue.length === 0) {
163 | return null
164 | }
165 |
166 | return keyValue[0]
167 | }
168 |
169 | parser.on('opentag', (name, attrs) => {
170 | const inheritFrom = {
171 | index: null,
172 | name: null
173 | }
174 |
175 | if (index >= 1) {
176 | const beforeIndex = index - 1
177 | const beforeKey = [
178 | ...parsedTags
179 | .get(beforeIndex)
180 | .keys()
181 | ].reverse()[0]
182 | inheritFrom.index = beforeIndex
183 | inheritFrom.name = beforeKey
184 | }
185 |
186 | if (!parsedTags.has(index)) {
187 | parsedTags.set(index, new Map())
188 | }
189 |
190 | if (!parsedTags.get(index).has(name)) {
191 | parsedTags.get(index).set(name, [])
192 | }
193 |
194 | const tag = new XmlTag(name, null, attrs, [])
195 | tag.inheritFrom = inheritFrom
196 |
197 | lastTag.index = index
198 | lastTag.name = name
199 | lastTag.tagIndex = parsedTags.get(index).get(name).push(tag) - 1
200 | tag.inheritFrom.tagIndex = lastTag.tagIndex
201 | index = index + 1
202 | })
203 |
204 | parser.on('text', (text) => {
205 | parsedTags
206 | .get(lastTag.index)
207 | .get(lastTag.name)[lastTag.tagIndex]
208 | .value = text
209 |
210 | lastTag.index = null
211 | lastTag.name = null
212 | lastTag.tagIndex = null
213 | })
214 |
215 | parser.on('closetag', (name) => {
216 | index = index - 1
217 |
218 | if (index === options.depth) {
219 | /**
220 | * must reorganize data to a single object
221 | * them emit it
222 | */
223 | let entries = Array.from(parsedTags).reverse()
224 | entries = entries.map(
225 | ([intIndex, tagsMap]) => ({
226 | intIndex, tagsMap: Array.from(tagsMap).reverse()
227 | })
228 | )
229 | entries.pop()
230 | entries.forEach(entry => {
231 | const intIndex = entry.intIndex === 0 ? entry.intIndex : entry.intIndex - 1
232 | const indexedTags = parsedTags.get(intIndex)
233 |
234 | entry.tagsMap.forEach(tag => {
235 | const list = tag[1]
236 | list.forEach(tagToBePushed => {
237 | indexedTags
238 | .get(tagToBePushed.inheritFrom.name)[0]
239 | .tags
240 | .push(tagToBePushed.reset())
241 | })
242 | })
243 | })
244 |
245 | parsedTags
246 | .get(index)
247 | .get(name)
248 | .forEach(tag => toEmit.push(tag.reset()))
249 | }
250 |
251 | if (name === getFirstTagName(parsedTags)) {
252 | parsedTags = new Map()
253 | }
254 | })
255 |
256 | parser.on('cdata', cdata => {
257 | const CData = new XmlCharacterData(cdata)
258 | toEmit.push(CData)
259 | })
260 |
261 | parser.on('instruction', (name, attrs) => {
262 | const declaration = new XmlDeclaration(attrs.version, attrs.encoding)
263 | toEmit.push(declaration)
264 | })
265 |
266 | return new Transform({
267 | objectMode: true,
268 | transform (chunk, encoding, ack) {
269 | parser.write(chunk.toString())
270 |
271 | if (toEmit.length > 0) {
272 | this.push(toEmit.shift())
273 | }
274 |
275 | ack()
276 | }
277 | })
278 | }
279 |
280 | /**
281 | * Xml.prototype.pipeStringify - stream from JS data into XML
282 | *
283 | * @param {object} [options] - all options to stringify
284 | * @param {object} [options.mainTag] - the wrapping tag
285 | * @param {string} [options.mainTag.name] - the wrapping tag's name
286 | */
287 | Xml.prototype.pipeStringify = function pipeStringify (options = {}) {
288 | options.mainTag = options.mainTag || {}
289 | const defaultContent = 'FAKE_CONTENT'
290 | const name = options.mainTag.name
291 | const contents = options.mainTag.text || defaultContent
292 | const tag = { [name || 'fake']: contents }
293 | const stringified = this.stringify(tag)
294 |
295 | const lastIndexOfArrow = stringified.lastIndexOf('<')
296 | let initialTag = stringified.substr(0, lastIndexOfArrow)
297 |
298 | if (initialTag.indexOf(defaultContent) !== -1) {
299 | initialTag.replace(defaultContent, '')
300 | }
301 |
302 | let endingTag = stringified.substr(
303 | lastIndexOfArrow,
304 | stringified.length
305 | )
306 |
307 | if (Reflect.has(options.mainTag, 'name') === false) {
308 | const firstArrowIndex = initialTag.indexOf('>') + 1
309 | initialTag = initialTag.substr(0, firstArrowIndex)
310 | endingTag = ''
311 | }
312 |
313 | const xml = this
314 | let isFirstData = true
315 |
316 | return new Transform({
317 | objectMode: true,
318 | transform (chunk, encoding, ack) {
319 | const options = {
320 | ignoreDeclaration: true
321 | }
322 |
323 | if (isFirstData) {
324 | this.push(initialTag)
325 | isFirstData = false
326 | }
327 |
328 | const toBePushed = xml.stringify(chunk, options)
329 | this.push(toBePushed)
330 |
331 | ack()
332 | },
333 | flush (cb) {
334 | this.push(endingTag)
335 | cb()
336 | }
337 | })
338 | }
339 |
340 | module.exports = Xml
341 |
--------------------------------------------------------------------------------
/src/strategies/Yaml.js:
--------------------------------------------------------------------------------
1 | const Base = require('./Base')
2 | const ParserError = require('../errors/ParserError')
3 | const yaml = require('js-yaml')
4 |
5 | /**
6 | * Yaml - Support for YAML filetype
7 | *
8 | * @constructor
9 | */
10 | function Yaml () {
11 | }
12 |
13 | Yaml.prototype = Object.create(Base.prototype)
14 |
15 | /**
16 | * Yaml.prototype.stringify - receives * valid JS data and returns it as YAML
17 | *
18 | * @param {object} data
19 | * @param {Object} options - options for turning JS data into YAML
20 | * @throws {ParserError} For invalid data type
21 | * @returns {string}
22 | */
23 | Yaml.prototype.stringify = function stringify (data, options = {}) {
24 | if (Array.isArray(data)) {
25 | throw new ParserError('Only plain objects are accepted for stringify()')
26 | }
27 |
28 | return yaml.safeDump(data)
29 | }
30 |
31 | /**
32 | * Yaml.prototype.parse - receives an YAML string and translate it to valid JavaScript
33 | *
34 | * @param {string} data
35 | * @param {object} options
36 | * @returns {object} Parsed YAML data as JS object
37 | */
38 | Yaml.prototype.parse = function parse (data, options = {}) {
39 | try {
40 | return yaml.safeLoad(data)
41 | } catch (e) {
42 | const context = {
43 | errorName: e.name,
44 | message: e.message,
45 | mark: e.mark
46 | }
47 |
48 | throw new ParserError('yaml', context)
49 | }
50 | }
51 |
52 | module.exports = Yaml
53 |
--------------------------------------------------------------------------------
/src/strategies/index.js:
--------------------------------------------------------------------------------
1 | const Json = require('./Json')
2 | const Xml = require('./Xml')
3 | const Yaml = require('./Yaml')
4 | const Csv = require('./Csv')
5 |
6 | module.exports = { Json, Xml, Yaml, Csv }
7 |
--------------------------------------------------------------------------------
/tests/Parser.test.js:
--------------------------------------------------------------------------------
1 | const Parser = require('../src/Parser')
2 |
3 | describe('Parser implements Strategy', () => {
4 | it('calls parse() strategy method', () => {
5 | const mock = { parse: jest.fn() }
6 | const parser = new Parser(mock)
7 | parser.parse()
8 | expect(mock.parse).toHaveBeenCalled()
9 | })
10 |
11 | it('calls stringify() strategy method', () => {
12 | const mock = { stringify: jest.fn() }
13 | const parser = new Parser(mock)
14 | parser.stringify()
15 | expect(mock.stringify).toHaveBeenCalled()
16 | })
17 |
18 | it('calls valid() strategy method', () => {
19 | const mock = { valid: jest.fn() }
20 | const parser = new Parser(mock)
21 | parser.valid()
22 | expect(mock.valid).toHaveBeenCalled()
23 | })
24 |
25 | it('calls pipeStringify() strategy method', () => {
26 | const mock = { pipeStringify: jest.fn() }
27 | const parser = new Parser(mock)
28 | parser.pipeStringify()
29 | expect(mock.pipeStringify).toHaveBeenCalled()
30 | })
31 |
32 | it('calls pipeParse() strategy method', () => {
33 | const mock = { pipeParse: jest.fn() }
34 | const parser = new Parser(mock)
35 | parser.pipeParse()
36 | expect(mock.pipeParse).toHaveBeenCalled()
37 | })
38 | })
39 |
--------------------------------------------------------------------------------
/tests/data/services.json:
--------------------------------------------------------------------------------
1 | {
2 | "services": [
3 | {
4 | "url": "netflix.com"
5 | }
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/tests/strategies/Base.test.js:
--------------------------------------------------------------------------------
1 | const Base = require('../../src/strategies/Base')
2 | const NotImplemented = require('../../src/errors/NotImplemented')
3 |
4 | function Implementation () {
5 | Base.call(this)
6 | }
7 |
8 | Implementation.prototype = Object.create(Base.prototype)
9 |
10 | const instance = new Implementation()
11 |
12 | describe('Base Strategy implementation', function () {
13 | it('throws NotImplemented for stringify() method', () => {
14 | expect(instance.stringify).toThrow(NotImplemented)
15 | })
16 |
17 | it('throws NotImplemented for parse() method', () => {
18 | expect(instance.parse).toThrow(NotImplemented)
19 | })
20 |
21 | it('throws NotImplemented for pipe() method', () => {
22 | expect(instance.pipe).toThrow(NotImplemented)
23 | })
24 |
25 | it('throws NotImplemented for pipeParse() method', () => {
26 | expect(instance.pipeParse).toThrow(NotImplemented)
27 | })
28 |
29 | it('throws NotImplemented for pipeStringify() method', () => {
30 | expect(instance.pipeStringify).toThrow(NotImplemented)
31 | })
32 |
33 | /*
34 | it('throws NotImplemented for valid() method, because parse() is not implemented', () => {
35 | expect(instance.valid).toThrow(NotImplemented)
36 | })
37 | */
38 | })
39 |
--------------------------------------------------------------------------------
/tests/strategies/Csv.test.js:
--------------------------------------------------------------------------------
1 | const Csv = require('../../src/strategies/Csv')
2 | const strategy = new Csv()
3 | const { Readable } = require('stream')
4 |
5 | const input = [
6 | 'name,email',
7 | 'Netflix,contact@netflix.com',
8 | 'Prime Video,contact@primevideo.com'
9 | ]
10 |
11 | const getReader = (inputArray, options = {}) => new Readable({
12 | objectMode: !!options.objectMode,
13 | read () {
14 | const next = inputArray.shift()
15 | this.push(next || null)
16 | }
17 | })
18 |
19 | describe('Csv Strategy', () => {
20 | describe('Csv.prototype.parse()', function () {
21 | it('parse CSV string ignoring headers', () => {
22 | const input = 'name,email\nNetflix,contact@netflix.com'
23 | const result = strategy.parse(input, { headers: false })
24 | expect(result).toEqual([
25 | ['name', 'email'],
26 | ['Netflix', 'contact@netflix.com']
27 | ])
28 | })
29 |
30 | it('parse CSV string with headers', () => {
31 | const input = 'name,email\nNetflix,contact@netflix.com'
32 | const result = strategy.parse(input)
33 |
34 | expect(result).toEqual([
35 | { name: 'Netflix', email: 'contact@netflix.com' }
36 | ])
37 | })
38 |
39 | it('parse CSV string with custom delimiters', () => {
40 | const input = 'name;email\nNetflix;contact@netflix.com'
41 | const result = strategy.parse(input, { delimiter: ';' })
42 |
43 | expect(result).toEqual([
44 | { name: 'Netflix', email: 'contact@netflix.com' }
45 | ])
46 | })
47 |
48 | it('parse CSV string skipping lines', () => {
49 | const input = 'insights\nname,email\nNetflix,contact@netflix.com'
50 | const result = strategy.parse(input, { skipLines: 2 })
51 |
52 | expect(result).toEqual([
53 | { name: 'Netflix', email: 'contact@netflix.com' }
54 | ])
55 | })
56 |
57 | it('parse CSV string with offset', () => {
58 | const input = 'name,email\nNetflix,contact@netflix.com\nAmazon,contact@amazon.com'
59 | const result = strategy.parse(input, { offset: 2 })
60 |
61 | expect(result).toEqual([
62 | { name: 'Netflix', email: 'contact@netflix.com' }
63 | ])
64 | })
65 | })
66 |
67 | describe('Csv.prototype.stringify()', function () {
68 | it('turns array of objects into CSV string', () => {
69 | const input = [
70 | { name: 'Netflix', email: 'contact@netflix.com' }
71 | ]
72 | const result = strategy.stringify(input)
73 |
74 | expect(result).toEqual(expect.stringMatching('name,email\nNetflix,contact@netflix.com'))
75 | })
76 |
77 | it('turns array of objects into CSV string without header', () => {
78 | const input = [
79 | { name: 'Netflix', email: 'contact@netflix.com' }
80 | ]
81 | const result = strategy.stringify(input, { headers: false })
82 |
83 | expect(result).toEqual(expect.stringMatching('Netflix,contact@netflix.com'))
84 | })
85 |
86 | it('turns array of objects into CSV string with custom column names', () => {
87 | const input = [
88 | { name: 'Netflix', email: 'contact@netflix.com' }
89 | ]
90 | const columns = [
91 | { key: 'name', header: 'Platform' },
92 | { key: 'email', header: 'e-mail' }
93 | ]
94 |
95 | const result = strategy.stringify(input, { columns })
96 |
97 | expect(result).toEqual(expect.stringMatching('Platform,e-mail\nNetflix,contact@netflix.com'))
98 | })
99 | })
100 |
101 | describe('Csv.prototype.valid', function () {
102 | it('returns false for invalid input data', () => {
103 | const result = strategy.valid('name\nstardew,pokemon')
104 | expect(result).toBe(false)
105 | })
106 |
107 | it('returns true for valid input data', () => {
108 | const result = strategy.valid('name,email\nNetflix,contact@netflix.com')
109 | expect(result).toBe(true)
110 | })
111 | })
112 |
113 | describe('Csv.prototype.pipeParse', () => {
114 | it('parses with default options', () => {
115 | const reader = getReader(Array.from(input))
116 | const parsedData = []
117 | reader
118 | .pipe(strategy.pipeParse())
119 | .on('readable', data => {
120 | parsedData.push(data)
121 | })
122 | .on('error', console.log)
123 | .on('end', () => {
124 | expect(parsedData).toHaveLength(2)
125 |
126 | const netflix = {
127 | name: 'Netflix',
128 | email: 'contact@netflix.com'
129 | }
130 | expect(parsedData[0]).toMatchObject(netflix)
131 |
132 | const prime = {
133 | name: 'Prime Video',
134 | email: 'contact@primevideo.com'
135 | }
136 | expect(parsedData[1]).toMatchObject(prime)
137 | })
138 | })
139 |
140 | it('parses with custom options.delimiter', () => {
141 | const input = [
142 | 'name;email',
143 | 'Netflix;contact@netflix.com',
144 | 'Prime Video;contact@primevideo.com'
145 | ]
146 | const reader = getReader(Array.from(input))
147 | const parsedData = []
148 | reader
149 | .pipe(strategy.pipeParse({ delimiter: ';' }))
150 | .on('readable', data => {
151 | parsedData.push(data)
152 | })
153 | .on('error', console.log)
154 | .on('end', () => {
155 | expect(parsedData).toHaveLength(2)
156 |
157 | const netflix = {
158 | name: 'Netflix',
159 | email: 'contact@netflix.com'
160 | }
161 | expect(parsedData[0]).toMatchObject(netflix)
162 |
163 | const prime = {
164 | name: 'Prime Video',
165 | email: 'contact@primevideo.com'
166 | }
167 | expect(parsedData[1]).toMatchObject(prime)
168 | })
169 | })
170 | })
171 |
172 | describe('Csv.prototype.pipeStringify', () => {
173 | it('stringify with default options', () => {
174 | const input = [
175 | { name: 'Netflix', site: 'netflix.com' },
176 | { name: 'Prime Video', site: 'primevideo.com' }
177 | ]
178 | const reader = getReader(
179 | Array.from(input),
180 | { objectMode: true }
181 | )
182 |
183 | const parsedData = []
184 | reader
185 | .pipe(strategy.pipeStringify())
186 | .on('readable', function () {
187 | let row
188 |
189 | while (row = this.read()) { // eslint-disable-line
190 | parsedData.push(row.toString())
191 | }
192 | })
193 | .on('error', e => { throw e })
194 | .on('end', () => {
195 | const str = parsedData[0]
196 | expect(str).toEqual('name,site\nNetflix,netflix.com\nPrime Video,primevideo.com\n')
197 | })
198 | })
199 |
200 | it('stringify with custom delimiter', () => {
201 | const input = [
202 | { name: 'Netflix', site: 'netflix.com' },
203 | { name: 'Prime Video', site: 'primevideo.com' }
204 | ]
205 | const reader = getReader(
206 | Array.from(input),
207 | { objectMode: true }
208 | )
209 |
210 | const parsedData = []
211 | reader
212 | .pipe(strategy.pipeStringify({ delimiter: ';' }))
213 | .on('readable', function () {
214 | let row
215 |
216 | while (row = this.read()) { // eslint-disable-line
217 | parsedData.push(row.toString())
218 | }
219 | })
220 | .on('error', e => { throw e })
221 | .on('end', () => {
222 | const str = parsedData[0]
223 | expect(str).toEqual('name;site\nNetflix;netflix.com\nPrime Video;primevideo.com\n')
224 | })
225 | })
226 |
227 | it('stringify with custom column', () => {
228 | const input = [
229 | { name: 'Netflix', site: 'netflix.com' },
230 | { name: 'Prime Video', site: 'primevideo.com' }
231 | ]
232 | const reader = getReader(
233 | Array.from(input),
234 | { objectMode: true }
235 | )
236 |
237 | const config = {
238 | columns: [
239 | { key: 'name', header: 'Name' },
240 | { key: 'site', header: 'Website URL' }
241 | ]
242 | }
243 |
244 | const parsedData = []
245 | reader
246 | .pipe(strategy.pipeStringify(config))
247 | .on('readable', function () {
248 | let row
249 |
250 | while (row = this.read()) { // eslint-disable-line
251 | parsedData.push(row.toString())
252 | }
253 | })
254 | .on('error', e => { throw e })
255 | .on('end', () => {
256 | const str = parsedData[0]
257 | expect(str).toEqual('Name,Website URL\nNetflix,netflix.com\nPrime Video,primevideo.com\n')
258 | })
259 | })
260 |
261 | it('stringify reordering columns', () => {
262 | const input = [
263 | { name: 'Netflix', site: 'netflix.com' },
264 | { name: 'Prime Video', site: 'primevideo.com' }
265 | ]
266 | const reader = getReader(
267 | Array.from(input),
268 | { objectMode: true }
269 | )
270 |
271 | const config = {
272 | columns: [
273 | { key: 'site' },
274 | { key: 'name' }
275 | ]
276 | }
277 |
278 | const parsedData = []
279 | reader
280 | .pipe(strategy.pipeStringify(config))
281 | .on('readable', function () {
282 | let row
283 |
284 | while (row = this.read()) { // eslint-disable-line
285 | parsedData.push(row.toString())
286 | }
287 | })
288 | .on('error', e => { throw e })
289 | .on('end', () => {
290 | const str = parsedData[0]
291 | expect(str).toEqual('site,name\nnetflix.com,Netflix\nprimevideo.com,Prime Video\n')
292 | })
293 | })
294 | })
295 | })
296 |
--------------------------------------------------------------------------------
/tests/strategies/Json.test.js:
--------------------------------------------------------------------------------
1 | const Json = require('../../src/strategies/Json')
2 | const ParserError = require('../../src/errors/ParserError')
3 | const NotImplemented = require('../../src/errors/NotImplemented')
4 | const Stream = require('stream')
5 | const strategy = new Json()
6 | const fs = require('fs')
7 | const path = require('path')
8 |
9 | const TEST_FILE = path.resolve(__dirname, '../data/services.json')
10 |
11 | describe('Json Strategy', function () {
12 | describe('Json.prototype.parse', function () {
13 | it('parses JSON object string to JS object properly', function () {
14 | const str = '{}'
15 | expect(strategy.parse(str)).toEqual({})
16 | })
17 |
18 | it('throws ParserError for invalid JSON string', function () {
19 | try {
20 | const str = '}'
21 | strategy.parse(str)
22 | } catch (e) {
23 | expect(e).toBeInstanceOf(ParserError)
24 | }
25 | })
26 | })
27 |
28 | describe('Json.prototype.stringify', function () {
29 | it('transforms JS object into JSON string', () => {
30 | const data = { name: 'Hernandes', package: 'parser' }
31 | expect(strategy.stringify(data)).toBe('{"name":"Hernandes","package":"parser"}')
32 | })
33 | })
34 |
35 | describe('Json.prototype.pipe', function () {
36 | it('throws NotImplemented error for pipe()', () => {
37 | expect(strategy.pipe).toThrow(NotImplemented)
38 | })
39 | })
40 |
41 | describe('Json.prototype.valid', function () {
42 | it('returns false for invalid input data', () => {
43 | const result = strategy.valid('}')
44 | expect(result).toBe(false)
45 | })
46 |
47 | it('returns true for valid array as input data', () => {
48 | const result = strategy.valid('[]')
49 | expect(result).toBe(true)
50 | })
51 |
52 | it('returns true for valid object as input data', () => {
53 | const result = strategy.valid('{}')
54 | expect(result).toBe(true)
55 | })
56 | })
57 |
58 | describe('Json.prototype.pipeStringify', () => {
59 | it('stringifies an array of objects', () => {
60 | const input = [{ game: 'Killing Floor' }, { game: 'Stardew Valley' }]
61 |
62 | const reader = new Stream.Readable({
63 | objectMode: true,
64 | read (size) {
65 | const next = input.shift()
66 |
67 | if (!next) {
68 | this.push(null)
69 | } else {
70 | this.push(next)
71 | }
72 | }
73 | })
74 |
75 | const result = []
76 | const writer = strategy.pipeStringify()
77 | reader.pipe(writer)
78 |
79 | writer.on('data', (data) => {
80 | result.push(data)
81 | })
82 |
83 | writer.on('error', console.log)
84 | writer.on('end', () => {
85 | const jsonString = result.join('')
86 | const parsed = JSON.parse(jsonString)
87 | expect(parsed).toEqual(expect.arrayContaining(input))
88 | })
89 | })
90 |
91 | it('stringifies an object', () => {
92 | const input = {
93 | services: [
94 | { url: 'cloud.google.com' }
95 | ]
96 | }
97 | const entries = Object.entries(input)
98 |
99 | const reader = new Stream.Readable({
100 | objectMode: true,
101 | read (size) {
102 | const next = entries.shift()
103 |
104 | if (!next) {
105 | this.push(null)
106 | } else {
107 | this.push(next)
108 | }
109 | }
110 | })
111 |
112 | const result = []
113 | const writer = strategy.pipeStringify({ type: 'object' })
114 | reader.pipe(writer)
115 |
116 | writer.on('data', (data) => {
117 | result.push(data)
118 | })
119 |
120 | writer.on('error', console.log)
121 | writer.on('end', () => {
122 | const jsonString = result.join('')
123 | const parsed = JSON.parse(jsonString)
124 | expect(parsed).toMatchObject(input)
125 | })
126 | })
127 | })
128 |
129 | describe('Json.prototype.pipeParse', () => {
130 | it('parses an object', () => {
131 | const reader = fs.createReadStream(TEST_FILE)
132 |
133 | const result = []
134 | const writer = strategy.pipeParse()
135 | reader.pipe(writer)
136 |
137 | writer.on('data', (data) => {
138 | result.push(data)
139 | })
140 |
141 | writer.on('error', console.log)
142 |
143 | writer.on('end', () => {
144 | expect(result).toHaveLength(1)
145 | expect(result[0]).toMatchObject({
146 | services: [{ url: 'netflix.com' }]
147 | })
148 | })
149 | })
150 | })
151 | })
152 |
--------------------------------------------------------------------------------
/tests/strategies/Xml.test.js:
--------------------------------------------------------------------------------
1 | const Xml = require('../../src/strategies/Xml')
2 | const ParserError = require('../../src/errors/ParserError')
3 | const NotImplemented = require('../../src/errors/NotImplemented')
4 | const strategy = new Xml()
5 | const { Readable } = require('stream')
6 | const { XmlTag, XmlDeclaration } = require('../../src/strategies/Xml/XmlTag')
7 |
8 | const input = `
9 |
10 |
11 | Naruto Shippuden Storm 3
12 |
13 | platform
14 |
15 | This is another tag
16 |
17 |
18 | Third tag another
19 |
20 |
21 |
22 | Netflix
23 |
24 | Possible description here
25 |
26 |
27 |
28 | `.split('')
29 |
30 | const getReader = (inputArray, options = {}) => new Readable({
31 | objectMode: !!options.objectMode,
32 | read () {
33 | const next = inputArray.shift()
34 | if (next) {
35 | this.push(next)
36 | } else {
37 | this.push(null)
38 | }
39 | }
40 | })
41 |
42 | describe('Xml Strategy', function () {
43 | describe('Xml.prototype.setXmlDeclaration()', function () {
44 | it('puts XML declaration on first position within array', () => {
45 | const data = [{ language: 'nodejs' }]
46 | const result = strategy.setXmlDeclaration(data)
47 | expect(result[0]).toEqual(strategy.XML_VERSION_TAG)
48 | })
49 |
50 | it('puts XML declaration on first position within object', () => {
51 | const data = { language: 'nodejs' }
52 | const result = strategy.setXmlDeclaration(data)
53 | const keys = Object.keys(result)
54 | expect(keys[0]).toEqual('_declaration')
55 | })
56 | })
57 |
58 | describe('Xml.prototype.stringify()', function () {
59 | it('transforms JS object into Xml string', () => {
60 | const data = { game: 'Stardew Valley' }
61 | const expected = 'Stardew Valley '
62 | expect(strategy.stringify(data)).toBe(expected)
63 | })
64 |
65 | it('transforms JS object into XML string without xml version', () => {
66 | const data = { game: 'Stardew Valley' }
67 | const expected = 'Stardew Valley '
68 | const result = strategy.stringify(data, { ignoreDeclaration: true })
69 | expect(result).toBe(expected)
70 | })
71 |
72 | it('transforms JS array into XML string', () => {
73 | const data = {
74 | packages: [
75 | { name: 'lodash' }
76 | ]
77 | }
78 | const expected = 'lodash '
79 | const result = strategy.stringify(data)
80 | expect(result).toBe(expected)
81 | })
82 | })
83 |
84 | describe('Xml.prototype.parse()', function () {
85 | it('parses XML string to JS object in verbose mode', function () {
86 | const data = 'Naruto Shippuden Storm 3 playstation '
87 | const expected = {
88 | elements: [
89 | {
90 | type: 'element',
91 | name: 'games',
92 | elements: [
93 | {
94 | type: 'element',
95 | name: 'name',
96 | elements: [
97 | {
98 | type: 'text',
99 | text: 'Naruto Shippuden Storm 3'
100 | }
101 | ]
102 | },
103 | {
104 | type: 'element',
105 | name: 'platform',
106 | elements: [
107 | {
108 | type: 'text',
109 | text: 'playstation'
110 | }
111 | ]
112 | }
113 | ]
114 | }
115 | ]
116 | }
117 | const result = strategy.parse(data, { verbose: true })
118 | expect(result).toStrictEqual(expected)
119 | })
120 |
121 | it('parses XML string to JS object', function () {
122 | const data = 'Naruto Shippuden Storm 3 playstation '
123 | const expected = {
124 | games: {
125 | name: { _text: 'Naruto Shippuden Storm 3' },
126 | platform: { _text: 'playstation' }
127 | }
128 | }
129 | const result = strategy.parse(data)
130 | expect(result).toStrictEqual(expected)
131 | })
132 |
133 | it('parses XML string to JS object array', function () {
134 | const data = 'mongoose sequelize '
135 | const expected = {
136 | packages: {
137 | name: [
138 | { _text: 'mongoose' },
139 | { _text: 'sequelize' }
140 | ]
141 | }
142 | }
143 | const result = strategy.parse(data)
144 | expect(result).toStrictEqual(expected)
145 | })
146 |
147 | it('throws ParserError for missing parent tag', function () {
148 | const data = 'mongoose sequelize '
149 | try {
150 | strategy.parse(data)
151 | } catch (error) {
152 | expect(error).toBeInstanceOf(ParserError)
153 | }
154 | })
155 |
156 | it('parses XML string, including _declaration', function () {
157 | const data = 'mongoose sequelize '
158 | const expected = {
159 | _declaration: {
160 | _attributes: {
161 | encoding: 'utf-8',
162 | version: 1
163 | }
164 | },
165 | packages: {
166 | name: [
167 | { _text: 'mongoose' },
168 | { _text: 'sequelize' }
169 | ]
170 | }
171 | }
172 | const result = strategy.parse(data, { showDeclaration: true })
173 | expect(result).toStrictEqual(expected)
174 | })
175 | })
176 |
177 | describe('Xml.prototype.pipe()', function () {
178 | it('throws NotImplemented error for pipe()', () => {
179 | expect(strategy.pipe).toThrow(NotImplemented)
180 | })
181 | })
182 |
183 | describe('Xml.prototype.valid()', function () {
184 | it('returns false for invalid input data', () => {
185 | const result = strategy.valid('phrase ')
186 | expect(result).toBe(false)
187 | })
188 |
189 | it('returns true for valid input data', () => {
190 | const result = strategy.valid('Stardew Valley ')
191 | expect(result).toBe(true)
192 | })
193 | })
194 |
195 | describe('Xml.prototype.pipeParse', () => {
196 | it('parses with default options.depth', () => {
197 | const reader = getReader(Array.from(input))
198 | const toExpect = {
199 | declaration: data => {
200 | expect(data).toBeInstanceOf(XmlDeclaration)
201 | expect(data.version).toEqual('1.0')
202 | expect(data.encoding).toEqual('utf-8')
203 | },
204 | games: data => {
205 | expect(data).toBeInstanceOf(XmlTag)
206 | expect(data.tags).toHaveLength(3)
207 | }
208 | }
209 | reader
210 | .pipe(strategy.pipeParse())
211 | .on('data', data => {
212 | toExpect[data.name](data)
213 | })
214 | .on('error', console.log)
215 | .on('end', () => {})
216 | })
217 |
218 | it('parses with custom options.depth 1', () => {
219 | const reader = getReader(Array.from(input))
220 | const toExpect = {
221 | declaration: data => {
222 | expect(data).toBeInstanceOf(XmlDeclaration)
223 | expect(data.version).toEqual('1.0')
224 | expect(data.encoding).toEqual('utf-8')
225 | },
226 | name: data => {
227 | expect(data).toBeInstanceOf(XmlTag)
228 | expect(data.tags).toHaveLength(0)
229 | },
230 | platform: data => {
231 | expect(data).toBeInstanceOf(XmlTag)
232 | expect(data.tags).toHaveLength(2)
233 | },
234 | site: data => {
235 | expect(data).toBeInstanceOf(XmlTag)
236 | expect(data.tags).toHaveLength(1)
237 | }
238 | }
239 | reader
240 | .pipe(strategy.pipeParse({ depth: 1 }))
241 | .on('data', data => {
242 | toExpect[data.name](data)
243 | })
244 | .on('error', console.log)
245 | .on('end', () => {})
246 | })
247 | })
248 |
249 | describe('Xml.prototype.pipeStringify', () => {
250 | it('stringifies an array of object', () => {
251 | const object = {
252 | games: 'none'
253 | }
254 |
255 | const contents = [object, object]
256 |
257 | const reader = getReader(contents, { objectMode: true })
258 |
259 | const expected = strategy.stringify(object) + strategy.stringify(object, { ignoreDeclaration: true })
260 | let parsed = ''
261 | reader
262 | .pipe(strategy.pipeStringify())
263 | .on('data', data => {
264 | parsed = parsed + data
265 | })
266 | .on('error', console.log)
267 | .on('end', () => {
268 | expect(parsed).toBe(expected)
269 | })
270 | })
271 |
272 | it('stringifies an array of object with custom parent', () => {
273 | const object = {
274 | games: 'none'
275 | }
276 |
277 | const contents = [object, object]
278 |
279 | const reader = getReader(contents, { objectMode: true })
280 |
281 | const expected = 'All my gamesnone none '
282 |
283 | const mainTag = {
284 | name: 'my-games',
285 | text: 'All my games'
286 | }
287 | let parsed = ''
288 | reader
289 | .pipe(strategy.pipeStringify({ mainTag }))
290 | .on('data', data => {
291 | parsed = parsed + data
292 | })
293 | .on('error', console.log)
294 | .on('end', () => {
295 | expect(parsed).toBe(expected)
296 | })
297 | })
298 | })
299 | })
300 |
--------------------------------------------------------------------------------
/tests/strategies/Yaml.test.js:
--------------------------------------------------------------------------------
1 | const Yaml = require('../../src/strategies/Yaml')
2 | const ParserError = require('../../src/errors/ParserError')
3 | const NotImplemented = require('../../src/errors/NotImplemented')
4 |
5 | const strategy = new Yaml()
6 |
7 | describe('Yaml Parser', () => {
8 | describe('Yaml.prototype.parse()', function () {
9 | it('parses YAML to JS object', () => {
10 | const data = 'series: Bleach\nseasons: 16'
11 | const result = strategy.parse(data)
12 | expect(result).toEqual({ series: 'Bleach', seasons: 16 })
13 | })
14 | })
15 |
16 | describe('Yaml.prototype.stringify()', function () {
17 | it('turns JS into YAML', () => {
18 | const data = { series: 'Bleach', seasons: 16 }
19 | const result = strategy.stringify(data)
20 | const expected = 'series: Bleach\nseasons: 16'
21 |
22 | expect(result).toEqual(expect.stringMatching(expected))
23 | })
24 |
25 | it('throws ParserError when calling stringify() with array data', () => {
26 | try {
27 | strategy.stringify([])
28 | } catch (error) {
29 | expect(error).toBeInstanceOf(ParserError)
30 | }
31 | })
32 | })
33 |
34 | describe('Yaml.prototype.pipe()', function () {
35 | it('throws NotImplemented error for pipe()', () => {
36 | expect(strategy.pipe).toThrow(NotImplemented)
37 | })
38 | })
39 |
40 | describe('Yaml.prototype.valid()', function () {
41 | it('returns false for invalid input data', () => {
42 | const result = strategy.valid('[name:\nStardew')
43 | expect(result).toBe(false)
44 | })
45 |
46 | it('returns true for valid input data', () => {
47 | const result = strategy.valid('name:"Stardew Valley"')
48 | expect(result).toBe(true)
49 | })
50 | })
51 | })
52 |
--------------------------------------------------------------------------------