Skip to content
This repository has been archived by the owner on Oct 1, 2024. It is now read-only.

How to use remark-react with hightlight.js #14

Closed
davegomez opened this issue Jan 17, 2016 · 30 comments · Fixed by #30
Closed

How to use remark-react with hightlight.js #14

davegomez opened this issue Jan 17, 2016 · 30 comments · Fixed by #30
Labels
🙋 no/question This does not need any changes

Comments

@davegomez
Copy link

I'm trying to use HightlightJS with remark-react but I don't quite understand how to use the syntax:

import remark from 'remark';
import reactRenderer from 'remark-react';
import hljs from 'remark-highlight.js';

...

this.markdownProcessor = remark().use([reactRenderer, hljs]);

Any hint on how to use it?

@tmcw
Copy link
Contributor

tmcw commented Jan 17, 2016

It's not currently possible to use highlight.js: highlight.js produces a string of HTML content, and remark-react is expressly written to avoid such HTML escapes. If there was a highlighter that output an AST instead, then we could integrate it into remark-react - otherwise, highlight.js is not compatible with remark-react's security model.

@davegomez
Copy link
Author

Do you have an example of the AST structure you would need to implement the highlighting?

@wooorm
Copy link
Member

wooorm commented Jan 24, 2016

I was thinking about the same problem recently, for example, I’d like to support syntax highlighting in remark-vdom, remark-man too.

I did some digging into highlight.js, and I don’t think their per-language syntax files needs changing (for example, javascript, wouldn’t need changing). However, their core is tightly coupled with outputting HTML.

So, either someone needs to create a fork which supports other syntaxes (in the form of specifying a compiler), or highlight.js itself should be rewritten, but I’m not sure whether they’d want to.

I have a full plate, but I’ll try my hands on whether it’s feasible, but I’d suggest you’d raise an issue regarding decoupling on highlight.js’s repository.

@wooorm
Copy link
Member

wooorm commented Jan 24, 2016

I actually have a hacky but working AST-based re-implementation of highlight.js working now. It’s probably bug filled though, will have to add lots of tests (I believe there’s like 60 supported languages? Gah.) But do expect more to come soon! 😄

@tmcw I’m writing it to expose HAST nodes, a unist and minimal HTML syntax tree. This means to get this working with react there needs to be a transformation between the two. I don’t expect it to be very hard, but I’m not experienced with react. could you help me there when the time comes?

@tmcw
Copy link
Contributor

tmcw commented Jan 25, 2016

Sure! Let me know when you get to the point of integration

@wooorm
Copy link
Member

wooorm commented Jan 25, 2016

Great! 👌

@wooorm
Copy link
Member

wooorm commented Jan 26, 2016

bebraw added a commit to survivejs/react-component-boilerplate that referenced this issue Jan 28, 2016
Still waiting for remarkjs/remark-react#14 to
get highlighting back. Otherwise this should be a stabler alternative
than mtrc as mdast-react is maintained actively.
@bebraw
Copy link
Contributor

bebraw commented Jan 28, 2016

FYI, here's a Node friendly highlighter that has been developed based on Prism: Illuminate.js. Prism deals with JSX better than Highlight.js.

It would be great if you could design the system so that it's possible to glue in Illuminate. Note that there's react-illuminate as well.

@wooorm
Copy link
Member

wooorm commented Feb 17, 2016

@bebraw just saw your comment, that looks nice too, although it doesn’t support many language, and it seems to be in beta?

@tmcw I think all the building blocks for syntax highlighting with remark-react are now here, there’s lowlight (or others), and there’s react-syntax-highlighter (which uses lowlight under the hood).

What remains is “component” support in remark-react, which I guess means that users of this remark plugin can specify that code is rendered as SyntaxHighlighter?

Again, I’m not very familiar with React though, so you probably have a better idea of how this should be handled :)

@bebraw
Copy link
Contributor

bebraw commented Feb 17, 2016

@wooorm It's a little limited at the moment, that's true. Given it's Prism based, it would be possible to add support to the missing languages if needed I think. I opened an issue so we get feedback from the author. Something Prism based would be a winner solution for me given how well it highlights.

@wooorm
Copy link
Member

wooorm commented Feb 17, 2016

Diversity is good! If you want prism, that’s fine and it should happen.

I think this issue will allow that, if we add support for components through something similar like the method I proposed above!

@bebraw
Copy link
Contributor

bebraw commented Apr 5, 2016

It would be nice to get this moving again. Can someone in the know explain what should happen code-wise? I could try to look into making the changes.

@wooorm
Copy link
Member

wooorm commented Apr 5, 2016

I don’t know the API of react, but you can use react-lowlight or react-syntax-highlighter, both based on lowlight, to do virtual (thus, safe), syntax highlighting. Then, wrap one of those components, and integrate with remarkReactComponents (see the readme of remark-react). I’m not entirely sure what API they use, and if you can access the lang of a code node though.

@tmcw, could you clarify whether this should work?

@bebraw
Copy link
Contributor

bebraw commented Apr 5, 2016

@wooorm Thanks. Esp. react-lowlight seems great. I think could be enough if I just replace the portion that does code now with that component. I'll give it a quick go and report back.

@bebraw
Copy link
Contributor

bebraw commented Apr 5, 2016

I gave it more thought. We probably should agree on a basic interface. Right now we have

remark().use(reactRenderer)

What if we did

remark().use(reactRenderer({
  code: codeRenderer // custom logic would go here
}))

The idea is that this type of hook system would allow you to replace h per node type. This would allow the maximum amount of customization without breaking backwards compatibility. Most importantly it would enable highlighting plugin.

@wooorm
Copy link
Member

wooorm commented Apr 5, 2016

I believe the current code already supports something like that:

var remark = require('remark');
var toReact = require('remark-react');

remark().use(toReact, {
  remarkReactComponents: {
    'code': 'Lowlight'
  }
}))

Then, Lowlight is passed to React.createElement. But, aside from supporting string, maybe function should be supported in remark-react too? Or, create a wrapper component around Lowlight which supports the attributes given by remark-react and transforms them to support the attributes expected by react-lowlight.

@bebraw
Copy link
Contributor

bebraw commented Apr 5, 2016

Oh, yeah. Missed that. It would need a tiny adapter right there. 👍

@wooorm
Copy link
Member

wooorm commented Apr 5, 2016

Maybe create remark-react-highlight? So people who use remark, react, and remark-react, need just remark().use(remarkReact).use(remarkReactHighlight)? That’d be my suggestion :)

@bebraw
Copy link
Contributor

bebraw commented Apr 6, 2016

I managed to set up highlighting to work with remark-react. Here's a quick demo.

@wooorm How would that remark().use(remarkReact).use(remarkReactHighlight) work? I would need to understand how to connect the plugins internally to patch remark-react behavior (literally that element definition). Alternatively we could do something like this:

{remark().use(reactRenderer, {
  remarkReactComponents: {
    code: RemarkLowlight({
      // list supported languages here, lowlight needs them
      js: jsLangugeDefinition
    })
  }
}).process(readme)}

Lowlight requires those language definitions. Instead of passing them explicitly we could hide the registration within the package itself and perhaps support optional registration that would override that default. I think the reasoning is that each language definition bloats the bundle so the decided to go with opt-in kind of system there.

@wooorm
Copy link
Member

wooorm commented Apr 6, 2016

Oh, you’re right, it wouldn’t work (currently) as a remark plug-in :/ I like your proposed idea; supporting an object mapping names to language definitions.

And, indeed, that’s the reason. Lowlight suggests against subscribing all languages when in the browser.

@tmcw
Copy link
Contributor

tmcw commented Apr 6, 2016

Awesome, been a bit quiet on this because of work-work but love the way this is shaping up.

@bebraw
Copy link
Contributor

bebraw commented Apr 8, 2016

I published the solution as remark-react-lowlight. You can see it in action at react-component-boilerplate.

It's probably safe to close this issue now.

@wooorm
Copy link
Member

wooorm commented Apr 8, 2016

Nice! Maybe add a link to the README here though?

@rgbkrk
Copy link

rgbkrk commented Apr 9, 2016

@bebraw is there a way to use that component without having to specify the highlight js lang (relying on code fence blocks)? The example in the README makes me think I'd have to specify it.

@rgbkrk
Copy link

rgbkrk commented Apr 9, 2016

Ah, I see. If it's in the object passed to RemarkLowlight({}) it gets registered. Noticed that it will break down if the language specified in a code fence block isn't registered though.

@bebraw
Copy link
Contributor

bebraw commented Apr 9, 2016

@rgbkrk Yeah. It has to register the languages first. Can you open an issue about the breakage at my repo? Maybe the right way to solve that is to skip highlighting in that case and just give a warning instead.

@wooorm wooorm mentioned this issue Jun 23, 2016
austinsc pushed a commit to austinsc/react-gooey that referenced this issue Aug 15, 2016
Still waiting for remarkjs/remark-react#14 to
get highlighting back. Otherwise this should be a stabler alternative
than mtrc as mdast-react is maintained actively.
@mattcreager
Copy link

mattcreager commented Oct 11, 2016

Hey I'm attempting to get remark-react and syntax highlighting to cooperate, hoping someone who's run the gauntlet before can help :)

My naive implementation looks something like

Lowlight.registerLanguage('js', js);

const Pre = ({children}) => {
  return <Lowlight language={'js'} value={children[0].props.children[0]} />
}

Pre.propTypes = {
  className: React.PropTypes.string,
  children: React.PropTypes.node,
};
...
const remarkReactComponents = { pre: Pre };

Which would work if I only needed to highlight js,. so from here I wrote a little parser that grabs the language from a node and drops it into an attributes object.

node.attributes.className = ['hljs', `language-${lang}`];

Which never appears to be available on the props of my React nodes.

P.S. I'm using pre rather than code to prevent nested elements.

@wooorm
Copy link
Member

wooorm commented Nov 8, 2016

I think @bebraw got something working with https://github.com/bebraw/remark-react-lowlight.

@bebraw, does remark-react-lowlight support pre?

@bebraw
Copy link
Contributor

bebraw commented Nov 8, 2016

@bebraw, does remark-react-lowlight support pre?

I can't see anything pre specific at the code. Maybe "it just works"™. Worth a go.

@wooorm
Copy link
Member

wooorm commented Nov 8, 2016

Cool, I’ll whip up a PR to include this in the docs here!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
🙋 no/question This does not need any changes
Development

Successfully merging a pull request may close this issue.

6 participants