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.
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/ast.d.ts +107 -0
- package/dist/ast.d.ts.map +1 -0
- package/dist/ast.js +125 -0
- package/dist/ast.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +53 -0
- package/dist/cli.js.map +1 -0
- package/dist/compiler.d.ts +10 -0
- package/dist/compiler.d.ts.map +1 -0
- package/dist/compiler.js +83 -0
- package/dist/compiler.js.map +1 -0
- package/dist/emitter.d.ts +4 -0
- package/dist/emitter.d.ts.map +1 -0
- package/dist/emitter.js +464 -0
- package/dist/emitter.js.map +1 -0
- package/dist/errors.d.ts +3 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +2 -0
- package/dist/errors.js.map +1 -0
- package/dist/expressions.d.ts +10 -0
- package/dist/expressions.d.ts.map +1 -0
- package/dist/expressions.js +70 -0
- package/dist/expressions.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/lexer.d.ts +29 -0
- package/dist/lexer.d.ts.map +1 -0
- package/dist/lexer.js +315 -0
- package/dist/lexer.js.map +1 -0
- package/dist/parser.d.ts +4 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +408 -0
- package/dist/parser.js.map +1 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/vite-plugin.d.ts +7 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +44 -0
- package/dist/vite-plugin.js.map +1 -0
- package/docs/README.md +110 -0
- package/docs/architecture.md +619 -0
- package/docs/ast.md +413 -0
- package/docs/examples.md +719 -0
- package/docs/grammar.md +432 -0
- package/docs/index.html +129 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 CoffeeHaml contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# ☕ CoffeeHaml
|
|
2
|
+
|
|
3
|
+
> **Haml structure. CoffeeScript semantics. React runtime.**
|
|
4
|
+
|
|
5
|
+
CoffeeHaml is a compiler that transforms an indentation-based, Haml-inspired
|
|
6
|
+
authoring language into JavaScript using the modern React JSX runtime
|
|
7
|
+
(`react/jsx-runtime`). It is **not** a UI framework. React remains the runtime.
|
|
8
|
+
CoffeeHaml replaces JSX — with 50% fewer tokens.
|
|
9
|
+
|
|
10
|
+
```haml
|
|
11
|
+
%SimulatorPanel{source: "gyro", width: "auto", height: 500}
|
|
12
|
+
- for servo in servos
|
|
13
|
+
%ServoGraph{channel: servo.channel}
|
|
14
|
+
%ServoPanel{servo: servo}
|
|
15
|
+
|
|
16
|
+
%Button{onClick: save, disabled: !connected}
|
|
17
|
+
Save
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
↓ compiles to ↓
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
24
|
+
|
|
25
|
+
jsxs(SimulatorPanel, { source: "gyro", width: "auto", height: 500 },
|
|
26
|
+
...servos.map(servo => jsxs(Fragment, {},
|
|
27
|
+
jsx(ServoGraph, { channel: servo.channel }),
|
|
28
|
+
jsx(ServoPanel, { servo: servo })
|
|
29
|
+
)),
|
|
30
|
+
jsx(Button, { onClick: save, disabled: !connected }, "Save")
|
|
31
|
+
);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Why
|
|
37
|
+
|
|
38
|
+
| JSX | CoffeeHaml |
|
|
39
|
+
|-----|------------|
|
|
40
|
+
| `<div className="box"><h1>Hello</h1><p>World</p></div>` | `%div.box%h1 Hello%p World` |
|
|
41
|
+
| Closing tags echo the opener verbatim | Indentation *is* structure |
|
|
42
|
+
| 17 structural tokens | 8 structural tokens |
|
|
43
|
+
| Angle brackets, braces, delimiters everywhere | Only semantic characters |
|
|
44
|
+
|
|
45
|
+
Every character carries weight. No syntactic ceremony — only meaning.
|
|
46
|
+
|
|
47
|
+
### The Indentation Renaissance
|
|
48
|
+
|
|
49
|
+
Modern LLMs are increasingly trained on indentation-based languages — Python,
|
|
50
|
+
YAML, Haml, Sass. Their tokenizers have internalized significant whitespace as
|
|
51
|
+
structural signal. CoffeeHaml enters this cycle at precisely the right moment:
|
|
52
|
+
the community is ready for a leap in expressiveness that *removes* boilerplate
|
|
53
|
+
rather than adding another layer of abstraction.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Install
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install coffeehaml
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
> **GitHub:** [dantiel/CoffeeHaml](https://github.com/dantiel/CoffeeHaml)
|
|
64
|
+
|
|
65
|
+
CoffeeScript is an optional peer dependency — install it to compile CoffeeScript
|
|
66
|
+
expressions in attributes and control flow:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install coffeescript
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Without CoffeeScript, expressions pass through as-is (valid for most JS
|
|
73
|
+
expressions like `{onClick: handler}` or `{disabled: !connected}`).
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
### CLI
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npx coffeehaml compile app.coffeehaml
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Accepts `.coffeehaml`, `.cohaml`, and `.chaml` extensions.
|
|
86
|
+
|
|
87
|
+
### Vite plugin
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
// vite.config.ts
|
|
91
|
+
import coffeehaml from 'coffeehaml/vite';
|
|
92
|
+
|
|
93
|
+
export default {
|
|
94
|
+
plugins: [coffeehaml()],
|
|
95
|
+
};
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Now import `.chaml` files directly — they compile to React components with HMR.
|
|
99
|
+
|
|
100
|
+
### Node API
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { compile, compileFile } from 'coffeehaml';
|
|
104
|
+
|
|
105
|
+
const result = compile('%div Hello', { filename: 'app.chaml' });
|
|
106
|
+
console.log(result.code);
|
|
107
|
+
// → import { jsx } from "react/jsx-runtime";
|
|
108
|
+
// jsx("div", null, "Hello");
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Syntax
|
|
114
|
+
|
|
115
|
+
| CoffeeHaml | Description |
|
|
116
|
+
|------------|-------------|
|
|
117
|
+
| `%tag` | HTML element (`%div`, `%span`, `%button`) |
|
|
118
|
+
| `%Component` | React component (uppercase) |
|
|
119
|
+
| `.class` | CSS class |
|
|
120
|
+
| `#id` | Element ID |
|
|
121
|
+
| `{attr: val}` / `(attr: val)` | Attributes (CoffeeScript expressions) |
|
|
122
|
+
| `{onClick}` | Shorthand — `onClick={onClick}` |
|
|
123
|
+
| `= expression` | Inline output (escaped) |
|
|
124
|
+
| `- if`, `- unless` | Conditional |
|
|
125
|
+
| `- for x in xs` | Loop → `.map()` |
|
|
126
|
+
| `- else`, `- else if` | Chain conditionals |
|
|
127
|
+
| `-# comment` | Haml comment (stripped) |
|
|
128
|
+
| `/ comment` | HTML comment |
|
|
129
|
+
| `.wrapper` | Implicit div |
|
|
130
|
+
|
|
131
|
+
Full grammar: [`docs/grammar.md`](docs/grammar.md)
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Design Principles
|
|
136
|
+
|
|
137
|
+
- **Zero runtime** — the compiler is the only artifact
|
|
138
|
+
- **Emitter-agnostic AST** — future backends possible (Solid, Vue, Mithril)
|
|
139
|
+
- **Excellent source maps** — debug in CoffeeHaml, not generated JS
|
|
140
|
+
- **Incremental compilation** — Vite HMR out of the box
|
|
141
|
+
- **No invention** — reuse Haml and CoffeeScript conventions
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Status
|
|
146
|
+
|
|
147
|
+
**v0.1.0 — experimental.** The core compiler pipeline (Lexer → Parser →
|
|
148
|
+
Emitter) is functional. Source maps and CoffeeScript expression compilation
|
|
149
|
+
require the `coffeescript` peer dependency.
|
|
150
|
+
|
|
151
|
+
| Feature | Status |
|
|
152
|
+
|---------|--------|
|
|
153
|
+
| Elements, components, implicit divs | ✅ |
|
|
154
|
+
| `.class` / `#id` modifiers | ✅ |
|
|
155
|
+
| `{attr: val}` / `(attr: val)` blocks | ✅ |
|
|
156
|
+
| Inline `= expression` output | ✅ |
|
|
157
|
+
| `- if` / `- unless` / `- else` / `- else if` | ✅ |
|
|
158
|
+
| `- for item in items` → `.map()` | ✅ |
|
|
159
|
+
| `- while` | ✅ |
|
|
160
|
+
| Haml/HTML comments | ✅ |
|
|
161
|
+
| `:filter` blocks | ✅ |
|
|
162
|
+
| Vite plugin with HMR | ✅ |
|
|
163
|
+
| Source maps | 🚧 Stubbed |
|
|
164
|
+
| CoffeeScript expression compilation | 🚧 Requires peer dep |
|
|
165
|
+
| React Fast Refresh | 📅 Planned |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
*Haml structure. CoffeeScript semantics. React runtime. Nothing more, nothing less.*
|
package/dist/ast.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { SourceLocation } from './types.js';
|
|
2
|
+
export type NodeKind = 'Document' | 'Element' | 'ImplicitDiv' | 'Text' | 'Output' | 'ControlFlow' | 'Comment' | 'Filter' | 'Doctype';
|
|
3
|
+
export type ControlFlowKind = 'if' | 'unless' | 'for' | 'while' | 'else';
|
|
4
|
+
export type CommentKind = 'haml' | 'html';
|
|
5
|
+
export type OutputKind = 'escaped' | 'unescaped';
|
|
6
|
+
export declare abstract class Node {
|
|
7
|
+
abstract readonly kind: NodeKind;
|
|
8
|
+
readonly location?: SourceLocation;
|
|
9
|
+
constructor(location?: SourceLocation);
|
|
10
|
+
}
|
|
11
|
+
/** Wraps a CoffeeScript expression — stores the raw source string.
|
|
12
|
+
* Parsed later by the CoffeeScript compiler. */
|
|
13
|
+
export declare class Expression {
|
|
14
|
+
readonly source: string;
|
|
15
|
+
/** Optional: the parsed CoffeeScript AST node (populated by expression phase). */
|
|
16
|
+
parsed?: any;
|
|
17
|
+
constructor(source: string, parsed?: any);
|
|
18
|
+
toString(): string;
|
|
19
|
+
}
|
|
20
|
+
export declare class Document extends Node {
|
|
21
|
+
readonly kind: "Document";
|
|
22
|
+
readonly children: Node[];
|
|
23
|
+
constructor(children: Node[], location?: SourceLocation);
|
|
24
|
+
}
|
|
25
|
+
export declare class Element extends Node {
|
|
26
|
+
readonly kind: "Element";
|
|
27
|
+
readonly tag: string | Expression;
|
|
28
|
+
readonly classes: string[];
|
|
29
|
+
readonly id: string | null;
|
|
30
|
+
readonly attributes: Attribute[];
|
|
31
|
+
readonly children: Node[];
|
|
32
|
+
readonly isComponent: boolean;
|
|
33
|
+
readonly isSelfClosing: boolean;
|
|
34
|
+
constructor(tag: string | Expression, opts?: {
|
|
35
|
+
classes?: string[];
|
|
36
|
+
id?: string | null;
|
|
37
|
+
attributes?: Attribute[];
|
|
38
|
+
children?: Node[];
|
|
39
|
+
isComponent?: boolean;
|
|
40
|
+
isSelfClosing?: boolean;
|
|
41
|
+
location?: SourceLocation;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/** An attribute on an element — key is always a string, value is an expression. */
|
|
45
|
+
export interface Attribute {
|
|
46
|
+
name: string;
|
|
47
|
+
value: Expression;
|
|
48
|
+
shorthand: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** A div created by `.class` or `#id` without an explicit `%tag`. */
|
|
51
|
+
export declare class ImplicitDiv extends Node {
|
|
52
|
+
readonly kind: "ImplicitDiv";
|
|
53
|
+
readonly classes: string[];
|
|
54
|
+
readonly id: string | null;
|
|
55
|
+
readonly attributes: Attribute[];
|
|
56
|
+
readonly children: Node[];
|
|
57
|
+
constructor(opts?: {
|
|
58
|
+
classes?: string[];
|
|
59
|
+
id?: string | null;
|
|
60
|
+
attributes?: Attribute[];
|
|
61
|
+
children?: Node[];
|
|
62
|
+
location?: SourceLocation;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export declare class Text extends Node {
|
|
66
|
+
readonly kind: "Text";
|
|
67
|
+
readonly value: string;
|
|
68
|
+
constructor(value: string, location?: SourceLocation);
|
|
69
|
+
}
|
|
70
|
+
export declare class Output extends Node {
|
|
71
|
+
readonly kind: "Output";
|
|
72
|
+
readonly expression: Expression;
|
|
73
|
+
readonly outputKind: OutputKind;
|
|
74
|
+
constructor(expression: Expression, outputKind: OutputKind, location?: SourceLocation);
|
|
75
|
+
}
|
|
76
|
+
export declare class ControlFlow extends Node {
|
|
77
|
+
readonly kind: "ControlFlow";
|
|
78
|
+
readonly controlKind: ControlFlowKind;
|
|
79
|
+
readonly expression: Expression;
|
|
80
|
+
readonly children: Node[];
|
|
81
|
+
readonly next: ControlFlow | null;
|
|
82
|
+
constructor(controlKind: ControlFlowKind, expression: Expression, children?: Node[], next?: ControlFlow | null, location?: SourceLocation);
|
|
83
|
+
/** Returns true if this is a for/while loop. */
|
|
84
|
+
get isLoop(): boolean;
|
|
85
|
+
/** Returns true if this is a conditional. */
|
|
86
|
+
get isConditional(): boolean;
|
|
87
|
+
/** The chain length including this node. */
|
|
88
|
+
get chainLength(): number;
|
|
89
|
+
}
|
|
90
|
+
export declare class Comment extends Node {
|
|
91
|
+
readonly kind: "Comment";
|
|
92
|
+
readonly commentKind: CommentKind;
|
|
93
|
+
readonly text: string;
|
|
94
|
+
constructor(commentKind: CommentKind, text: string, location?: SourceLocation);
|
|
95
|
+
}
|
|
96
|
+
export declare class Filter extends Node {
|
|
97
|
+
readonly kind: "Filter";
|
|
98
|
+
readonly filterName: string;
|
|
99
|
+
readonly content: string;
|
|
100
|
+
constructor(filterName: string, content: string, location?: SourceLocation);
|
|
101
|
+
}
|
|
102
|
+
export declare class Doctype extends Node {
|
|
103
|
+
readonly kind: "Doctype";
|
|
104
|
+
readonly value: string;
|
|
105
|
+
constructor(value: string, location?: SourceLocation);
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=ast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
|
package/dist/ast.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
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;
|
|
14
|
+
}
|
|
15
|
+
toString() {
|
|
16
|
+
return this.source;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// ─── Document ──────────────────────────────────────────────
|
|
20
|
+
export class Document extends Node {
|
|
21
|
+
constructor(children, location) {
|
|
22
|
+
super(location);
|
|
23
|
+
this.kind = 'Document';
|
|
24
|
+
this.children = children;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// ─── Element ───────────────────────────────────────────────
|
|
28
|
+
export class Element extends Node {
|
|
29
|
+
constructor(tag, opts = {}) {
|
|
30
|
+
super(opts.location);
|
|
31
|
+
this.kind = 'Element';
|
|
32
|
+
this.tag = tag;
|
|
33
|
+
this.classes = opts.classes ?? [];
|
|
34
|
+
this.id = opts.id ?? null;
|
|
35
|
+
this.attributes = opts.attributes ?? [];
|
|
36
|
+
this.children = opts.children ?? [];
|
|
37
|
+
this.isComponent = opts.isComponent ?? false;
|
|
38
|
+
this.isSelfClosing = opts.isSelfClosing ?? false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// ─── ImplicitDiv ───────────────────────────────────────────
|
|
42
|
+
/** A div created by `.class` or `#id` without an explicit `%tag`. */
|
|
43
|
+
export class ImplicitDiv extends Node {
|
|
44
|
+
constructor(opts = {}) {
|
|
45
|
+
super(opts.location);
|
|
46
|
+
this.kind = 'ImplicitDiv';
|
|
47
|
+
this.classes = opts.classes ?? [];
|
|
48
|
+
this.id = opts.id ?? null;
|
|
49
|
+
this.attributes = opts.attributes ?? [];
|
|
50
|
+
this.children = opts.children ?? [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// ─── Text ──────────────────────────────────────────────────
|
|
54
|
+
export class Text extends Node {
|
|
55
|
+
constructor(value, location) {
|
|
56
|
+
super(location);
|
|
57
|
+
this.kind = 'Text';
|
|
58
|
+
this.value = value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// ─── Output ────────────────────────────────────────────────
|
|
62
|
+
export class Output extends Node {
|
|
63
|
+
constructor(expression, outputKind, location) {
|
|
64
|
+
super(location);
|
|
65
|
+
this.kind = 'Output';
|
|
66
|
+
this.expression = expression;
|
|
67
|
+
this.outputKind = outputKind;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// ─── ControlFlow ───────────────────────────────────────────
|
|
71
|
+
export class ControlFlow extends Node {
|
|
72
|
+
constructor(controlKind, expression, children = [], next = null, location) {
|
|
73
|
+
super(location);
|
|
74
|
+
this.kind = 'ControlFlow';
|
|
75
|
+
this.controlKind = controlKind;
|
|
76
|
+
this.expression = expression;
|
|
77
|
+
this.children = children;
|
|
78
|
+
this.next = next;
|
|
79
|
+
}
|
|
80
|
+
/** Returns true if this is a for/while loop. */
|
|
81
|
+
get isLoop() {
|
|
82
|
+
return this.controlKind === 'for' || this.controlKind === 'while';
|
|
83
|
+
}
|
|
84
|
+
/** Returns true if this is a conditional. */
|
|
85
|
+
get isConditional() {
|
|
86
|
+
return this.controlKind === 'if' || this.controlKind === 'unless';
|
|
87
|
+
}
|
|
88
|
+
/** The chain length including this node. */
|
|
89
|
+
get chainLength() {
|
|
90
|
+
let n = this;
|
|
91
|
+
let count = 0;
|
|
92
|
+
while (n) {
|
|
93
|
+
count++;
|
|
94
|
+
n = n.next;
|
|
95
|
+
}
|
|
96
|
+
return count;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// ─── Comment ───────────────────────────────────────────────
|
|
100
|
+
export class Comment extends Node {
|
|
101
|
+
constructor(commentKind, text, location) {
|
|
102
|
+
super(location);
|
|
103
|
+
this.kind = 'Comment';
|
|
104
|
+
this.commentKind = commentKind;
|
|
105
|
+
this.text = text;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// ─── Filter ────────────────────────────────────────────────
|
|
109
|
+
export class Filter extends Node {
|
|
110
|
+
constructor(filterName, content, location) {
|
|
111
|
+
super(location);
|
|
112
|
+
this.kind = 'Filter';
|
|
113
|
+
this.filterName = filterName;
|
|
114
|
+
this.content = content;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// ─── Doctype ───────────────────────────────────────────────
|
|
118
|
+
export class Doctype extends Node {
|
|
119
|
+
constructor(value, location) {
|
|
120
|
+
super(location);
|
|
121
|
+
this.kind = 'Doctype';
|
|
122
|
+
this.value = value;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=ast.js.map
|
package/dist/ast.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { compileFile } from './index.js';
|
|
3
|
+
const args = process.argv.slice(2);
|
|
4
|
+
if (args.length === 0) {
|
|
5
|
+
console.log(`CoffeeHaml v0.1.0
|
|
6
|
+
Haml structure. CoffeeScript semantics. React runtime.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
coffeehaml compile <input> [-o <output>]
|
|
10
|
+
|
|
11
|
+
Examples:
|
|
12
|
+
coffeehaml compile app.coffeehaml -o app.js
|
|
13
|
+
coffeehaml compile app.coffeehaml # prints to stdout
|
|
14
|
+
`);
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
const cmd = args[0];
|
|
18
|
+
if (cmd === 'compile') {
|
|
19
|
+
const input = args[1];
|
|
20
|
+
if (!input) {
|
|
21
|
+
console.error('Error: no input file specified');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const outIdx = args.indexOf('-o');
|
|
25
|
+
const output = outIdx !== -1 ? args[outIdx + 1] : null;
|
|
26
|
+
const result = compileFile(input, { filename: input, sourceMap: true });
|
|
27
|
+
if (result.errors.length > 0) {
|
|
28
|
+
for (const err of result.errors) {
|
|
29
|
+
console.error(formatError(err, input));
|
|
30
|
+
}
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
if (output) {
|
|
34
|
+
const fs = await import('fs');
|
|
35
|
+
fs.writeFileSync(output, result.code);
|
|
36
|
+
console.log(`Compiled: ${input} → ${output}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.log(result.code);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.error(`Unknown command: ${cmd}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
function formatError(err, filename) {
|
|
47
|
+
const loc = err.location;
|
|
48
|
+
if (loc) {
|
|
49
|
+
return `${filename}:${loc.start.line + 1}:${loc.start.column + 1}: [${err.code}] ${err.message}`;
|
|
50
|
+
}
|
|
51
|
+
return `[${err.code}] ${err.message}`;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IACtB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;CASb,CAAC,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAEpB,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;IACtB,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEvD,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAExE,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAChC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9B,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,MAAM,MAAM,EAAE,CAAC,CAAC;IAChD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;KAAM,CAAC;IACN,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,EAAE,CAAC,CAAC;IACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,WAAW,CAAC,GAAQ,EAAE,QAAgB;IAC7C,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC;IACzB,IAAI,GAAG,EAAE,CAAC;QACR,OAAO,GAAG,QAAQ,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;IACnG,CAAC;IACD,OAAO,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CompilerOptions, CompileResult } from './types.js';
|
|
2
|
+
/** Supported CoffeeHaml file extensions (in order of preference). */
|
|
3
|
+
export declare const COFFEEHAML_EXTENSIONS: string[];
|
|
4
|
+
/** Resolve a CoffeeHaml file path, trying each extension if none given. */
|
|
5
|
+
export declare function resolveCoffeeHamlFile(filepath: string): string | null;
|
|
6
|
+
/** Compile CoffeeHaml source to JavaScript. */
|
|
7
|
+
export declare function compile(source: string, options?: CompilerOptions): CompileResult;
|
|
8
|
+
/** Compile a CoffeeHaml file from disk. */
|
|
9
|
+
export declare function compileFile(filepath: string, options?: CompilerOptions): CompileResult;
|
|
10
|
+
//# sourceMappingURL=compiler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAgC,MAAM,YAAY,CAAC;AAG1F,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,UAAuC,CAAC;AAE1E,2EAA2E;AAC3E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOrE;AAED,+CAA+C;AAC/C,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,aAAa,CAmDpF;AAED,2CAA2C;AAC3C,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,aAAa,CAG1F"}
|
package/dist/compiler.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { tokenize } from './lexer.js';
|
|
2
|
+
import { parse } from './parser.js';
|
|
3
|
+
import { emit } from './emitter.js';
|
|
4
|
+
import { CompileError } from './types.js';
|
|
5
|
+
import { readFileSync, existsSync } from 'fs';
|
|
6
|
+
/** Supported CoffeeHaml file extensions (in order of preference). */
|
|
7
|
+
export const COFFEEHAML_EXTENSIONS = ['.coffeehaml', '.cohaml', '.chaml'];
|
|
8
|
+
/** Resolve a CoffeeHaml file path, trying each extension if none given. */
|
|
9
|
+
export function resolveCoffeeHamlFile(filepath) {
|
|
10
|
+
if (existsSync(filepath))
|
|
11
|
+
return filepath;
|
|
12
|
+
for (const ext of COFFEEHAML_EXTENSIONS) {
|
|
13
|
+
const withExt = filepath + ext;
|
|
14
|
+
if (existsSync(withExt))
|
|
15
|
+
return withExt;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
/** Compile CoffeeHaml source to JavaScript. */
|
|
20
|
+
export function compile(source, options = {}) {
|
|
21
|
+
const errors = [];
|
|
22
|
+
const warnings = [];
|
|
23
|
+
// Phase 1: Lex
|
|
24
|
+
let tokens;
|
|
25
|
+
try {
|
|
26
|
+
tokens = tokenize(source, options.filename);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
return {
|
|
30
|
+
code: '',
|
|
31
|
+
errors: [wrapError(e, 'lexer')],
|
|
32
|
+
warnings: [],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// Phase 2: Parse
|
|
36
|
+
let ast;
|
|
37
|
+
try {
|
|
38
|
+
ast = parse(tokens, options.filename);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
return {
|
|
42
|
+
code: '',
|
|
43
|
+
errors: [wrapError(e, 'parser')],
|
|
44
|
+
warnings: [],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// Phase 3: Emit
|
|
48
|
+
let result;
|
|
49
|
+
try {
|
|
50
|
+
result = emit(ast, {
|
|
51
|
+
sourceMap: options.sourceMap,
|
|
52
|
+
filename: options.filename,
|
|
53
|
+
jsxRuntime: options.jsxRuntime,
|
|
54
|
+
filters: options.filters,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
return {
|
|
59
|
+
code: '',
|
|
60
|
+
errors: [wrapError(e, 'emitter')],
|
|
61
|
+
warnings: [],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
code: result.code,
|
|
66
|
+
sourceMap: result.sourceMap,
|
|
67
|
+
errors,
|
|
68
|
+
warnings,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Compile a CoffeeHaml file from disk. */
|
|
72
|
+
export function compileFile(filepath, options = {}) {
|
|
73
|
+
const source = readFileSync(filepath, 'utf-8');
|
|
74
|
+
return compile(source, { ...options, filename: filepath });
|
|
75
|
+
}
|
|
76
|
+
function wrapError(e, phase) {
|
|
77
|
+
if (e instanceof CompileError) {
|
|
78
|
+
e.phase = phase;
|
|
79
|
+
return e;
|
|
80
|
+
}
|
|
81
|
+
return new CompileError(e instanceof Error ? e.message : String(e), phase, 'INTERNAL_ERROR');
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=compiler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler.js","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EAET,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAsCxD,wBAAgB,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,GAAE,cAAmB,GAAG,UAAU,CAW5E"}
|