├── .eleventyignore ├── .gitattributes ├── test ├── issue-80 │ ├── index.md │ └── .eleventy.js ├── 11tyjs-test │ ├── test.11ty.js │ └── .eleventy.js ├── issue-75 │ ├── index.njk │ └── .eleventy.js ├── 11tyjs-diff │ ├── .eleventy.js │ └── test.11ty.js ├── HasTemplateFormatTest.js ├── JavaScriptFunctionTest.mjs ├── HighlightLinesTest.js ├── GetAttributesTest.js ├── EleventyTest.mjs ├── LiquidHighlightTagTest.js ├── HighlightLinesGroupTest.js ├── MarkdownHighlightTest.js └── HighlightPairedShortcodeTest.js ├── .gitignore ├── .editorconfig ├── demo ├── test.css ├── eleventy-config.js ├── test-liquid.liquid ├── test-markdown.md ├── test-nunjucks.njk └── prism-theme.css ├── .eslintrc.js ├── src ├── hasTemplateFormat.js ├── HighlightLines.js ├── PrismNormalizeAlias.js ├── PrismLoader.js ├── HighlightPairedShortcode.js ├── LiquidHighlightTag.js ├── markdownSyntaxHighlightOptions.js ├── HighlightLinesGroup.js └── getAttributes.js ├── README.md ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── syntax-highlight.webc ├── LICENSE ├── package.json └── .eleventy.js /.eleventyignore: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | text eol=lf 2 | -------------------------------------------------------------------------------- /test/issue-80/index.md: -------------------------------------------------------------------------------- 1 | ```diff-javascript 2 | - foo() 3 | ``` 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | _site/ 3 | package-lock.json 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /test/11tyjs-test/test.11ty.js: -------------------------------------------------------------------------------- 1 | module.exports = function(data) { 2 | let result = this.highlight("js", "var test;"); 3 | return result; 4 | }; 5 | -------------------------------------------------------------------------------- /test/issue-75/index.njk: -------------------------------------------------------------------------------- 1 | {% highlight "html" %} 2 |
Hello
3 | {% endhighlight %} 4 | {% highlight "diff-html" %} 5 | -Hello
6 | {% endhighlight %} 7 | -------------------------------------------------------------------------------- /test/issue-75/.eleventy.js: -------------------------------------------------------------------------------- 1 | const syntaxHighlight = require("../../"); 2 | 3 | module.exports = function(eleventyConfig) { 4 | eleventyConfig.addPlugin(syntaxHighlight); 5 | }; 6 | -------------------------------------------------------------------------------- /test/issue-80/.eleventy.js: -------------------------------------------------------------------------------- 1 | const syntaxHighlight = require("../../"); 2 | 3 | module.exports = function(eleventyConfig) { 4 | eleventyConfig.addPlugin(syntaxHighlight); 5 | }; 6 | -------------------------------------------------------------------------------- /test/11tyjs-diff/.eleventy.js: -------------------------------------------------------------------------------- 1 | const syntaxHighlight = require("../../"); 2 | 3 | module.exports = function(eleventyConfig) { 4 | eleventyConfig.addPlugin(syntaxHighlight); 5 | }; 6 | -------------------------------------------------------------------------------- /test/11tyjs-test/.eleventy.js: -------------------------------------------------------------------------------- 1 | const syntaxHighlight = require("../../"); 2 | 3 | module.exports = function(eleventyConfig) { 4 | eleventyConfig.addPlugin(syntaxHighlight); 5 | }; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | charset = utf-8 10 | -------------------------------------------------------------------------------- /test/11tyjs-diff/test.11ty.js: -------------------------------------------------------------------------------- 1 | module.exports = function(data) { 2 | let result1 = this.highlight("diff", "-var test;"); 3 | let result2 = this.highlight("diff-js", "-var test;"); 4 | return result1 + "\n" + result2; 5 | }; 6 | -------------------------------------------------------------------------------- /demo/test.css: -------------------------------------------------------------------------------- 1 | .highlight-line { 2 | display: inline-block; 3 | } 4 | 5 | /* allow highlighting empty lines */ 6 | .highlight-line:empty:before { 7 | content: " "; 8 | } 9 | 10 | .highlight-line:not(:last-child) { 11 | min-width: 100%; 12 | } 13 | .highlight-line .highlight-line:not(:last-child) { 14 | min-width: 0; 15 | } 16 | -------------------------------------------------------------------------------- /demo/eleventy-config.js: -------------------------------------------------------------------------------- 1 | const syntaxHighlight = require("../.eleventy.js"); 2 | 3 | module.exports = function(eleventyConfig) { 4 | eleventyConfig.addPlugin(syntaxHighlight, { 5 | // alwaysWrapLineHighlights: true 6 | preAttributes: { tabindex: 0 } 7 | }); 8 | 9 | eleventyConfig.setTemplateFormats("njk,liquid,md,css"); 10 | }; 11 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | es6: true, 4 | node: true 5 | }, 6 | extends: "eslint:recommended", 7 | parserOptions: { 8 | sourceType: "module", 9 | ecmaVersion: 2017 10 | }, 11 | rules: { 12 | indent: ["error", 2], 13 | "linebreak-style": ["error", "unix"], 14 | quotes: ["error", "double"], 15 | semi: ["error", "always"] 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/hasTemplateFormat.js: -------------------------------------------------------------------------------- 1 | module.exports = function(templateFormats = ["*"], format = false) { 2 | if(!Array.isArray(templateFormats)) { 3 | templateFormats = [templateFormats]; 4 | } 5 | 6 | if( Array.isArray(templateFormats) ) { 7 | if( templateFormats.indexOf("*") > -1 || templateFormats.indexOf(format) > -1 ) { 8 | return true; 9 | } 10 | } 11 | 12 | return false; 13 | }; 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |var test;`, rendered);
13 | });
14 |
15 | test("JavaScript Function Diff #76", async t => {
16 | let elev = new Eleventy("./test/11tyjs-diff/", "./test/11tyjs-diff/_site/", {
17 | configPath: "./test/11tyjs-diff/.eleventy.js"
18 | });
19 | let json = await elev.toJSON();
20 |
21 | t.is(json.length, 1);
22 | let rendered = json[0].content;
23 | t.is(`-var test;
24 | -var test;`, rendered);
25 | });
26 |
--------------------------------------------------------------------------------
/test/HighlightLinesTest.js:
--------------------------------------------------------------------------------
1 | const test = require("ava");
2 | const HighlightLines = require("../src/HighlightLines");
3 |
4 | test("HighlightLines empty", t => {
5 | let hilite = new HighlightLines("");
6 | t.is(hilite.isHighlighted(0), false);
7 | });
8 |
9 | test("HighlightLines single 0", t => {
10 | let hilite = new HighlightLines("0");
11 | t.is(hilite.isHighlighted(0), true);
12 | t.is(hilite.isHighlighted(1), false);
13 | });
14 |
15 | test("HighlightLines single 1", t => {
16 | let hilite = new HighlightLines("1");
17 | t.is(hilite.isHighlighted(0), false);
18 | t.is(hilite.isHighlighted(1), true);
19 | });
20 |
21 | test("HighlightLines range", t => {
22 | let hilite = new HighlightLines("1-3");
23 | t.is(hilite.isHighlighted(0), false);
24 | t.is(hilite.isHighlighted(1), true);
25 | t.is(hilite.isHighlighted(2), true);
26 | t.is(hilite.isHighlighted(3), true);
27 | t.is(hilite.isHighlighted(4), false);
28 | });
29 |
30 | test("HighlightLines multiple ranges", t => {
31 | let hilite = new HighlightLines("1-3,5-7");
32 | t.is(hilite.isHighlighted(0), false);
33 | t.is(hilite.isHighlighted(1), true);
34 | t.is(hilite.isHighlighted(2), true);
35 | t.is(hilite.isHighlighted(3), true);
36 | t.is(hilite.isHighlighted(4), false);
37 | t.is(hilite.isHighlighted(5), true);
38 | t.is(hilite.isHighlighted(6), true);
39 | t.is(hilite.isHighlighted(7), true);
40 | t.is(hilite.isHighlighted(8), false);
41 | });
42 |
--------------------------------------------------------------------------------
/src/PrismLoader.js:
--------------------------------------------------------------------------------
1 | const Prism = require("prismjs");
2 | const PrismLoader = require("prismjs/components/index.js");
3 | // Avoid "Language does not exist: " console logs
4 | PrismLoader.silent = true;
5 |
6 | require("prismjs/components/prism-diff.js");
7 |
8 | // Load diff-highlight plugin
9 | require("prismjs/plugins/diff-highlight/prism-diff-highlight");
10 |
11 | const PrismAlias = require("./PrismNormalizeAlias");
12 |
13 | module.exports = function(language, options = {}) {
14 | let diffRemovedRawName = language;
15 | if(language.startsWith("diff-")) {
16 | diffRemovedRawName = language.substr("diff-".length);
17 | }
18 | // aliasing should ignore diff-
19 | let aliasedName = PrismAlias(diffRemovedRawName);
20 |
21 | if(!Prism.languages[ aliasedName ]) { // matches `diff` too
22 | PrismLoader(aliasedName);
23 | }
24 |
25 | if(options.errorOnInvalidLanguage && !Prism.languages[ aliasedName ]) {
26 | throw new Error(`"${language}" is not a valid Prism.js language for eleventy-plugin-syntaxhighlight`);
27 | }
28 |
29 | if(!language.startsWith("diff-")) {
30 | return Prism.languages[ aliasedName ];
31 | }
32 |
33 | // language has diff- prefix
34 | let fullLanguageName = `diff-${aliasedName}`;
35 |
36 | // Store into with aliased keys
37 | // ts -> diff-typescript
38 | // js -> diff-javascript
39 | Prism.languages[ fullLanguageName ] = Prism.languages.diff;
40 |
41 | return Prism.languages[ fullLanguageName ];
42 | };
43 |
--------------------------------------------------------------------------------
/test/GetAttributesTest.js:
--------------------------------------------------------------------------------
1 | const test = require("ava");
2 | const ga = require("../src/getAttributes");
3 |
4 | test("Falsy", t => {
5 | t.is(ga(false), "");
6 | t.is(ga(null), "");
7 | t.is(ga(undefined), "");
8 | t.is(ga(""), "");
9 | t.throws(() => {
10 | ga(" test='1'");
11 | });
12 | });
13 |
14 | test("Object syntax", t => {
15 | t.is(ga({}), "");
16 | t.is(ga({ hi: 1 }), ' hi="1"');
17 | t.is(ga({ hi: 1, bye: 2 }), ' hi="1" bye="2"');
18 | t.is(ga({ class: "my-class", bye: 2 }), ' class="my-class" bye="2"');
19 | t.is(ga({ hi: function(ctx) { return '1'; }, bye: 2 }), ' hi="1" bye="2"');
20 | });
21 |
22 | test("Function callback", t => {
23 | t.is(ga({ "data-lang": ({language}) => language }, {
24 | language: "html"
25 | }), ' class="language-html" data-lang="html"');
26 | });
27 |
28 | test("Function callback with class attribute (override)", t => {
29 | t.is(ga({
30 | class: ({language}) => "my-custom-"+language
31 | }, {
32 | language: "html"
33 | }), ' class="my-custom-html"');
34 | });
35 |
36 | test("Function callback must return string or integer", t => {
37 | t.throws(() => {
38 | ga({ "data-lang": ({language}) => undefined }, {
39 | language: "html"
40 | })
41 | });
42 |
43 | t.throws(() => {
44 | ga({ "data-lang": ({language}) => {} }, {
45 | language: "html"
46 | })
47 | });
48 |
49 | t.throws(() => {
50 | ga({ "data-lang": ({language}) => false }, {
51 | language: "html"
52 | })
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/src/HighlightPairedShortcode.js:
--------------------------------------------------------------------------------
1 | const Prism = require("prismjs");
2 | const PrismLoader = require("./PrismLoader");
3 | const HighlightLinesGroup = require("./HighlightLinesGroup");
4 | const getAttributes = require("./getAttributes");
5 |
6 | module.exports = function (content, language, highlightNumbers, options = {}) {
7 | // default to on
8 | if(options.trim === undefined || options.trim === true) {
9 | content = content.trim();
10 | }
11 |
12 | let highlightedContent;
13 | if( language === "text" ) {
14 | highlightedContent = content;
15 | } else {
16 | let loader = PrismLoader(language, options);
17 | if(!loader) {
18 | highlightedContent = content;
19 | } else {
20 | highlightedContent = Prism.highlight(content, loader, language);
21 | }
22 | }
23 |
24 | let group = new HighlightLinesGroup(highlightNumbers);
25 | let lines = highlightedContent.split(/\r?\n/);
26 | lines = lines.map(function(line, j) {
27 | if(options.alwaysWrapLineHighlights || highlightNumbers) {
28 | let lineContent = group.getLineMarkup(j, line);
29 | return lineContent;
30 | }
31 | return line;
32 | });
33 |
34 | const context = { content: content, language: language, options: options };
35 | const preAttributes = getAttributes(options.preAttributes, context);
36 | const codeAttributes = getAttributes(options.codeAttributes, context);
37 |
38 | return `` + lines.join(options.lineSeparator || "
") + "";
39 | };
40 |
--------------------------------------------------------------------------------
/src/LiquidHighlightTag.js:
--------------------------------------------------------------------------------
1 | const HighlightPairedShortcode = require("./HighlightPairedShortcode");
2 |
3 | class LiquidHighlightTag {
4 | constructor(liquidEngine) {
5 | this.liquidEngine = liquidEngine;
6 | }
7 |
8 | getObject(options = {}) {
9 | let ret = function(highlighter) {
10 | return {
11 | parse: function(tagToken, remainTokens) {
12 | let split = tagToken.args.split(" ");
13 |
14 | this.language = split.shift();
15 | this.highlightLines = split.join(" ");
16 |
17 | this.tokens = [];
18 |
19 | var stream = highlighter.liquidEngine.parser.parseStream(remainTokens);
20 |
21 | stream
22 | .on("token", token => {
23 | if (token.name === "endhighlight") {
24 | stream.stop();
25 | } else {
26 | this.tokens.push(token);
27 | }
28 | })
29 | .on("end", x => {
30 | throw new Error(`tag ${tagToken.getText()} not closed`);
31 | });
32 |
33 | stream.start();
34 | },
35 | render: function(scope, hash) {
36 | let tokens = this.tokens.map(token => {
37 | return token.raw || token.getText();
38 | });
39 | let tokenStr = tokens.join("").trim();
40 | return Promise.resolve(HighlightPairedShortcode(tokenStr, this.language, this.highlightLines, options));
41 | }
42 | };
43 | };
44 |
45 | return ret(this);
46 | }
47 | }
48 |
49 | module.exports = LiquidHighlightTag;
50 |
--------------------------------------------------------------------------------
/demo/test-markdown.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ${lines.join(options.lineSeparator || "
")}`;
53 | };
54 | };
55 |
--------------------------------------------------------------------------------
/test/EleventyTest.mjs:
--------------------------------------------------------------------------------
1 | import test from "ava";
2 | import Eleventy from '@11ty/eleventy';
3 |
4 | function normalizeNewLines(str) {
5 | return str.replace(/\r\n/g, "\n");
6 | }
7 |
8 | test("Diff output escaped #75", async t => {
9 | let elev = new Eleventy("./test/issue-75/", "./test/issue-75/_site/", {
10 | configPath: "./test/issue-75/.eleventy.js"
11 | });
12 | let json = await elev.toJSON();
13 |
14 | t.is(json.length, 1);
15 | t.is(normalizeNewLines(json[0].content.trim()), normalizeNewLines(`<p>Hello</p>
16 | -<p>Hello</p>`));
17 | });
18 |
19 | test("diff-javascript #80", async t => {
20 | let elev = new Eleventy("./test/issue-80/", "./test/issue-80/_site/", {
21 | configPath: "./test/issue-80/.eleventy.js"
22 | });
23 | let json = await elev.toJSON();
24 |
25 | t.is(json.length, 1);
26 | t.is(normalizeNewLines(json[0].content.trim()), normalizeNewLines(`- foo()
27 | `));
28 | });
29 |
--------------------------------------------------------------------------------
/src/HighlightLinesGroup.js:
--------------------------------------------------------------------------------
1 | const HighlightLines = require("./HighlightLines");
2 |
3 | class HighlightLinesGroup {
4 | constructor(str, delimiter) {
5 | this.init(str, delimiter);
6 | }
7 |
8 | init(str = "", delimiter = " ") {
9 | this.str = str;
10 | this.delimiter = delimiter;
11 |
12 | let split = str.split(this.delimiter);
13 | this.highlights = new HighlightLines(split.length === 1 ? split[0] : "");
14 | this.highlightsAdd = new HighlightLines(split.length === 2 ? split[0] : "");
15 | this.highlightsRemove = new HighlightLines(split.length === 2 ? split[1] : "");
16 | }
17 |
18 | isHighlighted(lineNumber) {
19 | return this.highlights.isHighlighted(lineNumber);
20 | }
21 |
22 | isHighlightedAdd(lineNumber) {
23 | return this.highlightsAdd.isHighlighted(lineNumber);
24 | }
25 |
26 | isHighlightedRemove(lineNumber) {
27 | return this.highlightsRemove.isHighlighted(lineNumber);
28 | }
29 |
30 | hasTagMismatch(line) {
31 | let startCount = line.split(" or on the line.
43 | // for example, we can’t wrap with
44 | if(this.hasTagMismatch(line)) {
45 | return line;
46 | }
47 |
48 | return before + line + after;
49 | }
50 |
51 | getLineMarkup(lineNumber, line, extraClasses = []) {
52 | let extraClassesStr = (extraClasses.length ? " " + extraClasses.join(" ") : "");
53 |
54 | if (this.isHighlighted(lineNumber)) {
55 | return this.splitLineMarkup(line, ``, ``);
56 | }
57 | if (this.isHighlightedAdd(lineNumber)) {
58 | return this.splitLineMarkup(line, ``, ``);
59 | }
60 | if (this.isHighlightedRemove(lineNumber)) {
61 | return this.splitLineMarkup(line, `var test;`, rendered);
25 | });
26 |
27 | test("Njk Alias", async t => {
28 | let engine = new Liquid();
29 | let tag = new LiquidHighlightTag(engine);
30 | engine.registerTag("highlight", tag.getObject());
31 |
32 | let rendered = await renderLiquid("{% highlight njk %}{% raw %}hello{% endraw %}{% endhighlight %}", {}, engine);
33 | t.is(`{% raw %}hello{% endraw %}`, rendered);
34 | });
35 |
36 | test("Nunjucks alias", async t => {
37 | let engine = new Liquid();
38 | let tag = new LiquidHighlightTag(engine);
39 | engine.registerTag("highlight", tag.getObject());
40 |
41 | let rendered = await renderLiquid("{% highlight nunjucks %}{% raw %}hello{% endraw %}{% endhighlight %}", {}, engine);
42 | t.is(`{% raw %}hello{% endraw %}`, rendered);
43 | });
44 |
--------------------------------------------------------------------------------
/src/getAttributes.js:
--------------------------------------------------------------------------------
1 | function attributeEntryToString(attribute, context) {
2 | let [key, value] = attribute;
3 |
4 | if (typeof value === "function") { // Callback must return a string or a number
5 | value = value(context); // Run the provided callback and store the result
6 | }
7 |
8 | if (typeof value !== "string" && typeof value !== "number") {
9 | throw new Error(
10 | `Attribute "${key}" must have, or evaluate to, a value of type string or number, not "${typeof value}".`
11 | );
12 | }
13 |
14 | return `${key}="${value}"`;
15 | }
16 |
17 | /**
18 | * ## Usage
19 | * The function `getAttributes` is used to convert an object, `attributes`, with HTML attributes as keys and the values as the corresponding HTML attribute's values.
20 | * If it is falsey, an empty string will be returned.
21 | *
22 | * ```js
23 | getAttributes({
24 | tabindex: 0,
25 | 'data-language': function (context) { return context.language; },
26 | 'data-otherStuff': 'value'
27 | }) // => ' tabindex="0" data-language="JavaScript" data-otherStuff="value"'
28 | ```
29 | *
30 | * @param {{[s: string]: string | number}} attributes An object with key-value pairs that represent attributes.
31 | * @param {object} context An object with the current context.
32 | * @param {string} context.content The code to parse and highlight.
33 | * @param {string} context.language The language for the current instance.
34 | * @param {object} context.options The options passed to the syntax highlighter.
35 | * @returns {string} A string containing the above HTML attributes preceded by a single space.
36 | */
37 | function getAttributes(attributes, context = {}) {
38 | let langClass = context.language ? `language-${context.language}` : "";
39 |
40 | if (!attributes) {
41 | return langClass ? ` class="${langClass}"` : "";
42 | } else if (typeof attributes === "object") {
43 | if(!("class" in attributes) && langClass) {
44 | // class attribute should be first in order
45 | let tempAttrs = { class: langClass };
46 | for(let key in attributes) {
47 | tempAttrs[key] = attributes[key];
48 | }
49 | attributes = tempAttrs;
50 | }
51 |
52 | const formattedAttributes = Object.entries(attributes).map(
53 | entry => attributeEntryToString(entry, context)
54 | );
55 |
56 | return formattedAttributes.length ? ` ${formattedAttributes.join(" ")}` : "";
57 | } else if (typeof attributes === "string") {
58 | throw new Error("Syntax highlighter plugin custom attributes on and must be an object. Received: " + JSON.stringify(attributes));
59 | }
60 | }
61 |
62 | module.exports = getAttributes;
63 |
--------------------------------------------------------------------------------
/demo/prism-theme.css:
--------------------------------------------------------------------------------
1 | pre {
2 | display: block;
3 | padding: .75rem 1rem;
4 | line-height: 1.5;
5 |
6 | overflow-x: auto;
7 | background-color: #eee;
8 | font-size: 0.875em; /* 14px /16 */
9 | -moz-tab-size: 2;
10 | -o-tab-size: 2;
11 | tab-size: 2;
12 |
13 | text-align: left;
14 | white-space: pre;
15 | word-spacing: normal;
16 | word-break: normal;
17 | word-wrap: normal;
18 |
19 | background-color: #272822;
20 | color: #fff;
21 | }
22 |
23 |
24 | /**
25 | * a11y-dark theme for JavaScript, CSS, and HTML
26 | * Based on the okaidia theme: https://github.com/PrismJS/prism/blob/gh-pages/themes/prism-okaidia.css
27 | * @author ericwbailey
28 | */
29 |
30 | /* Inline code */
31 | :not(pre) > code[class*="language-"] {
32 | padding: 0.1em;
33 | border-radius: 0.3em;
34 | white-space: normal;
35 | }
36 |
37 | .token.comment,
38 | .token.prolog,
39 | .token.doctype,
40 | .token.cdata {
41 | color: #d4d0ab;
42 | }
43 |
44 | .token.punctuation {
45 | color: #fefefe;
46 | }
47 |
48 | .token.property,
49 | .token.tag,
50 | .token.constant,
51 | .token.symbol,
52 | .token.deleted {
53 | color: #ffa07a;
54 | }
55 |
56 | .token.boolean,
57 | .token.number {
58 | color: #00e0e0;
59 | }
60 |
61 | .token.selector,
62 | .token.attr-name,
63 | .token.string,
64 | .token.char,
65 | .token.builtin,
66 | .token.inserted {
67 | color: #abe338;
68 | }
69 |
70 | .token.operator,
71 | .token.entity,
72 | .token.url,
73 | .language-css .token.string,
74 | .style .token.string,
75 | .token.variable {
76 | color: #00e0e0;
77 | }
78 |
79 | .token.atrule,
80 | .token.attr-value,
81 | .token.function {
82 | color: #ffd700;
83 | }
84 |
85 | .token.keyword {
86 | color: #00e0e0;
87 | }
88 |
89 | .token.regex,
90 | .token.important {
91 | color: #ffd700;
92 | }
93 |
94 | .token.important,
95 | .token.bold {
96 | font-weight: bold;
97 | }
98 | .token.italic {
99 | font-style: italic;
100 | }
101 |
102 | .token.entity {
103 | cursor: help;
104 | }
105 |
106 | @media screen and (-ms-high-contrast: active) {
107 | code[class*="language-"],
108 | pre[class*="language-"] {
109 | color: windowText;
110 | background: window;
111 | }
112 |
113 | :not(pre) > code[class*="language-"],
114 | pre[class*="language-"] {
115 | background: window;
116 | }
117 |
118 | .token.important {
119 | background: highlight;
120 | color: window;
121 | font-weight: normal;
122 | }
123 |
124 | .token.atrule,
125 | .token.attr-value,
126 | .token.function,
127 | .token.keyword,
128 | .token.operator,
129 | .token.selector {
130 | font-weight: bold;
131 | }
132 |
133 | .token.attr-value,
134 | .token.comment,
135 | .token.doctype,
136 | .token.function,
137 | .token.keyword,
138 | .token.operator,
139 | .token.property,
140 | .token.string {
141 | color: highlight;
142 | }
143 |
144 | .token.attr-value,
145 | .token.url {
146 | font-weight: normal;
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/test/HighlightLinesGroupTest.js:
--------------------------------------------------------------------------------
1 | const test = require("ava");
2 | const HighlightLinesGroup = require("../src/HighlightLinesGroup");
3 |
4 | test("Empty", t => {
5 | let hilite = new HighlightLinesGroup("");
6 | t.is(hilite.isHighlighted(0), false);
7 | t.is(hilite.isHighlighted(1), false);
8 | });
9 |
10 | test("Highlight irrelevant (-)", t => {
11 | let hilite = new HighlightLinesGroup("-");
12 | t.is(hilite.isHighlighted(0), false);
13 | t.is(hilite.isHighlighted(1), false);
14 | });
15 |
16 | test("Highlight simple (0)", t => {
17 | let hilite = new HighlightLinesGroup("0");
18 | t.is(hilite.isHighlighted(0), true);
19 | t.is(hilite.isHighlighted(1), false);
20 | });
21 |
22 |
23 | test("Highlight simple (1)", t => {
24 | let hilite = new HighlightLinesGroup("1");
25 | t.is(hilite.isHighlighted(0), false);
26 | t.is(hilite.isHighlighted(1), true);
27 | });
28 |
29 | test("Highlight complex", t => {
30 | let hilite = new HighlightLinesGroup("1-2,4");
31 | t.is(hilite.isHighlighted(0), false);
32 | t.is(hilite.isHighlighted(1), true);
33 | t.is(hilite.isHighlighted(2), true);
34 | t.is(hilite.isHighlighted(3), false);
35 | t.is(hilite.isHighlighted(4), true);
36 | t.is(hilite.isHighlighted(5), false);
37 | });
38 |
39 | test("Add/Remove", t => {
40 | let hilite = new HighlightLinesGroup("1-2,4 3");
41 | t.is(hilite.isHighlighted(0), false);
42 | t.is(hilite.isHighlighted(1), false);
43 | t.is(hilite.isHighlighted(2), false);
44 | t.is(hilite.isHighlighted(3), false);
45 | t.is(hilite.isHighlighted(4), false);
46 | t.is(hilite.isHighlighted(5), false);
47 |
48 | t.is(hilite.isHighlightedAdd(0), false);
49 | t.is(hilite.isHighlightedAdd(1), true);
50 | t.is(hilite.isHighlightedAdd(2), true);
51 | t.is(hilite.isHighlightedAdd(3), false);
52 | t.is(hilite.isHighlightedAdd(4), true);
53 | t.is(hilite.isHighlightedAdd(5), false);
54 |
55 | t.is(hilite.isHighlightedRemove(0), false);
56 | t.is(hilite.isHighlightedRemove(1), false);
57 | t.is(hilite.isHighlightedRemove(2), false);
58 | t.is(hilite.isHighlightedRemove(3), true);
59 | t.is(hilite.isHighlightedRemove(4), false);
60 | t.is(hilite.isHighlightedRemove(5), false);
61 | });
62 |
63 | test("Add/Remove New Delimiter", t => {
64 | let hilite = new HighlightLinesGroup("1-2,4/3", "/");
65 | t.is(hilite.isHighlighted(0), false);
66 | t.is(hilite.isHighlighted(1), false);
67 | t.is(hilite.isHighlighted(2), false);
68 | t.is(hilite.isHighlighted(3), false);
69 | t.is(hilite.isHighlighted(4), false);
70 | t.is(hilite.isHighlighted(5), false);
71 |
72 | t.is(hilite.isHighlightedAdd(0), false);
73 | t.is(hilite.isHighlightedAdd(1), true);
74 | t.is(hilite.isHighlightedAdd(2), true);
75 | t.is(hilite.isHighlightedAdd(3), false);
76 | t.is(hilite.isHighlightedAdd(4), true);
77 | t.is(hilite.isHighlightedAdd(5), false);
78 |
79 | t.is(hilite.isHighlightedRemove(0), false);
80 | t.is(hilite.isHighlightedRemove(1), false);
81 | t.is(hilite.isHighlightedRemove(2), false);
82 | t.is(hilite.isHighlightedRemove(3), true);
83 | t.is(hilite.isHighlightedRemove(4), false);
84 | t.is(hilite.isHighlightedRemove(5), false);
85 | });
86 |
87 | test("Split Line Markup", t => {
88 | let hilite = new HighlightLinesGroup("", " ");
89 | t.is(hilite.splitLineMarkup("Test", "BEFORE", "AFTER"), "BEFORETestAFTER");
90 | t.is(hilite.splitLineMarkup("Test", "BEFORE", "AFTER"), "BEFORETestAFTER");
91 | t.is(hilite.splitLineMarkup("Test", "BEFORE", "AFTER"), "Test");
92 | t.is(hilite.splitLineMarkup("Test", "BEFORE", "AFTER"), "Test");
93 | });
94 |
--------------------------------------------------------------------------------
/test/MarkdownHighlightTest.js:
--------------------------------------------------------------------------------
1 | const test = require("ava");
2 | const md = require("markdown-it");
3 | const markdownPrismJsOptions = require("../src/markdownSyntaxHighlightOptions");
4 |
5 | test("Test Markdown Highlighter", t => {
6 | let mdLib = md();
7 | mdLib.set({
8 | highlight: markdownPrismJsOptions({ alwaysWrapLineHighlights: true })
9 | });
10 | t.is(mdLib.render(`\`\`\`js
11 | alert();
12 | \`\`\``).trim(), `alert();
`);
13 | });
14 |
15 | test("Test Markdown Highlighter No Line Highlights", t => {
16 | let mdLib = md();
17 | mdLib.set({
18 | highlight: markdownPrismJsOptions()
19 | });
20 | t.is(mdLib.render(`\`\`\`js
21 | alert();
22 | \`\`\``).trim(), `alert();
`);
23 | });
24 |
25 | test("Markdown with `preAttributes`", t => {
26 | let mdLib = md();
27 | mdLib.set({
28 | highlight: markdownPrismJsOptions({
29 | alwaysWrapLineHighlights: true,
30 | preAttributes: {
31 | // will override class="language-js"
32 | class: ({language}) => "not-a-lang-" + language
33 | }
34 | })
35 | });
36 | t.is(mdLib.render(`\`\`\`js
37 | alert();
38 | \`\`\``).trim(), `alert();
`);
39 | });
40 |
41 | test("Test Njk Alias", t => {
42 | let mdLib = md();
43 | mdLib.set({
44 | highlight: markdownPrismJsOptions()
45 | });
46 | t.is(mdLib.render(`\`\`\`njk
47 | {% raw %}hello{% endraw %}
48 | \`\`\``).trim(), `{% raw %}hello{% endraw %}
`);
49 | });
50 |
51 | test("Test Nunjucks Alias", t => {
52 | let mdLib = md();
53 | mdLib.set({
54 | highlight: markdownPrismJsOptions()
55 | });
56 | t.is(mdLib.render(`\`\`\`nunjucks
57 | {% raw %}hello{% endraw %}
58 | \`\`\``).trim(), `{% raw %}hello{% endraw %}
`);
59 | });
60 |
61 | test("Markdown Invalid language", t => {
62 | let mdLib = md();
63 | mdLib.set({
64 | highlight: markdownPrismJsOptions({
65 | errorOnInvalidLanguage: true
66 | })
67 | });
68 |
69 | t.throws(() => {
70 | mdLib.render(`\`\`\`asldkjflksdaj
71 | hello
72 | \`\`\``);
73 | });
74 | });
75 |
76 | test("Test loader invalid language with ignore", t => {
77 | let src = `\`\`\`asldkjflksdaj
78 | hello
79 | \`\`\``;
80 |
81 | let mdLib = md();
82 | mdLib.set({
83 | highlight: markdownPrismJsOptions()
84 | });
85 | t.is(mdLib.render(src).trim(), `hello
`);
86 | });
87 |
88 | // test("Test Markdown Highlighter Block Comment", t => {
89 | // let mdLib = md();
90 | // mdLib.set({
91 | // highlight: markdownPrismJsOptions({ alwaysWrapLineHighlights: true })
92 | // });
93 | // t.is(mdLib.render(`\`\`\`js
94 | // /*
95 | // * this is a string
96 | // */
97 | // \`\`\``).trim(), `/*
* this is a string
*/
`);
98 | // });
99 |
100 | // TODO this still ain’t working right with the line highlighter.
101 | // test("Test Markdown Highlighter GraphQL Example", t => {
102 | // let mdLib = md();
103 | // mdLib.set({
104 | // highlight: markdownPrismJsOptions({ alwaysWrapLineHighlights: true })
105 | // });
106 | // t.is(mdLib.render(`\`\`\`js
107 | // var schema = buildSchema(\`type Query {
108 | // hello: String
109 | // }\`);
110 | // \`\`\``).trim(), ``);
111 | // });
112 |
--------------------------------------------------------------------------------
/test/HighlightPairedShortcodeTest.js:
--------------------------------------------------------------------------------
1 | const test = require("ava");
2 | const HighlightPairedShortcode = require("../src/HighlightPairedShortcode");
3 |
4 | test("Base", async t => {
5 | t.is(await HighlightPairedShortcode(`alert();
6 | alert();`, "js", "", { alwaysWrapLineHighlights: true }), `alert();
alert();
`);
7 | });
8 |
9 | test("Base with LF EOL, always wrap highlights", async t => {
10 | t.is(await HighlightPairedShortcode('alert();\nalert();',
11 | "js", "", { alwaysWrapLineHighlights: true }), `alert();
alert();
`);
12 | });
13 |
14 | test("Base with LF EOL, no wrap highlights", async t => {
15 | t.is(await HighlightPairedShortcode('alert();\nalert();',
16 | "js", "", { alwaysWrapLineHighlights: false }), `alert();
alert();
`);
17 | });
18 |
19 | test("Base with CRLF EOL, always wrap highlights", async t => {
20 | t.is(await HighlightPairedShortcode('alert();\r\nalert();',
21 | "js", "", { alwaysWrapLineHighlights: true }), `alert();
alert();
`);
22 | });
23 |
24 | test("Base with CRLF EOL, no wrap highlights", async t => {
25 | t.is(await HighlightPairedShortcode('alert();\r\nalert();',
26 | "js", "", { alwaysWrapLineHighlights: false }), `alert();
alert();
`);
27 | });
28 |
29 | test("Base with custom attributes", async t => {
30 | t.is(await HighlightPairedShortcode(`alert();
31 | alert();`, "js", "", { alwaysWrapLineHighlights: true, preAttributes: { tabindex: 0, 'data-testid': 'code' } }), `alert();
alert();
`);
32 | });
33 |
34 | test("Base change the line separator", async t => {
35 | t.is(await HighlightPairedShortcode(`alert();
36 | alert();`, "js", "", {
37 | alwaysWrapLineHighlights: true,
38 | lineSeparator: "\n",
39 | }), `alert();
40 | alert();
`);
41 | });
42 |
43 | test("Base No line highlights", async t => {
44 | t.is(await HighlightPairedShortcode(`alert();
45 | alert();`, "js", ""), `alert();
alert();
`);
46 | });
47 |
48 | test("Highlight Active", async t => {
49 | t.is(await HighlightPairedShortcode(`alert();
50 | alert();`, "js", "0", { alwaysWrapLineHighlights: true }), `alert();
alert();
`);
51 |
52 | t.is(await HighlightPairedShortcode(`alert();
53 | alert();`, "js", "0-1", { alwaysWrapLineHighlights: true }), `alert();
alert();
`);
54 | });
55 |
56 | test("Highlight Add/Remove", async t => {
57 | t.is(await HighlightPairedShortcode(`alert();
58 | alert();`, "js", "0 1", { alwaysWrapLineHighlights: true }), `alert();
alert();
`);
59 |
60 | t.is(await HighlightPairedShortcode(`alert();
61 | alert();`, "js", "1 0", { alwaysWrapLineHighlights: true }), `alert();
alert();
`);
62 | });
63 |
64 | test("Test loader typescript", async t => {
65 | let script = `function greeter(person) {
66 | return "Hello, " + person;
67 | }
68 |
69 | let user = "Jane User";
70 |
71 | document.body.textContent = greeter(user);`;
72 |
73 | t.is(await HighlightPairedShortcode(script, "typescript"), `function greeter(person) {
return "Hello, " + person;
}
let user = "Jane User";
document.body.textContent = greeter(user);
`);
74 | });
75 |
76 | test("Test loader ts", async t => {
77 | let script = `function greeter(person) {
78 | return "Hello, " + person;
79 | }
80 |
81 | let user = "Jane User";
82 |
83 | document.body.textContent = greeter(user);`
84 |
85 | t.is(await HighlightPairedShortcode(script, "ts"), `function greeter(person) {
return "Hello, " + person;
}
let user = "Jane User";
document.body.textContent = greeter(user);
`);
86 | });
87 |
88 | test("Test loader invalid language, with errorOnInvalidLanguage option", async t => {
89 | await t.throwsAsync(async () => {
90 | await HighlightPairedShortcode("", "asldkjflksdaj", null, {
91 | errorOnInvalidLanguage: true
92 | });
93 | }, { message: `"asldkjflksdaj" is not a valid Prism.js language for eleventy-plugin-syntaxhighlight` });
94 | });
95 |
96 | test("Test loader invalid language (should pass)", async t => {
97 | t.is(await HighlightPairedShortcode("test test test", "asldkjflksdaj"), `test test test
`)
98 | });
99 |
100 | test("Test loader invalid language with ignore", async t => {
101 | let src = `hello
102 | hello`
103 | t.is(await HighlightPairedShortcode(src, "asldkjflksdaj"), `hello
hello
`);
104 | });
105 |
106 | test("Trim content option (defaults true)", async t => {
107 | t.is(await HighlightPairedShortcode(` alert();
108 | alert(); `, "js", "", {}), `alert();
alert();
`);
109 |
110 | t.is(await HighlightPairedShortcode(` alert();
111 | alert(); `, "js", "", { trim: true }), `alert();
alert();
`);
112 |
113 | t.is(await HighlightPairedShortcode(` alert();
114 | alert(); `, "js", "", { trim: false }), ` alert();
alert();
`);
115 |
116 | });
117 |
--------------------------------------------------------------------------------