coffeehaml 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +21 -5
  2. package/dist/ast.js +334 -123
  3. package/dist/cli.js +363 -291
  4. package/dist/compiler.js +126 -89
  5. package/dist/emitter.js +771 -736
  6. package/dist/errors.js +5 -2
  7. package/dist/expressions.js +211 -154
  8. package/dist/index.js +22 -4
  9. package/dist/lexer.js +536 -340
  10. package/dist/parser.js +533 -432
  11. package/dist/prettier/coffeescript-formatter.js +309 -0
  12. package/dist/prettier/index.js +105 -0
  13. package/dist/prettier/options.js +232 -0
  14. package/dist/prettier/printer.js +559 -0
  15. package/dist/src/ast.js +338 -0
  16. package/dist/src/cli.js +430 -0
  17. package/dist/src/compiler.js +132 -0
  18. package/dist/src/emitter.js +791 -0
  19. package/dist/src/errors.js +5 -0
  20. package/dist/src/expressions.js +223 -0
  21. package/dist/src/index.js +22 -0
  22. package/dist/src/lexer.js +504 -0
  23. package/dist/src/parser.js +575 -0
  24. package/dist/src/types.js +122 -0
  25. package/dist/src/vite-plugin.js +78 -0
  26. package/dist/types.js +122 -12
  27. package/dist/vite-plugin.js +78 -63
  28. package/docs/examples.md +201 -1
  29. package/docs/grammar.md +90 -1
  30. package/docs/index.html +260 -37
  31. package/package.json +12 -10
  32. package/syntaxes/CoffeeHaml.tmLanguage +815 -868
  33. package/syntaxes/coffeehaml.tmLanguage.json +352 -161
  34. package/dist/ast.d.ts +0 -117
  35. package/dist/ast.d.ts.map +0 -1
  36. package/dist/ast.js.map +0 -1
  37. package/dist/cli.d.ts +0 -3
  38. package/dist/cli.d.ts.map +0 -1
  39. package/dist/cli.js.map +0 -1
  40. package/dist/compiler.d.ts +0 -10
  41. package/dist/compiler.d.ts.map +0 -1
  42. package/dist/compiler.js.map +0 -1
  43. package/dist/emitter.d.ts +0 -4
  44. package/dist/emitter.d.ts.map +0 -1
  45. package/dist/emitter.js.map +0 -1
  46. package/dist/errors.d.ts +0 -3
  47. package/dist/errors.d.ts.map +0 -1
  48. package/dist/errors.js.map +0 -1
  49. package/dist/expressions.d.ts +0 -22
  50. package/dist/expressions.d.ts.map +0 -1
  51. package/dist/expressions.js.map +0 -1
  52. package/dist/index.d.ts +0 -5
  53. package/dist/index.d.ts.map +0 -1
  54. package/dist/index.js.map +0 -1
  55. package/dist/lexer.d.ts +0 -30
  56. package/dist/lexer.d.ts.map +0 -1
  57. package/dist/lexer.js.map +0 -1
  58. package/dist/parser.d.ts +0 -10
  59. package/dist/parser.d.ts.map +0 -1
  60. package/dist/parser.js.map +0 -1
  61. package/dist/types.d.ts +0 -76
  62. package/dist/types.d.ts.map +0 -1
  63. package/dist/types.js.map +0 -1
  64. package/dist/vite-plugin.d.ts +0 -7
  65. package/dist/vite-plugin.d.ts.map +0 -1
  66. package/dist/vite-plugin.js.map +0 -1
package/README.md CHANGED
@@ -76,6 +76,19 @@ expressions like `{onClick: handler}` or `{disabled: !connected}`).
76
76
 
77
77
  ## Usage
78
78
 
79
+ ### TextMate Bundle
80
+
81
+ A complete TextMate bundle ships with the repo at [`CoffeeHaml.tmbundle/`](CoffeeHaml.tmbundle/).
82
+ Install via symlink:
83
+
84
+ ```bash
85
+ ln -sfn "$(pwd)/CoffeeHaml.tmbundle" \
86
+ ~/"Library/Application Support/TextMate/Bundles/CoffeeHaml.tmbundle"
87
+ ```
88
+
89
+ Includes syntax highlighting, Prettier formatting (`⌃⌥F`), compilation commands,
90
+ snippets, and symbol navigation. See [bundle README](CoffeeHaml.tmbundle/README.md).
91
+
79
92
  ### CLI
80
93
 
81
94
  ```bash
@@ -160,23 +173,24 @@ Full grammar: [`docs/grammar.md`](docs/grammar.md)
160
173
 
161
174
  ## Status
162
175
 
163
- **v0.6.0 — production beta.** The compiler pipeline (Lexer → Parser → Emitter)
164
- is complete. Source maps, component wrapping, spread attributes, arrow
165
- continuation (`= items.map (item) ->`), statement/expression continuation,
166
- source-located errors, `coffeehaml init` scaffold, Fast Refresh annotations,
167
- and multi-error parser recovery are all functional.
176
+ **v0.7.0 — production beta.** The compiler pipeline (Lexer → Parser → Emitter)
177
+ is complete. The [Prettier plugin](src/prettier/) provides 16 deactivatable
178
+ formatting options including attribute style preservation, statement merging,
179
+ blank line handling, and CoffeeScript code formatting.
168
180
 
169
181
  | Feature | Status |
170
182
  |---------|--------|
171
183
  | Elements, components, implicit divs | ✅ |
172
184
  | `.class` / `#id` modifiers | ✅ |
173
185
  | `{attr: val}` / `(attr: val)` + spread `{props...}` | ✅ |
186
+ | attribute style preservation (braces/parens/bare) | ✅ |
174
187
  | Inline `= expression` output | ✅ |
175
188
  | `=` expression continuation (indented) | ✅ |
176
189
  | `-` statement continuation (indented) | ✅ |
177
190
  | `- if` / `- unless` / `- else` / `- else if` | ✅ |
178
191
  | `- for item in items` → `.map()` | ✅ |
179
192
  | `- while` | ✅ |
193
+ | Statement merging (`-` blocks) | ✅ |
180
194
  | Haml/HTML comments | ✅ |
181
195
  | `:filter` blocks | ✅ |
182
196
  | Prologue passthrough (`import`, `@decorator`) | ✅ |
@@ -191,6 +205,8 @@ and multi-error parser recovery are all functional.
191
205
  | Source maps | ✅ |
192
206
  | CoffeeScript expression compilation | ✅ (requires peer dep) |
193
207
  | React Fast Refresh annotations | ✅ |
208
+ | Prettier formatter plugin (16 options) | ✅ |
209
+ | TextMate bundle (commands, snippets, syntax) | ✅ |
194
210
 
195
211
  ---
196
212
 
package/dist/ast.js CHANGED
@@ -1,127 +1,338 @@
1
- // ─── Base Node ─────────────────────────────────────────────
2
- export class Node {
3
- constructor(location) {
4
- this.location = location;
5
- }
6
- }
7
- // ─── Expression ────────────────────────────────────────────
8
- /** Wraps a CoffeeScript expression — stores the raw source string.
9
- * Parsed later by the CoffeeScript compiler. */
10
- export class Expression {
11
- constructor(source, parsed) {
12
- this.source = source;
13
- this.parsed = parsed;
1
+ // Generated by CoffeeScript 2.7.0
2
+ // ast.coffee — CoffeeHaml AST node types
3
+ // Mirrors src/ast.ts with CoffeeScript idioms
4
+ import {
5
+ SourceLocation
6
+ } from './types.js';
7
+
8
+ // ─── Node Kinds ────────────────────────────────────────────
9
+ export var NodeKind = ['Document', 'Element', 'ImplicitDiv', 'Text', 'Output', 'ControlFlow', 'Comment', 'Filter', 'Doctype'];
10
+
11
+ export var ControlFlowKind = ['if', 'unless', 'for', 'while', 'else', 'statement'];
12
+
13
+ export var CommentKind = ['haml', 'html'];
14
+
15
+ export var OutputKind = ['escaped', 'unescaped'];
16
+
17
+ export var Node = (function() {
18
+ // ─── Base Node ─────────────────────────────────────────────
19
+ class Node {
20
+ constructor(location1) {
21
+ this.location = location1;
14
22
  }
23
+
24
+ };
25
+
26
+ Node.prototype.kind = null; // set by subclasses
27
+
28
+ Node.prototype.location = null;
29
+
30
+ return Node;
31
+
32
+ }).call(this);
33
+
34
+ export var Expression = (function() {
35
+ // ─── Expression ────────────────────────────────────────────
36
+ class Expression {
37
+ constructor(source, parsed = null) {
38
+ this.source = source;
39
+ this.parsed = parsed;
40
+ }
41
+
15
42
  toString() {
16
- return this.source;
17
- }
18
- }
19
- // ─── Document ──────────────────────────────────────────────
20
- export class Document extends Node {
21
- constructor(children, prologue = [], location) {
22
- super(location);
23
- this.kind = 'Document';
24
- this.prologue = prologue;
25
- this.children = children;
26
- }
27
- }
28
- // ─── Element ───────────────────────────────────────────────
29
- export class Element extends Node {
43
+ return this.source;
44
+ }
45
+
46
+ };
47
+
48
+ Expression.prototype.source = '';
49
+
50
+ Expression.prototype.parsed = null;
51
+
52
+ return Expression;
53
+
54
+ }).call(this);
55
+
56
+ export var Document = (function() {
57
+ // ─── Document ──────────────────────────────────────────────
58
+ class Document extends Node {
59
+ constructor(children, prologue = [], location = null) {
60
+ super(location);
61
+ this.children = children;
62
+ this.prologue = prologue;
63
+ }
64
+
65
+ };
66
+
67
+ Document.prototype.kind = 'Document';
68
+
69
+ Document.prototype.prologue = [];
70
+
71
+ Document.prototype.children = [];
72
+
73
+ return Document;
74
+
75
+ }).call(this);
76
+
77
+ export var Attribute = (function() {
78
+ // ─── Attribute types ───────────────────────────────────────
79
+ class Attribute {
80
+ constructor(name, value, shorthand = false) {
81
+ this.name = name;
82
+ this.value = value;
83
+ this.shorthand = shorthand;
84
+ }
85
+
86
+ };
87
+
88
+ Attribute.prototype.spread = void 0;
89
+
90
+ Attribute.prototype.name = '';
91
+
92
+ Attribute.prototype.value = null; // Expression
93
+
94
+ Attribute.prototype.shorthand = false;
95
+
96
+ return Attribute;
97
+
98
+ }).call(this);
99
+
100
+ export var SpreadAttribute = (function() {
101
+ class SpreadAttribute {
102
+ constructor(expression) {
103
+ this.expression = expression;
104
+ }
105
+
106
+ };
107
+
108
+ SpreadAttribute.prototype.spread = true;
109
+
110
+ SpreadAttribute.prototype.expression = null; // Expression
111
+
112
+ return SpreadAttribute;
113
+
114
+ }).call(this);
115
+
116
+ export var AnyAttribute = null; // Attribute | SpreadAttribute (union, runtime only)
117
+
118
+ export var Element = (function() {
119
+
120
+ // ─── Element ───────────────────────────────────────────────
121
+ class Element extends Node {
30
122
  constructor(tag, opts = {}) {
31
- super(opts.location);
32
- this.kind = 'Element';
33
- this.tag = tag;
34
- this.classes = opts.classes ?? [];
35
- this.id = opts.id ?? null;
36
- this.attributes = opts.attributes ?? [];
37
- this.children = opts.children ?? [];
38
- this.isComponent = opts.isComponent ?? false;
39
- this.isSelfClosing = opts.isSelfClosing ?? false;
40
- }
41
- }
42
- // ─── ImplicitDiv ───────────────────────────────────────────
43
- /** A div created by `.class` or `#id` without an explicit `%tag`. */
44
- export class ImplicitDiv extends Node {
123
+ var ref, ref1, ref2, ref3, ref4, ref5, ref6;
124
+ super(opts.location);
125
+ this.tag = tag;
126
+ this.classes = (ref = opts.classes) != null ? ref : [];
127
+ this.id = (ref1 = opts.id) != null ? ref1 : null;
128
+ this.attributes = (ref2 = opts.attributes) != null ? ref2 : [];
129
+ this.children = (ref3 = opts.children) != null ? ref3 : [];
130
+ this.isComponent = (ref4 = opts.isComponent) != null ? ref4 : false;
131
+ this.isSelfClosing = (ref5 = opts.isSelfClosing) != null ? ref5 : false;
132
+ this.attrStyle = (ref6 = opts.attrStyle) != null ? ref6 : null;
133
+ }
134
+
135
+ };
136
+
137
+ Element.prototype.kind = 'Element';
138
+
139
+ Element.prototype.tag = null; // string | Expression
140
+
141
+ Element.prototype.classes = [];
142
+
143
+ Element.prototype.id = null;
144
+
145
+ Element.prototype.attributes = [];
146
+
147
+ Element.prototype.children = [];
148
+
149
+ Element.prototype.isComponent = false;
150
+
151
+ Element.prototype.isSelfClosing = false;
152
+
153
+ Element.prototype.attrStyle = null; // 'braces' | 'parens' | 'bare' | null
154
+
155
+ return Element;
156
+
157
+ }).call(this);
158
+
159
+ export var ImplicitDiv = (function() {
160
+ // ─── ImplicitDiv ───────────────────────────────────────────
161
+ class ImplicitDiv extends Node {
45
162
  constructor(opts = {}) {
46
- super(opts.location);
47
- this.kind = 'ImplicitDiv';
48
- this.classes = opts.classes ?? [];
49
- this.id = opts.id ?? null;
50
- this.attributes = opts.attributes ?? [];
51
- this.children = opts.children ?? [];
52
- }
53
- }
54
- // ─── Text ──────────────────────────────────────────────────
55
- export class Text extends Node {
56
- constructor(value, location) {
57
- super(location);
58
- this.kind = 'Text';
59
- this.value = value;
60
- }
61
- }
62
- // ─── Output ────────────────────────────────────────────────
63
- export class Output extends Node {
64
- constructor(expression, outputKind, location, children = []) {
65
- super(location);
66
- this.kind = 'Output';
67
- this.expression = expression;
68
- this.outputKind = outputKind;
69
- this.children = children;
70
- }
71
- }
72
- // ─── ControlFlow ───────────────────────────────────────────
73
- export class ControlFlow extends Node {
74
- constructor(controlKind, expression, children = [], next = null, location) {
75
- super(location);
76
- this.kind = 'ControlFlow';
77
- this.controlKind = controlKind;
78
- this.expression = expression;
79
- this.children = children;
80
- this.next = next;
81
- }
82
- /** Returns true if this is a for/while loop. */
83
- get isLoop() {
84
- return this.controlKind === 'for' || this.controlKind === 'while';
85
- }
86
- /** Returns true if this is a conditional. */
87
- get isConditional() {
88
- return this.controlKind === 'if' || this.controlKind === 'unless';
89
- }
90
- /** The chain length including this node. */
91
- get chainLength() {
92
- let n = this;
93
- let count = 0;
94
- while (n) {
95
- count++;
96
- n = n.next;
97
- }
98
- return count;
99
- }
100
- }
101
- // ─── Comment ───────────────────────────────────────────────
102
- export class Comment extends Node {
103
- constructor(commentKind, text, location) {
104
- super(location);
105
- this.kind = 'Comment';
106
- this.commentKind = commentKind;
107
- this.text = text;
108
- }
109
- }
110
- // ─── Filter ────────────────────────────────────────────────
111
- export class Filter extends Node {
112
- constructor(filterName, content, location) {
113
- super(location);
114
- this.kind = 'Filter';
115
- this.filterName = filterName;
116
- this.content = content;
117
- }
118
- }
119
- // ─── Doctype ───────────────────────────────────────────────
120
- export class Doctype extends Node {
121
- constructor(value, location) {
122
- super(location);
123
- this.kind = 'Doctype';
124
- this.value = value;
125
- }
126
- }
127
- //# sourceMappingURL=ast.js.map
163
+ var ref, ref1, ref2, ref3, ref4;
164
+ super(opts.location);
165
+ this.classes = (ref = opts.classes) != null ? ref : [];
166
+ this.id = (ref1 = opts.id) != null ? ref1 : null;
167
+ this.attributes = (ref2 = opts.attributes) != null ? ref2 : [];
168
+ this.children = (ref3 = opts.children) != null ? ref3 : [];
169
+ this.attrStyle = (ref4 = opts.attrStyle) != null ? ref4 : null;
170
+ }
171
+
172
+ };
173
+
174
+ ImplicitDiv.prototype.kind = 'ImplicitDiv';
175
+
176
+ ImplicitDiv.prototype.classes = [];
177
+
178
+ ImplicitDiv.prototype.id = null;
179
+
180
+ ImplicitDiv.prototype.attributes = [];
181
+
182
+ ImplicitDiv.prototype.children = [];
183
+
184
+ ImplicitDiv.prototype.attrStyle = null;
185
+
186
+ return ImplicitDiv;
187
+
188
+ }).call(this);
189
+
190
+ export var Text = (function() {
191
+ // ─── Text ──────────────────────────────────────────────────
192
+ class Text extends Node {
193
+ constructor(value, location = null) {
194
+ super(location);
195
+ this.value = value;
196
+ }
197
+
198
+ };
199
+
200
+ Text.prototype.kind = 'Text';
201
+
202
+ Text.prototype.value = '';
203
+
204
+ return Text;
205
+
206
+ }).call(this);
207
+
208
+ export var Output = (function() {
209
+ // ─── Output ────────────────────────────────────────────────
210
+ class Output extends Node {
211
+ constructor(expression, outputKind, location = null, children = []) {
212
+ super(location);
213
+ this.expression = expression;
214
+ this.outputKind = outputKind;
215
+ this.children = children;
216
+ }
217
+
218
+ };
219
+
220
+ Output.prototype.kind = 'Output';
221
+
222
+ Output.prototype.expression = null; // Expression
223
+
224
+ Output.prototype.outputKind = 'escaped';
225
+
226
+ Output.prototype.children = [];
227
+
228
+ return Output;
229
+
230
+ }).call(this);
231
+
232
+ export var ControlFlow = (function() {
233
+ // ─── ControlFlow ───────────────────────────────────────────
234
+ class ControlFlow extends Node {
235
+ constructor(controlKind, expression, children = [], next = null, location = null) {
236
+ super(location);
237
+ this.controlKind = controlKind;
238
+ this.expression = expression;
239
+ this.children = children;
240
+ this.next = next;
241
+ }
242
+
243
+ isLoop() {
244
+ var ref;
245
+ return (ref = this.controlKind) === 'for' || ref === 'while';
246
+ }
247
+
248
+ isConditional() {
249
+ var ref;
250
+ return (ref = this.controlKind) === 'if' || ref === 'unless';
251
+ }
252
+
253
+ chainLength() {
254
+ var count, n;
255
+ count = 0;
256
+ n = this;
257
+ while (n) {
258
+ count++;
259
+ n = n.next;
260
+ }
261
+ return count;
262
+ }
263
+
264
+ };
265
+
266
+ ControlFlow.prototype.kind = 'ControlFlow';
267
+
268
+ ControlFlow.prototype.controlKind = 'if';
269
+
270
+ ControlFlow.prototype.expression = null;
271
+
272
+ ControlFlow.prototype.children = [];
273
+
274
+ ControlFlow.prototype.next = null;
275
+
276
+ return ControlFlow;
277
+
278
+ }).call(this);
279
+
280
+ export var Comment = (function() {
281
+ // ─── Comment ───────────────────────────────────────────────
282
+ class Comment extends Node {
283
+ constructor(commentKind, text, location = null) {
284
+ super(location);
285
+ this.commentKind = commentKind;
286
+ this.text = text;
287
+ }
288
+
289
+ };
290
+
291
+ Comment.prototype.kind = 'Comment';
292
+
293
+ Comment.prototype.commentKind = 'haml';
294
+
295
+ Comment.prototype.text = '';
296
+
297
+ return Comment;
298
+
299
+ }).call(this);
300
+
301
+ export var Filter = (function() {
302
+ // ─── Filter ────────────────────────────────────────────────
303
+ class Filter extends Node {
304
+ constructor(filterName, content, location = null) {
305
+ super(location);
306
+ this.filterName = filterName;
307
+ this.content = content;
308
+ }
309
+
310
+ };
311
+
312
+ Filter.prototype.kind = 'Filter';
313
+
314
+ Filter.prototype.filterName = '';
315
+
316
+ Filter.prototype.content = '';
317
+
318
+ return Filter;
319
+
320
+ }).call(this);
321
+
322
+ export var Doctype = (function() {
323
+ // ─── Doctype ───────────────────────────────────────────────
324
+ class Doctype extends Node {
325
+ constructor(value, location = null) {
326
+ super(location);
327
+ this.value = value;
328
+ }
329
+
330
+ };
331
+
332
+ Doctype.prototype.kind = 'Doctype';
333
+
334
+ Doctype.prototype.value = '';
335
+
336
+ return Doctype;
337
+
338
+ }).call(this);