Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any change to default highlighter breaks SvelteKit #514

Open
Tracked by #588
donmccurdy opened this issue May 15, 2023 · 2 comments
Open
Tracked by #588

Any change to default highlighter breaks SvelteKit #514

donmccurdy opened this issue May 15, 2023 · 2 comments
Labels
assigned Whether or not this bug has been assigned some to some other issues as a subtask or pre-req
Milestone

Comments

@donmccurdy
Copy link

Beginning from the template here:

https://github.com/josh-collinsworth/sveltekit-blog-starter

I'm finding that any change to the default highlighter ...

svelte.config.js

...
	preprocess: [
		preprocess({
			scss: { prependData: `@use 'src/lib/assets/scss/vars';` }
		}),
		mdsvex({
			extensions: ['.md'],
			rehypePlugins: [rehypeSlug, rehypeAutolinkHeadings]
			highlight: {
				highlighter: function (code, lang) {
					return `<pre><code>${code}</code></pre>`;
				}
			}
		})
	],

... will break the page entirely, with errors like these:

  • /src/lib/posts/syntax-highlighting-example.md:15:1 Unexpected token
  • /src/lib/posts/syntax-highlighting-example.md:15:6 Expected }

Originally I wanted to add highlight.js to my own app (not based on the blog starter above), and ran into these errors. But it's easier to reproduce using the recommended method to disable syntax highlighting, beginning from any SvelteKit+MDsveX template, and appears to be the same problem. Possibly the default syntax highlighter is escaping characters that these alternatives do not? Returning static text like "hello" from the function works fine.

Thanks!

@donmccurdy
Copy link
Author

Digging into source code a bit...

// escape curlies, backtick, \t, \r, \n to avoid breaking output of {@html `here`} in .svelte
const escape_svelty = (str: string) =>
str
.replace(
/[{}`]/g,
//@ts-ignore
(c) => ({ '{': '&#123;', '}': '&#125;', '`': '&#96;' }[c])
)
.replace(/\\([trn])/g, '&#92;$1');
export const code_highlight: Highlighter = (code, lang) => {
if (!(process as RollupProcess).browser) {
let _lang = !!lang && langs[lang];
if (!Prism) Prism = require('prismjs');
if (_lang && !Prism.languages[_lang.name]) {
load_language(_lang.name);
}
if (!_lang && lang && Prism.languages[lang]) {
langs[lang] = { name: lang } as MdsvexLanguage;
_lang = langs[lang];
}
const highlighted = escape_svelty(
_lang
? Prism.highlight(code, Prism.languages[_lang.name], _lang.name)
: escape(code)
);
return `<pre class="language-${lang}">{@html \`<code class="language-${lang}">${highlighted}</code>\`}</pre>`;
} else {
const highlighted = escape_svelty(escape(code));
return `<pre class="language-${lang}">{@html \`<code class="language-${lang}">${highlighted}</code>\`}</pre>`;
}
};

... I was able to work around the issue by doing:

import escape from 'escape-html';

// escape curlies, backtick, \t, \r, \n to avoid breaking output of {@html `here`} in .svelte
const escape_svelty = (str) => str
  .replace(
    /[{}`]/g,
    (c) => ({ '{': '&#123;', '}': '&#125;', '`': '&#96;' }[c])
  )
  .replace(/\\([trn])/g, '&#92;$1');

...

highlight: {
  highlighter: function (code, lang) {
    const html = escape_svelty(escape(code));
    return `<pre>{@html \`<code>${html}</code>\`}</pre>`;
  }
}

... probably with a bit more tweaking I can get this working for highlight.js too, but, am I going about this the wrong way? Or should MDsvex do this escaping automatically when a custom highlighter is provided? Would be happy to make a PR if that's the case.

@donmccurdy
Copy link
Author

Further update — I have a workaround for highlight.js, which requires writing the {@html ...} tag, but does not require escaping.

import hljs from 'highlight.js';

...

mdsvex({
	extensions: ['.md'],
	highlight: {
		highlighter: function (code, lang) {
			const language = hljs.getLanguage(lang) ? lang : 'plaintext';
			const html = hljs.highlight(code, { language }).value;
			return `<pre class="language-${lang}">{@html \`<code class="language-${lang}">${html}</code>\`}</pre>`;
		}
	}
})

@pngwn pngwn added this to the 1.0 milestone Feb 23, 2024
@pngwn pngwn added the assigned Whether or not this bug has been assigned some to some other issues as a subtask or pre-req label Feb 24, 2024
@pngwn pngwn mentioned this issue Feb 23, 2024
13 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
assigned Whether or not this bug has been assigned some to some other issues as a subtask or pre-req
Projects
None yet
Development

No branches or pull requests

2 participants