@nachoggodino/cello 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/BYLAWS.md +446 -0
- package/CHANGELOG.md +11 -0
- package/LICENSE +12 -0
- package/README.md +211 -0
- package/dist/cli/cli.d.ts +16 -0
- package/dist/cli/cli.js +360 -0
- package/dist/cli/serve.d.ts +15 -0
- package/dist/cli/serve.js +226 -0
- package/dist/evaluator/evaluate.d.ts +2 -0
- package/dist/evaluator/evaluate.js +129 -0
- package/dist/evaluator/formula.d.ts +13 -0
- package/dist/evaluator/formula.js +141 -0
- package/dist/formatter/format.d.ts +1 -0
- package/dist/formatter/format.js +112 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -0
- package/dist/parser/parse.d.ts +2 -0
- package/dist/parser/parse.js +552 -0
- package/dist/renderer/render.d.ts +2 -0
- package/dist/renderer/render.js +295 -0
- package/dist/serializer/serialize.d.ts +2 -0
- package/dist/serializer/serialize.js +104 -0
- package/dist/shared/types.d.ts +88 -0
- package/dist/shared/types.js +1 -0
- package/dist/shared/utils.d.ts +16 -0
- package/dist/shared/utils.js +142 -0
- package/dist/validator/validate.d.ts +8 -0
- package/dist/validator/validate.js +10 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/docs/ARCHITECTURE.md +43 -0
- package/docs/CLI.md +58 -0
- package/docs/COMPLIANCE.md +82 -0
- package/docs/ERROR_MODEL.md +25 -0
- package/docs/FORMULA_SUPPORT.md +33 -0
- package/docs/SPEC.md +723 -0
- package/docs/SYNTAX_HIGHLIGHTING.md +91 -0
- package/examples/advanced_kpi.cel +42 -0
- package/examples/basic.cel +8 -0
- package/examples/feature_showcase.cel +37 -0
- package/package.json +96 -0
- package/syntaxes/cel.language-configuration.json +31 -0
- package/syntaxes/cel.tmLanguage.json +250 -0
package/BYLAWS.md
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="playground/public/cello-logo-bylaws.svg" alt="cello" width="180" />
|
|
3
|
+
<h1>Cello Bylaws v1.0</h1>
|
|
4
|
+
<p><strong>Plain-text spreadsheets for humans, agents, diffs, and HTML previews.</strong></p>
|
|
5
|
+
<p>
|
|
6
|
+
<img alt="Format: .cel" src="https://img.shields.io/badge/format-.cel-e7662f?style=flat-square" />
|
|
7
|
+
<img alt="Syntax: BYLAWS first" src="https://img.shields.io/badge/syntax-BYLAWS--first-496f91?style=flat-square" />
|
|
8
|
+
<img alt="Renderer: HTML" src="https://img.shields.io/badge/render-HTML-5a7d54?style=flat-square" />
|
|
9
|
+
<img alt="Version: v1.0" src="https://img.shields.io/badge/version-v1.0-6f5f95?style=flat-square" />
|
|
10
|
+
</p>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
Cello is a plain-text spreadsheet format. It defines how spreadsheets are written as text, not which app edits them or who is allowed to use them.
|
|
16
|
+
|
|
17
|
+
This document is the canonical rulebook for `.cel` files. It states the core bylaws first, then the syntax and behavior rules that follow from them.
|
|
18
|
+
|
|
19
|
+
## 0. 📜 First bylaws
|
|
20
|
+
|
|
21
|
+
### A. Cello is free
|
|
22
|
+
|
|
23
|
+
Cello is open source. It will never be paid-only, metered, limited, or locked behind usage caps. Anyone can use it, implement it, render it, and extend tooling around it without permission or quota.
|
|
24
|
+
|
|
25
|
+
### B. Cello is a format
|
|
26
|
+
|
|
27
|
+
Cello is not an app, not an editor, and not a hosted product. It is a convention for writing spreadsheets as text so different tools can parse, render, validate, diff, and generate the same source.
|
|
28
|
+
|
|
29
|
+
### C. Cello is light
|
|
30
|
+
|
|
31
|
+
Cello exists to be lighter than the alternatives. A `.cel` file should be quick to open, quick to render, easy to diff, easy to generate, and cheap to move through both human and machine workflows.
|
|
32
|
+
|
|
33
|
+
## 1. 📄 Workbooks and sheets
|
|
34
|
+
|
|
35
|
+
A `.cel` file contains one or more sheets. A sheet starts with `@sheet`.
|
|
36
|
+
|
|
37
|
+
```cel
|
|
38
|
+
@sheet Summary
|
|
39
|
+
| Metric | Value |
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Syntax:
|
|
43
|
+
|
|
44
|
+
```cel
|
|
45
|
+
@sheet Name [format]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Rules:
|
|
49
|
+
|
|
50
|
+
1. `Name` is case-sensitive.
|
|
51
|
+
2. `[format]` is optional. If omitted, the sheet uses native Cello syntax.
|
|
52
|
+
3. Everything after a sheet declaration belongs to that sheet until the next `@sheet`.
|
|
53
|
+
4. If a file has no `@sheet`, it is treated as a single anonymous native Cello sheet.
|
|
54
|
+
|
|
55
|
+
Examples:
|
|
56
|
+
|
|
57
|
+
```cel
|
|
58
|
+
@sheet Report
|
|
59
|
+
@sheet Sales [csv]
|
|
60
|
+
@sheet Export [tsv]
|
|
61
|
+
@sheet RawData [json]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 2. 🧾 Input formats
|
|
65
|
+
|
|
66
|
+
Cello sheets can be written directly, or loaded from common data formats.
|
|
67
|
+
|
|
68
|
+
| Format | Example | Notes |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| Native Cello | `@sheet Report` | Full Cello syntax: formulas, modifiers, merges, headers |
|
|
71
|
+
| CSV | `@sheet Sales [csv]` | Comma-separated values |
|
|
72
|
+
| TSV | `@sheet Export [tsv]` or `@sheet Export [\t]` | Tab-separated values |
|
|
73
|
+
| Excel-style | `@sheet Sales [excel]` or `@sheet Sales [;]` | Semicolon-separated values |
|
|
74
|
+
| Custom delimiter | `@sheet Data [,]` | Any single-character delimiter |
|
|
75
|
+
| Markdown table | `@sheet Data [markdown]` | First row becomes headers; separator row is ignored |
|
|
76
|
+
| JSON | `@sheet Data [json]` | Flat arrays of objects |
|
|
77
|
+
|
|
78
|
+
Delimited sheets use their first row as column headers by default.
|
|
79
|
+
|
|
80
|
+
```cel
|
|
81
|
+
@sheet Sales [csv]
|
|
82
|
+
product,price,quantity
|
|
83
|
+
Apple,1.2,5
|
|
84
|
+
Pear,0.9,3
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Use `:noheader` when the first row is data.
|
|
88
|
+
|
|
89
|
+
```cel
|
|
90
|
+
@sheet Raw [csv:noheader]
|
|
91
|
+
Apple,1.2,5
|
|
92
|
+
Pear,0.9,3
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
In `noheader` sheets, columns are still available by coordinate references such as `A1`, `B1`, and `C1`.
|
|
96
|
+
|
|
97
|
+
## 3. 🔗 External sources
|
|
98
|
+
|
|
99
|
+
A sheet can load its content from another file. The source line must appear immediately after the sheet declaration and before any row content.
|
|
100
|
+
|
|
101
|
+
```cel
|
|
102
|
+
@sheet Sales [csv]
|
|
103
|
+
-> ./exports/sales.csv
|
|
104
|
+
|
|
105
|
+
@sheet Summary
|
|
106
|
+
@header | Metric | Value |
|
|
107
|
+
| Revenue | =SUM(Sales!amount[*]) |
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Rules:
|
|
111
|
+
|
|
112
|
+
1. The declared sheet format controls how the external file is parsed.
|
|
113
|
+
2. Relative paths are resolved from the parser base directory.
|
|
114
|
+
3. If loading fails, Cello records a diagnostic and continues parsing the rest of the workbook.
|
|
115
|
+
|
|
116
|
+
## 4. 🧱 Native rows and cells
|
|
117
|
+
|
|
118
|
+
Native Cello rows are written with pipe-separated cells.
|
|
119
|
+
|
|
120
|
+
```cel
|
|
121
|
+
| Product | Price | Quantity |
|
|
122
|
+
| Apple | 1.20 | 5 |
|
|
123
|
+
| Pear | 0.90 | 3 |
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Rules:
|
|
127
|
+
|
|
128
|
+
1. A native data row contains cells separated by `|`.
|
|
129
|
+
2. Write native rows in the form `| cell | cell |` for predictable parsing and readable diffs.
|
|
130
|
+
3. Blank lines do not consume row numbers and are not rendered.
|
|
131
|
+
4. Multiple spaces inside cells may be used for alignment in source files.
|
|
132
|
+
|
|
133
|
+
```cel
|
|
134
|
+
| A | B |
|
|
135
|
+
|
|
136
|
+
| C | D |
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
In this example, `C` and `D` are on row 2. The blank line is ignored.
|
|
140
|
+
|
|
141
|
+
## 5. 🏷️ Column headers
|
|
142
|
+
|
|
143
|
+
A header row assigns names to columns. Named columns make formulas easier to read than coordinate references.
|
|
144
|
+
|
|
145
|
+
```cel
|
|
146
|
+
@header | Product | Price | Quantity | Total |
|
|
147
|
+
| Apple | 1.20 | 5 | =Price*Quantity |
|
|
148
|
+
| Pear | 0.90 | 3 | =Price*Quantity |
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Rules:
|
|
152
|
+
|
|
153
|
+
1. Header rows use `@header` followed by a pipe-separated row: `@header | Product | Price | Quantity |`.
|
|
154
|
+
2. Headers apply from the next data row downward.
|
|
155
|
+
3. A later header row replaces the active column names from that point on.
|
|
156
|
+
4. Header rows render as table headers.
|
|
157
|
+
5. Header modifiers apply to every cell in that column.
|
|
158
|
+
|
|
159
|
+
```cel
|
|
160
|
+
@header | Product | Price[€][2d] | Quantity[0d] | Total[€][2d] |
|
|
161
|
+
| Apple | 1.20 | 5 | =Price*Quantity |
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Column letters are always available as well. The first column is `A`, then `B`, `C`, and so on.
|
|
165
|
+
|
|
166
|
+
## 6. 🎚️ Row-level formatting
|
|
167
|
+
|
|
168
|
+
Rows can carry formatting modifiers before the first pipe. These modifiers apply to every cell in that row.
|
|
169
|
+
|
|
170
|
+
```cel
|
|
171
|
+
[bold][bg:#f5f5f5] | Total | =SUM(Amount) |
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
This is formatting only. Only modifiers should appear before the first pipe in public `.cel` files; row references are not part of the public Cello format.
|
|
175
|
+
|
|
176
|
+
Modifier precedence is:
|
|
177
|
+
|
|
178
|
+
```text
|
|
179
|
+
cell > row > column
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
That means a cell modifier overrides a row modifier, and a row modifier overrides a column modifier.
|
|
183
|
+
|
|
184
|
+
## 7. 🔤 Data types
|
|
185
|
+
|
|
186
|
+
Cello infers basic data types automatically.
|
|
187
|
+
|
|
188
|
+
| Type | Rule | Example |
|
|
189
|
+
|---|---|---|
|
|
190
|
+
| Number | Numeric value | `42`, `3.14` |
|
|
191
|
+
| Date | ISO date | `2026-01-15` |
|
|
192
|
+
| Boolean | Uppercase literal | `TRUE`, `FALSE` |
|
|
193
|
+
| Text | Anything else | `North`, `pending`, `A-001` |
|
|
194
|
+
|
|
195
|
+
Use double quotes to force text when a value looks like another type.
|
|
196
|
+
|
|
197
|
+
```cel
|
|
198
|
+
| "00123" | "TRUE" | "2026-01-15" |
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The quotes are type markers. They prevent automatic number, boolean, or date inference.
|
|
202
|
+
|
|
203
|
+
## 8. 🧮 Formulas
|
|
204
|
+
|
|
205
|
+
Any cell starting with `=` is a formula.
|
|
206
|
+
|
|
207
|
+
```cel
|
|
208
|
+
| =Price*Quantity |
|
|
209
|
+
| =SUM(Total) |
|
|
210
|
+
| =B2*C2 |
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Formulas support both coordinate references and named column references.
|
|
214
|
+
|
|
215
|
+
| Syntax | Meaning |
|
|
216
|
+
|---|---|
|
|
217
|
+
| `=B2*C2` | Coordinate reference |
|
|
218
|
+
| `=Price*Quantity` | Named column reference on the current row |
|
|
219
|
+
| `=SUM(Total)` | Sum values above the formula row in the `Total` column |
|
|
220
|
+
| `=SUM(Total[*])` | Sum the full `Total` column |
|
|
221
|
+
| `=SUM(Total[2:5])` | Sum rows 2 through 5 in the `Total` column |
|
|
222
|
+
| `=Sales!B4` | Coordinate reference on another sheet |
|
|
223
|
+
| `=SUM(Sales!Total[*])` | Named column reference on another sheet |
|
|
224
|
+
| `=SUM(!!Amount)` | Reference the first sheet in the workbook |
|
|
225
|
+
|
|
226
|
+
Same-sheet named references are context-aware:
|
|
227
|
+
|
|
228
|
+
1. In scalar formulas, a bare column name refers to the current row.
|
|
229
|
+
2. In aggregate formulas, a bare column name refers to rows above the formula row.
|
|
230
|
+
3. Use `[*]` to force the full data range.
|
|
231
|
+
|
|
232
|
+
```cel
|
|
233
|
+
@header | Product | Revenue | Cost | Profit |
|
|
234
|
+
| A | 1200 | 800 | =Revenue-Cost |
|
|
235
|
+
| B | 1800 | 900 | =Revenue-Cost |
|
|
236
|
+
| Total | =SUM(Revenue) | =SUM(Cost) | =SUM(Profit) |
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The total row does not include itself when using `SUM(Revenue)`.
|
|
240
|
+
|
|
241
|
+
## 9. 📐 Named ranges
|
|
242
|
+
|
|
243
|
+
Named ranges are based on column headers.
|
|
244
|
+
|
|
245
|
+
```cel
|
|
246
|
+
@header | Month | Revenue |
|
|
247
|
+
| Jan | 1200 |
|
|
248
|
+
| Feb | 1800 |
|
|
249
|
+
| Mar | 1500 |
|
|
250
|
+
| Total | =SUM(Revenue) |
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Range forms:
|
|
254
|
+
|
|
255
|
+
| Syntax | Meaning |
|
|
256
|
+
|---|---|
|
|
257
|
+
| `Revenue` | Current row in scalar context, previous rows in aggregate context |
|
|
258
|
+
| `Revenue[*]` | Full data span of the column |
|
|
259
|
+
| `Revenue[2:5]` | Rows 2 through 5 |
|
|
260
|
+
| `Sales!Revenue` | Named column on another sheet |
|
|
261
|
+
| `Sales!Revenue[*]` | Full named column on another sheet |
|
|
262
|
+
|
|
263
|
+
Use named ranges when formulas should remain readable after columns move.
|
|
264
|
+
|
|
265
|
+
## 10. ↔️ Merges
|
|
266
|
+
|
|
267
|
+
Cello supports horizontal and vertical merges.
|
|
268
|
+
|
|
269
|
+
```cel
|
|
270
|
+
| ## Quarterly Report | < | < | 2026 |
|
|
271
|
+
| Region | City | Owner | Revenue |
|
|
272
|
+
| North | Madrid | Ana | 1200 |
|
|
273
|
+
| ^ | Bilbao | Luis | 900 |
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Rules:
|
|
277
|
+
|
|
278
|
+
1. `<` merges with the visible cell on the left.
|
|
279
|
+
2. `^` merges with the visible cell above.
|
|
280
|
+
3. Merge tokens must appear alone in the cell.
|
|
281
|
+
4. Merge tokens do not carry values or modifiers of their own.
|
|
282
|
+
|
|
283
|
+
## 11. 🎨 Modifiers
|
|
284
|
+
|
|
285
|
+
Modifiers are attached directly to headers, row prefixes, or cell values.
|
|
286
|
+
|
|
287
|
+
```cel
|
|
288
|
+
@header | Metric | Revenue[€][2d] | Margin[%][1d] |
|
|
289
|
+
[bold] | Total | =SUM(Revenue) | =AVG(Margin) |
|
|
290
|
+
| Critical[bg:red][#fff] | 1200 | 0.42 |
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Scopes:
|
|
294
|
+
|
|
295
|
+
| Location | Example | Scope |
|
|
296
|
+
|---|---|---|
|
|
297
|
+
| Column header | `@header | Revenue[€][2d] |` | Every cell in that column |
|
|
298
|
+
| Row modifiers | `[bold] | Total | ... |` | Every cell in that row |
|
|
299
|
+
| Cell value | `Late[bg:red][#fff]` | That cell only |
|
|
300
|
+
|
|
301
|
+
Supported modifiers:
|
|
302
|
+
|
|
303
|
+
| Modifier | Meaning |
|
|
304
|
+
|---|---|
|
|
305
|
+
| `[€]` | Display number with euro prefix |
|
|
306
|
+
| `[$]` | Display number with dollar prefix |
|
|
307
|
+
| `[£]` | Display number with pound prefix |
|
|
308
|
+
| `[%]` | Display number as a percentage |
|
|
309
|
+
| `[0d]`, `[1d]`, `[2d]` | Decimal places |
|
|
310
|
+
| `[bold]` | Bold text |
|
|
311
|
+
| `[italic]` | Italic text |
|
|
312
|
+
| `[#rrggbb]` | Text color |
|
|
313
|
+
| `[bg:#rrggbb]` | Background color |
|
|
314
|
+
| `[colorname]` | CSS named text color |
|
|
315
|
+
| `[bg:colorname]` | CSS named background color |
|
|
316
|
+
| `[#bg:#rrggbb:#rrggbb]` | Background and text color shorthand |
|
|
317
|
+
| `[tone:ok]`, `[tone:warn]`, `[tone:error]`, `[tone:info]`, `[tone:muted]`, `[tone:accent]` | Semantic tone preset |
|
|
318
|
+
| `[hidden]` | Parsed as hidden metadata for tooling |
|
|
319
|
+
|
|
320
|
+
Named CSS colors such as `red`, `blue`, `green`, `orange`, and `gold` are accepted.
|
|
321
|
+
Tone presets map to renderer-defined CSS classes so embedding clients can override their colors with custom CSS.
|
|
322
|
+
|
|
323
|
+
## 12. 🧩 Column default formulas
|
|
324
|
+
|
|
325
|
+
A column can define a default formula for empty cells in that column with a non-rendered `@defaults` row.
|
|
326
|
+
|
|
327
|
+
```cel
|
|
328
|
+
@header | Product | Price[€][2d] | Quantity[0d] | Total[€][2d] |
|
|
329
|
+
@defaults | | | | =Price*Quantity |
|
|
330
|
+
| Apple | 1.20 | 5 | |
|
|
331
|
+
| Pear | 0.90 | 3 | |
|
|
332
|
+
| Override | 10 | 2 | 99 |
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Rules:
|
|
336
|
+
|
|
337
|
+
1. Defaults are declared with `@defaults | ... |` below the active header.
|
|
338
|
+
2. `@defaults` rows are configuration rows. They do not render and do not consume row numbers.
|
|
339
|
+
3. Defaults only apply to empty cells.
|
|
340
|
+
4. Explicit cell values and formulas always win.
|
|
341
|
+
5. `default` is a column-level behavior; do not use it as header, row, or cell formatting.
|
|
342
|
+
6. The leading `=` is optional.
|
|
343
|
+
|
|
344
|
+
Header, row, and cell-level default modifiers are ignored:
|
|
345
|
+
|
|
346
|
+
```cel
|
|
347
|
+
@header | Total[default:=Price*Quantity] |
|
|
348
|
+
| [default:=Price*Quantity] |
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## 13. ✍️ Inline formatting
|
|
352
|
+
|
|
353
|
+
Cell text supports a small Markdown-like formatting set.
|
|
354
|
+
|
|
355
|
+
| Syntax | Result |
|
|
356
|
+
|---|---|
|
|
357
|
+
| `*text*` | Bold |
|
|
358
|
+
| `_text_` | Italic |
|
|
359
|
+
| `~~text~~` | Strikethrough |
|
|
360
|
+
| `# text` | Heading-style cell |
|
|
361
|
+
| `## text` | Larger heading-style cell |
|
|
362
|
+
|
|
363
|
+
Examples:
|
|
364
|
+
|
|
365
|
+
```cel
|
|
366
|
+
| *Priority* |
|
|
367
|
+
| _Estimated_ |
|
|
368
|
+
| ~~Deprecated~~ |
|
|
369
|
+
| ## Total |
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
`#` and `##` apply to the whole cell.
|
|
373
|
+
|
|
374
|
+
## 14. 💬 Comments
|
|
375
|
+
|
|
376
|
+
Comments use `//` and are valid outside rows.
|
|
377
|
+
|
|
378
|
+
```cel
|
|
379
|
+
// Data exported from CRM
|
|
380
|
+
@sheet Sales [csv]
|
|
381
|
+
product,amount
|
|
382
|
+
Enterprise,1200
|
|
383
|
+
SMB,800
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Rules:
|
|
387
|
+
|
|
388
|
+
1. A comment line starts with `//`.
|
|
389
|
+
2. Comments are not rendered.
|
|
390
|
+
3. Comments inside cell content are not supported.
|
|
391
|
+
|
|
392
|
+
## 15. 🔒 Reserved tokens
|
|
393
|
+
|
|
394
|
+
These tokens have special meaning in Cello.
|
|
395
|
+
|
|
396
|
+
| Token | Meaning |
|
|
397
|
+
|---|---|
|
|
398
|
+
| `@sheet` | Sheet declaration |
|
|
399
|
+
| `[format]` | Sheet format or modifier block |
|
|
400
|
+
| `->` | External source line |
|
|
401
|
+
| `|` | Cell separator in native rows |
|
|
402
|
+
| `@header` | Header row marker |
|
|
403
|
+
| `=` | Formula prefix |
|
|
404
|
+
| `!` | Cross-sheet reference separator |
|
|
405
|
+
| `!!` | First-sheet alias |
|
|
406
|
+
| `[n:m]` | Named column row slice |
|
|
407
|
+
| `[*]` | Full named column span |
|
|
408
|
+
| `<` | Horizontal merge token |
|
|
409
|
+
| `^` | Vertical merge token |
|
|
410
|
+
| `//` | Comment line |
|
|
411
|
+
| `"..."` | Force text type |
|
|
412
|
+
|
|
413
|
+
## 16. 🛟 Error handling and resilience
|
|
414
|
+
|
|
415
|
+
Cello is resilient by default. Local issues should not prevent the rest of the workbook from rendering.
|
|
416
|
+
|
|
417
|
+
Rules:
|
|
418
|
+
|
|
419
|
+
1. Formula evaluation errors render as cell-level error values.
|
|
420
|
+
2. Non-parseable formulas fall back to their raw formula text.
|
|
421
|
+
3. Unknown cell syntax is treated as plain text when possible.
|
|
422
|
+
4. Invalid or unsupported sheet content records diagnostics and parsing continues.
|
|
423
|
+
5. Broken external sources record diagnostics and the remaining workbook still renders.
|
|
424
|
+
|
|
425
|
+
This behavior is intentional. A `.cel` file should be useful even when part of it is incomplete, generated, or temporarily invalid.
|
|
426
|
+
|
|
427
|
+
## 📚 Quick reference
|
|
428
|
+
|
|
429
|
+
| Task | Syntax |
|
|
430
|
+
|---|---|
|
|
431
|
+
| Start a native sheet | `@sheet Summary` |
|
|
432
|
+
| Start a CSV sheet | `@sheet Sales [csv]` |
|
|
433
|
+
| Load an external CSV | `@sheet Sales [csv]` then `-> ./sales.csv` |
|
|
434
|
+
| Define headers | `@header | Product | Price | Quantity |` |
|
|
435
|
+
| Write a row | `| Apple | 1.20 | 5 |` |
|
|
436
|
+
| Format a row | `[bold] | Total | =SUM(Amount) |` |
|
|
437
|
+
| Format a column | `@header | Amount[€][2d] |` |
|
|
438
|
+
| Format a cell | `Late[bg:red][#fff]` |
|
|
439
|
+
| Write a formula | `=Price*Quantity` |
|
|
440
|
+
| Sum previous rows | `=SUM(Amount)` |
|
|
441
|
+
| Sum a full column | `=SUM(Amount[*])` |
|
|
442
|
+
| Reference another sheet | `=SUM(Sales!Amount[*])` |
|
|
443
|
+
| Merge right | `<` |
|
|
444
|
+
| Merge down | `^` |
|
|
445
|
+
| Force text | `"00123"` |
|
|
446
|
+
| Add a comment | `// source: CRM export` |
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## 0.1.0 - 2026-05-15
|
|
8
|
+
|
|
9
|
+
- Initial public package metadata for `@nachoggodino/cello` as a GPLv3 package.
|
|
10
|
+
- Added parser, HyperFormula-backed evaluator, validator, renderer, serializer, and live preview CLI commands.
|
|
11
|
+
- Added `.cel` examples and test coverage for API, CLI, parser, evaluator, renderer, serializer, and validator behavior.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cello contributors
|
|
4
|
+
|
|
5
|
+
Cello is licensed under the GNU General Public License version 3 only.
|
|
6
|
+
|
|
7
|
+
This package includes HyperFormula as a production dependency and configures it
|
|
8
|
+
with licenseKey: "gpl-v3". HyperFormula is distributed under GPLv3 or a
|
|
9
|
+
commercial license from Handsontable. This package uses the GPLv3 option.
|
|
10
|
+
|
|
11
|
+
The full GPLv3 license text is available at:
|
|
12
|
+
https://www.gnu.org/licenses/gpl-3.0.txt
|
package/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Cello
|
|
2
|
+
|
|
3
|
+
Plain-text spreadsheets with formulas. Cello gives you a readable `.cel` format, a TypeScript API, and a CLI that can parse, evaluate, validate, format, serialize, render, and serve workbooks as self-contained HTML.
|
|
4
|
+
|
|
5
|
+
It is useful when you want spreadsheet-like calculations in files that are easy to diff, review, generate, and keep in source control.
|
|
6
|
+
|
|
7
|
+
The npm package is `@nachoggodino/cello`. It is licensed as GPLv3 because formula evaluation uses HyperFormula under its GPLv3 option.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Multi-sheet `.cel` workbooks.
|
|
12
|
+
- Native Cello rows plus CSV, TSV, semicolon, Markdown table, JSON, and external-source sheets.
|
|
13
|
+
- Formula evaluation through HyperFormula, including cross-sheet references and named columns.
|
|
14
|
+
- HTML rendering with tabs, merged cells, inline formatting, and cell/row/column modifiers.
|
|
15
|
+
- JSON AST output for tooling.
|
|
16
|
+
- Validation diagnostics that return proper process exit codes.
|
|
17
|
+
- Library API and `cello` CLI.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @nachoggodino/cello
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Requirements:
|
|
26
|
+
|
|
27
|
+
- Node.js 22 or newer.
|
|
28
|
+
|
|
29
|
+
For local development in this repository:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install
|
|
33
|
+
npm run build
|
|
34
|
+
npm test
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
Create `sales.cel`:
|
|
40
|
+
|
|
41
|
+
```cel
|
|
42
|
+
@sheet Sales [csv]
|
|
43
|
+
product,price,quantity
|
|
44
|
+
Apple,1.2,5
|
|
45
|
+
Pear,0.9,3
|
|
46
|
+
|
|
47
|
+
@sheet KPI
|
|
48
|
+
@header | Metric | Value |
|
|
49
|
+
| Revenue | =Sales!B2*Sales!C2 + Sales!B3*Sales!C3 |
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Render it:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
cello render sales.cel -o sales.html
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Or open a live preview:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
cello serve sales.cel --open
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Rendered preview:
|
|
65
|
+
|
|
66
|
+
| Metric | Value |
|
|
67
|
+
| --- | ---: |
|
|
68
|
+
| Revenue | 8.7 |
|
|
69
|
+
|
|
70
|
+
For a larger example with named references, slices, and cross-sheet formulas, see [examples/advanced_kpi.cel](examples/advanced_kpi.cel).
|
|
71
|
+
|
|
72
|
+
## CLI
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
cello help
|
|
76
|
+
cello --version
|
|
77
|
+
cello parse <file.cel>
|
|
78
|
+
cello evaluate <file.cel>
|
|
79
|
+
cello format <file.cel> [--check] [-o out.cel]
|
|
80
|
+
cello validate <file.cel>
|
|
81
|
+
cello render <file.cel> [-o out.html] [--no-eval] [--format document|fragment]
|
|
82
|
+
cello serialize <file.cel> [-o out.cel]
|
|
83
|
+
cello serve <file.cel> [--port 4321] [--host 127.0.0.1] [--open] [--no-eval]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Exit codes:
|
|
87
|
+
|
|
88
|
+
- `0` means the command completed successfully.
|
|
89
|
+
- `1` means invalid arguments, validation diagnostics, or runtime failure.
|
|
90
|
+
|
|
91
|
+
Command details:
|
|
92
|
+
|
|
93
|
+
- `parse` prints the workbook AST as JSON.
|
|
94
|
+
- `evaluate` prints the AST with computed formula values.
|
|
95
|
+
- `format` pretty-prints native Cello pipe tables, writes in place by default, supports `-o/--out`, and uses `--check` to report formatting drift with exit code `1`.
|
|
96
|
+
- `validate` prints `{ "valid": boolean, "diagnostics": [...] }`; it exits `1` when diagnostics exist.
|
|
97
|
+
- `render` writes self-contained HTML with `-o/--out`, or prints HTML to stdout. `--format document` is the default full HTML document; `--format fragment` emits an embeddable chunk without `html`/`head`/`body` wrappers.
|
|
98
|
+
- `serialize` converts the parsed AST back to `.cel` text.
|
|
99
|
+
- `serve` starts a local live-preview server and only opens the browser when `--open` is provided.
|
|
100
|
+
|
|
101
|
+
## Library API
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { evaluate, format, parse, render, serialize, validate } from "@nachoggodino/cello";
|
|
105
|
+
|
|
106
|
+
const source = `
|
|
107
|
+
@sheet KPI
|
|
108
|
+
| Revenue | =1.2*5 + 0.9*3 |
|
|
109
|
+
`;
|
|
110
|
+
|
|
111
|
+
const ast = parse(source);
|
|
112
|
+
const evaluated = await evaluate(ast);
|
|
113
|
+
const pretty = format(source);
|
|
114
|
+
const result = await validate(source);
|
|
115
|
+
const html = await render(source);
|
|
116
|
+
const fragment = await render(source, { format: "fragment" });
|
|
117
|
+
const text = serialize(evaluated);
|
|
118
|
+
|
|
119
|
+
console.log(result.valid, pretty, html, text);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Primary exports:
|
|
123
|
+
|
|
124
|
+
- `parse(text, options?)`
|
|
125
|
+
- `evaluate(ast, options?)`
|
|
126
|
+
- `format(text)`
|
|
127
|
+
- `validate(text, options?)`
|
|
128
|
+
- `render(input, options?)`
|
|
129
|
+
- `serialize(ast)`
|
|
130
|
+
|
|
131
|
+
## Format Overview
|
|
132
|
+
|
|
133
|
+
Native Cello sheets use pipe-delimited rows:
|
|
134
|
+
|
|
135
|
+
```cel
|
|
136
|
+
@sheet Report
|
|
137
|
+
|
|
138
|
+
@header | Region | Revenue[€][2d] | Units[0d] |
|
|
139
|
+
| Madrid | 4280 | 15 |
|
|
140
|
+
| Barcelona | 2080 | 7 |
|
|
141
|
+
| Valencia | 760 | 2 |
|
|
142
|
+
| ## Total | =SUM(Revenue) | =SUM(Units) |
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Rendered preview:
|
|
146
|
+
|
|
147
|
+
| Region | Revenue | Units |
|
|
148
|
+
| --- | ---: | ---: |
|
|
149
|
+
| Madrid | €4,280.00 | 15 |
|
|
150
|
+
| Barcelona | €2,080.00 | 7 |
|
|
151
|
+
| Valencia | €760.00 | 2 |
|
|
152
|
+
| Total | €7,120.00 | 24 |
|
|
153
|
+
|
|
154
|
+
Useful syntax:
|
|
155
|
+
|
|
156
|
+
- `@sheet Name [format]` starts a sheet.
|
|
157
|
+
- `@header | Column | Names |` declares named columns.
|
|
158
|
+
- `@defaults | | | =Formula |` declares non-rendered column default formulas.
|
|
159
|
+
- `| cell | cell |` declares rows.
|
|
160
|
+
- `[bold] | ... |` applies row-level modifiers.
|
|
161
|
+
- `=A1+B1`, `=SUM(Revenue)`, and `=Sales!Amount` create formulas.
|
|
162
|
+
- `!!Amount` references a named column on the first sheet.
|
|
163
|
+
- `<` merges with the cell on the left; `^` merges with the cell above.
|
|
164
|
+
- `[€]`, `[2d]`, `[bold]`, `[bg:#fff9c4]`, and similar modifiers affect rendering.
|
|
165
|
+
|
|
166
|
+
The canonical syntax rules live in [BYLAWS.md](BYLAWS.md). The public specification lives in [docs/SPEC.md](docs/SPEC.md).
|
|
167
|
+
|
|
168
|
+
Editor integrations can reuse the TextMate grammar and VS Code language configuration documented in [docs/SYNTAX_HIGHLIGHTING.md](docs/SYNTAX_HIGHLIGHTING.md).
|
|
169
|
+
|
|
170
|
+
## Package Contents
|
|
171
|
+
|
|
172
|
+
The npm package publishes only the built library/CLI output and user-facing metadata:
|
|
173
|
+
|
|
174
|
+
- `dist/`
|
|
175
|
+
- `docs/`
|
|
176
|
+
- `examples/`
|
|
177
|
+
- `syntaxes/`
|
|
178
|
+
- `BYLAWS.md`
|
|
179
|
+
- `README.md`
|
|
180
|
+
- `CHANGELOG.md`
|
|
181
|
+
- `LICENSE`
|
|
182
|
+
|
|
183
|
+
## Development
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
npm run build
|
|
187
|
+
npm run typecheck
|
|
188
|
+
npm test
|
|
189
|
+
npm run coverage
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Repository layout:
|
|
193
|
+
|
|
194
|
+
- `src/parser/` parses workbooks into ASTs.
|
|
195
|
+
- `src/evaluator/` computes formulas.
|
|
196
|
+
- `src/formatter/` pretty-prints native Cello pipe tables.
|
|
197
|
+
- `src/validator/` reports parse/evaluation diagnostics.
|
|
198
|
+
- `src/renderer/` creates self-contained HTML.
|
|
199
|
+
- `src/serializer/` converts ASTs back to `.cel`.
|
|
200
|
+
- `src/cli/` exposes the command-line interface.
|
|
201
|
+
- `tests/` covers unit, integration, and fixture behavior.
|
|
202
|
+
|
|
203
|
+
## Versioning
|
|
204
|
+
|
|
205
|
+
This project uses Semantic Versioning. See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
GPL-3.0-only.
|
|
210
|
+
|
|
211
|
+
Cello uses HyperFormula for formula evaluation and configures it with `licenseKey: "gpl-v3"`. HyperFormula is available under GPLv3 or a commercial license from Handsontable; this package uses the GPLv3 option.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { startServe } from "./serve.js";
|
|
4
|
+
export interface CliDeps {
|
|
5
|
+
cwd: string;
|
|
6
|
+
readFileFn: typeof readFile;
|
|
7
|
+
writeFileFn: typeof writeFile;
|
|
8
|
+
startServeFn: typeof startServe;
|
|
9
|
+
stayOpenFn: () => Promise<number>;
|
|
10
|
+
stdoutWrite: (text: string) => void;
|
|
11
|
+
stderrWrite: (text: string) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function createCliDeps(overrides?: Partial<CliDeps>): CliDeps;
|
|
14
|
+
export declare function runCli(argv: string[], deps?: CliDeps): Promise<number>;
|
|
15
|
+
export declare function runMain(argv: string[], deps?: CliDeps, exitFn?: (code: number) => void, runCliFn?: (argv: string[], deps: CliDeps) => Promise<number>, stderrWrite?: (text: string) => void): Promise<void>;
|
|
16
|
+
export declare function isDirectCliExecution(argvPath: string, moduleUrl: string): boolean;
|