epicweb-dev / epicshop

The workshop app for all workshops on EpicWeb.dev
https://www.epicweb.dev
Other
207 stars 33 forks source link

Handle `+` in the filename for CodeFile #102

Closed kentcdodds closed 1 year ago

kentcdodds commented 1 year ago

I noticed the + gets stripped or something: https://github.com/epicweb-dev/web-forms/blob/3a0991a3c2980edfd621b0c01ab4ea025277a5bd/exercises/05.complex-structures/03.solution.add-remove/README.mdx#L15

For some reason clicking the "OPEN in solution" button here creates a file at exercises/05.complex-structures/03.solution.add-remove/app/routes/users /$username_ /notes.$noteId_.edit.tsx

onemen commented 1 year ago

i look into it tomorrow

onemen commented 1 year ago

error_open_file

I see this on windows.... no +

kentcdodds commented 1 year ago

Yeah, that whitelist should be adjusted to allow for + as it's an important part of our convention.

onemen commented 1 year ago

I think the issue is in '@kentcdodds/md-temp'

\`\`\`jsx filename=app/routes/users+/$username_+/notes.$noteId_.edit.tsx
console.log('hello world')
\`\`\`

look like this after remarkCodeBlocksShiki

Shiki

hellow !!! it's after midnight

kentcdodds commented 1 year ago

It may be an issue in both.

We can circle back on this later :)

onemen commented 1 year ago

It may be an issue in both.

the output from codefile-mdx.server.ts don't have this issue. it may also be some of the other plugins that runs after it

onemen commented 1 year ago

@kentcdodds

fix this in '@kentcdodds/md-temp'

  const { preNode, codeString, language, meta } = nodeStuff;
  let metaParams = new URLSearchParams();
  if (meta) {
-   metaParams = new URLSearchParams(meta.split(/\s+/).join("&"));
+    const encodedMeta = meta.split(/\s+/).map(component => {
+        const [key, value] = component.split('=');
+        return `${key}=${encodeURIComponent(value)}`;
+    }).join('&');
+    metaParams = new URLSearchParams(encodedMeta);
  }

or add this to compile-mdx.server.ts

+interface CodeNode extends Element {
+   data?: {
+       meta?: string
+   }
+}
+
+function rehypeEncodeMeta() {
+   return (tree: HastRoot) => {
+       visit(tree, { type: 'element', tagName: 'pre' }, node => {
+           const codeNode = node.children[0] as CodeNode
+           if (codeNode?.type !== 'element') return
+           if (codeNode.tagName !== 'code') return
+           if (typeof codeNode.data?.meta !== 'string') return
+           const meta = codeNode.data.meta
+           const encodedMeta = meta
+               .split(/\s+/)
+               .map(component => {
+                   const [key, value] = component.split('=')
+                   return `${key}=${encodeURIComponent(value)}`
+               })
+               .join('&')
+           codeNode.data.meta = encodedMeta
+       })
+   }
+}
+
 const rehypePlugins: PluggableList = [
+   rehypeEncodeMeta,
    trimCodeBlocks,
    remarkCodeBlocksShiki,
    removePreContainerDivs,
]
kentcdodds commented 1 year ago

I just pushed v4 to @kentcdodds/md-temp

onemen commented 1 year ago

user may call remarkCodeBlocksShiki with meta already encoded see #106

option 1

if (meta) {
  const encodedMeta =
    decodeURIComponent(meta) !== meta
      ? meta.split(/\s+/).join('&')
      : meta
          .split(/\s+/)
          .map((component) => {
            const [key, value] = component.split('=');
            return `${key}=${value ? encodeURIComponent(value) : ''}`;
          })
          .join('&');
  metaParams = new URLSearchParams(encodedMeta);
}

option 2

if (meta) {
  const encodedMeta = meta
    .split(/\s+/)
    .map((component) => {
      const [key, value] = component.split('=');
      return `${key}=${value ? encodeURIComponent(decodeURIComponent(value)) : ''}`;
    })
    .join('&');
  metaParams = new URLSearchParams(encodedMeta);
}

option 3: user should not use encoded meta

kentcdodds commented 1 year ago

I published 4.0.2 with option 1 👍 Thanks!