从 PrismJS 到 Better PrismJS 再到 MicroLighter:三步无痛迁移记
一个极好的特点十一是@11ty/eleventy-plugin-syntaxhighlight插件,通过 PrismJS 增加了构建时语法高亮支持。官方支持让110个网站无需担心基于JS的高亮导致客户端臃肿,以及常见的过度包含语言定义的失败模式。
但没有什么是完美的。
目录
官方插件自然集成,我开始用通常的三回溯标记语法使用它:
# The Usual Markdown Nonsense { #the-usual-nonsense }
And now for something entirely different:
```js
function yourCodeHere() { /* ... */ }
```
Etc., etc.这方法非常有效,直到你想要更多控制权。那时,看似神奇的东西开始变得束缚。
最近的目标是增加行号。
读了部分提到PrismJS的插件代码后,我沮丧地合上了笔记本,无法劝说eleventy-plugin-syntaxhighlight输入行号。几天后离开键盘终于有了回报,因为我回想起配对短码路径生成的稳定每行分隔符。
这允许重用插件的主逻辑,并包裹着字符串变换以添加必要的样式钩子:
// From this site's Eleventy config file
// Because my esm conversion is still partial:
const { default: syntaxHighlight } =
await import("@11ty/eleventy-plugin-syntaxhighlight");
// ...
module.exports = async function(config) {
// ...
let syntaxHighlightOptions = {
// For faster CSS selectors
preAttributes: { highlighted: "highlighted" },
codeAttributes: { highlighted: "highlighted" },
};
config.addPlugin(syntaxHighlight,
syntaxHighlightOptions);
config.addPairedShortcode("code",
function(content,
language="",
highlightLines="") {
// The design of cssMin and addToBundle are outside
// the scope of this post.
//
// Suffice to say, we're inlining the contents
// of the file path passed as an argument to it.
//
addToBundle(
this,
"css",
``);
let xformed = syntaxHighlight.pairedShortcode.call(
this,
content,
language,
highlightLines,
syntaxHighlightOptions
);
// Wrap each line in `...`
let _preamble = "";
let _end = "
";
let _content = "";
// Preamble and postfix might be different than
// defaults above, so preserve them.
let preambleEnd =
xformed.indexOf(">",
xformed.indexOf("");
_content =
xformed.substring(preambleEnd, contentEnd);
let parts = _content.split("
");
// Wrap each line in a stylable
_content = parts.map((line) => {
return `${line}`;
}).join(`
`);
return _preamble + _content + _end;
});
// ...
};
再加上一些CSS来帮我们算数,就是这样!,行号:
pre[highlighted] {
display: block;
overflow-x: auto;
isolation: isolate;
max-width: 100%;
contain: content;
& > code {
width: 100%;
text-wrap-mode: wrap;
counter-reset: step;
counter-increment: step 0;
& .line::before {
content: counter(step);
counter-increment: step;
width: 1.5rem;
padding-right: 0.5rem;
margin-right: 0.5rem;
display: inline-block;
text-align: right;
color: var(--color-mid);
border-right: 1px solid currentColor;
}
}
}如果JavaScript方法看起来很熟悉,可能是因为它的技术去年我将11ty Bundler作为穷人路线代码分拆器投入使用时,已经概述了这一点允许每个需要JavaScript的短代码只懒惰地包含包含这些组件的页面:
// An `.eleventy.js` configuration file
module.exports = async function(config) {
// Register some bundles that templates output.
config.addBundle("js");
// etc., ...
// Needed to avoid Markdown double-processing.
let removeNewlines = function(str) {
return str.replace(/[\n\r]/g, "");
}
// Invoke shortcodes that `config.addBundle()`
// generates from within *other* shortcodes,
// allowing us to use the Bundler for duplicate code
// removal and hoisting dependencies to the optimal
// place in output templates.
function addToBundle(scope, bundle, code) {
config.getPairedShortcode(bundle).
call(scope, removeNewlines(code));
}
config.addShortcode("someblock",
function(/* ... */) {
addToBundle(this, "js", `
`);
// ...
});
// ...
};11ty语法高亮技巧之所以有效,是因为调用输出时总有分隔符,通过HighlightPairedShortcode.虽然脆弱,但确实有效。通过Nunjucks配对短代码语法调用它比三重回溯更长,但并不痛苦:
# The Usual Markdown Nonsense { #the-usual-nonsense }
And now for something entirely different:
{% code "js" %}
function yourCodeHere() { /* ... */ }
{% endcode %}
Etc.这个博客最近代码不多,但这几个月运行得还算不错。如果有人想看到它被整合成一个11ty插件,告诉我。
行号满足了我的渴望,但情况很不稳定;PrismJS 的标记不够理想,像高亮这样的功能从未工作过。我一直对其他解决方案感到好奇。
这时,我的同事戴夫·鲁珀特的非常酷微型打火机项目。如果你想了解更多关于背后超现代网络决策的信息,他有一个很棒的博客文章这揭示了原因和原因。
我一直在思考语法突出的替代方案,以及即将推出的基于11ty的多文档HTML幻灯片系统(暂名:“Faro”),正在重新设计{% code "..." %}在幕后使用其他设备似乎越来越有益。
为此,以下是我最终采用的整合MicroLighter的总体方法:
- 确保所有文件都被复制到输出目录。实际上情况比这复杂一些,因为我在开发者模式下为了速度,我避免复制,更倾向于使用符号链接——这个博客在Chromebook上重建时间不到六秒,甚至更少秒——但效果是一样的:
config.addPassthroughCopy({
overwrite: false,
"node_modules/microlighter/dist": "assets/microlighter",
});- 用途去年文章的观点仅在需要时包含脚本并内联CSS。该系统包含三个组成部分:
- 将核心网页组件JS加入捆绑包。
- 内联主题 CSS(也通过捆绑器)。
- 确保每种使用语言都能生成标签。
js此外javascript例如 )。我没有语言依赖的解决方案,但顶层语法预加载在我有限的使用中避免了网络停滞。 - 用短码处理内容逃逸。默认情况下,11ty 和 Nunjucks(我喜欢的模板引擎)会导出内容,替换
<其中<,例如,我们希望存在于阻挡。在周围标记意图以HTML形式出现在最终页面的情况下,这种方式就不太理想,比如网页组件的情况。绕过Nunjucks逃逸依赖于旧的...env.filters.safe()以及env.filters.escape()黑客攻击,跳过Markdown逃脱则是markdown-it-ignore.这些工具结合起来,允许网页组件标记在其他处理过程中通过。
步骤#2是关键点。如果没有能够加载依赖、且仅在需要高亮时包含代码并引入语言定义的能力,我不会愿意采用客户端解决方案。
这个新短代码的依赖链有点笨重且针对特定网站,所以我不太愿意作为插件提供。如果有抱负的人感兴趣,我很乐意分享我的临时代码,并讨论如何让它变得更通用。
注释
- 我理解11ty's Build Awesome品牌重塑的品牌一致性论点。我非常喜欢扎克,他建立的可爱且支持的社区,以及Fonticons, Inc.团队通过Font Awesome、Web Awesome,现在还有Build Awesome所实施的OSS资助模式。我深深理解。当Build Awesome的众筹宣布时,我很早就投入资金支持这项工作,看到社区对项目的热情支持我感到无比欣慰。...以上都是前言,说明我不会无意中说出“打造超棒”这几个字。对我来说永远是110,我希望大家能原谅(或至少包容)我对风格指南的失误。
- 你可能会想“难道没有人会在2026年的捆绑包里包含所有PrismJS语言吧?”我会回答说,先生或女士,您并没有仔细看你所感受到的那堆页“有点慢”.不仅正确的依赖包含在“现代”前端中像鸡牙一样难,我个人在过去几年里诊断出近十起PrismJS语言定义过度包含的案例。也许未来会逐渐淡出语法突出错误,但那不是去年、上个月或昨天。