A YAML-driven Excel (.xlsx) compiler and CLI — manage spreadsheets as version-controllable YAML with reuse/dedup, built on bobzhang/mbtexcel.
Dependencies
⚠️ Status: pre-release, schema not yet frozen. yxl build works today — values, formulas, dates, durations, rich text, styles, layout, print setup, multi-file specs, external CSV/JSON data, parameters, one-off overrides, validations, conditional formatting, hyperlinks, notes, protection, Excel tables, charts, images, shapes, sheet backgrounds, sparklines, form controls, table slicers, and pivot tables all compile, and the CLI has init, --check, --set, and stable exit codes. Still ahead: performance work, import, and the schema freeze — the schema may change until v1.0. See ROADMAP.md for the phase plan and the living changelog.
# report.yxl.yaml
params: # overridable per build with --set
region: APAC
quarter: Q3
defs: # declared once, referenced by name
styles:
base: { font: { name: Calibri, size: 11 } }
title: { extends: base, font: { bold: true, size: 16 } }
header: { extends: base, font: { bold: true, color: "FFFFFF" }, fill: "1F3864" }
total: { extends: base, font: { bold: true } }
sheets:
- name: "${region}"
freeze: A4 # the header rows stay put while the data scrolls
merges: [A1:B1]
columns:
- at: A
width: 18
- at: B
width: 14
format: "#,##0" # a whole-column default format
cells:
A1: { value: "${quarter} ${region} sales", style: title }
A3: { value: Region, style: header }
B3: { value: Revenue, style: header }
A7: { value: Total, style: total }
B7: { formula: "SUM(B4:B6)", style: total }
data:
- at: A4
csv: data/sales.csv # rows from a file the spec never restates —
# or write them here with `values:` instead
print:
area: A1:B7
orientation: landscape
fit: { width: 1 } # shrink to one page acrossyxl build report.yxl.yaml -o q3-apac.xlsx
yxl build report.yxl.yaml -o q4-emea.xlsx --set region=EMEA --set quarter=Q4curl -fsSL https://raw.githubusercontent.com/t-ujiie-g/yxl/main/install.sh | shirm https://raw.githubusercontent.com/t-ujiie-g/yxl/main/install.ps1 | iexYXL_VERSION=0.1.0 YXL_INSTALL_DIR=/usr/local/bin \
curl -fsSL https://raw.githubusercontent.com/t-ujiie-g/yxl/main/install.sh | shmoonx t-ujiie-g/yxl/cmd/main@0.6.1 build report.yxl.yaml
moonx t-ujiie-g/yxl/cmd/main@0.6.1 extract existing.xlsx -o spec.yxl.yamlmoon add t-ujiie-g/yxl// spec text -> .xlsx bytes
let bytes = @cli.compile(spec_text, file="report.yxl.yaml")
// .xlsx bytes -> spec text, plus what could not be carried
let result = @cli.extract(bytes, file="existing.xlsx")
println(result.spec)git clone https://github.com/t-ujiie-g/yxl.git
cd yxl
moon build --target native --release
install -m 755 _build/native/release/build/cmd/main/main.exe ~/.local/bin/yxlyxl init -o sheet.yxl.yaml # a first spec: one empty sheet
yxl build # compile the one *.yxl.yaml here
yxl build report.yxl.yaml # compile → report.xlsx
yxl build report.yxl.yaml -o out.xlsx # … to a path of your choosing
yxl build report.yxl.yaml --check # validate, write nothing
yxl build report.yxl.yaml -o r.xlsx --set region=EMEA
yxl extract legacy.xlsx -o legacy.yxl.yaml # existing workbook → starting spec
yxl version # print the version
yxl help # full usagecode --install-extension redhat.vscode-yaml# yaml-language-server: $schema=https://raw.githubusercontent.com/t-ujiie-g/yxl/main/docs/yxl.schema.json{
"yaml.schemas": {
"https://raw.githubusercontent.com/t-ujiie-g/yxl/main/docs/yxl.schema.json": "*.yxl.yaml"
}
}| Code | Meaning |
|---|---|
| 0 | success |
| 1 | the spec is invalid, or a file could not be read or written |
| 2 | the command line itself was wrong |
| Example | Shows |
|---|---|
| quickstart.yxl.yaml | cell kinds, number formats, a formula, a filled formula column, a block of rows written inline |
| styling.yxl.yaml | declare-once styles, extends, defined names, rich text, conditional formatting |
| layout.yxl.yaml | merges, frozen headers, sized and grouped bands, sheet visibility, print setup, document properties, an image, a watermark background |
| modular.yxl.yaml | $include, CSV / JSON data:, an Excel table over the region they fill, a slicer filtering it |
| parameters.yxl.yaml | params: with ${} and --set |
| interactive.yxl.yaml | drop-downs and other validations, an auto filter saved with a criterion, hyperlinks, notes, sheet protection, a check box and spin button |
| charts.yxl.yaml | column, pie, and bar charts, series named from cells, one plotting another sheet |
| pivots.yxl.yaml | two pivot tables: rows, columns, a filters: axis, and two aggregations |
| shapes.yxl.yaml | a cloud stamp, a chevron with two text lines, a pinned star |
| sparklines.yxl.yaml | a trend line per row with markers, and win/loss cells plotting another sheet |
| overrides.yxl.yaml | one-off exceptions written down as exceptions: a row of a filled formula range that does not follow it, a corrected value, a style alone — each with its reason |
| columns.yxl.yaml | a year-on-year report whose columns are named, not lettered: layouts: with a defs.blocks group placed once per item, a two-row merged header, a totals row, {{name}} formulas, per-column conditional formatting, and rows that fill only the input columns |
| subtotals.yxl.yaml | a store list with subtotals beneath it — per branch, per channel within each branch, and a grand total — as live SUMIFS formulas in a fixed order; a named layout another sheet sums by name, a chart placed under the list and plotting it by name, a store drop-down fed by it, and an override that finds its row by the data's values |
| workbook.yxl.yaml | the layout to keep a real workbook in: a sheet per file, styles named once, one store master every sheet references, and a month's data swapped with --set |
yxl build examples/quickstart.yxl.yaml -o quickstart.xlsx| Skill | Covers |
|---|---|
| yxl-authoring | the general workflow: the default architecture to build unless told otherwise (a sheet per file, styles named once, one master per shared list), writing a spec from scratch, editing an existing one, and operating it month to month |
| extract-to-spec | migration: yxl extract gives a starting point from an existing workbook, and this is the rewrite that makes it maintainable |
npx skills add t-ujiie-g/yxl # any agent — installs from skills/report.yxl.yaml
→ parse (YAML → document tree)
→ load + validate + resolve references (typed model, diagnostics naming the file)
→ emit: intern shared styles, strings, and defined names ← the DRY engine
via a swappable backend (moonbitlang/mbtexcel)
→ report.xlsx| Package | Purpose |
|---|---|
| cli | compile and extract, I/O-free, plus argument parsing and exit codes |
| include | The resolver types compile takes for included files |
| cmd/main | The yxl executable: file I/O around cli |
| internal/diag | Diagnostics + subdomain errors with source spans |
| internal/units | Type-safe cell references, colors, dates |
| internal/yaml | YAML source → document tree |
| internal/model | Typed intermediate representation |
| internal/loader | Document tree → model: schema validation, reference resolution |
| internal/emit | Model → .xlsx bytes (mbtexcel-backed), style/string interning |
| internal/read | .xlsx bytes → model, the mirror of emit (for yxl extract) |
| internal/render | Model → document tree, the mirror of loader, where extracted styles get their names |
| examples | Test-only: compiles the examples/ cookbook and asserts on it |
moon check --deny-warn # type check
moon test # tests
moon fmt # format
moon info # regenerate the .mbti interface files
moon build --target native # build the CLIInstall
Download zipA YAML-driven Excel (.xlsx) compiler and CLI — manage spreadsheets as version-controllable YAML with reuse/dedup, built on bobzhang/mbtexcel.
Dependencies