Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80713 views
1
import Visitor from "./visitor";
2
3
function WhitespaceControl() {
4
}
5
WhitespaceControl.prototype = new Visitor();
6
7
WhitespaceControl.prototype.Program = function(program) {
8
var isRoot = !this.isRootSeen;
9
this.isRootSeen = true;
10
11
var body = program.body;
12
for (var i = 0, l = body.length; i < l; i++) {
13
var current = body[i],
14
strip = this.accept(current);
15
16
if (!strip) {
17
continue;
18
}
19
20
var _isPrevWhitespace = isPrevWhitespace(body, i, isRoot),
21
_isNextWhitespace = isNextWhitespace(body, i, isRoot),
22
23
openStandalone = strip.openStandalone && _isPrevWhitespace,
24
closeStandalone = strip.closeStandalone && _isNextWhitespace,
25
inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
26
27
if (strip.close) {
28
omitRight(body, i, true);
29
}
30
if (strip.open) {
31
omitLeft(body, i, true);
32
}
33
34
if (inlineStandalone) {
35
omitRight(body, i);
36
37
if (omitLeft(body, i)) {
38
// If we are on a standalone node, save the indent info for partials
39
if (current.type === 'PartialStatement') {
40
// Pull out the whitespace from the final line
41
current.indent = (/([ \t]+$)/).exec(body[i-1].original)[1];
42
}
43
}
44
}
45
if (openStandalone) {
46
omitRight((current.program || current.inverse).body);
47
48
// Strip out the previous content node if it's whitespace only
49
omitLeft(body, i);
50
}
51
if (closeStandalone) {
52
// Always strip the next node
53
omitRight(body, i);
54
55
omitLeft((current.inverse || current.program).body);
56
}
57
}
58
59
return program;
60
};
61
WhitespaceControl.prototype.BlockStatement = function(block) {
62
this.accept(block.program);
63
this.accept(block.inverse);
64
65
// Find the inverse program that is involed with whitespace stripping.
66
var program = block.program || block.inverse,
67
inverse = block.program && block.inverse,
68
firstInverse = inverse,
69
lastInverse = inverse;
70
71
if (inverse && inverse.chained) {
72
firstInverse = inverse.body[0].program;
73
74
// Walk the inverse chain to find the last inverse that is actually in the chain.
75
while (lastInverse.chained) {
76
lastInverse = lastInverse.body[lastInverse.body.length-1].program;
77
}
78
}
79
80
var strip = {
81
open: block.openStrip.open,
82
close: block.closeStrip.close,
83
84
// Determine the standalone candiacy. Basically flag our content as being possibly standalone
85
// so our parent can determine if we actually are standalone
86
openStandalone: isNextWhitespace(program.body),
87
closeStandalone: isPrevWhitespace((firstInverse || program).body)
88
};
89
90
if (block.openStrip.close) {
91
omitRight(program.body, null, true);
92
}
93
94
if (inverse) {
95
var inverseStrip = block.inverseStrip;
96
97
if (inverseStrip.open) {
98
omitLeft(program.body, null, true);
99
}
100
101
if (inverseStrip.close) {
102
omitRight(firstInverse.body, null, true);
103
}
104
if (block.closeStrip.open) {
105
omitLeft(lastInverse.body, null, true);
106
}
107
108
// Find standalone else statments
109
if (isPrevWhitespace(program.body)
110
&& isNextWhitespace(firstInverse.body)) {
111
112
omitLeft(program.body);
113
omitRight(firstInverse.body);
114
}
115
} else {
116
if (block.closeStrip.open) {
117
omitLeft(program.body, null, true);
118
}
119
}
120
121
return strip;
122
};
123
124
WhitespaceControl.prototype.MustacheStatement = function(mustache) {
125
return mustache.strip;
126
};
127
128
WhitespaceControl.prototype.PartialStatement =
129
WhitespaceControl.prototype.CommentStatement = function(node) {
130
/* istanbul ignore next */
131
var strip = node.strip || {};
132
return {
133
inlineStandalone: true,
134
open: strip.open,
135
close: strip.close
136
};
137
};
138
139
140
function isPrevWhitespace(body, i, isRoot) {
141
if (i === undefined) {
142
i = body.length;
143
}
144
145
// Nodes that end with newlines are considered whitespace (but are special
146
// cased for strip operations)
147
var prev = body[i-1],
148
sibling = body[i-2];
149
if (!prev) {
150
return isRoot;
151
}
152
153
if (prev.type === 'ContentStatement') {
154
return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original);
155
}
156
}
157
function isNextWhitespace(body, i, isRoot) {
158
if (i === undefined) {
159
i = -1;
160
}
161
162
var next = body[i+1],
163
sibling = body[i+2];
164
if (!next) {
165
return isRoot;
166
}
167
168
if (next.type === 'ContentStatement') {
169
return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original);
170
}
171
}
172
173
// Marks the node to the right of the position as omitted.
174
// I.e. {{foo}}' ' will mark the ' ' node as omitted.
175
//
176
// If i is undefined, then the first child will be marked as such.
177
//
178
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
179
// content is met.
180
function omitRight(body, i, multiple) {
181
var current = body[i == null ? 0 : i + 1];
182
if (!current || current.type !== 'ContentStatement' || (!multiple && current.rightStripped)) {
183
return;
184
}
185
186
var original = current.value;
187
current.value = current.value.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), '');
188
current.rightStripped = current.value !== original;
189
}
190
191
// Marks the node to the left of the position as omitted.
192
// I.e. ' '{{foo}} will mark the ' ' node as omitted.
193
//
194
// If i is undefined then the last child will be marked as such.
195
//
196
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
197
// content is met.
198
function omitLeft(body, i, multiple) {
199
var current = body[i == null ? body.length - 1 : i - 1];
200
if (!current || current.type !== 'ContentStatement' || (!multiple && current.leftStripped)) {
201
return;
202
}
203
204
// We omit the last node if it's whitespace only and not preceeded by a non-content node.
205
var original = current.value;
206
current.value = current.value.replace(multiple ? (/\s+$/) : (/[ \t]+$/), '');
207
current.leftStripped = current.value !== original;
208
return current.leftStripped;
209
}
210
211
export default WhitespaceControl;
212
213