
Research
Two Malicious Rust Crates Impersonate Popular Logger to Steal Wallet Keys
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
nunjucks-pagelet-zbj
Advanced tools
scrat后端渲染组件化开发模式通过扩展 nunjucks 模板引擎实现了组件资源管理方案。
const path = require('path');
const nunjucks = require('nunjucks');
const pagelet = require('nujucks-pagelet');
const baseDir = path.join(process.cwd(), './test/fixtures/general');
const env = nunjucks.configure(baseDir, {});
/**
* 初始化入口
* @method Engine#register
* @param {Object} opt 配置对象
* @param {String|Object|Function} opt.manifest 资源映射表, 可以是文件路径/映射表对象/读取函数
* @param {String} opt.root 静态文件的根目录
* @param {Boolean} [opt.cache] 是否缓存资源映射表
* @param {Object} [opt.logger] 日志对象
* @return {void}
*/
pagelet.configure({
root: baseDir,
manifest: path.join(baseDir, 'manifest.json'),
cache: true
});
// 注册 Tag 到 nunjucks
pagelet.register(env);
// 渲染
const locals = {};
const html = env.render('test.tpl', locals);
// use in express
const express = require('express');
const app = express();
app.set('view engine', 'tpl');
env.express(app);
// 注册中间件, 读取 Pagelet 的参数
app.use(function(req, res, next) {
res.locals._query = req.query;
res.locals._body = req.body;
var pagelets = req.get('X-Pagelets');
if(pagelets){
res.set('Content-Type', 'application/json');
// res.set('Cache-Control', 'no-cache, no-store');
// res.set('Pragma', 'no-cache');
// res.set('Expires', 0);
res.locals._pagelets = pagelets;
}
next();
});
app.get('/', function(req, res) {
res.render('test.tpl', res.locals);
});
manifest 文件是通过构建工具生成的, 主要描述了资源的依赖关系.
{
"combo": false,
"comboPattern": "/co??%s",
"hash": "f0ab3c",
"res": {
"components/nav/nav.js": {
"uri": "c/nav/nav.js",
"type": "js",
"deps": [
"views/jquery/jquery.js",
"components/nav/nav.css"
]
},
"components/nav/nav.css": {
"uri": "c/nav/nav.css",
"type": "css",
"deps": [
"views/reset/reset.css"
]
},
"components/bar/bar.tpl": {
"uri": "c/bar/bar.tpl",
"type": "tpl",
"deps": [
"nav",
"components/bar/bar.css"
]
},
"components/bar/bar.css": {
"uri": "c/bar/bar.css",
"type": "css"
},
"components/foo/foo.tpl": {
"uri": "c/foo/foo.tpl",
"type": "tpl",
"deps": [
"nav",
"components/foo/foo.css"
]
},
"components/foo/foo.css": {
"uri": "c/foo/foo.css",
"type": "css"
},
"views/jquery/jquery.js": {
"uri": "v/jquery/jquery.js",
"type": "js"
},
"views/reset/reset.css": {
"uri": "v/reset/reset.css",
"type": "css"
},
"views/index/index.tpl": {
"uri": "views/test.tpl",
"type": "tpl",
"deps": [
"foo"
]
}
}
}
对 nunjucks
的自定义标签语法进行了修订:
data-src="http://"
这类的属性名, 无需双引号包裹disabled
这类的没有赋值的属性, 还是维持官方的实现, 需要双引号包裹, 否则会当做变量{% body cdn="asd", data-src="http://", "disabled"" %}{% endbody %}
{% pagelet $id="asd" class=["a", "b"] style={a: true, b: someVar} %}{% endpagelet %}
swig版本传送门: scrat-swig, Base on Latest commit 6cdcb1f on 20 Oct .
功能: 资源/组件引用接口,调用后只是收集资源,并不会有内容输出。
闭合: NO
参数:
示例:
{% require $id="header" %} {# 组件id #}
{% require $id="./lib.js" %} {# 工程路径 #}
{% for val in obj %}
{% require $id=val %} {# 模板变量 #}
{% endfor %}
功能: 替代原生的 <html>
标签包裹页面主体部分,用于实现资源url输出时替换页面占位
闭合: YES
参数:
示例:
{% html class="abc" data-value="bcd" %}
...
{% endhtml %}
渲染后的html:
<!DOCTYPE html>
<html class="abc" data-value="bcd">
...
</html>
功能: 替代原生的 <head>
标签包裹页面head部分,用于实现资源CSS输出占位
闭合: YES
参数: 任何参数都将转换为输出的 head 标签的属性。
示例:
{% html%}
{% head class="abc" data-xxx="bcd" %}
...
{% endhead %}
...
{% endhtml %}
渲染后的html:
<html>
<head class="abc" "data-xxx"="bcd">
...
</head>
...
</html>
功能: 替代原生的 <body>
标签包裹页面body部分,用于实现资源JS输出占位
闭合: YES
参数: 任何参数都将转换为输出的 body 标签的属性。
示例:
{% html%}
{% head %}
...
{% endhead %}
{% body class="main" id="main" "data-ooo"="abc" %}
...
{% endbody %}
{% endhtml %}
渲染后的html:
<html>
<head>
...
</head>
<body class="main" id="main" data-ooo="abc">
...
</body>
</html>
功能: 替代原生的 <script>
标签,收集页面中散落的 script 脚本统一放到页面尾部输出。
闭合: YES
参数: 无
示例:
{% html%}
{% head %}
...
{% require $id="../lib/jquery.js" %} {# 引用资源 #}
{% endhead %}
{% body %}
...
{% script %}
var header = require('header');
header.init();
{% endscript %}
{% require $id="./index.js" %} {# 引用资源 #}
{% endbody %}
{% endhtml %}
渲染后的html:
<html>
<head>
...
</head>
<body>
...
<script src="/views/lib/jquery.js"></script>
<script src="/views/index/index.js"></script>
<script>
!function(){
var header = require('header');
header.init();
}();
</script>
</body>
</html>
注意: 在body闭合标签之前,js输出的顺序是:
功能: 页面区域划分,用于 quickling 加载页面
闭合: YES
注意: pagelet默认会生成一个dom结构,如果希望不生成任何结构,须设置 $tag
属性值为字符串 "none"
参数:
div
。如果指定为none,框架会输出一个注释来标注pagelet的范围。示例:
{% html%}
{% head %}
...
{% endhead %}
{% body %}
...
{% pagelet $id="main" class="main" %}
<ul>
{% pagelet $id="list" $tag="none" %}
{% for item in list %}
<li>{{ loop.index }} - {{ loop.key }}: {{ x }}</li>
{% endfor %}
{% endpagelet %}
</ul>
{% pagelet $id="form" $tag="form" id="my-form" class="form-control" %}
<input type="text" name="username">
{% endpagelet %}
{% endpagelet %}
...
{% endbody %}
{% endhtml %}
渲染后的html:
<html>
<head>
...
</head>
<body>
...
<div class="main" data-pagelet="main">
<ul>
<!-- pagelet [main.list] start -->
<li>0 - a: 123</li>
<li>0 - b: 456</li>
<!-- pagelet [main.list] end -->
</ul>
<form id="my-form" class="form-control" data-pagelet="main.form">
<input type="text" name="username">
</form>
</div>
...
</body>
</html>
功能: 如果使用 quickling 切换页面,你的页面此标签替代原生的 <title>
标签用以收集页面标题数据,这样页面切换后框架可以自动修改页面的title显示。
闭合: YES
参数: 无
示例:
{% html%}
{% head %}
...
{% title %}首页{% endtitle %}
{% endhead %}
{% body %}
...
{% endbody %}
{% endhtml %}
渲染后的html:
<html>
<head>
...
<title>首页</title>
</head>
<body>
...
</body>
</html>
功能: 在pagelet区域内收集模板数据将来在 quickling 加载时可以传递给前端框架。
闭合: NO
示例:
{% html%}
{% head %}
...
{% title %} 列表页 - 第{{ offset }}页 {% endtitle %}
{% endhead %}
{% body %}
...
{% pagelet $id="main" class="main" %}
{% pagelet $id="list" $tag="ul" %}
{% for item in list %}
<li>{{ loop.index }} - {{ loop.key }}: {{ x }}</li>
{% endfor %}
{% datalet page=offset %}
{% endpagelet %}
{% endpagelet %}
...
{% endbody %}
{% endhtml %}
假设前端获取main.list的pagelet数据:
{
"title": "列表页 - 第5页", // title收集
"html": {
"main.list": "..." // pagelet的html内容
},
"data": {
page: 5 // datalet收集
},
"js": [ ... ], // js依赖
"css": [ ... ], // css依赖
"script": [ ... ] // script收集
}
功能: 收集位于 ATF
标签以上的css资源,并内嵌到主文档的head标签中。只会收集使用 require
标签加载的css资源,并不会收集link标签的css资源。用于加快首屏渲染速度的优化。
闭合: NO
参数: 无
示例:
{% html%}
{% head %}
...
{% endhead %}
{% body %}
...
{% require $id="header" %}
{% require $id="./index.css" %}
{% ATF %}
{% require $id="footer" %}
...
{% endbody %}
{% endhtml %}
渲染后的html:
<html>
<head>
...
<style>
.header {...}
.index {...}
</style>
</head>
<body>
...
<link rel="stylesheet" href="/co??c/footer/footer.css" data-defer>
...
</body>
</html>
FAQs
scrat-swig的nunjucks版本, 提供pagelet支持
We found that nunjucks-pagelet-zbj demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
Research
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.