Skip to content

Commit 020d8c2

Browse files
committed
fix: correct jekyll highlight tag implementation
1 parent 605aa3f commit 020d8c2

File tree

27 files changed

+195
-23
lines changed

27 files changed

+195
-23
lines changed

bookshop-packages.json

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"javascript-modules/gen": {},
2121
"javascript-modules/generator-plugins/eleventy/cloudcannon-eleventy-bookshop": {},
2222
"javascript-modules/generator-plugins/eleventy/eleventy-bookshop": {},
23+
"javascript-modules/helpers": {},
2324
"javascript-modules/live": {},
2425
"javascript-modules/styles": {},
2526
"javascript-modules/toml-narrator": {}

javascript-modules/bookshop-sass/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@bookshop/sass",
3+
"packageManager": "[email protected]",
34
"version": "2.0.6",
45
"description": "Standalone sass compiler for Bookshop",
56
"type": "module",
@@ -22,6 +23,7 @@
2223
},
2324
"dependencies": {
2425
"@bookshop/builder": "2.0.6",
26+
"@bookshop/helpers": "2.0.6",
2527
"commander": "^8.1.0"
2628
},
2729
"engines": {

javascript-modules/browser/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@bookshop/browser",
3+
"packageManager": "[email protected]",
34
"version": "2.0.6",
45
"description": "Component browser for bookshop components",
56
"type": "module",
@@ -24,6 +25,7 @@
2425
},
2526
"dependencies": {
2627
"@bookshop/builder": "2.0.6",
28+
"@bookshop/helpers": "2.0.6",
2729
"@codemirror/basic-setup": "^0.18.2",
2830
"@codemirror/commands": "^0.18.3",
2931
"@codemirror/legacy-modes": "^0.18.1",

javascript-modules/builder/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@bookshop/builder",
3+
"packageManager": "[email protected]",
34
"version": "2.0.6",
45
"description": "esbuild wrapper for bringing bookshop components and engines throught to a frontend",
56
"type": "module",
@@ -18,6 +19,7 @@
1819
"nyc": "^15.1.0"
1920
},
2021
"dependencies": {
22+
"@bookshop/helpers": "2.0.6",
2123
"@bookshop/styles": "2.0.6",
2224
"esbuild": "^0.12.17",
2325
"normalize-path": "^3.0.0"

javascript-modules/cloudcannon-structures/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@bookshop/cloudcannon-structures",
3+
"packageManager": "[email protected]",
34
"version": "2.0.6",
45
"description": "Convert a Bookshop object into a CloudCannon structure",
56
"main": "main.js",

javascript-modules/engines/eleventy-engine/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@bookshop/eleventy-engine",
3+
"packageManager": "[email protected]",
34
"version": "2.0.6",
45
"description": "Bookshop frontend Eleventy renderer",
56
"type": "module",
@@ -22,6 +23,7 @@
2223
"nyc": "^15.1.0"
2324
},
2425
"dependencies": {
26+
"@bookshop/helpers": "2.0.6",
2527
"esbuild": "^0.11.23",
2628
"liquidjs": "9.28.0",
2729
"slugify": "^1.5.3"

javascript-modules/engines/jekyll-engine/lib/engine.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import translateLiquid from './translateLiquid.js';
44
/**
55
* LiquidJS plugins
66
*/
7+
import {liquidHighlight} from '@bookshop/helpers';
8+
79
import jsonify from './plugins/jsonify.js';
810
import slugify from './plugins/slugify-plugin.js';
911
import unbind from './plugins/unbind.js';
1012
import loop_context from './plugins/loop_context.js';
1113
import emulateJekyll from './plugins/emulate-jekyll.js';
1214
import local from './plugins/local.js';
13-
import highlight from './plugins/highlight.js';
15+
1416

1517
export class Engine {
1618
constructor(options) {
@@ -24,7 +26,7 @@ export class Engine {
2426
this.name = options.name;
2527
this.files = options.files;
2628
this.plugins = options.plugins || [];
27-
this.plugins.push(jsonify, slugify, unbind, emulateJekyll, local, highlight, loop_context);
29+
this.plugins.push(jsonify, slugify, unbind, emulateJekyll, local, liquidHighlight, loop_context);
2830

2931
this.initializeLiquid();
3032
this.applyLiquidPlugins();

javascript-modules/engines/jekyll-engine/lib/plugins/highlight.js

-21
This file was deleted.

javascript-modules/engines/jekyll-engine/main.test.js

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const files = {
1212
[component('include-title-deep-bind')]: "{% bookshop title bind=include.book %}",
1313
[component('uses-helper')]: "{% bookshop_include helper help=include.label %}",
1414
[shared('helper')]: "<span data-helper=\"{{include.help}}\"></span>",
15+
[shared('highlight')]: "{% highlight %}<{% endhighlight %}",
1516
}
1617
const livePost = `<!--bookshop-live end-->`
1718

@@ -72,3 +73,9 @@ test("should render bookshop_includes", async t => {
7273
const livePre = `<!--bookshop-live name(helper) params(help: include.label)-->`
7374
t.is(targetElementStub.innerHTML, `${livePre}<span data-helper=\"include-testing\"></span>${livePost}`);
7475
});
76+
77+
test("should support highlight tag", async t => {
78+
const targetElementStub = {};
79+
await je.render(targetElementStub, "highlight");
80+
t.regex(targetElementStub.innerHTML, /&lt;/);
81+
});

javascript-modules/engines/jekyll-engine/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@bookshop/jekyll-engine",
3+
"packageManager": "[email protected]",
34
"version": "2.0.6",
45
"description": "Bookshop frontend Jekyll renderer",
56
"type": "module",
@@ -22,6 +23,7 @@
2223
"nyc": "^15.1.0"
2324
},
2425
"dependencies": {
26+
"@bookshop/helpers": "2.0.6",
2527
"esbuild": "^0.11.23",
2628
"js-base64": "^3.6.1",
2729
"liquidjs": "9.28.0",

javascript-modules/engines/svelte-engine/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@bookshop/svelte-engine",
3+
"packageManager": "[email protected]",
34
"version": "2.0.6",
45
"description": "Bookshop frontend Svelte renderer",
56
"type": "module",
@@ -19,6 +20,7 @@
1920
},
2021
"devDependencies": {
2122
"@bookshop/builder": "2.0.6",
23+
"@bookshop/helpers": "2.0.6",
2224
"ava": "^3.15.0",
2325
"nyc": "^15.1.0"
2426
},

javascript-modules/gen/.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
engine-strict = true

javascript-modules/gen/package.json

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@bookshop/gen",
3+
"packageManager": "[email protected]",
34
"version": "2.0.6",
45
"description": "",
56
"main": "src/index.js",
@@ -20,7 +21,11 @@
2021
"access": "public"
2122
},
2223
"dependencies": {
24+
"@bookshop/helpers": "2.0.6",
2325
"ejs": "^3.1.6",
2426
"yargs": "^17.2.1"
27+
},
28+
"engines": {
29+
"node": ">=14.16"
2530
}
2631
}

javascript-modules/generator-plugins/eleventy/cloudcannon-eleventy-bookshop/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@bookshop/cloudcannon-eleventy-bookshop",
3+
"packageManager": "[email protected]",
34
"version": "2.0.6",
45
"description": "Eleventy plugin for generating CloudCannon Array Structures from Bookshop components",
56
"main": "main.js",
@@ -17,6 +18,7 @@
1718
},
1819
"dependencies": {
1920
"@bookshop/cloudcannon-structures": "2.0.6",
21+
"@bookshop/helpers": "2.0.6",
2022
"@bookshop/toml-narrator": "2.0.6",
2123
"@ltd/j-toml": "^1.17.0",
2224
"fast-glob": "^3.2.7",

javascript-modules/generator-plugins/eleventy/eleventy-bookshop/package.json

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@bookshop/eleventy-bookshop",
3+
"packageManager": "[email protected]",
34
"version": "2.0.6",
45
"description": "Eleventy plugin for consuming Bookshop components",
56
"main": "main.js",
@@ -15,6 +16,9 @@
1516
"ava": "^3.15.0",
1617
"nyc": "^15.1.0"
1718
},
19+
"dependencies": {
20+
"@bookshop/helpers": "2.0.6"
21+
},
1822
"engines": {
1923
"node": ">=14.16"
2024
}

javascript-modules/helpers/.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
engine-strict = true

javascript-modules/helpers/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Bookshop helpers
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Rough skeleton for the highlight tag in Jekyll.
3+
* This won't perform any code highlighting, but should create the same outer DOM structure.
4+
* This should also escape any HTML within.
5+
*/
6+
7+
const escapeHtml = (input) => {
8+
return input
9+
.replace(/&/g, "&amp;")
10+
.replace(/</g, "&lt;")
11+
.replace(/>/g, "&gt;")
12+
.replace(/"/g, "&quot;")
13+
.replace(/'/g, "&#039;");
14+
}
15+
16+
export const liquidHighlight = function (Liquid) {
17+
this.registerTag('highlight', {
18+
parse: function(token, remainingTokens) {
19+
this.lang = token.args.split(' ')[0];
20+
this.contents = [];
21+
22+
const stream = this.liquid.parser.parseStream(remainingTokens)
23+
.on('tag:endhighlight', () => stream.stop())
24+
.on('template', (tpl) => this.contents.push(tpl))
25+
.on('end', () => {
26+
throw new Error(`tag ${token.raw} not closed`)
27+
});
28+
29+
stream.start();
30+
},
31+
render: function*(ctx, hash) {
32+
const r = this.liquid.renderer;
33+
const html = yield r.renderTemplates(this.contents, ctx);
34+
const langAttrs = this.lang
35+
? ` class="language-${this.lang}" data-lang="${this.lang}"`
36+
: '';
37+
return `<figure class="highlight">
38+
<pre>
39+
<code${langAttrs}>
40+
${escapeHtml(html)}
41+
</code>
42+
</pre>
43+
</figure>`;
44+
}
45+
});
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import test from 'ava';
2+
import { Liquid } from 'liquidjs';
3+
import { liquidHighlight } from './highlight.js';
4+
5+
test("should escape HTML", async t => {
6+
const liquid = new Liquid({});
7+
liquid.plugin(liquidHighlight);
8+
9+
const result = await liquid.parseAndRender(`
10+
{%- highlight -%}
11+
<h1>Hello World</h1>
12+
{%- endhighlight -%}
13+
`);
14+
t.notRegex(result, /<h1>Hello World<\/h1>/);
15+
t.regex(result, /&lt;h1&gt;/);
16+
});
17+
18+
test("should evaluate inner liquid", async t => {
19+
const liquid = new Liquid({});
20+
liquid.plugin(liquidHighlight);
21+
22+
const result = await liquid.parseAndRender(`
23+
{%- highlight -%}
24+
<h1>Hello {{ test }}</h1>
25+
{%- endhighlight -%}
26+
`, {test: "Bookshop"});
27+
t.regex(result, /&lt;h1&gt;Hello Bookshop&lt;\/h1&gt;/);
28+
});
29+
30+
test("should respect raw tags", async t => {
31+
const liquid = new Liquid({});
32+
liquid.plugin(liquidHighlight);
33+
34+
const result = await liquid.parseAndRender(`
35+
{%- highlight -%}
36+
{% raw %}<h1>Hello {{ test }}</h1>{% endraw %}
37+
{%- endhighlight -%}
38+
`, {test: "Bookshop"});
39+
t.regex(result, /&lt;h1&gt;Hello {{ test }}&lt;\/h1&gt;/);
40+
});
41+
42+
test("should handle no language", async t => {
43+
const liquid = new Liquid({});
44+
liquid.plugin(liquidHighlight);
45+
46+
const result = await liquid.parseAndRender(`{% highlight %}{% endhighlight %}`);
47+
t.regex(result, /<code>/);
48+
});
49+
50+
test("should handle language", async t => {
51+
const liquid = new Liquid({});
52+
liquid.plugin(liquidHighlight);
53+
54+
const result = await liquid.parseAndRender(`{% highlight ruby %}{% endhighlight %}`);
55+
t.regex(result, /<code class="language-ruby" data-lang="ruby">/);
56+
});

javascript-modules/helpers/main.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {liquidHighlight} from './lib/plugins/liquid/highlight.js';
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "@bookshop/helpers",
3+
"packageManager": "[email protected]",
4+
"version": "2.0.6",
5+
"description": "",
6+
"type": "module",
7+
"main": "main.js",
8+
"scripts": {
9+
"test": "nyc ava -v"
10+
},
11+
"author": "CloudCannon",
12+
"license": "MIT",
13+
"publishConfig": {
14+
"access": "public"
15+
},
16+
"devDependencies": {
17+
"ava": "^3.15.0",
18+
"liquidjs": "9.28.0",
19+
"nyc": "^15.1.0"
20+
},
21+
"engines": {
22+
"node": ">=14.16"
23+
}
24+
}

javascript-modules/integration-tests/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "integration-tests",
3+
"packageManager": "[email protected]",
34
"private": true,
45
"main": "index.js",
56
"scripts": {

javascript-modules/live/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "@bookshop/live",
3+
"packageManager": "[email protected]",
34
"version": "2.0.6",
45
"description": "Live component renderer for editing from a CMS",
56
"type": "module",
@@ -22,6 +23,7 @@
2223
},
2324
"dependencies": {
2425
"@bookshop/builder": "2.0.6",
26+
"@bookshop/helpers": "2.0.6",
2527
"commander": "^8.1.0"
2628
},
2729
"engines": {

0 commit comments

Comments
 (0)