@rebasepro/common 0.14.1 → 0.14.2-canary.gca521e9
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.es.js +43 -11
- package/dist/index.es.js.map +1 -1
- package/package.json +3 -3
- package/src/util/resolve-relation.ts +55 -11
package/dist/index.es.js
CHANGED
|
@@ -486,7 +486,7 @@ function resolveRelation(relation, sourceCollection, propertyKey) {
|
|
|
486
486
|
const relationName = relation.relationName ?? propertyKey ?? toSnakeCase(targetCollection.slug);
|
|
487
487
|
const shared = {
|
|
488
488
|
relationName,
|
|
489
|
-
target,
|
|
489
|
+
target: () => unwrapModuleNamespace(target()),
|
|
490
490
|
targetSlug: targetCollection.slug,
|
|
491
491
|
onUpdate: relation.onUpdate,
|
|
492
492
|
onDelete: relation.onDelete,
|
|
@@ -554,35 +554,67 @@ function describe(relation, sourceCollection, propertyKey) {
|
|
|
554
554
|
return `Relation${name ? ` '${name}'` : ""} on '${sourceCollection.slug}'`;
|
|
555
555
|
}
|
|
556
556
|
/**
|
|
557
|
-
*
|
|
558
|
-
*
|
|
557
|
+
* A module namespace, unwrapped to the collection it exports.
|
|
558
|
+
*
|
|
559
|
+
* A cycle transpiled to CommonJS does not hand the importing module the
|
|
560
|
+
* *default export* — it hands it the module object, `{ __esModule: true,
|
|
561
|
+
* default: … }`, captured before the exporting module finished evaluating. The
|
|
562
|
+
* `default` slot fills in later, so by the time a lazy `target` thunk runs the
|
|
563
|
+
* collection is sitting right there, one level down. Returning the namespace is
|
|
564
|
+
* never a thing a thunk means to do, and there is exactly one reading of it.
|
|
565
|
+
*
|
|
566
|
+
* Only unwrapped when the inner value is itself a collection: a `default` that
|
|
567
|
+
* is not one is a genuinely wrong thunk, and it should reach the error below
|
|
568
|
+
* rather than be quietly swapped in.
|
|
569
|
+
*/
|
|
570
|
+
function unwrapModuleNamespace(value) {
|
|
571
|
+
if (!value || typeof value !== "object") return value;
|
|
572
|
+
if (value.slug) return value;
|
|
573
|
+
const inner = value.default;
|
|
574
|
+
return inner && typeof inner === "object" && inner.slug ? inner : value;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Call the `target` thunk, and translate the ways an import cycle breaks it into
|
|
578
|
+
* an error that names the cause — or, where the value is recoverable, into the
|
|
579
|
+
* collection the thunk meant.
|
|
559
580
|
*
|
|
560
581
|
* The thunk exists to defer the reference until every module has finished
|
|
561
|
-
* evaluating, and for a cycle that closes at import time it does.
|
|
562
|
-
*
|
|
563
|
-
* two shapes of that:
|
|
582
|
+
* evaluating, and for a cycle that closes at import time it does. Two cycles
|
|
583
|
+
* leave the binding permanently unusable:
|
|
564
584
|
*
|
|
565
585
|
* - **ESM/TDZ.** `const` and `class` bindings in a not-yet-evaluated module are
|
|
566
586
|
* in the temporal dead zone, so reading one throws `ReferenceError: x is not
|
|
567
587
|
* defined`. The stack points at the thunk — a one-line arrow function that is
|
|
568
588
|
* obviously fine — and says nothing about the cycle that made it throw.
|
|
569
|
-
* - **CJS interop.** The half-initialised module object has no
|
|
570
|
-
* the import resolves to `undefined`, and the thunk returns it
|
|
571
|
-
* complaint. That one used to surface here as "did not resolve to a
|
|
589
|
+
* - **CJS interop, unresolved.** The half-initialised module object has no
|
|
590
|
+
* `default` yet, the import resolves to `undefined`, and the thunk returns it
|
|
591
|
+
* without complaint. That one used to surface here as "did not resolve to a
|
|
572
592
|
* collection", which is true and unhelpful.
|
|
573
593
|
*
|
|
574
594
|
* Both mean the same thing, and the fix for both is the same: break the cycle,
|
|
575
595
|
* or move the relation into the collection that does not close it.
|
|
596
|
+
*
|
|
597
|
+
* A third shape is *not* an error, and used to be reported as one. A loader that
|
|
598
|
+
* transpiles ESM to CJS — jiti, which is what `rebase generate-sdk` and
|
|
599
|
+
* `rebase build` load collections with — gives the module entered second in a
|
|
600
|
+
* cycle a namespace object rather than the default export, and never replaces it
|
|
601
|
+
* with a live binding. The thunk then returns `{ __esModule: true, default: … }`
|
|
602
|
+
* holding the fully-initialised collection. Native ESM resolves the same thunk
|
|
603
|
+
* to the collection directly, so this was a loader artefact reported as an
|
|
604
|
+
* authoring mistake, and the advice it gave — make the target a lazy thunk — was
|
|
605
|
+
* already satisfied by the code it was rejecting. Bidirectional relations make
|
|
606
|
+
* these cycles unavoidable, and the lazy thunk is this framework's own answer to
|
|
607
|
+
* them, so {@link unwrapModuleNamespace} takes the collection and moves on.
|
|
576
608
|
*/
|
|
577
609
|
function callTarget(relation, sourceCollection, propertyKey, target) {
|
|
578
610
|
let targetCollection;
|
|
579
611
|
try {
|
|
580
|
-
targetCollection = target();
|
|
612
|
+
targetCollection = unwrapModuleNamespace(target());
|
|
581
613
|
} catch (error) {
|
|
582
614
|
if (error instanceof ReferenceError) throw new Error(`${describe(relation, sourceCollection, propertyKey)} targets a collection that is not initialized yet — almost always an import cycle between the two collection files. Break the cycle (move the shared piece into a third module, or import the target lazily) so the target's module finishes evaluating before the registry is built.`, { cause: error });
|
|
583
615
|
throw error;
|
|
584
616
|
}
|
|
585
|
-
if (!targetCollection?.slug) throw new Error(`${describe(relation, sourceCollection, propertyKey)} has a \`target\` that resolved to ${targetCollection === void 0 ? "`undefined`" : "something that is not a collection"}. ` + (targetCollection === void 0 ? "Under CommonJS interop an import cycle resolves the default import to `undefined`, so check whether this collection and its target import each other. Otherwise the thunk is returning the wrong value — it must return the collection itself, not a promise or a module." : "The thunk must return a collection config with a `slug`."));
|
|
617
|
+
if (!targetCollection?.slug) throw new Error(`${describe(relation, sourceCollection, propertyKey)} has a \`target\` that resolved to ${targetCollection === void 0 ? "`undefined`" : "something that is not a collection"}. ` + (targetCollection === void 0 ? "Under CommonJS interop an import cycle resolves the default import to `undefined`, so check whether this collection and its target import each other. Otherwise the thunk is returning the wrong value — it must return the collection itself, not a promise or a module." : typeof targetCollection.then === "function" ? "The thunk returned a promise — `target: () => import(\"./other\")` is asynchronous. Import the collection at the top of the file and return the binding: `target: () => otherCollection`." : "The thunk must return a collection config with a `slug`."));
|
|
586
618
|
return targetCollection;
|
|
587
619
|
}
|
|
588
620
|
//#endregion
|