@particle-academy/holy-sheet 2.3.0 → 2.3.1
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/dist/index.cjs +48 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +48 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -721,6 +721,7 @@ var ERR_REF = "#REF!";
|
|
|
721
721
|
var ERR_NAME = "#NAME?";
|
|
722
722
|
var ERR_DIV0 = "#DIV/0!";
|
|
723
723
|
var ERR_CIRC = "#CIRC!";
|
|
724
|
+
var SHEET_QUALIFIER = "('(?:[^']|'')+'|[A-Za-z_][A-Za-z0-9_.]*)!";
|
|
724
725
|
var LinterError = class extends Error {
|
|
725
726
|
constructor(errorCode) {
|
|
726
727
|
super(errorCode);
|
|
@@ -731,9 +732,16 @@ var FormulaLinter = class {
|
|
|
731
732
|
constructor() {
|
|
732
733
|
this.index = /* @__PURE__ */ new Map();
|
|
733
734
|
this.cache = /* @__PURE__ */ new Map();
|
|
735
|
+
/**
|
|
736
|
+
* Every sheet name, keyed by its lower-cased form. Excel matches sheet names
|
|
737
|
+
* without regard to case, and a reference to a sheet that is not here is
|
|
738
|
+
* `#REF!` rather than a range of blanks.
|
|
739
|
+
*/
|
|
740
|
+
this.sheetNames = /* @__PURE__ */ new Map();
|
|
734
741
|
}
|
|
735
742
|
lint(schema) {
|
|
736
743
|
const workbook = new Normalizer().normalize(schema);
|
|
744
|
+
this.sheetNames = new Map(workbook.sheets.map((sheet) => [sheet.name.toLowerCase(), sheet.name]));
|
|
737
745
|
this.index = this.buildIndex(workbook);
|
|
738
746
|
this.cache = /* @__PURE__ */ new Map();
|
|
739
747
|
const issues = [];
|
|
@@ -809,6 +817,24 @@ var FormulaLinter = class {
|
|
|
809
817
|
tokens.push({ type: "STRING", value });
|
|
810
818
|
continue;
|
|
811
819
|
}
|
|
820
|
+
if (ch === "'") {
|
|
821
|
+
const start = i;
|
|
822
|
+
i++;
|
|
823
|
+
for (; ; ) {
|
|
824
|
+
if (i >= len) throw new LinterError(ERR_NAME);
|
|
825
|
+
if (src[i] === "'") {
|
|
826
|
+
if (i + 1 < len && src[i + 1] === "'") {
|
|
827
|
+
i += 2;
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
i++;
|
|
831
|
+
break;
|
|
832
|
+
}
|
|
833
|
+
i++;
|
|
834
|
+
}
|
|
835
|
+
tokens.push({ type: "SHEET", value: src.slice(start, i) });
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
812
838
|
if (isAlpha(ch) || ch === "_" || ch === "$") {
|
|
813
839
|
const start = i;
|
|
814
840
|
while (i < len && (isAlnum(src[i]) || src[i] === "_" || src[i] === "$" || src[i] === ".")) i++;
|
|
@@ -926,16 +952,18 @@ var FormulaLinter = class {
|
|
|
926
952
|
c.pos++;
|
|
927
953
|
return tok.value;
|
|
928
954
|
}
|
|
929
|
-
if (tok.type === "IDENT") {
|
|
955
|
+
if (tok.type === "SHEET" || tok.type === "IDENT") {
|
|
930
956
|
const next = c.tokens[c.pos + 1];
|
|
931
957
|
const next2 = c.tokens[c.pos + 2];
|
|
932
958
|
if (next && next.type === "OP" && next.value === "!" && next2 && next2.type === "IDENT") {
|
|
933
|
-
const sheetName = this.cleanSheetName(tok.value);
|
|
959
|
+
const sheetName = this.sheetNames.get(this.cleanSheetName(tok.value).toLowerCase());
|
|
960
|
+
if (sheetName === void 0) throw new LinterError(ERR_REF);
|
|
934
961
|
c.pos += 2;
|
|
935
962
|
const startTok = c.tokens[c.pos].value;
|
|
936
963
|
c.pos++;
|
|
937
964
|
return this.resolveRefOrRange(startTok, sheetName, c, stack);
|
|
938
965
|
}
|
|
966
|
+
if (tok.type === "SHEET") throw new LinterError(ERR_NAME);
|
|
939
967
|
if (next && next.type === "OP" && next.value === "(") {
|
|
940
968
|
const name = tok.value.toUpperCase();
|
|
941
969
|
c.pos += 2;
|
|
@@ -980,9 +1008,13 @@ var FormulaLinter = class {
|
|
|
980
1008
|
const r = ref.replace(/\$/g, "").toUpperCase();
|
|
981
1009
|
return /^[A-Z]+\d+$/.test(r) ? r : null;
|
|
982
1010
|
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Unwrap the single quotes Excel puts around a sheet name that needs them,
|
|
1013
|
+
* and turn its doubled quote back into one: 'Q3 ''Final''' is Q3 'Final'.
|
|
1014
|
+
*/
|
|
983
1015
|
cleanSheetName(name) {
|
|
984
1016
|
if (name.length >= 2 && name[0] === "'" && name[name.length - 1] === "'") {
|
|
985
|
-
return name.slice(1, -1);
|
|
1017
|
+
return name.slice(1, -1).split("''").join("'");
|
|
986
1018
|
}
|
|
987
1019
|
return name;
|
|
988
1020
|
}
|
|
@@ -1150,7 +1182,7 @@ var FormulaLinter = class {
|
|
|
1150
1182
|
case ERR_VALUE:
|
|
1151
1183
|
return this.hintValue(formula, sheet);
|
|
1152
1184
|
case ERR_REF:
|
|
1153
|
-
return
|
|
1185
|
+
return this.hintRef(formula);
|
|
1154
1186
|
case ERR_NAME:
|
|
1155
1187
|
return "The formula references an unknown function or has a syntax error. Holy Sheet supports: SUM, AVERAGE, COUNT, COUNTA, MIN, MAX, IF, ROUND, ABS, LEN, UPPER, LOWER, CONCAT.";
|
|
1156
1188
|
case ERR_DIV0:
|
|
@@ -1161,11 +1193,20 @@ var FormulaLinter = class {
|
|
|
1161
1193
|
return "Formula evaluation failed.";
|
|
1162
1194
|
}
|
|
1163
1195
|
}
|
|
1196
|
+
hintRef(formula) {
|
|
1197
|
+
for (const m of formula.matchAll(new RegExp(SHEET_QUALIFIER, "g"))) {
|
|
1198
|
+
const name = this.cleanSheetName(m[1]);
|
|
1199
|
+
if (!this.sheetNames.has(name.toLowerCase())) {
|
|
1200
|
+
return `The formula refers to a sheet named '${name}', and this workbook has no such sheet. Its sheets are: ${[...this.sheetNames.values()].join(", ")}. Quote a name that contains spaces or punctuation: 'My Sheet'!A1.`;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
return "A cell reference points to a cell that doesn't exist in the workbook. Check column letters and row numbers.";
|
|
1204
|
+
}
|
|
1164
1205
|
hintValue(formula, sheet) {
|
|
1165
|
-
const re =
|
|
1206
|
+
const re = new RegExp(`(?:${SHEET_QUALIFIER})?\\$?([A-Z]+)\\$?(\\d+)`, "gi");
|
|
1166
1207
|
const offenders = [];
|
|
1167
1208
|
for (const m of formula.matchAll(re)) {
|
|
1168
|
-
const sheetName = m[1] ? m[1] : sheet;
|
|
1209
|
+
const sheetName = m[1] ? this.sheetNames.get(this.cleanSheetName(m[1]).toLowerCase()) ?? this.cleanSheetName(m[1]) : sheet;
|
|
1169
1210
|
const a1 = m[2].toUpperCase() + m[3];
|
|
1170
1211
|
const key = sheetName + "!" + a1;
|
|
1171
1212
|
const cell = this.index.get(key);
|
|
@@ -4115,7 +4156,7 @@ var holy_sheet_schema_default = {
|
|
|
4115
4156
|
};
|
|
4116
4157
|
|
|
4117
4158
|
// src/agent.ts
|
|
4118
|
-
var VERSION = "2.3.
|
|
4159
|
+
var VERSION = "2.3.1";
|
|
4119
4160
|
function toU8(input) {
|
|
4120
4161
|
return input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
4121
4162
|
}
|