coffeehaml 0.7.6 → 0.7.7
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/dist/expressions.js +91 -19
- package/package.json +3 -3
- package/dist/coffeescript-formatter.js +0 -309
- package/dist/options.js +0 -232
- package/dist/printer.js +0 -583
package/dist/expressions.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by CoffeeScript 2.7.0
|
|
2
|
-
// expressions.coffee — CoffeeScript expression compilation bridge
|
|
3
|
-
var _coffeeScriptAvailable, _coffeeScriptUnavailableReason, _require, checkCoffeeScript, collapseWs, loadCoffeeScript, splitTopLevelStatements, stripCoffeeWrapper, stripToConst
|
|
2
|
+
// expressions.coffee — CoffeeScript expression compilation bridge
|
|
3
|
+
var _coffeeScriptAvailable, _coffeeScriptUnavailableReason, _require, checkCoffeeScript, collapseWs, loadCoffeeScript, objectTarget, splitTopLevelStatements, stripCoffeeWrapper, stripToConst,
|
|
4
|
+
indexOf = [].indexOf;
|
|
4
5
|
|
|
5
6
|
import {
|
|
6
7
|
createRequire
|
|
@@ -179,8 +180,21 @@ export var compileStatement = function(source, location = null) {
|
|
|
179
180
|
};
|
|
180
181
|
|
|
181
182
|
// ─── Convert var → const/let ───────────────────────────────
|
|
183
|
+
|
|
184
|
+
// Extract the bound variable name from one entry of an object
|
|
185
|
+
// destructuring pattern — shorthand `x` stays `x`, aliased `key: target`
|
|
186
|
+
// yields `target`.
|
|
187
|
+
objectTarget = function(pair) {
|
|
188
|
+
pair = pair.trim();
|
|
189
|
+
if (pair.includes(':')) {
|
|
190
|
+
return pair.split(':')[1].trim();
|
|
191
|
+
} else {
|
|
192
|
+
return pair;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
182
196
|
stripToConst = function(js) {
|
|
183
|
-
var
|
|
197
|
+
var allConsumed, assignments, body, consumed, inner, j, len, m, n, name, names, s, stmts, varIdx, varNames;
|
|
184
198
|
stmts = splitTopLevelStatements(js);
|
|
185
199
|
if (stmts.length === 0) {
|
|
186
200
|
return js.replace(/;\s*$/, '');
|
|
@@ -194,28 +208,86 @@ stripToConst = function(js) {
|
|
|
194
208
|
varNames = stmts[varIdx].replace(/^\s*var\s+/, '').replace(/;\s*$/, '').split(',').map(function(s) {
|
|
195
209
|
return s.trim();
|
|
196
210
|
}).filter(Boolean);
|
|
197
|
-
body =
|
|
198
|
-
|
|
199
|
-
|
|
211
|
+
body = (function() {
|
|
212
|
+
var j, len, ref, results;
|
|
213
|
+
ref = stmts.filter(function(s, i) {
|
|
214
|
+
return i !== varIdx;
|
|
215
|
+
});
|
|
216
|
+
results = [];
|
|
217
|
+
for (j = 0, len = ref.length; j < len; j++) {
|
|
218
|
+
s = ref[j];
|
|
219
|
+
results.push(collapseWs(s).replace(/;\s*$/, ''));
|
|
220
|
+
}
|
|
221
|
+
return results;
|
|
222
|
+
})();
|
|
200
223
|
assignments = [];
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
224
|
+
consumed = [];
|
|
225
|
+
for (j = 0, len = body.length; j < len; j++) {
|
|
226
|
+
s = body[j];
|
|
227
|
+
m = s.match(/^\[([^\]]*)\]\s*=/);
|
|
228
|
+
if (m != null) {
|
|
229
|
+
// Array destructuring: [a, b] = expr
|
|
230
|
+
names = (function() {
|
|
231
|
+
var k, len1, ref, results;
|
|
232
|
+
ref = m[1].split(',');
|
|
233
|
+
results = [];
|
|
234
|
+
for (k = 0, len1 = ref.length; k < len1; k++) {
|
|
235
|
+
n = ref[k];
|
|
236
|
+
if (n.trim()) {
|
|
237
|
+
results.push(n.trim());
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return results;
|
|
241
|
+
})();
|
|
242
|
+
consumed = consumed.concat(names);
|
|
243
|
+
assignments.push('const ' + s);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
m = s.match(/^\(\s*\{([^}]*)\}\s*=.*\)$/);
|
|
247
|
+
if (m != null) {
|
|
248
|
+
// Object destructuring: ({x, y} = expr) -> const {x, y} = expr
|
|
249
|
+
inner = s.replace(/^\s*\(\s*/, '').replace(/\s*\)\s*$/, '');
|
|
250
|
+
names = (function() {
|
|
251
|
+
var k, len1, ref, results;
|
|
252
|
+
ref = m[1].split(',');
|
|
253
|
+
results = [];
|
|
254
|
+
for (k = 0, len1 = ref.length; k < len1; k++) {
|
|
255
|
+
n = ref[k];
|
|
256
|
+
if (n.trim()) {
|
|
257
|
+
results.push(objectTarget(n));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return results;
|
|
261
|
+
})();
|
|
262
|
+
consumed = consumed.concat(names);
|
|
263
|
+
assignments.push('const ' + inner);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
name = varNames.find(function(n) {
|
|
267
|
+
return s.startsWith(`${n} =`) || s.startsWith(`${n}=`);
|
|
207
268
|
});
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
assignments.push('const ' +
|
|
269
|
+
if (name != null) {
|
|
270
|
+
consumed.push(name);
|
|
271
|
+
assignments.push('const ' + s);
|
|
211
272
|
}
|
|
212
273
|
}
|
|
213
|
-
|
|
274
|
+
allConsumed = varNames.every(function(n) {
|
|
275
|
+
return indexOf.call(consumed, n) >= 0;
|
|
276
|
+
});
|
|
277
|
+
if (assignments.length > 0 && allConsumed) {
|
|
214
278
|
return assignments.join('; ');
|
|
215
279
|
}
|
|
216
|
-
return
|
|
217
|
-
|
|
218
|
-
|
|
280
|
+
return ((function() {
|
|
281
|
+
var k, len1, results;
|
|
282
|
+
results = [];
|
|
283
|
+
for (k = 0, len1 = stmts.length; k < len1; k++) {
|
|
284
|
+
s = stmts[k];
|
|
285
|
+
// Fallback: keep the original var declaration so no variable goes
|
|
286
|
+
// undeclared (e.g. chained assignment `a = b = 1`).
|
|
287
|
+
results.push(collapseWs(s).replace(/;\s*$/, ''));
|
|
288
|
+
}
|
|
289
|
+
return results;
|
|
290
|
+
})()).join('; ');
|
|
219
291
|
};
|
|
220
292
|
|
|
221
293
|
// Split compiled JS into top-level statements, respecting string literals
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coffeehaml",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"description": "Haml structure. CoffeeScript semantics. React runtime. Zero runtime. Vite-native.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "coffee -c -o dist/ src/*.coffee && coffee -c -o dist/prettier/ src/prettier/*.coffee",
|
|
64
64
|
"dev": "coffee -c -w -o dist/ src/*.coffee & coffee -c -w -o dist/prettier/ src/prettier/*.coffee",
|
|
65
|
-
"test": "node test/prettier/run-integration-tests.mjs && node test/prettier/run-coffeescript-tests.mjs && node test/prettier/run-statement-merge-tests.mjs",
|
|
65
|
+
"test": "node test/run-compiler-tests.mjs && node test/prettier/run-integration-tests.mjs && node test/prettier/run-coffeescript-tests.mjs && node test/prettier/run-statement-merge-tests.mjs",
|
|
66
66
|
"prepublishOnly": "npm run build",
|
|
67
67
|
"fmt": "prettier --write --plugin=. **/*.chaml"
|
|
68
68
|
},
|
|
@@ -91,4 +91,4 @@
|
|
|
91
91
|
"dependencies": {
|
|
92
92
|
"source-map": "^0.7.4"
|
|
93
93
|
}
|
|
94
|
-
}
|
|
94
|
+
}
|
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
// Generated by CoffeeScript 2.7.0
|
|
2
|
-
// coffeescript-formatter.coffee — CoffeeScript token-based expression formatter
|
|
3
|
-
var DEFAULT_OPTS, EXPR_STARTERS, FOR_KEYWORDS, KEYWORD_SPACE_AFTER, NO_SPACE_AFTER, NO_SPACE_BEFORE, SPACE_AFTER, SPACE_BOTH, _require, getTokenText, hasImplicitTokens, hasMethodChain, isUnaryOp, lineWrap, loadCoffeeScript, needsSpaceBetween, normalizeSpacing, wrapMethodChain;
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
createRequire
|
|
7
|
-
} from 'module';
|
|
8
|
-
|
|
9
|
-
_require = createRequire(import.meta.url);
|
|
10
|
-
|
|
11
|
-
loadCoffeeScript = function() {
|
|
12
|
-
try {
|
|
13
|
-
return _require('coffeescript');
|
|
14
|
-
} catch (error) {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// ─── Token types ──────────────────────────────────────────
|
|
20
|
-
// CSToken: { type, value, range, generated?, originalText? }
|
|
21
|
-
|
|
22
|
-
// ─── Spacing categories ───────────────────────────────────
|
|
23
|
-
NO_SPACE_BEFORE = new Set([',', ':', ';', '.', '?.', '::', ')', ']', '}', 'CALL_END', 'PARAM_END', 'INDEX_END', 'POST_IF', 'POST_FOR', 'POST_WHILE', 'TERMINATOR']);
|
|
24
|
-
|
|
25
|
-
NO_SPACE_AFTER = new Set(['.', '?.', '::', '...', '..', '(', '[', '{', 'CALL_START', 'PARAM_START', 'INDEX_START', 'STRING_START', 'INTERPOLATION_START', 'INDENT', '@']);
|
|
26
|
-
|
|
27
|
-
SPACE_AFTER = new Set([',', ':']);
|
|
28
|
-
|
|
29
|
-
SPACE_BOTH = new Set(['=', '?=', '+', '-', 'MATH', '**', 'SHIFT', '&', '|', '^', '&&', '||', '?', '=>', '->', 'COMPARE', 'COMPOUND_ASSIGN', 'RELATION', 'FOR', 'ELSE', 'IF', 'WHILE']);
|
|
30
|
-
|
|
31
|
-
KEYWORD_SPACE_AFTER = new Set(['RETURN', 'THROW', 'NEW', 'TYPEOF', 'INSTANCEOF', 'DELETE', 'CLASS', 'EXTENDS', 'DEFAULT', 'IMPORT', 'EXPORT', 'AWAIT', 'YIELD', 'BREAK', 'CONTINUE', 'SWITCH']);
|
|
32
|
-
|
|
33
|
-
FOR_KEYWORDS = new Set(['FORIN', 'FOROF', 'WHEN', 'BY']);
|
|
34
|
-
|
|
35
|
-
EXPR_STARTERS = new Set(['(', '[', '{', ',', ':', '=', '?=', '+', '-', 'MATH', '**', 'SHIFT', '&', '|', '^', '&&', '||', '?', '=>', '->', 'COMPARE', 'COMPOUND_ASSIGN', 'RETURN', 'INDENT', 'OUTDENT', 'TERMINATOR', 'FOR', 'FORIN', 'FOROF', 'WHILE', 'IF', 'ELSE', 'THROW', 'NEW', 'TYPEOF', 'INSTANCEOF', 'DELETE', 'EXTENDS', 'DEFAULT', 'CALL_START', 'PARAM_START', 'INDEX_START', 'UNARY']);
|
|
36
|
-
|
|
37
|
-
DEFAULT_OPTS = {
|
|
38
|
-
printWidth: 80,
|
|
39
|
-
methodChainAlign: true,
|
|
40
|
-
enabled: true
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// ─── Public API ───────────────────────────────────────────
|
|
44
|
-
export var formatCoffeeScript = function(source, options = {}) {
|
|
45
|
-
var cs, normalized, opts, rawTokens, tokens;
|
|
46
|
-
opts = {...DEFAULT_OPTS, ...options};
|
|
47
|
-
if (!opts.enabled) {
|
|
48
|
-
return source;
|
|
49
|
-
}
|
|
50
|
-
if (!(source && source.trim() !== '')) {
|
|
51
|
-
return source;
|
|
52
|
-
}
|
|
53
|
-
try {
|
|
54
|
-
cs = loadCoffeeScript();
|
|
55
|
-
if (!(cs && typeof cs.tokens === 'function')) {
|
|
56
|
-
return source;
|
|
57
|
-
}
|
|
58
|
-
rawTokens = cs.tokens(source);
|
|
59
|
-
if (!(rawTokens && rawTokens.length > 0)) {
|
|
60
|
-
return source;
|
|
61
|
-
}
|
|
62
|
-
tokens = rawTokens.map(function(t) {
|
|
63
|
-
var ref, ref1, ref2, ref3;
|
|
64
|
-
return {
|
|
65
|
-
type: t[0],
|
|
66
|
-
value: t[1],
|
|
67
|
-
range: (ref = (ref1 = t[2]) != null ? ref1.range : void 0) != null ? ref : [0, 0],
|
|
68
|
-
generated: !!((ref2 = t[2]) != null ? ref2.generated : void 0),
|
|
69
|
-
originalText: ((ref3 = t[2]) != null ? ref3.range : void 0) && t[2].range[0] < t[2].range[1] ? source.slice(t[2].range[0], t[2].range[1]) : t[1]
|
|
70
|
-
};
|
|
71
|
-
});
|
|
72
|
-
if (hasImplicitTokens(tokens)) {
|
|
73
|
-
return source;
|
|
74
|
-
}
|
|
75
|
-
normalized = normalizeSpacing(tokens, source);
|
|
76
|
-
if (normalized.length > opts.printWidth) {
|
|
77
|
-
return lineWrap(normalized, opts, source);
|
|
78
|
-
}
|
|
79
|
-
return normalized;
|
|
80
|
-
} catch (error) {
|
|
81
|
-
return source;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// ─── Block formatting ─────────────────────────────────────
|
|
86
|
-
export var formatCoffeeScriptBlock = function(source, options = {}) {
|
|
87
|
-
var formatted, lines, opts;
|
|
88
|
-
opts = {...DEFAULT_OPTS, ...options};
|
|
89
|
-
if (!opts.enabled) {
|
|
90
|
-
return source;
|
|
91
|
-
}
|
|
92
|
-
if (!(source && source.trim() !== '')) {
|
|
93
|
-
return source;
|
|
94
|
-
}
|
|
95
|
-
lines = source.split('\n');
|
|
96
|
-
formatted = lines.map(function(line) {
|
|
97
|
-
var formattedExpr, leading, ref, ref1, trimmed;
|
|
98
|
-
trimmed = line.trim();
|
|
99
|
-
if (!trimmed) {
|
|
100
|
-
return '';
|
|
101
|
-
}
|
|
102
|
-
leading = (ref = (ref1 = line.match(/^(\s*)/)) != null ? ref1[1] : void 0) != null ? ref : '';
|
|
103
|
-
try {
|
|
104
|
-
formattedExpr = formatCoffeeScript(trimmed, options);
|
|
105
|
-
return leading + formattedExpr;
|
|
106
|
-
} catch (error) {
|
|
107
|
-
return line;
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
return formatted.join('\n');
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// ─── Implicit token detection ─────────────────────────────
|
|
114
|
-
hasImplicitTokens = function(tokens) {
|
|
115
|
-
var j, len, ref, t;
|
|
116
|
-
for (j = 0, len = tokens.length; j < len; j++) {
|
|
117
|
-
t = tokens[j];
|
|
118
|
-
if ((ref = t.type) === 'INDENT' || ref === 'OUTDENT' || ref === 'TERMINATOR') {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (t.generated) {
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
if (t.range && t.range[0] >= t.range[1]) {
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return false;
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
// ─── Spacing normalization ────────────────────────────────
|
|
132
|
-
normalizeSpacing = function(tokens, originalSource = null) {
|
|
133
|
-
var afterPrevIsExprStart, cur, j, len, parts, prevVisible, prevWasAtExprStart, ref, ref1, ref2, text, trimmed;
|
|
134
|
-
parts = [];
|
|
135
|
-
prevVisible = null;
|
|
136
|
-
prevWasAtExprStart = true;
|
|
137
|
-
afterPrevIsExprStart = true;
|
|
138
|
-
for (j = 0, len = tokens.length; j < len; j++) {
|
|
139
|
-
cur = tokens[j];
|
|
140
|
-
// Skip implicit CALL/PARAM tokens
|
|
141
|
-
if ((ref = cur.type) === 'CALL_START' || ref === 'CALL_END') {
|
|
142
|
-
if (cur.generated) {
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if (cur.range && cur.range[0] >= cur.range[1]) {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if ((ref1 = cur.type) === 'PARAM_START' || ref1 === 'PARAM_END') {
|
|
150
|
-
if (cur.generated) {
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
if (cur.range && cur.range[0] >= cur.range[1]) {
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
// Handle INDENT — represents inline then/when/else/etc from CoffeeScript
|
|
158
|
-
if (cur.type === 'INDENT') {
|
|
159
|
-
text = getTokenText(cur, originalSource);
|
|
160
|
-
trimmed = text != null ? text.trim() : void 0;
|
|
161
|
-
if (trimmed && trimmed !== '') {
|
|
162
|
-
parts.push(' ' + trimmed + ' ');
|
|
163
|
-
prevWasAtExprStart = false;
|
|
164
|
-
afterPrevIsExprStart = true;
|
|
165
|
-
prevVisible = cur;
|
|
166
|
-
}
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
if (cur.type === 'OUTDENT') {
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
// Skip TERMINATOR
|
|
173
|
-
if (cur.type === 'TERMINATOR') {
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
// Space before this token
|
|
177
|
-
if (prevVisible && ((ref2 = prevVisible.type) !== 'INDENT' && ref2 !== 'OUTDENT')) {
|
|
178
|
-
if (needsSpaceBetween(prevVisible, cur, prevWasAtExprStart)) {
|
|
179
|
-
parts.push(' ');
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
parts.push(getTokenText(cur, originalSource));
|
|
183
|
-
prevWasAtExprStart = afterPrevIsExprStart;
|
|
184
|
-
afterPrevIsExprStart = EXPR_STARTERS.has(cur.type);
|
|
185
|
-
prevVisible = cur;
|
|
186
|
-
}
|
|
187
|
-
return parts.join('');
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
getTokenText = function(token, originalSource = null) {
|
|
191
|
-
if (token.generated) {
|
|
192
|
-
return token.value;
|
|
193
|
-
}
|
|
194
|
-
if (!(token.range && token.range[0] < token.range[1])) {
|
|
195
|
-
return token.value;
|
|
196
|
-
}
|
|
197
|
-
if (originalSource) {
|
|
198
|
-
return originalSource.slice(token.range[0], token.range[1]);
|
|
199
|
-
}
|
|
200
|
-
return token.value;
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
needsSpaceBetween = function(prev, cur, prevAtExprStart) {
|
|
204
|
-
var text;
|
|
205
|
-
if (NO_SPACE_AFTER.has(prev.type)) {
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
if (NO_SPACE_BEFORE.has(cur.type)) {
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
if (prev.type === '@') {
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
if (SPACE_AFTER.has(prev.type)) {
|
|
215
|
-
return true;
|
|
216
|
-
}
|
|
217
|
-
if (FOR_KEYWORDS.has(prev.type) || FOR_KEYWORDS.has(cur.type)) {
|
|
218
|
-
return true;
|
|
219
|
-
}
|
|
220
|
-
if (KEYWORD_SPACE_AFTER.has(prev.type)) {
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
if (prev.type === 'UNARY') {
|
|
224
|
-
text = prev.originalText || prev.value;
|
|
225
|
-
if (text === '!' || text === '!!') {
|
|
226
|
-
return false;
|
|
227
|
-
}
|
|
228
|
-
return true;
|
|
229
|
-
}
|
|
230
|
-
if (SPACE_BOTH.has(prev.type) || SPACE_BOTH.has(cur.type)) {
|
|
231
|
-
if (isUnaryOp(prev, prevAtExprStart)) {
|
|
232
|
-
return false;
|
|
233
|
-
}
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
if (prev.type === 'IDENTIFIER' && cur.type === 'IDENTIFIER') {
|
|
237
|
-
return true;
|
|
238
|
-
}
|
|
239
|
-
return false;
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
isUnaryOp = function(prev, prevAtExprStart) {
|
|
243
|
-
var ref;
|
|
244
|
-
if ((ref = prev.type) !== '+' && ref !== '-') {
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
if (prevAtExprStart) {
|
|
248
|
-
return true;
|
|
249
|
-
}
|
|
250
|
-
if (prev.range && prev.range[0] === 0) {
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
253
|
-
return false;
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
// ─── Line wrapping ────────────────────────────────────────
|
|
257
|
-
lineWrap = function(source, opts, fallback) {
|
|
258
|
-
if (opts.methodChainAlign && hasMethodChain(source)) {
|
|
259
|
-
return wrapMethodChain(source, opts);
|
|
260
|
-
}
|
|
261
|
-
return fallback;
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
hasMethodChain = function(source) {
|
|
265
|
-
return (source.match(/\.[a-zA-Z_$]/g) || []).length >= 2;
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
wrapMethodChain = function(source, _opts) {
|
|
269
|
-
var ch, chars, current, depth, i, indent, result;
|
|
270
|
-
indent = ' ';
|
|
271
|
-
result = [];
|
|
272
|
-
chars = [...source];
|
|
273
|
-
depth = 0;
|
|
274
|
-
current = '';
|
|
275
|
-
i = 0;
|
|
276
|
-
while (i < chars.length) {
|
|
277
|
-
ch = chars[i];
|
|
278
|
-
if (ch === '(' || ch === '[' || ch === '{') {
|
|
279
|
-
depth++;
|
|
280
|
-
}
|
|
281
|
-
if (ch === ')' || ch === ']' || ch === '}') {
|
|
282
|
-
depth--;
|
|
283
|
-
}
|
|
284
|
-
if (ch === '.' && depth === 0 && i > 0 && chars[i - 1] !== '?' && current.trim().length > 0) {
|
|
285
|
-
if (result.length === 0) {
|
|
286
|
-
result.push(current.trimEnd());
|
|
287
|
-
} else {
|
|
288
|
-
result.push(indent + current.trimEnd());
|
|
289
|
-
}
|
|
290
|
-
current = ch;
|
|
291
|
-
i++;
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
current += ch;
|
|
295
|
-
i++;
|
|
296
|
-
}
|
|
297
|
-
if (current.trim().length > 0) {
|
|
298
|
-
if (result.length === 0) {
|
|
299
|
-
result.push(current.trim());
|
|
300
|
-
} else {
|
|
301
|
-
result.push(indent + current.trim());
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
if (result.length > 1) {
|
|
305
|
-
return result.join('\n');
|
|
306
|
-
} else {
|
|
307
|
-
return source;
|
|
308
|
-
}
|
|
309
|
-
};
|
package/dist/options.js
DELETED
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
// Generated by CoffeeScript 2.7.0
|
|
2
|
-
// options.coffee — CoffeeHaml Prettier formatting options
|
|
3
|
-
export var CAT = {
|
|
4
|
-
STRUCTURE: 'CoffeeHaml: Structure',
|
|
5
|
-
ATTRIBUTES: 'CoffeeHaml: Attributes',
|
|
6
|
-
COFFEESCRIPT: 'CoffeeHaml: CoffeeScript',
|
|
7
|
-
WHITESPACE: 'CoffeeHaml: Whitespace',
|
|
8
|
-
CONTROL_FLOW: 'CoffeeHaml: Control Flow',
|
|
9
|
-
COMMENTS: 'CoffeeHaml: Comments'
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export var options = {
|
|
13
|
-
tagCase: {
|
|
14
|
-
category: CAT.STRUCTURE,
|
|
15
|
-
type: 'choice',
|
|
16
|
-
description: 'Normalize HTML tag casing. Component tags (%MyComponent) never affected.',
|
|
17
|
-
choices: [
|
|
18
|
-
{
|
|
19
|
-
value: 'preserve',
|
|
20
|
-
description: 'Leave tag case as written'
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
value: 'lowercase',
|
|
24
|
-
description: '%DIV to %div (HTML tags only)'
|
|
25
|
-
}
|
|
26
|
-
]
|
|
27
|
-
},
|
|
28
|
-
implicitDivExpansion: {
|
|
29
|
-
category: CAT.STRUCTURE,
|
|
30
|
-
type: 'boolean',
|
|
31
|
-
description: 'Expand .class to %div.class (anti-idiomatic in HAML).'
|
|
32
|
-
},
|
|
33
|
-
maxChainLength: {
|
|
34
|
-
category: CAT.STRUCTURE,
|
|
35
|
-
type: 'int',
|
|
36
|
-
description: 'Max .class chain length before breaking into indented hierarchy. 0 = always break.'
|
|
37
|
-
},
|
|
38
|
-
inlineThreshold: {
|
|
39
|
-
category: CAT.STRUCTURE,
|
|
40
|
-
type: 'int',
|
|
41
|
-
description: 'Try single-line if estimated width <= this. -1 = never inline children.'
|
|
42
|
-
},
|
|
43
|
-
voidElementStyle: {
|
|
44
|
-
category: CAT.STRUCTURE,
|
|
45
|
-
type: 'choice',
|
|
46
|
-
description: 'How void elements (%br, %img, etc.) are formatted.',
|
|
47
|
-
choices: [
|
|
48
|
-
{
|
|
49
|
-
value: 'self-closing',
|
|
50
|
-
description: 'Self-closing (no children)'
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
value: 'explicit',
|
|
54
|
-
description: 'Preserve %br/ syntax, otherwise self-close'
|
|
55
|
-
}
|
|
56
|
-
]
|
|
57
|
-
},
|
|
58
|
-
attributeStyle: {
|
|
59
|
-
category: CAT.ATTRIBUTES,
|
|
60
|
-
type: 'choice',
|
|
61
|
-
description: 'Enforce attribute syntax: {braces}, (parens), bare, or preserve.',
|
|
62
|
-
choices: [
|
|
63
|
-
{
|
|
64
|
-
value: 'preserve',
|
|
65
|
-
description: 'Keep as written'
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
value: 'braces',
|
|
69
|
-
description: 'Convert to {CoffeeScript} style'
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
value: 'parens',
|
|
73
|
-
description: 'Convert to HTML (parens) style'
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
value: 'bare',
|
|
77
|
-
description: 'Convert to bare HAML style'
|
|
78
|
-
}
|
|
79
|
-
]
|
|
80
|
-
},
|
|
81
|
-
attributeMultilineThreshold: {
|
|
82
|
-
category: CAT.ATTRIBUTES,
|
|
83
|
-
type: 'int',
|
|
84
|
-
description: 'Break attributes across lines when count >= this. 0 = always multiline.'
|
|
85
|
-
},
|
|
86
|
-
attributeSort: {
|
|
87
|
-
category: CAT.ATTRIBUTES,
|
|
88
|
-
type: 'choice',
|
|
89
|
-
description: 'Sort attribute keys (spread attributes always last).',
|
|
90
|
-
choices: [
|
|
91
|
-
{
|
|
92
|
-
value: 'none',
|
|
93
|
-
description: 'Preserve author order'
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
value: 'alphabetical',
|
|
97
|
-
description: 'Sort A to Z'
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
value: 'idiomatic',
|
|
101
|
-
description: 'id first, class second, rest A to Z'
|
|
102
|
-
}
|
|
103
|
-
]
|
|
104
|
-
},
|
|
105
|
-
quoteStyle: {
|
|
106
|
-
category: CAT.ATTRIBUTES,
|
|
107
|
-
type: 'choice',
|
|
108
|
-
description: 'Quote style for bare/HAML attributes.',
|
|
109
|
-
choices: [
|
|
110
|
-
{
|
|
111
|
-
value: 'preserve',
|
|
112
|
-
description: 'Keep as written'
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
value: 'double',
|
|
116
|
-
description: 'Force double quotes'
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
value: 'single',
|
|
120
|
-
description: 'Force single quotes'
|
|
121
|
-
}
|
|
122
|
-
]
|
|
123
|
-
},
|
|
124
|
-
coffeeScriptFormat: {
|
|
125
|
-
category: CAT.COFFEESCRIPT,
|
|
126
|
-
type: 'boolean',
|
|
127
|
-
description: 'Format CoffeeScript expressions using prettier/plugins/coffeescript.'
|
|
128
|
-
},
|
|
129
|
-
methodChainAlign: {
|
|
130
|
-
category: CAT.COFFEESCRIPT,
|
|
131
|
-
type: 'boolean',
|
|
132
|
-
description: 'Align .method chains on newlines for = expressions.'
|
|
133
|
-
},
|
|
134
|
-
blankLineHandling: {
|
|
135
|
-
category: CAT.WHITESPACE,
|
|
136
|
-
type: 'choice',
|
|
137
|
-
description: 'How blank lines between sibling elements are treated.',
|
|
138
|
-
choices: [
|
|
139
|
-
{
|
|
140
|
-
value: 'preserve',
|
|
141
|
-
description: 'Keep single; collapse multiples'
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
value: 'collapse',
|
|
145
|
-
description: 'Remove all blank lines between siblings'
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
value: 'respect',
|
|
149
|
-
description: 'Keep exactly as written'
|
|
150
|
-
}
|
|
151
|
-
]
|
|
152
|
-
},
|
|
153
|
-
trailingWhitespace: {
|
|
154
|
-
category: CAT.WHITESPACE,
|
|
155
|
-
type: 'choice',
|
|
156
|
-
description: 'Handle trailing whitespace.',
|
|
157
|
-
choices: [
|
|
158
|
-
{
|
|
159
|
-
value: 'remove',
|
|
160
|
-
description: 'Strip trailing whitespace'
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
value: 'preserve',
|
|
164
|
-
description: 'Leave as written'
|
|
165
|
-
}
|
|
166
|
-
]
|
|
167
|
-
},
|
|
168
|
-
continuationStyle: {
|
|
169
|
-
category: CAT.CONTROL_FLOW,
|
|
170
|
-
type: 'choice',
|
|
171
|
-
description: 'How \\ continuations are formatted.',
|
|
172
|
-
choices: [
|
|
173
|
-
{
|
|
174
|
-
value: 'preserve',
|
|
175
|
-
description: 'Keep as written'
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
value: 'indent',
|
|
179
|
-
description: 'Always use indented continuation'
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
value: 'backslash',
|
|
183
|
-
description: 'Always use \\ continuation'
|
|
184
|
-
}
|
|
185
|
-
]
|
|
186
|
-
},
|
|
187
|
-
controlFlowInline: {
|
|
188
|
-
category: CAT.CONTROL_FLOW,
|
|
189
|
-
type: 'boolean',
|
|
190
|
-
description: 'Allow - if x then .ok one-liners (anti-Prettier by default).'
|
|
191
|
-
},
|
|
192
|
-
statementMerging: {
|
|
193
|
-
category: CAT.CONTROL_FLOW,
|
|
194
|
-
type: 'choice',
|
|
195
|
-
description: 'Merge consecutive - lines into a single indented block. Only childless statements.',
|
|
196
|
-
choices: [
|
|
197
|
-
{
|
|
198
|
-
value: 'preserve',
|
|
199
|
-
description: 'Keep each - line separate'
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
value: 'merge',
|
|
203
|
-
description: 'Merge consecutive childless statements into one indented block'
|
|
204
|
-
}
|
|
205
|
-
]
|
|
206
|
-
},
|
|
207
|
-
commentFormat: {
|
|
208
|
-
category: CAT.COMMENTS,
|
|
209
|
-
type: 'boolean',
|
|
210
|
-
description: 'Reflow/word-wrap comment text (experimental).'
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
export var defaultOptions = {
|
|
215
|
-
tagCase: 'preserve',
|
|
216
|
-
implicitDivExpansion: false,
|
|
217
|
-
maxChainLength: 4,
|
|
218
|
-
inlineThreshold: -1,
|
|
219
|
-
voidElementStyle: 'self-closing',
|
|
220
|
-
attributeStyle: 'preserve',
|
|
221
|
-
attributeMultilineThreshold: 1,
|
|
222
|
-
attributeSort: 'none',
|
|
223
|
-
quoteStyle: 'preserve',
|
|
224
|
-
coffeeScriptFormat: true,
|
|
225
|
-
methodChainAlign: true,
|
|
226
|
-
blankLineHandling: 'preserve',
|
|
227
|
-
trailingWhitespace: 'remove',
|
|
228
|
-
continuationStyle: 'indent',
|
|
229
|
-
controlFlowInline: false,
|
|
230
|
-
statementMerging: 'preserve',
|
|
231
|
-
commentFormat: false
|
|
232
|
-
};
|
package/dist/printer.js
DELETED
|
@@ -1,583 +0,0 @@
|
|
|
1
|
-
// Generated by CoffeeScript 2.7.0
|
|
2
|
-
// printer.coffee — AST → Prettier Doc IR
|
|
3
|
-
// Core formatting engine for CoffeeHaml Prettier plugin
|
|
4
|
-
var MergedStatements, VOID_ELEMENTS, _prettier, _require, analyzeBlankLines, defaultOpts, detectStyle, formatAttributes, formatTag, group, hardline, indent, isSingleInlineChild, isVoidElement, join, joinChildrenWithBlanks, mergeAllStatements, opts, printComment, printControlFlow, printDoc, printDoctype, printElement, printFilter, printFlow, printImplicitDiv, printMergedFlow, printNode, printOutput, reflowCommentText;
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
Document,
|
|
8
|
-
Element,
|
|
9
|
-
ImplicitDiv,
|
|
10
|
-
Text,
|
|
11
|
-
Output,
|
|
12
|
-
ControlFlow,
|
|
13
|
-
Comment,
|
|
14
|
-
Filter,
|
|
15
|
-
Doctype,
|
|
16
|
-
Node,
|
|
17
|
-
Expression,
|
|
18
|
-
Attribute,
|
|
19
|
-
SpreadAttribute
|
|
20
|
-
} from '../ast.js';
|
|
21
|
-
|
|
22
|
-
import {
|
|
23
|
-
formatCoffeeScript,
|
|
24
|
-
formatCoffeeScriptBlock
|
|
25
|
-
} from './coffeescript-formatter.js';
|
|
26
|
-
|
|
27
|
-
import {
|
|
28
|
-
createRequire
|
|
29
|
-
} from 'module';
|
|
30
|
-
|
|
31
|
-
_require = createRequire(import.meta.url);
|
|
32
|
-
|
|
33
|
-
_prettier = _require('prettier');
|
|
34
|
-
|
|
35
|
-
({group, indent, hardline, join} = _prettier.doc.builders);
|
|
36
|
-
|
|
37
|
-
// ─── Format Options ────────────────────────────────────────
|
|
38
|
-
// CoffeeHamlFormatOptions shape (runtime):
|
|
39
|
-
// tagCase, implicitDivExpansion, maxChainLength, inlineThreshold
|
|
40
|
-
// voidElementStyle, attributeStyle, attributeMultilineThreshold
|
|
41
|
-
// attributeSort, quoteStyle, coffeeScriptFormat, methodChainAlign
|
|
42
|
-
// blankLineHandling, trailingWhitespace, continuationStyle
|
|
43
|
-
// controlFlowInline, statementMerging, commentFormat
|
|
44
|
-
// tabWidth, useTabs, printWidth, originalText?
|
|
45
|
-
defaultOpts = {
|
|
46
|
-
tagCase: 'preserve',
|
|
47
|
-
implicitDivExpansion: false,
|
|
48
|
-
maxChainLength: 4,
|
|
49
|
-
inlineThreshold: -1,
|
|
50
|
-
voidElementStyle: 'self-closing',
|
|
51
|
-
attributeStyle: 'preserve',
|
|
52
|
-
attributeMultilineThreshold: 1,
|
|
53
|
-
attributeSort: 'none',
|
|
54
|
-
quoteStyle: 'preserve',
|
|
55
|
-
coffeeScriptFormat: true,
|
|
56
|
-
methodChainAlign: true,
|
|
57
|
-
blankLineHandling: 'preserve',
|
|
58
|
-
trailingWhitespace: 'remove',
|
|
59
|
-
continuationStyle: 'indent',
|
|
60
|
-
controlFlowInline: false,
|
|
61
|
-
statementMerging: 'preserve',
|
|
62
|
-
commentFormat: false,
|
|
63
|
-
tabWidth: 2,
|
|
64
|
-
useTabs: false,
|
|
65
|
-
printWidth: 80,
|
|
66
|
-
originalText: void 0
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
opts = function(p) {
|
|
70
|
-
return p.__opts || defaultOpts;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
MergedStatements = (function() {
|
|
74
|
-
// ─── MergedStatements wrapper ──────────────────────────────
|
|
75
|
-
class MergedStatements {
|
|
76
|
-
constructor(statements) {
|
|
77
|
-
this.statements = statements;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
MergedStatements.prototype.kind = 'MergedStatements';
|
|
83
|
-
|
|
84
|
-
MergedStatements.prototype.statements = [];
|
|
85
|
-
|
|
86
|
-
return MergedStatements;
|
|
87
|
-
|
|
88
|
-
}).call(this);
|
|
89
|
-
|
|
90
|
-
// ─── Constants ─────────────────────────────────────────────
|
|
91
|
-
VOID_ELEMENTS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
|
|
92
|
-
|
|
93
|
-
isVoidElement = function(tag) {
|
|
94
|
-
return VOID_ELEMENTS.has(tag.toLowerCase());
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
isSingleInlineChild = function(children) {
|
|
98
|
-
return children.length === 1 && (children[0] instanceof Text || children[0] instanceof Output);
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
// ─── Blank-line-aware join ─────────────────────────────────
|
|
102
|
-
joinChildrenWithBlanks = function(nodes, docs, o) {
|
|
103
|
-
var hasBlank, i, k, ref, ref1, result;
|
|
104
|
-
if (docs.length === 0) {
|
|
105
|
-
return '';
|
|
106
|
-
}
|
|
107
|
-
if (docs.length === 1) {
|
|
108
|
-
return docs[0];
|
|
109
|
-
}
|
|
110
|
-
result = [];
|
|
111
|
-
for (i = k = 0, ref = docs.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
112
|
-
if (i > 0) {
|
|
113
|
-
hasBlank = ((ref1 = nodes[i]) != null ? ref1._blankBefore : void 0) || false;
|
|
114
|
-
result.push(hasBlank ? [hardline, hardline] : hardline);
|
|
115
|
-
}
|
|
116
|
-
result.push(docs[i]);
|
|
117
|
-
}
|
|
118
|
-
return result;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
analyzeBlankLines = function(children, originalText, locations) {
|
|
122
|
-
var between, currStartOffset, i, k, node, prev, prevEndOffset, ref, results;
|
|
123
|
-
if (!(originalText && children.length > 1)) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
results = [];
|
|
127
|
-
for (i = k = 0, ref = children.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
128
|
-
node = children[i];
|
|
129
|
-
if (!(node != null ? node.location : void 0)) {
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
prev = children[i - 1];
|
|
133
|
-
if (!(prev != null ? prev.location : void 0)) {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
// Check for blank line in source between prev end and this node start
|
|
137
|
-
prevEndOffset = prev.location.offset + prev.location.length;
|
|
138
|
-
currStartOffset = node.location.offset;
|
|
139
|
-
between = originalText.slice(prevEndOffset, currStartOffset);
|
|
140
|
-
if (/\n\s*\n/.test(between)) {
|
|
141
|
-
results.push(node._blankBefore = true);
|
|
142
|
-
} else {
|
|
143
|
-
results.push(void 0);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return results;
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
// ─── Merge consecutive statements ──────────────────────────
|
|
150
|
-
mergeAllStatements = function(children) {
|
|
151
|
-
var child, i, j, next, result;
|
|
152
|
-
if (!((children != null ? children.length : void 0) > 1)) {
|
|
153
|
-
return children;
|
|
154
|
-
}
|
|
155
|
-
result = [];
|
|
156
|
-
i = 0;
|
|
157
|
-
while (i < children.length) {
|
|
158
|
-
child = children[i];
|
|
159
|
-
if (child instanceof ControlFlow && child.controlKind === 'statement' && child.children.length === 0) {
|
|
160
|
-
group = [child];
|
|
161
|
-
j = i + 1;
|
|
162
|
-
while (j < children.length) {
|
|
163
|
-
next = children[j];
|
|
164
|
-
if (next instanceof ControlFlow && next.controlKind === 'statement' && next.children.length === 0) {
|
|
165
|
-
group.push(next);
|
|
166
|
-
j++;
|
|
167
|
-
} else {
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
if (group.length > 1) {
|
|
172
|
-
result.push(new MergedStatements(group));
|
|
173
|
-
i = j;
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
result.push(child);
|
|
178
|
-
i++;
|
|
179
|
-
}
|
|
180
|
-
return result;
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
// ─── Main entry ────────────────────────────────────────────
|
|
184
|
-
export var print = function(path, o, printFn) {
|
|
185
|
-
var node;
|
|
186
|
-
node = path.node;
|
|
187
|
-
if (!node) {
|
|
188
|
-
return '';
|
|
189
|
-
}
|
|
190
|
-
// Hang options on path for nested lookups
|
|
191
|
-
path.__opts = o;
|
|
192
|
-
return printNode(path, o, printFn);
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
printNode = function(path, o, printFn) {
|
|
196
|
-
var node;
|
|
197
|
-
node = path.node;
|
|
198
|
-
if (!node) {
|
|
199
|
-
return '';
|
|
200
|
-
}
|
|
201
|
-
if (node instanceof Document) {
|
|
202
|
-
return printDoc(path, o, printFn);
|
|
203
|
-
}
|
|
204
|
-
if (node instanceof Element) {
|
|
205
|
-
return printElement(path, o, printFn);
|
|
206
|
-
}
|
|
207
|
-
if (node instanceof ImplicitDiv) {
|
|
208
|
-
return printImplicitDiv(path, o, printFn);
|
|
209
|
-
}
|
|
210
|
-
if (node instanceof Text) {
|
|
211
|
-
return node.value;
|
|
212
|
-
}
|
|
213
|
-
if (node instanceof Output) {
|
|
214
|
-
return printOutput(path, o, printFn);
|
|
215
|
-
}
|
|
216
|
-
if (node instanceof ControlFlow) {
|
|
217
|
-
return printControlFlow(path, o, printFn);
|
|
218
|
-
}
|
|
219
|
-
if (node instanceof MergedStatements) {
|
|
220
|
-
return printMergedFlow(path, o, printFn);
|
|
221
|
-
}
|
|
222
|
-
if (node instanceof Comment) {
|
|
223
|
-
return printComment(path, o, printFn);
|
|
224
|
-
}
|
|
225
|
-
if (node instanceof Filter) {
|
|
226
|
-
return printFilter(path, o, printFn);
|
|
227
|
-
}
|
|
228
|
-
if (node instanceof Doctype) {
|
|
229
|
-
return printDoctype(path, o);
|
|
230
|
-
}
|
|
231
|
-
return '';
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// ─── Document ──────────────────────────────────────────────
|
|
235
|
-
printDoc = function(path, o, printFn) {
|
|
236
|
-
var childDocs, k, len, n, next, node, ref, ref1;
|
|
237
|
-
node = path.node;
|
|
238
|
-
// Pre-pass: merge statements if enabled
|
|
239
|
-
if (o.statementMerging === 'merge') {
|
|
240
|
-
node.children = mergeAllStatements(node.children);
|
|
241
|
-
}
|
|
242
|
-
// Pre-pass: analyze blank lines
|
|
243
|
-
if (o.blankLineHandling === 'preserve' && o.originalText) {
|
|
244
|
-
ref = node.children;
|
|
245
|
-
for (k = 0, len = ref.length; k < len; k++) {
|
|
246
|
-
n = ref[k];
|
|
247
|
-
next = node.children[node.children.indexOf(n) + 1];
|
|
248
|
-
if (next) {
|
|
249
|
-
analyzeBlankLines([n, next], o.originalText);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
// Print children
|
|
254
|
-
childDocs = (ref1 = path.map(printFn, 'children')) != null ? ref1 : [];
|
|
255
|
-
return join(hardline, childDocs);
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
// ─── Element ───────────────────────────────────────────────
|
|
259
|
-
printElement = function(path, o, printFn) {
|
|
260
|
-
var attrStyle, attrs, childDoc, childDocs, children, classes, header, id, isVoid, joined, node, ref, ref1, ref2, ref3, selfClose, tag;
|
|
261
|
-
node = path.node;
|
|
262
|
-
tag = formatTag(node.tag, o);
|
|
263
|
-
// Classes and ID
|
|
264
|
-
classes = node.classes.map(function(c) {
|
|
265
|
-
return `.${c}`;
|
|
266
|
-
}).join('');
|
|
267
|
-
id = node.id ? `#${node.id}` : '';
|
|
268
|
-
// Attributes
|
|
269
|
-
attrStyle = detectStyle(node, o);
|
|
270
|
-
attrs = formatAttributes(node, attrStyle, o);
|
|
271
|
-
// Self-close marker
|
|
272
|
-
isVoid = typeof node.tag === 'string' && isVoidElement(node.tag);
|
|
273
|
-
selfClose = '';
|
|
274
|
-
if (isVoid) {
|
|
275
|
-
if (o.voidElementStyle === 'self-closing') {
|
|
276
|
-
selfClose = '/';
|
|
277
|
-
} else if (node.isSelfClosing) {
|
|
278
|
-
selfClose = '/';
|
|
279
|
-
}
|
|
280
|
-
} else if (node.isSelfClosing) {
|
|
281
|
-
selfClose = '/';
|
|
282
|
-
}
|
|
283
|
-
header = `%${tag}${classes}${id}${attrs}${selfClose}`;
|
|
284
|
-
// Children
|
|
285
|
-
children = (ref = node.children) != null ? ref : [];
|
|
286
|
-
if (children.length === 0) {
|
|
287
|
-
return header;
|
|
288
|
-
}
|
|
289
|
-
// Inline child
|
|
290
|
-
if (isSingleInlineChild(children) && o.controlFlowInline !== false) {
|
|
291
|
-
childDoc = (ref1 = (ref2 = path.map(printFn, 'children')) != null ? ref2[0] : void 0) != null ? ref1 : '';
|
|
292
|
-
return `${header} ${childDoc}`;
|
|
293
|
-
}
|
|
294
|
-
childDocs = (ref3 = path.map(printFn, 'children')) != null ? ref3 : [];
|
|
295
|
-
joined = joinChildrenWithBlanks(children, childDocs, o);
|
|
296
|
-
return [header, indent([hardline, joined])];
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
// ─── ImplicitDiv ───────────────────────────────────────────
|
|
300
|
-
printImplicitDiv = function(path, o, printFn) {
|
|
301
|
-
var attrStyle, attrs, childDoc, childDocs, children, classes, header, id, joined, node, ref, ref1, ref2, ref3;
|
|
302
|
-
node = path.node;
|
|
303
|
-
classes = node.classes.map(function(c) {
|
|
304
|
-
return `.${c}`;
|
|
305
|
-
}).join('');
|
|
306
|
-
id = node.id ? `#${node.id}` : '';
|
|
307
|
-
attrStyle = detectStyle(node, o);
|
|
308
|
-
attrs = formatAttributes(node, attrStyle, o);
|
|
309
|
-
header = `${classes}${id}${attrs}`;
|
|
310
|
-
children = (ref = node.children) != null ? ref : [];
|
|
311
|
-
if (children.length === 0) {
|
|
312
|
-
return header;
|
|
313
|
-
}
|
|
314
|
-
if (isSingleInlineChild(children)) {
|
|
315
|
-
childDoc = (ref1 = (ref2 = path.map(printFn, 'children')) != null ? ref2[0] : void 0) != null ? ref1 : '';
|
|
316
|
-
return `${header} ${childDoc}`;
|
|
317
|
-
}
|
|
318
|
-
childDocs = (ref3 = path.map(printFn, 'children')) != null ? ref3 : [];
|
|
319
|
-
joined = joinChildrenWithBlanks(children, childDocs, o);
|
|
320
|
-
return [header, indent([hardline, joined])];
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
// ─── Output ────────────────────────────────────────────────
|
|
324
|
-
printOutput = function(path, o, printFn) {
|
|
325
|
-
var childDocs, children, expr, joined, marker, node, ref, ref1, ref2;
|
|
326
|
-
node = path.node;
|
|
327
|
-
marker = node.outputKind === 'unescaped' ? '!=' : '=';
|
|
328
|
-
expr = (ref = node.expression.source) != null ? ref : '';
|
|
329
|
-
if (o.coffeeScriptFormat) {
|
|
330
|
-
expr = formatCoffeeScript(expr, o);
|
|
331
|
-
}
|
|
332
|
-
children = (ref1 = node.children) != null ? ref1 : [];
|
|
333
|
-
if (children.length === 0) {
|
|
334
|
-
return `${marker} ${expr}`;
|
|
335
|
-
}
|
|
336
|
-
childDocs = (ref2 = path.map(printFn, 'children')) != null ? ref2 : [];
|
|
337
|
-
joined = joinChildrenWithBlanks(children, childDocs, o);
|
|
338
|
-
return [`${marker} ${expr}`, indent([hardline, joined])];
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
// ─── Control Flow ──────────────────────────────────────────
|
|
342
|
-
printControlFlow = function(path, o, printFn) {
|
|
343
|
-
return printFlow(path, o, printFn);
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
printFlow = function(path, o, printFn) {
|
|
347
|
-
var childLines, children, expr, header, kind, node, ref, ref1, ref2, sep;
|
|
348
|
-
node = path.node;
|
|
349
|
-
kind = node.controlKind;
|
|
350
|
-
// Format expression
|
|
351
|
-
expr = (ref = node.expression.source) != null ? ref : '';
|
|
352
|
-
if (o.coffeeScriptFormat && kind !== 'statement') {
|
|
353
|
-
expr = formatCoffeeScript(expr, o);
|
|
354
|
-
}
|
|
355
|
-
// Build header
|
|
356
|
-
header = (function() {
|
|
357
|
-
switch (kind) {
|
|
358
|
-
case 'statement':
|
|
359
|
-
if (expr) {
|
|
360
|
-
return `- ${expr}`;
|
|
361
|
-
} else {
|
|
362
|
-
return '-';
|
|
363
|
-
}
|
|
364
|
-
break;
|
|
365
|
-
case 'else':
|
|
366
|
-
return '- else';
|
|
367
|
-
case 'else if':
|
|
368
|
-
return `- else if ${expr}`;
|
|
369
|
-
default:
|
|
370
|
-
return `- ${kind} ${expr}`;
|
|
371
|
-
}
|
|
372
|
-
})();
|
|
373
|
-
children = (ref1 = node.children) != null ? ref1 : [];
|
|
374
|
-
// Inline expansion (controlFlowInline)
|
|
375
|
-
if (o.controlFlowInline && children.length > 0 && (kind === 'if' || kind === 'unless' || kind === 'for' || kind === 'while')) {
|
|
376
|
-
if (children.length === 1 && (children[0] instanceof Element || children[0] instanceof ImplicitDiv)) {
|
|
377
|
-
sep = kind === 'if' || kind === 'unless' ? ' then ' : ' ';
|
|
378
|
-
return `${header}${sep}${printNode(path.map(printFn, 'children.0'), o, printFn)}`;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
if (children.length === 0) {
|
|
382
|
-
return header;
|
|
383
|
-
}
|
|
384
|
-
// Re-parsed merged statements: statement-kind with empty expr + TEXT children
|
|
385
|
-
// Use baked indentation (matching printMergedFlow) to avoid Prettier indent quirks
|
|
386
|
-
if (kind === 'statement' && !expr && children.every(function(c) {
|
|
387
|
-
return c instanceof Text || c instanceof Output;
|
|
388
|
-
})) {
|
|
389
|
-
childLines = children.map(function(c) {
|
|
390
|
-
var val;
|
|
391
|
-
val = (function() {
|
|
392
|
-
var ref2, ref3;
|
|
393
|
-
switch (false) {
|
|
394
|
-
case !(c instanceof Text):
|
|
395
|
-
return c.value;
|
|
396
|
-
case !(c instanceof Output):
|
|
397
|
-
return (ref2 = (ref3 = c.expression) != null ? ref3.source : void 0) != null ? ref2 : '';
|
|
398
|
-
default:
|
|
399
|
-
return '';
|
|
400
|
-
}
|
|
401
|
-
})();
|
|
402
|
-
return ' ' + val;
|
|
403
|
-
});
|
|
404
|
-
return join(hardline, ['-', childLines.join('\n')]);
|
|
405
|
-
}
|
|
406
|
-
return [header, indent([hardline, join(hardline, (ref2 = path.map(printFn, 'children')) != null ? ref2 : [])])];
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
printMergedFlow = function(path, o, printFn) {
|
|
410
|
-
var lines, node;
|
|
411
|
-
node = path.node;
|
|
412
|
-
lines = node.statements.map(function(stmt) {
|
|
413
|
-
var expr, ref;
|
|
414
|
-
expr = (ref = stmt.expression.source) != null ? ref : '';
|
|
415
|
-
if (o.coffeeScriptFormat) {
|
|
416
|
-
expr = formatCoffeeScript(expr, o);
|
|
417
|
-
}
|
|
418
|
-
return ' ' + expr;
|
|
419
|
-
});
|
|
420
|
-
return join(hardline, ['-', lines.join('\n')]);
|
|
421
|
-
};
|
|
422
|
-
|
|
423
|
-
// ─── Comment ───────────────────────────────────────────────
|
|
424
|
-
printComment = function(path, o, _printFn) {
|
|
425
|
-
var node, text;
|
|
426
|
-
node = path.node;
|
|
427
|
-
if (node.commentKind === 'html') {
|
|
428
|
-
return `/ ${node.text}`;
|
|
429
|
-
}
|
|
430
|
-
text = node.text || '';
|
|
431
|
-
if (o.commentFormat && text.length > o.printWidth * 0.7) {
|
|
432
|
-
text = reflowCommentText(text, o.printWidth);
|
|
433
|
-
}
|
|
434
|
-
return `-# ${text}`;
|
|
435
|
-
};
|
|
436
|
-
|
|
437
|
-
reflowCommentText = function(text, width) {
|
|
438
|
-
var current, k, len, lines, word, words;
|
|
439
|
-
words = text.split(/\s+/);
|
|
440
|
-
lines = [];
|
|
441
|
-
current = '';
|
|
442
|
-
for (k = 0, len = words.length; k < len; k++) {
|
|
443
|
-
word = words[k];
|
|
444
|
-
if (current.length + word.length + 1 > width - 3) { // 3 = "-# "
|
|
445
|
-
if (current) {
|
|
446
|
-
lines.push(current.trim());
|
|
447
|
-
}
|
|
448
|
-
current = word;
|
|
449
|
-
} else {
|
|
450
|
-
current += current ? ' ' + word : word;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
if (current) {
|
|
454
|
-
lines.push(current.trim());
|
|
455
|
-
}
|
|
456
|
-
return lines.join('\n-# ');
|
|
457
|
-
};
|
|
458
|
-
|
|
459
|
-
// ─── Filter ────────────────────────────────────────────────
|
|
460
|
-
printFilter = function(path, o, _printFn) {
|
|
461
|
-
var header, indented, node;
|
|
462
|
-
node = path.node;
|
|
463
|
-
header = `:${node.filterName}`;
|
|
464
|
-
if (node.content) {
|
|
465
|
-
indented = node.content.split('\n').map(function(l) {
|
|
466
|
-
return ' ' + l;
|
|
467
|
-
}).join('\n');
|
|
468
|
-
return `${header}\n${indented}`;
|
|
469
|
-
} else {
|
|
470
|
-
return header;
|
|
471
|
-
}
|
|
472
|
-
};
|
|
473
|
-
|
|
474
|
-
// ─── Doctype ───────────────────────────────────────────────
|
|
475
|
-
printDoctype = function(path, o) {
|
|
476
|
-
var node;
|
|
477
|
-
node = path.node;
|
|
478
|
-
return `!!! ${node.value || 'html'}`;
|
|
479
|
-
};
|
|
480
|
-
|
|
481
|
-
// ─── Tag formatting ────────────────────────────────────────
|
|
482
|
-
formatTag = function(tag, o) {
|
|
483
|
-
if (tag instanceof Expression) {
|
|
484
|
-
return tag.source;
|
|
485
|
-
}
|
|
486
|
-
if (o.tagCase === 'lowercase' && /^[a-z]/.test(tag)) {
|
|
487
|
-
return tag.toLowerCase();
|
|
488
|
-
}
|
|
489
|
-
return tag;
|
|
490
|
-
};
|
|
491
|
-
|
|
492
|
-
// ─── Attribute style detection ─────────────────────────────
|
|
493
|
-
detectStyle = function(node, o) {
|
|
494
|
-
var ref;
|
|
495
|
-
if (o.attributeStyle !== 'preserve') {
|
|
496
|
-
return o.attributeStyle;
|
|
497
|
-
}
|
|
498
|
-
return (ref = node.attrStyle) != null ? ref : 'bare';
|
|
499
|
-
};
|
|
500
|
-
|
|
501
|
-
formatAttributes = function(node, style, o) {
|
|
502
|
-
var attr, attrs, bareParts, expr, k, len, name, parts, ref, ref1, ref2, val;
|
|
503
|
-
attrs = (ref = node.attributes) != null ? ref : [];
|
|
504
|
-
if (!(attrs.length > 0)) {
|
|
505
|
-
return '';
|
|
506
|
-
}
|
|
507
|
-
// Bare style: HTML-style key="val" key2={expr} — space-separated
|
|
508
|
-
if (style === 'bare') {
|
|
509
|
-
bareParts = (function() {
|
|
510
|
-
var k, len, ref1, ref2, results;
|
|
511
|
-
results = [];
|
|
512
|
-
for (k = 0, len = attrs.length; k < len; k++) {
|
|
513
|
-
attr = attrs[k];
|
|
514
|
-
if (attr.spread) {
|
|
515
|
-
expr = (ref1 = attr.expression.source) != null ? ref1 : '';
|
|
516
|
-
if (o.coffeeScriptFormat) {
|
|
517
|
-
expr = formatCoffeeScript(expr, o);
|
|
518
|
-
}
|
|
519
|
-
results.push(`{${expr}...}`);
|
|
520
|
-
} else if (attr.shorthand) {
|
|
521
|
-
results.push(attr.name);
|
|
522
|
-
} else {
|
|
523
|
-
results.push(`${attr.name}=${(ref2 = attr.value.source) != null ? ref2 : ''}`);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return results;
|
|
527
|
-
})();
|
|
528
|
-
return ` ${bareParts.join(' ')}`;
|
|
529
|
-
} else {
|
|
530
|
-
parts = [];
|
|
531
|
-
for (k = 0, len = attrs.length; k < len; k++) {
|
|
532
|
-
attr = attrs[k];
|
|
533
|
-
if (attr.spread) {
|
|
534
|
-
expr = (ref1 = attr.expression.source) != null ? ref1 : '';
|
|
535
|
-
if (o.coffeeScriptFormat) {
|
|
536
|
-
expr = formatCoffeeScript(expr, o);
|
|
537
|
-
}
|
|
538
|
-
parts.push(style === 'parens' ? `...${expr}` : `{${expr}...}`);
|
|
539
|
-
continue;
|
|
540
|
-
}
|
|
541
|
-
name = attr.name;
|
|
542
|
-
val = (ref2 = attr.value.source) != null ? ref2 : '';
|
|
543
|
-
if (o.coffeeScriptFormat) {
|
|
544
|
-
val = formatCoffeeScript(val, o);
|
|
545
|
-
}
|
|
546
|
-
if (attr.shorthand) {
|
|
547
|
-
parts.push(name);
|
|
548
|
-
} else {
|
|
549
|
-
parts.push(`${name}: ${val}`);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
switch (style) {
|
|
553
|
-
case 'braces':
|
|
554
|
-
return `{ ${parts.join(', ')} }`;
|
|
555
|
-
case 'parens':
|
|
556
|
-
return `( ${parts.join(', ')} )`;
|
|
557
|
-
default:
|
|
558
|
-
return parts.join(', ');
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
// ─── CoffeeScript Embed (for Prettier embed) ───────────────
|
|
564
|
-
export var embedCoffeeScript = function(path, _print) {
|
|
565
|
-
var expr, node, ref;
|
|
566
|
-
node = path.node;
|
|
567
|
-
if (!node) {
|
|
568
|
-
return null;
|
|
569
|
-
}
|
|
570
|
-
if (node instanceof Output || node instanceof ControlFlow) {
|
|
571
|
-
expr = (ref = node.expression) != null ? ref.source : void 0;
|
|
572
|
-
if (!(expr && /[\u0000-\u00ff]/.test(expr))) {
|
|
573
|
-
return null;
|
|
574
|
-
}
|
|
575
|
-
null;
|
|
576
|
-
}
|
|
577
|
-
return null;
|
|
578
|
-
};
|
|
579
|
-
|
|
580
|
-
export {
|
|
581
|
-
MergedStatements,
|
|
582
|
-
mergeAllStatements
|
|
583
|
-
};
|