@trenskow/parse 0.1.0 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  {
8
8
  "type": "pwa-node",
9
9
  "request": "launch",
10
- "name": "Launch Program",
10
+ "name": "Run test",
11
11
  "skipFiles": [
12
12
  "<node_internals>/**"
13
13
  ],
package/README.md CHANGED
@@ -10,7 +10,9 @@ Below is an example on how to use the library.
10
10
  ````javascript
11
11
  import parse from '@trenskow/parse';
12
12
 
13
- parse('${', '}').do('This ${is ${my ${nested ${string}}}}');
13
+ parse('${', '}', {
14
+ ignoreInside: ['"', '\'']
15
+ }).do('This ${\'is\' ${my ${"nested" ${string}}}}');
14
16
  ````
15
17
 
16
18
  The above example will return the following structure
@@ -31,6 +33,8 @@ The above example will return the following structure
31
33
  ]
32
34
  ````
33
35
 
36
+ > One caveat: opening and closing token cannot be the same.
37
+
34
38
  # License
35
39
 
36
40
  See license in LICENSE.
package/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Created 02/05/22 by Kristian Trenskow
2
2
  // See LICENSE for license.
3
3
 
4
- export default (opening, closing) => {
4
+ export default (opening, closing, options) => {
5
5
 
6
6
  if (typeof opening !== 'string' || typeof closing !== 'string') {
7
7
  throw new TypeError('Opening and closing tokens must be strings.');
@@ -15,38 +15,101 @@ export default (opening, closing) => {
15
15
  throw new TypeError('Opening and closing tokens cannot be the same.');
16
16
  }
17
17
 
18
+ if (typeof options === 'undefined') options = {};
19
+ if (typeof options !== 'object' || options === null) {
20
+ throw new TypeError('Options must be an object.');
21
+ }
22
+
23
+ let ignoreInside = options.ignoreInside;
24
+
25
+ if (typeof ignoreInside === 'undefined') ignoreInside = [];
26
+ if (!Array.isArray(ignoreInside)) ignoreInside = [ignoreInside];
27
+
28
+ if (ignoreInside.filter((ignore) => typeof ignore !== 'string').length) {
29
+ throw new TypeError('Ignore inside must be a string.');
30
+ }
31
+
32
+ let maxDepth = options.maxDepth;
33
+
34
+ if (typeof maxDepth === 'undefined') maxDepth = Infinity;
35
+
36
+ if (typeof maxDepth !== 'number') {
37
+ throw new TypeError('Max depth must be a number.');
38
+ }
39
+
40
+ if (maxDepth < 0) {
41
+ throw new TypeError('Max depth must be greater than zero.');
42
+ }
43
+
44
+ const ignoring = [];
45
+
18
46
  return {
19
47
  do: (text) => {
20
48
 
21
- const next = (text, offset = 0) => {
49
+ const next = (text, offset, depth) => {
22
50
 
23
51
  let result = [];
24
52
 
25
53
  let literal = '';
26
54
 
27
55
  let idx = offset;
56
+ let ignoredDepths = 0;
28
57
 
29
58
  for (idx ; idx < text.length ; idx++) {
30
- if (text[idx] === '\\') {
31
- literal += text[++idx];
32
- } else if (text.substring(idx, idx + opening.length) === opening) {
33
- result.push(literal);
34
- let value;
35
- [idx, value] = next(text, idx + opening.length);
36
- result.push(value);
37
- literal = '';
38
- } else if (text.substring(idx, idx + closing.length) === closing) {
39
-
40
- if (literal.length > 0) result.push(literal);
41
-
42
- if (result.length === 1 && typeof result[0] === 'string') {
43
- result = result[0];
59
+
60
+ if (text[idx] === '\\') literal += text[++idx];
61
+ else {
62
+
63
+ const matchedIgnore = ignoreInside
64
+ .map((ignore) => [ignore, text.substring(idx, idx + ignore.length)])
65
+ .filter(([ignore, matched]) => ignore === matched)
66
+ .map(([_, matched]) => matched)[0];
67
+
68
+ if (typeof matchedIgnore !== 'undefined') {
69
+ literal += matchedIgnore;
70
+ if (matchedIgnore === ignoring[ignoring.length - 1]) {
71
+ ignoring.pop();
72
+ } else {
73
+ ignoring.push(matchedIgnore);
74
+ }
44
75
  }
76
+ else if (ignoring.length === 0) {
77
+ if (text.substring(idx, idx + opening.length) === opening) {
78
+
79
+ if (maxDepth > depth) {
80
+ result.push(literal);
81
+ let value;
82
+ [idx, value] = next(text, idx + opening.length, depth + 1);
83
+ result.push(value);
84
+ literal = '';
85
+ } else {
86
+ literal += text[idx];
87
+ ignoredDepths++;
88
+ }
89
+
90
+ } else if (text.substring(idx, idx + closing.length) === closing) {
91
+
92
+ if (ignoredDepths === 0) {
45
93
 
46
- return [idx + closing.length - 1, result];
94
+ if (literal.length > 0) result.push(literal);
95
+
96
+ if (result.length === 1 && typeof result[0] === 'string') {
97
+ result = result[0];
98
+ }
99
+
100
+ return [idx + closing.length - 1, result];
101
+
102
+ } else {
103
+ literal += text[idx];
104
+ ignoredDepths--;
105
+ }
106
+
107
+ } else {
108
+ literal += text[idx];
109
+ }
110
+ }
111
+ else literal += text[idx];
47
112
 
48
- } else {
49
- literal += text[idx];
50
113
  }
51
114
  }
52
115
 
@@ -54,7 +117,7 @@ export default (opening, closing) => {
54
117
 
55
118
  };
56
119
 
57
- return next(text + closing)[1];
120
+ return next(text + closing, 0, 0)[1];
58
121
 
59
122
  }
60
123
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trenskow/parse",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "A small library for parsing a string into a tree.",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/test.js CHANGED
@@ -5,31 +5,29 @@ import { expect } from 'chai';
5
5
 
6
6
  import parse from './index.js';
7
7
 
8
- console.info(JSON.stringify(parse('[', ']').do('This [is [my [nested [string]]]]'), null, 4));
9
-
10
8
  describe('parser', () => {
11
- it ('should come back with parsed tree (with escapes).', () => {
12
- expect(parse('[', ']').do('This [is [my [\\[nested\\]] string]].')).to.eql([
9
+ it ('should come back with parsed tree.', () => {
10
+ expect(parse('{', '}').do('This {is {my {nested} string}}.')).to.eql([
13
11
  'This ',
14
12
  [
15
13
  'is ',
16
14
  [
17
15
  'my ',
18
- '[nested]',
16
+ 'nested',
19
17
  ' string'
20
18
  ]
21
19
  ],
22
20
  '.'
23
21
  ]);
24
22
  });
25
- it ('should come back with parsed tree.', () => {
26
- expect(parse('{', '}').do('This {is {my {nested} string}}.')).to.eql([
23
+ it ('should come back with parsed tree (with escapes).', () => {
24
+ expect(parse('[', ']').do('This [is [my [\\[nested\\]] string]].')).to.eql([
27
25
  'This ',
28
26
  [
29
27
  'is ',
30
28
  [
31
29
  'my ',
32
- 'nested',
30
+ '[nested]',
33
31
  ' string'
34
32
  ]
35
33
  ],
@@ -50,6 +48,27 @@ describe('parser', () => {
50
48
  ],
51
49
  '.']);
52
50
  });
51
+ it ('should come back with parsed tree (with ignore).', () => {
52
+ expect(parse('[', ']', { ignoreInside: ['"', '\''] }).do('This [is "my [\'nested\']" [string]]')).to.eql([
53
+ 'This ',
54
+ [
55
+ 'is "my [\'nested\']" ',
56
+ 'string'
57
+ ]
58
+ ]);
59
+ });
60
+ it ('should come back with parsed tree (max depth = 1).', () => {
61
+ expect(parse('[', ']', { maxDepth: 1 }).do('This [is [my nested string]].')).to.eql([
62
+ 'This ',
63
+ 'is [my nested string]',
64
+ '.'
65
+ ]);
66
+ });
67
+ it ('should come back with parsed tree (max depth = 0).', () => {
68
+ expect(parse('[', ']', { maxDepth: 0 }).do('This [is [my nested string]].')).to.eql(
69
+ 'This [is [my nested string]].'
70
+ );
71
+ });
53
72
  it ('should throw an error if closing token is missing.', () => {
54
73
  expect(() => {
55
74
  parse('[', ']').do('[this');
@@ -70,4 +89,24 @@ describe('parser', () => {
70
89
  parse('123', '123').do('[this');
71
90
  }).to.throw('Opening and closing tokens cannot be the same.');
72
91
  });
92
+ it ('should throw an error if options is not an object.', () => {
93
+ expect(() => {
94
+ parse('[', ']', '123');
95
+ }).to.throw('Options must be an object');
96
+ });
97
+ it ('should throw an error if ignoreInside is not a string.', () => {
98
+ expect(() => {
99
+ parse('[', ']', { ignoreInside: null });
100
+ }).to.throw('Ignore inside must be a string.');
101
+ });
102
+ it ('should throw an error if maxDepth is not a number.', () => {
103
+ expect(() => {
104
+ parse('[', ']', { maxDepth: true });
105
+ }).to.throw('Max depth must be a number.');
106
+ });
107
+ it ('should throw an error if max depth is less that zero.', () => {
108
+ expect(() => {
109
+ parse('[', ']', { maxDepth: -1 });
110
+ }).to.throw('Max depth must be greater than zero.');
111
+ });
73
112
  });