coffeehaml 0.7.1 → 0.7.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.
- package/dist/prettier/printer.js +4 -4
- package/package.json +2 -2
- package/dist/src/ast.js +0 -338
- package/dist/src/cli.js +0 -430
- package/dist/src/compiler.js +0 -132
- package/dist/src/emitter.js +0 -791
- package/dist/src/errors.js +0 -5
- package/dist/src/expressions.js +0 -223
- package/dist/src/index.js +0 -22
- package/dist/src/lexer.js +0 -504
- package/dist/src/parser.js +0 -575
- package/dist/src/types.js +0 -122
- package/dist/src/vite-plugin.js +0 -78
package/dist/src/parser.js
DELETED
|
@@ -1,575 +0,0 @@
|
|
|
1
|
-
// Generated by CoffeeScript 2.7.0
|
|
2
|
-
// parser.coffee — recursive descent parser
|
|
3
|
-
// Converts token stream into AST
|
|
4
|
-
var ParserState, findColon, isElse, parseAttributeBlock, parseBlock, parseComment, parseControlFlow, parseControlKind, parseDoctype, parseElement, parseFilter, parseHtmlComment, parseImplicitDiv, parseNode, parseOutput, parseText, splitAttributePairs, stripControlKeyword;
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
TokenType
|
|
8
|
-
} from './lexer.js';
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
Document,
|
|
12
|
-
Element,
|
|
13
|
-
ImplicitDiv,
|
|
14
|
-
Text,
|
|
15
|
-
Output,
|
|
16
|
-
ControlFlow,
|
|
17
|
-
Comment,
|
|
18
|
-
Filter,
|
|
19
|
-
Doctype,
|
|
20
|
-
Expression,
|
|
21
|
-
Attribute,
|
|
22
|
-
SpreadAttribute
|
|
23
|
-
} from './ast.js';
|
|
24
|
-
|
|
25
|
-
import {
|
|
26
|
-
CompileError
|
|
27
|
-
} from './types.js';
|
|
28
|
-
|
|
29
|
-
ParserState = (function() {
|
|
30
|
-
// ─── Parser State ──────────────────────────────────────────
|
|
31
|
-
class ParserState {
|
|
32
|
-
constructor(tokens1, filename1 = null) {
|
|
33
|
-
this.tokens = tokens1;
|
|
34
|
-
this.filename = filename1;
|
|
35
|
-
this.pos = 0;
|
|
36
|
-
this.errors = [];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
current() {
|
|
40
|
-
if (this.pos < this.tokens.length) {
|
|
41
|
-
return this.tokens[this.pos];
|
|
42
|
-
} else {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
peek() {
|
|
48
|
-
if (this.pos + 1 < this.tokens.length) {
|
|
49
|
-
return this.tokens[this.pos + 1];
|
|
50
|
-
} else {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
advance() {
|
|
56
|
-
var token;
|
|
57
|
-
token = this.current();
|
|
58
|
-
this.pos++;
|
|
59
|
-
return token;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
error(message, code = 'PARSE_ERROR') {
|
|
63
|
-
var ref;
|
|
64
|
-
return this.errors.push(new CompileError(message, 'parser', code, (ref = this.current()) != null ? ref.location : void 0));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
expect(type) {
|
|
68
|
-
var ref, token;
|
|
69
|
-
token = this.current();
|
|
70
|
-
if (!token || token.type !== type) {
|
|
71
|
-
this.error(`Expected ${type} but got ${(ref = token != null ? token.type : void 0) != null ? ref : 'EOF'}`, 'UNEXPECTED_TOKEN');
|
|
72
|
-
this.recover();
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
return this.advance();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
recover() {
|
|
79
|
-
var t;
|
|
80
|
-
while (this.current()) {
|
|
81
|
-
t = this.current().type;
|
|
82
|
-
if (t === TokenType.TAG || t === TokenType.CLASS || t === TokenType.ID || t === TokenType.OUTPUT || t === TokenType.OUTPUT_UNESC || t === TokenType.CONTROL || t === TokenType.FILTER || t === TokenType.COMMENT || t === TokenType.HTML_COMMENT || t === TokenType.DOCTYPE || t === TokenType.INDENT || t === TokenType.DEDENT) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
this.advance();
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
skip(type) {
|
|
90
|
-
var ref;
|
|
91
|
-
if (((ref = this.current()) != null ? ref.type : void 0) === type) {
|
|
92
|
-
this.advance();
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
isAt(type) {
|
|
99
|
-
var ref;
|
|
100
|
-
return ((ref = this.current()) != null ? ref.type : void 0) === type;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
ParserState.prototype.tokens = [];
|
|
106
|
-
|
|
107
|
-
ParserState.prototype.pos = 0;
|
|
108
|
-
|
|
109
|
-
ParserState.prototype.filename = null;
|
|
110
|
-
|
|
111
|
-
ParserState.prototype.errors = [];
|
|
112
|
-
|
|
113
|
-
return ParserState;
|
|
114
|
-
|
|
115
|
-
}).call(this);
|
|
116
|
-
|
|
117
|
-
// ─── Public API ────────────────────────────────────────────
|
|
118
|
-
export var parse = function(tokens, filename = null) {
|
|
119
|
-
var children, prologue, ref, state;
|
|
120
|
-
state = new ParserState(tokens, filename);
|
|
121
|
-
prologue = [];
|
|
122
|
-
while (((ref = state.current()) != null ? ref.type : void 0) === TokenType.PROLOGUE) {
|
|
123
|
-
prologue.push(state.advance().value);
|
|
124
|
-
}
|
|
125
|
-
children = parseBlock(state);
|
|
126
|
-
return {
|
|
127
|
-
document: new Document(children, prologue),
|
|
128
|
-
errors: state.errors
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// ─── Block Parsing ─────────────────────────────────────────
|
|
133
|
-
parseBlock = function(state) {
|
|
134
|
-
var node, nodes;
|
|
135
|
-
nodes = [];
|
|
136
|
-
while (state.current() && state.current().type !== TokenType.DEDENT) {
|
|
137
|
-
node = parseNode(state);
|
|
138
|
-
if (node) {
|
|
139
|
-
nodes.push(node);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return nodes;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
// ─── Node Dispatcher ───────────────────────────────────────
|
|
146
|
-
parseNode = function(state) {
|
|
147
|
-
var token;
|
|
148
|
-
token = state.current();
|
|
149
|
-
if (!token) {
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
switch (token.type) {
|
|
153
|
-
case TokenType.TAG:
|
|
154
|
-
return parseElement(state);
|
|
155
|
-
case TokenType.CLASS:
|
|
156
|
-
return parseImplicitDiv(state);
|
|
157
|
-
case TokenType.ID:
|
|
158
|
-
return parseImplicitDiv(state);
|
|
159
|
-
case TokenType.OUTPUT:
|
|
160
|
-
return parseOutput(state);
|
|
161
|
-
case TokenType.OUTPUT_UNESC:
|
|
162
|
-
return parseOutput(state);
|
|
163
|
-
case TokenType.CONTROL:
|
|
164
|
-
return parseControlFlow(state);
|
|
165
|
-
case TokenType.COMMENT:
|
|
166
|
-
return parseComment(state);
|
|
167
|
-
case TokenType.HTML_COMMENT:
|
|
168
|
-
return parseHtmlComment(state);
|
|
169
|
-
case TokenType.FILTER:
|
|
170
|
-
return parseFilter(state);
|
|
171
|
-
case TokenType.DOCTYPE:
|
|
172
|
-
return parseDoctype(state);
|
|
173
|
-
case TokenType.TEXT:
|
|
174
|
-
return parseText(state);
|
|
175
|
-
case TokenType.INDENT:
|
|
176
|
-
state.advance();
|
|
177
|
-
return null;
|
|
178
|
-
case TokenType.DEDENT:
|
|
179
|
-
return null;
|
|
180
|
-
case TokenType.NEWLINE:
|
|
181
|
-
state.advance();
|
|
182
|
-
return null;
|
|
183
|
-
default:
|
|
184
|
-
state.advance();
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
// ─── Element ───────────────────────────────────────────────
|
|
190
|
-
parseElement = function(state) {
|
|
191
|
-
var attrStyle, attributes, children, classes, id, isComponent, isSelfClosing, ref, ref1, ref2, tag, tagToken, textToken, tok;
|
|
192
|
-
tagToken = state.expect(TokenType.TAG);
|
|
193
|
-
if (!tagToken) {
|
|
194
|
-
return new Element('div', {
|
|
195
|
-
location: (ref = state.current()) != null ? ref.location : void 0
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
tag = tagToken.value;
|
|
199
|
-
isComponent = /^[A-Z]/.test(tag);
|
|
200
|
-
classes = [];
|
|
201
|
-
id = null;
|
|
202
|
-
attributes = [];
|
|
203
|
-
isSelfClosing = false;
|
|
204
|
-
attrStyle = null;
|
|
205
|
-
while (state.current() && ((ref1 = state.current().type) === TokenType.CLASS || ref1 === TokenType.ID || ref1 === TokenType.ATTRS_BRACE || ref1 === TokenType.ATTRS_PAREN || ref1 === TokenType.SELF_CLOSE)) {
|
|
206
|
-
tok = state.current();
|
|
207
|
-
switch (tok.type) {
|
|
208
|
-
case TokenType.CLASS:
|
|
209
|
-
state.advance();
|
|
210
|
-
classes.push(tok.value);
|
|
211
|
-
break;
|
|
212
|
-
case TokenType.ID:
|
|
213
|
-
state.advance();
|
|
214
|
-
id = tok.value;
|
|
215
|
-
break;
|
|
216
|
-
case TokenType.ATTRS_BRACE:
|
|
217
|
-
state.advance();
|
|
218
|
-
attributes.push(...parseAttributeBlock(tok.value, '{}', tagToken.location));
|
|
219
|
-
attrStyle = 'braces';
|
|
220
|
-
break;
|
|
221
|
-
case TokenType.ATTRS_PAREN:
|
|
222
|
-
state.advance();
|
|
223
|
-
attributes.push(...parseAttributeBlock(tok.value, '()', tagToken.location));
|
|
224
|
-
attrStyle = 'parens';
|
|
225
|
-
break;
|
|
226
|
-
case TokenType.SELF_CLOSE:
|
|
227
|
-
state.advance();
|
|
228
|
-
isSelfClosing = true;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
// Parse inline text or output
|
|
232
|
-
children = [];
|
|
233
|
-
if (state.current() && !isSelfClosing) {
|
|
234
|
-
switch (state.current().type) {
|
|
235
|
-
case TokenType.OUTPUT:
|
|
236
|
-
tok = state.advance();
|
|
237
|
-
children.push(new Output(new Expression(tok.value), 'escaped', tok.location));
|
|
238
|
-
break;
|
|
239
|
-
case TokenType.OUTPUT_UNESC:
|
|
240
|
-
tok = state.advance();
|
|
241
|
-
children.push(new Output(new Expression(tok.value), 'unescaped', tok.location));
|
|
242
|
-
break;
|
|
243
|
-
case TokenType.TEXT:
|
|
244
|
-
textToken = state.advance();
|
|
245
|
-
children.push(new Text(textToken.value, textToken.location));
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
// Parse child block if INDENT follows
|
|
249
|
-
if (((ref2 = state.current()) != null ? ref2.type : void 0) === TokenType.INDENT && !isSelfClosing) {
|
|
250
|
-
state.advance();
|
|
251
|
-
children = children.concat(parseBlock(state));
|
|
252
|
-
state.expect(TokenType.DEDENT);
|
|
253
|
-
}
|
|
254
|
-
return new Element(tag, {
|
|
255
|
-
classes,
|
|
256
|
-
id,
|
|
257
|
-
attributes,
|
|
258
|
-
children,
|
|
259
|
-
isComponent,
|
|
260
|
-
isSelfClosing,
|
|
261
|
-
attrStyle,
|
|
262
|
-
location: tagToken.location
|
|
263
|
-
});
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
// ─── ImplicitDiv ───────────────────────────────────────────
|
|
267
|
-
parseImplicitDiv = function(state) {
|
|
268
|
-
var attrStyle, attributes, children, classes, firstToken, id, ref, ref1, ref2, textToken, tok;
|
|
269
|
-
classes = [];
|
|
270
|
-
id = null;
|
|
271
|
-
attributes = [];
|
|
272
|
-
attrStyle = null;
|
|
273
|
-
firstToken = state.current();
|
|
274
|
-
while (state.current() && ((ref = state.current().type) === TokenType.CLASS || ref === TokenType.ID || ref === TokenType.ATTRS_BRACE || ref === TokenType.ATTRS_PAREN)) {
|
|
275
|
-
tok = state.current();
|
|
276
|
-
switch (tok.type) {
|
|
277
|
-
case TokenType.CLASS:
|
|
278
|
-
state.advance();
|
|
279
|
-
classes.push(tok.value);
|
|
280
|
-
break;
|
|
281
|
-
case TokenType.ID:
|
|
282
|
-
state.advance();
|
|
283
|
-
id = tok.value;
|
|
284
|
-
break;
|
|
285
|
-
case TokenType.ATTRS_BRACE:
|
|
286
|
-
state.advance();
|
|
287
|
-
attributes.push(...parseAttributeBlock(tok.value, '{}', firstToken.location));
|
|
288
|
-
attrStyle = 'braces';
|
|
289
|
-
break;
|
|
290
|
-
case TokenType.ATTRS_PAREN:
|
|
291
|
-
state.advance();
|
|
292
|
-
attributes.push(...parseAttributeBlock(tok.value, '()', firstToken.location));
|
|
293
|
-
attrStyle = 'parens';
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
// Parse inline text
|
|
297
|
-
children = [];
|
|
298
|
-
if (((ref1 = state.current()) != null ? ref1.type : void 0) === TokenType.TEXT) {
|
|
299
|
-
textToken = state.advance();
|
|
300
|
-
children.push(new Text(textToken.value, textToken.location));
|
|
301
|
-
}
|
|
302
|
-
// Parse child block
|
|
303
|
-
if (((ref2 = state.current()) != null ? ref2.type : void 0) === TokenType.INDENT) {
|
|
304
|
-
state.advance();
|
|
305
|
-
children = children.concat(parseBlock(state));
|
|
306
|
-
state.expect(TokenType.DEDENT);
|
|
307
|
-
}
|
|
308
|
-
return new ImplicitDiv({
|
|
309
|
-
classes,
|
|
310
|
-
id,
|
|
311
|
-
attributes,
|
|
312
|
-
children,
|
|
313
|
-
attrStyle,
|
|
314
|
-
location: firstToken.location
|
|
315
|
-
});
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
// ─── Output ────────────────────────────────────────────────
|
|
319
|
-
parseOutput = function(state) {
|
|
320
|
-
var children, expr, outputKind, ref, token;
|
|
321
|
-
token = state.current();
|
|
322
|
-
outputKind = token.type === TokenType.OUTPUT ? 'escaped' : 'unescaped';
|
|
323
|
-
state.advance();
|
|
324
|
-
expr = new Expression(token.value);
|
|
325
|
-
children = [];
|
|
326
|
-
if (((ref = state.current()) != null ? ref.type : void 0) === TokenType.INDENT) {
|
|
327
|
-
state.advance();
|
|
328
|
-
children = parseBlock(state);
|
|
329
|
-
state.expect(TokenType.DEDENT);
|
|
330
|
-
}
|
|
331
|
-
return new Output(expr, outputKind, token.location, children);
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
// ─── Control Flow ──────────────────────────────────────────
|
|
335
|
-
parseControlFlow = function(state) {
|
|
336
|
-
var children, controlKind, expr, exprSource, next, nextSource, ref, ref1, ref2, source, token;
|
|
337
|
-
token = state.expect(TokenType.CONTROL);
|
|
338
|
-
if (!token) {
|
|
339
|
-
return new ControlFlow('statement', new Expression(''), [], null, (ref = state.current()) != null ? ref.location : void 0);
|
|
340
|
-
}
|
|
341
|
-
source = token.value;
|
|
342
|
-
// Normalize "else if" → "if"
|
|
343
|
-
if (/^\s*else\s+if\b/.test(source)) {
|
|
344
|
-
source = source.replace(/^\s*else\s+/, '');
|
|
345
|
-
}
|
|
346
|
-
controlKind = parseControlKind(source);
|
|
347
|
-
exprSource = stripControlKeyword(source, controlKind);
|
|
348
|
-
expr = new Expression(exprSource);
|
|
349
|
-
children = [];
|
|
350
|
-
if (((ref1 = state.current()) != null ? ref1.type : void 0) === TokenType.INDENT) {
|
|
351
|
-
state.advance();
|
|
352
|
-
children = parseBlock(state);
|
|
353
|
-
state.expect(TokenType.DEDENT);
|
|
354
|
-
}
|
|
355
|
-
// Check for chained else / else if
|
|
356
|
-
next = null;
|
|
357
|
-
if (((ref2 = state.current()) != null ? ref2.type : void 0) === TokenType.CONTROL) {
|
|
358
|
-
nextSource = state.current().value.trimStart();
|
|
359
|
-
if (isElse(nextSource)) {
|
|
360
|
-
next = parseControlFlow(state);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
return new ControlFlow(controlKind, expr, children, next, token.location);
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
parseControlKind = function(source) {
|
|
367
|
-
var keyword;
|
|
368
|
-
keyword = source.trimStart().split(/\s+/)[0];
|
|
369
|
-
switch (keyword) {
|
|
370
|
-
case 'if':
|
|
371
|
-
return 'if';
|
|
372
|
-
case 'unless':
|
|
373
|
-
return 'unless';
|
|
374
|
-
case 'for':
|
|
375
|
-
return 'for';
|
|
376
|
-
case 'while':
|
|
377
|
-
return 'while';
|
|
378
|
-
case 'else':
|
|
379
|
-
return 'else';
|
|
380
|
-
default:
|
|
381
|
-
return 'statement';
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
|
|
385
|
-
stripControlKeyword = function(source, kind) {
|
|
386
|
-
var keyword, re, trimmed;
|
|
387
|
-
trimmed = source.trimStart();
|
|
388
|
-
if (kind === 'statement') {
|
|
389
|
-
return trimmed;
|
|
390
|
-
}
|
|
391
|
-
keyword = kind === 'else' ? 'else' : kind;
|
|
392
|
-
re = new RegExp(`^\\s*${keyword}\\b\\s*`);
|
|
393
|
-
return trimmed.replace(re, '');
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
isElse = function(source) {
|
|
397
|
-
return /^\s*else\b/.test(source);
|
|
398
|
-
};
|
|
399
|
-
|
|
400
|
-
// ─── Comments ──────────────────────────────────────────────
|
|
401
|
-
parseComment = function(state) {
|
|
402
|
-
var token;
|
|
403
|
-
token = state.expect(TokenType.COMMENT);
|
|
404
|
-
if (!token) {
|
|
405
|
-
return new Comment('haml', '');
|
|
406
|
-
}
|
|
407
|
-
return new Comment('haml', token.value, token.location);
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
parseHtmlComment = function(state) {
|
|
411
|
-
var token;
|
|
412
|
-
token = state.expect(TokenType.HTML_COMMENT);
|
|
413
|
-
if (!token) {
|
|
414
|
-
return new Comment('html', '');
|
|
415
|
-
}
|
|
416
|
-
return new Comment('html', token.value, token.location);
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
// ─── Filter ────────────────────────────────────────────────
|
|
420
|
-
parseFilter = function(state) {
|
|
421
|
-
var content, filterName, lines, parts, tok, token;
|
|
422
|
-
token = state.expect(TokenType.FILTER);
|
|
423
|
-
if (!token) {
|
|
424
|
-
return new Filter('', '');
|
|
425
|
-
}
|
|
426
|
-
parts = token.value.split('\n');
|
|
427
|
-
filterName = parts[0];
|
|
428
|
-
content = parts.slice(1).join('\n');
|
|
429
|
-
// Consume indented body
|
|
430
|
-
if (state.isAt(TokenType.INDENT)) {
|
|
431
|
-
state.advance();
|
|
432
|
-
lines = [];
|
|
433
|
-
while (state.current() && !state.isAt(TokenType.DEDENT)) {
|
|
434
|
-
tok = state.current();
|
|
435
|
-
if (tok.type === TokenType.TEXT) {
|
|
436
|
-
lines.push(tok.value);
|
|
437
|
-
state.advance();
|
|
438
|
-
} else if (tok.type === TokenType.NEWLINE) {
|
|
439
|
-
state.advance();
|
|
440
|
-
} else {
|
|
441
|
-
break;
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
if (state.isAt(TokenType.DEDENT)) {
|
|
445
|
-
state.advance();
|
|
446
|
-
}
|
|
447
|
-
if (lines.length > 0) {
|
|
448
|
-
content = content ? content + '\n' + lines.join('\n') : lines.join('\n');
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
return new Filter(filterName, content, token.location);
|
|
452
|
-
};
|
|
453
|
-
|
|
454
|
-
// ─── Doctype ───────────────────────────────────────────────
|
|
455
|
-
parseDoctype = function(state) {
|
|
456
|
-
var token;
|
|
457
|
-
token = state.expect(TokenType.DOCTYPE);
|
|
458
|
-
if (!token) {
|
|
459
|
-
return new Doctype('html');
|
|
460
|
-
}
|
|
461
|
-
return new Doctype(token.value || 'html', token.location);
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
// ─── Text ──────────────────────────────────────────────────
|
|
465
|
-
parseText = function(state) {
|
|
466
|
-
var token;
|
|
467
|
-
token = state.expect(TokenType.TEXT);
|
|
468
|
-
if (!token) {
|
|
469
|
-
return new Text('');
|
|
470
|
-
}
|
|
471
|
-
return new Text(token.value, token.location);
|
|
472
|
-
};
|
|
473
|
-
|
|
474
|
-
// ─── Attribute Block Parser ────────────────────────────────
|
|
475
|
-
parseAttributeBlock = function(source, _style, _location = null) {
|
|
476
|
-
var attrs, cleanName, colonIdx, expr, j, len, name, pair, pairs, trimmed, value;
|
|
477
|
-
attrs = [];
|
|
478
|
-
if (!source.trim()) {
|
|
479
|
-
return attrs;
|
|
480
|
-
}
|
|
481
|
-
pairs = splitAttributePairs(source);
|
|
482
|
-
for (j = 0, len = pairs.length; j < len; j++) {
|
|
483
|
-
pair = pairs[j];
|
|
484
|
-
trimmed = pair.trim();
|
|
485
|
-
if (!trimmed) {
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
// Spread: props... or ...props
|
|
489
|
-
if (trimmed.endsWith('...')) {
|
|
490
|
-
expr = trimmed.slice(0, -3).trim();
|
|
491
|
-
if (expr) {
|
|
492
|
-
attrs.push(new SpreadAttribute(new Expression(expr)));
|
|
493
|
-
}
|
|
494
|
-
continue;
|
|
495
|
-
}
|
|
496
|
-
if (trimmed.startsWith('...')) {
|
|
497
|
-
expr = trimmed.slice(3).trim();
|
|
498
|
-
if (expr) {
|
|
499
|
-
attrs.push(new SpreadAttribute(new Expression(expr)));
|
|
500
|
-
}
|
|
501
|
-
continue;
|
|
502
|
-
}
|
|
503
|
-
colonIdx = findColon(trimmed);
|
|
504
|
-
if (colonIdx === -1) {
|
|
505
|
-
// Shorthand: {foo} → foo={foo}
|
|
506
|
-
attrs.push(new Attribute(trimmed, new Expression(trimmed), true));
|
|
507
|
-
} else {
|
|
508
|
-
name = trimmed.slice(0, colonIdx).trim();
|
|
509
|
-
value = trimmed.slice(colonIdx + 1).trim();
|
|
510
|
-
cleanName = name.replace(/^['"]|['"]$/g, '');
|
|
511
|
-
attrs.push(new Attribute(cleanName, new Expression(value), false));
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
return attrs;
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
splitAttributePairs = function(source) {
|
|
518
|
-
var ch, depth, i, q, result, start;
|
|
519
|
-
result = [];
|
|
520
|
-
depth = 0;
|
|
521
|
-
start = 0;
|
|
522
|
-
i = 0;
|
|
523
|
-
while (i < source.length) {
|
|
524
|
-
ch = source[i];
|
|
525
|
-
if (ch === '{' || ch === '[' || ch === '(') {
|
|
526
|
-
depth++;
|
|
527
|
-
} else if (ch === '}' || ch === ']' || ch === ')') {
|
|
528
|
-
depth--;
|
|
529
|
-
} else if ((ch === '"' || ch === "'") && depth === 0) {
|
|
530
|
-
q = ch;
|
|
531
|
-
i++;
|
|
532
|
-
while (i < source.length && source[i] !== q) {
|
|
533
|
-
if (source[i] === '\\') {
|
|
534
|
-
i++;
|
|
535
|
-
}
|
|
536
|
-
i++;
|
|
537
|
-
}
|
|
538
|
-
} else if (ch === ',' && depth === 0) {
|
|
539
|
-
result.push(source.slice(start, i));
|
|
540
|
-
start = i + 1;
|
|
541
|
-
}
|
|
542
|
-
i++;
|
|
543
|
-
}
|
|
544
|
-
result.push(source.slice(start));
|
|
545
|
-
return result;
|
|
546
|
-
};
|
|
547
|
-
|
|
548
|
-
findColon = function(source) {
|
|
549
|
-
var ch, depth, i, q;
|
|
550
|
-
depth = 0;
|
|
551
|
-
i = 0;
|
|
552
|
-
while (i < source.length) {
|
|
553
|
-
ch = source[i];
|
|
554
|
-
if (ch === '{' || ch === '[' || ch === '(') {
|
|
555
|
-
depth++;
|
|
556
|
-
} else if (ch === '}' || ch === ']' || ch === ')') {
|
|
557
|
-
depth--;
|
|
558
|
-
} else if ((ch === '"' || ch === "'") && depth === 0) {
|
|
559
|
-
q = ch;
|
|
560
|
-
i++;
|
|
561
|
-
while (i < source.length && source[i] !== q) {
|
|
562
|
-
if (source[i] === '\\') {
|
|
563
|
-
i++;
|
|
564
|
-
}
|
|
565
|
-
i++;
|
|
566
|
-
}
|
|
567
|
-
} else if (ch === ':' && depth === 0) {
|
|
568
|
-
return i;
|
|
569
|
-
}
|
|
570
|
-
i++;
|
|
571
|
-
}
|
|
572
|
-
return -1;
|
|
573
|
-
};
|
|
574
|
-
|
|
575
|
-
// ParseResult shape: { document: Document, errors: CompileError[] }
|
package/dist/src/types.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
// Generated by CoffeeScript 2.7.0
|
|
2
|
-
// types.coffee — shared types for CoffeeHaml compiler
|
|
3
|
-
// CoffeeScript version — interfaces become plain classes with JSDoc
|
|
4
|
-
|
|
5
|
-
// ─── Source Location ───────────────────────────────────────
|
|
6
|
-
export var SourcePosition = (function() {
|
|
7
|
-
class SourcePosition {
|
|
8
|
-
constructor(line, column) {
|
|
9
|
-
this.line = line;
|
|
10
|
-
this.column = column;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
SourcePosition.prototype.line = 0; // 0-based
|
|
16
|
-
|
|
17
|
-
SourcePosition.prototype.column = 0; // 0-based
|
|
18
|
-
|
|
19
|
-
return SourcePosition;
|
|
20
|
-
|
|
21
|
-
}).call(this);
|
|
22
|
-
|
|
23
|
-
export var SourceLocation = (function() {
|
|
24
|
-
class SourceLocation {
|
|
25
|
-
constructor({start, end, offset, length, file}) {
|
|
26
|
-
this.start = start;
|
|
27
|
-
this.end = end;
|
|
28
|
-
this.offset = offset;
|
|
29
|
-
this.length = length;
|
|
30
|
-
this.file = file;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
SourceLocation.prototype.start = null; // SourcePosition
|
|
36
|
-
|
|
37
|
-
SourceLocation.prototype.end = null; // SourcePosition
|
|
38
|
-
|
|
39
|
-
SourceLocation.prototype.offset = 0;
|
|
40
|
-
|
|
41
|
-
SourceLocation.prototype.length = 0;
|
|
42
|
-
|
|
43
|
-
SourceLocation.prototype.file = null;
|
|
44
|
-
|
|
45
|
-
return SourceLocation;
|
|
46
|
-
|
|
47
|
-
}).call(this);
|
|
48
|
-
|
|
49
|
-
export var CompileError = (function() {
|
|
50
|
-
// ─── Compile Error ─────────────────────────────────────────
|
|
51
|
-
class CompileError extends Error {
|
|
52
|
-
constructor(message, phase, code, location = null, hint = null) {
|
|
53
|
-
super(message);
|
|
54
|
-
this.phase = phase;
|
|
55
|
-
this.code = code;
|
|
56
|
-
this.location = location;
|
|
57
|
-
this.hint = hint;
|
|
58
|
-
this.name = 'CompileError';
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
CompileError.prototype.location = null;
|
|
64
|
-
|
|
65
|
-
CompileError.prototype.phase = '';
|
|
66
|
-
|
|
67
|
-
CompileError.prototype.hint = null;
|
|
68
|
-
|
|
69
|
-
CompileError.prototype.code = '';
|
|
70
|
-
|
|
71
|
-
return CompileError;
|
|
72
|
-
|
|
73
|
-
}).call(this);
|
|
74
|
-
|
|
75
|
-
export var CompileWarning = (function() {
|
|
76
|
-
// ─── Compile Warning ───────────────────────────────────────
|
|
77
|
-
class CompileWarning {
|
|
78
|
-
constructor(message1, location = null, phase = '') {
|
|
79
|
-
this.message = message1;
|
|
80
|
-
this.location = location;
|
|
81
|
-
this.phase = phase;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
CompileWarning.prototype.message = '';
|
|
87
|
-
|
|
88
|
-
CompileWarning.prototype.location = null;
|
|
89
|
-
|
|
90
|
-
CompileWarning.prototype.phase = '';
|
|
91
|
-
|
|
92
|
-
return CompileWarning;
|
|
93
|
-
|
|
94
|
-
}).call(this);
|
|
95
|
-
|
|
96
|
-
// ─── Compiler Options ──────────────────────────────────────
|
|
97
|
-
export var CompilerOptions = null; // plain object, documented below
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// CompilerOptions shape:
|
|
101
|
-
// filename?: string
|
|
102
|
-
// sourceMap?: boolean
|
|
103
|
-
// jsxRuntime?: string
|
|
104
|
-
// jsxImportSource?: string
|
|
105
|
-
// filters?: { [name: string]: (content: string, filterName: string) => string }
|
|
106
|
-
// wrap?: 'none' | 'component' | 'observer' | string[]
|
|
107
|
-
// componentName?: string
|
|
108
|
-
|
|
109
|
-
// ─── Compile Result ────────────────────────────────────────
|
|
110
|
-
export var CompileResult = null; // { code, sourceMap?, errors: CompileError[], warnings: CompileWarning[] }
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
// ─── Emitter Options ───────────────────────────────────────
|
|
114
|
-
export var EmitterOptions = null; // same shape as CompilerOptions without export-related fields
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// ─── Emit Result ───────────────────────────────────────────
|
|
118
|
-
export var EmitResult = null; // { code, sourceMap?, warnings: CompileWarning[] }
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// ─── Filter Handler ────────────────────────────────────────
|
|
122
|
-
export var FilterHandler = null; // (content: string, filterName: string) => string
|