@objectstack/types 17.1.0 → 17.3.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/CHANGELOG.md +1026 -0
- package/dist/index.d.mts +533 -9
- package/dist/index.d.ts +533 -9
- package/dist/index.js +309 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +293 -3
- package/dist/index.mjs.map +1 -1
- package/dist/node.d.mts +90 -11
- package/dist/node.d.ts +90 -11
- package/dist/node.js +213 -10
- package/dist/node.js.map +1 -1
- package/dist/node.mjs +215 -12
- package/dist/node.mjs.map +1 -1
- package/package.json +3 -3
package/dist/node.d.mts
CHANGED
|
@@ -7,6 +7,32 @@
|
|
|
7
7
|
* did.
|
|
8
8
|
*/
|
|
9
9
|
type HostImporter = (pkg: string) => Promise<any>;
|
|
10
|
+
/**
|
|
11
|
+
* The importing package's OWN dynamic import — write it literally, in the
|
|
12
|
+
* calling module:
|
|
13
|
+
*
|
|
14
|
+
* createHostImporter(hostRoot, { fallbackImport: (s) => import(s) })
|
|
15
|
+
*
|
|
16
|
+
* `any` for the same reason {@link HostImporter} uses it: the module namespace
|
|
17
|
+
* belongs to a package this repo does not compile against.
|
|
18
|
+
*/
|
|
19
|
+
type FallbackImport = (specifier: string) => Promise<any>;
|
|
20
|
+
/** Options for {@link createHostImporter}. */
|
|
21
|
+
interface HostImporterOptions {
|
|
22
|
+
/**
|
|
23
|
+
* The resolution base for everything the host app does NOT declare — supplied
|
|
24
|
+
* as the caller's own `import()` rather than as a URL string, because a
|
|
25
|
+
* string base was MEASURED to be unimplementable without a regression. See
|
|
26
|
+
* {@link createHostImporter}'s "why a function" note for both measurements.
|
|
27
|
+
*
|
|
28
|
+
* Omitted ⇒ the fallback resolves from `@objectstack/types`, which sees only
|
|
29
|
+
* `@objectstack/types`'s own dependencies. That default is retained so an
|
|
30
|
+
* out-of-tree caller cannot be broken by this parameter's arrival, and the
|
|
31
|
+
* `undeclared` failure text names it explicitly so the gap reports itself
|
|
32
|
+
* instead of being rediscovered.
|
|
33
|
+
*/
|
|
34
|
+
fallbackImport?: FallbackImport;
|
|
35
|
+
}
|
|
10
36
|
/**
|
|
11
37
|
* A `require` anchored at the **host app's** `package.json` — i.e. the project
|
|
12
38
|
* `objectstack serve` was invoked in, or the app `bootStack` is verifying, whose
|
|
@@ -95,10 +121,19 @@ declare function isDeclaredByHost(specifier: string, hostRoot?: string): boolean
|
|
|
95
121
|
* in the app and install.
|
|
96
122
|
* - `declared-unresolvable` — the app declares it and it still would not
|
|
97
123
|
* resolve. Remedy: fix the INSTALL. Re-reading the manifest is wasted effort.
|
|
124
|
+
* - `declared-no-loadable-entry` (#14041) — the app declares it, the install
|
|
125
|
+
* delivered it, and the package's own `exports` names NO entry Node can load
|
|
126
|
+
* for the requested subpath — no `require`-condition target (which is why the
|
|
127
|
+
* CJS resolution refused) and no `import`-condition one for the fallback
|
|
128
|
+
* either (a `types`-only or `browser`-only publish, or a subpath the map
|
|
129
|
+
* never names). Remedy: change the PACKAGE — neither the app's manifest nor
|
|
130
|
+
* its install can ever fix this, which is exactly why it must not share the
|
|
131
|
+
* `declared-unresolvable` INSTALL wording.
|
|
98
132
|
*
|
|
99
|
-
* An evaluation crash is
|
|
133
|
+
* An evaluation crash is none of these: it propagates untouched and carries no
|
|
134
|
+
* kind.
|
|
100
135
|
*/
|
|
101
|
-
type HostImportFailureKind = 'undeclared' | 'declared-unresolvable';
|
|
136
|
+
type HostImportFailureKind = 'undeclared' | 'declared-unresolvable' | 'declared-no-loadable-entry';
|
|
102
137
|
/**
|
|
103
138
|
* Property carrying {@link HostImportFailureKind} on a thrown error.
|
|
104
139
|
*
|
|
@@ -123,20 +158,64 @@ declare function hostImportFailureKind(err: unknown): HostImportFailureKind | un
|
|
|
123
158
|
* the install is broken. It is not retried bare: falling back there would
|
|
124
159
|
* reintroduce exactly the "some other package happens to supply it" accident
|
|
125
160
|
* this gate closes, and would report an install problem as an absence.
|
|
126
|
-
* 3. Undeclared falls back to the
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
161
|
+
* 3. Undeclared falls back to the CALLER's own resolution, which is what keeps
|
|
162
|
+
* every framework-owned load working (`serve`'s plugin-auth / service-i18n
|
|
163
|
+
* path, `bootStack`'s service plugins). Bare `import()` is ESM, and ESM does
|
|
164
|
+
* not honour `NODE_PATH`, so the fallback cannot re-open the hole either.
|
|
165
|
+
* Only when that fails as module-not-found does the undeclared error
|
|
166
|
+
* surface; a package that RESOLVES and then throws while evaluating is a
|
|
167
|
+
* genuine crash and propagates untouched, as before.
|
|
168
|
+
*
|
|
169
|
+
* ── The caller supplies that base, and why it is a FUNCTION (#10943) ─────────
|
|
170
|
+
*
|
|
171
|
+
* Step 3 said "the importing package's own resolution" long before anything
|
|
172
|
+
* made it true. The fallback was a bare `import()` written HERE, and ESM
|
|
173
|
+
* resolves a bare specifier against the module containing the call — so it
|
|
174
|
+
* resolved from `@objectstack/types`, which under a pnpm-isolated layout can
|
|
175
|
+
* see only `@objectstack/types`'s own dependencies. Measured on `main` from an
|
|
176
|
+
* app declaring nothing, `@objectstack/plugin-auth`, `@objectstack/plugin-audit`
|
|
177
|
+
* and `chalk` all resolve from `packages/cli` and all failed through this
|
|
178
|
+
* helper; `@objectstack/spec` — the one dependency this package declares — was
|
|
179
|
+
* the only name that came back OK, which is the whole pattern. Under a hoisted
|
|
180
|
+
* npm/yarn layout the same fallback usually DOES find the caller's
|
|
181
|
+
* dependencies, so the claim was green in some installs and absent in others:
|
|
182
|
+
* the layout-dependence class cloud#1013 and #10645 exist to close, one level
|
|
183
|
+
* up. A declared contract the implementation does not keep is the thing this
|
|
184
|
+
* repo fixes at the producer (Prime Directive #12), so the mechanism moved
|
|
185
|
+
* rather than the sentence.
|
|
186
|
+
*
|
|
187
|
+
* The base arrives as the caller's own `import()` and NOT as a `parentURL` /
|
|
188
|
+
* `import.meta.url` string. Both string spellings were measured on Node
|
|
189
|
+
* v22.22.2 and both are wrong:
|
|
190
|
+
*
|
|
191
|
+
* - `import.meta.resolve(specifier, parentURL)` — the parent argument is
|
|
192
|
+
* SILENTLY IGNORED without `--experimental-import-meta-resolve`. Measured:
|
|
193
|
+
* resolving `@objectstack/plugin-auth` against a `packages/types` parent
|
|
194
|
+
* returned `packages/cli/node_modules/...`, i.e. the caller's own answer,
|
|
195
|
+
* byte-identical to passing no parent at all. It would have compiled, run,
|
|
196
|
+
* and pinned green while ignoring the base — a phantom fix of exactly the
|
|
197
|
+
* kind this card is about.
|
|
198
|
+
* - `createRequire(parentURL).resolve(specifier)` — CJS resolution, which
|
|
199
|
+
* honours `NODE_PATH`. Measured against a store reachable only through
|
|
200
|
+
* `NODE_PATH`: the CJS resolve found it (with and without the `paths`
|
|
201
|
+
* option, since GLOBAL_FOLDERS are always appended) while the ESM bare
|
|
202
|
+
* `import()` did not. That is #4719's hole re-opened on the fallback path,
|
|
203
|
+
* and it would have falsified the "ESM does not honour NODE_PATH" sentence
|
|
204
|
+
* three lines above.
|
|
205
|
+
*
|
|
206
|
+
* A function written in the calling module is the only spelling that uses
|
|
207
|
+
* Node's real ESM resolver anchored where the caller actually lives: no flag,
|
|
208
|
+
* no `NODE_PATH`, no second resolution algorithm to drift from the first.
|
|
133
209
|
*
|
|
134
210
|
* @param hostRoot Directory holding the host app's `package.json` (default: the
|
|
135
211
|
* process CWD, which is where the CLI reads `objectstack.config.ts` from too).
|
|
136
212
|
* Note this used to take a pre-built `NodeRequire`; it needs the ROOT now,
|
|
137
213
|
* because a `NodeRequire` cannot be asked where it was anchored and the manifest
|
|
138
214
|
* has to be read from there.
|
|
215
|
+
* @param options {@link HostImporterOptions.fallbackImport} carries the caller's
|
|
216
|
+
* resolution base. Omitting it keeps the pre-#10943 behaviour (this package's
|
|
217
|
+
* own resolution) so no out-of-tree caller changes under its feet.
|
|
139
218
|
*/
|
|
140
|
-
declare function createHostImporter(hostRoot?: string): HostImporter;
|
|
219
|
+
declare function createHostImporter(hostRoot?: string, options?: HostImporterOptions): HostImporter;
|
|
141
220
|
|
|
142
|
-
export { HOST_DECLARATION_FIELDS, HOST_IMPORT_FAILURE_KIND, type HostDeclaration, type HostDeclarationField, type HostImportFailureKind, type HostImporter, createHostImporter, createHostRequire, hostImportFailureKind, isDeclaredByHost, packageNameFromSpecifier, readHostDeclaration };
|
|
221
|
+
export { type FallbackImport, HOST_DECLARATION_FIELDS, HOST_IMPORT_FAILURE_KIND, type HostDeclaration, type HostDeclarationField, type HostImportFailureKind, type HostImporter, type HostImporterOptions, createHostImporter, createHostRequire, hostImportFailureKind, isDeclaredByHost, packageNameFromSpecifier, readHostDeclaration };
|
package/dist/node.d.ts
CHANGED
|
@@ -7,6 +7,32 @@
|
|
|
7
7
|
* did.
|
|
8
8
|
*/
|
|
9
9
|
type HostImporter = (pkg: string) => Promise<any>;
|
|
10
|
+
/**
|
|
11
|
+
* The importing package's OWN dynamic import — write it literally, in the
|
|
12
|
+
* calling module:
|
|
13
|
+
*
|
|
14
|
+
* createHostImporter(hostRoot, { fallbackImport: (s) => import(s) })
|
|
15
|
+
*
|
|
16
|
+
* `any` for the same reason {@link HostImporter} uses it: the module namespace
|
|
17
|
+
* belongs to a package this repo does not compile against.
|
|
18
|
+
*/
|
|
19
|
+
type FallbackImport = (specifier: string) => Promise<any>;
|
|
20
|
+
/** Options for {@link createHostImporter}. */
|
|
21
|
+
interface HostImporterOptions {
|
|
22
|
+
/**
|
|
23
|
+
* The resolution base for everything the host app does NOT declare — supplied
|
|
24
|
+
* as the caller's own `import()` rather than as a URL string, because a
|
|
25
|
+
* string base was MEASURED to be unimplementable without a regression. See
|
|
26
|
+
* {@link createHostImporter}'s "why a function" note for both measurements.
|
|
27
|
+
*
|
|
28
|
+
* Omitted ⇒ the fallback resolves from `@objectstack/types`, which sees only
|
|
29
|
+
* `@objectstack/types`'s own dependencies. That default is retained so an
|
|
30
|
+
* out-of-tree caller cannot be broken by this parameter's arrival, and the
|
|
31
|
+
* `undeclared` failure text names it explicitly so the gap reports itself
|
|
32
|
+
* instead of being rediscovered.
|
|
33
|
+
*/
|
|
34
|
+
fallbackImport?: FallbackImport;
|
|
35
|
+
}
|
|
10
36
|
/**
|
|
11
37
|
* A `require` anchored at the **host app's** `package.json` — i.e. the project
|
|
12
38
|
* `objectstack serve` was invoked in, or the app `bootStack` is verifying, whose
|
|
@@ -95,10 +121,19 @@ declare function isDeclaredByHost(specifier: string, hostRoot?: string): boolean
|
|
|
95
121
|
* in the app and install.
|
|
96
122
|
* - `declared-unresolvable` — the app declares it and it still would not
|
|
97
123
|
* resolve. Remedy: fix the INSTALL. Re-reading the manifest is wasted effort.
|
|
124
|
+
* - `declared-no-loadable-entry` (#14041) — the app declares it, the install
|
|
125
|
+
* delivered it, and the package's own `exports` names NO entry Node can load
|
|
126
|
+
* for the requested subpath — no `require`-condition target (which is why the
|
|
127
|
+
* CJS resolution refused) and no `import`-condition one for the fallback
|
|
128
|
+
* either (a `types`-only or `browser`-only publish, or a subpath the map
|
|
129
|
+
* never names). Remedy: change the PACKAGE — neither the app's manifest nor
|
|
130
|
+
* its install can ever fix this, which is exactly why it must not share the
|
|
131
|
+
* `declared-unresolvable` INSTALL wording.
|
|
98
132
|
*
|
|
99
|
-
* An evaluation crash is
|
|
133
|
+
* An evaluation crash is none of these: it propagates untouched and carries no
|
|
134
|
+
* kind.
|
|
100
135
|
*/
|
|
101
|
-
type HostImportFailureKind = 'undeclared' | 'declared-unresolvable';
|
|
136
|
+
type HostImportFailureKind = 'undeclared' | 'declared-unresolvable' | 'declared-no-loadable-entry';
|
|
102
137
|
/**
|
|
103
138
|
* Property carrying {@link HostImportFailureKind} on a thrown error.
|
|
104
139
|
*
|
|
@@ -123,20 +158,64 @@ declare function hostImportFailureKind(err: unknown): HostImportFailureKind | un
|
|
|
123
158
|
* the install is broken. It is not retried bare: falling back there would
|
|
124
159
|
* reintroduce exactly the "some other package happens to supply it" accident
|
|
125
160
|
* this gate closes, and would report an install problem as an absence.
|
|
126
|
-
* 3. Undeclared falls back to the
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
161
|
+
* 3. Undeclared falls back to the CALLER's own resolution, which is what keeps
|
|
162
|
+
* every framework-owned load working (`serve`'s plugin-auth / service-i18n
|
|
163
|
+
* path, `bootStack`'s service plugins). Bare `import()` is ESM, and ESM does
|
|
164
|
+
* not honour `NODE_PATH`, so the fallback cannot re-open the hole either.
|
|
165
|
+
* Only when that fails as module-not-found does the undeclared error
|
|
166
|
+
* surface; a package that RESOLVES and then throws while evaluating is a
|
|
167
|
+
* genuine crash and propagates untouched, as before.
|
|
168
|
+
*
|
|
169
|
+
* ── The caller supplies that base, and why it is a FUNCTION (#10943) ─────────
|
|
170
|
+
*
|
|
171
|
+
* Step 3 said "the importing package's own resolution" long before anything
|
|
172
|
+
* made it true. The fallback was a bare `import()` written HERE, and ESM
|
|
173
|
+
* resolves a bare specifier against the module containing the call — so it
|
|
174
|
+
* resolved from `@objectstack/types`, which under a pnpm-isolated layout can
|
|
175
|
+
* see only `@objectstack/types`'s own dependencies. Measured on `main` from an
|
|
176
|
+
* app declaring nothing, `@objectstack/plugin-auth`, `@objectstack/plugin-audit`
|
|
177
|
+
* and `chalk` all resolve from `packages/cli` and all failed through this
|
|
178
|
+
* helper; `@objectstack/spec` — the one dependency this package declares — was
|
|
179
|
+
* the only name that came back OK, which is the whole pattern. Under a hoisted
|
|
180
|
+
* npm/yarn layout the same fallback usually DOES find the caller's
|
|
181
|
+
* dependencies, so the claim was green in some installs and absent in others:
|
|
182
|
+
* the layout-dependence class cloud#1013 and #10645 exist to close, one level
|
|
183
|
+
* up. A declared contract the implementation does not keep is the thing this
|
|
184
|
+
* repo fixes at the producer (Prime Directive #12), so the mechanism moved
|
|
185
|
+
* rather than the sentence.
|
|
186
|
+
*
|
|
187
|
+
* The base arrives as the caller's own `import()` and NOT as a `parentURL` /
|
|
188
|
+
* `import.meta.url` string. Both string spellings were measured on Node
|
|
189
|
+
* v22.22.2 and both are wrong:
|
|
190
|
+
*
|
|
191
|
+
* - `import.meta.resolve(specifier, parentURL)` — the parent argument is
|
|
192
|
+
* SILENTLY IGNORED without `--experimental-import-meta-resolve`. Measured:
|
|
193
|
+
* resolving `@objectstack/plugin-auth` against a `packages/types` parent
|
|
194
|
+
* returned `packages/cli/node_modules/...`, i.e. the caller's own answer,
|
|
195
|
+
* byte-identical to passing no parent at all. It would have compiled, run,
|
|
196
|
+
* and pinned green while ignoring the base — a phantom fix of exactly the
|
|
197
|
+
* kind this card is about.
|
|
198
|
+
* - `createRequire(parentURL).resolve(specifier)` — CJS resolution, which
|
|
199
|
+
* honours `NODE_PATH`. Measured against a store reachable only through
|
|
200
|
+
* `NODE_PATH`: the CJS resolve found it (with and without the `paths`
|
|
201
|
+
* option, since GLOBAL_FOLDERS are always appended) while the ESM bare
|
|
202
|
+
* `import()` did not. That is #4719's hole re-opened on the fallback path,
|
|
203
|
+
* and it would have falsified the "ESM does not honour NODE_PATH" sentence
|
|
204
|
+
* three lines above.
|
|
205
|
+
*
|
|
206
|
+
* A function written in the calling module is the only spelling that uses
|
|
207
|
+
* Node's real ESM resolver anchored where the caller actually lives: no flag,
|
|
208
|
+
* no `NODE_PATH`, no second resolution algorithm to drift from the first.
|
|
133
209
|
*
|
|
134
210
|
* @param hostRoot Directory holding the host app's `package.json` (default: the
|
|
135
211
|
* process CWD, which is where the CLI reads `objectstack.config.ts` from too).
|
|
136
212
|
* Note this used to take a pre-built `NodeRequire`; it needs the ROOT now,
|
|
137
213
|
* because a `NodeRequire` cannot be asked where it was anchored and the manifest
|
|
138
214
|
* has to be read from there.
|
|
215
|
+
* @param options {@link HostImporterOptions.fallbackImport} carries the caller's
|
|
216
|
+
* resolution base. Omitting it keeps the pre-#10943 behaviour (this package's
|
|
217
|
+
* own resolution) so no out-of-tree caller changes under its feet.
|
|
139
218
|
*/
|
|
140
|
-
declare function createHostImporter(hostRoot?: string): HostImporter;
|
|
219
|
+
declare function createHostImporter(hostRoot?: string, options?: HostImporterOptions): HostImporter;
|
|
141
220
|
|
|
142
|
-
export { HOST_DECLARATION_FIELDS, HOST_IMPORT_FAILURE_KIND, type HostDeclaration, type HostDeclarationField, type HostImportFailureKind, type HostImporter, createHostImporter, createHostRequire, hostImportFailureKind, isDeclaredByHost, packageNameFromSpecifier, readHostDeclaration };
|
|
221
|
+
export { type FallbackImport, HOST_DECLARATION_FIELDS, HOST_IMPORT_FAILURE_KIND, type HostDeclaration, type HostDeclarationField, type HostImportFailureKind, type HostImporter, type HostImporterOptions, createHostImporter, createHostRequire, hostImportFailureKind, isDeclaredByHost, packageNameFromSpecifier, readHostDeclaration };
|
package/dist/node.js
CHANGED
|
@@ -87,7 +87,7 @@ function isDeclaredByHost(specifier, hostRoot) {
|
|
|
87
87
|
var HOST_IMPORT_FAILURE_KIND = "objectstackHostImportFailureKind";
|
|
88
88
|
function hostImportFailureKind(err) {
|
|
89
89
|
const kind = err?.[HOST_IMPORT_FAILURE_KIND];
|
|
90
|
-
return kind === "undeclared" || kind === "declared-unresolvable" ? kind : void 0;
|
|
90
|
+
return kind === "undeclared" || kind === "declared-unresolvable" || kind === "declared-no-loadable-entry" ? kind : void 0;
|
|
91
91
|
}
|
|
92
92
|
function hostImportError(kind, message, cause) {
|
|
93
93
|
const err = new Error(message);
|
|
@@ -97,9 +97,10 @@ function hostImportError(kind, message, cause) {
|
|
|
97
97
|
[HOST_IMPORT_FAILURE_KIND]: kind
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
|
-
function undeclaredMessage(declaration, cause) {
|
|
100
|
+
function undeclaredMessage(declaration, cause, callerBaseSupplied) {
|
|
101
101
|
const { packageName, hostRoot, manifestMissing } = declaration;
|
|
102
102
|
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
103
|
+
const baseNote = callerBaseSupplied ? "" : "\n (the caller did not pass `fallbackImport`, so that fallback resolved from\n @objectstack/types, which can see only its own dependencies \u2014 a caller that\n needs its own resolution passes `{ fallbackImport: (s) => import(s) }`, #10943)";
|
|
103
104
|
return `Cannot find package '${packageName}': the host app does not declare it.
|
|
104
105
|
host app: ${hostRoot}
|
|
105
106
|
` + (manifestMissing ? " no readable package.json was found there \u2014 nothing can be declared\n" : ` checked: ${HOST_DECLARATION_FIELDS.join(", ")}
|
|
@@ -112,7 +113,7 @@ function undeclaredMessage(declaration, cause) {
|
|
|
112
113
|
at in every pnpm bin shim \u2014 used to resolve here regardless of the app's
|
|
113
114
|
package.json, so the same app booted or refused depending on how the
|
|
114
115
|
process was launched. The declaration is the contract.
|
|
115
|
-
(fallback resolution also failed: ${detail})`;
|
|
116
|
+
(fallback resolution also failed: ${detail})${baseNote}`;
|
|
116
117
|
}
|
|
117
118
|
function unresolvableMessage(declaration, cause) {
|
|
118
119
|
const { packageName, hostRoot, field, specifier } = declaration;
|
|
@@ -128,8 +129,192 @@ function unresolvableMessage(declaration, cause) {
|
|
|
128
129
|
\u2022 it IS installed but its "main"/"exports" points at a dist that was never built
|
|
129
130
|
(resolver: ${detail})`;
|
|
130
131
|
}
|
|
131
|
-
|
|
132
|
+
var ESM_IMPORT_CONDITIONS = /* @__PURE__ */ new Set([
|
|
133
|
+
"node-addons",
|
|
134
|
+
"node",
|
|
135
|
+
"import",
|
|
136
|
+
"default"
|
|
137
|
+
]);
|
|
138
|
+
var CJS_REQUIRE_CONDITIONS = /* @__PURE__ */ new Set([
|
|
139
|
+
"node-addons",
|
|
140
|
+
"node",
|
|
141
|
+
"require",
|
|
142
|
+
"default"
|
|
143
|
+
]);
|
|
144
|
+
function selectConditionTarget(node, conditions) {
|
|
145
|
+
if (typeof node === "string") return node;
|
|
146
|
+
if (Array.isArray(node)) {
|
|
147
|
+
for (const alternative of node) {
|
|
148
|
+
const hit = selectConditionTarget(alternative, conditions);
|
|
149
|
+
if (hit !== void 0) return hit;
|
|
150
|
+
}
|
|
151
|
+
return void 0;
|
|
152
|
+
}
|
|
153
|
+
if (node === null || typeof node !== "object") return void 0;
|
|
154
|
+
for (const entry of Object.entries(node)) {
|
|
155
|
+
if (!conditions.has(entry[0])) continue;
|
|
156
|
+
const hit = selectConditionTarget(entry[1], conditions);
|
|
157
|
+
if (hit !== void 0) return hit;
|
|
158
|
+
}
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
function resolveExportsSubpath(exportsField, subpath, conditions = ESM_IMPORT_CONDITIONS) {
|
|
162
|
+
if (exportsField === void 0) return void 0;
|
|
163
|
+
const keys = typeof exportsField === "object" && exportsField !== null && !Array.isArray(exportsField) ? Object.keys(exportsField) : void 0;
|
|
164
|
+
const isSubpathMap = keys !== void 0 && keys.length > 0 && keys.every((key) => key === "." || key.indexOf("./") === 0);
|
|
165
|
+
if (!isSubpathMap) {
|
|
166
|
+
return subpath === "." ? selectConditionTarget(exportsField, conditions) : void 0;
|
|
167
|
+
}
|
|
168
|
+
const map = exportsField;
|
|
169
|
+
if (Object.prototype.hasOwnProperty.call(map, subpath)) {
|
|
170
|
+
return selectConditionTarget(map[subpath], conditions);
|
|
171
|
+
}
|
|
172
|
+
let best;
|
|
173
|
+
for (const entry of Object.entries(map)) {
|
|
174
|
+
const star = entry[0].indexOf("*");
|
|
175
|
+
if (star < 0 || entry[0].indexOf("*", star + 1) >= 0) continue;
|
|
176
|
+
const prefix = entry[0].slice(0, star);
|
|
177
|
+
const suffix = entry[0].slice(star + 1);
|
|
178
|
+
if (subpath.indexOf(prefix) !== 0) continue;
|
|
179
|
+
if (suffix !== "" && subpath.slice(subpath.length - suffix.length) !== suffix) continue;
|
|
180
|
+
if (subpath.length < prefix.length + suffix.length) continue;
|
|
181
|
+
if (best !== void 0 && (best.prefix.length > prefix.length || best.prefix.length === prefix.length && best.suffix.length >= suffix.length)) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
best = { prefix, suffix, target: entry[1] };
|
|
185
|
+
}
|
|
186
|
+
if (best === void 0) return void 0;
|
|
187
|
+
const matched = subpath.slice(best.prefix.length, subpath.length - best.suffix.length);
|
|
188
|
+
const target = selectConditionTarget(best.target, conditions);
|
|
189
|
+
return target === void 0 ? void 0 : target.split("*").join(matched);
|
|
190
|
+
}
|
|
191
|
+
function exportsSubpathOf(specifier, packageName) {
|
|
192
|
+
return specifier === packageName ? "." : `.${specifier.slice(packageName.length)}`;
|
|
193
|
+
}
|
|
194
|
+
function packageRootOf(resolvedFile, packageName) {
|
|
195
|
+
let dir = (0, import_node_path.dirname)(resolvedFile);
|
|
196
|
+
for (let hop = 0; hop < 64; hop += 1) {
|
|
197
|
+
try {
|
|
198
|
+
const manifest = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(dir, "package.json"), "utf8"));
|
|
199
|
+
if (manifest.name === packageName) return dir;
|
|
200
|
+
} catch {
|
|
201
|
+
}
|
|
202
|
+
const parent = (0, import_node_path.dirname)(dir);
|
|
203
|
+
if (parent === dir) return void 0;
|
|
204
|
+
dir = parent;
|
|
205
|
+
}
|
|
206
|
+
return void 0;
|
|
207
|
+
}
|
|
208
|
+
function esmEntryForDeclared(specifier, packageName, cjsResolved) {
|
|
209
|
+
const root = packageRootOf(cjsResolved, packageName);
|
|
210
|
+
if (root === void 0) return void 0;
|
|
211
|
+
let exportsField;
|
|
212
|
+
try {
|
|
213
|
+
exportsField = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(root, "package.json"), "utf8")).exports;
|
|
214
|
+
} catch {
|
|
215
|
+
return void 0;
|
|
216
|
+
}
|
|
217
|
+
if (exportsField === void 0 || exportsField === null) return void 0;
|
|
218
|
+
const subpath = exportsSubpathOf(specifier, packageName);
|
|
219
|
+
const target = resolveExportsSubpath(exportsField, subpath);
|
|
220
|
+
if (typeof target !== "string" || target.indexOf("./") !== 0) return void 0;
|
|
221
|
+
const entry = (0, import_node_path.resolve)(root, target);
|
|
222
|
+
if (entry.indexOf(root + import_node_path.sep) !== 0) return void 0;
|
|
223
|
+
return (0, import_node_fs.existsSync)(entry) ? entry : void 0;
|
|
224
|
+
}
|
|
225
|
+
function hasInvalidExportsSubpathSegments(subpath) {
|
|
226
|
+
if (subpath === ".") return false;
|
|
227
|
+
return subpath.slice(2).split(/[/\\]/).some((raw) => {
|
|
228
|
+
const segment = raw.toLowerCase();
|
|
229
|
+
return segment === "" || segment === "." || segment === ".." || segment === "node_modules";
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
var ALIAS_DECLARATION_PROTOCOLS = [
|
|
233
|
+
{ prefix: "npm:", rangeRequired: false },
|
|
234
|
+
{ prefix: "workspace:", rangeRequired: true }
|
|
235
|
+
];
|
|
236
|
+
function declaredManifestName(declaration) {
|
|
237
|
+
const { packageName, specifier } = declaration;
|
|
238
|
+
if (specifier === void 0) return packageName;
|
|
239
|
+
const protocol = ALIAS_DECLARATION_PROTOCOLS.find((p) => specifier.indexOf(p.prefix) === 0);
|
|
240
|
+
if (protocol === void 0) return packageName;
|
|
241
|
+
const value = specifier.slice(protocol.prefix.length);
|
|
242
|
+
const at = value.lastIndexOf("@");
|
|
243
|
+
if (at <= 0 && protocol.rangeRequired) return packageName;
|
|
244
|
+
const name = at > 0 ? value.slice(0, at) : value;
|
|
245
|
+
return packageNameFromSpecifier(name) === name ? name : packageName;
|
|
246
|
+
}
|
|
247
|
+
function hostInstalledPackageDir(declaration) {
|
|
248
|
+
const { packageName, hostRoot } = declaration;
|
|
249
|
+
const linked = (0, import_node_path.join)(hostRoot, "node_modules", ...packageName.split("/"));
|
|
250
|
+
try {
|
|
251
|
+
const manifest = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(linked, "package.json"), "utf8"));
|
|
252
|
+
if (manifest.name !== declaredManifestName(declaration)) return void 0;
|
|
253
|
+
} catch {
|
|
254
|
+
return void 0;
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
return (0, import_node_fs.realpathSync)(linked);
|
|
258
|
+
} catch {
|
|
259
|
+
return linked;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function declaredCjsResolveFallback(specifier, declaration) {
|
|
263
|
+
const { packageName } = declaration;
|
|
264
|
+
const packageDir = hostInstalledPackageDir(declaration);
|
|
265
|
+
if (packageDir === void 0) return { outcome: "absent" };
|
|
266
|
+
let exportsField;
|
|
267
|
+
try {
|
|
268
|
+
exportsField = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(packageDir, "package.json"), "utf8")).exports;
|
|
269
|
+
} catch {
|
|
270
|
+
return { outcome: "absent" };
|
|
271
|
+
}
|
|
272
|
+
if (exportsField === void 0 || exportsField === null) return { outcome: "install-broken" };
|
|
273
|
+
const subpath = exportsSubpathOf(specifier, packageName);
|
|
274
|
+
if (hasInvalidExportsSubpathSegments(subpath)) return { outcome: "invalid-specifier" };
|
|
275
|
+
const importTarget = resolveExportsSubpath(exportsField, subpath, ESM_IMPORT_CONDITIONS);
|
|
276
|
+
if (typeof importTarget === "string" && importTarget.indexOf("./") === 0) {
|
|
277
|
+
const entry = (0, import_node_path.resolve)(packageDir, importTarget);
|
|
278
|
+
if (entry.indexOf(packageDir + import_node_path.sep) === 0 && (0, import_node_fs.existsSync)(entry)) {
|
|
279
|
+
return { outcome: "entry", entry };
|
|
280
|
+
}
|
|
281
|
+
return { outcome: "install-broken" };
|
|
282
|
+
}
|
|
283
|
+
const requireTarget = resolveExportsSubpath(exportsField, subpath, CJS_REQUIRE_CONDITIONS);
|
|
284
|
+
if (typeof requireTarget === "string" && requireTarget.indexOf("./") === 0) {
|
|
285
|
+
return { outcome: "install-broken" };
|
|
286
|
+
}
|
|
287
|
+
return { outcome: "no-loadable-entry", packageDir };
|
|
288
|
+
}
|
|
289
|
+
function noLoadableEntryMessage(declaration, packageDir, subpath, cause) {
|
|
290
|
+
const { packageName, hostRoot, field, specifier } = declaration;
|
|
291
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
292
|
+
const subpathNote = subpath === "." ? 'its main entry (".")' : `the subpath '${subpath}'`;
|
|
293
|
+
return `Cannot load module '${packageName}': the host app DECLARES it (${field}: ${JSON.stringify(specifier)}) and it IS installed, but the package publishes no entry that Node can load.
|
|
294
|
+
host app: ${hostRoot}
|
|
295
|
+
installed at: ${packageDir}
|
|
296
|
+
|
|
297
|
+
This is a problem with the PACKAGE's own published shape, not with the app or
|
|
298
|
+
its install \u2014 the declaration is right and the package is on disk, so neither
|
|
299
|
+
re-reading package.json nor re-running \`pnpm install\` can change anything.
|
|
300
|
+
Measured from its manifest:
|
|
301
|
+
\u2022 its "exports" map names no \`require\`-condition entry for ${subpathNote},
|
|
302
|
+
so a CommonJS resolution cannot see it at all
|
|
303
|
+
\u2022 and no \`import\`-condition entry either, so there is nothing for the ESM
|
|
304
|
+
fallback to load
|
|
305
|
+
The remedy lives in the package: it must publish a runtime entry for this
|
|
306
|
+
subpath (an \`import\` condition suffices here; a dual build adds \`require\`).
|
|
307
|
+
A publish carrying only \`types\` / \`browser\`-style conditions cannot be loaded
|
|
308
|
+
by a Node host at all.
|
|
309
|
+
(resolver: ${detail})`;
|
|
310
|
+
}
|
|
311
|
+
function createHostImporter(hostRoot = process.cwd(), options = {}) {
|
|
132
312
|
const hostRequire = createHostRequire(hostRoot);
|
|
313
|
+
const { fallbackImport } = options;
|
|
314
|
+
const importAsCaller = fallbackImport ?? ((specifier) => import(
|
|
315
|
+
/* webpackIgnore: true */
|
|
316
|
+
specifier
|
|
317
|
+
));
|
|
133
318
|
return async (pkg) => {
|
|
134
319
|
if (packageNameFromSpecifier(pkg) === void 0) {
|
|
135
320
|
return import(
|
|
@@ -143,22 +328,40 @@ function createHostImporter(hostRoot = process.cwd()) {
|
|
|
143
328
|
try {
|
|
144
329
|
resolved = hostRequire.resolve(pkg);
|
|
145
330
|
} catch (cause) {
|
|
331
|
+
const fallback = declaredCjsResolveFallback(pkg, declaration);
|
|
332
|
+
if (fallback.outcome === "entry") {
|
|
333
|
+
return import((0, import_node_url.pathToFileURL)(fallback.entry).href);
|
|
334
|
+
}
|
|
335
|
+
if (fallback.outcome === "no-loadable-entry") {
|
|
336
|
+
throw hostImportError(
|
|
337
|
+
"declared-no-loadable-entry",
|
|
338
|
+
noLoadableEntryMessage(
|
|
339
|
+
declaration,
|
|
340
|
+
fallback.packageDir,
|
|
341
|
+
exportsSubpathOf(pkg, declaration.packageName),
|
|
342
|
+
cause
|
|
343
|
+
),
|
|
344
|
+
cause
|
|
345
|
+
);
|
|
346
|
+
}
|
|
146
347
|
throw hostImportError(
|
|
147
348
|
"declared-unresolvable",
|
|
148
349
|
unresolvableMessage(declaration, cause),
|
|
149
350
|
cause
|
|
150
351
|
);
|
|
151
352
|
}
|
|
152
|
-
|
|
353
|
+
const entry = esmEntryForDeclared(pkg, declaration.packageName, resolved) ?? resolved;
|
|
354
|
+
return import((0, import_node_url.pathToFileURL)(entry).href);
|
|
153
355
|
}
|
|
154
356
|
try {
|
|
155
|
-
return await
|
|
156
|
-
/* webpackIgnore: true */
|
|
157
|
-
pkg
|
|
158
|
-
);
|
|
357
|
+
return await importAsCaller(pkg);
|
|
159
358
|
} catch (cause) {
|
|
160
359
|
if (!isModuleNotFoundError(cause)) throw cause;
|
|
161
|
-
throw hostImportError(
|
|
360
|
+
throw hostImportError(
|
|
361
|
+
"undeclared",
|
|
362
|
+
undeclaredMessage(declaration, cause, fallbackImport !== void 0),
|
|
363
|
+
cause
|
|
364
|
+
);
|
|
162
365
|
}
|
|
163
366
|
};
|
|
164
367
|
}
|