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

[FEATURE] Enhance types/docs to avoid relying on browser dynamic import for language files #46

Open
ryanmr opened this issue Jun 21, 2024 · 1 comment
Labels
enhancement New feature or request

Comments

@ryanmr
Copy link

ryanmr commented Jun 21, 2024

Is your feature request related to a problem? Please describe.

I'm using this great small library to syntax highlight yaml. I'm working with Vite. It worked OK for me during pnpm run dev but I found out after deployment, built with pnpm run build, the library was trying to dynamic import the language file. Since the build didn't include the dynamic import anymore (not sure how dev and built versions differ), it was 404'ing on the yaml.js since it wasn't in the bundle.

I found this issue that mentioned a workaround for vite, using a plugin to help with dynamic imports. I wanted to avoid that step.

In short, my workaround solution is like this:

// 🔥 1. import the syntax you need, use * as languageSyntax to import it and later use it
//       note: this is not using any special vite features!
import * as yamlSyntax from "../../node_modules/@speed-highlight/core/dist/languages/yaml";

// 🔥 2. set language and the module syntax; the loadLanguage function has a bit of a misleading type; should also accept this module
loadLanguage("yaml", yamlSyntax as unknown as ShjLanguage);

I think this is intended, and I think it's worth documenting a little more explicitly.

I have a full example below too, for future readers.

Source code example
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
import { Box, Button, useTheme } from "@mui/material";
import {
  ShjLanguage,
  highlightText,
  loadLanguage,
} from "@speed-highlight/core";
import jsYaml from "js-yaml";
import { useEffect, useState } from "react";
import darkTheme from "../../node_modules/@speed-highlight/core/dist/themes/github-dark.css?inline";
import lightTheme from "../../node_modules/@speed-highlight/core/dist/themes/github-light.css?inline";

// 🔥 1. import the syntax you need, use * as languageSyntax to import it and later use it
//       note: this is not using any special vite features!
import * as yamlSyntax from "../../node_modules/@speed-highlight/core/dist/languages/yaml";

// 🔥 2. set language and the module syntax; the loadLanguage function has a bit of a misleading type; should also accept this module
loadLanguage("yaml", yamlSyntax as unknown as ShjLanguage);

interface YamlDisplayProps {
  yaml: string;
}

export function YamlDisplay({ yaml }: YamlDisplayProps) {
  const [text, setText] = useState<string | null>(null);
  const [css, setCss] = useState<string | null>(null);
  const theme = useTheme();

  const mode = theme.palette.mode;

  const content = yaml
    ? jsYaml.dump(JSON.parse(JSON.stringify(yaml)))
    : "";

  useEffect(() => {
    // 🔥 3. use highlightText, use the same key as before
    highlightText(content, "yaml", undefined, {
      hideLineNumbers: false,
    }).then((output) => {
      setText(output);
      if (mode === "light") {
        setCss(lightTheme);
      } else {
        setCss(darkTheme);
      }
    });
  }, [content, mode]);

  if (!yaml) {
    return <p>no yaml to display</p>;
  }

  return (
    <>
      {css && (
        <style type="text/css" dangerouslySetInnerHTML={{ __html: css }} />
      )}
      <Box sx={{ position: "relative" }}>
        <Box
          sx={{
            position: "absolute",
            top: 0,
            right: 0,
            p: 2,
            
          }}
        >
          <Button
            startIcon={<ContentCopyIcon />}
            onClick={() => {
              navigator.clipboard.writeText(content);
            }}
          >
            Copy
          </Button>
        </Box>

        {text && (
          <pre>
            <code>
              <Box

                component="div"
                // 🔥 3. set the class name to match the language
                className="shj-lang-yaml"

                // 🔥 4. set the text/html that was produced by the highlighter
                dangerouslySetInnerHTML={{ __html: text }}
              />
            </code>
          </pre>
        )}
      </Box>
    </>
  );
}

Describe the solution you'd like

I think the types could use an update to make it clearer at the typescript level level how to achieve this, and if the docs had an example of this it would also help too.

Describe alternatives you've considered

The alternative would have been to use the vite plugin mentioned in the issue.

Additional context

@ryanmr ryanmr added the enhancement New feature or request label Jun 21, 2024
@shash7
Copy link

shash7 commented Aug 21, 2024

I had to go through similar mental gymnastics to get it working in vite. I'm using astrojs with vuejs.

import { highlightElement, highlightText } from "@speed-highlight/core";

import * as jsSyntax from "/../node_modules/@speed-highlight/core/dist/languages/js.js";

export default {
methods : {
async render() {
      let el = this.$refs.code.querySelector("pre");

      if (!el) {
        return;
      }

      let lang = "js";

      let txt = el.textContent;

      let opt;

      let mode = `${el.tagName == "CODE" ? "in" : txt.split("\n").length < 2 ? "one" : "multi"}line`;
      el.dataset.lang = lang;
      el.className = `${[...el.classList]
        .filter((className) => !className.startsWith("shj-"))
        .join(" ")} shj-lang-${lang} shj-${mode}`;
      el.innerHTML = await highlightText(txt, { sub: jsSyntax.default }, mode == "multiline", opt);
    },
},
mounted : function() {
this.render();
}
}

Hopefully this helps someone in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants