Skip to content

Commit

Permalink
fix(vue): useId() collisions (#12112)
Browse files Browse the repository at this point in the history
  • Loading branch information
florian-lefebvre authored Oct 3, 2024
1 parent 34d7952 commit f9dd942
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/beige-students-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/vue': patch
---

Fixes a case where IDs generated by `useId()` (introduced in Vue 3.5) would not be unique between islands
1 change: 1 addition & 0 deletions packages/integrations/vue/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default (element) =>
return content;
},
});
app.config.idPrefix = element.getAttribute('prefix');
await setup(app);
app.mount(element, isHydrate);
appMap.set(element, appInstance);
Expand Down
24 changes: 24 additions & 0 deletions packages/integrations/vue/context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const contexts = new WeakMap();

const ID_PREFIX = 'v';

function getContext(rendererContextResult) {
if (contexts.has(rendererContextResult)) {
return contexts.get(rendererContextResult);
}
const ctx = {
currentIndex: 0,
get id() {
return ID_PREFIX + this.currentIndex.toString();
},
};
contexts.set(rendererContextResult, ctx);
return ctx;
}

export function incrementId(rendererContextResult) {
const ctx = getContext(rendererContextResult);
const id = ctx.id;
ctx.currentIndex++;
return id;
}
10 changes: 9 additions & 1 deletion packages/integrations/vue/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ import { setup } from 'virtual:@astrojs/vue/app';
import { createSSRApp, h } from 'vue';
import { renderToString } from 'vue/server-renderer';
import StaticHtml from './static-html.js';
import { incrementId } from './context.js';

function check(Component) {
return !!Component['ssrRender'] || !!Component['__ssrInlineRender'];
}

async function renderToStaticMarkup(Component, inputProps, slotted, metadata) {
let prefix;
if (this && this.result) {
prefix = incrementId(this.result);
}
const attrs = { prefix };

const slots = {};
const props = { ...inputProps };
delete props.slot;
Expand All @@ -21,9 +28,10 @@ async function renderToStaticMarkup(Component, inputProps, slotted, metadata) {
});
}
const app = createSSRApp({ render: () => h(Component, props, slots) });
app.config.idPrefix = prefix;
await setup(app);
const html = await renderToString(app);
return { html };
return { html, attrs };
}

export default {
Expand Down
9 changes: 9 additions & 0 deletions packages/integrations/vue/test/basics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,13 @@ describe('Basics', () => {
assert.notEqual(img, undefined);
assert.equal(img.getAttribute('src'), '/light_walrus.avif');
});

it('Should generate unique ids when using useId()', async () => {
const data = await fixture.readFile('/index.html');
const { document } = parseHTML(data);

const els = document.querySelectorAll('.vue-use-id');
assert.equal(els.length, 2);
assert.notEqual(els[0].getAttribute('id'), els[1].getAttribute('id'));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script setup>
import { useId } from "vue"
const id = useId()
</script>

<template>
<p class="vue-use-id" :id="id">{{ id }}</p>
</template>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
import Bar from '../components/Foo.vue';
import Parent from '../components/Parent.astro';
import WithId from '../components/WithId.vue';
---
<html>
<head>
Expand All @@ -10,5 +11,7 @@ import Parent from '../components/Parent.astro';
<Parent>
<Bar slot="footer" />
</Parent>
<WithId client:idle />
<WithId client:idle />
</body>
</html>

0 comments on commit f9dd942

Please sign in to comment.