werkmap 0.6.0 → 0.8.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/README.md +22 -2
- package/lib/index.d.ts +30 -1
- package/lib/index.js +156 -20
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Writing is the whole surface, so the package stays small enough to audit. The co
|
|
|
9
9
|
- **Zero dependencies.** 7.7 kB minified and brotlied, for the whole writer.
|
|
10
10
|
- **Strict CSP, including in the browser.** No `unsafe-eval` and no `unsafe-inline`. A Chromium page under `default-src 'none'; script-src 'self'` writes a workbook and reports any violation back, and the Node suite runs on `--disallow-code-generation-from-strings`.
|
|
11
11
|
- **Write-only, deliberately.** No reader, no formula engine, no chart support — see [Is werkmap the right tool?](#is-werkmap-the-right-tool) before you install it.
|
|
12
|
-
- **Hardened.**
|
|
12
|
+
- **Hardened.** 85 tests at 100% branch coverage.
|
|
13
13
|
|
|
14
14
|
```js
|
|
15
15
|
import { workbook } from "werkmap";
|
|
@@ -118,7 +118,17 @@ The finished package. This does not seal the document: call it as often as you l
|
|
|
118
118
|
|
|
119
119
|
`cells` is an array; the returned number is the row's 1-based position, which `merge`, `freeze` and `place` take.
|
|
120
120
|
|
|
121
|
-
`options.level` is the row's **outline level**, an integer from 0 to 7. A reader draws the levels as collapsible groups in its left margin — the way Excel's own Group command does — and reads the summary row as the one **below** each group, which is where a total row sits. `0`, the default, is a row outside any group, and a row given no options is at 0.
|
|
121
|
+
`options.level` is the row's **outline level**, an integer from 0 to 7. A reader draws the levels as collapsible groups in its left margin — the way Excel's own Group command does — and reads the summary row as the one **below** each group, which is where a total row sits. `0`, the default, is a row outside any group, and a row given no options is at 0.
|
|
122
|
+
|
|
123
|
+
`options.hidden` and `options.collapsed` say how a group opens. A collapsed group is **both**: its content rows `hidden`, and its summary row `collapsed`. Hiding alone leaves the group's control showing expanded over rows nobody can see, which is a document in two minds rather than a collapsed group. Neither is set by default, so a row you write is in the document and open unless you say otherwise, and what the reader then does with the controls is theirs.
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
sheet.row([{ value: "North" }], { level: 1 });
|
|
127
|
+
sheet.row([{ value: "Laptop" }], { level: 2, hidden: true });
|
|
128
|
+
sheet.row([{ value: "Subtotal" }], { level: 1, collapsed: true });
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
An option this does not know is **refused**, not ignored: a misspelt one would otherwise be a row attribute you asked for and never got.
|
|
122
132
|
|
|
123
133
|
A sheet that outlines any row also states the workbook's default row height, 15 points, because the format requires it beside the deepest level. A sheet with no outline states neither, and a reader keeps its own default. That is the one place this writer names a row height, and it names the default rather than one of its own.
|
|
124
134
|
|
|
@@ -141,6 +151,16 @@ Rich text is a **bare array** — there is no wrapper object, because nothing el
|
|
|
141
151
|
|
|
142
152
|
One merged range across `width` columns of `row`, starting at the 1-based column `at`.
|
|
143
153
|
|
|
154
|
+
### `sheet.link(row, at, target)`
|
|
155
|
+
|
|
156
|
+
Link the cell at 1-based `row` and column `at`. `target` names exactly one of the two destinations the format has: `{ url }`, somewhere outside this workbook, or `{ location }`, a reference inside it — a cell as `'Sheet2'!A1`, or a defined name.
|
|
157
|
+
|
|
158
|
+
Linking **writes no cell**. What a reader shows is whatever the row already put there, which is why the row must exist first, as a merge's must. A reader holds one link per cell, so a second link on the same cell throws rather than quietly replacing the first, and an empty destination throws rather than becoming a link onto nothing.
|
|
159
|
+
|
|
160
|
+
Cells pointing at the same `url` share one relationship, however many of them there are — a column of a thousand rows linking one address writes one. An internal `location` needs no relationship at all.
|
|
161
|
+
|
|
162
|
+
A worksheet that never calls this carries **no `hyperlinks` element**, and no relationship part unless it also places a picture.
|
|
163
|
+
|
|
144
164
|
### `sheet.freeze(rows)`
|
|
145
165
|
|
|
146
166
|
Freeze the top `rows` rows. `0` clears.
|
package/lib/index.d.ts
CHANGED
|
@@ -152,15 +152,35 @@ export interface RowOptions {
|
|
|
152
152
|
* the one below the group. `0`, the default, is a row outside any group.
|
|
153
153
|
*/
|
|
154
154
|
level?: number;
|
|
155
|
+
/**
|
|
156
|
+
* Whether the row is hidden. A collapsed outline group is its content rows
|
|
157
|
+
* hidden and its summary row `collapsed`; hiding alone leaves the group's
|
|
158
|
+
* control showing expanded over rows nobody can see.
|
|
159
|
+
*/
|
|
160
|
+
hidden?: boolean;
|
|
161
|
+
/**
|
|
162
|
+
* Whether this row carries the control of a collapsed group. It goes on the
|
|
163
|
+
* **summary** row — the one below the group — not on the hidden rows.
|
|
164
|
+
*/
|
|
165
|
+
collapsed?: boolean;
|
|
155
166
|
}
|
|
156
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Where one linked cell points: outside the workbook, or inside it. A
|
|
170
|
+
* `location` is a reference this file can resolve — a cell as `'Sheet2'!A1`,
|
|
171
|
+
* or a defined name.
|
|
172
|
+
*/
|
|
173
|
+
export type LinkTarget = { url: string } | { location: string };
|
|
174
|
+
|
|
157
175
|
export interface Sheet {
|
|
158
176
|
/** This sheet's name. */
|
|
159
177
|
readonly name: string;
|
|
160
178
|
/**
|
|
161
179
|
* Append a row. `null` is an empty unstyled cell. Returns the row's 1-based
|
|
162
180
|
* position, which `merge` and `place` take. `options.level` puts the row at
|
|
163
|
-
* an outline level
|
|
181
|
+
* an outline level, and `hidden` / `collapsed` say whether it is shown and
|
|
182
|
+
* whether it carries a collapsed group's control. An option this does not
|
|
183
|
+
* know is refused rather than ignored.
|
|
164
184
|
*/
|
|
165
185
|
row(cells: readonly (Cell | null)[], options?: RowOptions): number;
|
|
166
186
|
/**
|
|
@@ -168,6 +188,15 @@ export interface Sheet {
|
|
|
168
188
|
* Throws on a width below 2, an overlap, or a row that does not exist yet.
|
|
169
189
|
*/
|
|
170
190
|
merge(row: number, at: number, width: number): void;
|
|
191
|
+
/**
|
|
192
|
+
* Link one cell: a destination outside this workbook (`url`) or a reference
|
|
193
|
+
* inside it (`location`), exactly one of the two.
|
|
194
|
+
*
|
|
195
|
+
* Linking writes no cell — a reader shows whatever the row already put
|
|
196
|
+
* there — so the row must exist first, as a merge's must. A reader holds
|
|
197
|
+
* one link per cell, so a second on the same cell throws.
|
|
198
|
+
*/
|
|
199
|
+
link(row: number, at: number, target: LinkTarget): void;
|
|
171
200
|
/** Freeze the top `count` rows. `0` clears. */
|
|
172
201
|
freeze(count: number): void;
|
|
173
202
|
/**
|
package/lib/index.js
CHANGED
|
@@ -903,12 +903,29 @@ const MAX_LEVEL = 7;
|
|
|
903
903
|
/** @param {unknown} level */
|
|
904
904
|
const isLevel = (level) => level === 0 || isIndex(level, MAX_LEVEL);
|
|
905
905
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
906
|
+
// Everything a row may say about itself. Named here because the reader below
|
|
907
|
+
// refuses anything else: a key it quietly dropped would be a caller asking for
|
|
908
|
+
// a row attribute and getting no error and no attribute.
|
|
909
|
+
const ROW_OPTIONS = ["level", "hidden", "collapsed"];
|
|
910
|
+
|
|
911
|
+
/** @param {unknown} value @param {string} where */
|
|
912
|
+
const requireFlag = (value, where) => {
|
|
913
|
+
if (value !== undefined && value !== null && typeof value !== "boolean")
|
|
914
|
+
throw TypeError(`${where}: expected a boolean, got ${JSON.stringify(value)}`);
|
|
915
|
+
return value === true;
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
/** The options object, with nothing in it this writer cannot write. */
|
|
919
|
+
/** @param {unknown} options */
|
|
920
|
+
const requireOptions = (options) => {
|
|
921
|
+
const said = requireRecord(options ?? {}, "row: options", `{ ${ROW_OPTIONS.join(", ")} }`);
|
|
922
|
+
const stray = Object.keys(said).find((name) => !ROW_OPTIONS.includes(name));
|
|
923
|
+
if (stray !== undefined) throw TypeError(`row: options: unknown option ${JSON.stringify(stray)}`);
|
|
924
|
+
return said;
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
/** @param {unknown} level */
|
|
928
|
+
const requireOutline = (level) => {
|
|
912
929
|
if (!isLevel(level))
|
|
913
930
|
throw RangeError(
|
|
914
931
|
`row: level: expected an integer between 0 and ${MAX_LEVEL}, got ${JSON.stringify(level)}`,
|
|
@@ -917,11 +934,31 @@ const requireLevel = (options) => {
|
|
|
917
934
|
};
|
|
918
935
|
|
|
919
936
|
/**
|
|
920
|
-
*
|
|
937
|
+
* What a row says about itself: its outline level, and whether it is hidden or
|
|
938
|
+
* carries a collapsed group's control.
|
|
939
|
+
* @param {unknown} options
|
|
940
|
+
*/
|
|
941
|
+
const requireRow = (options) => {
|
|
942
|
+
const said = requireOptions(options);
|
|
943
|
+
return {
|
|
944
|
+
level: requireOutline(said.level ?? 0),
|
|
945
|
+
hidden: requireFlag(said.hidden, "row: hidden"),
|
|
946
|
+
collapsed: requireFlag(said.collapsed, "row: collapsed"),
|
|
947
|
+
};
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
/**
|
|
951
|
+
* A row's opening tag. The attributes sit in the order `CT_Row` declares them,
|
|
952
|
+
* and each rides along only where it says something.
|
|
921
953
|
* @param {number} at
|
|
922
|
-
* @param {
|
|
954
|
+
* @param {ReturnType<typeof requireRow>} row
|
|
923
955
|
*/
|
|
924
|
-
const rowOpen = (at,
|
|
956
|
+
const rowOpen = (at, row) =>
|
|
957
|
+
`<row r="${at}"` +
|
|
958
|
+
(row.hidden ? ' hidden="1"' : "") +
|
|
959
|
+
(row.level === 0 ? "" : ` outlineLevel="${row.level}"`) +
|
|
960
|
+
(row.collapsed ? ' collapsed="1"' : "") +
|
|
961
|
+
">";
|
|
925
962
|
|
|
926
963
|
/**
|
|
927
964
|
* The sheet's `sheetFormatPr`, or nothing for a sheet that outlines no row.
|
|
@@ -1079,6 +1116,59 @@ const requireFilter = (range, rows, columns) => {
|
|
|
1079
1116
|
return { top, left, bottom, right };
|
|
1080
1117
|
};
|
|
1081
1118
|
|
|
1119
|
+
/**
|
|
1120
|
+
* @typedef {{ url: string } | { location: string }} LinkTarget
|
|
1121
|
+
* @typedef {{ ref: string } & LinkTarget} Link
|
|
1122
|
+
*/
|
|
1123
|
+
|
|
1124
|
+
// A link names one of the two: a destination outside the workbook, or a
|
|
1125
|
+
// reference inside it. Both at once names two destinations and neither names
|
|
1126
|
+
// none, so the count is what this checks rather than each key in turn.
|
|
1127
|
+
/** @param {unknown} target @returns {LinkTarget} */
|
|
1128
|
+
const requireTarget = (target) => {
|
|
1129
|
+
const url = /** @type {any} */ (target)?.url;
|
|
1130
|
+
const location = /** @type {any} */ (target)?.location;
|
|
1131
|
+
if ((url === undefined) === (location === undefined))
|
|
1132
|
+
throw RangeError(
|
|
1133
|
+
`link: expected exactly one of url and location, got ${JSON.stringify(target)}`,
|
|
1134
|
+
);
|
|
1135
|
+
if (url === undefined) return { location: requireDestination(location, "link: location") };
|
|
1136
|
+
return { url: requireDestination(url, "link: url") };
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
// A destination is a string with something in it: an empty one is a link a
|
|
1140
|
+
// reader opens onto nothing, which is a caller's mistake rather than a link.
|
|
1141
|
+
/** @param {unknown} value @param {string} where */
|
|
1142
|
+
const requireDestination = (value, where) => {
|
|
1143
|
+
const text = requireString(value, where);
|
|
1144
|
+
if (text === "") throw RangeError(`${where}: expected a destination, got an empty string`);
|
|
1145
|
+
return text;
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
// The destinations outside the workbook, deduplicated and in first-seen
|
|
1149
|
+
// order: one relationship each, however many cells point at it. The same walk
|
|
1150
|
+
// answers for the sheet part and for its relationships, so the two cannot
|
|
1151
|
+
// disagree about which id a link carries.
|
|
1152
|
+
/** @param {ReadonlyArray<Link>} links */
|
|
1153
|
+
const externals = (links) => [
|
|
1154
|
+
...new Set(links.filter((link) => "url" in link).map((link) => /** @type {any} */ (link).url)),
|
|
1155
|
+
];
|
|
1156
|
+
|
|
1157
|
+
// Every link of one sheet, in call order. An external one points at a
|
|
1158
|
+
// relationship of this worksheet's own part; an internal one carries its
|
|
1159
|
+
// reference and needs none. `first` is the id the relationships start at,
|
|
1160
|
+
// which is after the drawing where the sheet has one.
|
|
1161
|
+
/** @param {ReadonlyArray<Link>} links @param {number} first */
|
|
1162
|
+
const hyperlinksXml = (links, first) => {
|
|
1163
|
+
if (links.length === 0) return "";
|
|
1164
|
+
const targets = externals(links);
|
|
1165
|
+
const one = (/** @type {Link} */ link) =>
|
|
1166
|
+
"url" in link
|
|
1167
|
+
? `<hyperlink ref="${link.ref}" r:id="rId${first + targets.indexOf(link.url)}"/>`
|
|
1168
|
+
: `<hyperlink ref="${link.ref}" location="${esc(link.location)}"/>`;
|
|
1169
|
+
return `<hyperlinks>${links.map(one).join("")}</hyperlinks>`;
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1082
1172
|
/** @param {ReadonlyArray<Range>} merges */
|
|
1083
1173
|
const mergesXml = (merges) =>
|
|
1084
1174
|
merges.length === 0
|
|
@@ -1195,6 +1285,8 @@ const worksheet = (name, styles, sst, knows) => {
|
|
|
1195
1285
|
const merges = [];
|
|
1196
1286
|
/** @type {Array<{ id: number, row: number, col: number, width: number, height: number }>} */
|
|
1197
1287
|
const pictures = [];
|
|
1288
|
+
/** @type {Link[]} */
|
|
1289
|
+
const links = [];
|
|
1198
1290
|
let frozen = 0;
|
|
1199
1291
|
let widest = 1;
|
|
1200
1292
|
// The deepest outline level any row carries; 0 writes no `sheetFormatPr`.
|
|
@@ -1242,13 +1334,13 @@ const worksheet = (name, styles, sst, knows) => {
|
|
|
1242
1334
|
const at = rows.length + 1;
|
|
1243
1335
|
if (cells.length > MAX_COLUMN)
|
|
1244
1336
|
throw RangeError(`row: a sheet holds at most ${MAX_COLUMN} columns`);
|
|
1245
|
-
const
|
|
1246
|
-
deepest = Math.max(deepest, level);
|
|
1337
|
+
const row = requireRow(options);
|
|
1338
|
+
deepest = Math.max(deepest, row.level);
|
|
1247
1339
|
|
|
1248
1340
|
let body = "";
|
|
1249
1341
|
for (let index = 0; index < cells.length; index++) body += cellAt(cells[index], index, at);
|
|
1250
1342
|
widest = Math.max(widest, cells.length);
|
|
1251
|
-
rows.push(rowOpen(at,
|
|
1343
|
+
rows.push(rowOpen(at, row) + body + "</row>");
|
|
1252
1344
|
return at;
|
|
1253
1345
|
},
|
|
1254
1346
|
|
|
@@ -1269,6 +1361,34 @@ const worksheet = (name, styles, sst, knows) => {
|
|
|
1269
1361
|
widest = Math.max(widest, range.right);
|
|
1270
1362
|
},
|
|
1271
1363
|
|
|
1364
|
+
/**
|
|
1365
|
+
* Link one cell. `target` names exactly one of `url`, a destination
|
|
1366
|
+
* outside this workbook, and `location`, a reference inside it -- a cell
|
|
1367
|
+
* as `'Sheet2'!A1`, or a defined name.
|
|
1368
|
+
*
|
|
1369
|
+
* Linking writes no cell: what a reader shows is whatever the row already
|
|
1370
|
+
* put there, which is why the row must exist first, as a merge's must. A
|
|
1371
|
+
* reader holds one link per cell, so a second on the same cell throws
|
|
1372
|
+
* rather than quietly replacing the first.
|
|
1373
|
+
*
|
|
1374
|
+
* @param {number} row 1-based
|
|
1375
|
+
* @param {number} at 1-based column
|
|
1376
|
+
* @param {LinkTarget} target
|
|
1377
|
+
*/
|
|
1378
|
+
link(row, at, target) {
|
|
1379
|
+
requireIndex(row, MAX_ROW, "link: row");
|
|
1380
|
+
requireIndex(at, MAX_COLUMN, "link: at");
|
|
1381
|
+
requireWritten(row, rows.length, "row", "link");
|
|
1382
|
+
const ref = `${letters(at)}${row}`;
|
|
1383
|
+
if (links.some((other) => other.ref === ref))
|
|
1384
|
+
throw RangeError(`link: ${ref} already carries a link`);
|
|
1385
|
+
links.push({ ref, ...requireTarget(target) });
|
|
1386
|
+
},
|
|
1387
|
+
|
|
1388
|
+
get links() {
|
|
1389
|
+
return links;
|
|
1390
|
+
},
|
|
1391
|
+
|
|
1272
1392
|
/**
|
|
1273
1393
|
* How this worksheet prints. Every key is optional, and a worksheet that
|
|
1274
1394
|
* never calls this carries no print setup at all: a reader's own defaults
|
|
@@ -1372,6 +1492,7 @@ const worksheet = (name, styles, sst, knows) => {
|
|
|
1372
1492
|
`<sheetData>${rows.join("")}</sheetData>` +
|
|
1373
1493
|
filtered +
|
|
1374
1494
|
mergesXml(merges) +
|
|
1495
|
+
hyperlinksXml(links, drawing === null ? 1 : drawing + 1) +
|
|
1375
1496
|
printXml(printing) +
|
|
1376
1497
|
(drawing === null ? "" : `<drawing r:id="rId${drawing}"/>`) +
|
|
1377
1498
|
`</worksheet>`
|
|
@@ -1571,9 +1692,28 @@ const workbookRelsXml = (sheets) =>
|
|
|
1571
1692
|
* @typedef {{ sheet: ReturnType<typeof worksheet>, index: number }} Drawing
|
|
1572
1693
|
*/
|
|
1573
1694
|
|
|
1695
|
+
// What one worksheet's own part points at: its drawing, where it has one, and
|
|
1696
|
+
// then a relationship per destination outside the workbook. The drawing leads
|
|
1697
|
+
// because it did before links existed, and `hyperlinksXml` counts from the
|
|
1698
|
+
// same place.
|
|
1699
|
+
/** @param {number} at @param {ReadonlyArray<Link>} links */
|
|
1700
|
+
const sheetRels = (at, links) =>
|
|
1701
|
+
DECLARATION +
|
|
1702
|
+
`<Relationships xmlns="${NS_PKG_REL}">` +
|
|
1703
|
+
(at === -1
|
|
1704
|
+
? ""
|
|
1705
|
+
: `<Relationship Id="rId1" Type="${REL}/drawing" Target="../drawings/drawing${at + 1}.xml"/>`) +
|
|
1706
|
+
externals(links)
|
|
1707
|
+
.map(
|
|
1708
|
+
(url, index) =>
|
|
1709
|
+
`<Relationship Id="rId${(at === -1 ? 1 : 2) + index}" Type="${REL}/hyperlink" Target="${esc(url)}" TargetMode="External"/>`,
|
|
1710
|
+
)
|
|
1711
|
+
.join("") +
|
|
1712
|
+
`</Relationships>`;
|
|
1713
|
+
|
|
1574
1714
|
/**
|
|
1575
|
-
* Every worksheet part, and the relationship part of each sheet that
|
|
1576
|
-
* a drawing.
|
|
1715
|
+
* Every worksheet part, and the relationship part of each sheet that points
|
|
1716
|
+
* at anything of its own — a drawing, a link, or both.
|
|
1577
1717
|
* @param {Part} part
|
|
1578
1718
|
* @param {ReadonlyArray<ReturnType<typeof worksheet>>} sheets
|
|
1579
1719
|
* @param {ReadonlyArray<Drawing>} drawings
|
|
@@ -1582,12 +1722,8 @@ const sheetParts = (part, sheets, drawings) => {
|
|
|
1582
1722
|
for (const [index, sheet] of sheets.entries()) {
|
|
1583
1723
|
const at = drawings.findIndex((each) => each.index === index);
|
|
1584
1724
|
part(`xl/worksheets/sheet${index + 1}.xml`, sheet.xml(at === -1 ? null : 1));
|
|
1585
|
-
if (at !== -1)
|
|
1586
|
-
part(
|
|
1587
|
-
`xl/worksheets/_rels/sheet${index + 1}.xml.rels`,
|
|
1588
|
-
DECLARATION +
|
|
1589
|
-
`<Relationships xmlns="${NS_PKG_REL}"><Relationship Id="rId1" Type="${REL}/drawing" Target="../drawings/drawing${at + 1}.xml"/></Relationships>`,
|
|
1590
|
-
);
|
|
1725
|
+
if (at !== -1 || externals(sheet.links).length > 0)
|
|
1726
|
+
part(`xl/worksheets/_rels/sheet${index + 1}.xml.rels`, sheetRels(at, sheet.links));
|
|
1591
1727
|
}
|
|
1592
1728
|
};
|
|
1593
1729
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "werkmap",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Tiny, CSP-safe OOXML spreadsheet writer. Write-only, zero dependencies, byte-identical output.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"csp",
|
|
@@ -24,10 +24,12 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
|
-
"check": "run-s fmt:check lint fallow size test test:browser",
|
|
27
|
+
"check": "run-s fmt:check lint fallow:lint size test fallow:health test:browser",
|
|
28
28
|
"bench": "node --disallow-code-generation-from-strings --expose-gc bench/index.js",
|
|
29
29
|
"commitlint": "commitlint",
|
|
30
|
-
"fallow": "fallow",
|
|
30
|
+
"fallow": "run-s fallow:lint fallow:health",
|
|
31
|
+
"fallow:health": "fallow health --coverage coverage/coverage-final.json",
|
|
32
|
+
"fallow:lint": "fallow dead-code && fallow dupes",
|
|
31
33
|
"fmt": "oxfmt",
|
|
32
34
|
"fmt:check": "oxfmt --check",
|
|
33
35
|
"lint": "oxlint",
|
|
@@ -57,7 +59,7 @@
|
|
|
57
59
|
"size-limit": [
|
|
58
60
|
{
|
|
59
61
|
"path": "lib/index.js",
|
|
60
|
-
"limit": "8 kB"
|
|
62
|
+
"limit": "8.5 kB"
|
|
61
63
|
}
|
|
62
64
|
],
|
|
63
65
|
"engines": {
|