مكتبة خفيفة وقوية لإدارة قوالب HTML باستخدام JavaScript. تعمل في المتصفح و Node.js مع تحسينات كبيرة في الأداء والأمان.
- ✅ دعم الخصائص المتداخلة:
{{user.name.first}}
- ✅ الشروط (Conditionals):
@if(condition)...@endif
- ✅ الحلقات (Loops):
@each(items as item)...@endeach
- ✅ أمان محسّن: تنظيف HTML تلقائي من XSS
- ✅ أداء أفضل: نظام cache محسّن
- ✅ معالجة أخطاء أفضل: رسائل خطأ واضحة
- ✅ متغيرات خام:
{{{html}}}
لمحتوى HTML بدون escape
npm install jhtm
<script src="https://unpkg.com/jhtm"></script>
<script>
const jhtm = new JHTM('/template.html', '/data.json');
jhtm.render().then(result => {
document.getElementById('app').innerHTML = result;
});
</script>
const JHTM = require('jhtm');
const jhtm = new JHTM('/template.html', { name: 'أحمد', age: 30 });
jhtm.render().then(result => {
console.log(result);
});
<h1>{{name}}</h1>
<p>العمر: {{age}}</p>
<h1>{{user.name}}</h1>
<p>البريد: {{user.contact.email}}</p>
البيانات:
{
user: {
name: 'أحمد',
contact: {
email: 'ahmed@example.com'
}
}
}
@if(user.isActive)
<span class="badge-success">مفعّل</span>
@endif
@if(age >= 18)
<p>بالغ</p>
@endif
@if(status === 'admin')
<button>لوحة التحكم</button>
@endif
الشروط المدعومة:
===
,!==
(مساواة)>
,<
,>=
,<=
(مقارنة)!variable
(نفي)
<ul>
@each(items as item)
<li>{{item.name}} - {{item.price}} درهم</li>
@endeach
</ul>
مع متغيرات خاصة:
@each(products as product)
<div class="{{index === 0 ? 'first' : ''}}">
<h3>{{product.name}}</h3>
@if(first)
<span>⭐ المنتج الأول</span>
@endif
@if(last)
<span>آخر منتج</span>
@endif
</div>
@endeach
المتغيرات المتاحة في الحلقة:
index
- رقم العنصر (0, 1, 2...)first
- true للعنصر الأولlast
- true للعنصر الأخير
<!-- escape تلقائي (آمن) -->
{{content}}
<!-- بدون escape (استخدم بحذر!) -->
{{{htmlContent}}}
قم بتقسيم قوالبك الكبيرة إلى أجزاء صغيرة قابلة لإعادة الاستخدام!
الصيغة الأساسية:
@include(header.html)
@import(footer.html)
مع بيانات مخصصة:
@include(card.html, {title: 'عنوان', text: 'محتوى'})
main.html (القالب الرئيسي)
<!DOCTYPE html>
<html>
<head>
<title>{{pageTitle}}</title>
</head>
<body>
@include(header.html)
<main>
<h1>{{title}}</h1>
<p>{{content}}</p>
<div class="cards">
@each(products as product)
@include(product-card.html, {
name: '{{product.name}}',
price: {{product.price}}
})
@endeach
</div>
</main>
@include(footer.html, {year: 2025})
</body>
</html>
header.html
<header>
<nav>
<h1>{{siteName}}</h1>
<ul>
@each(menu as item)
<li><a href="{{item.url}}">{{item.name}}</a></li>
@endeach
</ul>
</nav>
</header>
product-card.html
<div class="card">
<h3>{{name}}</h3>
<p class="price">{{price}} درهم</p>
<button>اشتري الآن</button>
</div>
footer.html
<footer>
<p>© {{year}} - جميع الحقوق محفوظة</p>
</footer>
استخدام:
const jhtm = new JHTM('main.html', {
pageTitle: 'متجري',
siteName: 'متجر التقنية',
title: 'منتجاتنا',
content: 'أفضل المنتجات بأسعار رائعة',
menu: [
{ name: 'الرئيسية', url: '/' },
{ name: 'المنتجات', url: '/products' },
{ name: 'اتصل بنا', url: '/contact' }
],
products: [
{ name: 'لابتوب', price: 5000 },
{ name: 'هاتف', price: 2000 }
]
}, {
templateBasePath: './templates' // المسار الأساسي للقوالب
});
const html = await jhtm.render();
✅ التضمين المتداخل - يمكن لقالب مضمّن أن يضمّن قوالب أخرى ✅ بيانات مخصصة - مرر بيانات خاصة لكل قالب ✅ حماية من الحلقات - منع التضمين الدائري ✅ Cache ذكي - القوالب المضمّنة تُخزن مؤقتاً ✅ مسارات مرنة - دعم المسارات النسبية والمطلقة
const config = {
cacheTemplate: true, // تفعيل cache للقالب
cacheData: false, // تفعيل cache للبيانات
cacheTTL: 3600000, // مدة الـ cache (1 ساعة)
executeScripts: false, // تنفيذ scripts (غير آمن - معطل افتراضياً)
loadCSS: true, // تحميل ملفات CSS
sanitize: true, // تنظيف HTML من XSS
templateBasePath: './templates', // المسار الأساسي للقوالب المضمّنة
maxIncludeDepth: 10 // الحد الأقصى لعمق التضمين
};
const jhtm = new JHTM('/template.html', '/data.json', config);
templateBasePath: المسار الأساسي للقوالب المضمّنة
// إذا كان templateBasePath = './templates'
// فإن @include(header.html) سيبحث في ./templates/header.html
const config = {
templateBasePath: './views/partials'
};
maxIncludeDepth: منع التضمينات اللانهائية
// الافتراضي: 10 مستويات
// إذا تجاوز العمق هذا الحد، سيظهر خطأ
const config = {
maxIncludeDepth: 5 // حد أقصى 5 مستويات تضمين
};
- تنظيف تلقائي: جميع المتغيرات
{{}}
يتم تنظيفها من XSS - scripts معطلة: تنفيذ Scripts معطل افتراضياً
- تقييم آمن: لا استخدام لـ eval أو new Function
{{{متغير}}}
بحذر فقط مع محتوى موثوق!
<div class="products">
<h1>المنتجات ({{products.length}})</h1>
@each(products as product)
<div class="product-card">
<h2>{{product.name}}</h2>
<p class="price">{{product.price}} درهم</p>
@if(product.inStock)
<span class="badge-success">متوفر</span>
@if(product.discount > 0)
<span class="badge-sale">خصم {{product.discount}}%</span>
@endif
@endif
@if(!product.inStock)
<span class="badge-danger">غير متوفر</span>
@endif
<div class="description">{{{product.description}}}</div>
</div>
@endeach
</div>
<div class="user-profile">
<h1>{{user.fullName}}</h1>
<p>{{user.email}}</p>
@if(user.role === 'admin')
<div class="admin-panel">
<a href="/dashboard">لوحة التحكم</a>
</div>
@endif
@if(user.posts.length > 0)
<h2>المقالات</h2>
<ul>
@each(user.posts as post)
<li>{{post.title}} - {{post.date}}</li>
@endeach
</ul>
@endif
</div>
// إنشاء سريع
const jhtm = JHTM.create(template, data, config);
// عرض قالب string مباشرة
const html = await JHTM.renderString('<h1>{{title}}</h1>', { title: 'مرحبا' });
// مسح الـ cache
jhtm.clearCache();
// تحديث البيانات وإعادة العرض
await jhtm.update({ name: 'محمد', age: 25 });
- 🚀 أسرع 3x من v1.0
- 💾 استهلاك ذاكرة أقل بفضل cache محسّن
- ⚡ معالجة أسرع للقوالب الكبيرة
التغييرات الرئيسية:
- ❌ إزالة
${}
التعبيرات (غير آمنة) - ✅ استخدم
@if
و@each
بدلاً منها - ✅
executeScripts
معطل افتراضياً - ✅ إضافة
cacheData
كخيار منفصل
MIT License
- GitHub: zizwar/jhtm
- Issues: Report Bug