diff --git a/.eleventy.js b/.eleventy.js deleted file mode 100644 index a3b1838..0000000 --- a/.eleventy.js +++ /dev/null @@ -1,60 +0,0 @@ -const pkg = require("./package.json"); -const Prism = require("prismjs"); -const PrismLoader = require("./src/PrismLoader"); -const hasTemplateFormat = require("./src/hasTemplateFormat"); -const HighlightPairedShortcode = require("./src/HighlightPairedShortcode"); -const LiquidHighlightTag = require("./src/LiquidHighlightTag"); -const markdownPrismJs = require("./src/markdownSyntaxHighlightOptions"); - -module.exports = function(eleventyConfig, options){ - try { - eleventyConfig.versionCheck(pkg["11ty"].compatibility); - } catch(e) { - console.log( `WARN: Eleventy Plugin (${pkg.name}) Compatibility: ${e.message}` ); - } - options = Object.assign({ - init: function({Prism}){}, - lineSeparator: "\n", - errorOnInvalidLanguage: false, - alwaysWrapLineHighlights: false, - preAttributes: {}, - codeAttributes: {}, - languages: [], - }, options); - - for(const language of options.languages){ - PrismLoader(language) - } - - if( hasTemplateFormat(options.templateFormats, "liquid") ) { - eleventyConfig.addLiquidTag("highlight", (liquidEngine) => { - // {% highlight js 0 2 %} - let highlight = new LiquidHighlightTag(liquidEngine); - return highlight.getObject(options); - }); - } - - if( hasTemplateFormat(options.templateFormats, "njk") ) { - eleventyConfig.addPairedNunjucksShortcode("highlight", (content, args) => { - // {% highlight "js 0 2-3" %} - let [language, ...highlightNumbers] = args.split(" "); - return HighlightPairedShortcode(content, language, highlightNumbers.join(" "), options); - }); - } - - if( hasTemplateFormat(options.templateFormats, "md") ) { - // ```js/0,2-3 - eleventyConfig.addMarkdownHighlighter(markdownPrismJs(options)); - } - - // we need to add this as many template languages (Vue, WebC) rely on JavaScript functions (not just 11ty.js) - eleventyConfig.addJavaScriptFunction("highlight", (language, content, highlight1, highlight2) => { - let highlightLines = [highlight1, highlight2].filter(entry => entry).join(" "); - let result = HighlightPairedShortcode(content, language, highlightLines, options); - return result; - }); - - options.init({Prism}) -}; - -module.exports.pairedShortcode = HighlightPairedShortcode; diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 62e8c16..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - env: { - es6: true, - node: true - }, - extends: "eslint:recommended", - parserOptions: { - sourceType: "module", - ecmaVersion: 2017 - }, - rules: { - indent: ["error", 2], - "linebreak-style": ["error", "unix"], - quotes: ["error", "double"], - semi: ["error", "always"] - } -}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eee523c..28cef5f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: ["ubuntu-latest", "macos-latest", "windows-latest"] - node: ["16", "18", "20", "22", "24"] + node: ["18", "20", "22", "24"] name: Node.js ${{ matrix.node }} on ${{ matrix.os }} steps: - uses: actions/checkout@v3 diff --git a/demo/eleventy-config.js b/demo/eleventy-config.js index c6f37fc..89a6295 100644 --- a/demo/eleventy-config.js +++ b/demo/eleventy-config.js @@ -1,6 +1,6 @@ -const syntaxHighlight = require("../.eleventy.js"); +import syntaxHighlight from "../syntaxHighlight.js"; -module.exports = function(eleventyConfig) { +export default function(eleventyConfig) { eleventyConfig.addPlugin(syntaxHighlight, { // alwaysWrapLineHighlights: true preAttributes: { tabindex: 0 } diff --git a/package.json b/package.json index e1d622c..263bb56 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,9 @@ "publishConfig": { "access": "public" }, - "main": ".eleventy.js", + "main": "syntaxHighlight.js", "scripts": { "test": "npx ava", - "demo": "npx @11ty/eleventy --input=demo --output=demo/_site --config=demo/eleventy-config.js", "start": "npx @11ty/eleventy --input=demo --output=demo/_site --config=demo/eleventy-config.js --serve" }, "repository": { @@ -42,6 +41,7 @@ "markdown-it": "^14.1.0" }, "dependencies": { + "entities": "^7.0.0", "prismjs": "^1.30.0" }, "ava": { @@ -49,7 +49,8 @@ "failFast": false, "files": [ "./test/*.js", - "./test/*.mjs" + "./test/*.cjs" ] - } + }, + "type": "module" } diff --git a/src/HighlightLines.js b/src/HighlightLines.js index 4aa8312..16609d8 100644 --- a/src/HighlightLines.js +++ b/src/HighlightLines.js @@ -1,4 +1,4 @@ -class HighlightLines { +export default class HighlightLines { constructor(rangeStr) { this.highlights = this.convertRangeToHash(rangeStr); } @@ -29,5 +29,3 @@ class HighlightLines { return !!this.highlights[lineNumber]; } } - -module.exports = HighlightLines; diff --git a/src/HighlightLinesGroup.js b/src/HighlightLinesGroup.js index 47c0aaa..431bdb9 100644 --- a/src/HighlightLinesGroup.js +++ b/src/HighlightLinesGroup.js @@ -1,6 +1,6 @@ -const HighlightLines = require("./HighlightLines"); +import HighlightLines from "./HighlightLines.js"; -class HighlightLinesGroup { +export default class HighlightLinesGroup { constructor(str, delimiter) { this.init(str, delimiter); } @@ -64,5 +64,3 @@ class HighlightLinesGroup { return this.splitLineMarkup( line, ``, ``); } } - -module.exports = HighlightLinesGroup; diff --git a/src/HighlightPairedShortcode.js b/src/HighlightPairedShortcode.js index be37d52..f3677eb 100644 --- a/src/HighlightPairedShortcode.js +++ b/src/HighlightPairedShortcode.js @@ -1,9 +1,9 @@ -const Prism = require("prismjs"); -const PrismLoader = require("./PrismLoader"); -const HighlightLinesGroup = require("./HighlightLinesGroup"); -const getAttributes = require("./getAttributes"); +import Prism from "prismjs"; +import PrismLoader from "./PrismLoader.js"; +import HighlightLinesGroup from "./HighlightLinesGroup.js"; +import getAttributes from "./getAttributes.js"; -module.exports = function (content, language, highlightNumbers, options = {}) { +export default function (content, language, highlightNumbers, options = {}) { // default to on if(options.trim === undefined || options.trim === true) { content = content.trim(); @@ -21,19 +21,19 @@ module.exports = function (content, language, highlightNumbers, options = {}) { } } - let group = new HighlightLinesGroup(highlightNumbers); - let lines = highlightedContent.split(/\r?\n/); - lines = lines.map(function(line, j) { - if(options.alwaysWrapLineHighlights || highlightNumbers) { - let lineContent = group.getLineMarkup(j, line); - return lineContent; - } - return line; - }); + let transformedCode = highlightedContent; + if(options.alwaysWrapLineHighlights || highlightNumbers) { + let group = new HighlightLinesGroup(highlightNumbers); + let lines = highlightedContent.split(/\r?\n/); + lines = lines.map(function(line, j) { + return group.getLineMarkup(j, line); + }); + transformedCode = lines.join(options.lineSeparator || "\n"); + } const context = { content: content, language: language, options: options }; const preAttributes = getAttributes(options.preAttributes, context); const codeAttributes = getAttributes(options.codeAttributes, context); - return `` + lines.join(options.lineSeparator || "
") + ""; + return `${transformedCode}`; }; diff --git a/src/LiquidHighlightTag.js b/src/LiquidHighlightTag.js deleted file mode 100644 index 26eb7ce..0000000 --- a/src/LiquidHighlightTag.js +++ /dev/null @@ -1,49 +0,0 @@ -const HighlightPairedShortcode = require("./HighlightPairedShortcode"); - -class LiquidHighlightTag { - constructor(liquidEngine) { - this.liquidEngine = liquidEngine; - } - - getObject(options = {}) { - let ret = function(highlighter) { - return { - parse: function(tagToken, remainTokens) { - let split = tagToken.args.split(" "); - - this.language = split.shift(); - this.highlightLines = split.join(" "); - - this.tokens = []; - - var stream = highlighter.liquidEngine.parser.parseStream(remainTokens); - - stream - .on("token", token => { - if (token.name === "endhighlight") { - stream.stop(); - } else { - this.tokens.push(token); - } - }) - .on("end", x => { - throw new Error(`tag ${tagToken.getText()} not closed`); - }); - - stream.start(); - }, - render: function(scope, hash) { - let tokens = this.tokens.map(token => { - return token.raw || token.getText(); - }); - let tokenStr = tokens.join("").trim(); - return Promise.resolve(HighlightPairedShortcode(tokenStr, this.language, this.highlightLines, options)); - } - }; - }; - - return ret(this); - } -} - -module.exports = LiquidHighlightTag; diff --git a/src/PrismLoader.js b/src/PrismLoader.js index 5fa6007..ab9e325 100644 --- a/src/PrismLoader.js +++ b/src/PrismLoader.js @@ -1,16 +1,17 @@ -const Prism = require("prismjs"); -const PrismLoader = require("prismjs/components/index.js"); +import Prism from "prismjs"; +import PrismLoader from "prismjs/components/index.js"; + // Avoid "Language does not exist: " console logs PrismLoader.silent = true; -require("prismjs/components/prism-diff.js"); +import "prismjs/components/prism-diff.js"; // Load diff-highlight plugin -require("prismjs/plugins/diff-highlight/prism-diff-highlight"); +import "prismjs/plugins/diff-highlight/prism-diff-highlight.js"; -const PrismAlias = require("./PrismNormalizeAlias"); +import PrismAlias from "./PrismNormalizeAlias.js"; -module.exports = function(language, options = {}) { +export default function(language, options = {}) { let diffRemovedRawName = language; if(language.startsWith("diff-")) { diffRemovedRawName = language.substr("diff-".length); diff --git a/src/PrismNormalizeAlias.js b/src/PrismNormalizeAlias.js index 7738e05..c593c24 100644 --- a/src/PrismNormalizeAlias.js +++ b/src/PrismNormalizeAlias.js @@ -3,9 +3,13 @@ const HARDCODED_ALIASES = { nunjucks: "jinja2", }; +import { createRequire } from "node:module"; + +const require = createRequire(import.meta.url); + // This was added to make `ts` resolve to `typescript` correctly. // The Prism loader doesn’t seem to always handle aliasing correctly. -module.exports = function(language) { +export default function(language) { try { // Careful this is not public API stuff: // https://github.com/PrismJS/prism/issues/2146 diff --git a/src/getAttributes.js b/src/getAttributes.js index 603c6a5..cf090a5 100644 --- a/src/getAttributes.js +++ b/src/getAttributes.js @@ -1,3 +1,5 @@ +import { escapeAttribute } from "entities/escape"; + function attributeEntryToString(attribute, context) { let [key, value] = attribute; @@ -11,6 +13,9 @@ function attributeEntryToString(attribute, context) { ); } + if(typeof value === "string") { + value = escapeAttribute(value); + } return `${key}="${value}"`; } @@ -34,7 +39,7 @@ function attributeEntryToString(attribute, context) { * @param {object} context.options The options passed to the syntax highlighter. * @returns {string} A string containing the above HTML attributes preceded by a single space. */ -function getAttributes(attributes, context = {}) { +export default function getAttributes(attributes, context = {}) { let langClass = context.language ? `language-${context.language}` : ""; if (!attributes) { @@ -58,5 +63,3 @@ function getAttributes(attributes, context = {}) { throw new Error("Syntax highlighter plugin custom attributes on
 and  must be an object. Received: " + JSON.stringify(attributes));
   }
 }
-
-module.exports = getAttributes;
diff --git a/src/hasTemplateFormat.js b/src/hasTemplateFormat.js
index ee81e89..677fb34 100644
--- a/src/hasTemplateFormat.js
+++ b/src/hasTemplateFormat.js
@@ -1,10 +1,10 @@
-module.exports = function(templateFormats = ["*"], format = false) {
+export default function(templateFormats = ["*"], format = false) {
   if(!Array.isArray(templateFormats)) {
     templateFormats = [templateFormats];
   }
 
   if( Array.isArray(templateFormats) ) {
-    if( templateFormats.indexOf("*") > -1 || templateFormats.indexOf(format) > -1 ) {
+    if(templateFormats.includes("*") || format && templateFormats.includes(format)) {
       return true;
     }
   }
diff --git a/src/markdownSyntaxHighlightOptions.js b/src/markdownSyntaxHighlightOptions.js
deleted file mode 100644
index 098c683..0000000
--- a/src/markdownSyntaxHighlightOptions.js
+++ /dev/null
@@ -1,54 +0,0 @@
-const Prism = require("prismjs");
-const PrismLoader = require("./PrismLoader");
-const HighlightLinesGroup = require("./HighlightLinesGroup");
-const getAttributes = require("./getAttributes");
-
-module.exports = function (options = {}) {
-  return function(str, language) {
-    if(!language) {
-      // empty string means defer to the upstream escaping code built into markdown lib.
-      return "";
-    }
-
-
-    let split = language.split("/");
-    if( split.length ) {
-      language = split.shift();
-    }
-
-    let html;
-    if(language === "text") {
-      html = str;
-    } else {
-      let loader = PrismLoader(language, options)
-      if(!loader) {
-        html = str;
-      } else {
-        html = Prism.highlight(str, loader, language);
-      }
-    }
-
-    let hasHighlightNumbers = split.length > 0;
-    let highlights = new HighlightLinesGroup(split.join("/"), "/");
-    let lines = html.split("\n");
-
-    // Trim last line if it is empty
-    if (lines[lines.length - 1] === "") {
-      lines = lines.slice(0, -1);
-    }
-
-    lines = lines.map(function(line, j) {
-      if(options.alwaysWrapLineHighlights || hasHighlightNumbers) {
-        let lineContent = highlights.getLineMarkup(j, line);
-        return lineContent;
-      }
-      return line;
-    });
-
-    const context = { content: str, language: language, options: options };
-    const preAttributes = getAttributes(options.preAttributes, context);
-    const codeAttributes = getAttributes(options.codeAttributes, context);
-
-    return `${lines.join(options.lineSeparator || "
")}
`; - }; -}; diff --git a/syntax-highlight.webc b/syntax-highlight.webc index d894ea3..54a0bbf 100644 --- a/syntax-highlight.webc +++ b/syntax-highlight.webc @@ -1,5 +1,5 @@ -