fix(plugin): export plugin-open before its entry block
c017f6d made /ccam-open call bootstrap() when the live server belongs to a
retired plugin root, which closed a require cycle that had never been closed
before: bootstrap() lazily requires plugin-open back for buildClient, and when
plugin-open is itself the entry point that require resolves to whatever sits on
its module.exports at that instant. The assignment was the last statement in the
file, below the entry block, so it was still empty and /ccam-open died with
"TypeError: buildClient is not a function" before building anything.
Move the assignment above the entry block. The test asserts that ordering
directly — it fails against the previous layout — alongside a check that the
module a re-entrant bootstrap sees really does carry buildClient.
This commit is contained in:
@@ -76,6 +76,11 @@ function dashboardUrl() {
|
||||
}
|
||||
}
|
||||
|
||||
// Exported before the entry block below, not after it: `bootstrap()` requires
|
||||
// this module back for `buildClient`, and when plugin-open is itself the entry
|
||||
// that lazy require resolves to whatever is on module.exports at that moment.
|
||||
module.exports = { buildClient, dashboardUrl };
|
||||
|
||||
if (require.main === module) {
|
||||
// A plugin update applied mid-session (/plugin + /reload-plugins) fires no
|
||||
// SessionStart, so the server keeps executing a cache dir Claude Code has
|
||||
@@ -95,5 +100,3 @@ if (require.main === module) {
|
||||
console.log(dashboardUrl());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { buildClient, dashboardUrl };
|
||||
|
||||
@@ -271,3 +271,26 @@ describe("ccam CLI launcher", () => {
|
||||
assert.match(fs.readFileSync(target, "utf8"), /my\/checkout/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("plugin-open / plugin-bootstrap require cycle", () => {
|
||||
// bootstrap() lazily requires plugin-open for buildClient, and plugin-open
|
||||
// requires bootstrap at load. When plugin-open is the entry point (/ccam-open)
|
||||
// that lazy require resolves to whatever sits on its module.exports at that
|
||||
// instant — so the assignment must come before the entry block that calls
|
||||
// bootstrap(), or /ccam-open dies with "buildClient is not a function".
|
||||
const src = fs.readFileSync(
|
||||
path.join(__dirname, "..", "..", "scripts", "plugin-open.js"),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
it("assigns module.exports before running the entry block", () => {
|
||||
const exportsAt = src.indexOf("module.exports =");
|
||||
const entryAt = src.indexOf("require.main === module");
|
||||
assert.ok(exportsAt !== -1 && entryAt !== -1);
|
||||
assert.ok(exportsAt < entryAt, "module.exports must be assigned before the entry block");
|
||||
});
|
||||
|
||||
it("exposes buildClient to a bootstrap that requires it back", () => {
|
||||
assert.equal(typeof require("../../scripts/plugin-open").buildClient, "function");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user