From 53130be2ea6e881aee648aa08bee7af85539701a Mon Sep 17 00:00:00 2001 From: Chen Yangjian <252317+cyjake@users.noreply.github.com> Date: Tue, 11 Nov 2025 20:57:54 +0800 Subject: [PATCH] feat: upgrade liquid template engine, support `link` tag --- lib/liquid/FileSystem.js | 6 ++-- lib/liquid/index.js | 59 +++++++++++++++++++++++++++++----------- lib/parsers/sass.js | 0 lib/writers/asset.js | 28 +++++++++++++------ lib/writers/sass.js | 39 ++++++++++++++++++++++++++ lib/writers/templated.js | 45 ++++++++++++------------------ package.json | 3 +- 7 files changed, 124 insertions(+), 56 deletions(-) create mode 100644 lib/parsers/sass.js create mode 100644 lib/writers/sass.js diff --git a/lib/liquid/FileSystem.js b/lib/liquid/FileSystem.js index feea1fe..a2dafbc 100644 --- a/lib/liquid/FileSystem.js +++ b/lib/liquid/FileSystem.js @@ -1,18 +1,18 @@ 'use strict' -const Liquid = require('liquid-node') +const Liquid = require('liquid') const fs = require('fs') const path = require('path') const { readFile } = fs.promises -const PathPattern = /^[^.\/][a-zA-Z0-9-_\/]+$/ +const PathPattern = /^[^.\/][a-zA-Z0-9-_\/]+(?:\.\w+)?$/ class FileSystem extends Liquid.BlankFileSystem { constructor(root, encoding = 'utf-8', ...extensions) { super(root, encoding, ...extensions) this.root = root this.encoding = encoding - this.extensions = extensions + this.extensions = Array.from(new Set([''].concat(extensions))) } readTemplateFile(templatePath) { diff --git a/lib/liquid/index.js b/lib/liquid/index.js index b36dec9..64e0c10 100644 --- a/lib/liquid/index.js +++ b/lib/liquid/index.js @@ -1,37 +1,64 @@ 'use strict' -const Liquid = require('liquid-node') +const Liquid = require('liquid') const md = require('../markdown') const highlight = require('../highlight') const engine = new Liquid.Engine() -engine.registerTag('highlight', () => { - return class HighlightBlock extends Liquid.Block { - render(context) { - return highlight.render(this.nodelist.join('').trim(), this.markup) - } +engine.registerTag('highlight', class HighlightBlock extends Liquid.Block { + render(context) { + return highlight.render(this.nodelist.join('').trim(), this.markup) } }) -engine.registerTag('post_url', () => { - return class PostUrl extends Liquid.Tag { - constructor(template, tagName, markup) { - super(template, tagName, markup) - this.postPath = markup.trim() +engine.registerTag('post_url', class PostUrl extends Liquid.Tag { + constructor(template, tagName, markup) { + super(template, tagName, markup) + this.postPath = markup.trim() + } + + render(context) { + for (const post of engine.site.posts) { + if (post.path.split('_posts/').pop().replace(/\.\w+/, '') === this.postPath) { + return post.url + } } + } +}) - render(context) { - for (const post of engine.site.posts) { - if (post.path.split('_posts/').pop().replace(/\.\w+/, '') === this.postPath) { - return post.url - } +engine.registerTag('link', class LinkTag extends Liquid.Tag { + constructor(template, tagName, markup) { + super(template, tagName, markup) + this.linkPath = markup.trim() + } + + render(context) { + for (const page of [...engine.site.pages, ...engine.site.posts]) { + if (page.path === this.linkPath) { + return page.url } } } }) +engine.registerTag('feed_meta', class FeedMetaTag extends Liquid.Tag { + constructor(template, tagName) { + super(template, tagName) + this.markup = ''; + } + + async render(context) { + const site = engine.site; + const subTemplate = await this.template.engine.parse(this.markup); + return subTemplate.render({ + title: site.config.title, + url: new URL(site.feed.path, site.url).toString(), + }); + } +}) + engine.registerFilters({ date_to_xmlschema(input) { return this.date(input, '%Y-%m-%dT%H:%M:%S%z').replace(/00$/, ':00') diff --git a/lib/parsers/sass.js b/lib/parsers/sass.js new file mode 100644 index 0000000..e69de29 diff --git a/lib/writers/asset.js b/lib/writers/asset.js index fba9f4c..047a7eb 100644 --- a/lib/writers/asset.js +++ b/lib/writers/asset.js @@ -1,10 +1,10 @@ 'use strict' -var path = require('path') -var fs = require('fs') -var mkdirp = require('mkdirp') -var debug = require('debug')('darko') - +const path = require('path') +const fs = require('fs') +const mkdirp = require('mkdirp') +const debug = require('debug')('darko') +const writeSass = require('./sass') function mkdirpAsync(dir) { return new Promise(function(resolve, reject) { @@ -16,10 +16,20 @@ function mkdirpAsync(dir) { } module.exports = function writeAsset(asset) { - var site = asset.site - var rpath = asset.path - var fpath = path.join(site.cwd, rpath) - var dest = path.resolve(site.dest, site.baseurl.slice(1), rpath) + switch (path.extname(asset.path).toLowerCase()) { + case '.scss': + case '.sass': + return writeSass(asset) + default: + return copyFile(asset) + } +} + +function copyFile(asset) { + const site = asset.site + const rpath = asset.path + const fpath = path.join(site.cwd, rpath) + const dest = path.resolve(site.dest, site.baseurl.slice(1), rpath) debug('Coping file ' + rpath) diff --git a/lib/writers/sass.js b/lib/writers/sass.js new file mode 100644 index 0000000..763d322 --- /dev/null +++ b/lib/writers/sass.js @@ -0,0 +1,39 @@ +'use strict'; + +const fs = require('fs').promises; +const path = require('path'); +const sass = require('sass'); +const { pathToFileURL } = require('url'); +const debug = require('debug')('darko'); + +module.exports = async function writeSass(asset) { + const { site } = asset; + const fpath = path.join(site.cwd, asset.path); + const dest = path.resolve(site.dest, site.baseurl.slice(1), asset.path.replace(/\.(scss|sass)$/, '.css')); + + debug('Processing SASS file ' + asset.path); + + await fs.mkdir(path.dirname(dest), { recursive: true }); + + const source = await fs.readFile(fpath, site.encoding); + const parts = source?.split('---'); + if (!parts || parts.length < 2) return; + const result = await sass.compileStringAsync(parts.slice(2).join('---'), { + importers: [{ + findFileUrl(url) { + if (url.startsWith('.')) return null; + return new URL(url, pathToFileURL(path.join(site.cwd, '_sass/') )); + }, + }], + syntax: path.extname(fpath).toLowerCase() === '.sass' ? 'indented' : 'scss', + sourceMap: true, + sourceMapIncludeSources: true, + url: pathToFileURL(fpath), + }); + await Promise.all([ + fs.writeFile(dest, result.css, 'utf-8'), + fs.writeFile(dest + '.map', JSON.stringify(result.sourceMap), 'utf-8'), + ]); + + debug('Written CSS file ' + asset.path.replace(/\.(scss|sass)$/, '.css')); +} diff --git a/lib/writers/templated.js b/lib/writers/templated.js index 82ebc72..46ee081 100644 --- a/lib/writers/templated.js +++ b/lib/writers/templated.js @@ -38,25 +38,21 @@ function markup(page) { var _layouts = {} -function layout(page) { - if (!page.layoutWas) page.layoutWas = page.layout - if (!page.layout || page.layout == 'nil' || page.layout_ == page.layout) { - // Finished layout renderring, restore the original layout of current page - page.layout = page.layoutWas - page.layout_ = '' +async function layout(page, data) { + const { layout: layoutName } = data || page; + if (!layoutName || layoutName == 'nil') { return page } - page.layout_ = page.layout - var site = page.site + const site = page.site // support .html layouts only, for now. - var lpath = path.join(site.cwd, '_layouts', page.layout) + '.html' - var lcache = _layouts[lpath] + const lpath = path.join(site.cwd, '_layouts', layoutName) + '.html' + let lcache = _layouts[lpath] if (!lcache) { - var lsource = fs.readFileSync(lpath, site.encoding) - var parts = lsource.split('---') - var matter + let lsource = fs.readFileSync(lpath, site.encoding) + const parts = lsource.split('---') + let matter if (parts.length >= 3) { matter = yaml.load(parts[1]) @@ -69,20 +65,15 @@ function layout(page) { } } - return lcache.promise - .then(function(template) { - return template.render({ - site: site, - page: page, - content: page.output - }) - }) - .then(function(res) { - page.output = res - if (lcache.matter) Object.assign(page, lcache.matter) - - return layout(page) - }) + const template = await lcache.promise + const res = await template.render({ + site: site, + page: page, + content: page.output + }) + page.output = res + if (lcache.matter) return layout(page, lcache.matter) + return page } function render(page) { diff --git a/package.json b/package.json index 41040f0..945e24e 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,12 @@ "commander": "^2.19.0", "debug": "^4.1.0", "highlight.js": "^11.11.1", - "liquid-node": "^3.0.0", + "liquid": "^5.1.1", "markit": "~0.1.0", "mime-types": "^3.0.1", "mkdirp": "~0.3.5", "rimraf": "^2.6.2", + "sass": "^1.94.0", "strftime": "~0.9.0", "yaml-js": "^0.2.3" },