dropsh 0.3.3 → 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.
@@ -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(resolve(filePath)).href);
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 = Array.isArray(raw.plugins) ? raw.plugins : [];
87
+ const plugins = await resolvePlugins(raw.plugins, configPath);
25
88
  return {
26
89
  site: {
27
90
  base_url: site.base_url,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dropsh",
3
- "version": "0.3.3",
3
+ "version": "0.4.1",
4
4
  "description": "Entity-agnostic helper CLI for Drupal 11 JSON:API, used by the dropsh Claude skill for editorial publishing workflows.",
5
5
  "type": "module",
6
6
  "bin": {