@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
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { buildWorkbookRefIndex, translateFormulaForEngine } from "./formula.js";
|
|
2
|
+
import { deepClone, workbookHasFormulas } from "../shared/utils.js";
|
|
3
|
+
let hyperFormulaCtor = null;
|
|
4
|
+
export async function evaluate(ast, options = {}) {
|
|
5
|
+
const output = deepClone(ast);
|
|
6
|
+
if (!workbookHasFormulas(output)) {
|
|
7
|
+
return output;
|
|
8
|
+
}
|
|
9
|
+
if (!(await loadHyperFormula(output))) {
|
|
10
|
+
return output;
|
|
11
|
+
}
|
|
12
|
+
const ctor = hyperFormulaCtor;
|
|
13
|
+
if (!ctor) {
|
|
14
|
+
output.diagnostics.push({
|
|
15
|
+
level: "error",
|
|
16
|
+
message: "HyperFormula failed to initialize."
|
|
17
|
+
});
|
|
18
|
+
return output;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const refIndex = buildWorkbookRefIndex(output);
|
|
22
|
+
const sheetsData = buildSheetsData(output, refIndex);
|
|
23
|
+
const hf = ctor.buildFromSheets(sheetsData, { licenseKey: "gpl-v3" });
|
|
24
|
+
applyComputedValues(output, hf);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
28
|
+
output.diagnostics.push({
|
|
29
|
+
level: "error",
|
|
30
|
+
message: `Formula evaluation failed: ${message}`
|
|
31
|
+
});
|
|
32
|
+
if (options.strict) {
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return output;
|
|
37
|
+
}
|
|
38
|
+
async function loadHyperFormula(output) {
|
|
39
|
+
try {
|
|
40
|
+
if (!hyperFormulaCtor) {
|
|
41
|
+
const module = await import("hyperformula");
|
|
42
|
+
const maybeCtor = module.HyperFormula;
|
|
43
|
+
if (!isHyperFormulaCtor(maybeCtor)) {
|
|
44
|
+
throw new Error("Invalid HyperFormula module export.");
|
|
45
|
+
}
|
|
46
|
+
hyperFormulaCtor = maybeCtor;
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
output.diagnostics.push({
|
|
52
|
+
level: "warning",
|
|
53
|
+
message: "HyperFormula is not available. Formula cells were not evaluated."
|
|
54
|
+
});
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function isHyperFormulaCtor(value) {
|
|
59
|
+
return ((typeof value === "object" || typeof value === "function") &&
|
|
60
|
+
value !== null &&
|
|
61
|
+
"buildFromSheets" in value &&
|
|
62
|
+
typeof value.buildFromSheets === "function");
|
|
63
|
+
}
|
|
64
|
+
function buildSheetsData(workbook, refIndex) {
|
|
65
|
+
return Object.fromEntries(workbook.sheets.map((sheet) => [sheet.name, buildSheetMatrix(sheet, workbook, refIndex)]));
|
|
66
|
+
}
|
|
67
|
+
function buildSheetMatrix(sheet, workbook, refIndex) {
|
|
68
|
+
const matrix = createEmptyMatrix(sheet.rows.length, sheet.columns.length);
|
|
69
|
+
for (const row of sheet.rows) {
|
|
70
|
+
const matrixRow = matrix[row.index - 1];
|
|
71
|
+
if (!matrixRow) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
for (const cell of row.cells) {
|
|
75
|
+
if (cell.kind === "merge-left" || cell.kind === "merge-up") {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
matrixRow[cell.col - 1] = toHyperFormulaValue(cell, sheet.name, refIndex, workbook);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return matrix;
|
|
82
|
+
}
|
|
83
|
+
function createEmptyMatrix(rowCount, colCount) {
|
|
84
|
+
return Array.from({ length: rowCount }, () => Array.from({ length: colCount }, () => null));
|
|
85
|
+
}
|
|
86
|
+
function applyComputedValues(workbook, hf) {
|
|
87
|
+
for (const sheet of workbook.sheets) {
|
|
88
|
+
const sheetId = hf.getSheetId(sheet.name);
|
|
89
|
+
if (sheetId === undefined || sheetId === null) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
for (const row of sheet.rows) {
|
|
93
|
+
for (const cell of row.cells) {
|
|
94
|
+
if (cell.kind !== "formula") {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const evaluated = hf.getCellValue({ sheet: sheetId, row: row.index - 1, col: cell.col - 1 });
|
|
98
|
+
cell.computed = normalizeValue(evaluated, cell.formula);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function toHyperFormulaValue(cell, sheetName, refIndex, output) {
|
|
104
|
+
if (cell.kind === "formula" && cell.formula) {
|
|
105
|
+
return translateFormulaForEngine(cell.formula, sheetName, refIndex, output.diagnostics, cell.row);
|
|
106
|
+
}
|
|
107
|
+
return cell.value;
|
|
108
|
+
}
|
|
109
|
+
function normalizeValue(value, formula) {
|
|
110
|
+
if (value === null || value === undefined) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "string") {
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
if (isDetailedCellError(value)) {
|
|
117
|
+
if (isFormulaParseError(value) && formula) {
|
|
118
|
+
return formula;
|
|
119
|
+
}
|
|
120
|
+
return value.value;
|
|
121
|
+
}
|
|
122
|
+
return String(value);
|
|
123
|
+
}
|
|
124
|
+
function isDetailedCellError(value) {
|
|
125
|
+
return typeof value === "object" && value !== null && "value" in value && typeof value.value === "string";
|
|
126
|
+
}
|
|
127
|
+
function isFormulaParseError(value) {
|
|
128
|
+
return value.type === "ERROR" && typeof value.message === "string" && value.message.includes("Parsing error");
|
|
129
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Diagnostic, WorkbookAst } from "../shared/types.js";
|
|
2
|
+
interface SheetRefIndex {
|
|
3
|
+
firstDataRow: number;
|
|
4
|
+
lastDataRow: number;
|
|
5
|
+
columnsByName: Map<string, number>;
|
|
6
|
+
}
|
|
7
|
+
interface WorkbookRefIndex {
|
|
8
|
+
firstSheetName: string | undefined;
|
|
9
|
+
bySheetName: Map<string, SheetRefIndex>;
|
|
10
|
+
}
|
|
11
|
+
export declare function buildWorkbookRefIndex(workbook: WorkbookAst): WorkbookRefIndex;
|
|
12
|
+
export declare function translateFormulaForEngine(formula: string, sheetName: string, index: WorkbookRefIndex, diagnostics: Diagnostic[], currentRow?: number): string;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { columnLetter } from "../shared/utils.js";
|
|
2
|
+
export function buildWorkbookRefIndex(workbook) {
|
|
3
|
+
const bySheetName = new Map();
|
|
4
|
+
for (const sheet of workbook.sheets) {
|
|
5
|
+
bySheetName.set(sheet.name, buildSheetRefIndex(sheet));
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
firstSheetName: workbook.sheets[0]?.name,
|
|
9
|
+
bySheetName
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function translateFormulaForEngine(formula, sheetName, index, diagnostics, currentRow) {
|
|
13
|
+
let translated = normalizeFunctionAliases(formula);
|
|
14
|
+
translated = translated.replace(/!!([A-Za-z_][A-Za-z0-9_]*(?:\[(?:\d+:\d+|\*)\])?|[A-Za-z]+\d+)/g, (_m, token) => {
|
|
15
|
+
if (!index.firstSheetName) {
|
|
16
|
+
return `!!${token}`;
|
|
17
|
+
}
|
|
18
|
+
return `${index.firstSheetName}!${token}`;
|
|
19
|
+
});
|
|
20
|
+
translated = translated.replace(/([A-Za-z_][A-Za-z0-9_]*)!([A-Za-z_][A-Za-z0-9_]*)(?:\[(?:(\d+):(\d+)|(\*))\])?/g, (m, targetSheet, token, start, end, fullSpan) => {
|
|
21
|
+
if (isA1Ref(token)) {
|
|
22
|
+
return m;
|
|
23
|
+
}
|
|
24
|
+
const target = index.bySheetName.get(targetSheet);
|
|
25
|
+
if (!target) {
|
|
26
|
+
return m;
|
|
27
|
+
}
|
|
28
|
+
const col = target.columnsByName.get(token.toLowerCase());
|
|
29
|
+
if (!col) {
|
|
30
|
+
return m;
|
|
31
|
+
}
|
|
32
|
+
if (fullSpan) {
|
|
33
|
+
return toFullColumnRange(targetSheet, col, target, m, sheetName, diagnostics, token);
|
|
34
|
+
}
|
|
35
|
+
if (start && end) {
|
|
36
|
+
return `${targetSheet}!${columnLetter(col)}${start}:${columnLetter(col)}${end}`;
|
|
37
|
+
}
|
|
38
|
+
return toFullColumnRange(targetSheet, col, target, m, sheetName, diagnostics, token);
|
|
39
|
+
});
|
|
40
|
+
translated = translated.replace(/([A-Za-z_][A-Za-z0-9_]*)(?:\[(?:(\d+):(\d+)|(\*))\])?/g, (m, token, start, end, fullSpan, offset, source) => {
|
|
41
|
+
const prev = offset > 0 ? (source[offset - 1] ?? "") : "";
|
|
42
|
+
const next = source[offset + m.length] ?? "";
|
|
43
|
+
const prevOk = prev === "" || /[^\w.]/.test(prev);
|
|
44
|
+
const nextOk = next === "" || /[^\w]/.test(next);
|
|
45
|
+
if (!prevOk || !nextOk || prev === "!" || prev === '"' || next === "(" || isA1Ref(token) || isKeyword(token)) {
|
|
46
|
+
return m;
|
|
47
|
+
}
|
|
48
|
+
const currentSheet = index.bySheetName.get(sheetName);
|
|
49
|
+
if (!currentSheet) {
|
|
50
|
+
return m;
|
|
51
|
+
}
|
|
52
|
+
const col = currentSheet.columnsByName.get(token.toLowerCase());
|
|
53
|
+
if (!col) {
|
|
54
|
+
return m;
|
|
55
|
+
}
|
|
56
|
+
if (fullSpan) {
|
|
57
|
+
return toFullColumnRange(undefined, col, currentSheet, m, sheetName, diagnostics, token);
|
|
58
|
+
}
|
|
59
|
+
if (start && end) {
|
|
60
|
+
return `${columnLetter(col)}${start}:${columnLetter(col)}${end}`;
|
|
61
|
+
}
|
|
62
|
+
if (isAggregateReferenceContext(source, offset, token)) {
|
|
63
|
+
if (currentRow && currentRow > currentSheet.firstDataRow) {
|
|
64
|
+
return `${columnLetter(col)}${currentSheet.firstDataRow}:${columnLetter(col)}${currentRow - 1}`;
|
|
65
|
+
}
|
|
66
|
+
return toFullColumnRange(undefined, col, currentSheet, m, sheetName, diagnostics, token);
|
|
67
|
+
}
|
|
68
|
+
if (!currentRow) {
|
|
69
|
+
return m;
|
|
70
|
+
}
|
|
71
|
+
return `${columnLetter(col)}${currentRow}`;
|
|
72
|
+
});
|
|
73
|
+
return translated;
|
|
74
|
+
}
|
|
75
|
+
function normalizeFunctionAliases(formula) {
|
|
76
|
+
return formula.replace(/\bAVG\s*\(/gi, "AVERAGE(");
|
|
77
|
+
}
|
|
78
|
+
function toFullColumnRange(targetSheet, col, target, fallback, sheetName, diagnostics, token) {
|
|
79
|
+
if (target.firstDataRow === 0 || target.lastDataRow === 0) {
|
|
80
|
+
diagnostics.push({
|
|
81
|
+
level: "warning",
|
|
82
|
+
sheet: sheetName,
|
|
83
|
+
message: `Named reference "${targetSheet ? `${targetSheet}!` : ""}${token}" has no data rows to resolve.`
|
|
84
|
+
});
|
|
85
|
+
return fallback;
|
|
86
|
+
}
|
|
87
|
+
const range = `${columnLetter(col)}${target.firstDataRow}:${columnLetter(col)}${target.lastDataRow}`;
|
|
88
|
+
return targetSheet ? `${targetSheet}!${range}` : range;
|
|
89
|
+
}
|
|
90
|
+
function isAggregateReferenceContext(source, offset, token) {
|
|
91
|
+
const aggregateFunctions = new Set(["SUM", "AVG", "AVERAGE", "MIN", "MAX", "COUNT", "COUNTA", "PRODUCT"]);
|
|
92
|
+
let depth = 0;
|
|
93
|
+
for (let i = offset - 1; i >= 0; i -= 1) {
|
|
94
|
+
const ch = source[i] ?? "";
|
|
95
|
+
if (ch === ")") {
|
|
96
|
+
depth += 1;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (ch === "(") {
|
|
100
|
+
if (depth > 0) {
|
|
101
|
+
depth -= 1;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
let end = i - 1;
|
|
105
|
+
while (end >= 0 && /\s/.test(source[end] ?? "")) {
|
|
106
|
+
end -= 1;
|
|
107
|
+
}
|
|
108
|
+
let start = end;
|
|
109
|
+
while (start >= 0 && /[A-Za-z_]/.test(source[start] ?? "")) {
|
|
110
|
+
start -= 1;
|
|
111
|
+
}
|
|
112
|
+
const fn = source.slice(start + 1, end + 1).toUpperCase();
|
|
113
|
+
if (!aggregateFunctions.has(fn)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const between = source.slice(i + 1, offset).trim();
|
|
117
|
+
const after = source.slice(offset + token.length).trimStart();
|
|
118
|
+
return (between === "" || between === ",") && (after === "" || after.startsWith(")") || after.startsWith(","));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
function buildSheetRefIndex(sheet) {
|
|
124
|
+
const dataRows = sheet.rows.filter((row) => row.kind === "data").map((row) => row.index);
|
|
125
|
+
const firstDataRow = dataRows[0] ?? 0;
|
|
126
|
+
const lastDataRow = dataRows[dataRows.length - 1] ?? 0;
|
|
127
|
+
const columnsByName = new Map();
|
|
128
|
+
for (const column of sheet.columns) {
|
|
129
|
+
if (column.name && column.name.trim().length > 0) {
|
|
130
|
+
columnsByName.set(column.name.toLowerCase(), column.index);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return { firstDataRow, lastDataRow, columnsByName };
|
|
134
|
+
}
|
|
135
|
+
function isKeyword(token) {
|
|
136
|
+
const upper = token.toUpperCase();
|
|
137
|
+
return upper === "TRUE" || upper === "FALSE";
|
|
138
|
+
}
|
|
139
|
+
function isA1Ref(token) {
|
|
140
|
+
return /^\$?[A-Za-z]{1,3}\$?\d+$/.test(token);
|
|
141
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function format(text: string): string;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { parseSheetFormat, parseTrailingModifiers } from "../shared/utils.js";
|
|
2
|
+
export function format(text) {
|
|
3
|
+
const lines = text.replace(/\r\n/g, "\n").split("\n");
|
|
4
|
+
const output = [];
|
|
5
|
+
let activeSheetFormat = "cello";
|
|
6
|
+
let block = [];
|
|
7
|
+
const flushBlock = () => {
|
|
8
|
+
if (block.length === 0) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
output.push(...formatBlock(block));
|
|
12
|
+
block = [];
|
|
13
|
+
};
|
|
14
|
+
for (const line of lines) {
|
|
15
|
+
const trimmed = line.trim();
|
|
16
|
+
if (trimmed.startsWith("//") || trimmed.length === 0) {
|
|
17
|
+
flushBlock();
|
|
18
|
+
output.push(line);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const sheetMatch = trimmed.match(/^@sheet\s+(.+?)(?:\s+\[(.+)\])?$/);
|
|
22
|
+
if (sheetMatch) {
|
|
23
|
+
flushBlock();
|
|
24
|
+
activeSheetFormat = parseSheetFormat(sheetMatch[2]?.trim()).kind === "cello" ? "cello" : "other";
|
|
25
|
+
output.push(line);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (activeSheetFormat !== "cello") {
|
|
29
|
+
flushBlock();
|
|
30
|
+
output.push(line);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const parsed = parseFormatRow(line);
|
|
34
|
+
if (parsed) {
|
|
35
|
+
block.push(parsed);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
flushBlock();
|
|
39
|
+
output.push(line);
|
|
40
|
+
}
|
|
41
|
+
flushBlock();
|
|
42
|
+
return output.join("\n");
|
|
43
|
+
}
|
|
44
|
+
function parseFormatRow(line) {
|
|
45
|
+
const leading = "";
|
|
46
|
+
const content = line.trimStart();
|
|
47
|
+
if (/^@header(?:\s|$)/.test(content)) {
|
|
48
|
+
return parseDirectiveRow(leading, content, "@header");
|
|
49
|
+
}
|
|
50
|
+
if (/^@defaults(?:\s|$)/.test(content)) {
|
|
51
|
+
return parseDirectiveRow(leading, content, "@defaults");
|
|
52
|
+
}
|
|
53
|
+
const firstPipe = content.indexOf("|");
|
|
54
|
+
if (firstPipe < 0) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const prefix = content.slice(0, firstPipe).trim();
|
|
58
|
+
const body = content.slice(firstPipe);
|
|
59
|
+
const cells = splitPipeCells(body);
|
|
60
|
+
if (prefix.length === 0) {
|
|
61
|
+
return { leading, prefix: "", cells };
|
|
62
|
+
}
|
|
63
|
+
const parsed = parseTrailingModifiers(prefix);
|
|
64
|
+
if (parsed.base.length === 0) {
|
|
65
|
+
return { leading, prefix, cells };
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
function parseDirectiveRow(leading, content, directive) {
|
|
70
|
+
const body = content.slice(directive.length).trim();
|
|
71
|
+
if (!body.includes("|")) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
leading,
|
|
76
|
+
prefix: directive,
|
|
77
|
+
cells: splitPipeCells(body)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function splitPipeCells(line) {
|
|
81
|
+
const tokens = line.split("|").map((token) => token.trim());
|
|
82
|
+
return tokens
|
|
83
|
+
.filter((_, index) => !(index === 0 && tokens[0] === ""))
|
|
84
|
+
.filter((_, index, all) => !(index === all.length - 1 && all[index] === ""));
|
|
85
|
+
}
|
|
86
|
+
function formatBlock(rows) {
|
|
87
|
+
const prefixWidth = rows.reduce((max, row) => Math.max(max, row.prefix.length), 0);
|
|
88
|
+
const columnCount = rows.reduce((max, row) => Math.max(max, row.cells.length), 0);
|
|
89
|
+
const columnWidths = new Array(columnCount).fill(0);
|
|
90
|
+
for (const row of rows) {
|
|
91
|
+
for (let index = 0; index < columnCount; index += 1) {
|
|
92
|
+
const cell = row.cells[index] ?? "";
|
|
93
|
+
if (cell.length > (columnWidths[index] ?? 0)) {
|
|
94
|
+
columnWidths[index] = cell.length;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return rows.map((row) => formatRow(row, prefixWidth, columnWidths));
|
|
99
|
+
}
|
|
100
|
+
function formatRow(row, prefixWidth, columnWidths) {
|
|
101
|
+
const formattedPrefix = row.prefix.length > 0
|
|
102
|
+
? `${row.prefix}${" ".repeat(prefixWidth - row.prefix.length + 1)}`
|
|
103
|
+
: prefixWidth > 0
|
|
104
|
+
? " ".repeat(prefixWidth + 1)
|
|
105
|
+
: "";
|
|
106
|
+
let formatted = `${row.leading}${formattedPrefix}|`;
|
|
107
|
+
for (let index = 0; index < columnWidths.length; index += 1) {
|
|
108
|
+
const cell = row.cells[index] ?? "";
|
|
109
|
+
formatted += ` ${cell.padEnd(columnWidths[index] ?? 0)} |`;
|
|
110
|
+
}
|
|
111
|
+
return formatted;
|
|
112
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { parse } from "./parser/parse.js";
|
|
2
|
+
export { evaluate } from "./evaluator/evaluate.js";
|
|
3
|
+
export { format } from "./formatter/format.js";
|
|
4
|
+
export { render } from "./renderer/render.js";
|
|
5
|
+
export { serialize } from "./serializer/serialize.js";
|
|
6
|
+
export { validate } from "./validator/validate.js";
|
|
7
|
+
export type { CellKind, CellNode, ColumnNode, Diagnostic, EvaluateOptions, InferredType, Modifier, ParseOptions, RenderOptions, RowKind, RowNode, SheetFormat, SheetNode, WorkbookAst } from "./shared/types.js";
|
|
8
|
+
export type { ValidateOptions, ValidateResult } from "./validator/validate.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { parse } from "./parser/parse.js";
|
|
2
|
+
export { evaluate } from "./evaluator/evaluate.js";
|
|
3
|
+
export { format } from "./formatter/format.js";
|
|
4
|
+
export { render } from "./renderer/render.js";
|
|
5
|
+
export { serialize } from "./serializer/serialize.js";
|
|
6
|
+
export { validate } from "./validator/validate.js";
|