@williamthorsen/toolbelt.numbers 7.0.0 → 7.0.2
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/.readyup/kits/default.js +31 -197
- package/.readyup/manifest.json +6 -16
- package/CHANGELOG.md +38 -0
- package/README.md +8 -21
- package/dist/esm/3-candidate/index.d.ts +1 -1
- package/dist/esm/3-candidate/index.js +0 -1
- package/package.json +3 -3
- package/dist/esm/3-candidate/types.d.ts +0 -1
- package/dist/esm/3-candidate/types.js +0 -1
package/.readyup/kits/default.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/** @noformat
|
|
1
|
+
/** @noformat -- @generated. Do not edit. Compiled by rdy. */
|
|
2
2
|
/* eslint-disable */
|
|
3
|
-
export const __readyupVersion = "0.
|
|
3
|
+
export const __readyupVersion = "0.34.0";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
// ../adoption/src/conventions/path-predicates.ts
|
|
@@ -48,13 +48,14 @@ import { defineRdyKit } from "readyup";
|
|
|
48
48
|
import {
|
|
49
49
|
buildFindingReport,
|
|
50
50
|
countPackageUsage,
|
|
51
|
-
discoverWorkspaces,
|
|
52
51
|
readTrackedSources
|
|
53
52
|
} from "readyup/check-utils";
|
|
54
53
|
var NOT_A_REPO = "the project is not a git working tree, and these checks read the files git tracks";
|
|
55
|
-
var
|
|
54
|
+
var NOTHING_TO_REPORT = { findings: [] };
|
|
56
55
|
function defineAdoptionKit(spec) {
|
|
56
|
+
assertCheckIdsAreUnique();
|
|
57
57
|
const cache = {};
|
|
58
|
+
const adoptedPackage = { exportNames: spec.exportNames, packageName: spec.packageName };
|
|
58
59
|
return defineRdyKit({
|
|
59
60
|
description: spec.description,
|
|
60
61
|
defaultSeverity: "warn",
|
|
@@ -63,14 +64,27 @@ function defineAdoptionKit(spec) {
|
|
|
63
64
|
name: "adoption",
|
|
64
65
|
checks: spec.checks.map((check) => ({
|
|
65
66
|
name: check.name,
|
|
67
|
+
id: check.id,
|
|
66
68
|
...check.severity !== void 0 && { severity: check.severity },
|
|
67
|
-
skip:
|
|
69
|
+
skip: skipUnlessProjectHoldsSources,
|
|
68
70
|
check: () => reportKinds(check.kinds),
|
|
69
71
|
fix: check.fix
|
|
70
72
|
}))
|
|
71
73
|
}
|
|
72
74
|
]
|
|
73
75
|
});
|
|
76
|
+
function assertCheckIdsAreUnique() {
|
|
77
|
+
const seen = /* @__PURE__ */ new Set();
|
|
78
|
+
const duplicated = /* @__PURE__ */ new Set();
|
|
79
|
+
for (const { id } of spec.checks) {
|
|
80
|
+
if (seen.has(id)) duplicated.add(id);
|
|
81
|
+
seen.add(id);
|
|
82
|
+
}
|
|
83
|
+
if (duplicated.size > 0) {
|
|
84
|
+
const ids = [...duplicated].toSorted().join(", ");
|
|
85
|
+
throw new Error(`${spec.packageName}'s kit gives one id to more than one check: ${ids}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
74
88
|
function loadSummary() {
|
|
75
89
|
cache.summary ??= readProject();
|
|
76
90
|
return cache.summary;
|
|
@@ -79,203 +93,26 @@ function defineAdoptionKit(spec) {
|
|
|
79
93
|
const sources = await readTrackedSources(spec.pathFilter);
|
|
80
94
|
if (sources === void 0) return void 0;
|
|
81
95
|
return {
|
|
82
|
-
adoptedCount: countPackageUsage(sources,
|
|
83
|
-
exportNames: spec.exportNames,
|
|
84
|
-
packageName: spec.packageName
|
|
85
|
-
}),
|
|
96
|
+
adoptedCount: countPackageUsage(sources, adoptedPackage),
|
|
86
97
|
findings: sources.flatMap((source) => spec.detect(source.text).map((site) => ({ ...site, path: source.path }))),
|
|
87
|
-
|
|
98
|
+
sources
|
|
88
99
|
};
|
|
89
100
|
}
|
|
90
101
|
async function reportKinds(kinds) {
|
|
91
102
|
const summary = await loadSummary();
|
|
92
|
-
if (summary === void 0) return
|
|
103
|
+
if (summary === void 0) return NOTHING_TO_REPORT;
|
|
93
104
|
return buildFindingReport({
|
|
94
105
|
adoptedCount: summary.adoptedCount,
|
|
95
106
|
findings: summary.findings,
|
|
107
|
+
ownImplementation: { ...adoptedPackage, sources: summary.sources },
|
|
96
108
|
shouldReport: (finding) => kinds.includes(finding.kind)
|
|
97
109
|
});
|
|
98
110
|
}
|
|
99
|
-
async function
|
|
100
|
-
if (discoverWorkspaces().some((workspace) => workspace.name === spec.packageName)) return SELF;
|
|
111
|
+
async function skipUnlessProjectHoldsSources() {
|
|
101
112
|
const summary = await loadSummary();
|
|
102
113
|
if (summary === void 0) return NOT_A_REPO;
|
|
103
|
-
return summary.
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// ../adoption/src/portable/blankNonCode.ts
|
|
108
|
-
var REGEX_PRECEDERS = /* @__PURE__ */ new Set([
|
|
109
|
-
"!",
|
|
110
|
-
"%",
|
|
111
|
-
"&",
|
|
112
|
-
"(",
|
|
113
|
-
"*",
|
|
114
|
-
"+",
|
|
115
|
-
",",
|
|
116
|
-
"-",
|
|
117
|
-
":",
|
|
118
|
-
";",
|
|
119
|
-
"=",
|
|
120
|
-
">",
|
|
121
|
-
"?",
|
|
122
|
-
"[",
|
|
123
|
-
"^",
|
|
124
|
-
"{",
|
|
125
|
-
"|",
|
|
126
|
-
"~"
|
|
127
|
-
]);
|
|
128
|
-
var EXPRESSION_KEYWORDS2 = /* @__PURE__ */ new Set([
|
|
129
|
-
"await",
|
|
130
|
-
"case",
|
|
131
|
-
"delete",
|
|
132
|
-
"do",
|
|
133
|
-
"else",
|
|
134
|
-
"in",
|
|
135
|
-
"instanceof",
|
|
136
|
-
"new",
|
|
137
|
-
"of",
|
|
138
|
-
"return",
|
|
139
|
-
"typeof",
|
|
140
|
-
"void",
|
|
141
|
-
"yield"
|
|
142
|
-
]);
|
|
143
|
-
var WORD_CHAR = /[\w$]/;
|
|
144
|
-
function blankNonCode(source) {
|
|
145
|
-
const scan = { out: source.split(""), source };
|
|
146
|
-
const start = source.startsWith("#!") ? blankSpan(scan, 0, findLineEnd(source, 0)) : 0;
|
|
147
|
-
scanCode(scan, start, false);
|
|
148
|
-
return scan.out.join("");
|
|
149
|
-
}
|
|
150
|
-
function blankQuoted(scan, start, quote) {
|
|
151
|
-
const { source } = scan;
|
|
152
|
-
let index = start + 1;
|
|
153
|
-
while (index < source.length) {
|
|
154
|
-
const char = source[index];
|
|
155
|
-
if (char === "\\") {
|
|
156
|
-
index += 2;
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
if (char === "\n") break;
|
|
160
|
-
if (char === quote) return blankSpan(scan, start + 1, index) + 1;
|
|
161
|
-
index += 1;
|
|
162
|
-
}
|
|
163
|
-
return start + 1;
|
|
164
|
-
}
|
|
165
|
-
function blankSpan(scan, from, to) {
|
|
166
|
-
for (let index = from; index < to; index += 1) {
|
|
167
|
-
const char = scan.source[index];
|
|
168
|
-
if (char !== "\n" && char !== "\r") scan.out[index] = " ";
|
|
169
|
-
}
|
|
170
|
-
return to;
|
|
171
|
-
}
|
|
172
|
-
function blankTemplate(scan, start) {
|
|
173
|
-
const { source } = scan;
|
|
174
|
-
let index = start + 1;
|
|
175
|
-
let textStart = index;
|
|
176
|
-
while (index < source.length) {
|
|
177
|
-
const char = source[index];
|
|
178
|
-
if (char === "\\") {
|
|
179
|
-
index += 2;
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
if (char === "`") return blankSpan(scan, textStart, index) + 1;
|
|
183
|
-
if (char === "$" && source[index + 1] === "{") {
|
|
184
|
-
blankSpan(scan, textStart, index);
|
|
185
|
-
const close = scanCode(scan, index + 2, true);
|
|
186
|
-
index = close < source.length ? close + 1 : close;
|
|
187
|
-
textStart = index;
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
index += 1;
|
|
191
|
-
}
|
|
192
|
-
return blankSpan(scan, textStart, source.length);
|
|
193
|
-
}
|
|
194
|
-
function findBlockCommentEnd(source, from) {
|
|
195
|
-
const end = source.indexOf("*/", from + 2);
|
|
196
|
-
return end === -1 ? source.length : end + 2;
|
|
197
|
-
}
|
|
198
|
-
function findLineEnd(source, from) {
|
|
199
|
-
const end = source.indexOf("\n", from);
|
|
200
|
-
return end === -1 ? source.length : end;
|
|
201
|
-
}
|
|
202
|
-
function findRegexEnd(source, start) {
|
|
203
|
-
let index = start + 1;
|
|
204
|
-
let isInClass = false;
|
|
205
|
-
while (index < source.length) {
|
|
206
|
-
const char = source[index];
|
|
207
|
-
if (char === "\\") {
|
|
208
|
-
index += 2;
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
if (char === "\n") return void 0;
|
|
212
|
-
if (char === "[") isInClass = true;
|
|
213
|
-
else if (char === "]") isInClass = false;
|
|
214
|
-
else if (char === "/" && !isInClass) return index + 1;
|
|
215
|
-
index += 1;
|
|
216
|
-
}
|
|
217
|
-
return void 0;
|
|
218
|
-
}
|
|
219
|
-
function findWordEnd(source, from) {
|
|
220
|
-
let index = from;
|
|
221
|
-
while (index < source.length && WORD_CHAR.test(source[index] ?? "")) index += 1;
|
|
222
|
-
return index;
|
|
223
|
-
}
|
|
224
|
-
function scanCode(scan, from, isInterpolation) {
|
|
225
|
-
const { source } = scan;
|
|
226
|
-
let previousToken = "";
|
|
227
|
-
let braceDepth = 0;
|
|
228
|
-
let index = from;
|
|
229
|
-
while (index < source.length) {
|
|
230
|
-
const char = source[index] ?? "";
|
|
231
|
-
const next = source[index + 1];
|
|
232
|
-
if (char === "/" && next === "/") {
|
|
233
|
-
index = blankSpan(scan, index, findLineEnd(source, index));
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
if (char === "/" && next === "*") {
|
|
237
|
-
index = blankSpan(scan, index, findBlockCommentEnd(source, index));
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
if (char === "'" || char === '"') {
|
|
241
|
-
index = blankQuoted(scan, index, char);
|
|
242
|
-
previousToken = char;
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
if (char === "`") {
|
|
246
|
-
index = blankTemplate(scan, index);
|
|
247
|
-
previousToken = char;
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
if (char === "/" && startsRegex(previousToken)) {
|
|
251
|
-
const end = findRegexEnd(source, index);
|
|
252
|
-
if (end !== void 0) {
|
|
253
|
-
blankSpan(scan, index + 1, end - 1);
|
|
254
|
-
index = end;
|
|
255
|
-
previousToken = "/";
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
if (isInterpolation && char === "{") braceDepth += 1;
|
|
260
|
-
else if (isInterpolation && char === "}") {
|
|
261
|
-
if (braceDepth === 0) return index;
|
|
262
|
-
braceDepth -= 1;
|
|
263
|
-
}
|
|
264
|
-
if (WORD_CHAR.test(char)) {
|
|
265
|
-
const end = findWordEnd(source, index);
|
|
266
|
-
previousToken = source.slice(index, end);
|
|
267
|
-
index = end;
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
if (!/\s/.test(char)) previousToken = char;
|
|
271
|
-
index += 1;
|
|
114
|
+
return summary.sources.length === 0 ? spec.noSourcesReason : false;
|
|
272
115
|
}
|
|
273
|
-
return index;
|
|
274
|
-
}
|
|
275
|
-
function startsRegex(previousToken) {
|
|
276
|
-
if (previousToken === "") return true;
|
|
277
|
-
if (previousToken.length === 1) return REGEX_PRECEDERS.has(previousToken);
|
|
278
|
-
return EXPRESSION_KEYWORDS2.has(previousToken);
|
|
279
116
|
}
|
|
280
117
|
|
|
281
118
|
// ../adoption/src/portable/condenseWhitespace.ts
|
|
@@ -283,15 +120,6 @@ function condenseWhitespace(text) {
|
|
|
283
120
|
return text.replaceAll(/\s+/g, " ");
|
|
284
121
|
}
|
|
285
122
|
|
|
286
|
-
// ../adoption/src/portable/getLineAtOffset.ts
|
|
287
|
-
function getLineAtOffset(source, offset) {
|
|
288
|
-
let line = 1;
|
|
289
|
-
for (let index = 0; index < offset; index += 1) {
|
|
290
|
-
if (source[index] === "\n") line += 1;
|
|
291
|
-
}
|
|
292
|
-
return line;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
123
|
// ../adoption/src/portable/readBalancedGroup.ts
|
|
296
124
|
var PARENTHESES = { close: ")", open: "(" };
|
|
297
125
|
function readBalancedGroup(source, from, delimiters) {
|
|
@@ -316,6 +144,9 @@ function readAnchoredWindow(source, offset, lengths) {
|
|
|
316
144
|
};
|
|
317
145
|
}
|
|
318
146
|
|
|
147
|
+
// ../adoption/src/mod.ts
|
|
148
|
+
import { blankNonCode, getLineAtOffset } from "readyup/check-utils";
|
|
149
|
+
|
|
319
150
|
// src/readiness/adoptedExports.ts
|
|
320
151
|
var ADOPTED_EXPORTS = [
|
|
321
152
|
"clamp",
|
|
@@ -448,18 +279,21 @@ var default_default = defineAdoptionKit({
|
|
|
448
279
|
checks: [
|
|
449
280
|
{
|
|
450
281
|
name: "No source clamps a value by hand",
|
|
282
|
+
id: "no-hand-rolled-clamp",
|
|
451
283
|
kinds: ["clamp-nest"],
|
|
452
284
|
severity: "recommend",
|
|
453
285
|
fix: `Replace each expression named above with clamp from ${PACKAGE_NAME}/candidate, called as clamp(value, { min, max }). It is not a silent substitution: clamp throws a RangeError on a reversed range or a NaN bound, where the nested Math calls return a value for both. Reference: ${README_URL}`
|
|
454
286
|
},
|
|
455
287
|
{
|
|
456
288
|
name: "No source rounds to decimal places by hand",
|
|
289
|
+
id: "no-hand-rolled-round",
|
|
457
290
|
kinds: ["round-scale"],
|
|
458
291
|
severity: "recommend",
|
|
459
292
|
fix: `Replace each expression named above with round from ${PACKAGE_NAME}/candidate, called as round(value, places). The substitution is exact: round scales by the same power of ten these sites write out. Reference: ${README_URL}`
|
|
460
293
|
},
|
|
461
294
|
{
|
|
462
295
|
name: "No source derives a random integer by hand",
|
|
296
|
+
id: "no-hand-rolled-random-integer",
|
|
463
297
|
kinds: ["random-integer"],
|
|
464
298
|
severity: "recommend",
|
|
465
299
|
fix: `Replace each expression named above with pickInteger from ${PACKAGE_NAME}/candidate, which also takes a seed. Mind the bound: Math.floor(Math.random() * N) stops at N - 1, where pickInteger's max is inclusive, so the replacement is pickInteger({ max: N - 1 }). A site indexing an array is left to toolbelt.arrays, whose pickItem covers it. Reference: ${README_URL}`
|
package/.readyup/manifest.json
CHANGED
|
@@ -19,30 +19,20 @@
|
|
|
19
19
|
"path": "../../adoption/src/conventions/site-handoffs.ts"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
|
-
"hash": "
|
|
22
|
+
"hash": "781c3f53",
|
|
23
23
|
"kind": "module",
|
|
24
24
|
"path": "../../adoption/src/kits/defineAdoptionKit.ts"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
|
-
"hash": "
|
|
27
|
+
"hash": "6e5a8dda",
|
|
28
28
|
"kind": "module",
|
|
29
29
|
"path": "../../adoption/src/mod.ts"
|
|
30
30
|
},
|
|
31
|
-
{
|
|
32
|
-
"hash": "6617674b",
|
|
33
|
-
"kind": "module",
|
|
34
|
-
"path": "../../adoption/src/portable/blankNonCode.ts"
|
|
35
|
-
},
|
|
36
31
|
{
|
|
37
32
|
"hash": "e8d0444d",
|
|
38
33
|
"kind": "module",
|
|
39
34
|
"path": "../../adoption/src/portable/condenseWhitespace.ts"
|
|
40
35
|
},
|
|
41
|
-
{
|
|
42
|
-
"hash": "4da99027",
|
|
43
|
-
"kind": "module",
|
|
44
|
-
"path": "../../adoption/src/portable/getLineAtOffset.ts"
|
|
45
|
-
},
|
|
46
36
|
{
|
|
47
37
|
"hash": "b6d99ce7",
|
|
48
38
|
"kind": "module",
|
|
@@ -59,7 +49,7 @@
|
|
|
59
49
|
"path": "../../adoption/src/portable/readBalancedGroup.ts"
|
|
60
50
|
},
|
|
61
51
|
{
|
|
62
|
-
"hash": "
|
|
52
|
+
"hash": "e6df5682",
|
|
63
53
|
"kind": "module",
|
|
64
54
|
"path": "kits/default.ts"
|
|
65
55
|
},
|
|
@@ -91,10 +81,10 @@
|
|
|
91
81
|
],
|
|
92
82
|
"name": "default",
|
|
93
83
|
"path": "kits/default.js",
|
|
94
|
-
"readyupVersion": "0.
|
|
84
|
+
"readyupVersion": "0.34.0",
|
|
95
85
|
"source": "kits/default.ts",
|
|
96
|
-
"sourceHash": "
|
|
97
|
-
"targetHash": "
|
|
86
|
+
"sourceHash": "e6df5682",
|
|
87
|
+
"targetHash": "44b320aa"
|
|
98
88
|
}
|
|
99
89
|
]
|
|
100
90
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 7.0.2 — 2026-08-28
|
|
6
|
+
|
|
7
|
+
### Refactoring
|
|
8
|
+
|
|
9
|
+
- Upgrade eslint-config-typescript to v12.0.1 and satisfy its new rules (#236)
|
|
10
|
+
|
|
11
|
+
Upgrades `@williamthorsen/eslint-config-typescript` to v12 and fixes violations surfaced by the new rules banning unpublished barrels and floating disposables.
|
|
12
|
+
|
|
13
|
+
### Dependencies
|
|
14
|
+
|
|
15
|
+
- Upgrade all deps to latest version
|
|
16
|
+
- Upgrade deps to latest version
|
|
17
|
+
- Upgrade all deps to latest version
|
|
18
|
+
|
|
19
|
+
## 7.0.1 — 2026-08-24
|
|
20
|
+
|
|
21
|
+
### Bug fixes
|
|
22
|
+
|
|
23
|
+
- Stop the adoption kits from blanking code after `++`, `!`, and keyword-named members (#208)
|
|
24
|
+
|
|
25
|
+
Fixes an issue where the `errors`, `numbers`, and `vitest` adoption kits stopped reading a line's code after a postfix `++`, a non-null `!`, or a property spelled like a keyword. The source blanker each kit runs before its anchor scan read the `/` that followed as the opening of a regular expression and blanked to the line's next `/`, so a replaceable idiom written after one of the three went unreported.
|
|
26
|
+
|
|
27
|
+
`@williamthorsen/toolbelt.adoption` now takes `blankNonCode` and `getLineAtOffset` from `readyup/check-utils` in place of the copies it held, which had fallen behind readyup's on those three cases.
|
|
28
|
+
|
|
29
|
+
### Internal
|
|
30
|
+
|
|
31
|
+
- Migrate the adoption kits onto FindingOutcome and add check ids (#217)
|
|
32
|
+
|
|
33
|
+
Upgrades `readyup` to 0.32.0 and migrates `defineAdoptionKit` onto the `FindingOutcome` that `buildFindingReport` returns as of 0.31.0.
|
|
34
|
+
|
|
35
|
+
Gives every adoption check an `id`, which a consumer's `rdy-ignore` pragma names to suppress that one check; a pragma naming none still silences every check on the line. The kits of `@williamthorsen/toolbelt.errors`, `@williamthorsen/toolbelt.numbers`, and `@williamthorsen/toolbelt.vitest` each declare ids, and `defineAdoptionKit` refuses a kit that gives one id to two checks.
|
|
36
|
+
|
|
37
|
+
- Hold this repo to its own adoption kits and exempt each package's implementation (#218)
|
|
38
|
+
|
|
39
|
+
Removes the exemption that kept this repository out of its own adoption kits. `rdy run --packages` now covers this repo like any other project.
|
|
40
|
+
|
|
41
|
+
Each package's own implementation stays exempt, but that exemption now covers the function alone rather than the whole repository.
|
|
42
|
+
|
|
5
43
|
## 7.0.0 — 2026-08-21
|
|
6
44
|
|
|
7
45
|
### Features
|
package/README.md
CHANGED
|
@@ -2,27 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Utility functions for working with numbers.
|
|
4
4
|
|
|
5
|
-
<!-- section:release-notes -->
|
|
6
|
-
## Release notes — v7.0.0 (2026-08-21)
|
|
7
|
-
|
|
8
|
-
### Features
|
|
9
|
-
|
|
10
|
-
- 🚨 **Breaking:** Promote clamp to the candidate tier with a stricter bounds contract (#185)
|
|
11
|
-
|
|
12
|
-
Promotes `clamp` to `@williamthorsen/toolbelt.numbers/candidate` and tightens its bounds contract: A `NaN` bound now throws a `RangeError`, where it previously returned `NaN` silently. A `NaN` value still passes through. Publishes the bounds type as `ClampBounds`, whose optional properties admit `undefined`, so a bound that may be absent typechecks under `exactOptionalPropertyTypes`.
|
|
13
|
-
|
|
14
|
-
Migration: `clamp` is no longer exported from the `/draft` subpath; consumers import it from `/candidate`.
|
|
15
|
-
|
|
16
|
-
- Add a ReadyUp adoption kit to `numbers` (#188)
|
|
17
|
-
|
|
18
|
-
Adds a ReadyUp adoption kit to `@williamthorsen/toolbelt.numbers`. When run against a project, the kit identifies hand-rolled code that can be replaced by the package's `clamp`, `round`, or `pickInteger` functions.
|
|
19
|
-
|
|
20
|
-
### Bug fixes
|
|
21
|
-
|
|
22
|
-
- Blank comments and literals before a detector reads a source (#191)
|
|
23
|
-
|
|
24
|
-
Fixes the issue that the `errors`, `numbers`, and `vitest` adoption kits' detectors did not distinguish code from comments and literals, and so flagged a pattern written in a comment or a string as a candidate replacement site. Each detector now blanks every comment, string, template literal, and regular expression before its anchor scan, leaving interpolated expressions intact.
|
|
25
|
-
<!-- /section:release-notes -->
|
|
5
|
+
<!-- section:release-notes --><!-- /section:release-notes -->
|
|
26
6
|
|
|
27
7
|
## Installation
|
|
28
8
|
|
|
@@ -93,6 +73,13 @@ A random integer used as an array subscript is left alone. That site belongs to
|
|
|
93
73
|
|
|
94
74
|
Bootstrap wrappers under `bin/` are exempt: such a wrapper imports only builtins so its build-first message survives an incomplete install, and importing this package there would replace that message with a module-resolution failure. Tests are exempt too, since they compute these values deliberately.
|
|
95
75
|
|
|
76
|
+
A reviewed site is silenced by an `rdy-ignore` pragma on its own line, or `rdy-ignore-next-line` on the line above. A pragma naming a check's id suppresses that check alone; with no id it covers every check on the line. A failed check prints its id ahead of its fraction, which is the form to write:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// rdy-ignore-next-line toolbelt.numbers/no-hand-rolled-clamp -- a reversed range is unreachable here
|
|
80
|
+
const bounded = Math.max(min, Math.min(max, value));
|
|
81
|
+
```
|
|
82
|
+
|
|
96
83
|
Add the package to `.config/readyup.config.ts` to include it in a routine sweep:
|
|
97
84
|
|
|
98
85
|
```ts
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type { Seed } from '../internal/evaluateSeed.js';
|
|
1
2
|
export { clamp, type ClampBounds } from './clamp.js';
|
|
2
3
|
export { generateRandom } from './generateRandom.js';
|
|
3
4
|
export { isIntegerString, safeParseInteger } from './integer-string.js';
|
|
@@ -6,4 +7,3 @@ export { pickInteger } from './pickInteger.js';
|
|
|
6
7
|
export { round } from './round.js';
|
|
7
8
|
export { scale } from './scale.js';
|
|
8
9
|
export { IntSeededRng, SeededRng } from './seeded-rng.js';
|
|
9
|
-
export * from './types.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/toolbelt.numbers",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.2",
|
|
4
4
|
"description": "Utility functions for working with numbers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"esm",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
],
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"esbuild": "0.28.2",
|
|
50
|
-
"readyup": "0.
|
|
50
|
+
"readyup": "0.34.0",
|
|
51
51
|
"@williamthorsen/toolbelt.adoption": "0.1.0",
|
|
52
|
-
"@williamthorsen/toolbelt.testing": "0.
|
|
52
|
+
"@williamthorsen/toolbelt.testing": "0.5.0"
|
|
53
53
|
},
|
|
54
54
|
"engines": {
|
|
55
55
|
"node": ">=24.0.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type { Seed } from '../internal/evaluateSeed.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|