(
candidate: Candidate,
kind: PluginKind,
retry: boolean,
finish: ((load: Loaded, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined,
missing: ((value: Missing, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined,
report: Report | undefined,
)
| 147 | // Run one candidate through the full pipeline: resolve, optionally surface a missing entry, |
| 148 | // import the module, and finally let the caller transform the loaded plugin into any result type. |
| 149 | async function attempt<R>( |
| 150 | candidate: Candidate, |
| 151 | kind: PluginKind, |
| 152 | retry: boolean, |
| 153 | finish: ((load: Loaded, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined, |
| 154 | missing: ((value: Missing, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined, |
| 155 | report: Report | undefined, |
| 156 | ): Promise<AttemptResult<R>> { |
| 157 | const plan = candidate.plan |
| 158 | const filePlugin = pluginSource(plan.spec) === "file" |
| 159 | |
| 160 | // Deprecated plugin packages are silently ignored because they are now built in. |
| 161 | if (plan.deprecated) return { retry: false } |
| 162 | |
| 163 | report?.start?.(candidate, retry) |
| 164 | |
| 165 | const resolved = await resolve(plan, kind) |
| 166 | if (!resolved.ok) { |
| 167 | if (resolved.stage === "missing") { |
| 168 | // Missing entrypoints are handled separately so callers can still inspect package metadata, |
| 169 | // for example to load theme files from a tui plugin package that has no code entrypoint. |
| 170 | if (missing) { |
| 171 | const value = await missing(resolved.value, candidate.origin, retry) |
| 172 | if (value !== undefined) return { value, retry: false } |
| 173 | } |
| 174 | report?.missing?.(candidate, retry, resolved.value.message, resolved.value) |
| 175 | return { retry: false } |
| 176 | } |
| 177 | report?.error?.(candidate, retry, resolved.stage, resolved.error) |
| 178 | return { retry: filePlugin && isRetryableResolveError(resolved.stage, resolved.error) } |
| 179 | } |
| 180 | |
| 181 | const loaded = await load(resolved.value) |
| 182 | if (!loaded.ok) { |
| 183 | report?.error?.(candidate, retry, "load", loaded.error, resolved.value) |
| 184 | return { retry: false } |
| 185 | } |
| 186 | |
| 187 | // The default behavior is to return the successfully loaded plugin as-is, but callers can |
| 188 | // provide a finisher to adapt the result into a more specific runtime shape. |
| 189 | if (!finish) return { value: loaded.value as R, retry: false } |
| 190 | const value = await finish(loaded.value, candidate.origin, retry) |
| 191 | return { value, retry: false } |
| 192 | } |
| 193 | |
| 194 | type Input<R> = { |
| 195 | items: ConfigPlugin.Origin[] |
no test coverage detected