base files from eleventy-plugin-syntaxhighlight-4.2.0 and chromahighlight npm package as dependency

This commit is contained in:
Robert McGovern 2023-02-02 14:36:15 +00:00
commit c40be6836a
40 changed files with 1597 additions and 0 deletions

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8

59
.eleventy.js Normal file
View File

@ -0,0 +1,59 @@
const pkg = require("./package.json");
const Prism = require("prismjs");
const hasTemplateFormat = require("./src/hasTemplateFormat");
const HighlightPairedShortcode = require("./src/HighlightPairedShortcode");
const LiquidHighlightTag = require("./src/LiquidHighlightTag");
const CharacterWrap = require("./src/CharacterWrap");
const markdownPrismJs = require("./src/markdownSyntaxHighlightOptions");
module.exports = {
initArguments: { Prism },
configFunction: function(eleventyConfig, options = {}) {
try {
eleventyConfig.versionCheck(pkg["11ty"].compatibility);
} catch(e) {
console.log( `WARN: Eleventy Plugin (${pkg.name}) Compatibility: ${e.message}` );
}
options = Object.assign({
alwaysWrapLineHighlights: false,
// eligible to change the default to \n in a new major version.
lineSeparator: "<br>",
preAttributes: {},
codeAttributes: {}
}, options);
// TODO hbs?
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));
}
if( hasTemplateFormat(options.templateFormats, "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;
});
}
}
};
module.exports.pairedShortcode = HighlightPairedShortcode;
module.exports.CharacterWrap = CharacterWrap;

1
.eleventyignore Normal file
View File

@ -0,0 +1 @@
README.md

17
.eslintrc.js Normal file
View File

@ -0,0 +1,17 @@
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"]
}
};

2
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,2 @@
# These are supported funding model platforms
open_collective: 11ty

23
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,23 @@
on:
push:
branches-ignore:
- "gh-pages"
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
node: ["14", "16", "18"]
name: Node.js ${{ matrix.node }} on ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
# cache: npm
- run: npm install
- run: npm test
env:
YARN_GPG: no

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules/
_site/
package-lock.json
yarn.lock

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Zach Leatherman @zachleat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

8
README.md Normal file
View File

@ -0,0 +1,8 @@
<p align="center"><img src="https://www.11ty.dev/img/logo-github.svg" width="200" height="200" alt="eleventy Logo"></p>
# eleventy-plugin-syntaxhighlight 🕚⚡️🎈🐀
A pack of [Eleventy](https://github.com/11ty/eleventy) plugins for syntax highlighting. No browser/client JavaScript here, these highlight transformations are all done at build-time.
## Read the [Full Documentation on 11ty.dev](https://www.11ty.dev/docs/plugins/syntaxhighlight/)

10
demo/eleventy-config.js Normal file
View File

@ -0,0 +1,10 @@
const syntaxHighlight = require("../.eleventy.js");
module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(syntaxHighlight, {
// alwaysWrapLineHighlights: true
preAttributes: { tabindex: 0 }
});
eleventyConfig.setTemplateFormats("njk,liquid,md,css");
};

148
demo/prism-theme.css Normal file
View File

@ -0,0 +1,148 @@
pre {
display: block;
padding: .75rem 1rem;
line-height: 1.5;
overflow-x: auto;
background-color: #eee;
font-size: 0.875em; /* 14px /16 */
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
background-color: #272822;
color: #fff;
}
/**
* a11y-dark theme for JavaScript, CSS, and HTML
* Based on the okaidia theme: https://github.com/PrismJS/prism/blob/gh-pages/themes/prism-okaidia.css
* @author ericwbailey
*/
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: 0.1em;
border-radius: 0.3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #d4d0ab;
}
.token.punctuation {
color: #fefefe;
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: #ffa07a;
}
.token.boolean,
.token.number {
color: #00e0e0;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #abe338;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #00e0e0;
}
.token.atrule,
.token.attr-value,
.token.function {
color: #ffd700;
}
.token.keyword {
color: #00e0e0;
}
.token.regex,
.token.important {
color: #ffd700;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
@media screen and (-ms-high-contrast: active) {
code[class*="language-"],
pre[class*="language-"] {
color: windowText;
background: window;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: window;
}
.token.important {
background: highlight;
color: window;
font-weight: normal;
}
.token.atrule,
.token.attr-value,
.token.function,
.token.keyword,
.token.operator,
.token.selector {
font-weight: bold;
}
.token.attr-value,
.token.comment,
.token.doctype,
.token.function,
.token.keyword,
.token.operator,
.token.property,
.token.string {
color: highlight;
}
.token.attr-value,
.token.url {
font-weight: normal;
}
}

33
demo/test-liquid.liquid Normal file
View File

@ -0,0 +1,33 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<link rel="stylesheet" href="../test.css">
<link rel="stylesheet" href="../prism-theme.css">
</head>
<body>
{% highlight js %}
function myFunction() {
return true;
}
{% endhighlight %}
{% highlight js %}
let multilineString = `
this is the first line
this is the middle line
this is the last line
`;
{% endhighlight %}
{% highlight js 1,3 %}
let multilineString = `
this is the first line
this is the middle line
this is the last line
`;
{% endhighlight %}
</body>
</html>

62
demo/test-markdown.md Normal file
View File

@ -0,0 +1,62 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<link rel="stylesheet" href="../test.css">
<link rel="stylesheet" href="../prism-theme.css">
</head>
<body>
```ts
function myFunction() {
return true;
}
```
```typescript
function myFunction() {
return true;
}
```
```js
function myFunction() {
return true;
}
```
```js
let multilineString = `
this is the first line
this is the middle line
this is the last line
`;
```
## Dash line
```js/-
let multilineString = `
this is the first line
this is the middle line
this is the last line
`;
```
```js/1,3
let multilineString = `
this is the first line
this is the middle line
this is the last line
`;
```
## Scrollbar
```js
import { aReallyLongFunctionNameThatCouldBeLongerButThisShouldBeLongEnoughByNowHopefully as anEvenLongerFunctionNameWithMoreCharactersThanCouldBeImaginedByAnyOnePersonInThisEntireWorldOfPeopleThatOneMightKnowAtLeastThatIsWhatIsTheorizedByThisLongName } from 'wow-this-is-so-long-you-might-need-a-scrollbar-to-see-it.long-ol-file-extension-that-should-not-be-this-long-on-a-real-site-but-this-is-to-demonstrate-the-accessibility-of-tabindex-and-scrollbars.js';
```
</body>
</html>

74
demo/test-nunjucks.njk Normal file
View File

@ -0,0 +1,74 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<link rel="stylesheet" href="../test.css">
<link rel="stylesheet" href="../prism-theme.css">
</head>
<body>
{% highlight "js" %}
function myFunction() {
return true;
}
{% endhighlight %}
{% highlight "js" %}
let multilineString = `
this is the first line
this is the middle line
this is the last line
`;
{% endhighlight %}
{% highlight "js 1,3" %}
let multilineString = `
this is the first line
this is the middle line
this is the last line
`;
{% endhighlight %}
{% highlight "js 1,3" %}
alert("test");
let multilineString = buildSchema(`
this is the first line
this is the middle line
this is the last line
`);
alert("test");
{% endhighlight %}
{% highlight "js" %}
module.exports = function({collections}) {
return `<ul>
${collections.post.map((post) => `<li>${ post.data.title }</li>`).join("\n")}
</ul>`;
};
{% endhighlight %}
{% highlight "js 1,3" %}
module.exports = function({collections}) {
return `<ul>
${collections.post.map((post) => `<li>${ post.data.title }</li>`).join("\n")}
</ul>`;
};
{% endhighlight %}
{% highlight "typescript" %}
function myFunction() {
return true;
}
{% endhighlight %}
{% highlight "ts" %}
function myFunction() {
return true;
}
{% endhighlight %}
</body>
</html>

15
demo/test.css Normal file
View File

@ -0,0 +1,15 @@
.highlight-line {
display: inline-block;
}
/* allow highlighting empty lines */
.highlight-line:empty:before {
content: " ";
}
.highlight-line:not(:last-child) {
min-width: 100%;
}
.highlight-line .highlight-line:not(:last-child) {
min-width: 0;
}

57
package.json Normal file
View File

@ -0,0 +1,57 @@
{
"name": "@11ty/eleventy-plugin-syntaxhighlight",
"version": "4.2.0",
"description": "Prism.js based syntax highlighting for Markdown, Liquid, Nunjucks, and 11ty.js templates.",
"publishConfig": {
"access": "public"
},
"main": ".eleventy.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": {
"type": "git",
"url": "git+https://github.com/11ty/eleventy-plugin-syntaxhighlight.git"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/11ty"
},
"keywords": [
"eleventy",
"eleventy-plugin"
],
"author": {
"name": "Zach Leatherman",
"email": "zachleatherman@gmail.com",
"url": "https://zachleat.com/"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/11ty/eleventy-plugin-syntaxhighlight/issues"
},
"homepage": "https://www.11ty.dev/docs/plugins/syntaxhighlight/",
"11ty": {
"compatibility": ">=0.5.4"
},
"devDependencies": {
"@11ty/eleventy": "^1.0.2",
"ava": "^5.0.1",
"liquidjs": "^9.42.1",
"markdown-it": "^13.0.1"
},
"dependencies": {
"chroma-highlight": "^2.4.2",
"linkedom": "^0.14.19",
"prismjs": "^1.29.0"
},
"ava": {
"environmentVariables": {},
"failFast": false,
"files": [
"./test/*.js"
]
}
}

151
src/CharacterWrap.js Normal file
View File

@ -0,0 +1,151 @@
const HighlightPairedShortcode = require("./HighlightPairedShortcode");
const {parseHTML} = require("linkedom");
class IndexCounter {
constructor() {
this.index = 0;
}
add() {
this.index++;
}
valueOf() {
return this.index;
}
}
class CharacterWrap {
constructor() {
this.multipleCursors = false;
// array of character indeces
this.typingConfig = ["0"];
this.contentTransforms = [];
this.classPrefix = "charwrap";
}
setClassPrefix(prefix) {
this.classPrefix = prefix;
}
setMultipleCursors(useMultipleCursors) {
this.multipleCursors = !!useMultipleCursors;
}
setTypingConfigArray(typingConfig) {
this.typingConfig = typingConfig;
}
addContentTransform(callback) {
this.contentTransforms.push(callback);
}
// 0 => type to show all
// 1 => type to show from 1+
// 1,2 => type to show from 1 to 2
// 2,3 5,6 => type to show from 2 to 3 and from 5 to 6
// in other words, show 1 and 4 and 7+
getTypingConfigResults(indexCounter) {
let charIndex = indexCounter.valueOf();
let lowestIndex = 99999999;
let waitToShow = {};
let showCursor = false;
for(let cfg of this.typingConfig) {
let start, end;
cfg = "" + cfg;
if(cfg.indexOf(",") > -1) { // start,length
let split = cfg.split(",");
start = parseInt(split[0], 10);
end = split.length > 1 ? start + parseInt(split[1], 10) : (charIndex+1);
} else if(cfg.indexOf("-") > -1) { // start,end
let split = cfg.split("-");
start = parseInt(split[0], 10);
end = split.length > 1 ? parseInt(split[1], 10) : (charIndex+1);
} else {
start = parseInt(cfg, 10);
end = charIndex + 1;
}
for(let j = start+1; j < end; j++) {
waitToShow[j] = true;
}
if(this.multipleCursors && start === charIndex) {
showCursor = true;
}
lowestIndex = Math.min(lowestIndex, start);
}
if(!this.multipleCursors && lowestIndex === charIndex) {
showCursor = true;
}
return { showTyped: !waitToShow[charIndex], showCursor };
}
modifyNode(node, indexCounter) {
let classes = [this.classPrefix];
let showTyped = true;
let showCursor = false;
indexCounter.add();
let results = this.getTypingConfigResults(indexCounter);
showTyped = results.showTyped;
showCursor = results.showCursor;
if(showTyped) {
classes.push(`${this.classPrefix}-typed ${this.classPrefix}-typed-initial`);
}
if(showCursor) {
classes.push(`${this.classPrefix}-cursor ${this.classPrefix}-cursor-initial`);
}
node.className = classes.join(" ");
node.setAttribute("data-index", indexCounter.valueOf());
}
walkTree(doc, root, indexCounter = null) {
for(let node of root.childNodes) {
if(node.nodeType === 3) {
let characters = Array.from(node.textContent); // convert string to character array
for(let char of characters) {
let newTextEl = doc.createElement("span");
this.modifyNode(newTextEl, indexCounter);
newTextEl.innerHTML = char;
node.parentNode.insertBefore(newTextEl, node);
}
node.remove();
} else if(node.nodeType === 1) {
if(node.classList.contains(this.classPrefix)) {
continue;
}
if(node.nodeName === "BR") {
this.modifyNode(node, indexCounter);
} else {
this.walkTree(doc, node, indexCounter);
}
}
}
}
wrapContent(content, codeFormat) {
for(let transform of this.contentTransforms) {
let result = transform(content);
if(result === false) {
return content;
}
}
let highlightedContent = HighlightPairedShortcode(content, codeFormat, "", {
trim: false
});
let {document} = parseHTML(`<html><body>${highlightedContent}</body></html>`);
let counter = new IndexCounter();
let bodyEl = document.getElementsByTagName("body")[0];
this.walkTree(document, bodyEl, counter);
return bodyEl.innerHTML;
}
}
module.exports = CharacterWrap;

33
src/HighlightLines.js Normal file
View File

@ -0,0 +1,33 @@
class HighlightLines {
constructor(rangeStr) {
this.highlights = this.convertRangeToHash(rangeStr);
}
convertRangeToHash(rangeStr) {
let hash = {};
if( !rangeStr ) {
return hash;
}
let ranges = rangeStr.split(",").map(function(range) {
return range.trim();
});
for(let range of ranges) {
let startFinish = range.split('-');
let start = parseInt(startFinish[0], 10);
let end = parseInt(startFinish[1] || start, 10);
for( let j = start, k = end; j<=k; j++ ) {
hash[j] = true;
}
}
return hash;
}
isHighlighted(lineNumber) {
return !!this.highlights[lineNumber];
}
}
module.exports = HighlightLines;

View File

@ -0,0 +1,68 @@
const HighlightLines = require("./HighlightLines");
class HighlightLinesGroup {
constructor(str, delimiter) {
this.init(str, delimiter);
}
init(str = "", delimiter = " ") {
this.str = str;
this.delimiter = delimiter;
let split = str.split(this.delimiter);
this.highlights = new HighlightLines(split.length === 1 ? split[0] : "");
this.highlightsAdd = new HighlightLines(split.length === 2 ? split[0] : "");
this.highlightsRemove = new HighlightLines(split.length === 2 ? split[1] : "");
}
isHighlighted(lineNumber) {
return this.highlights.isHighlighted(lineNumber);
}
isHighlightedAdd(lineNumber) {
return this.highlightsAdd.isHighlighted(lineNumber);
}
isHighlightedRemove(lineNumber) {
return this.highlightsRemove.isHighlighted(lineNumber);
}
hasTagMismatch(line) {
let startCount = line.split("<span").length;
let endCount = line.split("</span").length;
if( startCount !== endCount ) {
return true;
}
return false;
}
splitLineMarkup(line, before, after) {
// skip line highlighting if there is an uneven number of <span> or </span> on the line.
// for example, we cant wrap <span> with <span><span></span>
if(this.hasTagMismatch(line)) {
return line;
}
return before + line + after;
}
getLineMarkup(lineNumber, line, extraClasses = []) {
let extraClassesStr = (extraClasses.length ? " " + extraClasses.join(" ") : "");
if (this.isHighlighted(lineNumber)) {
return this.splitLineMarkup(line, `<mark class="highlight-line highlight-line-active${extraClassesStr}">`, `</mark>`);
}
if (this.isHighlightedAdd(lineNumber)) {
return this.splitLineMarkup(line, `<ins class="highlight-line highlight-line-add${extraClassesStr}">`, `</ins>`);
}
if (this.isHighlightedRemove(lineNumber)) {
return this.splitLineMarkup(line, `<del class="highlight-line highlight-line-remove${extraClassesStr}">`, `</del>`);
}
return this.splitLineMarkup( line, `<span class="highlight-line${extraClassesStr}">`, `</span>`);
}
}
module.exports = HighlightLinesGroup;

View File

@ -0,0 +1,34 @@
const Prism = require("prismjs");
const PrismLoader = require("./PrismLoader");
const HighlightLinesGroup = require("./HighlightLinesGroup");
const getAttributes = require("./getAttributes");
module.exports = function (content, language, highlightNumbers, options = {}) {
// default to on
if(options.trim === undefined || options.trim === true) {
content = content.trim();
}
let highlightedContent;
if( language === "text" ) {
highlightedContent = content;
} else {
highlightedContent = Prism.highlight(content, PrismLoader(language), language);
}
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;
});
const context = { content: content, language: language, options: options };
const preAttributes = getAttributes(options.preAttributes, context);
const codeAttributes = getAttributes(options.codeAttributes, context);
return `<pre${preAttributes}><code${codeAttributes}>` + lines.join(options.lineSeparator || "<br>") + "</code></pre>";
};

49
src/LiquidHighlightTag.js Normal file
View File

@ -0,0 +1,49 @@
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;

42
src/PrismLoader.js Normal file
View File

@ -0,0 +1,42 @@
const Prism = require("prismjs");
const PrismLoader = require("prismjs/components/index.js");
// Avoid "Language does not exist: " console logs
PrismLoader.silent = true;
const PrismAlias = require("./PrismNormalizeAlias");
module.exports = function(language) {
let diffRemovedRawName = language;
if(language.startsWith("diff-")) {
diffRemovedRawName = language.substr("diff-".length);
}
// aliasing should ignore diff-
let aliasedName = PrismAlias(diffRemovedRawName);
if(!Prism.languages[ aliasedName ]) {
PrismLoader(aliasedName);
}
if(!Prism.languages[ aliasedName ]) {
throw new Error(`"${language}" is not a valid Prism.js language for eleventy-plugin-syntaxhighlight`);
}
if(!language.startsWith("diff-")) {
return Prism.languages[ aliasedName ];
}
// language has diff- prefix
let fullLanguageName = `diff-${aliasedName}`;
if(!Prism.languages.diff) {
PrismLoader("diff");
// Bundled Plugin
require("prismjs/plugins/diff-highlight/prism-diff-highlight");
}
// Store into with aliased keys
// ts -> diff-typescript
// js -> diff-javascript
Prism.languages[ fullLanguageName ] = Prism.languages.diff;
return Prism.languages[ fullLanguageName ];
};

View File

@ -0,0 +1,42 @@
const Prism = require("prismjs");
const HARDCODED_ALIASES = {
njk: "jinja2",
nunjucks: "jinja2",
};
// This was added to make `ts` resolve to `typescript` correctly.
// The Prism loader doesnt seem to always handle aliasing correctly.
module.exports = function(language) {
try {
// Careful this is not public API stuff:
// https://github.com/PrismJS/prism/issues/2146
const PrismComponents = require("prismjs/components.json");
let langs = PrismComponents.languages;
// Manual override
if(HARDCODED_ALIASES[language]) {
language = HARDCODED_ALIASES[language];
}
if(langs[ language ]) {
return language;
}
for(let langName in langs) {
if(Array.isArray(langs[langName].alias)) {
for(let alias of langs[langName].alias) {
if(alias === language) {
return langName;
}
}
} else if(langs[langName].alias === language) {
return langName;
}
}
} catch(e) {
// Couldnt find the components file, aliases may not resolve correctly
// See https://github.com/11ty/eleventy-plugin-syntaxhighlight/issues/19
}
return language;
}

62
src/getAttributes.js Normal file
View File

@ -0,0 +1,62 @@
function attributeEntryToString(attribute, context) {
let [key, value] = attribute;
if (typeof value === "function") { // Callback must return a string or a number
value = value(context); // Run the provided callback and store the result
}
if (typeof value !== "string" && typeof value !== "number") {
throw new Error(
`Attribute "${key}" must have, or evaluate to, a value of type string or number, not "${typeof value}".`
);
}
return `${key}="${value}"`;
}
/**
* ## Usage
* 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.
* If it is falsey, an empty string will be returned.
*
* ```js
getAttributes({
tabindex: 0,
'data-language': function (context) { return context.language; },
'data-otherStuff': 'value'
}) // => ' tabindex="0" data-language="JavaScript" data-otherStuff="value"'
```
*
* @param {{[s: string]: string | number}} attributes An object with key-value pairs that represent attributes.
* @param {object} context An object with the current context.
* @param {string} context.content The code to parse and highlight.
* @param {string} context.language The language for the current instance.
* @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 = {}) {
let langClass = context.language ? `language-${context.language}` : "";
if (!attributes) {
return langClass ? ` class="${langClass}"` : "";
} else if (typeof attributes === "object") {
if(!("class" in attributes) && langClass) {
// class attribute should be first in order
let tempAttrs = { class: langClass };
for(let key in attributes) {
tempAttrs[key] = attributes[key];
}
attributes = tempAttrs;
}
const formattedAttributes = Object.entries(attributes).map(
entry => attributeEntryToString(entry, context)
);
return formattedAttributes.length ? ` ${formattedAttributes.join(" ")}` : "";
} else if (typeof attributes === "string") {
throw new Error("Syntax highlighter plugin custom attributes on <pre> and <code> must be an object. Received: " + JSON.stringify(attributes));
}
}
module.exports = getAttributes;

13
src/hasTemplateFormat.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = function(templateFormats = ["*"], format = false) {
if(!Array.isArray(templateFormats)) {
templateFormats = [templateFormats];
}
if( Array.isArray(templateFormats) ) {
if( templateFormats.indexOf("*") > -1 || templateFormats.indexOf(format) > -1 ) {
return true;
}
}
return false;
};

View File

@ -0,0 +1,44 @@
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 {
html = Prism.highlight(str, PrismLoader(language), language);
}
let hasHighlightNumbers = split.length > 0;
let highlights = new HighlightLinesGroup(split.join("/"), "/");
let lines = html.split("\n").slice(0, -1); // The last line is empty.
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 `<pre${preAttributes}><code${codeAttributes}>${lines.join(options.lineSeparator || "<br>")}</code></pre>`;
};
};

16
syntax-highlight.webc Normal file
View File

@ -0,0 +1,16 @@
<script webc:type="render" webc:is="template" webc:raw webc:nokeep>
function() {
let errorPrefix = "[11ty/eleventy-plugin-syntaxhighlight] <syntax-highlight> WebC component: ";
if(!this.slots || !this.slots.text) {
throw new Error(errorPrefix + "requires WebC v0.6.0");
}
if(!this.helpers || !this.helpers.highlight) {
throw new Error(errorPrefix+ "missing the `highlight` helper! Did you run addPlugin on the syntax highlighter in your Eleventy config file?");
}
let content = this.slots.text.default;
return this.helpers.highlight(this.language, content);
}
</script>

View File

@ -0,0 +1,5 @@
const syntaxHighlight = require("../../");
module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(syntaxHighlight);
};

View File

@ -0,0 +1,4 @@
module.exports = function(data) {
let result = this.highlight("diff-js", "-var test;");
return result;
};

View File

@ -0,0 +1,5 @@
const syntaxHighlight = require("../../");
module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(syntaxHighlight);
};

View File

@ -0,0 +1,4 @@
module.exports = function(data) {
let result = this.highlight("js", "var test;");
return result;
};

13
test/CharacterWrapTest.js Normal file
View File

@ -0,0 +1,13 @@
const test = require("ava");
const CharacterWrap = require("../src/CharacterWrap");
test("CharacterWrap", async t => {
let wrapper = new CharacterWrap();
t.is(wrapper.wrapContent("<html></html>", "html"), `<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><span data-index="1" class="charwrap">&lt;</span></span><span data-index="2" class="charwrap">h</span><span data-index="3" class="charwrap">t</span><span data-index="4" class="charwrap">m</span><span data-index="5" class="charwrap">l</span></span><span class="token punctuation"><span data-index="6" class="charwrap">&gt;</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><span data-index="7" class="charwrap">&lt;</span><span data-index="8" class="charwrap">/</span></span><span data-index="9" class="charwrap">h</span><span data-index="10" class="charwrap">t</span><span data-index="11" class="charwrap">m</span><span data-index="12" class="charwrap">l</span></span><span class="token punctuation"><span data-index="13" class="charwrap">&gt;</span></span></span></code></pre>`);
});
test("CharacterWrap with different class prefix", async t => {
let wrapper = new CharacterWrap();
wrapper.setClassPrefix("customprefix");
t.is(wrapper.wrapContent("<html></html>", "html"), `<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><span data-index="1" class="customprefix">&lt;</span></span><span data-index="2" class="customprefix">h</span><span data-index="3" class="customprefix">t</span><span data-index="4" class="customprefix">m</span><span data-index="5" class="customprefix">l</span></span><span class="token punctuation"><span data-index="6" class="customprefix">&gt;</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><span data-index="7" class="customprefix">&lt;</span><span data-index="8" class="customprefix">/</span></span><span data-index="9" class="customprefix">h</span><span data-index="10" class="customprefix">t</span><span data-index="11" class="customprefix">m</span><span data-index="12" class="customprefix">l</span></span><span class="token punctuation"><span data-index="13" class="customprefix">&gt;</span></span></span></code></pre>`);
});

54
test/GetAttributesTest.js Normal file
View File

@ -0,0 +1,54 @@
const test = require("ava");
const ga = require("../src/getAttributes");
test("Falsy", t => {
t.is(ga(false), "");
t.is(ga(null), "");
t.is(ga(undefined), "");
t.is(ga(""), "");
t.throws(() => {
ga(" test='1'");
});
});
test("Object syntax", t => {
t.is(ga({}), "");
t.is(ga({ hi: 1 }), ' hi="1"');
t.is(ga({ hi: 1, bye: 2 }), ' hi="1" bye="2"');
t.is(ga({ class: "my-class", bye: 2 }), ' class="my-class" bye="2"');
t.is(ga({ hi: function(ctx) { return '1'; }, bye: 2 }), ' hi="1" bye="2"');
});
test("Function callback", t => {
t.is(ga({ "data-lang": ({language}) => language }, {
language: "html"
}), ' class="language-html" data-lang="html"');
});
test("Function callback with class attribute (override)", t => {
t.is(ga({
class: ({language}) => "my-custom-"+language
}, {
language: "html"
}), ' class="my-custom-html"');
});
test("Function callback must return string or integer", t => {
t.throws(() => {
ga({ "data-lang": ({language}) => undefined }, {
language: "html"
})
});
t.throws(() => {
ga({ "data-lang": ({language}) => {} }, {
language: "html"
})
});
t.throws(() => {
ga({ "data-lang": ({language}) => false }, {
language: "html"
})
});
});

View File

@ -0,0 +1,25 @@
const test = require("ava");
const hasTemplateFormat = require("../src/hasTemplateFormat");
test("hasTemplateFormats", t => {
t.true(hasTemplateFormat("*", "liquid"));
t.false(hasTemplateFormat([], "liquid"));
// options not specified, defaults to *
t.true(hasTemplateFormat(undefined, "liquid"));
t.false(hasTemplateFormat(null, "liquid"));
t.true(hasTemplateFormat("*", false));
t.false(hasTemplateFormat([], false));
// options not specified, defaults to *
t.true(hasTemplateFormat(undefined, false));
t.false(hasTemplateFormat(null, false));
t.true(hasTemplateFormat(["*"], "liquid"));
t.true(hasTemplateFormat(["liquid"], "liquid"));
t.true(hasTemplateFormat(["liquid", "njk"], "liquid"));
t.true(hasTemplateFormat(["liquid", "njk"], "njk"));
t.true(hasTemplateFormat(["liquid", "njk", "md"], "md"));
t.false(hasTemplateFormat(["liquid", "njk", "md"], "pug"));
});

View File

@ -0,0 +1,93 @@
const test = require("ava");
const HighlightLinesGroup = require("../src/HighlightLinesGroup");
test("Empty", t => {
let hilite = new HighlightLinesGroup("");
t.is(hilite.isHighlighted(0), false);
t.is(hilite.isHighlighted(1), false);
});
test("Highlight irrelevant (-)", t => {
let hilite = new HighlightLinesGroup("-");
t.is(hilite.isHighlighted(0), false);
t.is(hilite.isHighlighted(1), false);
});
test("Highlight simple (0)", t => {
let hilite = new HighlightLinesGroup("0");
t.is(hilite.isHighlighted(0), true);
t.is(hilite.isHighlighted(1), false);
});
test("Highlight simple (1)", t => {
let hilite = new HighlightLinesGroup("1");
t.is(hilite.isHighlighted(0), false);
t.is(hilite.isHighlighted(1), true);
});
test("Highlight complex", t => {
let hilite = new HighlightLinesGroup("1-2,4");
t.is(hilite.isHighlighted(0), false);
t.is(hilite.isHighlighted(1), true);
t.is(hilite.isHighlighted(2), true);
t.is(hilite.isHighlighted(3), false);
t.is(hilite.isHighlighted(4), true);
t.is(hilite.isHighlighted(5), false);
});
test("Add/Remove", t => {
let hilite = new HighlightLinesGroup("1-2,4 3");
t.is(hilite.isHighlighted(0), false);
t.is(hilite.isHighlighted(1), false);
t.is(hilite.isHighlighted(2), false);
t.is(hilite.isHighlighted(3), false);
t.is(hilite.isHighlighted(4), false);
t.is(hilite.isHighlighted(5), false);
t.is(hilite.isHighlightedAdd(0), false);
t.is(hilite.isHighlightedAdd(1), true);
t.is(hilite.isHighlightedAdd(2), true);
t.is(hilite.isHighlightedAdd(3), false);
t.is(hilite.isHighlightedAdd(4), true);
t.is(hilite.isHighlightedAdd(5), false);
t.is(hilite.isHighlightedRemove(0), false);
t.is(hilite.isHighlightedRemove(1), false);
t.is(hilite.isHighlightedRemove(2), false);
t.is(hilite.isHighlightedRemove(3), true);
t.is(hilite.isHighlightedRemove(4), false);
t.is(hilite.isHighlightedRemove(5), false);
});
test("Add/Remove New Delimiter", t => {
let hilite = new HighlightLinesGroup("1-2,4/3", "/");
t.is(hilite.isHighlighted(0), false);
t.is(hilite.isHighlighted(1), false);
t.is(hilite.isHighlighted(2), false);
t.is(hilite.isHighlighted(3), false);
t.is(hilite.isHighlighted(4), false);
t.is(hilite.isHighlighted(5), false);
t.is(hilite.isHighlightedAdd(0), false);
t.is(hilite.isHighlightedAdd(1), true);
t.is(hilite.isHighlightedAdd(2), true);
t.is(hilite.isHighlightedAdd(3), false);
t.is(hilite.isHighlightedAdd(4), true);
t.is(hilite.isHighlightedAdd(5), false);
t.is(hilite.isHighlightedRemove(0), false);
t.is(hilite.isHighlightedRemove(1), false);
t.is(hilite.isHighlightedRemove(2), false);
t.is(hilite.isHighlightedRemove(3), true);
t.is(hilite.isHighlightedRemove(4), false);
t.is(hilite.isHighlightedRemove(5), false);
});
test("Split Line Markup", t => {
let hilite = new HighlightLinesGroup("", " ");
t.is(hilite.splitLineMarkup("Test", "BEFORE", "AFTER"), "BEFORETestAFTER");
t.is(hilite.splitLineMarkup("<span>Test</span>", "BEFORE", "AFTER"), "BEFORE<span>Test</span>AFTER");
t.is(hilite.splitLineMarkup("<span>Test", "BEFORE", "AFTER"), "<span>Test");
t.is(hilite.splitLineMarkup("Test</span>", "BEFORE", "AFTER"), "Test</span>");
});

View File

@ -0,0 +1,41 @@
const test = require("ava");
const HighlightLines = require("../src/HighlightLines");
test("HighlightLines empty", t => {
let hilite = new HighlightLines("");
t.is(hilite.isHighlighted(0), false);
});
test("HighlightLines single 0", t => {
let hilite = new HighlightLines("0");
t.is(hilite.isHighlighted(0), true);
t.is(hilite.isHighlighted(1), false);
});
test("HighlightLines single 1", t => {
let hilite = new HighlightLines("1");
t.is(hilite.isHighlighted(0), false);
t.is(hilite.isHighlighted(1), true);
});
test("HighlightLines range", t => {
let hilite = new HighlightLines("1-3");
t.is(hilite.isHighlighted(0), false);
t.is(hilite.isHighlighted(1), true);
t.is(hilite.isHighlighted(2), true);
t.is(hilite.isHighlighted(3), true);
t.is(hilite.isHighlighted(4), false);
});
test("HighlightLines multiple ranges", t => {
let hilite = new HighlightLines("1-3,5-7");
t.is(hilite.isHighlighted(0), false);
t.is(hilite.isHighlighted(1), true);
t.is(hilite.isHighlighted(2), true);
t.is(hilite.isHighlighted(3), true);
t.is(hilite.isHighlighted(4), false);
t.is(hilite.isHighlighted(5), true);
t.is(hilite.isHighlighted(6), true);
t.is(hilite.isHighlighted(7), true);
t.is(hilite.isHighlighted(8), false);
});

View File

@ -0,0 +1,104 @@
const test = require("ava");
const HighlightPairedShortcode = require("../src/HighlightPairedShortcode");
test("Base", async t => {
t.is(await HighlightPairedShortcode(`alert();
alert();`, "js", "", { alwaysWrapLineHighlights: true }), `<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>`);
});
test("Base with LF EOL, always wrap highlights", async t => {
t.is(await HighlightPairedShortcode('alert();\nalert();',
"js", "", { alwaysWrapLineHighlights: true }), `<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>`);
});
test("Base with LF EOL, no wrap highlights", async t => {
t.is(await HighlightPairedShortcode('alert();\nalert();',
"js", "", { alwaysWrapLineHighlights: false }), `<pre class="language-js"><code class="language-js"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>`);
});
test("Base with CRLF EOL, always wrap highlights", async t => {
t.is(await HighlightPairedShortcode('alert();\r\nalert();',
"js", "", { alwaysWrapLineHighlights: true }), `<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>`);
});
test("Base with CRLF EOL, no wrap highlights", async t => {
t.is(await HighlightPairedShortcode('alert();\r\nalert();',
"js", "", { alwaysWrapLineHighlights: false }), `<pre class="language-js"><code class="language-js"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>`);
});
test("Base with custom attributes", async t => {
t.is(await HighlightPairedShortcode(`alert();
alert();`, "js", "", { alwaysWrapLineHighlights: true, preAttributes: { tabindex: 0, 'data-testid': 'code' } }), `<pre class="language-js" tabindex="0" data-testid="code"><code class="language-js"><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>`);
});
test("Base change the line separator", async t => {
t.is(await HighlightPairedShortcode(`alert();
alert();`, "js", "", {
alwaysWrapLineHighlights: true,
lineSeparator: "\n",
}), `<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>`);
});
test("Base No line highlights", async t => {
t.is(await HighlightPairedShortcode(`alert();
alert();`, "js", ""), `<pre class="language-js"><code class="language-js"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>`);
});
test("Highlight Active", async t => {
t.is(await HighlightPairedShortcode(`alert();
alert();`, "js", "0", { alwaysWrapLineHighlights: true }), `<pre class="language-js"><code class="language-js"><mark class="highlight-line highlight-line-active"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></mark><br><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>`);
t.is(await HighlightPairedShortcode(`alert();
alert();`, "js", "0-1", { alwaysWrapLineHighlights: true }), `<pre class="language-js"><code class="language-js"><mark class="highlight-line highlight-line-active"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></mark><br><mark class="highlight-line highlight-line-active"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></mark></code></pre>`);
});
test("Highlight Add/Remove", async t => {
t.is(await HighlightPairedShortcode(`alert();
alert();`, "js", "0 1", { alwaysWrapLineHighlights: true }), `<pre class="language-js"><code class="language-js"><ins class="highlight-line highlight-line-add"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></ins><br><del class="highlight-line highlight-line-remove"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></del></code></pre>`);
t.is(await HighlightPairedShortcode(`alert();
alert();`, "js", "1 0", { alwaysWrapLineHighlights: true }), `<pre class="language-js"><code class="language-js"><del class="highlight-line highlight-line-remove"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></del><br><ins class="highlight-line highlight-line-add"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></ins></code></pre>`);
});
test("Test loader typescript", async t => {
let script = `function greeter(person) {
return "Hello, " + person;
}
let user = "Jane User";
document.body.textContent = greeter(user);`;
t.is(await HighlightPairedShortcode(script, "typescript"), `<pre class="language-typescript"><code class="language-typescript"><span class="token keyword">function</span> <span class="token function">greeter</span><span class="token punctuation">(</span>person<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token string">"Hello, "</span> <span class="token operator">+</span> person<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">let</span> user <span class="token operator">=</span> <span class="token string">"Jane User"</span><span class="token punctuation">;</span><br><br>document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token function">greeter</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>`);
});
test("Test loader ts", async t => {
let script = `function greeter(person) {
return "Hello, " + person;
}
let user = "Jane User";
document.body.textContent = greeter(user);`
t.is(await HighlightPairedShortcode(script, "ts"), `<pre class="language-ts"><code class="language-ts"><span class="token keyword">function</span> <span class="token function">greeter</span><span class="token punctuation">(</span>person<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token string">"Hello, "</span> <span class="token operator">+</span> person<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">let</span> user <span class="token operator">=</span> <span class="token string">"Jane User"</span><span class="token punctuation">;</span><br><br>document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token function">greeter</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>`);
});
test("Test loader invalid language", async t => {
await t.throwsAsync(async () => {
await HighlightPairedShortcode("", "asldkjflksdaj");
});
});
test("Trim content option (defaults true)", async t => {
t.is(await HighlightPairedShortcode(` alert();
alert(); `, "js", "", {}), `<pre class="language-js"><code class="language-js"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>`);
t.is(await HighlightPairedShortcode(` alert();
alert(); `, "js", "", { trim: true }), `<pre class="language-js"><code class="language-js"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>`);
t.is(await HighlightPairedShortcode(` alert();
alert(); `, "js", "", { trim: false }), `<pre class="language-js"><code class="language-js"> <span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </code></pre>`);
});

View File

@ -0,0 +1,24 @@
const test = require("ava");
const Eleventy = require('@11ty/eleventy');
test("JavaScript Function", async t => {
let elev = new Eleventy("./test/11tyjs-test/", "./test/11tyjs-test/_site/", {
configPath: "./test/11tyjs-test/.eleventy.js"
});
let json = await elev.toJSON();
t.is(json.length, 1);
let rendered = json[0].content;
t.is(`<pre class="language-js"><code class="language-js"><span class="token keyword">var</span> test<span class="token punctuation">;</span></code></pre>`, rendered);
});
test("JavaScript Function Diff", async t => {
let elev = new Eleventy("./test/11tyjs-diff/", "./test/11tyjs-diff/_site/", {
configPath: "./test/11tyjs-diff/.eleventy.js"
});
let json = await elev.toJSON();
t.is(json.length, 1);
let rendered = json[0].content;
t.is(`<pre class="language-diff-js"><code class="language-diff-js"><span class="token deleted-sign deleted language-js"><span class="token prefix deleted">-</span><span class="token keyword">var</span> test<span class="token punctuation">;</span></span></code></pre>`, rendered);
});

View File

@ -0,0 +1,43 @@
const test = require("ava");
const { Liquid } = require('liquidjs');
const LiquidHighlightTag = require("../src/LiquidHighlightTag");
async function renderLiquid(str, data = {}, engine = null) {
if(!engine) {
engine = new Liquid();
}
let result = await engine.parseAndRender(str, data);
return result;
}
test("Test Render", async t => {
t.is("Hi Zach", await renderLiquid("Hi {{name}}", {name: "Zach"}));
});
test("Test Highlight Tag Render", async t => {
let engine = new Liquid();
let tag = new LiquidHighlightTag(engine);
engine.registerTag("highlight", tag.getObject());
let rendered = await renderLiquid("{% highlight js %}var test;{% endhighlight %}", {}, engine);
t.is(`<pre class="language-js"><code class="language-js"><span class="token keyword">var</span> test<span class="token punctuation">;</span></code></pre>`, rendered);
});
test("Njk Alias", async t => {
let engine = new Liquid();
let tag = new LiquidHighlightTag(engine);
engine.registerTag("highlight", tag.getObject());
let rendered = await renderLiquid("{% highlight njk %}{% raw %}hello{% endraw %}{% endhighlight %}", {}, engine);
t.is(`<pre class="language-njk"><code class="language-njk"><span class="token delimiter punctuation">{%</span> <span class="token tag keyword">raw</span> <span class="token operator">%</span><span class="token punctuation">}</span><span class="token variable">hello</span><span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">endraw</span> <span class="token delimiter punctuation">%}</span></code></pre>`, rendered);
});
test("Nunjucks alias", async t => {
let engine = new Liquid();
let tag = new LiquidHighlightTag(engine);
engine.registerTag("highlight", tag.getObject());
let rendered = await renderLiquid("{% highlight nunjucks %}{% raw %}hello{% endraw %}{% endhighlight %}", {}, engine);
t.is(`<pre class="language-nunjucks"><code class="language-nunjucks"><span class="token delimiter punctuation">{%</span> <span class="token tag keyword">raw</span> <span class="token operator">%</span><span class="token punctuation">}</span><span class="token variable">hello</span><span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">endraw</span> <span class="token delimiter punctuation">%}</span></code></pre>`, rendered);
});

View File

@ -0,0 +1,85 @@
const test = require("ava");
const md = require("markdown-it");
const markdownPrismJsOptions = require("../src/markdownSyntaxHighlightOptions");
test("Test Markdown Highlighter", t => {
let mdLib = md();
mdLib.set({
highlight: markdownPrismJsOptions({ alwaysWrapLineHighlights: true })
});
t.is(mdLib.render(`\`\`\`js
alert();
\`\`\``).trim(), `<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>`);
});
test("Test Markdown Highlighter No Line Highlights", t => {
let mdLib = md();
mdLib.set({
highlight: markdownPrismJsOptions()
});
t.is(mdLib.render(`\`\`\`js
alert();
\`\`\``).trim(), `<pre class="language-js"><code class="language-js"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>`);
});
test("Markdown with `preAttributes`", t => {
let mdLib = md();
mdLib.set({
highlight: markdownPrismJsOptions({
alwaysWrapLineHighlights: true,
preAttributes: {
// will override class="language-js"
class: ({language}) => "not-a-lang-" + language
}
})
});
t.is(mdLib.render(`\`\`\`js
alert();
\`\`\``).trim(), `<pre class="not-a-lang-js"><code class="language-js"><span class="highlight-line"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>`);
});
test("Test Njk Alias", t => {
let mdLib = md();
mdLib.set({
highlight: markdownPrismJsOptions()
});
t.is(mdLib.render(`\`\`\`njk
{% raw %}hello{% endraw %}
\`\`\``).trim(), `<pre class="language-njk"><code class="language-njk"><span class="token delimiter punctuation">{%</span> <span class="token tag keyword">raw</span> <span class="token operator">%</span><span class="token punctuation">}</span><span class="token variable">hello</span><span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">endraw</span> <span class="token operator">%</span><span class="token punctuation">}</span></code></pre>`);
});
test("Test Nunjucks Alias", t => {
let mdLib = md();
mdLib.set({
highlight: markdownPrismJsOptions()
});
t.is(mdLib.render(`\`\`\`nunjucks
{% raw %}hello{% endraw %}
\`\`\``).trim(), `<pre class="language-nunjucks"><code class="language-nunjucks"><span class="token delimiter punctuation">{%</span> <span class="token tag keyword">raw</span> <span class="token operator">%</span><span class="token punctuation">}</span><span class="token variable">hello</span><span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">endraw</span> <span class="token operator">%</span><span class="token punctuation">}</span></code></pre>`);
});
// test("Test Markdown Highlighter Block Comment", t => {
// let mdLib = md();
// mdLib.set({
// highlight: markdownPrismJsOptions({ alwaysWrapLineHighlights: true })
// });
// t.is(mdLib.render(`\`\`\`js
// /*
// * this is a string
// */
// \`\`\``).trim(), `<pre class="language-js"><code class="language-js"><span class="token comment"><span class="highlight-line">/*</span><br><span class="highlight-line"> * this is a string</span><br><span class="highlight-line"> */</span></span></code></pre>`);
// });
// TODO this still aint working right with the line highlighter.
// test("Test Markdown Highlighter GraphQL Example", t => {
// let mdLib = md();
// mdLib.set({
// highlight: markdownPrismJsOptions({ alwaysWrapLineHighlights: true })
// });
// t.is(mdLib.render(`\`\`\`js
// var schema = buildSchema(\`type Query {
// hello: String
// }\`);
// \`\`\``).trim(), ``);
// });