Skip to content

Commit c792b6e

Browse files
committed
fix(core): DSD injection regex now handles slashes inside attribute values
Bug: <auth-forms then=\"/dashboard\"> rendered as an EMPTY custom element with no shadow root — the sign-in and sign-up pages appeared blank. Root cause: the DSD-injection regex in render-server.js used `[^>/]*` to bound the attribute section, which stopped matching the instant it saw \`/\` inside an attribute value. The whole opening tag then failed to match the pattern, so injectDSD silently skipped the tag and the server sent the bare \`<auth-forms>\` (which has no light-DOM content) to the browser. Fix: attribute section is now \`(?:\"[^\"]*\"|'[^']*'|[^>])*?\` — quoted values are matched as a single unit (so slashes inside them are fine), anything-but-\`>\` outside quotes. Non-greedy so self-closing \`/>\` still lands in the selfClose capture group. + 1 regression test: component whose attr value is a URL path renders with DSD (without the fix, this test would show DSD absent). Verified end-to-end: /login now renders the tabbed login/signup form server-side.
1 parent a4d5c8c commit c792b6e

2 files changed

Lines changed: 17 additions & 1 deletion

File tree

packages/core/src/render-server.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,12 @@ async function renderTemplate(tr, ctx) {
220220
async function injectDSD(html, ctx) {
221221
const tags = allTags();
222222
if (!tags.length) return html;
223+
// Attribute section is "anything that isn't `>`, with quoted values as a
224+
// single unit" so slashes in URL-valued attrs (e.g. then="/dashboard") don't
225+
// prevent the match. Non-greedy so self-closing `/>` still captures into the
226+
// third group.
223227
const pattern = new RegExp(
224-
`<(${tags.map(escapeRegex).join('|')})((?:\\s+[^>/]*)?)(/?)>`,
228+
`<(${tags.map(escapeRegex).join('|')})((?:"[^"]*"|'[^']*'|[^>])*?)(/?)>`,
225229
'g'
226230
);
227231
/** @type {{start:number, end:number, text:string}[]} */

test/render-server.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ test('awaits async template (page-style)', async () => {
5555
assert.equal(await renderToString(page), '<p>data</p>');
5656
});
5757

58+
test('DSD injection handles attribute values containing slashes', async () => {
59+
class SlashTag extends WebComponent {
60+
static tag = 'slash-tag';
61+
static properties = { href: { type: String } };
62+
render() { return html`<a href=${this.href}>x</a>`; }
63+
}
64+
SlashTag.register();
65+
const out = await renderToString(html`<slash-tag href="/some/path"></slash-tag>`);
66+
// The opening tag must have DSD injected even though the attribute has /.
67+
assert.match(out, /<slash-tag href="\/some\/path"><template shadowrootmode="open">/);
68+
});
69+
5870
test('custom element injects declarative shadow DOM', async () => {
5971
class Greet extends WebComponent {
6072
static tag = 'g-reet';

0 commit comments

Comments
 (0)