From 0d77a1c23f04a4091a04c7d65b08de8079456d64 Mon Sep 17 00:00:00 2001 From: Romain Lanz Date: Mon, 12 Oct 2026 11:42:03 +0000 Subject: [PATCH] fix: keep superCharged components per edge instance and after configure The superCharged plugin kept its state in one module variable. A second Edge instance replaced it, and the first instance then refreshed the components of the second one. Its own components list never changed again. configure() also created new compilers without the tag claimed by superCharged. The plugin runs once, so after a configure() call that came after the first render, component tags such as @!foo() were no longer converted. The plugin now keeps one state per Edge instance in a WeakMap. configure() copies the claimTag function of the previous compilers to the new ones. The Compiler class gets a claimTagFn getter for this. --- src/compiler.ts | 7 +++++++ src/edge/main.ts | 14 ++++++++++++++ src/plugins/supercharged.ts | 6 +++--- tests/edge.spec.ts | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 534c4ef..01630e8 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -248,6 +248,13 @@ export class Compiler { return new Function(...args, template) as CompiledTemplate } + /** + * The function registered using "claimTag", if any + */ + get claimTagFn(): ClaimTagFn | undefined { + return this.#claimTagFn + } + /** * Define a function to claim tags */ diff --git a/src/edge/main.ts b/src/edge/main.ts index facd557..4be211d 100644 --- a/src/edge/main.ts +++ b/src/edge/main.ts @@ -123,6 +123,9 @@ export class Edge { this.loader = new Loader() } + const previousCompiler = this.compiler + const previousAsyncCompiler = this.asyncCompiler + this.compiler = new Compiler(this.loader, this.tags, this.processor, { cache: !!options.cache, async: false, @@ -132,6 +135,17 @@ export class Edge { cache: !!options.cache, async: true, }) + + /** + * Keep the tags claimed on the previous compilers. Plugins run + * once and do not claim them again on the new compilers + */ + if (previousCompiler?.claimTagFn) { + this.compiler.claimTag(previousCompiler.claimTagFn) + } + if (previousAsyncCompiler?.claimTagFn) { + this.asyncCompiler.claimTag(previousAsyncCompiler.claimTagFn) + } } /** diff --git a/src/plugins/supercharged.ts b/src/plugins/supercharged.ts index 8734b91..7e908e8 100644 --- a/src/plugins/supercharged.ts +++ b/src/plugins/supercharged.ts @@ -85,10 +85,10 @@ class SuperChargedComponents { * The superCharged plugin converts components stored within the * components directory of all the disk to Edge tags. */ -let superCharged: SuperChargedComponents +const instances = new WeakMap() export const pluginSuperCharged: PluginFn<{ recurring: boolean }> = (edge, firstRun) => { if (firstRun) { - superCharged = new SuperChargedComponents(edge) + instances.set(edge, new SuperChargedComponents(edge)) } - superCharged.refreshComponents() + instances.get(edge)!.refreshComponents() } diff --git a/tests/edge.spec.ts b/tests/edge.spec.ts index b949272..02691bf 100644 --- a/tests/edge.spec.ts +++ b/tests/edge.spec.ts @@ -399,6 +399,41 @@ test.group('Edge', () => { assert.equal(output.trim(), 'Hello virk') }) + test('refresh components list of each edge instance', async ({ assert, fs }) => { + const edgeA = new Edge() + edgeA.mount(join(fs.basePath, 'a')) + await fs.create('a/foo.edge', '@!foo({ username })') + await fs.create('a/components/foo.edge', 'Hello {{ username }}') + await fs.create('a/bar.edge', '@!bar({ username })') + + const edgeB = new Edge() + edgeB.mount(join(fs.basePath, 'b')) + await fs.create('b/foo.edge', '@!foo({ username })') + + const output1 = await edgeA.render('foo', { username: 'virk' }) + assert.equal(output1.trim(), 'Hello virk') + const output2 = await edgeB.render('foo', { username: 'virk' }) + assert.equal(output2.trim(), '@!foo({ username })') + + await fs.create('a/components/bar.edge', 'Bye {{ username }}') + const output3 = await edgeA.render('bar', { username: 'virk' }) + assert.equal(output3.trim(), 'Bye virk') + }) + + test('render components as tags after configure', async ({ assert, fs }) => { + const edge = Edge.create() + await fs.create('foo.edge', '@!foo({ username })') + await fs.create('components/foo.edge', 'Hello {{ username }}') + edge.mount(fs.basePath) + + const output = await edge.render('foo', { username: 'virk' }) + assert.equal(output.trim(), 'Hello virk') + + edge.configure({ cache: true }) + const output1 = await edge.render('foo', { username: 'virk' }) + assert.equal(output1.trim(), 'Hello virk') + }) + test('refresh components list on each render', async ({ assert, fs }) => { const edge = new Edge() await fs.create('foo.edge', '@!foo({ username })')