@seorii/libcollect 0.1.0 → 1.0.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/include-deps.mjs +274 -71
- package/include-deps.test.mjs +437 -0
- package/package.json +1507 -5
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import { includeDependencies } from "./include-deps.mjs";
|
|
8
|
+
|
|
9
|
+
function cloneJson(value) {
|
|
10
|
+
return JSON.parse(JSON.stringify(value));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function createRegistryMeta(name, version, extra = {}) {
|
|
14
|
+
return {
|
|
15
|
+
name,
|
|
16
|
+
version,
|
|
17
|
+
dependencies: {},
|
|
18
|
+
peerDependencies: {},
|
|
19
|
+
optionalDependencies: {},
|
|
20
|
+
...extra,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function withMockedFetch(packageMetas, fn) {
|
|
25
|
+
const originalFetch = globalThis.fetch;
|
|
26
|
+
|
|
27
|
+
globalThis.fetch = async function mockedFetch(url) {
|
|
28
|
+
const name = decodeURIComponent(new URL(url).pathname.slice(1));
|
|
29
|
+
const entry = packageMetas[name];
|
|
30
|
+
const meta = Array.isArray(entry) ? entry.shift() : entry;
|
|
31
|
+
|
|
32
|
+
if (meta instanceof Error) {
|
|
33
|
+
throw meta;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!meta) {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
status: 404,
|
|
40
|
+
async json() {
|
|
41
|
+
return { error: "not found" };
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (typeof meta === "object" && (typeof meta.ok === "boolean" || typeof meta.status === "number")) {
|
|
47
|
+
const status = meta.status ?? (meta.ok ? 200 : 500);
|
|
48
|
+
return {
|
|
49
|
+
ok: meta.ok ?? (status >= 200 && status < 300),
|
|
50
|
+
status,
|
|
51
|
+
async json() {
|
|
52
|
+
return cloneJson(meta.body || {});
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
ok: true,
|
|
59
|
+
async json() {
|
|
60
|
+
return cloneJson(meta);
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
return await fn();
|
|
67
|
+
} finally {
|
|
68
|
+
globalThis.fetch = originalFetch;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function withImmediateTimers(fn) {
|
|
73
|
+
const originalSetTimeout = globalThis.setTimeout;
|
|
74
|
+
const delays = [];
|
|
75
|
+
|
|
76
|
+
globalThis.setTimeout = (callback, delay = 0, ...args) => {
|
|
77
|
+
delays.push(delay);
|
|
78
|
+
callback(...args);
|
|
79
|
+
return 0;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
return await fn(delays);
|
|
84
|
+
} finally {
|
|
85
|
+
globalThis.setTimeout = originalSetTimeout;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function createFixturePackage(packageJson) {
|
|
90
|
+
const fixtureDir = await fs.mkdtemp(path.join(os.tmpdir(), "libcollect-"));
|
|
91
|
+
const packageJsonPath = path.join(fixtureDir, "package.json");
|
|
92
|
+
await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, "utf8");
|
|
93
|
+
return { fixtureDir, packageJsonPath };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
test("includeDependencies resolves mixed dist-tag and semver disjunctions", async () => {
|
|
97
|
+
const { packageJsonPath } = await createFixturePackage({
|
|
98
|
+
name: "fixture-app",
|
|
99
|
+
version: "1.0.0",
|
|
100
|
+
dependencies: {
|
|
101
|
+
"fixture-root": "^1.0.0",
|
|
102
|
+
},
|
|
103
|
+
libcollect: {
|
|
104
|
+
sourceSections: ["dependencies"],
|
|
105
|
+
includeDependencyTypes: ["optionalDependency"],
|
|
106
|
+
concurrency: 1,
|
|
107
|
+
exactVersions: true,
|
|
108
|
+
autoIncludedDependencies: [],
|
|
109
|
+
autoIncludedDependencySpecs: {},
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const progressLogs = [];
|
|
114
|
+
|
|
115
|
+
await withMockedFetch(
|
|
116
|
+
{
|
|
117
|
+
"fixture-root": {
|
|
118
|
+
"dist-tags": {
|
|
119
|
+
latest: "1.0.0",
|
|
120
|
+
},
|
|
121
|
+
versions: {
|
|
122
|
+
"1.0.0": createRegistryMeta("fixture-root", "1.0.0", {
|
|
123
|
+
optionalDependencies: {
|
|
124
|
+
tailwindcss: ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1",
|
|
125
|
+
},
|
|
126
|
+
}),
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
tailwindcss: {
|
|
130
|
+
"dist-tags": {
|
|
131
|
+
latest: "4.2.2",
|
|
132
|
+
insiders: "4.0.0-beta.1",
|
|
133
|
+
},
|
|
134
|
+
versions: {
|
|
135
|
+
"3.4.17": createRegistryMeta("tailwindcss", "3.4.17"),
|
|
136
|
+
"4.0.0-alpha.20": createRegistryMeta("tailwindcss", "4.0.0-alpha.20"),
|
|
137
|
+
"4.0.0-beta.1": createRegistryMeta("tailwindcss", "4.0.0-beta.1"),
|
|
138
|
+
"4.2.2": createRegistryMeta("tailwindcss", "4.2.2"),
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
async () => {
|
|
143
|
+
const result = await includeDependencies(packageJsonPath, {
|
|
144
|
+
logger(message) {
|
|
145
|
+
progressLogs.push(message);
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
assert.equal(result.autoIncludedCount, 1);
|
|
150
|
+
},
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const updatedPackage = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
|
154
|
+
assert.equal(updatedPackage.dependencies["fixture-root"], "^1.0.0");
|
|
155
|
+
assert.equal(updatedPackage.dependencies.tailwindcss, "4.2.2");
|
|
156
|
+
assert.ok(progressLogs.some((entry) => entry.includes("collecting dependency graph")));
|
|
157
|
+
assert.ok(progressLogs.some((entry) => entry.includes("writing updated package.json")));
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("includeDependencies defaults concurrency to 50 when config omits it", async () => {
|
|
161
|
+
const { packageJsonPath } = await createFixturePackage({
|
|
162
|
+
name: "fixture-app",
|
|
163
|
+
version: "1.0.0",
|
|
164
|
+
dependencies: {
|
|
165
|
+
"fixture-root": "^1.0.0",
|
|
166
|
+
},
|
|
167
|
+
libcollect: {
|
|
168
|
+
sourceSections: ["dependencies"],
|
|
169
|
+
includeDependencyTypes: ["optionalDependency"],
|
|
170
|
+
exactVersions: true,
|
|
171
|
+
autoIncludedDependencies: [],
|
|
172
|
+
autoIncludedDependencySpecs: {},
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await withMockedFetch(
|
|
177
|
+
{
|
|
178
|
+
"fixture-root": {
|
|
179
|
+
"dist-tags": {
|
|
180
|
+
latest: "1.0.0",
|
|
181
|
+
},
|
|
182
|
+
versions: {
|
|
183
|
+
"1.0.0": createRegistryMeta("fixture-root", "1.0.0"),
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
async () => {
|
|
188
|
+
await includeDependencies(packageJsonPath);
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const updatedPackage = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
|
193
|
+
assert.equal(updatedPackage.libcollect.concurrency, 50);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("includeDependencies retries failed HTTP responses with exponential backoff", async () => {
|
|
197
|
+
const { packageJsonPath } = await createFixturePackage({
|
|
198
|
+
name: "fixture-app",
|
|
199
|
+
version: "1.0.0",
|
|
200
|
+
dependencies: {
|
|
201
|
+
"fixture-root": "^1.0.0",
|
|
202
|
+
},
|
|
203
|
+
libcollect: {
|
|
204
|
+
sourceSections: ["dependencies"],
|
|
205
|
+
includeDependencyTypes: ["optionalDependency"],
|
|
206
|
+
exactVersions: true,
|
|
207
|
+
autoIncludedDependencies: [],
|
|
208
|
+
autoIncludedDependencySpecs: {},
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const progressLogs = [];
|
|
213
|
+
|
|
214
|
+
await withImmediateTimers(async (delays) => {
|
|
215
|
+
await withMockedFetch(
|
|
216
|
+
{
|
|
217
|
+
"fixture-root": [
|
|
218
|
+
{ ok: false, status: 500, body: {} },
|
|
219
|
+
{ ok: false, status: 500, body: {} },
|
|
220
|
+
{
|
|
221
|
+
"dist-tags": {
|
|
222
|
+
latest: "1.0.0",
|
|
223
|
+
},
|
|
224
|
+
versions: {
|
|
225
|
+
"1.0.0": createRegistryMeta("fixture-root", "1.0.0"),
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
async () => {
|
|
231
|
+
const result = await includeDependencies(packageJsonPath, {
|
|
232
|
+
logger(message) {
|
|
233
|
+
progressLogs.push(message);
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
assert.equal(result.autoIncludedCount, 0);
|
|
238
|
+
},
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
assert.deepEqual(delays, [250, 500]);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
assert.equal(progressLogs.filter((entry) => entry.includes("HTTP 500")).length, 2);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("includeDependencies retries thrown fetch errors with exponential backoff", async () => {
|
|
248
|
+
const { packageJsonPath } = await createFixturePackage({
|
|
249
|
+
name: "fixture-app",
|
|
250
|
+
version: "1.0.0",
|
|
251
|
+
dependencies: {
|
|
252
|
+
"fixture-root": "^1.0.0",
|
|
253
|
+
},
|
|
254
|
+
libcollect: {
|
|
255
|
+
sourceSections: ["dependencies"],
|
|
256
|
+
includeDependencyTypes: ["optionalDependency"],
|
|
257
|
+
exactVersions: true,
|
|
258
|
+
autoIncludedDependencies: [],
|
|
259
|
+
autoIncludedDependencySpecs: {},
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const progressLogs = [];
|
|
264
|
+
|
|
265
|
+
await withImmediateTimers(async (delays) => {
|
|
266
|
+
await withMockedFetch(
|
|
267
|
+
{
|
|
268
|
+
"fixture-root": [
|
|
269
|
+
new TypeError("fetch failed"),
|
|
270
|
+
new Error("socket hang up"),
|
|
271
|
+
{
|
|
272
|
+
"dist-tags": {
|
|
273
|
+
latest: "1.0.0",
|
|
274
|
+
},
|
|
275
|
+
versions: {
|
|
276
|
+
"1.0.0": createRegistryMeta("fixture-root", "1.0.0"),
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
},
|
|
281
|
+
async () => {
|
|
282
|
+
const result = await includeDependencies(packageJsonPath, {
|
|
283
|
+
logger(message) {
|
|
284
|
+
progressLogs.push(message);
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
assert.equal(result.autoIncludedCount, 0);
|
|
289
|
+
},
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
assert.deepEqual(delays, [250, 500]);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
assert.ok(progressLogs.some((entry) => entry.includes("fetch failed")));
|
|
296
|
+
assert.ok(progressLogs.some((entry) => entry.includes("socket hang up")));
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("includeDependencies falls back missing nightly optional dependencies without dropping sibling deps", async () => {
|
|
300
|
+
const { packageJsonPath } = await createFixturePackage({
|
|
301
|
+
name: "fixture-app",
|
|
302
|
+
version: "1.0.0",
|
|
303
|
+
dependencies: {
|
|
304
|
+
"fixture-root": "^1.0.0",
|
|
305
|
+
},
|
|
306
|
+
libcollect: {
|
|
307
|
+
sourceSections: ["dependencies"],
|
|
308
|
+
includeDependencyTypes: ["optionalDependency"],
|
|
309
|
+
exactVersions: true,
|
|
310
|
+
autoIncludedDependencies: [],
|
|
311
|
+
autoIncludedDependencySpecs: {},
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
let result;
|
|
316
|
+
|
|
317
|
+
await withMockedFetch(
|
|
318
|
+
{
|
|
319
|
+
"fixture-root": {
|
|
320
|
+
"dist-tags": {
|
|
321
|
+
latest: "1.0.0",
|
|
322
|
+
},
|
|
323
|
+
versions: {
|
|
324
|
+
"1.0.0": createRegistryMeta("fixture-root", "1.0.0", {
|
|
325
|
+
optionalDependencies: {
|
|
326
|
+
"react-native-worklets": "0.9.0-nightly-20260401-430b7c2f5",
|
|
327
|
+
tailwindcss: "^4.2.2",
|
|
328
|
+
},
|
|
329
|
+
}),
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
"react-native-worklets": {
|
|
333
|
+
"dist-tags": {
|
|
334
|
+
latest: "0.8.1",
|
|
335
|
+
nightly: "0.9.0-nightly-20260331-651c56393",
|
|
336
|
+
},
|
|
337
|
+
versions: {
|
|
338
|
+
"0.8.1": createRegistryMeta("react-native-worklets", "0.8.1"),
|
|
339
|
+
"0.9.0-nightly-20260331-651c56393": createRegistryMeta(
|
|
340
|
+
"react-native-worklets",
|
|
341
|
+
"0.9.0-nightly-20260331-651c56393",
|
|
342
|
+
),
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
tailwindcss: {
|
|
346
|
+
"dist-tags": {
|
|
347
|
+
latest: "4.2.2",
|
|
348
|
+
},
|
|
349
|
+
versions: {
|
|
350
|
+
"4.2.2": createRegistryMeta("tailwindcss", "4.2.2"),
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
async () => {
|
|
355
|
+
result = await includeDependencies(packageJsonPath);
|
|
356
|
+
},
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
const updatedPackage = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
|
360
|
+
assert.equal(updatedPackage.dependencies["fixture-root"], "^1.0.0");
|
|
361
|
+
assert.equal(updatedPackage.dependencies.tailwindcss, "4.2.2");
|
|
362
|
+
assert.equal(
|
|
363
|
+
updatedPackage.dependencies["react-native-worklets"],
|
|
364
|
+
"0.9.0-nightly-20260331-651c56393",
|
|
365
|
+
);
|
|
366
|
+
assert.ok(
|
|
367
|
+
result.warnings.some((warning) =>
|
|
368
|
+
warning.includes("react-native-worklets") && warning.includes("nearest published"),
|
|
369
|
+
),
|
|
370
|
+
);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
test("includeDependencies falls back to nearest published nightly prerelease when exact snapshot is gone", async () => {
|
|
374
|
+
const { packageJsonPath } = await createFixturePackage({
|
|
375
|
+
name: "fixture-app",
|
|
376
|
+
version: "1.0.0",
|
|
377
|
+
dependencies: {
|
|
378
|
+
"fixture-root": "^1.0.0",
|
|
379
|
+
},
|
|
380
|
+
libcollect: {
|
|
381
|
+
sourceSections: ["dependencies"],
|
|
382
|
+
includeDependencyTypes: ["optionalDependency"],
|
|
383
|
+
exactVersions: true,
|
|
384
|
+
autoIncludedDependencies: [],
|
|
385
|
+
autoIncludedDependencySpecs: {},
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
const warnings = [];
|
|
390
|
+
|
|
391
|
+
await withMockedFetch(
|
|
392
|
+
{
|
|
393
|
+
"fixture-root": {
|
|
394
|
+
"dist-tags": {
|
|
395
|
+
latest: "1.0.0",
|
|
396
|
+
},
|
|
397
|
+
versions: {
|
|
398
|
+
"1.0.0": createRegistryMeta("fixture-root", "1.0.0", {
|
|
399
|
+
optionalDependencies: {
|
|
400
|
+
"react-native-worklets": "0.9.0-nightly-20260401-430b7c2f5",
|
|
401
|
+
},
|
|
402
|
+
}),
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
"react-native-worklets": {
|
|
406
|
+
"dist-tags": {
|
|
407
|
+
latest: "0.8.1",
|
|
408
|
+
nightly: "0.9.0-nightly-20260331-651c56393",
|
|
409
|
+
},
|
|
410
|
+
versions: {
|
|
411
|
+
"0.8.1": createRegistryMeta("react-native-worklets", "0.8.1"),
|
|
412
|
+
"0.9.0-nightly-20260329-941d1eb01": createRegistryMeta(
|
|
413
|
+
"react-native-worklets",
|
|
414
|
+
"0.9.0-nightly-20260329-941d1eb01",
|
|
415
|
+
),
|
|
416
|
+
"0.9.0-nightly-20260331-651c56393": createRegistryMeta(
|
|
417
|
+
"react-native-worklets",
|
|
418
|
+
"0.9.0-nightly-20260331-651c56393",
|
|
419
|
+
),
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
async () => {
|
|
424
|
+
const result = await includeDependencies(packageJsonPath);
|
|
425
|
+
warnings.push(...result.warnings);
|
|
426
|
+
assert.equal(result.autoIncludedCount, 1);
|
|
427
|
+
},
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
const updatedPackage = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
|
431
|
+
assert.equal(
|
|
432
|
+
updatedPackage.dependencies["react-native-worklets"],
|
|
433
|
+
"0.9.0-nightly-20260331-651c56393",
|
|
434
|
+
);
|
|
435
|
+
assert.ok(warnings.some((entry) => entry.includes("react-native-worklets")));
|
|
436
|
+
assert.ok(warnings.some((entry) => entry.includes("nearest published")));
|
|
437
|
+
});
|