Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions lib/esm-bridge.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict';

const pathFn = require('path');
const { pathToFileURL } = require('url');

// Hexo runs files under scripts/ as CJS text in a vm sandbox, where ESM syntax
// and direct dynamic import() are unavailable (hexojs/hexo#5525). Each script is
// therefore a one-line CJS stub calling this bridge, which lives outside the vm
// and can import() the real ESM module from lib/. Delete the stubs and this file
// once hexo loads ESM scripts natively (hexojs/hexo#5820).
exports.load = function load(hexo, relToLib) {
const url = pathToFileURL(pathFn.join(__dirname, relToLib));
return import(url.href).then(m => m.default(hexo));
};
39 changes: 39 additions & 0 deletions lib/filters/after_post_render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import pathFn from 'path';

export default function register(hexo) {
function image_version(oldPath, { thumbProfile = 'body', hugeProfile = 'huge' } = {}) {
const dir = pathFn.dirname(oldPath);
let base = pathFn.basename(oldPath);

// if base already starts with a prefix
for (const key in hexo.config.responsive_images.sizes) {
if (base.startsWith(key + '_')) {
thumbProfile = key;
base = base.slice(key.length + 1);
break;
}
}

if (dir === '.') {
return {
thumb: thumbProfile + '_' + base,
huge: hugeProfile + '_' + base,
};
}
return {
thumb: dir + '/' + thumbProfile + '_' + base,
huge: dir + '/' + hugeProfile + '_' + base,
};
}

function mediumZoomFilter(post) {
post.content = post.content.replace(/(<img[^>]*?) src="([^"]+)"/img, (match, p1, p2) => {
hexo.log.info('Responsive image', p2);
const { thumb, huge } = image_version(p2);
return `${p1} data-zoom-src="${huge}" src="${thumb}"`;
});
}

// the priority must before the next theme's img lazy load filter's 0
hexo.extend.filter.register('after_post_render', mediumZoomFilter, -10);
}
23 changes: 23 additions & 0 deletions lib/tags/open_sidebar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import _ from 'lodash';

/**
* A link to open sidebar. Basically only adds open-sidebar class to the tag.
*
* Syntax:
* {% open_sidebar text [, title] %}
*/
export default function register(hexo) {
function openSidebarTag(args) {
var [text, title] = args.join(' ').split(',');
if (!text) return;

if (title) {
title = _.trim(title);
title = `title="${title}"`;
}
// the href has to be a fragment, so it's pjax safe
return `<a href="#" class="open-sidebar" ${title} >${text}</a>`;
}

hexo.extend.tag.register('open_sidebar', openSidebarTag, { ends: false });
}
39 changes: 39 additions & 0 deletions lib/tags/post_link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { htmlTag } from 'hexo-util';

/**
* Post link tag
*
* Syntax:
* {% post_link slug[#fragment] [title] %}
*/
export default function register(hexo) {
const Post = hexo.model('Post');

function postLinkTag(args) {
let slug = args.shift();

if (!slug) return;

let frag = '';
[slug, frag] = slug.split('#');
if (!frag) {
frag = '';
}

if (frag.length > 0) {
frag = '#' + frag;
}

let post = Post.findOne({ slug });
if (!post) return;

let title = args.length ? args.join(' ') : post.title;

return htmlTag('a', {
href: hexo.config.root + post.path + frag,
title: title
}, title);
}

hexo.extend.tag.register('post_link', postLinkTag, { ends: false });
}
40 changes: 40 additions & 0 deletions lib/tags/random_xkcd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import util from 'util';
import crypto from 'crypto';

import xkcdApi from 'xkcd-api';

const xkcd_get = util.promisify(xkcdApi.get);

export default function register(hexo) {
async function randomXkcd() {
// Hexo >= 7 invokes tags with the nunjucks render context as `this`, which has no
// `site`. Go through hexo's locals instead, which works on every version.
const comics = hexo.locals.get('data').xkcd || [];
const infos = await Promise.all(comics.map(c => xkcd_get(c)));
// build a JSON list with less info to include in the page
const list = infos.map(el => ({
num: el.num,
alt: el.alt,
title: el.title,
img: el.img
}));

const tag_id = crypto.randomBytes(4).toString('hex');
return `<a id="${tag_id}" ref="external" target="_blank"><img data-proofer-ignore/></a>
<script type="text/javascript" data-pjax>
(() => {
window.xkcd_infos = window.xkcd_infos || ${JSON.stringify(list)};
const thecomic = xkcd_infos[Math.floor(Math.random() * xkcd_infos.length)];
const atag = document.getElementById("${tag_id}");
if (atag) {
atag.setAttribute('href', 'https://xkcd.com/' + thecomic.num);
atag.firstChild.setAttribute('src', thecomic.img);
atag.firstChild.setAttribute('alt', thecomic.title);
atag.firstChild.setAttribute('title', thecomic.alt);
}
})();
</script>`;
}

hexo.extend.tag.register('random_xkcd', randomXkcd, { ends: false, async: true });
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "hexo-site",
"version": "0.0.0",
"private": true,
"type": "module",
"hexo": {
"version": "8.1.2"
},
Expand Down
45 changes: 2 additions & 43 deletions scripts/filters/after_post_render.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,3 @@
'use strict';

const pathFn = require('path');

function image_version(oldPath, { thumbProfile = 'body', hugeProfile = 'huge' } = {}) {
const dir = pathFn.dirname(oldPath);
let base = pathFn.basename(oldPath);

// if base already starts with a prefix
for (const key in hexo.config.responsive_images.sizes) {
if (base.startsWith(key + '_')) {
thumbProfile = key;
base = base.slice(key.length + 1);
break;
}
}

if (dir === '.') {
return {
thumb: thumbProfile + '_' + base,
huge: hugeProfile + '_' + base,
};
}
return {
thumb: dir + '/' + thumbProfile + '_' + base,
huge: dir + '/' + hugeProfile + '_' + base,
};
}

function mediumZoomFilter(post) {
const content = post.content;
post.content = post.content.replace(/(<img[^>]*?) src="([^"]+)"/img, (match, p1, p2) => {
hexo.log.info('Responsive image', p2);
const { thumb, huge } = image_version(p2);
return `${p1} data-zoom-src="${huge}" src="${thumb}"`;
});

if (content.includes('img')) {
// console.log('after render', content);
}
}

// the priority must before the next theme's img lazy load filter's 0
hexo.extend.filter.register('after_post_render', mediumZoomFilter, -10);
// CJS stub: hexo runs scripts/ files as CJS in a vm; real logic is ESM in lib/. See lib/esm-bridge.cjs.
await require('../../lib/esm-bridge.cjs').load(hexo, 'filters/after_post_render.js');
24 changes: 2 additions & 22 deletions scripts/tags/open_sidebar.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,3 @@
'use strict';

const _ = require('lodash');

/**
* A link to open sidebar. Basically only adds open-sidebar class to the tag.
*
* Syntax:
* {% open_sidebar text [, title] %}
*/
function openSidebarTag(args) {
var [text, title] = args.join(' ').split(',');
if (!text) return;

if (title) {
title = _.trim(title);
title = `title="${title}"`;
}
// the href has to be a fragment, so it's pjax safe
return `<a href="#" class="open-sidebar" ${title} >${text}</a>`;
};

hexo.extend.tag.register('open_sidebar', openSidebarTag, { ends: false });
// CJS stub: hexo runs scripts/ files as CJS in a vm; real logic is ESM in lib/. See lib/esm-bridge.cjs.
await require('../../lib/esm-bridge.cjs').load(hexo, 'tags/open_sidebar.js');
39 changes: 2 additions & 37 deletions scripts/tags/post_link.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,3 @@
'use strict';

const { htmlTag } = require('hexo-util');
const Post = hexo.model('Post');

/**
* Post link tag
*
* Syntax:
* {% post_link slug[#fragment] [title] %}
*/
function postLinkTag(args) {
let slug = args.shift();

if (!slug) return;

let frag = '';
[slug, frag] = slug.split('#');
if (!frag) {
frag = '';
}

if (frag.length > 0) {
frag = '#' + frag;
}

let post = Post.findOne({ slug });
if (!post) return;

let title = args.length ? args.join(' ') : post.title;

return htmlTag('a', {
href: hexo.config.root + post.path + frag,
title: title
}, title);
};

hexo.extend.tag.register('post_link', postLinkTag, { ends: false });
// CJS stub: hexo runs scripts/ files as CJS in a vm; real logic is ESM in lib/. See lib/esm-bridge.cjs.
await require('../../lib/esm-bridge.cjs').load(hexo, 'tags/post_link.js');
39 changes: 2 additions & 37 deletions scripts/tags/random_xkcd.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,3 @@
'use strict';

const util = require('util');
const crypto = require('crypto');

const xkcd_get = util.promisify(require('xkcd-api').get);

async function randomXkcd() {
// Hexo >= 7 invokes tags with the nunjucks render context as `this`, which has no
// `site`. Go through hexo's locals instead, which works on every version.
const comics = hexo.locals.get('data').xkcd || [];
const infos = await Promise.all(comics.map(c => xkcd_get(c)));
// build a JSON list with less info to include in the page
const list = infos.map(el => ({
num: el.num,
alt: el.alt,
title: el.title,
img: el.img
}));

const tag_id = crypto.randomBytes(4).toString('hex');
return `<a id="${tag_id}" ref="external" target="_blank"><img data-proofer-ignore/></a>
<script type="text/javascript" data-pjax>
(() => {
window.xkcd_infos = window.xkcd_infos || ${JSON.stringify(list)};
const thecomic = xkcd_infos[Math.floor(Math.random() * xkcd_infos.length)];
const atag = document.getElementById("${tag_id}");
if (atag) {
atag.setAttribute('href', 'https://xkcd.com/' + thecomic.num);
atag.firstChild.setAttribute('src', thecomic.img);
atag.firstChild.setAttribute('alt', thecomic.title);
atag.firstChild.setAttribute('title', thecomic.alt);
}
})();
</script>`;
}

hexo.extend.tag.register('random_xkcd', randomXkcd, { ends: false, async: true });
// CJS stub: hexo runs scripts/ files as CJS in a vm; real logic is ESM in lib/. See lib/esm-bridge.cjs.
await require('../../lib/esm-bridge.cjs').load(hexo, 'tags/random_xkcd.js');
25 changes: 12 additions & 13 deletions tests/helpers/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const fs = require('fs/promises');
const pathFn = require('path');
const { JSDOM } = require('jsdom');
import fs from 'fs/promises';
import pathFn from 'path';
import { fileURLToPath } from 'url';

import { JSDOM } from 'jsdom';

const __dirname = pathFn.dirname(fileURLToPath(import.meta.url));
const PUBLIC_DIR = pathFn.resolve(__dirname, "..", "..", "public");

async function recursiveRoutes(basedir, prefixUrl) {
Expand All @@ -18,27 +21,23 @@ async function recursiveRoutes(basedir, prefixUrl) {
return fileRoutes.concat(subRoutes);
}

async function listRoutes() {
export async function listRoutes() {
return await recursiveRoutes(pathFn.join(PUBLIC_DIR, 'blog'), '/');
}

async function getRoute(path) {
export async function getRoute(path) {
return await JSDOM.fromFile(pathFn.join(PUBLIC_DIR, path));
}

async function getRouteFile(path) {
export async function getRouteFile(path) {
return await fs.readFile(pathFn.join(PUBLIC_DIR, path), { encoding: 'utf-8' });
}

async function getHexo(level) {
const Hexo = require('hexo');
export async function getHexo(level) {
// import lazily so tests not using hexo don't pay for loading it
const { default: Hexo } = await import('hexo');
const hexo = new Hexo();
hexo.log.level = level || 40 // WARN;
await hexo.init();
return hexo;
}

module.exports.getRoute = getRoute;
module.exports.getRouteFile = getRouteFile;
module.exports.getHexo = getHexo;
module.exports.listRoutes = listRoutes;
8 changes: 5 additions & 3 deletions tests/math-rendering.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const test = require('ava');
const fs = require('fs/promises');
const { getHexo } = require('./helpers');
import fs from 'fs/promises';

import test from 'ava';

import { getHexo } from './helpers/index.js';

const saveHtml = false;

Expand Down
5 changes: 3 additions & 2 deletions tests/ocs_site_verification.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const test = require('ava');
const { getRoute } = require('./helpers');
import test from 'ava';

import { getRoute } from './helpers/index.js';

test('home page contains OCS site verification', async t => {
const dom = await getRoute('index.html');
Expand Down
Loading
Loading