From 2307f88e09d57808ef6616e9fed667551f420558 Mon Sep 17 00:00:00 2001 From: ZainnQureshii <43629888+ZainnQureshii@users.noreply.github.com> Date: Tue, 10 Nov 2026 18:18:09 +0100 Subject: [PATCH 1/2] fix(remark-snackplayer): avoid double-decoding metadata parseParams already decodes each SnackPlayer meta value, and the plugin decoded name, description and ext a second time, so an encoded % sequence in a value (such as %2520) was turned into a different character. Use the parsed values as they are. Co-Authored-By: Claude Opus 5.5 --- plugins/remark-snackplayer/src/index.ts | 8 ++-- plugins/remark-snackplayer/tests/index.ts | 48 +++++++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/plugins/remark-snackplayer/src/index.ts b/plugins/remark-snackplayer/src/index.ts index ac404875cf5..aa0e917a58d 100644 --- a/plugins/remark-snackplayer/src/index.ts +++ b/plugins/remark-snackplayer/src/index.ts @@ -33,11 +33,9 @@ async function toJsxNode(node: Code) { const params = parseParams(node.meta ?? undefined); // Gather necessary Params - const name = params.name ? decodeURIComponent(params.name) : 'Example'; - const description = params.description - ? decodeURIComponent(params.description) - : 'Example usage'; - const ext = params.ext ? decodeURIComponent(params.ext) : 'tsx'; + const name = params.name || 'Example'; + const description = params.description || 'Example usage'; + const ext = params.ext || 'tsx'; const filename = `App.${ext}`; const files = encodeURIComponent( JSON.stringify({ diff --git a/plugins/remark-snackplayer/tests/index.ts b/plugins/remark-snackplayer/tests/index.ts index 5522c3f2726..2772dd71fb5 100644 --- a/plugins/remark-snackplayer/tests/index.ts +++ b/plugins/remark-snackplayer/tests/index.ts @@ -44,3 +44,51 @@ test('remark-snackplayer', async t => { t.end(); }); + +test('decodes SnackPlayer metadata once', async t => { + const processor = remark().use(remarkMdx).use(SnackPlayer); + for (const [param, attribute] of [ + ['name', 'data-snack-name'], + ['description', 'data-snack-description'], + ]) { + for (const value of ['100% ready', 'literal %20 text']) { + const input = `\`\`\`SnackPlayer ${param}=${encodeURIComponent(value)} +export default function App() { return null; } +\`\`\``; + try { + const file = await processor.process(input); + t.ok( + String(file).includes(`${attribute}="${value}"`), + `generated ${attribute} preserves ${value}` + ); + } catch (error) { + t.fail(`${param} rejected ${value}: ${String(error)}`); + } + } + } + t.end(); +}); + +test('decodes SnackPlayer filename metadata once', async t => { + const processor = remark().use(remarkMdx).use(SnackPlayer); + for (const ext of ['100% ready', 'literal %20 text']) { + const input = `\`\`\`SnackPlayer ext=${encodeURIComponent(ext)} +export default function App() { return null; } +\`\`\``; + try { + const file = await processor.process(input); + const files = String(file).match(/data-snack-files="([^"]+)"/)?.[1]; + if (!files) { + throw new Error('Missing data-snack-files'); + } + t.deepEqual( + Object.keys(JSON.parse(decodeURIComponent(files))), + [`App.${ext}`], + `generated files preserve extension ${ext}` + ); + } catch (error) { + t.fail(`ext rejected ${ext}: ${String(error)}`); + } + } + t.end(); +}); From c821ae666fac2f095530328cb768a69c669efe83 Mon Sep 17 00:00:00 2001 From: Simek Date: Wed, 11 Nov 2026 12:38:02 +0000 Subject: [PATCH 2/2] reuse existing conversion test case instead off adding manual ones --- plugins/remark-snackplayer/tests/index.ts | 48 ------------------- .../tests/markdown/test1.md | 2 +- .../tests/output/output1.html | 2 +- 3 files changed, 2 insertions(+), 50 deletions(-) diff --git a/plugins/remark-snackplayer/tests/index.ts b/plugins/remark-snackplayer/tests/index.ts index 2772dd71fb5..5522c3f2726 100644 --- a/plugins/remark-snackplayer/tests/index.ts +++ b/plugins/remark-snackplayer/tests/index.ts @@ -44,51 +44,3 @@ test('remark-snackplayer', async t => { t.end(); }); - -test('decodes SnackPlayer metadata once', async t => { - const processor = remark().use(remarkMdx).use(SnackPlayer); - for (const [param, attribute] of [ - ['name', 'data-snack-name'], - ['description', 'data-snack-description'], - ]) { - for (const value of ['100% ready', 'literal %20 text']) { - const input = `\`\`\`SnackPlayer ${param}=${encodeURIComponent(value)} -export default function App() { return null; } -\`\`\``; - try { - const file = await processor.process(input); - t.ok( - String(file).includes(`${attribute}="${value}"`), - `generated ${attribute} preserves ${value}` - ); - } catch (error) { - t.fail(`${param} rejected ${value}: ${String(error)}`); - } - } - } - t.end(); -}); - -test('decodes SnackPlayer filename metadata once', async t => { - const processor = remark().use(remarkMdx).use(SnackPlayer); - for (const ext of ['100% ready', 'literal %20 text']) { - const input = `\`\`\`SnackPlayer ext=${encodeURIComponent(ext)} -export default function App() { return null; } -\`\`\``; - try { - const file = await processor.process(input); - const files = String(file).match(/data-snack-files="([^"]+)"/)?.[1]; - if (!files) { - throw new Error('Missing data-snack-files'); - } - t.deepEqual( - Object.keys(JSON.parse(decodeURIComponent(files))), - [`App.${ext}`], - `generated files preserve extension ${ext}` - ); - } catch (error) { - t.fail(`ext rejected ${ext}: ${String(error)}`); - } - } - t.end(); -}); diff --git a/plugins/remark-snackplayer/tests/markdown/test1.md b/plugins/remark-snackplayer/tests/markdown/test1.md index 052c2822cf4..5b72ca8e12b 100644 --- a/plugins/remark-snackplayer/tests/markdown/test1.md +++ b/plugins/remark-snackplayer/tests/markdown/test1.md @@ -1,4 +1,4 @@ -```SnackPlayer name=Hello%20World +```SnackPlayer name=Hello%20World%20100%25%20Native import React from 'react'; import { Text, View } from 'react-native'; diff --git a/plugins/remark-snackplayer/tests/output/output1.html b/plugins/remark-snackplayer/tests/output/output1.html index 3080ebb7414..6da9f267405 100644 --- a/plugins/remark-snackplayer/tests/output/output1.html +++ b/plugins/remark-snackplayer/tests/output/output1.html @@ -1,6 +1,6 @@