coffeehaml 0.1.0

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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +175 -0
  3. package/dist/ast.d.ts +107 -0
  4. package/dist/ast.d.ts.map +1 -0
  5. package/dist/ast.js +125 -0
  6. package/dist/ast.js.map +1 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +53 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/compiler.d.ts +10 -0
  12. package/dist/compiler.d.ts.map +1 -0
  13. package/dist/compiler.js +83 -0
  14. package/dist/compiler.js.map +1 -0
  15. package/dist/emitter.d.ts +4 -0
  16. package/dist/emitter.d.ts.map +1 -0
  17. package/dist/emitter.js +464 -0
  18. package/dist/emitter.js.map +1 -0
  19. package/dist/errors.d.ts +3 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +2 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/expressions.d.ts +10 -0
  24. package/dist/expressions.d.ts.map +1 -0
  25. package/dist/expressions.js +70 -0
  26. package/dist/expressions.js.map +1 -0
  27. package/dist/index.d.ts +4 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +3 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/lexer.d.ts +29 -0
  32. package/dist/lexer.d.ts.map +1 -0
  33. package/dist/lexer.js +315 -0
  34. package/dist/lexer.js.map +1 -0
  35. package/dist/parser.d.ts +4 -0
  36. package/dist/parser.d.ts.map +1 -0
  37. package/dist/parser.js +408 -0
  38. package/dist/parser.js.map +1 -0
  39. package/dist/types.d.ts +65 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +12 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/vite-plugin.d.ts +7 -0
  44. package/dist/vite-plugin.d.ts.map +1 -0
  45. package/dist/vite-plugin.js +44 -0
  46. package/dist/vite-plugin.js.map +1 -0
  47. package/docs/README.md +110 -0
  48. package/docs/architecture.md +619 -0
  49. package/docs/ast.md +413 -0
  50. package/docs/examples.md +719 -0
  51. package/docs/grammar.md +432 -0
  52. package/docs/index.html +129 -0
  53. package/package.json +61 -0
@@ -0,0 +1,432 @@
1
+ # CoffeeHaml Grammar
2
+
3
+ ## Notation
4
+
5
+ ```
6
+ Rule := Alternative1 | Alternative2
7
+ X? := zero or one X
8
+ X* := zero or more X
9
+ X+ := one or more X
10
+ X (sep Y)* := X optionally followed by repeated (Y X)
11
+ 'literal' := literal token
12
+ [class] := character class
13
+ [^class] := negated character class
14
+ ```
15
+
16
+ Whitespace is **significant**: indentation determines nesting. The lexer
17
+ emits `INDENT`/`DEDENT` tokens analogous to Python/CoffeeScript.
18
+
19
+ ---
20
+
21
+ ## Top-Level Production
22
+
23
+ ```
24
+ Document := (Node | BlankLine)* EOF
25
+
26
+ BlankLine := Whitespace* Newline
27
+
28
+ Node := Doctype
29
+ | Comment
30
+ | Filter
31
+ | ControlFlow
32
+ | Output
33
+ | Element
34
+ | ImplicitDiv
35
+ | RawText
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Doctype
41
+
42
+ ```
43
+ Doctype := '!!!' Whitespace? DoctypeValue? Newline
44
+ DoctypeValue := [^\n]+
45
+ ```
46
+
47
+ Examples: `!!!`, `!!! 5`, `!!! html`
48
+
49
+ Emits nothing in React output (React renders into a DOM container, not a full
50
+ document). Retained for Haml compatibility; may emit an HTML comment in
51
+ non-React backends.
52
+
53
+ ---
54
+
55
+ ## Comments
56
+
57
+ ```
58
+ Comment := HamlComment | HTMLComment
59
+
60
+ HamlComment := '-#' Whitespace? CommentText? Newline
61
+ (Indent Node* Dedent)?
62
+ // Removed entirely from output
63
+
64
+ HTMLComment := '/' Whitespace? CommentText? Newline
65
+ (Indent Node* Dedent)?
66
+ // Emitted as <!-- ... --> in development
67
+ ```
68
+
69
+ Haml comments (`-#`) are stripped. HTML comments (`/`) are preserved in the
70
+ AST and emitted as `<!-- ... -->` (wrapped in a raw HTML insertion at
71
+ runtime).
72
+
73
+ ---
74
+
75
+ ## Elements
76
+
77
+ ```
78
+ Element := '%' TagName TagModifiers? AttributeBlock? InlineContent? SelfClose? Newline
79
+ (Indent Node* Dedent)?
80
+
81
+ SelfClose := '/' Whitespace? // preceding newline
82
+ // %br/ → <br />
83
+
84
+ TagName := Identifier (':' Identifier)*
85
+ // Allows SVG: %svg:circle, %math:mi
86
+
87
+ TagModifiers := (ClassModifier | IdModifier)+
88
+ ClassModifier := '.' ClassName
89
+ IdModifier := '#' IdName
90
+
91
+ ClassName := Identifier
92
+ IdName := Identifier
93
+
94
+ Identifier := [a-zA-Z_$] [a-zA-Z0-9_$]*
95
+ ```
96
+
97
+ ### Implicit Div
98
+
99
+ When a line begins with `.class` or `#id` without a `%tag`, a `<div>` is
100
+ implied — exactly as in Haml:
101
+
102
+ ```
103
+ ImplicitDiv := TagModifiers AttributeBlock? InlineContent? Newline
104
+ (Indent Node* Dedent)?
105
+ ```
106
+
107
+ ```
108
+ .container
109
+ %h1 Hello
110
+ ```
111
+
112
+ Is equivalent to:
113
+
114
+ ```
115
+ %div.container
116
+ %h1 Hello
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Attributes
122
+
123
+ ```
124
+ AttributeBlock := AttrBraces | AttrParens
125
+
126
+ AttrBraces := '{' AttrContent? '}'
127
+ AttrParens := '(' AttrContent? ')'
128
+
129
+ AttrContent := Arbitrary CoffeeScript until matching delimiter.
130
+ // Parsed as a CoffeeScript object literal.
131
+ // Comma-optional, supports splats, nested objects,
132
+ // function expressions, destructuring.
133
+ ```
134
+
135
+ The content between `{...}` or `(...)` is extracted verbatim and parsed
136
+ by the CoffeeScript compiler as an object literal. This means:
137
+
138
+ ```haml
139
+ %div{class: "container", id: if active then "on" else "off"}
140
+ %Button{onClick: (e) -> handleClick(e), disabled: !ready}
141
+ %Panel{
142
+ style: {color: "red", margin: 10}
143
+ "aria-label": "Close"
144
+ }
145
+ ```
146
+
147
+ All CoffeeScript expression forms are valid inside attribute values:
148
+ strings, numbers, bools, functions (`->`), conditionals, existential
149
+ operator (`?.`), destructuring, splats (`...`).
150
+
151
+ ---
152
+
153
+ ## Inline Content
154
+
155
+ ```
156
+ InlineContent := Whitespace InlineText? OutputSuffix?
157
+
158
+ InlineText := ( [^\n%=] [^\n%]* )?
159
+ // Text up to newline, '%', or '='
160
+
161
+ OutputSuffix := '=' Whitespace? Expression
162
+ // Mixed text + output on same line:
163
+ // %p Hello, = user.name
164
+ ```
165
+
166
+ Inline content follows the tag and attributes on the same line:
167
+
168
+ ```haml
169
+ %h1 Welcome, = user.name
170
+ %p This is a paragraph with inline content.
171
+ %span.status{class: status} = statusText
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Output
177
+
178
+ ```
179
+ Output := '=' Whitespace? Expression Newline // escaped
180
+ | '==' Whitespace? Expression Newline // unescaped (raw)
181
+ ```
182
+
183
+ `=` inserts the result of a CoffeeScript expression as a child node.
184
+ `==` inserts without HTML escaping (for raw HTML — use cautiously).
185
+
186
+ ```haml
187
+ %h1
188
+ = pageTitle
189
+ %div
190
+ = formatMarkdown(content)
191
+ == user.bioHTML
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Control Flow
197
+
198
+ ```
199
+ ControlFlow := '-' Whitespace? ControlExpression
200
+
201
+ ControlExpression := IfChain
202
+ | UnlessBlock
203
+ | ForBlock
204
+ | WhileBlock
205
+ | CoffeeStatement
206
+
207
+ IfChain := IfBlock (ElseIfBlock | ElseBlock)*
208
+
209
+ IfBlock := 'if' Whitespace Expression Newline
210
+ (Indent Node* Dedent)?
211
+ UnlessBlock := 'unless' Whitespace Expression Newline
212
+ (Indent Node* Dedent)?
213
+ ElseIfBlock := 'else' Whitespace 'if' Whitespace Expression Newline
214
+ (Indent Node* Dedent)?
215
+ ElseBlock := 'else' Newline
216
+ (Indent Node* Dedent)?
217
+
218
+ ForBlock := 'for' Whitespace ForClause Newline
219
+ (Indent Node* Dedent)?
220
+ ForClause := ForIn | ForOf
221
+ ForIn := Pattern 'in' Expression
222
+ ForOf := Pattern 'of' Expression
223
+ Pattern := Identifier (',' Identifier)*
224
+ | '[' Pattern (',' Pattern)* ']'
225
+ | '{' Pattern (',' Pattern)* '}'
226
+
227
+ WhileBlock := 'while' Whitespace Expression Newline
228
+ (Indent Node* Dedent)?
229
+
230
+ CoffeeStatement := Any valid CoffeeScript statement.
231
+ // Arbitrary CoffeeScript code inserted verbatim.
232
+ ```
233
+
234
+ Control flow bodies contain CoffeeHaml nodes (elements, output, nested
235
+ control flow) and compile to JavaScript that produces child arrays.
236
+
237
+ ```haml
238
+ - if user
239
+ %WelcomeBanner{user: user}
240
+ - else
241
+ %LoginPrompt
242
+
243
+ - for item, index in items
244
+ %ItemCard{item: item, key: index}
245
+
246
+ - unless loading
247
+ %Content{data: data}
248
+ ```
249
+
250
+ ### Semantics
251
+
252
+ Control flow bodies compile to expressions that evaluate to arrays of
253
+ React elements:
254
+
255
+ ```
256
+ - for x in xs → ...xs.map((x) => jsx(...))
257
+ - if cond → ...(cond ? [jsx(...)] : [])
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Filters
263
+
264
+ ```
265
+ Filter := ':' FilterName Newline
266
+ (Indent FilterContent Dedent)?
267
+
268
+ FilterName := Identifier
269
+ FilterContent := Lines of text at increased indentation.
270
+ ```
271
+
272
+ Haml-style filters for embedded content. Common filters:
273
+
274
+ | Filter | Purpose |
275
+ |--------|---------|
276
+ | `:css` | Embedded CSS block |
277
+ | `:javascript` | Embedded JS block |
278
+ | `:coffeescript` | Embedded CoffeeScript block |
279
+ | `:markdown` | Markdown content → compiled at build time |
280
+ | `:plain` | Pass-through text (no processing) |
281
+
282
+ ```haml
283
+ %head
284
+ :css
285
+ body { margin: 0; }
286
+ :javascript
287
+ console.log("loaded");
288
+ ```
289
+
290
+ Filters are compiled at build time. `:css` may be extracted or inlined
291
+ depending on configuration.
292
+
293
+ ---
294
+
295
+ ## Raw Text / Passthrough
296
+
297
+ ```
298
+ RawText := [^%#\-=/:!. \t\n] [^\n]* Newline
299
+ // Any line not matching a recognized pattern.
300
+ // Treated as literal text (wrapped in a text node).
301
+ ```
302
+
303
+ Lines that don't start with a special character (`%`, `.`, `#`, `-`, `=`,
304
+ `/`, `:`, `!`) are treated as raw text. This enables "plain text" regions
305
+ within templates.
306
+
307
+ ---
308
+
309
+ ## Expression Reference
310
+
311
+ All `Expression` productions delegate to the CoffeeScript parser. Any
312
+ valid CoffeeScript expression is accepted — the CoffeeHaml parser does not
313
+ re-invent CoffeeScript's expression grammar.
314
+
315
+ Key CoffeeScript expression forms used in CoffeeHaml:
316
+
317
+ ```
318
+ // Functions
319
+ -> // zero-arg arrow
320
+ (x) -> // single-arg arrow
321
+ (x, y) -> // multi-arg arrow
322
+ => // bound arrow (fat arrow)
323
+
324
+ // Conditionals (expression form)
325
+ if cond then a else b
326
+ unless cond then a
327
+
328
+ // Existential
329
+ obj?.prop
330
+ obj?[key]
331
+
332
+ // Comprehensions
333
+ (x for x in arr)
334
+ (x for x in arr when x > 0)
335
+
336
+ // Splats
337
+ arr...
338
+ {obj..., newKey: val}
339
+
340
+ // String interpolation
341
+ "Hello, #{name}"
342
+
343
+ // Heregex
344
+ /// pattern /g
345
+
346
+ // Destructuring
347
+ {a, b} = obj
348
+ [first, rest...] = arr
349
+ ```
350
+
351
+ ---
352
+
353
+ ## Tokens (Lexer)
354
+
355
+ The lexer produces these token types:
356
+
357
+ | Token | Pattern | Notes |
358
+ |-------|---------|-------|
359
+ | `TAG` | `%[a-zA-Z_$]` | Element tag |
360
+ | `CLASS` | `\.[a-zA-Z_$]` | Class modifier |
361
+ | `ID` | `#[a-zA-Z_$]` | ID modifier |
362
+ | `ATTR_OPEN_BRACE` | `{` | Attribute block start |
363
+ | `ATTR_OPEN_PAREN` | `(` | Attribute block start (alt) |
364
+ | `ATTR_CLOSE_BRACE` | `}` | Attribute block end |
365
+ | `ATTR_CLOSE_PAREN` | `)` | Attribute block end (alt) |
366
+ | `OUTPUT` | `=` | Inline expression output |
367
+ | `OUTPUT_RAW` | `==` | Unescaped output |
368
+ | `CONTROL` | `-` | Control flow prefix |
369
+ | `COMMENT` | `-#` | Haml comment |
370
+ | `HTML_COMMENT` | `/` | HTML comment |
371
+ | `FILTER` | `:` | Filter prefix |
372
+ | `DOCTYPE` | `!!!` | Doctype declaration |
373
+ | `SELF_CLOSE` | `/` | Self-closing tag |
374
+ | `TEXT` | (fallback) | Literal text |
375
+ | `NEWLINE` | `\n`, `\r\n` | Line terminator |
376
+ | `INDENT` | (virtual) | Increased indentation |
377
+ | `DEDENT` | (virtual) | Decreased indentation |
378
+ | `WHITESPACE` | `[ \t]` | Spaces/tabs (handled by indenter) |
379
+ | `EOF` | (virtual) | End of file |
380
+
381
+ The lexer is **context-aware** after certain tokens:
382
+ - After `{` or `(`, scan for matching closing delimiter, extracting the
383
+ contained source as a raw string for CoffeeScript parsing.
384
+ - After `=`, scan to end of line for expression source.
385
+ - After `-`, scan to end of line for control expression source.
386
+ - After `:`, scan for filter name.
387
+
388
+ ---
389
+
390
+ ## Indentation Rules
391
+
392
+ CoffeeHaml uses **significant indentation** exactly like Haml and
393
+ CoffeeScript:
394
+
395
+ 1. The first non-blank line of a document establishes the base indent
396
+ level (typically 0).
397
+ 2. A line with **more** indentation than the current line opens a new
398
+ nesting level → emit `INDENT`.
399
+ 3. A line with **less** indentation closes one or more nesting levels →
400
+ emit `DEDENT` for each closed level.
401
+ 4. Tabs and spaces **cannot** be mixed. The indent character is detected
402
+ from the first indented line.
403
+ 5. Blank lines are ignored for indentation purposes.
404
+ 6. Multi-line expressions (attribute blocks, expressions) defer the
405
+ `NEWLINE` token until the construct is complete.
406
+
407
+ ```
408
+ %div // indent 0
409
+ %p // indent 2 → INDENT
410
+ Hello // indent 4 → INDENT
411
+ %p // indent 2 → DEDENT
412
+ World // indent 4 → INDENT
413
+ // EOF → DEDENT, DEDENT
414
+ ```
415
+
416
+ ---
417
+
418
+ ## Precedence of Line-Level Patterns
419
+
420
+ When the lexer encounters the start of a line (after optional whitespace),
421
+ it checks patterns in this order:
422
+
423
+ 1. `!!!` → Doctype
424
+ 2. `%` + identifier → Element
425
+ 3. `.` + identifier → ImplicitDiv (class)
426
+ 4. `#` + identifier → ImplicitDiv (id)
427
+ 5. `-#` → HamlComment
428
+ 6. `/` → HTMLComment (unless followed by `/` or `*`)
429
+ 7. `-` → ControlFlow
430
+ 8. `=` / `==` → Output
431
+ 9. `:` + identifier → Filter
432
+ 10. anything else → RawText
@@ -0,0 +1,129 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>CoffeeHaml — Haml structure. CoffeeScript semantics. React runtime.</title>
7
+ <meta name="description" content="CoffeeHaml compiles Haml-inspired indentation syntax with CoffeeScript expressions directly to React's JSX runtime. Zero runtime. Vite-native. 50% fewer tokens than JSX.">
8
+ <meta property="og:title" content="CoffeeHaml">
9
+ <meta property="og:description" content="Haml structure. CoffeeScript semantics. React runtime. Zero overhead.">
10
+ <meta property="og:url" content="https://dantiel.github.io/CoffeeHaml/">
11
+ <meta property="og:type" content="website">
12
+ <meta name="twitter:card" content="summary">
13
+ <style>
14
+ :root {
15
+ --bg: #1a1a1a;
16
+ --fg: #e0d8c8;
17
+ --accent: #c8a45c;
18
+ --code-bg: #0d0d0d;
19
+ --muted: #888;
20
+ --border: #333;
21
+ }
22
+ * { margin: 0; padding: 0; box-sizing: border-box; }
23
+ body {
24
+ font-family: system-ui, -apple-system, sans-serif;
25
+ background: var(--bg);
26
+ color: var(--fg);
27
+ line-height: 1.6;
28
+ max-width: 800px;
29
+ margin: 0 auto;
30
+ padding: 3rem 1.5rem;
31
+ }
32
+ h1 { font-size: 2.5rem; margin-bottom: 0.25rem; color: var(--accent); }
33
+ .tagline { font-size: 1.2rem; color: var(--muted); margin-bottom: 2rem; font-style: italic; }
34
+ h2 { font-size: 1.4rem; margin: 2rem 0 0.75rem; color: var(--accent); }
35
+ pre {
36
+ background: var(--code-bg);
37
+ padding: 1.25rem;
38
+ border-radius: 8px;
39
+ overflow-x: auto;
40
+ font-family: 'SF Mono', 'Fira Code', monospace;
41
+ font-size: 0.9rem;
42
+ line-height: 1.5;
43
+ border: 1px solid var(--border);
44
+ }
45
+ code { font-family: 'SF Mono', 'Fira Code', monospace; }
46
+ a { color: var(--accent); }
47
+ .btn {
48
+ display: inline-block;
49
+ padding: 0.6rem 1.4rem;
50
+ background: var(--accent);
51
+ color: var(--bg);
52
+ border-radius: 6px;
53
+ text-decoration: none;
54
+ font-weight: 600;
55
+ margin-right: 0.75rem;
56
+ margin-top: 0.5rem;
57
+ }
58
+ .btn-outline {
59
+ background: transparent;
60
+ color: var(--accent);
61
+ border: 1px solid var(--accent);
62
+ }
63
+ .stats {
64
+ display: flex;
65
+ gap: 2rem;
66
+ margin: 1.5rem 0;
67
+ }
68
+ .stat { text-align: center; }
69
+ .stat-num { font-size: 2rem; color: var(--accent); font-weight: bold; }
70
+ .stat-label { font-size: 0.8rem; color: var(--muted); text-transform: uppercase; }
71
+ footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--border); color: var(--muted); font-size: 0.85rem; }
72
+ </style>
73
+ </head>
74
+ <body>
75
+ <h1>☕ CoffeeHaml</h1>
76
+ <p class="tagline">Haml structure. CoffeeScript semantics. React runtime. <br>Zero overhead. 50% fewer tokens than JSX.</p>
77
+
78
+ <p>
79
+ <a class="btn" href="https://github.com/dantiel/CoffeeHaml">GitHub</a>
80
+ <a class="btn btn-outline" href="https://www.npmjs.com/package/coffeehaml">npm</a>
81
+ </p>
82
+
83
+ <div class="stats">
84
+ <div class="stat"><div class="stat-num">0</div><div class="stat-label">Runtime KB</div></div>
85
+ <div class="stat"><div class="stat-num">~50%</div><div class="stat-label">Token Reduction</div></div>
86
+ <div class="stat"><div class="stat-num">3</div><div class="stat-label">Extensions</div></div>
87
+ </div>
88
+
89
+ <h2>Write This</h2>
90
+ <pre>.card
91
+ %h2= post.title
92
+ - if showBody
93
+ .body= post.content</pre>
94
+
95
+ <h2>Get This</h2>
96
+ <pre>import { jsx, jsxs } from "react/jsx-runtime";
97
+
98
+ jsxs("div", { className: "card" },
99
+ jsx("h2", null, post.title),
100
+ ...(showBody ? [
101
+ jsx("div", { className: "body" }, post.content)
102
+ ] : [])
103
+ );</pre>
104
+
105
+ <h2>Quick Start</h2>
106
+ <pre>npm install coffeehaml</pre>
107
+
108
+ <h3>Vite</h3>
109
+ <pre>// vite.config.ts
110
+ import coffeehaml from 'coffeehaml/vite';
111
+
112
+ export default {
113
+ plugins: [coffeehaml()],
114
+ };</pre>
115
+
116
+ <h3>CLI</h3>
117
+ <pre>npx coffeehaml compile app.coffeehaml</pre>
118
+
119
+ <p style="margin-top:1.5rem;">
120
+ Supports <code>.chaml</code>, <code>.coffeehaml</code>, and <code>.cohaml</code> extensions.
121
+ Full documentation on <a href="https://github.com/dantiel/CoffeeHaml">GitHub</a>.
122
+ </p>
123
+
124
+ <footer>
125
+ MIT License · Created by <a href="https://github.com/dantiel">dantiel</a><br>
126
+ CoffeeHaml replaces JSX — not React. No runtime. No framework. Just a compiler.
127
+ </footer>
128
+ </body>
129
+ </html>
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "coffeehaml",
3
+ "version": "0.1.0",
4
+ "description": "Haml structure. CoffeeScript semantics. React runtime.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "dantiel <dantiel@proton.me>",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/dantiel/CoffeeHaml.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/dantiel/CoffeeHaml/issues"
14
+ },
15
+ "homepage": "https://github.com/dantiel/CoffeeHaml#readme",
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "bin": {
19
+ "coffeehaml": "./dist/cli.js"
20
+ },
21
+ "exports": {
22
+ ".": "./dist/index.js",
23
+ "./vite": "./dist/vite-plugin.js"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE",
29
+ "docs"
30
+ ],
31
+ "scripts": {
32
+ "build": "tsc",
33
+ "dev": "tsc --watch",
34
+ "test": "vitest run",
35
+ "test:watch": "vitest",
36
+ "prepublishOnly": "npm run build"
37
+ },
38
+ "keywords": [
39
+ "coffeehaml",
40
+ "haml",
41
+ "coffeescript",
42
+ "react",
43
+ "jsx",
44
+ "compiler",
45
+ "vite",
46
+ "template-language",
47
+ "indentation-based",
48
+ "jsx-runtime"
49
+ ],
50
+ "peerDependencies": {
51
+ "coffeescript": "^2.7.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^20.0.0",
55
+ "typescript": "^5.3.0",
56
+ "vitest": "^1.0.0"
57
+ },
58
+ "dependencies": {
59
+ "source-map": "^0.7.4"
60
+ }
61
+ }