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

Using the plugins in NodeJS #1171

Open
florinpop17 opened this issue Aug 2, 2017 · 25 comments
Open

Using the plugins in NodeJS #1171

florinpop17 opened this issue Aug 2, 2017 · 25 comments

Comments

@florinpop17
Copy link

How can the plugins be used in NodeJS when importing like:
import Prism from 'prismjs';

I tried to access the line-numbers plugin, but when I do Prism.plugins it's an empty object.

Any ideas?

@ghost
Copy link

ghost commented Aug 8, 2017

This doesn't work:

var prism = require('prismjs');
require('prismjs/plugins/keep-markup/prism-keep-markup.js');

In node doesn't work for me either. This is awful.
I really wish I could use the Keep Markup plugin with node.

@florinpop17
Copy link
Author

What I did was to copy the entire JS file into my static folder, and use it from there... Not the most elegant thing to do...

@ghost
Copy link

ghost commented Aug 8, 2017

@florinpop17 Thank you! Good advice. I was about to go down that path but I've given up for now. Not sure how document.createRange can even be used from the keep-markup plugin in NodeJs so I'm stuck until deeper investigation.

@schwastek
Copy link

@megamindbrian both import 'prismjs' and require('prismjs') should work. They create Prism object.

To check if you are able to access the properties of the created Prism object, open the DevTools Console and type Prism (it's case sensitive). Initially, Prism.plugins is an empty object {}.

Everytime you require Prism's plugin it injects itself into the Prism.plugins object.
Take a look at prism-keep-markup.js file, line 7:

Prism.plugins.KeepMarkup = true;

In conclusion, this should work for you:

import 'prismjs';
import 'prismjs/plugins/keep-markup/prism-keep-markup.js';

Maybe you should check your bundler's paths - what directories are being searched when resolving modules and if the node_modules directory is being taken into account.

@ghost
Copy link

ghost commented Sep 13, 2017 via email

@Golmote
Copy link
Contributor

Golmote commented Sep 13, 2017

@florinpop17 @megamindbrian At the moment, most plugins won't work on Node because they use the DOM (Line numbers and Keep markup are one of those).

Are you trying to use those with some virtual DOM node modules or something?

@ghost
Copy link

ghost commented Sep 13, 2017 via email

@mAAdhaTTah
Copy link
Member

Is this issue still valid? Are any of the plugins workable in Node? If they are, we could expose a loadPlugins, similar to loadComponents, for Node, but not worth doing if we can't even use them.

@Golmote
Copy link
Contributor

Golmote commented Apr 16, 2018

@mAAdhaTTah Most plugin don't work in Node indeed. From a quick look:

  • Autolinker: Should work if the before-highlight hook is run manually.
  • Autoloader: Does not work and returns early. Requires DOM.
  • Command line: Does not work and returns early. Requires DOM.
  • Copy to clipboard: Does not work and returns early. Requires DOM.
  • Custom class: Should work.
  • Data-URI highlight: Should work if the before-highlight hook is run manually.
  • File Highlight: Does not work and returns early. Requires DOM.
  • Highlight keywords: Should work.
  • JSONP highlight: Does not work and returns early. Requires DOM.
  • Keep Markup: Does not work and returns early. Requires DOM.
  • Line Highlight: Does not work and returns early. Requires DOM.
  • Line Numbers: Does not work and returns early. Requires DOM.
  • Normalize Whitespace: Should work if the before-sanity-check hook is run manually.
  • Previewers: Does not work and returns early. Requires DOM.
  • Remove initial line feed: Deprecated, but should work if the before-sanity-check hook is run manually.
  • Show invisibles: Should work if the before-highlight hook is run manually.
  • Show language: Does not work and returns early. Requires DOM.
  • Toolbar: Does not work and returns early. Requires DOM.
  • Unescaped markup: Does not work and returns early. Requires DOM.
  • WPD: Should work.

I left this issue open, because I know this is a common expectation from our users. Yet I think you are right, most of the plugins that do not work currently probably won't ever be compatible with Node in the future.

Regarding a loadPlugins function, is it something that would be useful? They usually don't have any dependency...

@mAAdhaTTah
Copy link
Member

Regarding a loadPlugins function, is it something that would be useful? They usually don't have any dependency...

The main benefit would be not needing to know the paths. I dunno if it's that beneficial, but it does at least provide some symmetry with the way loadLanguages works.

@benwiley4000
Copy link

This is a major bummer especially when using something like Spectacle/react-live which relies on Node prism for the client side. Any thoughts on improving this? 🙂

@mAAdhaTTah
Copy link
Member

Right now the suggestion is to use the babel plugin: https://github.com/mAAdhaTTah/babel-plugin-prismjs/ – there are definitely still details to iron out if you're providing a library using Prism, as it suggests the consumer of the library needs to run babel over their dependencies (or at least that library), which may or may not be easy to do.

@benwiley4000
Copy link

I may be incorrect but I don't think this will work for the Node API (which react-live uses for rendering server and client side) because the plugins (line numbers, line highlight) rely on DOM attributes being present, and prism node doesn't operate on DOM elements.

@mAAdhaTTah
Copy link
Member

That's correct. May not be worth it / possible to highlight server-side in that case.

@benwiley4000
Copy link

Right. The issue is that most React based libraries including this one use the node variant of a library even for the client side because it fits much better into the way React renders. So more and more consumers of popular libraries like spectacle are going to be unable to take full advantage of Prismjs's client side capabilities, which is a shame. :/

@mAAdhaTTah
Copy link
Member

They can highlight on componentDidUpdate, so it doesn't fire on the server. They also should not be using loadLanguages in any code intended to be used on the client.

@lunelson
Copy link

lunelson commented Mar 8, 2019

AFAICT currently some plugins are using Node's global object to pick up Prism and register the plugins. The following two work, but other plugins do not do this. Ultimately the best solution is probably to copy the body of the IIFEs in those files in to the file where you require Prism.

global.Prism = require('prismjs');
require('prismjs/plugins/normalize-whitespace/prism-normalize-whitespace');
require('prismjs/plugins/data-uri-highlight/prism-data-uri-highlight');

Object.keys(Prism.plugins); // [ 'NormalizeWhitespace', 'dataURIHighlight' ]

@tig
Copy link

tig commented Jan 1, 2020

I was really expecting Prism's line number support to work from node. Found this issue and I'm quite bummed.

I'm using node.js to generate static html files from code, formatting them with prismjs, so there's no DOM to access.

@xcatliu
Copy link

xcatliu commented May 20, 2020

Autolinker: Should work if the before-highlight hook is run manually

@Golmote How to run before-highlight hook manually?

@RunDevelopment
Copy link
Member

You can run any hook using Prism.hooks.run('<hook name>', env);. Insert any hook name (e.g. before-highlight).
env is tricky but for Autolinker, we only need a grammar.

const code = `var foo = 0;`;
const language = 'javascript';

const grammar = Prism.languages[language];
Prism.hooks.run('before-highlight', { grammar });
const highlighted = Prism.highlight(code, grammar, language);

Please keep in mind that hooks are mostly internal at this point in time, so this code my break at any time.

TBH, Autolinker, DataURI URL, and Show Invisibles should be implemented in a way that works only with the hooks from Prism.highlight, so you don't have to manually call any hooks.

@asm0dey
Copy link

asm0dey commented Sep 26, 2020

Is it possible to implement line-highlight in NodeJS? For example highlightjs (which is much less powerful) has it.
I'm building HTML5 presentations and would like to use line highlighting, but the code is almost not readable without line highlighting.

@peterbe
Copy link

peterbe commented Sep 28, 2020

Is it possible to implement line-highlight in browser-independent way?

I think you mean NodeJS :)
Yes, I'm very much in favor of this plugin working in Node.

Line-highlighting and line-numbering are the only plugins I genuinely need for Node.

@judocode
Copy link

Hopefully this helps someone as I didn't find an answer in any of these comments. I am using Prism with React via NPM and was having some difficulty with getting line numbers to work. It seems line-numbers is relying on the complete hook, so I was able to get it to work by manually calling it after the component was mounted. This is what I came up with that seems to work (yes I use class components still and css styles are coming from Tailwind):

import * as React from 'react'
import Prism from 'prismjs'
import DOMPurify from 'dompurify'
import 'prismjs/plugins/line-numbers/prism-line-numbers.js'
const language = 'javascript'

const grammar = Prism.languages[language]

interface IProps {
  code: string
}

class CodeComponent extends React.Component<IProps, {}> {
  codeRef: React.RefObject<HTMLDivElement>

  constructor(props: IProps) {
    super(props)

    this.codeRef = React.createRef()
  }

  componentDidMount() {
    // @ts-ignore
    Prism.hooks.run('complete', {
      grammar,
      element: this.codeRef.current,
      code: this.props.code,
    })
  }

  render(): React.ReactNode {
    const code = Prism.highlight(this.props.code, grammar, language)

    return (
      <pre className="pr-6 py-4 pl-12">
        <code
          ref={this.codeRef}
          className="language-js block line-numbers relative"
          dangerouslySetInnerHTML={{
            __html: DOMPurify.sanitize(
              code.trim().replaceAll('\n', '<br />'),
            ),
          }}
        />
      </pre>
    )
  }
}

export default CodeComponent

ahilke added a commit to ahilke/js-crap-score that referenced this issue May 18, 2023
Use Node API from Prism instead of provided HTML script, as this
has some advantages:

- Manage Prism script depedency with npm.
- Have easier control over Prism options.
- Considerably reduce output size by not including the JS in every page,
  although for projects with very long functions this might have the
  opposite effect, as the size of the shown source increases with added HTML.

Sadly, most plugins do not work using the Node API, see
PrismJS/prism#1171.

May be revisited with `prismjs@2`.
@quilicicf
Copy link

quilicicf commented Sep 28, 2023

I've had some success with plugins using NodeJS but it's hacky.

I'm using jsdom to create a document from the HTML, then I update the global context to fool Prism into thinking it's running in a browser.

I tried other DOM parsers, but jsdom produces a DOM that's the closest from a real browser DOM.

Something like this:

import jsdom from 'jsdom';
import Prism from 'prismjs';

async function compileDeck(rawDeckHtml) {
  const dom = new jsdom.JSDOM(rawDeckHtml);
  
  // Fool Prims & plugins into thinking they're running in a browser
  global.window = dom.window;
  global.document = dom.window.document;
  global.getComputedStyle = window.getComputedStyle; // Line-numbers plugin uses getComputedStyle as if in a browser
  // Other plugin-dependent hacks as needed
  
  await import('prismjs/plugins/line-numbers/prism-line-numbers.js'); // Load plugins as needed
  
  Prism.highlightAllUnder(dom.window.document); // Highlight all code blocks in my presentation deck
  
  return dom.serialize(); // Generate the final HTML
}

It would be nice not to have to hack that much.

@TrebledJ
Copy link

TrebledJ commented Dec 1, 2024

Used the same strat as quilicicf, but used domino instead of jsdom for speed improvements and zero additional dependencies. Got command-line, line-numbers, and toolbar to work for my Eleventy site.

Writeup: https://trebledj.me/posts/prism-plugins-in-node/

I agree it's not the most optimal experience. :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests