Nuxt + Markdown-it: Cannot convert object to primitive value

Hello, I am trying to use markdown-it in my application. It works fine on Nuxt and other serverless platforms like Vercel, but it does not work on Cloudflare Workers for some reason. Here is my code:
<template>
<div v-html="parsed">
</div>
</template>
<script setup>
import { ref } from 'vue'
import MarkdownIt from 'markdown-it'

const md = new MarkdownIt('default', {
html: true,
linkify: true,
breaks: false
})

const markdown = 'https://modrinth.com'

const parsed = ref(md.render(markdown))
</script>
<template>
<div v-html="parsed">
</div>
</template>
<script setup>
import { ref } from 'vue'
import MarkdownIt from 'markdown-it'

const md = new MarkdownIt('default', {
html: true,
linkify: true,
breaks: false
})

const markdown = 'https://modrinth.com'

const parsed = ref(md.render(markdown))
</script>
Here is a minimum repro example I have made: https://github.com/Geometrically/repro-nuxt-markdown. Let me know what I can do to fix this. The full error and calltrace is:
TypeError: Cannot convert object to primitive value
at Yc.format (core:user:repro-nuxi-markdown:19126:164)
at MarkdownIt$1.normalizeLink (core:user:repro-nuxi-markdown:20480:27)
at Array.Up (core:user:repro-nuxi-markdown:20146:377)
at ParserInline$1.tokenize (core:user:repro-nuxi-markdown:20315:48)
at ParserInline$1.parse (core:user:repro-nuxi-markdown:20326:17)
at Array.Kl (core:user:repro-nuxi-markdown:19540:57)
at Core.process (core:user:repro-nuxi-markdown:19586:15)
at MarkdownIt$1.parse (core:user:repro-nuxi-markdown:20536:24)
at MarkdownIt$1.render (core:user:repro-nuxi-markdown:20538:55)
at setup (core:user:repro-nuxi-markdown:20795:23) {
stack: TypeError: Cannot convert object to primitive valu…at setup (core:user:repro-nuxi-markdown:20795:23),
message: Cannot convert object to primitive value
}
TypeError: Cannot convert object to primitive value
at Yc.format (core:user:repro-nuxi-markdown:19126:164)
at MarkdownIt$1.normalizeLink (core:user:repro-nuxi-markdown:20480:27)
at Array.Up (core:user:repro-nuxi-markdown:20146:377)
at ParserInline$1.tokenize (core:user:repro-nuxi-markdown:20315:48)
at ParserInline$1.parse (core:user:repro-nuxi-markdown:20326:17)
at Array.Kl (core:user:repro-nuxi-markdown:19540:57)
at Core.process (core:user:repro-nuxi-markdown:19586:15)
at MarkdownIt$1.parse (core:user:repro-nuxi-markdown:20536:24)
at MarkdownIt$1.render (core:user:repro-nuxi-markdown:20538:55)
at setup (core:user:repro-nuxi-markdown:20795:23) {
stack: TypeError: Cannot convert object to primitive valu…at setup (core:user:repro-nuxi-markdown:20795:23),
message: Cannot convert object to primitive value
}
29 Replies
James
James16mo ago
Okay, I see what is happening, but not sure why yet It's in this URL format function:
var format = function format(url) {
var result = '';

result += url.protocol || '';
result += url.slashes ? '//' : '';
result += url.auth ? url.auth + '@' : '';

if (url.hostname && url.hostname.indexOf(':') !== -1) {
// ipv6 address
result += '[' + url.hostname + ']';
} else {
result += url.hostname || '';
}

result += url.port ? ':' + url.port : '';
result += url.pathname || '';
result += url.search || '';
result += url.hash || '';

return result;
};
var format = function format(url) {
var result = '';

result += url.protocol || '';
result += url.slashes ? '//' : '';
result += url.auth ? url.auth + '@' : '';

if (url.hostname && url.hostname.indexOf(':') !== -1) {
// ipv6 address
result += '[' + url.hostname + ']';
} else {
result += url.hostname || '';
}

result += url.port ? ':' + url.port : '';
result += url.pathname || '';
result += url.search || '';
result += url.hash || '';

return result;
};
For some reason, the url being passed into this is... odd. .hostname on it is a Proxy, so then when it tries to serialise that, it blows up.
James
James16mo ago
If I manually hotpatch it and comment out the hostname accessors, it works, but obviously without a hostname; https://up.jross.me/8nr73wc6
James
James16mo ago
I'm digging in to try and see where this Proxy is coming from, and why it's in place
Geometrically
Geometrically16mo ago
vue in general adds proxies to a lot of things the weird thing I'm confused about is why workers causes it i was thinking maybe a missing polyfill from nodejs or something? but when I looked at the code point I couldn't find anything related if you do url.hostname.value does it work?
James
James16mo ago
Workers isn't node, so a lot of weird stuff like this that works in other envs, doesn't here Nope, .value doesn't work either Do you know where this format function is coming from? The bundled output from nuxt isn't the easiest to work through 😅
Geometrically
Geometrically16mo ago
it's all from markdown-it this might not have anything to do with nuxt, it might be a workers only issue https://github.com/markdown-it/markdown-it
James
James16mo ago
Okay it's coming from https://github.com/markdown-it/mdurl/blob/master/format.js Which is used in normalizeLink, which is used in linkify-it Yeah there's something deeply weird happening here with how this is being Proxy'd and then stringified. This is probably about as deep as I'm gonna dive into this rabbit hole though, so your best bet is probably going to be to try and reproduce this manually with a Proxy or something, to determine if it's a Workers issue and if/how it can be fixed. My gut would say this is just some weird Proxy usage that Workers is handling more to-spec, whereas other envs are more loose with handling. I've found it! @geometrically parsed.hostname = punycode.toASCII(parsed.hostname);
Geometrically
Geometrically16mo ago
Wow
James
James16mo ago
punycode is a nodejs thing and not available in Workers so this is getting mangled
Geometrically
Geometrically16mo ago
can I make my own polyfill?
James
James16mo ago
You might be able to polyfill punycode, but that's probably gonna be a massive polyfill
Geometrically
Geometrically16mo ago
well, luckily I don't think anything we use has punycode support so it could just return the passed string right?
Geometrically
Geometrically16mo ago
npm
punycode
A robust Punycode converter that fully complies to RFC 3492 and RFC 5891, and works on nearly all JavaScript platforms.. Latest version: 2.3.0, last published: 5 months ago. Start using punycode in your project by running npm i punycode. There are 1711 other projects in the npm registry using punycode.
James
James16mo ago
yeah if you stub out this, it should work: https://github.com/markdown-it/markdown-it/blob/2b6cac25823af011ff3bc7628bc9b06e483c5a08/lib/index.js#L59 or you could do that, yeah
Geometrically
Geometrically16mo ago
is there a good guide on making polyfills? a little confused on how to do it for this case specifically like since it's not my project
James
James16mo ago
Not really - I'd probably patch-package it or something honestly lemme take a quick look
Geometrically
Geometrically16mo ago
should I still make an issue in workers? I guess the module is deprecated but it would be a pretty easy fix oh, I found a way to do it
James
James16mo ago
You could open an issue in https://github.com/cloudflare/workerd to support punycode, but I'd be surprised if there's much want to support deprecated modules Your best bet is probably opening an issue in https://github.com/markdown-it/markdown-it and recommending they use a maintained solution like https://www.npmjs.com/package/tr46 or https://www.npmjs.com/package/punycode over the node.js version. so this can work in non-node environments easily And then doing something hacky in the meantime to polyfill / stub it
Geometrically
Geometrically16mo ago
thanks a lot for your help james 😄 i'll open an issue in the markdown it repo and figure out a hack around it
James
James16mo ago
I've got a very hacky solution working lemme PR this to your repro and you can use whatever bits you think are most helpful
Geometrically
Geometrically16mo ago
that would be great, yeah I tried pnpm patch but realized I would have to edit the dist/minfied version
James
James16mo ago
Something like this: https://github.com/Geometrically/repro-nuxt-markdown/pull/1 Yeah I essentially had to fork markdown-it, edit it there, rebuild to grab dist, and then patch it in My actual code change was this
diff --git a/lib/index.js b/lib/index.js
index afec8d8..eb35705 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -11,7 +11,6 @@ var ParserBlock = require('./parser_block');
var ParserInline = require('./parser_inline');
var LinkifyIt = require('linkify-it');
var mdurl = require('mdurl');
-var punycode = require('punycode');


var config = {
@@ -56,7 +55,8 @@ function normalizeLink(url) {
//
if (!parsed.protocol || RECODE_HOSTNAME_FOR.indexOf(parsed.protocol) >= 0) {
try {
- parsed.hostname = punycode.toASCII(parsed.hostname);
+ var parsedUrl = new URL(parsed.href);
+ parsed.hostname = parsedUrl.hostname;
} catch (er) { /**/ }
}
}
diff --git a/lib/index.js b/lib/index.js
index afec8d8..eb35705 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -11,7 +11,6 @@ var ParserBlock = require('./parser_block');
var ParserInline = require('./parser_inline');
var LinkifyIt = require('linkify-it');
var mdurl = require('mdurl');
-var punycode = require('punycode');


var config = {
@@ -56,7 +55,8 @@ function normalizeLink(url) {
//
if (!parsed.protocol || RECODE_HOSTNAME_FOR.indexOf(parsed.protocol) >= 0) {
try {
- parsed.hostname = punycode.toASCII(parsed.hostname);
+ var parsedUrl = new URL(parsed.href);
+ parsed.hostname = parsedUrl.hostname;
} catch (er) { /**/ }
}
}
Geometrically
Geometrically16mo ago
wow git sucks with minified files lol
James
James16mo ago
yeah lol my change is very naive, assumes URL exists, etc. but something like that will fix it if they can land it upstream I also don't know for sure how URL handles punycode, so that should probably be tested to replicate the old behaviour But it works, and your App.vue renders 😄
Geometrically
Geometrically16mo ago
yup! well it looks like this patch will be needed for a while anyways, since the mantainer is fairly inactive
James
James16mo ago
Yep 🙂 Glad we got through that - that was a fun one to dive into haha
Geometrically
Geometrically16mo ago
@James how did you rebuild it btw? found another issue so need to compile it, but the repo doesn't have a build cmd lol
James
James16mo ago
it was npm run browserify if I recall
Geometrically
Geometrically16mo ago
thanks!
Want results from more Discord servers?
Add your server