@handlebars/parser 2.1.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/helpers.js CHANGED
@@ -1,4 +1,4 @@
1
- import Exception from './exception';
1
+ import Exception from './exception.js';
2
2
 
3
3
  function validateClose(open, close) {
4
4
  close = close.path ? close.path.original : close;
@@ -17,11 +17,11 @@ export function SourceLocation(source, locInfo) {
17
17
  this.source = source;
18
18
  this.start = {
19
19
  line: locInfo.first_line,
20
- column: locInfo.first_column
20
+ column: locInfo.first_column,
21
21
  };
22
22
  this.end = {
23
23
  line: locInfo.last_line,
24
- column: locInfo.last_column
24
+ column: locInfo.last_column,
25
25
  };
26
26
  }
27
27
 
@@ -36,7 +36,7 @@ export function id(token) {
36
36
  export function stripFlags(open, close) {
37
37
  return {
38
38
  open: open.charAt(2) === '~',
39
- close: close.charAt(close.length - 3) === '~'
39
+ close: close.charAt(close.length - 3) === '~',
40
40
  };
41
41
  }
42
42
 
@@ -61,11 +61,15 @@ export function preparePath(data, sexpr, parts, loc) {
61
61
  let depth = 0;
62
62
 
63
63
  for (let i = 0, l = parts.length; i < l; i++) {
64
- let part = parts[i].part,
65
- // If we have [] syntax then we do not treat path references as operators,
66
- // i.e. foo.[this] resolves to approximately context.foo['this']
67
- isLiteral = parts[i].original !== part;
68
- original += (parts[i].separator || '') + part;
64
+ let part = parts[i].part;
65
+ // If we have [] syntax then we do not treat path references as operators,
66
+ // i.e. foo.[this] resolves to approximately context.foo['this']
67
+ let isLiteral = parts[i].original !== part;
68
+ let separator = parts[i].separator;
69
+
70
+ let partPrefix = separator === '.#' ? '#' : '';
71
+
72
+ original += (separator || '') + part;
69
73
 
70
74
  if (!isLiteral && (part === '..' || part === '.' || part === 'this')) {
71
75
  if (tail.length > 0) {
@@ -74,7 +78,7 @@ export function preparePath(data, sexpr, parts, loc) {
74
78
  depth++;
75
79
  }
76
80
  } else {
77
- tail.push(part);
81
+ tail.push(`${partPrefix}${part}`);
78
82
  }
79
83
  }
80
84
 
@@ -82,13 +86,14 @@ export function preparePath(data, sexpr, parts, loc) {
82
86
 
83
87
  return {
84
88
  type: 'PathExpression',
89
+ this: original.startsWith('this.'),
85
90
  data,
86
91
  depth,
87
92
  head,
88
93
  tail,
89
- parts: [head, ...tail],
94
+ parts: head ? [head, ...tail] : tail,
90
95
  original,
91
- loc
96
+ loc,
92
97
  };
93
98
  }
94
99
 
@@ -105,7 +110,7 @@ export function prepareMustache(path, params, hash, open, strip, locInfo) {
105
110
  hash,
106
111
  escaped,
107
112
  strip,
108
- loc: this.locInfo(locInfo)
113
+ loc: this.locInfo(locInfo),
109
114
  };
110
115
  }
111
116
 
@@ -117,7 +122,7 @@ export function prepareRawBlock(openRawBlock, contents, close, locInfo) {
117
122
  type: 'Program',
118
123
  body: contents,
119
124
  strip: {},
120
- loc: locInfo
125
+ loc: locInfo,
121
126
  };
122
127
 
123
128
  return {
@@ -129,7 +134,7 @@ export function prepareRawBlock(openRawBlock, contents, close, locInfo) {
129
134
  openStrip: {},
130
135
  inverseStrip: {},
131
136
  closeStrip: {},
132
- loc: locInfo
137
+ loc: locInfo,
133
138
  };
134
139
  }
135
140
 
@@ -183,7 +188,7 @@ export function prepareBlock(
183
188
  openStrip: openBlock.strip,
184
189
  inverseStrip,
185
190
  closeStrip: close && close.strip,
186
- loc: this.locInfo(locInfo)
191
+ loc: this.locInfo(locInfo),
187
192
  };
188
193
  }
189
194
 
@@ -198,12 +203,12 @@ export function prepareProgram(statements, loc) {
198
203
  source: firstLoc.source,
199
204
  start: {
200
205
  line: firstLoc.start.line,
201
- column: firstLoc.start.column
206
+ column: firstLoc.start.column,
202
207
  },
203
208
  end: {
204
209
  line: lastLoc.end.line,
205
- column: lastLoc.end.column
206
- }
210
+ column: lastLoc.end.column,
211
+ },
207
212
  };
208
213
  }
209
214
  }
@@ -212,7 +217,7 @@ export function prepareProgram(statements, loc) {
212
217
  type: 'Program',
213
218
  body: statements,
214
219
  strip: {},
215
- loc: loc
220
+ loc: loc,
216
221
  };
217
222
  }
218
223
 
@@ -227,6 +232,6 @@ export function preparePartialBlock(open, program, close, locInfo) {
227
232
  program,
228
233
  openStrip: open.strip,
229
234
  closeStrip: close && close.strip,
230
- loc: this.locInfo(locInfo)
235
+ loc: this.locInfo(locInfo),
231
236
  };
232
237
  }
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
- export { default as Visitor } from './visitor';
2
- export { default as WhitespaceControl } from './whitespace-control';
3
- export { default as parser } from './parser';
4
- export { default as Exception } from './exception';
5
- export { print, PrintVisitor } from './printer';
6
- export { parse, parseWithoutProcessing } from './parse';
1
+ export { default as Visitor } from './visitor.js';
2
+ export { default as WhitespaceControl } from './whitespace-control.js';
3
+ export { default as parser } from './parser.js';
4
+ export { default as Exception } from './exception.js';
5
+ export { print, PrintVisitor } from './printer.js';
6
+ export { parse, parseWithoutProcessing } from './parse.js';
package/lib/parse.js CHANGED
@@ -1,6 +1,6 @@
1
- import parser from './parser';
2
- import WhitespaceControl from './whitespace-control';
3
- import * as Helpers from './helpers';
1
+ import parser from './parser.js';
2
+ import WhitespaceControl from './whitespace-control.js';
3
+ import * as Helpers from './helpers.js';
4
4
 
5
5
  let baseHelpers = {};
6
6
 
@@ -19,13 +19,50 @@ export function parseWithoutProcessing(input, options) {
19
19
  parser.yy = baseHelpers;
20
20
 
21
21
  // Altering the shared object here, but this is ok as parser is a sync operation
22
- parser.yy.locInfo = function(locInfo) {
22
+ parser.yy.locInfo = function (locInfo) {
23
23
  return new Helpers.SourceLocation(options && options.srcName, locInfo);
24
24
  };
25
25
 
26
- let ast = parser.parse(input);
26
+ let squareSyntax;
27
27
 
28
- return ast;
28
+ if (typeof options?.syntax?.square === 'function') {
29
+ squareSyntax = options.syntax.square;
30
+ } else if (options?.syntax?.square === 'node') {
31
+ squareSyntax = arrayLiteralNode;
32
+ } else {
33
+ squareSyntax = 'string';
34
+ }
35
+
36
+ let hashSyntax;
37
+
38
+ if (typeof options?.syntax?.hash === 'function') {
39
+ hashSyntax = options.syntax.hash;
40
+ } else {
41
+ hashSyntax = hashLiteralNode;
42
+ }
43
+
44
+ parser.yy.syntax = {
45
+ square: squareSyntax,
46
+ hash: hashSyntax,
47
+ };
48
+
49
+ return parser.parse(input);
50
+ }
51
+
52
+ function arrayLiteralNode(array, loc) {
53
+ return {
54
+ type: 'ArrayLiteral',
55
+ items: array,
56
+ loc,
57
+ };
58
+ }
59
+
60
+ function hashLiteralNode(hash, loc) {
61
+ return {
62
+ type: 'HashLiteral',
63
+ pairs: hash.pairs,
64
+ loc,
65
+ };
29
66
  }
30
67
 
31
68
  export function parse(input, options) {
package/lib/printer.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable new-cap */
2
- import Visitor from './visitor';
2
+ import Visitor from './visitor.js';
3
3
 
4
4
  export function print(ast) {
5
5
  return new PrintVisitor().accept(ast);
@@ -11,7 +11,7 @@ export function PrintVisitor() {
11
11
 
12
12
  PrintVisitor.prototype = new Visitor();
13
13
 
14
- PrintVisitor.prototype.pad = function(string) {
14
+ PrintVisitor.prototype.pad = function (string) {
15
15
  let out = '';
16
16
 
17
17
  for (let i = 0, l = this.padding; i < l; i++) {
@@ -22,7 +22,7 @@ PrintVisitor.prototype.pad = function(string) {
22
22
  return out;
23
23
  };
24
24
 
25
- PrintVisitor.prototype.Program = function(program) {
25
+ PrintVisitor.prototype.Program = function (program) {
26
26
  let out = '',
27
27
  body = program.body,
28
28
  i,
@@ -46,47 +46,50 @@ PrintVisitor.prototype.Program = function(program) {
46
46
  return out;
47
47
  };
48
48
 
49
- PrintVisitor.prototype.MustacheStatement = function(mustache) {
50
- return this.pad('{{ ' + this.SubExpression(mustache) + ' }}');
49
+ PrintVisitor.prototype.MustacheStatement = function (mustache) {
50
+ if (mustache.params.length > 0 || mustache.hash) {
51
+ return this.pad('{{ ' + this.callBody(mustache) + ' }}');
52
+ } else {
53
+ return this.pad('{{ ' + this.accept(mustache.path) + ' }}');
54
+ }
51
55
  };
52
- PrintVisitor.prototype.Decorator = function(mustache) {
53
- return this.pad('{{ DIRECTIVE ' + this.SubExpression(mustache) + ' }}');
56
+ PrintVisitor.prototype.Decorator = function (mustache) {
57
+ return this.pad('{{ DIRECTIVE ' + this.callBody(mustache) + ' }}');
54
58
  };
55
59
 
56
- PrintVisitor.prototype.BlockStatement = PrintVisitor.prototype.DecoratorBlock = function(
57
- block
58
- ) {
59
- let out = '';
60
+ PrintVisitor.prototype.BlockStatement = PrintVisitor.prototype.DecoratorBlock =
61
+ function (block) {
62
+ let out = '';
60
63
 
61
- out += this.pad(
62
- (block.type === 'DecoratorBlock' ? 'DIRECTIVE ' : '') + 'BLOCK:'
63
- );
64
- this.padding++;
65
- out += this.pad(this.SubExpression(block));
66
- if (block.program) {
67
- out += this.pad('PROGRAM:');
64
+ out += this.pad(
65
+ (block.type === 'DecoratorBlock' ? 'DIRECTIVE ' : '') + 'BLOCK:'
66
+ );
68
67
  this.padding++;
69
- out += this.accept(block.program);
70
- this.padding--;
71
- }
72
- if (block.inverse) {
68
+ out += this.pad(this.callBody(block));
73
69
  if (block.program) {
70
+ out += this.pad('PROGRAM:');
74
71
  this.padding++;
72
+ out += this.accept(block.program);
73
+ this.padding--;
75
74
  }
76
- out += this.pad('{{^}}');
77
- this.padding++;
78
- out += this.accept(block.inverse);
79
- this.padding--;
80
- if (block.program) {
75
+ if (block.inverse) {
76
+ if (block.program) {
77
+ this.padding++;
78
+ }
79
+ out += this.pad('{{^}}');
80
+ this.padding++;
81
+ out += this.accept(block.inverse);
81
82
  this.padding--;
83
+ if (block.program) {
84
+ this.padding--;
85
+ }
82
86
  }
83
- }
84
- this.padding--;
87
+ this.padding--;
85
88
 
86
- return out;
87
- };
89
+ return out;
90
+ };
88
91
 
89
- PrintVisitor.prototype.PartialStatement = function(partial) {
92
+ PrintVisitor.prototype.PartialStatement = function (partial) {
90
93
  let content = 'PARTIAL:' + partial.name.original;
91
94
  if (partial.params[0]) {
92
95
  content += ' ' + this.accept(partial.params[0]);
@@ -96,7 +99,7 @@ PrintVisitor.prototype.PartialStatement = function(partial) {
96
99
  }
97
100
  return this.pad('{{> ' + content + ' }}');
98
101
  };
99
- PrintVisitor.prototype.PartialBlockStatement = function(partial) {
102
+ PrintVisitor.prototype.PartialBlockStatement = function (partial) {
100
103
  let content = 'PARTIAL BLOCK:' + partial.name.original;
101
104
  if (partial.params[0]) {
102
105
  content += ' ' + this.accept(partial.params[0]);
@@ -113,16 +116,20 @@ PrintVisitor.prototype.PartialBlockStatement = function(partial) {
113
116
  return this.pad('{{> ' + content + ' }}');
114
117
  };
115
118
 
116
- PrintVisitor.prototype.ContentStatement = function(content) {
119
+ PrintVisitor.prototype.ContentStatement = function (content) {
117
120
  return this.pad("CONTENT[ '" + content.value + "' ]");
118
121
  };
119
122
 
120
- PrintVisitor.prototype.CommentStatement = function(comment) {
123
+ PrintVisitor.prototype.CommentStatement = function (comment) {
121
124
  return this.pad("{{! '" + comment.value + "' }}");
122
125
  };
123
126
 
124
- PrintVisitor.prototype.SubExpression = function(sexpr) {
125
- let params = sexpr.params,
127
+ PrintVisitor.prototype.SubExpression = function (sexpr) {
128
+ return `(${this.callBody(sexpr)})`;
129
+ };
130
+
131
+ PrintVisitor.prototype.callBody = function (callExpr) {
132
+ let params = callExpr.params,
126
133
  paramStrings = [],
127
134
  hash;
128
135
 
@@ -130,50 +137,75 @@ PrintVisitor.prototype.SubExpression = function(sexpr) {
130
137
  paramStrings.push(this.accept(params[i]));
131
138
  }
132
139
 
133
- params = '[' + paramStrings.join(', ') + ']';
140
+ params =
141
+ paramStrings.length === 0 ? '' : ' [' + paramStrings.join(', ') + ']';
134
142
 
135
- hash = sexpr.hash ? ' ' + this.accept(sexpr.hash) : '';
143
+ hash = callExpr.hash ? ' ' + this.accept(callExpr.hash) : '';
136
144
 
137
- return this.accept(sexpr.path) + ' ' + params + hash;
145
+ return this.accept(callExpr.path) + params + hash;
138
146
  };
139
147
 
140
- PrintVisitor.prototype.PathExpression = function(id) {
141
- let head = typeof id.head === 'string' ? id.head : `[${this.accept(id.head)}]`;
148
+ PrintVisitor.prototype.PathExpression = function (id) {
149
+ let head =
150
+ typeof id.head === 'string' ? id.head : `[${this.accept(id.head)}]`;
142
151
  let path = [head, ...id.tail].join('/');
143
- return (id.data ? '@' : '') + 'PATH:' + path;
152
+ return 'p%' + prefix(id) + path;
144
153
  };
145
154
 
146
- PrintVisitor.prototype.StringLiteral = function(string) {
155
+ function prefix(path) {
156
+ if (path.data) {
157
+ return '@';
158
+ } else if (path.this) {
159
+ return 'this.';
160
+ } else {
161
+ return '';
162
+ }
163
+ }
164
+
165
+ PrintVisitor.prototype.StringLiteral = function (string) {
147
166
  return '"' + string.value + '"';
148
167
  };
149
168
 
150
- PrintVisitor.prototype.NumberLiteral = function(number) {
151
- return 'NUMBER{' + number.value + '}';
169
+ PrintVisitor.prototype.NumberLiteral = function (number) {
170
+ return 'n%' + number.value;
152
171
  };
153
172
 
154
- PrintVisitor.prototype.BooleanLiteral = function(bool) {
155
- return 'BOOLEAN{' + bool.value + '}';
173
+ PrintVisitor.prototype.BooleanLiteral = function (bool) {
174
+ return 'b%' + bool.value;
156
175
  };
157
176
 
158
- PrintVisitor.prototype.UndefinedLiteral = function() {
177
+ PrintVisitor.prototype.UndefinedLiteral = function () {
159
178
  return 'UNDEFINED';
160
179
  };
161
180
 
162
- PrintVisitor.prototype.NullLiteral = function() {
181
+ PrintVisitor.prototype.NullLiteral = function () {
163
182
  return 'NULL';
164
183
  };
165
184
 
166
- PrintVisitor.prototype.Hash = function(hash) {
185
+ PrintVisitor.prototype.ArrayLiteral = function (array) {
186
+ return `Array[${array.items.map((item) => this.accept(item)).join(', ')}]`;
187
+ };
188
+
189
+ PrintVisitor.prototype.HashLiteral = function (hash) {
190
+ return `Hash{${this.hashPairs(hash)}}`;
191
+ };
192
+
193
+ PrintVisitor.prototype.Hash = function (hash) {
194
+ return `HASH{${this.hashPairs(hash)}}`;
195
+ };
196
+
197
+ PrintVisitor.prototype.hashPairs = function (hash) {
167
198
  let pairs = hash.pairs,
168
199
  joinedPairs = [];
169
200
 
170
201
  for (let i = 0, l = pairs.length; i < l; i++) {
171
- joinedPairs.push(this.accept(pairs[i]));
202
+ joinedPairs.push(this.HashPair(pairs[i]));
172
203
  }
173
204
 
174
- return 'HASH{' + joinedPairs.join(', ') + '}';
205
+ return joinedPairs.join(' ');
175
206
  };
176
- PrintVisitor.prototype.HashPair = function(pair) {
207
+
208
+ PrintVisitor.prototype.HashPair = function (pair) {
177
209
  return pair.key + '=' + this.accept(pair.value);
178
210
  };
179
211
  /* eslint-enable new-cap */
package/lib/visitor.js CHANGED
@@ -1,4 +1,4 @@
1
- import Exception from './exception';
1
+ import Exception from './exception.js';
2
2
 
3
3
  function Visitor() {
4
4
  this.parents = [];
@@ -9,7 +9,7 @@ Visitor.prototype = {
9
9
  mutating: false,
10
10
 
11
11
  // Visits a given value. If mutating, will replace the value if necessary.
12
- acceptKey: function(node, name) {
12
+ acceptKey: function (node, name) {
13
13
  let value = this.accept(node[name]);
14
14
  if (this.mutating) {
15
15
  // Hacky sanity check: This may have a few false positives for type for the helper
@@ -30,7 +30,7 @@ Visitor.prototype = {
30
30
 
31
31
  // Performs an accept operation with added sanity check to ensure
32
32
  // required keys are not removed.
33
- acceptRequired: function(node, name) {
33
+ acceptRequired: function (node, name) {
34
34
  this.acceptKey(node, name);
35
35
 
36
36
  if (!node[name]) {
@@ -38,9 +38,9 @@ Visitor.prototype = {
38
38
  }
39
39
  },
40
40
 
41
- // Traverses a given array. If mutating, empty respnses will be removed
41
+ // Traverses a given array. If mutating, empty responses will be removed
42
42
  // for child elements.
43
- acceptArray: function(array) {
43
+ acceptArray: function (array) {
44
44
  for (let i = 0, l = array.length; i < l; i++) {
45
45
  this.acceptKey(array, i);
46
46
 
@@ -52,7 +52,7 @@ Visitor.prototype = {
52
52
  }
53
53
  },
54
54
 
55
- accept: function(object) {
55
+ accept: function (object) {
56
56
  if (!object) {
57
57
  return;
58
58
  }
@@ -78,7 +78,7 @@ Visitor.prototype = {
78
78
  }
79
79
  },
80
80
 
81
- Program: function(program) {
81
+ Program: function (program) {
82
82
  this.acceptArray(program.body);
83
83
  },
84
84
 
@@ -89,31 +89,31 @@ Visitor.prototype = {
89
89
  DecoratorBlock: visitBlock,
90
90
 
91
91
  PartialStatement: visitPartial,
92
- PartialBlockStatement: function(partial) {
92
+ PartialBlockStatement: function (partial) {
93
93
  visitPartial.call(this, partial);
94
94
 
95
95
  this.acceptKey(partial, 'program');
96
96
  },
97
97
 
98
- ContentStatement: function(/* content */) {},
99
- CommentStatement: function(/* comment */) {},
98
+ ContentStatement: function (/* content */) {},
99
+ CommentStatement: function (/* comment */) {},
100
100
 
101
101
  SubExpression: visitSubExpression,
102
102
 
103
- PathExpression: function(/* path */) {},
103
+ PathExpression: function (/* path */) {},
104
104
 
105
- StringLiteral: function(/* string */) {},
106
- NumberLiteral: function(/* number */) {},
107
- BooleanLiteral: function(/* bool */) {},
108
- UndefinedLiteral: function(/* literal */) {},
109
- NullLiteral: function(/* literal */) {},
105
+ StringLiteral: function (/* string */) {},
106
+ NumberLiteral: function (/* number */) {},
107
+ BooleanLiteral: function (/* bool */) {},
108
+ UndefinedLiteral: function (/* literal */) {},
109
+ NullLiteral: function (/* literal */) {},
110
110
 
111
- Hash: function(hash) {
111
+ Hash: function (hash) {
112
112
  this.acceptArray(hash.pairs);
113
113
  },
114
- HashPair: function(pair) {
114
+ HashPair: function (pair) {
115
115
  this.acceptRequired(pair, 'value');
116
- }
116
+ },
117
117
  };
118
118
 
119
119
  function visitSubExpression(mustache) {