dropsh 0.3.1 → 0.4.1
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/src/commands/auth.js +7 -1
- package/dist/src/core/config.js +65 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { loginCapableProviders, providerById } from "../core/auth/registry.js";
|
|
1
|
+
import { defaultProvider, loginCapableProviders, providerById } from "../core/auth/registry.js";
|
|
2
2
|
import { clearAll, clearProfile, readProfiles, setActive, writeProfile, } from "../core/auth/session-store.js";
|
|
3
3
|
import { AuthError, ConfigError } from "../errors.js";
|
|
4
4
|
function authContext(deps) {
|
|
@@ -24,6 +24,12 @@ async function pickProvider(deps, requestedId) {
|
|
|
24
24
|
throw new ConfigError(`unknown provider '${requestedId}'. Available: ${capable.map((p) => p.id).join(", ")}`);
|
|
25
25
|
return found;
|
|
26
26
|
}
|
|
27
|
+
// No explicit --provider: fall back to the configured default profile
|
|
28
|
+
// (default: true), or the sole provider. Only then, if still ambiguous and
|
|
29
|
+
// interactive, prompt; non-interactive with no default stays an error.
|
|
30
|
+
const fallback = defaultProvider(capable) ?? (capable.length === 1 ? capable[0] : undefined);
|
|
31
|
+
if (fallback)
|
|
32
|
+
return fallback;
|
|
27
33
|
if (!deps.isTTY)
|
|
28
34
|
throw new ConfigError("non-interactive: pass --provider <id>");
|
|
29
35
|
deps.stdout("Select an auth provider:\n");
|
package/dist/src/core/config.js
CHANGED
|
@@ -1,13 +1,76 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import { resolve } from "node:path";
|
|
2
3
|
import { pathToFileURL } from "node:url";
|
|
3
4
|
import { ConfigError } from "../errors.js";
|
|
4
5
|
function isRecord(v) {
|
|
5
6
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
6
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* A plugin descriptor is plain config data that names a plugin package as a
|
|
10
|
+
* string (`plugin`). This lets a committed config stay import-free: the config
|
|
11
|
+
* carries only data, and dropsh resolves + constructs the plugin from its own
|
|
12
|
+
* install — so nothing has to resolve the plugin package from the config file's
|
|
13
|
+
* own `import` statements (which resolve relative to the config file, not the CLI).
|
|
14
|
+
*/
|
|
15
|
+
function isPluginDescriptor(e) {
|
|
16
|
+
return isRecord(e) && typeof e.plugin === "string";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a named plugin package the way ESLint resolves plugins: by string
|
|
20
|
+
* name, relative to the config file first, then the cwd, then dropsh's own
|
|
21
|
+
* install (so a plugin installed alongside dropsh — a dropsh dep, or globally
|
|
22
|
+
* next to a global dropsh, or via `npx -p`— is found). The config never imports
|
|
23
|
+
* the package itself.
|
|
24
|
+
*/
|
|
25
|
+
async function loadNamedPlugin(entry, configPath) {
|
|
26
|
+
const name = entry.plugin;
|
|
27
|
+
const bases = [
|
|
28
|
+
pathToFileURL(configPath).href,
|
|
29
|
+
pathToFileURL(`${process.cwd()}/`).href,
|
|
30
|
+
import.meta.url,
|
|
31
|
+
];
|
|
32
|
+
let resolved;
|
|
33
|
+
for (const base of bases) {
|
|
34
|
+
try {
|
|
35
|
+
resolved = createRequire(base).resolve(name);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// try the next base
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (resolved === undefined)
|
|
43
|
+
throw new ConfigError(`cannot resolve plugin '${name}'. Install it next to dropsh (project dep, dropsh dep, global next to a global dropsh, or 'npx -p dropsh -p ${name}').`);
|
|
44
|
+
let mod;
|
|
45
|
+
try {
|
|
46
|
+
mod = (await import(pathToFileURL(resolved).href));
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
throw new ConfigError(`cannot load plugin '${name}'`, { cause: String(err) });
|
|
50
|
+
}
|
|
51
|
+
const exportName = typeof entry.export === "string" ? entry.export : "default";
|
|
52
|
+
const factory = mod[exportName];
|
|
53
|
+
if (typeof factory !== "function")
|
|
54
|
+
throw new ConfigError(`plugin '${name}' has no callable export '${exportName}'`);
|
|
55
|
+
return factory(entry.with ?? entry.options);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Expands `plugins[]` entries: named-package descriptors are resolved + built
|
|
59
|
+
* into real plugins; already-constructed plugins pass through unchanged
|
|
60
|
+
* (backward compat).
|
|
61
|
+
*/
|
|
62
|
+
async function resolvePlugins(raw, configPath) {
|
|
63
|
+
if (!Array.isArray(raw))
|
|
64
|
+
return [];
|
|
65
|
+
return Promise.all(raw.map((entry) => isPluginDescriptor(entry)
|
|
66
|
+
? loadNamedPlugin(entry, configPath)
|
|
67
|
+
: Promise.resolve(entry)));
|
|
68
|
+
}
|
|
7
69
|
export async function loadConfig(filePath) {
|
|
70
|
+
const configPath = resolve(filePath);
|
|
8
71
|
let mod;
|
|
9
72
|
try {
|
|
10
|
-
mod = await import(pathToFileURL(
|
|
73
|
+
mod = await import(pathToFileURL(configPath).href);
|
|
11
74
|
}
|
|
12
75
|
catch (err) {
|
|
13
76
|
throw new ConfigError(`Cannot load config file: ${filePath}`, { cause: String(err) });
|
|
@@ -21,7 +84,7 @@ export async function loadConfig(filePath) {
|
|
|
21
84
|
if (typeof site.base_url !== "string" || site.base_url.length === 0)
|
|
22
85
|
throw new ConfigError("site.base_url required");
|
|
23
86
|
const defaults = isRecord(raw.defaults) ? raw.defaults : {};
|
|
24
|
-
const plugins =
|
|
87
|
+
const plugins = await resolvePlugins(raw.plugins, configPath);
|
|
25
88
|
return {
|
|
26
89
|
site: {
|
|
27
90
|
base_url: site.base_url,
|