Search code examples
javascriptweb-componentprismjs

how do i use prismjs when importing into a web-component


I am attempting to use prismjs from a file I am importing into a web-component.

Here is the function which is supposed to return the prismjs highlighted code block

import Prism from 'prismjs';

function article() {
     const code = `var data = 1`;
     const html = Prism.highlight(code, Prism.languages.javascript, 'javascript')
     const pre = document.createElement('pre')
     const _code = document.createElement('code')
     _code.classList = 'language-js'
     _code.innerHTML = html
     pre.appendChild(_code)

     return pre
}

and here is my web-component

 export default class PostComponent extends HTMLElement {
     constructor() {
         super()
         this.shadow = this.attachShadow({mode: 'open'})
         this.styleSheet = document.createElement('style')

         window.addEventListener('popstate', async (e) => {
             const path = e.state;
             const module = await GetArticle(path)

             this.articleName = module.title()
             this.canvasApp = module.canvasApp()
             this.article = module.article()

             this.shadow.replaceChildren(
                 this.styleSheet,
                 this.articleName,
                 this.canvasApp,
                 this.article
             )
         });

     }

     connectedCallback() {
         this.RenderView();
     }

     RenderView() {
         this.styleSheet.textContent = Style;
     }
 }

I am including the prism.min.css from index.html.

What gets ultimately rendered is an unhighlighted code element which is not what I am looking for. I have tried using Prism.highlightAll() in the article() function and the constructor but neither works.


Solution

  • You are adding the CSS to the global scope; but global CSS can not style shadowDOM

    You need to load that CSS inside the shadowDOM.

    You also do not want to load the <script src=".../prism.js"> in your HTML. It is a dependency of the Web Component. So the (JSWC) Web Component loads it when required

    About that setTimeout, see my blogpost:
    Web Component developers do not connect with the connectedCallback (yet)

    As alternative see: https://github.com/FIameCaster/prism-code-editor

    <script>
      customElements.define("prism-code", class extends HTMLElement {
        connectedCallback() {
          const createElement = (tag, props = {}, element = document.createElement(tag)) => {
            if (props.append) { element.append(...props.append); delete props.append }
            return Object.assign(element, props);
          };
          setTimeout(() => { // wait till lightDOM is parsed
            new Promise((resolve, reject) => {
              if (window.Prism) { resolve(); return }
              document.head.append(
                createElement("script", {
                  src: "https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/prism.min.js",
                  onload: resolve, onerror: reject,
                })
              );
            }).then(() => {
              this.attachShadow({ mode: "open" }).append(
                createElement("style", {
                  textContent: `@import url('https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism.min.css');`,
                }),
                createElement("pre", {
                  append: [
                    createElement("code", {
                      className: `language-${this.getAttribute("language") || "javascript"}`,
                      textContent: this.textContent.trim(),
                    }),
                  ],
                })
              ); //append
              Prism.highlightAllUnder(this.shadowRoot);
            }); // then
          }, 0); // setTimeout
        } // connectedCallback
       } // class
      ); // customElements.define
    </script>
    
    <prism-code>
    // This is a sample JavaScript code
    function helloWorld() {
        console.log('Hello, world!');
    }
    helloWorld();
    </prism-code>