coffeehaml 0.5.0 → 0.7.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.
- package/README.md +13 -4
- package/dist/ast.d.ts +6 -0
- package/dist/ast.d.ts.map +1 -1
- package/dist/ast.js +2 -0
- package/dist/ast.js.map +1 -1
- package/dist/cli.js +178 -2
- package/dist/cli.js.map +1 -1
- package/dist/compiler.js +8 -10
- package/dist/compiler.js.map +1 -1
- package/dist/emitter.js +81 -5
- package/dist/emitter.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/parser.d.ts +7 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +46 -4
- package/dist/parser.js.map +1 -1
- package/dist/prettier/coffeescript-formatter.d.ts +31 -0
- package/dist/prettier/coffeescript-formatter.d.ts.map +1 -0
- package/dist/prettier/coffeescript-formatter.js +409 -0
- package/dist/prettier/coffeescript-formatter.js.map +1 -0
- package/dist/prettier/index.d.ts +8 -0
- package/dist/prettier/index.d.ts.map +1 -0
- package/dist/prettier/index.js +69 -0
- package/dist/prettier/index.js.map +1 -0
- package/dist/prettier/options.d.ts +10 -0
- package/dist/prettier/options.d.ts.map +1 -0
- package/dist/prettier/options.js +200 -0
- package/dist/prettier/options.js.map +1 -0
- package/dist/prettier/printer.d.ts +45 -0
- package/dist/prettier/printer.d.ts.map +1 -0
- package/dist/prettier/printer.js +652 -0
- package/dist/prettier/printer.js.map +1 -0
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +11 -1
- package/dist/vite-plugin.js.map +1 -1
- package/docs/examples.md +201 -1
- package/docs/grammar.md +90 -1
- package/docs/index.html +260 -37
- package/package.json +10 -3
- package/syntaxes/CoffeeHaml.tmLanguage +815 -868
- package/syntaxes/coffeehaml.tmLanguage.json +352 -161
package/docs/index.html
CHANGED
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
--code-bg: #0d0d0d;
|
|
19
19
|
--muted: #888;
|
|
20
20
|
--border: #333;
|
|
21
|
+
--green: #8fb573;
|
|
22
|
+
--blue: #7ea9d0;
|
|
23
|
+
--red: #d4848c;
|
|
24
|
+
--purple: #b999d6;
|
|
21
25
|
}
|
|
22
26
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
23
27
|
body {
|
|
@@ -25,25 +29,28 @@
|
|
|
25
29
|
background: var(--bg);
|
|
26
30
|
color: var(--fg);
|
|
27
31
|
line-height: 1.6;
|
|
28
|
-
max-width:
|
|
32
|
+
max-width: 840px;
|
|
29
33
|
margin: 0 auto;
|
|
30
34
|
padding: 3rem 1.5rem;
|
|
31
35
|
}
|
|
32
36
|
h1 { font-size: 2.5rem; margin-bottom: 0.25rem; color: var(--accent); }
|
|
33
|
-
.tagline { font-size: 1.2rem; color: var(--muted); margin-bottom:
|
|
34
|
-
h2 { font-size: 1.4rem; margin:
|
|
37
|
+
.tagline { font-size: 1.2rem; color: var(--muted); margin-bottom: 1.5rem; font-style: italic; }
|
|
38
|
+
h2 { font-size: 1.4rem; margin: 2.5rem 0 0.75rem; color: var(--accent); border-bottom: 1px solid var(--border); padding-bottom: 0.4rem; }
|
|
39
|
+
h3 { font-size: 1.1rem; margin: 1.5rem 0 0.5rem; color: var(--fg); }
|
|
35
40
|
pre {
|
|
36
41
|
background: var(--code-bg);
|
|
37
42
|
padding: 1.25rem;
|
|
38
43
|
border-radius: 8px;
|
|
39
44
|
overflow-x: auto;
|
|
40
45
|
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
41
|
-
font-size: 0.
|
|
42
|
-
line-height: 1.
|
|
46
|
+
font-size: 0.85rem;
|
|
47
|
+
line-height: 1.55;
|
|
43
48
|
border: 1px solid var(--border);
|
|
49
|
+
margin: 0.75rem 0;
|
|
44
50
|
}
|
|
45
|
-
code { font-family: 'SF Mono', 'Fira Code', monospace; }
|
|
46
|
-
a { color: var(--accent); }
|
|
51
|
+
code { font-family: 'SF Mono', 'Fira Code', monospace; font-size: 0.9em; }
|
|
52
|
+
a { color: var(--accent); text-decoration: none; }
|
|
53
|
+
a:hover { text-decoration: underline; }
|
|
47
54
|
.btn {
|
|
48
55
|
display: inline-block;
|
|
49
56
|
padding: 0.6rem 1.4rem;
|
|
@@ -55,75 +62,291 @@
|
|
|
55
62
|
margin-right: 0.75rem;
|
|
56
63
|
margin-top: 0.5rem;
|
|
57
64
|
}
|
|
65
|
+
.btn:hover { background: #d4b06a; text-decoration: none; }
|
|
58
66
|
.btn-outline {
|
|
59
67
|
background: transparent;
|
|
60
68
|
color: var(--accent);
|
|
61
69
|
border: 1px solid var(--accent);
|
|
62
70
|
}
|
|
71
|
+
.btn-outline:hover { background: rgba(200,164,92,0.1); text-decoration: none; }
|
|
63
72
|
.stats {
|
|
64
73
|
display: flex;
|
|
65
74
|
gap: 2rem;
|
|
66
75
|
margin: 1.5rem 0;
|
|
76
|
+
flex-wrap: wrap;
|
|
67
77
|
}
|
|
68
|
-
.stat { text-align: center; }
|
|
78
|
+
.stat { text-align: center; min-width: 90px; }
|
|
69
79
|
.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
|
-
|
|
80
|
+
.stat-label { font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
|
|
81
|
+
.badge {
|
|
82
|
+
display: inline-block;
|
|
83
|
+
padding: 0.15rem 0.5rem;
|
|
84
|
+
border-radius: 4px;
|
|
85
|
+
font-size: 0.7rem;
|
|
86
|
+
font-weight: 600;
|
|
87
|
+
text-transform: uppercase;
|
|
88
|
+
letter-spacing: 0.05em;
|
|
89
|
+
background: #2a3a1a;
|
|
90
|
+
color: var(--green);
|
|
91
|
+
border: 1px solid rgba(143,181,115,0.3);
|
|
92
|
+
}
|
|
93
|
+
.badge-new {
|
|
94
|
+
background: #2a2010;
|
|
95
|
+
color: var(--accent);
|
|
96
|
+
border: 1px solid rgba(200,164,92,0.3);
|
|
97
|
+
}
|
|
98
|
+
table {
|
|
99
|
+
width: 100%;
|
|
100
|
+
border-collapse: collapse;
|
|
101
|
+
margin: 1rem 0;
|
|
102
|
+
font-size: 0.9rem;
|
|
103
|
+
}
|
|
104
|
+
th, td {
|
|
105
|
+
padding: 0.5rem 0.75rem;
|
|
106
|
+
text-align: left;
|
|
107
|
+
border-bottom: 1px solid var(--border);
|
|
108
|
+
}
|
|
109
|
+
th { color: var(--accent); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; }
|
|
110
|
+
td:last-child { text-align: right; }
|
|
111
|
+
.two-col {
|
|
112
|
+
display: grid;
|
|
113
|
+
grid-template-columns: 1fr 1fr;
|
|
114
|
+
gap: 1.5rem;
|
|
115
|
+
margin: 1rem 0;
|
|
116
|
+
}
|
|
117
|
+
@media (max-width: 640px) {
|
|
118
|
+
.two-col { grid-template-columns: 1fr; }
|
|
119
|
+
body { padding: 1.5rem 1rem; }
|
|
120
|
+
h1 { font-size: 2rem; }
|
|
121
|
+
}
|
|
122
|
+
.example-label {
|
|
123
|
+
font-size: 0.75rem;
|
|
124
|
+
text-transform: uppercase;
|
|
125
|
+
letter-spacing: 0.08em;
|
|
126
|
+
color: var(--muted);
|
|
127
|
+
margin-bottom: 0.25rem;
|
|
128
|
+
margin-top: 1.25rem;
|
|
129
|
+
}
|
|
130
|
+
.example-label:first-of-type { margin-top: 0; }
|
|
131
|
+
.hl { color: var(--muted); } /* comment */
|
|
132
|
+
.kw { color: var(--red); } /* keyword */
|
|
133
|
+
.tg { color: var(--blue); } /* tag */
|
|
134
|
+
.at { color: var(--green); } /* attr */
|
|
135
|
+
.st { color: var(--green); } /* string */
|
|
136
|
+
.fn { color: var(--purple); } /* func */
|
|
137
|
+
.cl { color: var(--blue); } /* class */
|
|
138
|
+
.op { color: var(--accent); } /* operator */
|
|
139
|
+
footer { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--border); color: var(--muted); font-size: 0.85rem; }
|
|
140
|
+
.feature-grid {
|
|
141
|
+
display: grid;
|
|
142
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
143
|
+
gap: 0.5rem 1.5rem;
|
|
144
|
+
margin: 1rem 0;
|
|
145
|
+
}
|
|
146
|
+
.feature-grid li { font-size: 0.9rem; margin-bottom: 0.3rem; }
|
|
147
|
+
blockquote {
|
|
148
|
+
border-left: 2px solid var(--accent);
|
|
149
|
+
padding-left: 1rem;
|
|
150
|
+
margin: 1rem 0;
|
|
151
|
+
color: var(--muted);
|
|
152
|
+
font-style: italic;
|
|
153
|
+
}
|
|
72
154
|
</style>
|
|
73
155
|
</head>
|
|
74
156
|
<body>
|
|
75
|
-
|
|
76
|
-
<
|
|
157
|
+
|
|
158
|
+
<h1>☕ CoffeeHaml</h1>
|
|
159
|
+
<p class="tagline">Haml structure. CoffeeScript semantics. React runtime.<br>Zero overhead. 50% fewer tokens than JSX.</p>
|
|
77
160
|
|
|
78
161
|
<p>
|
|
79
162
|
<a class="btn" href="https://github.com/dantiel/CoffeeHaml">GitHub</a>
|
|
80
163
|
<a class="btn btn-outline" href="https://www.npmjs.com/package/coffeehaml">npm</a>
|
|
164
|
+
<a class="btn btn-outline" href="#quick-start">Quick Start</a>
|
|
81
165
|
</p>
|
|
82
166
|
|
|
83
167
|
<div class="stats">
|
|
84
168
|
<div class="stat"><div class="stat-num">0</div><div class="stat-label">Runtime KB</div></div>
|
|
85
169
|
<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">
|
|
170
|
+
<div class="stat"><div class="stat-num">52 kB</div><div class="stat-label">Compiler Size</div></div>
|
|
171
|
+
<div class="stat"><div class="stat-num">25+</div><div class="stat-label">Features</div></div>
|
|
87
172
|
</div>
|
|
88
173
|
|
|
174
|
+
<!-- EXAMPLE -->
|
|
89
175
|
<h2>Write This</h2>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
176
|
+
|
|
177
|
+
<pre><span class="hl">-# Dashboard.chaml</span>
|
|
178
|
+
<span class="hl">import</span> { observer } <span class="hl">from</span> <span class="st">'mobx-react-lite'</span>
|
|
179
|
+
<span class="hl">import</span> { useMemo } <span class="hl">from</span> <span class="st">'react'</span>
|
|
180
|
+
|
|
181
|
+
<span class="hl">@observer</span>
|
|
182
|
+
<span class="tg">%div</span>.<span class="cl">page</span>
|
|
183
|
+
<span class="tg">%header</span>
|
|
184
|
+
<span class="tg">%h1</span>= <span class="st">"Welcome, #{user.name}"</span>
|
|
185
|
+
<span class="tg">%nav</span>{ <span class="at">role</span>: <span class="st">'primary'</span> }
|
|
186
|
+
- <span class="kw">for</span> link <span class="kw">in</span> navLinks
|
|
187
|
+
<span class="tg">%a</span>{ <span class="at">href</span>: link.url }= link.label
|
|
188
|
+
|
|
189
|
+
<span class="tg">%main</span>
|
|
190
|
+
.dashboard-grid
|
|
191
|
+
= widgets.map <span class="fn">(widget) -></span>
|
|
192
|
+
<span class="tg">%Widget</span>{ widget, <span class="at">onDelete</span>: <span class="fn">-></span> handleDelete(widget.id) }
|
|
193
|
+
|
|
194
|
+
<span class="tg">%aside</span>
|
|
195
|
+
- <span class="kw">if</span> alerts.length > 0
|
|
196
|
+
.alert-banner{ <span class="at">role</span>: <span class="st">'alert'</span> }
|
|
197
|
+
- <span class="kw">for</span> alert <span class="kw">in</span> alerts
|
|
198
|
+
<span class="tg">%p</span>= alert.message
|
|
199
|
+
- <span class="kw">else</span>
|
|
200
|
+
<span class="tg">%p</span>.<span class="cl">empty-state</span> No alerts — all clear.</pre>
|
|
94
201
|
|
|
95
202
|
<h2>Get This</h2>
|
|
96
|
-
<pre>import { jsx, jsxs } from "react/jsx-runtime";
|
|
97
203
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
jsx("div", { className: "body" }, post.content)
|
|
102
|
-
] : [])
|
|
103
|
-
);</pre>
|
|
204
|
+
<pre><span class="hl">import</span> { jsx, jsxs, Fragment } <span class="hl">from</span> <span class="st">"react/jsx-runtime"</span>;
|
|
205
|
+
<span class="hl">import</span> { observer } <span class="hl">from</span> <span class="st">"mobx-react-lite"</span>;
|
|
206
|
+
<span class="hl">import</span> { useMemo } <span class="hl">from</span> <span class="st">"react"</span>;
|
|
104
207
|
|
|
105
|
-
|
|
106
|
-
<
|
|
208
|
+
<span class="hl">export default</span> observer(<span class="kw">function</span> <span class="fn">Dashboard</span>(props) {
|
|
209
|
+
<span class="kw">return</span> jsxs(<span class="st">"div"</span>, { <span class="at">className</span>: <span class="st">"page"</span> },
|
|
210
|
+
jsxs(<span class="st">"header"</span>, <span class="kw">null</span>,
|
|
211
|
+
jsx(<span class="st">"h1"</span>, <span class="kw">null</span>, <span class="st">"Welcome, "</span> + user.name),
|
|
212
|
+
jsx(<span class="st">"nav"</span>, { <span class="at">role</span>: <span class="st">"primary"</span> },
|
|
213
|
+
...navLinks.map(link => jsx(<span class="st">"a"</span>, { <span class="at">href</span>: link.url }, link.label))
|
|
214
|
+
)
|
|
215
|
+
),
|
|
216
|
+
jsxs(<span class="st">"main"</span>, <span class="kw">null</span>,
|
|
217
|
+
jsx(<span class="st">"div"</span>, { <span class="at">className</span>: <span class="st">"dashboard-grid"</span> },
|
|
218
|
+
...widgets.map(widget => jsx(Widget, { widget, <span class="at">onDelete</span>: () => handleDelete(widget.id) }))
|
|
219
|
+
),
|
|
220
|
+
jsx(<span class="st">"aside"</span>, <span class="kw">null</span>,
|
|
221
|
+
...(alerts.length > 0 ? [
|
|
222
|
+
jsxs(<span class="st">"div"</span>, { <span class="at">className</span>: <span class="st">"alert-banner"</span>, <span class="at">role</span>: <span class="st">"alert"</span> },
|
|
223
|
+
...alerts.map(alert => jsx(<span class="st">"p"</span>, <span class="kw">null</span>, alert.message))
|
|
224
|
+
)
|
|
225
|
+
] : [
|
|
226
|
+
jsx(<span class="st">"p"</span>, { <span class="at">className</span>: <span class="st">"empty-state"</span> }, <span class="st">"No alerts — all clear."</span>)
|
|
227
|
+
])
|
|
228
|
+
)
|
|
229
|
+
)
|
|
230
|
+
);
|
|
231
|
+
});</pre>
|
|
107
232
|
|
|
108
|
-
|
|
109
|
-
<
|
|
110
|
-
import coffeehaml from 'coffeehaml/vite';
|
|
233
|
+
<!-- QUICK START -->
|
|
234
|
+
<h2 id="quick-start">Quick Start</h2>
|
|
111
235
|
|
|
112
|
-
|
|
113
|
-
|
|
236
|
+
<h3>Scaffold a project <span class="badge badge-new">v0.6.0</span></h3>
|
|
237
|
+
<pre>$ <span class="op">npx coffeehaml init</span>
|
|
238
|
+
|
|
239
|
+
☕ CoffeeHaml init — project scaffold
|
|
240
|
+
Working directory: /my-react-app
|
|
241
|
+
Detected: package.json, Vite, TypeScript
|
|
242
|
+
|
|
243
|
+
Add coffeehaml as devDependency? <span class="op">(Y/n):</span>
|
|
244
|
+
Configure Vite plugin? <span class="op">(Y/n):</span>
|
|
245
|
+
Create sample .chaml component? <span class="op">(Y/n):</span>
|
|
246
|
+
Add build/watch scripts to package.json? <span class="op">(Y/n):</span></pre>
|
|
247
|
+
|
|
248
|
+
<h3>Or install manually</h3>
|
|
249
|
+
<pre>$ npm install coffeehaml</pre>
|
|
250
|
+
|
|
251
|
+
<div class="two-col">
|
|
252
|
+
<div>
|
|
253
|
+
<h3>Vite</h3>
|
|
254
|
+
<pre><span class="hl">// vite.config.ts</span>
|
|
255
|
+
<span class="hl">import</span> coffeehaml <span class="hl">from</span> <span class="st">'coffeehaml/vite'</span>;
|
|
256
|
+
|
|
257
|
+
<span class="hl">export default</span> {
|
|
258
|
+
<span class="at">plugins</span>: [coffeehaml()],
|
|
114
259
|
};</pre>
|
|
260
|
+
</div>
|
|
261
|
+
<div>
|
|
262
|
+
<h3>CLI</h3>
|
|
263
|
+
<pre><span class="hl"># One-shot compile</span>
|
|
264
|
+
$ npx coffeehaml compile app.chaml <span class="op">-o</span> app.js
|
|
115
265
|
|
|
116
|
-
|
|
117
|
-
|
|
266
|
+
<span class="hl"># Watch mode (v0.5.0)</span>
|
|
267
|
+
$ npx coffeehaml watch src/ <span class="op">--wrap</span> component</pre>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
118
270
|
|
|
119
|
-
<p style="margin-top:
|
|
271
|
+
<p style="margin-top:0.5rem; color:var(--muted);">
|
|
120
272
|
Supports <code>.chaml</code>, <code>.coffeehaml</code>, and <code>.cohaml</code> extensions.
|
|
121
|
-
|
|
273
|
+
</p>
|
|
274
|
+
|
|
275
|
+
<!-- FEATURES -->
|
|
276
|
+
<h2>All Features</h2>
|
|
277
|
+
|
|
278
|
+
<table>
|
|
279
|
+
<tr><th>Feature</th><th>Status</th></tr>
|
|
280
|
+
<tr><td>Elements, components, implicit divs</td><td>✓</td></tr>
|
|
281
|
+
<tr><td><code>.class</code> / <code>#id</code> shorthand</td><td>✓</td></tr>
|
|
282
|
+
<tr><td><code>{attr: val}</code> + spread <code>{props...}</code></td><td>✓</td></tr>
|
|
283
|
+
<tr><td>Inline <code>= expression</code> output</td><td>✓</td></tr>
|
|
284
|
+
<tr><td><code>=</code> expression continuation <span class="badge badge-new">v0.5.0</span></td><td>✓</td></tr>
|
|
285
|
+
<tr><td><code>= expr -></code> arrow continuation <span class="badge badge-new">v0.6.0</span></td><td>✓</td></tr>
|
|
286
|
+
<tr><td><code>-</code> statement continuation <span class="badge">v0.4.2</span></td><td>✓</td></tr>
|
|
287
|
+
<tr><td><code>- if</code> / <code>- unless</code> / <code>- else</code> / <code>- else if</code></td><td>✓</td></tr>
|
|
288
|
+
<tr><td><code>- for item in items</code> → <code>.map()</code></td><td>✓</td></tr>
|
|
289
|
+
<tr><td><code>- while</code> loops</td><td>✓</td></tr>
|
|
290
|
+
<tr><td>Optional chaining <code>props.gyro?.roll</code></td><td>✓</td></tr>
|
|
291
|
+
<tr><td>Haml / HTML comments</td><td>✓</td></tr>
|
|
292
|
+
<tr><td><code>:filter</code> plugins (markdown, asciidoc)</td><td>✓</td></tr>
|
|
293
|
+
<tr><td>Prologue passthrough (<code>import</code>, <code>@decorator</code>)</td><td>✓</td></tr>
|
|
294
|
+
<tr><td>Component / HOC wrapping</td><td>✓</td></tr>
|
|
295
|
+
<tr><td>Vite plugin (HMR + Fast Refresh <span class="badge badge-new">v0.6.0</span>)</td><td>✓</td></tr>
|
|
296
|
+
<tr><td>CLI <code>--wrap</code> flag <span class="badge badge-new">v0.5.0</span></td><td>✓</td></tr>
|
|
297
|
+
<tr><td>CLI <code>watch</code> mode <span class="badge badge-new">v0.5.0</span></td><td>✓</td></tr>
|
|
298
|
+
<tr><td>CLI <code>init</code> scaffold <span class="badge badge-new">v0.6.0</span></td><td>✓</td></tr>
|
|
299
|
+
<tr><td>Source-located error messages <span class="badge badge-new">v0.5.0</span></td><td>✓</td></tr>
|
|
300
|
+
<tr><td>Multi-error parser recovery <span class="badge badge-new">v0.6.0</span></td><td>✓</td></tr>
|
|
301
|
+
<tr><td>Source maps</td><td>✓</td></tr>
|
|
302
|
+
<tr><td>CoffeeScript expression compilation</td><td>✓ (peer dep)</td></tr>
|
|
303
|
+
</table>
|
|
304
|
+
|
|
305
|
+
<!-- NODE API -->
|
|
306
|
+
<h2>Node API</h2>
|
|
307
|
+
|
|
308
|
+
<pre><span class="hl">import</span> { compile, compileFile } <span class="hl">from</span> <span class="st">'coffeehaml'</span>;
|
|
309
|
+
|
|
310
|
+
<span class="kw">const</span> result = compile(<span class="st">'%div Hello'</span>, {
|
|
311
|
+
<span class="at">filename</span>: <span class="st">'app.chaml'</span>,
|
|
312
|
+
<span class="at">wrap</span>: <span class="st">'component'</span>, <span class="hl">// 'none' | 'component' | 'observer' | string[]</span>
|
|
313
|
+
<span class="at">sourceMap</span>: <span class="kw">true</span>,
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
console.log(result.code);
|
|
317
|
+
<span class="hl">// → import { jsx } from "react/jsx-runtime";</span>
|
|
318
|
+
<span class="hl">// export default function App(props) {</span>
|
|
319
|
+
<span class="hl">// return jsx("div", null, "Hello");</span>
|
|
320
|
+
<span class="hl">// }</span></pre>
|
|
321
|
+
|
|
322
|
+
<!-- DESIGN -->
|
|
323
|
+
<h2>Design Principles</h2>
|
|
324
|
+
|
|
325
|
+
<ul class="feature-grid">
|
|
326
|
+
<li><strong>Zero runtime</strong> — compiler is the only artifact</li>
|
|
327
|
+
<li><strong>CoffeeScript-centric</strong> — not a Ruby-Haml port; CoffeeScript's semantics, not Ruby's</li>
|
|
328
|
+
<li><strong>Emitter-agnostic AST</strong> — future backends (Solid, Vue, Mithril)</li>
|
|
329
|
+
<li><strong>Source-located errors</strong> — line/column hints on every failure</li>
|
|
330
|
+
<li><strong>Source maps</strong> — debug in <code>.chaml</code>, not generated JS</li>
|
|
331
|
+
<li><strong>Incremental compilation</strong> — Vite HMR + Fast Refresh</li>
|
|
332
|
+
<li><strong>Multi-error recovery</strong> — parser collects all errors, not just first</li>
|
|
333
|
+
<li><strong>No invention</strong> — reuse Haml and CoffeeScript conventions</li>
|
|
334
|
+
</ul>
|
|
335
|
+
|
|
336
|
+
<!-- DOCS -->
|
|
337
|
+
<h2>Documentation</h2>
|
|
338
|
+
<p>
|
|
339
|
+
<a href="grammar.md">Grammar Reference</a> ·
|
|
340
|
+
<a href="examples.md">Syntax Examples</a> ·
|
|
341
|
+
<a href="architecture.md">Architecture</a> ·
|
|
342
|
+
<a href="ast.md">AST Specification</a> ·
|
|
343
|
+
<a href="https://github.com/dantiel/CoffeeHaml">GitHub README</a>
|
|
122
344
|
</p>
|
|
123
345
|
|
|
124
346
|
<footer>
|
|
125
|
-
MIT License
|
|
126
|
-
|
|
347
|
+
<strong>CoffeeHaml v0.6.0</strong> — MIT License · Created by <a href="https://github.com/dantiel">dantiel</a><br>
|
|
348
|
+
Haml structure. CoffeeScript semantics. React runtime. Nothing more, nothing less.
|
|
127
349
|
</footer>
|
|
350
|
+
|
|
128
351
|
</body>
|
|
129
352
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coffeehaml",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Haml structure. CoffeeScript semantics. React runtime. Zero runtime. Vite-native.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"./vite": {
|
|
27
27
|
"types": "./dist/vite-plugin.d.ts",
|
|
28
28
|
"default": "./dist/vite-plugin.js"
|
|
29
|
+
},
|
|
30
|
+
"./prettier": {
|
|
31
|
+
"types": "./dist/prettier/index.d.ts",
|
|
32
|
+
"default": "./dist/prettier/index.js"
|
|
29
33
|
}
|
|
30
34
|
},
|
|
31
35
|
"files": [
|
|
@@ -64,7 +68,8 @@
|
|
|
64
68
|
"dev": "tsc --watch",
|
|
65
69
|
"test": "vitest run",
|
|
66
70
|
"test:watch": "vitest",
|
|
67
|
-
"prepublishOnly": "npm run build"
|
|
71
|
+
"prepublishOnly": "npm run build",
|
|
72
|
+
"fmt": "prettier --write --plugin=. **/*.chaml"
|
|
68
73
|
},
|
|
69
74
|
"keywords": [
|
|
70
75
|
"coffeehaml",
|
|
@@ -79,10 +84,12 @@
|
|
|
79
84
|
"jsx-runtime"
|
|
80
85
|
],
|
|
81
86
|
"peerDependencies": {
|
|
82
|
-
"coffeescript": "^2.7.0"
|
|
87
|
+
"coffeescript": "^2.7.0",
|
|
88
|
+
"prettier": "^3.9.6"
|
|
83
89
|
},
|
|
84
90
|
"devDependencies": {
|
|
85
91
|
"@types/node": "^20.0.0",
|
|
92
|
+
"prettier": "*",
|
|
86
93
|
"typescript": "^5.3.0",
|
|
87
94
|
"vitest": "^1.0.0"
|
|
88
95
|
},
|