Skip to content

Commit 0de1c77

Browse files
authored
Merge pull request #157 from posthtml/textnode-ignore
Fix @-ignoring expressions
2 parents 7bc1660 + 48337fc commit 0de1c77

File tree

7 files changed

+90
-68
lines changed

7 files changed

+90
-68
lines changed

lib/backup.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ function makeLocalsBackup (keys, locals) {
3737
function revertBackupedLocals (keys, locals, backup) {
3838
for (let i = 0; i < keys.length; i++) {
3939
const key = keys[i]
40-
// remove key from locals
40+
// Remove key from locals
4141
delete locals[key]
4242

43-
// revert copied key value
43+
// Revert copied key value
4444
if (Object.prototype.hasOwnProperty.call(backup, key)) locals[key] = backup[key]
4545
}
4646

lib/index.js

Lines changed: 70 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -119,33 +119,35 @@ module.exports = function postHTMLExpressions (options) {
119119
removeScriptLocals: false
120120
}, options)
121121

122-
// set tags
122+
// Set tags
123123
loops = options.loopTags
124124
scopes = options.scopeTags
125125
conditionals = options.conditionalTags
126126
switches = options.switchTags
127127
ignored = options.ignoredTag
128128

129-
// make a RegExp's to search for placeholders
129+
// Define regex to search for placeholders
130130
let before = escapeRegexpString(options.delimiters[0])
131131
let after = escapeRegexpString(options.delimiters[1])
132132

133-
const delimitersRegexp = new RegExp(`(?<!@)${before}(.+?)${after}`, 'g')
133+
const delimitersRegexp = new RegExp(`(?<!@${options.delimiters[0][0]}?)${before}(.+?)${after}`, 'g')
134134

135135
before = escapeRegexpString(options.unescapeDelimiters[0])
136136
after = escapeRegexpString(options.unescapeDelimiters[1])
137137

138-
const unescapeDelimitersRegexp = new RegExp(`(?<!@)${before}(.+?)${after}`, 'g')
138+
const unescapeDelimitersRegexp = new RegExp(`(?<!@${options.unescapeDelimiters[0][0]}?)${before}(.+?)${after}`, 'g')
139139

140-
// make array of delimiters
140+
// Create an array of delimiters
141141
const delimiters = [
142142
{ text: options.delimiters, regexp: delimitersRegexp, escape: true },
143143
{ text: options.unescapeDelimiters, regexp: unescapeDelimitersRegexp, escape: false }
144144
]
145145

146-
// we arrange delimiter search order by length, since it's possible that one
147-
// delimiter could 'contain' another delimiter, like '{{' and '{{{'. But if
148-
// you sort by length, the longer one will always match first.
146+
/**
147+
* We arrange delimiter search order by length, since it's possible that one
148+
* delimiter could 'contain' another delimiter, like `{{{` contains `{{`.
149+
* But if we sort by length, the longer one will always match first.
150+
*/
149151
if (options.delimiters.join().length > options.unescapeDelimiters.join().length) {
150152
delimitersSettings[0] = delimiters[0]
151153
delimitersSettings[1] = delimiters[1]
@@ -157,7 +159,7 @@ module.exports = function postHTMLExpressions (options) {
157159
delimitersReplace = new RegExp(`@${escapeRegexpString(delimitersSettings[1].text[0])}`, 'g')
158160
unescapeDelimitersReplace = new RegExp(`@${escapeRegexpString(delimitersSettings[0].text[0])}`, 'g')
159161

160-
// kick off the parsing
162+
// Kick off the parsing
161163
return function (tree) {
162164
const { locals } = scriptDataLocals(tree, options)
163165

@@ -174,26 +176,28 @@ module.exports = function postHTMLExpressions (options) {
174176
}
175177

176178
function walk (opts, nodes) {
177-
// the context in which expressions are evaluated
179+
// The context in which expressions are evaluated
178180
const ctx = vm.createContext(opts.locals)
179181

180-
// After a conditional has been resolved, we remove the conditional elements
181-
// from the tree. This variable determines how many to skip afterwards.
182+
/**
183+
* After a conditional has been resolved, we remove the conditional elements
184+
* from the tree. This variable determines how many to skip afterwards.
185+
* */
182186
let skip
183187

184-
// loop through each node in the tree
188+
// Loop through each node in the tree
185189
return [].concat(nodes).reduce((m, node, i) => {
186-
// if we're skipping this node, return immediately
190+
// If we're skipping this node, return immediately
187191
if (skip) { skip--; return m }
188192

189-
// don't parse ignoredTag
193+
// Don't parse `ignoredTag` from options
190194
if (node.tag === ignored) {
191195
m.push(node)
192196

193197
return m
194198
}
195199

196-
// if we have a string, match and replace it
200+
// If we have a string, match and replace it
197201
if (typeof node === 'string') {
198202
node = placeholders(node, ctx, delimitersSettings, opts)
199203
node = node
@@ -205,7 +209,7 @@ function walk (opts, nodes) {
205209
return m
206210
}
207211

208-
// if not, we have an object, so we need to run the attributes and contents
212+
// If not, we have an object, so we need to run the attributes and contents
209213
if (node.attrs) {
210214
for (const key in node.attrs) {
211215
if (typeof node.attrs[key] === 'string') {
@@ -215,7 +219,7 @@ function walk (opts, nodes) {
215219
.replace(delimitersReplace, delimitersSettings[1].text[0])
216220
}
217221

218-
// if key is parametr
222+
// If `key` is a parameter
219223
const _key = placeholders(key, ctx, delimitersSettings, opts)
220224
if (key !== _key) {
221225
node.attrs[_key] = node.attrs[key]
@@ -224,27 +228,31 @@ function walk (opts, nodes) {
224228
}
225229
}
226230

227-
// if the node has content, recurse (unless it's a loop, handled later)
231+
// If the node has content, recurse (unless it's a loop, which we handle later)
228232
if (node.content && loops.includes(node.tag) === false && node.tag !== scopes[0]) {
229233
node.content = walk(opts, node.content)
230234
}
231235

232-
// if we have an element matching "if", we've got a conditional
233-
// this comes after the recursion to correctly handle nested loops
236+
/**
237+
* If we have an element matching `<if>`, we've got a conditional; this
238+
* comes after the recursion, to correctly handle nested loops.
239+
* */
234240
if (node.tag === conditionals[0]) {
235-
// throw an error if it's missing the "condition" attribute
241+
// Throw an error if it's missing the "condition" attribute
236242
if (!(node.attrs && node.attrs.condition)) {
237243
throw new Error(`the "${conditionals[0]}" tag must have a "condition" attribute`)
238244
}
239245

240-
// сalculate the first path of condition expression
246+
// Calculate the first path of condition expression
241247
let expressionIndex = 1
242248
let expression = `if (${node.attrs.condition}) { 0 } `
243249

244250
const branches = [node.content]
245251

246-
// move through the nodes and collect all others that are part of the same
247-
// conditional statement
252+
/**
253+
* Move through the nodes and collect all others that
254+
* are part of the same conditional statement
255+
* */
248256
let computedNextTag = getNextTag(nodes, ++i)
249257

250258
let current = computedNextTag[0]
@@ -254,24 +262,26 @@ function walk (opts, nodes) {
254262
let statement = nextTag.tag
255263
let condition = ''
256264

257-
// ensure the "else" tag is represented in our little AST as 'else',
258-
// even if a custom tag was used
265+
/**
266+
* Ensure the "else" tag is represented in our little AST as 'else',
267+
* even if a custom tag was used.
268+
* */
259269
if (nextTag.tag === conditionals[2]) statement = 'else'
260270

261-
// add the condition if it's an else if
271+
// Add the condition if it's an else if
262272
if (nextTag.tag === conditionals[1]) {
263-
// throw an error if an "else if" is missing a condition
273+
// Throw an error if an "else if" is missing a condition
264274
if (!(nextTag.attrs && nextTag.attrs.condition)) {
265275
throw new Error(`the "${conditionals[1]}" tag must have a "condition" attribute`)
266276
}
267277
condition = nextTag.attrs.condition
268278

269-
// while we're here, expand "elseif" to "else if"
279+
// While we're here, expand "elseif" to "else if"
270280
statement = 'else if'
271281
}
272282
branches.push(nextTag.content)
273283

274-
// calculate next part of condition expression
284+
// Calculate next part of condition expression
275285
expression += statement + (condition ? ` (${condition})` : '') + ` { ${expressionIndex++} } `
276286

277287
computedNextTag = getNextTag(nodes, ++current)
@@ -280,7 +290,7 @@ function walk (opts, nodes) {
280290
nextTag = computedNextTag[1]
281291
}
282292

283-
// evaluate the expression, get the winning condition branch
293+
// Evaluate the expression and get the winning condition branch
284294
let branch
285295
try {
286296
branch = branches[vm.runInContext(expression, ctx)]
@@ -290,25 +300,27 @@ function walk (opts, nodes) {
290300
}
291301
}
292302

293-
// remove all of the conditional tags from the tree
294-
// we subtract 1 from i as it's incremented from the initial if statement
295-
// in order to get the next node
303+
/**
304+
* Remove all of the conditional tags from the tree.
305+
* We subtract 1 from i as it's incremented from the initial if statement
306+
* in order to get the next node.
307+
* */
296308
skip = current - i
297309

298-
// recursive evaluate of condition branch
310+
// Recursive evaluate of condition branch
299311
if (branch) Array.prototype.push.apply(m, walk(opts, branch))
300312

301313
return m
302314
}
303315

304-
// switch tag
316+
// Switch tag
305317
if (node.tag === switches[0]) {
306-
// throw an error if it's missing the "expression" attribute
318+
// Throw an error if it's missing the "expression" attribute
307319
if (!(node.attrs && node.attrs.expression)) {
308320
throw new Error(`the "${switches[0]}" tag must have a "expression" attribute`)
309321
}
310322

311-
// сalculate the first path of condition expression
323+
// Calculate the first path of condition expression
312324
let expressionIndex = 0
313325
let expression = `switch(${node.attrs.expression}) {`
314326

@@ -321,7 +333,7 @@ function walk (opts, nodes) {
321333
}
322334

323335
if (currentNode.tag === switches[1]) {
324-
// throw an error if it's missing the "n" attribute
336+
// Throw an error if it's missing the "n" attribute
325337
if (!(currentNode.attrs && currentNode.attrs.n)) {
326338
throw new Error(`the "${switches[1]}" tag must have a "n" attribute`)
327339
}
@@ -336,23 +348,23 @@ function walk (opts, nodes) {
336348

337349
expression += '}'
338350

339-
// evaluate the expression, get the winning switch branch
351+
// Evaluate the expression, get the winning switch branch
340352
const branch = branches[vm.runInContext(expression, ctx)]
341353

342-
// recursive evaluate of branch
354+
// Recursive evaluate of branch
343355
Array.prototype.push.apply(m, walk(opts, branch.content))
344356

345357
return m
346358
}
347359

348-
// parse loops
360+
// Parse loops
349361
if (loops.includes(node.tag)) {
350-
// handle syntax error
362+
// Handle syntax error
351363
if (!(node.attrs && node.attrs.loop)) {
352364
throw new Error(`the "${node.tag}" tag must have a "loop" attribute`)
353365
}
354366

355-
// parse the "loop" param
367+
// Parse the "loop" param
356368
const loopParams = parseLoopStatement(node.attrs.loop)
357369
let target = {}
358370
try {
@@ -363,7 +375,7 @@ function walk (opts, nodes) {
363375
}
364376
}
365377

366-
// handle additional syntax errors
378+
// Handle additional syntax errors
367379
if (typeof target !== 'object' && opts.strictMode) {
368380
throw new Error('You must provide an array or object to loop through')
369381
}
@@ -372,14 +384,14 @@ function walk (opts, nodes) {
372384
throw new Error('You must provide at least one loop argument')
373385
}
374386

375-
// converts nodes to a string. These nodes will be changed within the loop
387+
// Converts nodes to a string. These nodes will be changed within the loop
376388
const treeString = JSON.stringify(node.content)
377389
const keys = loopParams.keys
378390

379-
// creates a copy of the keys that will be changed within the loop
391+
// Creates a copy of the keys that will be changed within the loop
380392
const localsBackup = makeLocalsBackup(keys, opts.locals)
381393

382-
// run the loop, different types of loops for arrays and objects
394+
// Run the loop, different types of loops for arrays and objects
383395
if (Array.isArray(target)) {
384396
for (let index = 0; index < target.length; index++) {
385397
opts.locals.loop = getLoopMeta(index, target)
@@ -392,42 +404,42 @@ function walk (opts, nodes) {
392404
}
393405
}
394406

395-
// returns the original keys values that was changed within the loop
407+
// Returns the original keys values that was changed within the loop
396408
opts.locals = revertBackupedLocals(keys, opts.locals, localsBackup)
397409

398-
// return directly out of the loop, which will skip the "each" tag
410+
// Return directly out of the loop, which will skip the "each" tag
399411
return m
400412
}
401413

402-
// parse scopes
414+
// Parse scopes
403415
if (node.tag === scopes[0]) {
404-
// handle syntax error
416+
// Handle syntax error
405417
if (!node.attrs || !node.attrs.with) {
406418
throw new Error(`the "${scopes[0]}" tag must have a "with" attribute`)
407419
}
408420

409421
const target = vm.runInContext(node.attrs.with, ctx)
410422

411-
// handle additional syntax errors
423+
// Handle additional syntax errors
412424
if (typeof target !== 'object' || Array.isArray(target)) {
413425
throw new Error('You must provide an object to make scope')
414426
}
415427

416428
const keys = Object.keys(target)
417429

418-
// creates a copy of the keys that will be changed within the loop
430+
// Creates a copy of the keys that will be changed within the loop
419431
const localsBackup = makeLocalsBackup(keys, opts.locals)
420432

421433
m.push(executeScope(target, opts.locals, node))
422434

423-
// returns the original keys values that was changed within the loop
435+
// Returns the original keys values that was changed within the loop
424436
opts.locals = revertBackupedLocals(keys, opts.locals, localsBackup)
425437

426-
// return directly out of the loop, which will skip the "scope" tag
438+
// Return directly out of the loop, which will skip the "scope" tag
427439
return m
428440
}
429441

430-
// return the node
442+
// Return the node
431443
m.push(node)
432444

433445
return m

lib/loops.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010
* @return {Object} {} Keys && Expression
1111
*/
1212
function parseLoopStatement (input) {
13-
// try to find ` in ` keyword
13+
// Try to find ` in ` keyword
1414
const inKeywordIndex = input.search(/\sin\s/)
1515

16-
// if we reach the end of the string without getting "in", it's an error
16+
// If we reach the end of the string without getting "in", it's an error
1717
if (inKeywordIndex === -1) {
1818
throw new Error("Loop statement lacking 'in' keyword")
1919
}
2020

21-
// expression is always after `in` keyword
21+
// Expression is always after `in` keyword
2222
const expression = input.substr(inKeywordIndex + 4)
2323

2424
// keys is always before `in` keyword

lib/placeholders.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ function escapeHTML (unescaped) {
3737
* @return {String} input Replaced Input
3838
*/
3939
function placeholders (input, ctx, settings, opts) {
40-
// Since we are matching multiple sets of delimiters, we need to run a loop
41-
// here to match each one.
40+
/**
41+
* Since we are matching multiple sets of delimiters,
42+
* we need to run a loop here to match each one.
43+
*/
4244
for (let i = 0; i < settings.length; i++) {
4345
const matches = input.match(settings[i].regexp)
4446

lib/tags.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* @return {Array} [] Array containing the next tag
1212
*/
1313
function getNextTag (nodes, i) {
14-
// loop until we get the next tag (bypassing newlines etc)
14+
// Loop until we get the next tag (bypassing newlines etc)
1515
while (i < nodes.length) {
1616
const node = nodes[i]
1717

0 commit comments

Comments
 (0)