@cubing/dev-config 0.6.2 → 0.6.4
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/bin/package.json.ts +93 -31
- package/package.json +1 -1
package/bin/package.json.ts
CHANGED
|
@@ -155,7 +155,7 @@ try {
|
|
|
155
155
|
break;
|
|
156
156
|
}
|
|
157
157
|
default:
|
|
158
|
-
throw new Error("Invalid
|
|
158
|
+
throw new Error("Invalid subcommand.") as never;
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
|
|
@@ -260,8 +260,11 @@ function field<T>(
|
|
|
260
260
|
optional?: boolean;
|
|
261
261
|
additionalChecks?: { [requirementMessage: string]: (t: T) => boolean };
|
|
262
262
|
skipPrintingSuccess?: boolean;
|
|
263
|
+
mustBePopulatedMessage?: string;
|
|
263
264
|
},
|
|
264
265
|
) {
|
|
266
|
+
const mustBePopulatedMessage = () =>
|
|
267
|
+
options?.mustBePopulatedMessage ?? "Field must be populated.";
|
|
265
268
|
const { breadcrumbString, maybeValue } = traverse(breadcrumbs);
|
|
266
269
|
if (!maybeValue) {
|
|
267
270
|
if (options?.optional) {
|
|
@@ -270,7 +273,7 @@ function field<T>(
|
|
|
270
273
|
}
|
|
271
274
|
return;
|
|
272
275
|
} else {
|
|
273
|
-
console.log(`❌ ${breadcrumbString} —
|
|
276
|
+
console.log(`❌ ${breadcrumbString} — ${mustBePopulatedMessage()}`);
|
|
274
277
|
exitCode = 1;
|
|
275
278
|
return;
|
|
276
279
|
}
|
|
@@ -294,9 +297,11 @@ function field<T>(
|
|
|
294
297
|
}
|
|
295
298
|
} else {
|
|
296
299
|
if (category === "undefined") {
|
|
297
|
-
console.log(`❌ ${breadcrumbString} —
|
|
300
|
+
console.log(`❌ ${breadcrumbString} — ${mustBePopulatedMessage()}.`);
|
|
298
301
|
} else if (type === "undefined") {
|
|
299
|
-
console.log(
|
|
302
|
+
console.log(
|
|
303
|
+
`❌ ${breadcrumbString} — Field is populated (but must not be).`,
|
|
304
|
+
);
|
|
300
305
|
} else {
|
|
301
306
|
if (Array.isArray(type)) {
|
|
302
307
|
console.log(
|
|
@@ -313,7 +318,7 @@ function field<T>(
|
|
|
313
318
|
}
|
|
314
319
|
}
|
|
315
320
|
|
|
316
|
-
function
|
|
321
|
+
function mustNotBePopulated(breadcrumbs: Breadcrumbs) {
|
|
317
322
|
const { breadcrumbString, maybeValue } = traverse(breadcrumbs);
|
|
318
323
|
if (maybeValue) {
|
|
319
324
|
console.log(`❌ ${breadcrumbString} — Must not be present.`);
|
|
@@ -393,6 +398,19 @@ field(["type"], "string", {
|
|
|
393
398
|
'Type must be `"module"`.': (type: string) => type === "module",
|
|
394
399
|
},
|
|
395
400
|
});
|
|
401
|
+
if ("main" in packageJSON || "types" in packageJSON) {
|
|
402
|
+
field(["main"], "string", {
|
|
403
|
+
mustBePopulatedMessage: 'Must be populated if "types" is populated.',
|
|
404
|
+
});
|
|
405
|
+
field(["types"], "string", {
|
|
406
|
+
mustBePopulatedMessage: 'Must be populated if "main" is populated.',
|
|
407
|
+
});
|
|
408
|
+
} else {
|
|
409
|
+
console.log("☑️ .main");
|
|
410
|
+
console.log("☑️ .types");
|
|
411
|
+
}
|
|
412
|
+
mustNotBePopulated(["module"]);
|
|
413
|
+
mustNotBePopulated(["browser"]);
|
|
396
414
|
field(["exports"], "object");
|
|
397
415
|
field(["bin"], "object", { optional: true });
|
|
398
416
|
field(["dependencies"], "object", { optional: true });
|
|
@@ -407,15 +425,6 @@ field(["files"], "array");
|
|
|
407
425
|
field(["scripts"], "object");
|
|
408
426
|
// Set to `"# no-op"` if needed.
|
|
409
427
|
field(["scripts", "prepublishOnly"], "string");
|
|
410
|
-
if ("main" in packageJSON || "types" in packageJSON) {
|
|
411
|
-
field(["main"], "string");
|
|
412
|
-
field(["types"], "string");
|
|
413
|
-
} else {
|
|
414
|
-
console.log("☑️ .main");
|
|
415
|
-
console.log("☑️ .types");
|
|
416
|
-
}
|
|
417
|
-
mustNotBePresent(["module"]);
|
|
418
|
-
mustNotBePresent(["browser"]);
|
|
419
428
|
|
|
420
429
|
console.log("Checking paths of binaries and exports:");
|
|
421
430
|
|
|
@@ -485,7 +494,7 @@ function checkPath(
|
|
|
485
494
|
break;
|
|
486
495
|
}
|
|
487
496
|
default:
|
|
488
|
-
throw new Error("Invalid
|
|
497
|
+
throw new Error("Invalid subcommand.") as never;
|
|
489
498
|
}
|
|
490
499
|
}
|
|
491
500
|
}
|
|
@@ -500,14 +509,14 @@ function checkPath(
|
|
|
500
509
|
// TODO: allow folders (with a required trailing slash)?
|
|
501
510
|
if (!(await resolvedPath.existsAsFile())) {
|
|
502
511
|
exitCode = 1;
|
|
503
|
-
return `❌ ${breadcrumbString} —
|
|
512
|
+
return `❌ ${breadcrumbString} — Path is not present on disk. — ${value}`;
|
|
504
513
|
}
|
|
505
514
|
if (options.mustBeExecutable) {
|
|
506
515
|
if (!((await resolvedPath.stat()).mode ^ constants.X_OK)) {
|
|
507
516
|
// This is not considered fixable because the binary may be the output
|
|
508
517
|
// of a build process. In that case, the build process is responsible
|
|
509
518
|
// for marking it as executable.
|
|
510
|
-
return `❌ ${breadcrumbString} —
|
|
519
|
+
return `❌ ${breadcrumbString} — File at path must be executable — ${value}`;
|
|
511
520
|
}
|
|
512
521
|
}
|
|
513
522
|
return `✅ ${breadcrumbString} — OK — ${value}`;
|
|
@@ -520,14 +529,15 @@ checkPath(["types"], { expectPrefix: ResolutionPrefix.Relative });
|
|
|
520
529
|
checkPath(["module"], { expectPrefix: ResolutionPrefix.Relative });
|
|
521
530
|
checkPath(["browser"], { expectPrefix: ResolutionPrefix.Relative });
|
|
522
531
|
|
|
523
|
-
|
|
524
|
-
|
|
532
|
+
const { exports } = packageJSON;
|
|
533
|
+
if (exports) {
|
|
534
|
+
for (const [subpath, value] of Object.entries(exports)) {
|
|
525
535
|
if (!value) {
|
|
526
536
|
// biome-ignore lint/complexity/noUselessContinue: Explicit control flow.
|
|
527
537
|
continue;
|
|
528
538
|
} else if (typeof value === "string") {
|
|
529
539
|
// TODO: error?
|
|
530
|
-
checkPath(["exports", [
|
|
540
|
+
checkPath(["exports", [subpath]], {
|
|
531
541
|
expectPrefix: ResolutionPrefix.Relative,
|
|
532
542
|
});
|
|
533
543
|
} else if (value === null) {
|
|
@@ -542,25 +552,70 @@ if (packageJSON.exports) {
|
|
|
542
552
|
|
|
543
553
|
checks.push(
|
|
544
554
|
(async () => {
|
|
545
|
-
const { breadcrumbString } = traverse(["exports", [
|
|
555
|
+
const { breadcrumbString } = traverse(["exports", [subpath]]);
|
|
556
|
+
const fixingLines = [];
|
|
546
557
|
const orderingErrorLines = [];
|
|
547
558
|
/**
|
|
548
559
|
* https://nodejs.org/api/packages.html#conditional-exports
|
|
549
560
|
*/
|
|
561
|
+
let updateKeys = false;
|
|
550
562
|
if (keys.includes("types")) {
|
|
551
563
|
if (keys[0] !== "types") {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
564
|
+
switch (subcommand) {
|
|
565
|
+
case "check": {
|
|
566
|
+
orderingErrorLines.push(
|
|
567
|
+
` ↪ "types" must be the first export if present — 📝 fixable!`,
|
|
568
|
+
);
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
case "format": {
|
|
572
|
+
fixingLines.push(
|
|
573
|
+
` ↪ "types" must be the first export if present — 📝 fixing!`,
|
|
574
|
+
);
|
|
575
|
+
keys.splice(keys.indexOf("types"), 1);
|
|
576
|
+
keys.splice(0, 0, "types");
|
|
577
|
+
updateKeys = true;
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
default:
|
|
581
|
+
throw new Error("Invalid subcommand.") as never;
|
|
582
|
+
}
|
|
555
583
|
}
|
|
556
584
|
}
|
|
557
585
|
if (keys.includes("default")) {
|
|
558
586
|
if (keys.at(-1) !== "default") {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
587
|
+
switch (subcommand) {
|
|
588
|
+
case "check": {
|
|
589
|
+
orderingErrorLines.push(
|
|
590
|
+
` ↪ "default" must be the last export if present — 📝 fixable!`,
|
|
591
|
+
);
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
case "format": {
|
|
595
|
+
fixingLines.push(
|
|
596
|
+
` ↪ "default" must be the last export if present — 📝 fixing!`,
|
|
597
|
+
);
|
|
598
|
+
keys.splice(keys.indexOf("default"), 1);
|
|
599
|
+
keys.push("default");
|
|
600
|
+
updateKeys = true;
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
default:
|
|
604
|
+
throw new Error("Invalid subcommand.") as never;
|
|
605
|
+
}
|
|
562
606
|
}
|
|
563
607
|
}
|
|
608
|
+
if (updateKeys) {
|
|
609
|
+
// TODO: avoid type wrangling.
|
|
610
|
+
const newConditionalExports: Record<string, string> = {};
|
|
611
|
+
for (const key of keys) {
|
|
612
|
+
newConditionalExports[key] = (value as Record<string, string>)[
|
|
613
|
+
key
|
|
614
|
+
];
|
|
615
|
+
}
|
|
616
|
+
(exports as Record<string, Record<string, string>>)[subpath] =
|
|
617
|
+
newConditionalExports;
|
|
618
|
+
}
|
|
564
619
|
for (const key of keys) {
|
|
565
620
|
// Note `"require"` is *emphatically not allowed*.
|
|
566
621
|
if (!["types", "import", "default"].includes(key)) {
|
|
@@ -569,19 +624,26 @@ if (packageJSON.exports) {
|
|
|
569
624
|
);
|
|
570
625
|
}
|
|
571
626
|
}
|
|
572
|
-
if (orderingErrorLines) {
|
|
573
|
-
exitCode =
|
|
627
|
+
if (orderingErrorLines.length > 0) {
|
|
628
|
+
exitCode = 1;
|
|
574
629
|
return [
|
|
575
630
|
`❌ ${breadcrumbString} — Invalid keys:`,
|
|
576
631
|
...orderingErrorLines,
|
|
577
632
|
].join("\n");
|
|
578
633
|
} else {
|
|
579
|
-
|
|
634
|
+
if (fixingLines.length > 0) {
|
|
635
|
+
return [
|
|
636
|
+
`✅ ${breadcrumbString} — Fixing key ordering:`,
|
|
637
|
+
...fixingLines,
|
|
638
|
+
].join("\n");
|
|
639
|
+
} else {
|
|
640
|
+
return `✅ ${breadcrumbString} — Key set and ordering is okay.`;
|
|
641
|
+
}
|
|
580
642
|
}
|
|
581
643
|
})(),
|
|
582
644
|
);
|
|
583
645
|
for (const secondaryKey of keys) {
|
|
584
|
-
checkPath(["exports", [
|
|
646
|
+
checkPath(["exports", [subpath], secondaryKey], {
|
|
585
647
|
expectPrefix: ResolutionPrefix.Relative,
|
|
586
648
|
});
|
|
587
649
|
}
|