Follow symlinked plugin folders, and keep a plugin .npmrc out of the image
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { test, type TestContext } from "node:test";
|
||||
@@ -127,6 +127,23 @@ test("a plugin may carry its own package.json, node_modules and dependencies", a
|
||||
assert.deepEqual(await plugins[0]?.routes?.[0]?.handler(null as never), { html: "20 kr" });
|
||||
});
|
||||
|
||||
test("a plugin folder may be a symlink — a plugin kept in its own repo", async (t) => {
|
||||
const ownRepo = scaffold(t, { "my-plugin/plugin.ts": full("my-plugin") });
|
||||
const dir = scaffold(t, {});
|
||||
symlinkSync(join(ownRepo, "my-plugin"), join(dir, "linked"));
|
||||
|
||||
const plugins = await discoverPlugins({ dir });
|
||||
|
||||
assert.deepEqual(plugins.map((p) => p.id), ["linked"]); // the link name is the id, not the target's
|
||||
});
|
||||
|
||||
test("a dangling plugin symlink fails loud rather than vanishing", async (t) => {
|
||||
const dir = scaffold(t, {});
|
||||
symlinkSync(join(dir, "gone"), join(dir, "broken"));
|
||||
|
||||
await assert.rejects(discoverPlugins({ dir }), /broken.*plugin\.ts/s);
|
||||
});
|
||||
|
||||
test("a shared permission name only warns — both plugins still load", async (t) => {
|
||||
const shared = `export default { apiVersion: "1.0.0", permissions: [{ name: "shared:read" }] };`;
|
||||
const dir = scaffold(t, { "x/plugin.ts": shared, "y/plugin.ts": shared });
|
||||
|
||||
@@ -84,11 +84,11 @@ export async function discoverPlugins(options: DiscoverOptions = {}): Promise<Pl
|
||||
}
|
||||
|
||||
// Subfolders of plugins/, sorted for deterministic load order + stable conflict messages. Hidden
|
||||
// entries (.git, .DS_Store, …) and non-directories are skipped — only folders are plugins. So is
|
||||
// node_modules, which npm leaves here when a dependency install is pointed at plugins/ itself.
|
||||
// entries, plain files and node_modules are skipped; a symlink counts, so a plugin kept in its own
|
||||
// repo joins the tree with `ln -s`, and a dangling one trips "no plugin.ts found" rather than vanishing.
|
||||
function pluginFolders(dir: string): string[] {
|
||||
return readdirSync(dir, { withFileTypes: true })
|
||||
.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules")
|
||||
.filter((e) => (e.isDirectory() || e.isSymbolicLink()) && !e.name.startsWith(".") && e.name !== "node_modules")
|
||||
.map((e) => e.name)
|
||||
.sort();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user